From a933526483a15da282bacac54608d44d2173beb4 Mon Sep 17 00:00:00 2001 From: shuffy <794338160@qq.com> Date: Sat, 28 Apr 2018 21:56:42 +0800 Subject: [PATCH 001/343] fix a small mistake in the README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c2cd24d8..8128c28d 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Selection refers to processing the image only in the the specific identification ![](http://idoc.imagepy.org/imgs/astroi.png "ROI") -**Geometric Transformation:**ImagePy supports geometric transformations. It can carry out rotation, translation and other conventional matrix transformations. What’s more, these rotations are interactive and support selection. +**Geometric Transformation:** ImagePy supports geometric transformations. It can carry out rotation, translation and other conventional matrix transformations. What’s more, these rotations are interactive and support selection. ![](http://idoc.imagepy.org/imgs/asttransform.png "transform") From d1e3a6df3c23bf30641e7001df7ad8ed6bc0bb25 Mon Sep 17 00:00:00 2001 From: Prevalenter <4346liuxing> Date: Thu, 10 May 2018 11:49:36 +0800 Subject: [PATCH 002/343] add .dat Signed-off-by: Prevalenter <4346liuxing> --- imagepy/menus/File/DAT/__init__.py | 0 imagepy/menus/File/DAT/dat_plgs.py | 16 ++++++++++++++++ imagepy/menus/File/__init__.py | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 imagepy/menus/File/DAT/__init__.py create mode 100644 imagepy/menus/File/DAT/dat_plgs.py diff --git a/imagepy/menus/File/DAT/__init__.py b/imagepy/menus/File/DAT/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/menus/File/DAT/dat_plgs.py b/imagepy/menus/File/DAT/dat_plgs.py new file mode 100644 index 00000000..b50a85e9 --- /dev/null +++ b/imagepy/menus/File/DAT/dat_plgs.py @@ -0,0 +1,16 @@ +from imagepy.core.util import fileio +import numpy as np +from imagepy.core.manager import ReaderManager, WriterManager + + + +def imread(path): + # return pydicom.read_file(path, force=True).pixel_array + return np.loadtxt(path,dtype=float) +ReaderManager.add('dat', imread) + +class OpenFile(fileio.Reader): + title = 'DAT Open' + filt = ['DAT'] + +plgs = [OpenFile] \ No newline at end of file diff --git a/imagepy/menus/File/__init__.py b/imagepy/menus/File/__init__.py index 44de16a4..f82f71b9 100644 --- a/imagepy/menus/File/__init__.py +++ b/imagepy/menus/File/__init__.py @@ -6,4 +6,4 @@ ''' ### TODO: Fixme! In this directory, many path should be corrected?! catlog = ['new_plg', '-', 'open_plg', 'save_plg', '-', 'Open Recent', 'Samples Local', 'Samples Online', - '-', 'Import', 'Export', '-', 'BMP', 'JPG', 'PNG', 'TIF', 'GIF','DICOM', '-', 'exit_plg'] \ No newline at end of file + '-', 'Import', 'Export', '-', 'BMP', 'JPG', 'PNG', 'TIF', 'GIF','DICOM','DAT', '-', 'exit_plg'] \ No newline at end of file From ab30b33129d0fb5366efefda8563c18f1a4a6bfb Mon Sep 17 00:00:00 2001 From: Prevalenter <4346liuxing> Date: Fri, 11 May 2018 12:36:50 +0800 Subject: [PATCH 003/343] add .dat save Signed-off-by: Prevalenter <4346liuxing> --- imagepy/menus/File/DAT/dat_plgs.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/imagepy/menus/File/DAT/dat_plgs.py b/imagepy/menus/File/DAT/dat_plgs.py index b50a85e9..837e13de 100644 --- a/imagepy/menus/File/DAT/dat_plgs.py +++ b/imagepy/menus/File/DAT/dat_plgs.py @@ -1,16 +1,18 @@ from imagepy.core.util import fileio import numpy as np from imagepy.core.manager import ReaderManager, WriterManager - - - def imread(path): - # return pydicom.read_file(path, force=True).pixel_array return np.loadtxt(path,dtype=float) +def imsave(path,img): + np.savetxt(path,img) ReaderManager.add('dat', imread) - +WriterManager.add('dat', imsave) class OpenFile(fileio.Reader): title = 'DAT Open' filt = ['DAT'] -plgs = [OpenFile] \ No newline at end of file +class SaveFile(fileio.Writer): + title = 'DAT Save' + filt = ['DAT'] + +plgs = [OpenFile,SaveFile] \ No newline at end of file From ca14a78e8446da2acf10b64a0b2a8b3ae922df29 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 27 May 2018 08:35:46 +0800 Subject: [PATCH 004/343] table --- imagepy/core/myvi/manager.py | 2 +- imagepy/core/util/testdata.py | 2 +- imagepy/core/wraper/imageplus.py | 2 +- imagepy/ipyalg/hydrology/findmax.py | 2 +- imagepy/ipyalg/hydrology/ridge.py | 2 +- imagepy/ipyalg/hydrology/watershed.py | 4 +- imagepy/menus/Analysis/Tables/__init__.py | 1 - imagepy/menus/Analysis/Tables/operate_plg.py | 27 --------- .../menus/Analysis/Tables/savetable_plg.py | 55 ------------------- imagepy/menus/Analysis/__init__.py | 2 +- imagepy/menus/File/BMP/bmp_plgs.py | 2 +- imagepy/menus/File/Export/sequence_plg.py | 2 +- imagepy/menus/File/GIF/gif_plgs.py | 2 +- imagepy/menus/File/Import/raw_plg.py | 2 +- imagepy/menus/File/Import/sequence_plg.py | 2 +- imagepy/menus/File/JPG/jpg_plgs.py | 2 +- imagepy/menus/File/PNG/png_plgs.py | 2 +- imagepy/menus/File/TIF/tif_plgs.py | 2 +- imagepy/menus/File/open_plg.py | 2 +- imagepy/menus/Plugins/Games/drawstep_plg.py | 2 +- imagepy/ui/canvas.py | 2 +- requirements.txt | 4 ++ setup.py | 6 +- 23 files changed, 28 insertions(+), 103 deletions(-) delete mode 100644 imagepy/menus/Analysis/Tables/__init__.py delete mode 100644 imagepy/menus/Analysis/Tables/operate_plg.py delete mode 100644 imagepy/menus/Analysis/Tables/savetable_plg.py diff --git a/imagepy/core/myvi/manager.py b/imagepy/core/myvi/manager.py index e7ad218c..a0af2a86 100644 --- a/imagepy/core/myvi/manager.py +++ b/imagepy/core/myvi/manager.py @@ -3,7 +3,7 @@ import moderngl from time import time -from scipy.misc import imread +from skimage.io import imread import numpy as np from math import sin, cos, tan, pi import scipy.ndimage as nimg diff --git a/imagepy/core/util/testdata.py b/imagepy/core/util/testdata.py index c0150756..b2d70365 100644 --- a/imagepy/core/util/testdata.py +++ b/imagepy/core/util/testdata.py @@ -1,7 +1,7 @@ from imagepy import IPy from imagepy.core.engine import Free from imagepy.core.manager import ReaderManager, WriterManager, ViewerManager -from scipy.misc import imread +from skimage.io import imread import os.path as osp, os import numpy as np diff --git a/imagepy/core/wraper/imageplus.py b/imagepy/core/wraper/imageplus.py index 563282d2..8d29b92c 100644 --- a/imagepy/core/wraper/imageplus.py +++ b/imagepy/core/wraper/imageplus.py @@ -139,7 +139,7 @@ def swap(self): self.snap, self.imgs[self.cur] = self.imgs[self.cur], self.snap if __name__=='__main__': - from scipy.misc import imread + from skimage.io import imread img = imread('results.bmp') ips = ImagePlus([img, 255-img]) diff --git a/imagepy/ipyalg/hydrology/findmax.py b/imagepy/ipyalg/hydrology/findmax.py index fa9a27c3..fefb307b 100644 --- a/imagepy/ipyalg/hydrology/findmax.py +++ b/imagepy/ipyalg/hydrology/findmax.py @@ -122,7 +122,7 @@ def find_maximum(img, tor, mode = True): return idx if __name__ == '__main__': - from scipy.misc import imread + from skimage.io import imread from scipy.ndimage import gaussian_filter from time import time import matplotlib.pyplot as plt diff --git a/imagepy/ipyalg/hydrology/ridge.py b/imagepy/ipyalg/hydrology/ridge.py index 50f0abb8..09a49527 100644 --- a/imagepy/ipyalg/hydrology/ridge.py +++ b/imagepy/ipyalg/hydrology/ridge.py @@ -201,7 +201,7 @@ def ridge(img, mark, up=True): else: mark[i] = 0 if __name__ == '__main__': - from scipy.misc import imread + from skimage.io import imread import scipy.ndimage as ndimg import matplotlib.pyplot as plt from time import time diff --git a/imagepy/ipyalg/hydrology/watershed.py b/imagepy/ipyalg/hydrology/watershed.py index cb1b1312..b7ebfb2d 100644 --- a/imagepy/ipyalg/hydrology/watershed.py +++ b/imagepy/ipyalg/hydrology/watershed.py @@ -1,7 +1,7 @@ import numpy as np from numba import jit from scipy.ndimage import label, generate_binary_structure -from scipy.misc import imread, imsave +from skimage.io import imread, imsave def neighbors(shape, conn=1): dim = len(shape) @@ -117,7 +117,7 @@ def watershed(img, mark, conn=1, line=False, up=True): plt.imshow(markers) plt.show() ''' - from scipy.misc import imread + from skimage.io import imread import matplotlib.pyplot as plt from time import time diff --git a/imagepy/menus/Analysis/Tables/__init__.py b/imagepy/menus/Analysis/Tables/__init__.py deleted file mode 100644 index 9461a9a1..00000000 --- a/imagepy/menus/Analysis/Tables/__init__.py +++ /dev/null @@ -1 +0,0 @@ -catlog = ['savetable_plg', '-', 'operate_plg'] \ No newline at end of file diff --git a/imagepy/menus/Analysis/Tables/operate_plg.py b/imagepy/menus/Analysis/Tables/operate_plg.py deleted file mode 100644 index 674df575..00000000 --- a/imagepy/menus/Analysis/Tables/operate_plg.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Tue Dec 27 12:04:18 2016 -@author: yxl -""" -from imagepy import IPy -from imagepy.core.engine import Free - -class Join(Free): - title = 'Join Tables *' - - def run(self, para = None): - IPy.alert('join two tabl with special field, not implemented!') - -class AddField(Free): - title = 'Add Field *' - - def run(self, para = None): - IPy.alert('add ont field to the table, not implemented!') - -class Frequence(Free): - title = 'Frequence Field *' - - def run(self, para = None): - IPy.alert('merge the same value and count frequence, not implemented!') - -plgs = [Join, AddField, Frequence] \ No newline at end of file diff --git a/imagepy/menus/Analysis/Tables/savetable_plg.py b/imagepy/menus/Analysis/Tables/savetable_plg.py deleted file mode 100644 index 4373ed9b..00000000 --- a/imagepy/menus/Analysis/Tables/savetable_plg.py +++ /dev/null @@ -1,55 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Tue Dec 27 11:30:24 2016 -@author: yxl -""" -import wx -from imagepy import IPy, root_dir -from imagepy.core.engine import Free -from imagepy.core.manager import TableManager - -class Csv(Free): - title = 'Save Table As CSV' - #para = {'tab': None, 'path':root_dir} - para = {'tab': None, 'path':'./'} - - def load(self): - n = len(TableManager.get_titles()) - if n>0:return True - IPy.alert('No table opened!') - return False - - def show(self): - self.view = [('tab', 'Table', 'tab', '')] - rst = IPy.getpara('Import sequence', self.view, self.para) - if rst!=wx.ID_OK:return rst - filt = 'CSV files (*.csv)|*.csv' - return IPy.getpath('Import sequence', filt, self.para) - - def run(self, para = None): - table = TableManager.get(para['tab']) - table.save_tab(para['path'], ',') - -class Tab(Free): - title = 'Save Table As Tab' - #para = {'tab': None, 'path':root_dir} - para = {'tab': None, 'path':'./'} - - def load(self): - n = len(TableManager.get_titles()) - if n>0:return True - IPy.alert('No table opened!') - return False - - def show(self): - self.view = [('tab', 'Table', 'tab', '')] - rst = IPy.getpara('Import sequence', self.view, self.para) - if rst!=wx.ID_OK:return rst - filt = 'TXT files (*.txt)|*.txt' - return IPy.getpath('Import sequence', filt, self.para) - - def run(self, para = None): - table = TableManager.get(para['tab']) - table.save_tab(para['path'], '\t') - -plgs = [Csv, Tab] \ No newline at end of file diff --git a/imagepy/menus/Analysis/__init__.py b/imagepy/menus/Analysis/__init__.py index 2450fc20..708242ce 100644 --- a/imagepy/menus/Analysis/__init__.py +++ b/imagepy/menus/Analysis/__init__.py @@ -1 +1 @@ -catlog = ['label_plg', '-', 'statistic_plg', '-', 'Region Analysis', '3D Analysis', 'Skeleton Network', '-', 'Tables'] \ No newline at end of file +catlog = ['label_plg', '-', 'statistic_plg', '-', 'Region Analysis', '3D Analysis', 'Skeleton Network'] \ No newline at end of file diff --git a/imagepy/menus/File/BMP/bmp_plgs.py b/imagepy/menus/File/BMP/bmp_plgs.py index 21795a0e..61a5c11a 100644 --- a/imagepy/menus/File/BMP/bmp_plgs.py +++ b/imagepy/menus/File/BMP/bmp_plgs.py @@ -1,5 +1,5 @@ from imagepy.core.util import fileio -from scipy.misc import imread, imsave +from skimage.io import imread, imsave from imagepy.core.manager import ReaderManager, WriterManager ReaderManager.add('bmp', imread) diff --git a/imagepy/menus/File/Export/sequence_plg.py b/imagepy/menus/File/Export/sequence_plg.py index 333da6ec..6b02c82a 100644 --- a/imagepy/menus/File/Export/sequence_plg.py +++ b/imagepy/menus/File/Export/sequence_plg.py @@ -5,7 +5,7 @@ @author: yxl """ import wx -from scipy.misc import imsave +from skimage.io import imsave from imagepy.core.engine import Simple from imagepy.core.manager import WriterManager, ViewerManager from imagepy import IPy, root_dir diff --git a/imagepy/menus/File/GIF/gif_plgs.py b/imagepy/menus/File/GIF/gif_plgs.py index 7e48cd12..f0a21b45 100644 --- a/imagepy/menus/File/GIF/gif_plgs.py +++ b/imagepy/menus/File/GIF/gif_plgs.py @@ -1,5 +1,5 @@ from imagepy.core.util import fileio -from scipy.misc import imread, imsave +from skimage.io import imread, imsave from imagepy.core.manager import ReaderManager, WriterManager ReaderManager.add('gif', imread) diff --git a/imagepy/menus/File/Import/raw_plg.py b/imagepy/menus/File/Import/raw_plg.py index 99c5504a..54f81079 100644 --- a/imagepy/menus/File/Import/raw_plg.py +++ b/imagepy/menus/File/Import/raw_plg.py @@ -1,7 +1,7 @@ import wx,os,sys import numpy as np import io# urllib2 urllib.request, urllib.error, urllib.parse -from scipy.misc import imread +from skimage.io import imread from imagepy import IPy from imagepy.core.engine import Free diff --git a/imagepy/menus/File/Import/sequence_plg.py b/imagepy/menus/File/Import/sequence_plg.py index c2d667cc..1974292e 100644 --- a/imagepy/menus/File/Import/sequence_plg.py +++ b/imagepy/menus/File/Import/sequence_plg.py @@ -5,7 +5,7 @@ """ from imagepy.core.util import fileio -from scipy.misc import imread +from skimage.io import imread from imagepy.core.manager import ReaderManager, ViewerManager from imagepy.core.engine import Free from imagepy import IPy diff --git a/imagepy/menus/File/JPG/jpg_plgs.py b/imagepy/menus/File/JPG/jpg_plgs.py index e3fdb924..3f17670f 100644 --- a/imagepy/menus/File/JPG/jpg_plgs.py +++ b/imagepy/menus/File/JPG/jpg_plgs.py @@ -1,5 +1,5 @@ from imagepy.core.util import fileio -from scipy.misc import imread, imsave +from skimage.io import imread, imsave from imagepy.core.manager import ReaderManager, WriterManager ReaderManager.add('jpg', imread) diff --git a/imagepy/menus/File/PNG/png_plgs.py b/imagepy/menus/File/PNG/png_plgs.py index 4e576b70..b913676e 100644 --- a/imagepy/menus/File/PNG/png_plgs.py +++ b/imagepy/menus/File/PNG/png_plgs.py @@ -1,5 +1,5 @@ from imagepy.core.util import fileio -from scipy.misc import imread, imsave +from skimage.io import imread, imsave from imagepy.core.manager import ReaderManager, WriterManager ReaderManager.add('png', imread) diff --git a/imagepy/menus/File/TIF/tif_plgs.py b/imagepy/menus/File/TIF/tif_plgs.py index c4b5fdf1..6610cf9e 100644 --- a/imagepy/menus/File/TIF/tif_plgs.py +++ b/imagepy/menus/File/TIF/tif_plgs.py @@ -1,5 +1,5 @@ from imagepy.core.util import fileio -from scipy.misc import imread, imsave +from skimage.io import imread, imsave from imagepy.core.manager import ReaderManager, WriterManager ReaderManager.add('tif', imread) diff --git a/imagepy/menus/File/open_plg.py b/imagepy/menus/File/open_plg.py index 37138c95..7de84d04 100644 --- a/imagepy/menus/File/open_plg.py +++ b/imagepy/menus/File/open_plg.py @@ -1,5 +1,5 @@ import wx,os,sys -from scipy.misc import imread +from skimage.io import imread if sys.version_info[0]==2: from urllib2 import urlopen diff --git a/imagepy/menus/Plugins/Games/drawstep_plg.py b/imagepy/menus/Plugins/Games/drawstep_plg.py index 19b514f3..b9bbfc90 100644 --- a/imagepy/menus/Plugins/Games/drawstep_plg.py +++ b/imagepy/menus/Plugins/Games/drawstep_plg.py @@ -1,4 +1,4 @@ -from scipy.misc import imread +from skimage.io import imread import matplotlib.pyplot as plt from skimage.morphology import skeletonize from scipy.ndimage import distance_transform_edt diff --git a/imagepy/ui/canvas.py b/imagepy/ui/canvas.py index d687b9b5..22787f1e 100644 --- a/imagepy/ui/canvas.py +++ b/imagepy/ui/canvas.py @@ -303,7 +303,7 @@ def __del__(self): sys.path.append('../') import numpy as np from imageplus import ImagePlus - from scipy.misc import imread + from skimage.io import imread img = imread('../imgs/flower.jpg') app = wx.PySimpleApp() frame = wx.Frame(None) diff --git a/requirements.txt b/requirements.txt index f6e0cff8..eb307278 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,8 @@ scikit-image shapely wxpython numba +pandas +xlrd +xlwt +openpyxl pydicom \ No newline at end of file diff --git a/setup.py b/setup.py index c81cfcca..40a6d18d 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ def get_data_files(): if __name__ == '__main__': setup(name='imagepy', - version='0.16', + version='0.19', url='https://github.com/Image-Py/imagepy', description='interactive python image-processing plugin framework', long_description=descr, @@ -27,6 +27,10 @@ def get_data_files(): 'scikit-image', 'shapely', 'wxpython', + 'pandas', + 'xlrd', + 'xlwt', + 'openpyxl', 'numba' ], ) From df9fb188f35b9aa00a42911c40e7c502d4200d2c Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 27 May 2018 16:41:44 +0800 Subject: [PATCH 005/343] nothing --- imagepy/IPy.py | 12 +++++----- imagepy/core/engine/filter.py | 22 ++++++++----------- imagepy/core/engine/free.py | 19 +--------------- imagepy/core/manager/colormanager.py | 10 ++++----- imagepy/core/manager/windowmanager.py | 3 --- .../menus/Plugins/Install/installpkg_plgs.py | 2 +- imagepy/tools/Transform/scale_tol.py | 4 ++-- imagepy/ui/mainframe.py | 2 +- imagepy/ui/panelconfig.py | 2 -- imagepy/ui/widgets/cmappanel.py | 10 ++++----- imagepy/ui/widgets/normal.py | 10 ++++----- 11 files changed, 35 insertions(+), 61 deletions(-) diff --git a/imagepy/IPy.py b/imagepy/IPy.py index 10a8aa27..0b578011 100644 --- a/imagepy/IPy.py +++ b/imagepy/IPy.py @@ -108,16 +108,16 @@ def alert(info, title="ImagePy Alert!"): print('ImagePy Alert >>> %s'%title) print(info) else: - dlg=wx.MessageDialog(curapp, info, title, wx.OK) - dlg.ShowModal() - dlg.Destroy() + dialog=wx.MessageDialog(curapp, info, title, wx.OK) + dialog.ShowModal() + dialog.Destroy() # MT alert = lambda info, title='image-py':callafter(alert_, *(info, title)) def yes_no(info, title="ImagePy Yes-No ?!"): - dlg = wx.MessageDialog(curapp, info, title, wx.YES_NO | wx.CANCEL) - rst = dlg.ShowModal() - dlg.Destroy() + dialog = wx.MessageDialog(curapp, info, title, wx.YES_NO | wx.CANCEL) + rst = dialog.ShowModal() + dialog.Destroy() dic = {wx.ID_YES:'yes', wx.ID_NO:'no', wx.ID_CANCEL:'cancel'} return dic[rst] diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index d73f191e..551606e0 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -91,20 +91,20 @@ class Filter: def __init__(self, ips=None): if ips==None:ips = IPy.get_ips() - self.dlg = None + self.dialog = None self.ips = ips def progress(self, i, n): self.prgs = (i, n) def show(self, temp=ParaDialog): - self.dlg = temp(WindowsManager.get(), self.title) - self.dlg.init_view(self.view, self.para, 'preview' in self.note, modal=self.modal) - self.dlg.set_handle(lambda x:self.preview(self.ips, self.para)) - if self.modal: return self.dlg.ShowModal() - self.dlg.on_ok = lambda : self.ok(self.ips) - self.dlg.on_cancel = lambda : self.cancel(self.ips) - self.dlg.Show() + self.dialog = temp(WindowsManager.get(), self.title) + self.dialog.init_view(self.view, self.para, 'preview' in self.note, modal=self.modal) + self.dialog.set_handle(lambda x:self.preview(self.ips, self.para)) + if self.modal: return self.dialog.ShowModal() + self.dialog.on_ok = lambda : self.ok(self.ips) + self.dialog.on_cancel = lambda : self.cancel(self.ips) + self.dialog.Show() def run(self, ips, snap, img, para = None): return 255-img @@ -149,7 +149,6 @@ def ok(self, ips, para=None, callafter=None): win = WidgetsManager.getref('Macros Recorder') if ips.get_nslices()==1 or 'not_slice' in self.note: # process_one(self, ips, ips.snap, ips.img, para) - print(111) if IPy.uimode() == 'no': process_one(self, ips, ips.snap, ips.img, para, callafter) else: threading.Thread(target = process_one, args = @@ -163,7 +162,6 @@ def ok(self, ips, para=None, callafter=None): if has and para['stack'] or rst == 'yes': para['stack'] = True #process_stack(self, ips, ips.snap, ips.imgs, para) - print(222) if IPy.uimode() == 'no': process_stack(self, ips, ips.snap, ips.imgs, para, callafter) else: threading.Thread(target = process_stack, args = @@ -172,7 +170,6 @@ def ok(self, ips, para=None, callafter=None): elif has and not para['stack'] or rst == 'no': para['stack'] = False #process_one(self, ips, ips.snap, ips.img, para) - print(333) if IPy.uimode() == 'no': process_one(self, ips, ips.snap, ips.img, para, callafter) else: threading.Thread(target = process_one, args = @@ -188,7 +185,6 @@ def cancel(self, ips): def start(self, para=None, callafter=None): ips = self.ips - print('who am i', ips) if not self.check(ips):return if not self.load(ips):return if 'auto_snap' in self.note:ips.snapshot() @@ -204,5 +200,5 @@ def start(self, para=None, callafter=None): if self.show() == wx.ID_OK: self.ok(ips, None, callafter) else:self.cancel(ips) - self.dlg.Destroy() + self.dialog.Destroy() else: self.show() \ No newline at end of file diff --git a/imagepy/core/engine/free.py b/imagepy/core/engine/free.py index 51d3eae8..d6bd1f4e 100644 --- a/imagepy/core/engine/free.py +++ b/imagepy/core/engine/free.py @@ -40,7 +40,6 @@ def show(self): return self.dialog.ShowModal() def start(self, para=None, callback=None): - print('xxxxxxxxxxxxxxxxxx') if not self.load():return if para!=None or self.show() == wx.ID_OK: if para==None:para = self.para @@ -50,20 +49,4 @@ def start(self, para=None, callback=None): if self.asyn and IPy.uimode()!='no': threading.Thread(target = self.runasyn, args = (para, callback)).start() else: - self.runasyn(para, callback) - ''' - self.run(para) - if not callback is None: - callback() - ''' - #if not thd:t.join() - - #self.run(para) - ''' - run = lambda p=para:self.run(p) - - print( thd, '--------------------new thread') - thread = threading.Thread(None, run, ()) - thread.start() - if not thd: thread.join() - ''' \ No newline at end of file + self.runasyn(para, callback) \ No newline at end of file diff --git a/imagepy/core/manager/colormanager.py b/imagepy/core/manager/colormanager.py index 76cca0a8..aede1db1 100644 --- a/imagepy/core/manager/colormanager.py +++ b/imagepy/core/manager/colormanager.py @@ -29,11 +29,11 @@ class ColorManager: @classmethod def get_color(cls, app=None): rst = None - dlg = wx.ColourDialog(app) - dlg.GetColourData().SetChooseFull(True) - if dlg.ShowModal() == wx.ID_OK: - rst = dlg.GetColourData().GetColour() - dlg.Destroy() + dialog = wx.ColourDialog(app) + dialog.GetColourData().SetChooseFull(True) + if dialog.ShowModal() == wx.ID_OK: + rst = dialog.GetColourData().GetColour() + dialog.Destroy() return rst @classmethod diff --git a/imagepy/core/manager/windowmanager.py b/imagepy/core/manager/windowmanager.py index f12ae52a..0a89e55d 100644 --- a/imagepy/core/manager/windowmanager.py +++ b/imagepy/core/manager/windowmanager.py @@ -82,7 +82,6 @@ class TextLogManager: @classmethod def name(cls, name): - print(list(cls.windows.keys()), name) if name==None:name='Log' if name not in cls.windows: return name @@ -187,10 +186,8 @@ class PlotManager: @classmethod def add(cls, win): - print(win) cls.remove(win) callback = lambda a: cls.remove(a()) - print('windows add!') cls.windows.insert(0, weakref.ref(win, callback)) @classmethod diff --git a/imagepy/menus/Plugins/Install/installpkg_plgs.py b/imagepy/menus/Plugins/Install/installpkg_plgs.py index 793b093c..f98f21b9 100644 --- a/imagepy/menus/Plugins/Install/installpkg_plgs.py +++ b/imagepy/menus/Plugins/Install/installpkg_plgs.py @@ -22,7 +22,7 @@ def run(self, para=None): p = subprocess.Popen('%s -m pip list'%sys.executable, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = True) lst = str(p.stdout.read(), encoding='utf-8').replace('\r\n', '\n').split('\n') - IPy.show_table(pd.pandas([[i] for i in lst], columns=['Packages']), 'Packages') + IPy.show_table(pd.DataFrame([[i] for i in lst], columns=['Packages']), 'Packages') plgs = [Install, List] \ No newline at end of file diff --git a/imagepy/tools/Transform/scale_tol.py b/imagepy/tools/Transform/scale_tol.py index 7f7cde85..ddbd90e2 100644 --- a/imagepy/tools/Transform/scale_tol.py +++ b/imagepy/tools/Transform/scale_tol.py @@ -62,8 +62,8 @@ class Plugin(Filter): title = 'Scale' note = ['all', 'auto_msk', 'auto_snap', 'preview'] para = {'kx': 1, 'ky':1, 'ox':0, 'oy':0, 'img':True, 'msk':False} - view = [(float, 'KX', (-100,100), 3, ''), - (float, 'kx', (-100,100), 3, 'KY', 'ky', ''), + view = [(float, 'kx', (-100,100), 3, 'KX', ''), + (float, 'ky', (-100,100), 3, 'KY', ''), (int, 'ox', (-10000,10000), 0, 'OffX', 'pix'), (int, 'oy', (-10000,10000), 0, 'OffY', 'pix'), (bool, 'img', 'scale image'), diff --git a/imagepy/ui/mainframe.py b/imagepy/ui/mainframe.py index 1a23ad0d..5fb74410 100644 --- a/imagepy/ui/mainframe.py +++ b/imagepy/ui/mainframe.py @@ -75,7 +75,7 @@ def __init__( self, parent ): self.Centre( wx.BOTH ) if(IPy.uimode()=='ij'): self.SetMaxSize((-1, self.GetSize()[1])) - self.SetMinSize((-1, self.GetSize()[1])) + self.SetMinSize(self.GetSize()) self.update = False self.Bind(wx.EVT_CLOSE, self.on_close) diff --git a/imagepy/ui/panelconfig.py b/imagepy/ui/panelconfig.py index 1268c507..031025f5 100644 --- a/imagepy/ui/panelconfig.py +++ b/imagepy/ui/panelconfig.py @@ -87,12 +87,10 @@ def pack(self): def para_check(self, para, key):pass def para_changed(self, key): - para = self.para for p in list(para.keys()): if p in self.ctrl_dic: para[p] = self.ctrl_dic[p].GetValue() - sta = sum([i is None for i in list(para.values())])==0 self.btn_OK.Enable(sta) if not sta: return diff --git a/imagepy/ui/widgets/cmappanel.py b/imagepy/ui/widgets/cmappanel.py index 91905d32..40e0bc07 100644 --- a/imagepy/ui/widgets/cmappanel.py +++ b/imagepy/ui/widgets/cmappanel.py @@ -86,16 +86,16 @@ def on_rdc(self, event): x,y = event.GetX()-self.offset[0], event.GetY()-self.offset[1] self.idx = self.pick(x, y) if self.idx==-1:return - dlg = wx.ColourDialog(self) - dlg.GetColourData().SetChooseFull(True) - if dlg.ShowModal() == wx.ID_OK: - rst = dlg.GetColourData().GetColour() + dialog = wx.ColourDialog(self) + dialog.GetColourData().SetChooseFull(True) + if dialog.ShowModal() == wx.ID_OK: + rst = dialog.GetColourData().GetColour() x = self.pts[self.idx][0] self.pts[self.idx] = (x,)+rst[:-1] self.idx=-1 self.cmap[:] = self.linear_color(self.pts) self.update = True - dlg.Destroy() + dialog.Destroy() self.handle() diff --git a/imagepy/ui/widgets/normal.py b/imagepy/ui/widgets/normal.py index 2a3ce898..6f15b213 100644 --- a/imagepy/ui/widgets/normal.py +++ b/imagepy/ui/widgets/normal.py @@ -114,14 +114,14 @@ def ontext(self, event): def oncolor(self, event): rst = None - dlg = wx.ColourDialog(self) - dlg.GetColourData().SetChooseFull(True) - if dlg.ShowModal() == wx.ID_OK: - rst = dlg.GetColourData().GetColour() + dialog = wx.ColourDialog(self) + dialog.GetColourData().SetChooseFull(True) + if dialog.ShowModal() == wx.ID_OK: + rst = dialog.GetColourData().GetColour() self.ctrl.SetBackgroundColour(rst) self.ctrl.SetValue(rst.GetAsString(wx.C2S_HTML_SYNTAX)) self.f(event) - dlg.Destroy() + dialog.Destroy() def SetValue(self, color): self.ctrl.SetBackgroundColour(color) From fea7f46766429a98de6a6dd191afa2bebf3d5c50 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 27 May 2018 16:50:53 +0800 Subject: [PATCH 006/343] ipy --- imagepy/IPy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/IPy.py b/imagepy/IPy.py index 0b578011..3814f523 100644 --- a/imagepy/IPy.py +++ b/imagepy/IPy.py @@ -22,7 +22,7 @@ def load_plugins(): def uimode(): if curapp is None: return 'no' from .core import manager - return manager.ConfigManager.get('uistyle') + return manager.ConfigManager.get('uistyle') or 'ipy' def get_window(): from .core import manager From 1bacd9e6a972686819b2361e84892400c625b51e Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 27 May 2018 17:41:18 +0800 Subject: [PATCH 007/343] nothing --- imagepy/ui/widgets/curvepanel.py | 10 ++++++++-- imagepy/ui/widgets/histpanel.py | 9 +++++++-- imagepy/widgets/histogram/curve_wgt.py | 6 +++--- imagepy/widgets/histogram/histogram_wgt.py | 4 ++-- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/imagepy/ui/widgets/curvepanel.py b/imagepy/ui/widgets/curvepanel.py index f056cac6..d9e66917 100644 --- a/imagepy/ui/widgets/curvepanel.py +++ b/imagepy/ui/widgets/curvepanel.py @@ -96,7 +96,9 @@ def on_paint(self, event): def set_hist(self, hist): if hist is None:self.hist=None - else:self.hist = (hist*self.l/hist.max()).astype(np.uint8) + else: + self.hist = (hist*self.l/hist.max()) + self.logh = (np.log(self.hist+1.0))*(self.l/(np.log(self.l+1))) self.update = True def set_pts(self, pts): @@ -111,8 +113,12 @@ def draw(self): # w, h = self.GetClientSize() # the main draw process - dc.SetPen(wx.Pen((100,100,100), width=1, style=wx.SOLID)) + if not self.hist is None: + dc.SetPen(wx.Pen((200,200,200), width=1, style=wx.SOLID)) + for i in np.linspace(0,self.l,256).astype(np.int16): + dc.DrawLine(i+ox,self.l+1+oy,i+ox,self.l+1-self.logh[i]+oy) + dc.SetPen(wx.Pen((100,100,100), width=1, style=wx.SOLID)) for i in np.linspace(0,self.l,256).astype(np.int16): dc.DrawLine(i+ox,self.l+1+oy,i+ox,self.l+1-self.hist[i]+oy) x, y = np.array(self.pts).T diff --git a/imagepy/ui/widgets/histpanel.py b/imagepy/ui/widgets/histpanel.py index 68ee91e7..20903f98 100644 --- a/imagepy/ui/widgets/histpanel.py +++ b/imagepy/ui/widgets/histpanel.py @@ -34,7 +34,8 @@ def on_paint(self, event): wx.BufferedPaintDC(self, self.buffer) def SetValue(self, hist): - self.hist = (hist*80/hist.max()).astype(np.uint8) + self.hist = (hist*80.0/hist.max()) + self.logh = (np.log(self.hist+1.0))*(80/(np.log(81))) self.update = True def set_lim(self, x1, x2): @@ -49,8 +50,12 @@ def draw(self): # the main draw process print("drawing histogram") - dc.SetPen(wx.Pen((100,100,100), width=1, style=wx.SOLID)) + if not self.hist is None: + dc.SetPen(wx.Pen((200,200,200), width=1, style=wx.SOLID)) + for i in range(256): + dc.DrawLine(i,80,i,80-self.logh[i]) + dc.SetPen(wx.Pen((100,100,100), width=1, style=wx.SOLID)) for i in range(256): dc.DrawLine(i,80,i,80-self.hist[i]) dc.SetPen(wx.Pen((0,0,0), width=1, style=wx.SOLID)) diff --git a/imagepy/widgets/histogram/curve_wgt.py b/imagepy/widgets/histogram/curve_wgt.py index 634dc6df..f53701d7 100644 --- a/imagepy/widgets/histogram/curve_wgt.py +++ b/imagepy/widgets/histogram/curve_wgt.py @@ -67,7 +67,7 @@ def on_apply(self, event): if ips is None:return hist = ips.histogram() self.curvepan.set_hist(hist) - self.handle() + self.handle(None) def on_clear(self, event): ips = IPy.get_ips() @@ -83,7 +83,7 @@ def on_reset(self, event): if ips is None:return hist = ips.histogram() self.curvepan.set_hist(hist) - self.handle() + self.handle(None) def on_invert(self, event): self.curvepan.SetValue([(0,255),(255,0)]) @@ -91,4 +91,4 @@ def on_invert(self, event): if ips is None:return hist = ips.histogram() self.curvepan.set_hist(hist) - self.handle() \ No newline at end of file + self.handle(None) \ No newline at end of file diff --git a/imagepy/widgets/histogram/histogram_wgt.py b/imagepy/widgets/histogram/histogram_wgt.py index a23190ae..3d87bfa5 100644 --- a/imagepy/widgets/histogram/histogram_wgt.py +++ b/imagepy/widgets/histogram/histogram_wgt.py @@ -150,10 +150,10 @@ def on_slice( self, event ): ips = IPy.get_ips() if ips is None: return hist = ips.histogram() - self.histpan.set_hist(hist) + self.histpan.SetValue(hist) def on_stack( self, event ): ips = IPy.get_ips() if ips is None: return hists = ips.histogram(stack=True) - self.histpan.set_hist(hists) \ No newline at end of file + self.histpan.SetValue(hists) \ No newline at end of file From 806e0deb9cdd87f7e52deeeffee61933f9a8264d Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 27 May 2018 18:26:09 +0800 Subject: [PATCH 008/343] nothing --- imagepy/menus/Image/background_plg.py | 5 +++-- imagepy/menus/Process/Hydrology/hydrology_plgs.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/imagepy/menus/Image/background_plg.py b/imagepy/menus/Image/background_plg.py index f0cbd6ab..84682261 100644 --- a/imagepy/menus/Image/background_plg.py +++ b/imagepy/menus/Image/background_plg.py @@ -12,8 +12,8 @@ class SetBackground(Simple): """Calculator Plugin derived from imagepy.core.engine.Simple """ title = 'Set Background' note = ['all'] - para = {'img':'None','op':'Mean', 'k':0.5, 'kill':False} - view = [('img','background', 'img', '8-bit'), + para = {'img':None,'op':'Mean', 'k':0.5, 'kill':False} + view = [('img','img', 'background', '8-bit'), (list, 'op', ['Mean', 'Clip'], str, 'mode', ''), (float, 'k', (0,1), 1, 'blender', ''), (bool, 'kill', 'kill')] @@ -22,6 +22,7 @@ def run(self, ips, imgs, para = None): if para['kill']: ips.backimg = None else: + print(ImageManager.get()) img = ImageManager.get(para['img']).img if img.dtype != np.uint8 or img.shape[:2] != ips.img.shape[:2]: IPy.alert('a background image must be 8-bit and with the same size') diff --git a/imagepy/menus/Process/Hydrology/hydrology_plgs.py b/imagepy/menus/Process/Hydrology/hydrology_plgs.py index a487d7a8..cfc42ba2 100644 --- a/imagepy/menus/Process/Hydrology/hydrology_plgs.py +++ b/imagepy/menus/Process/Hydrology/hydrology_plgs.py @@ -123,7 +123,7 @@ class Watershed(Filter): note = ['8-bit', 'auto_snap', 'not_channel', 'preview'] para = {'sigma':1.0, 'thr':0, 'con':False, 'ud':True, 'type':'white line'} - view = [(float, (0,5), 1, 'sigma', 'sigma', 'pix'), + view = [(float, 'sigma', (0,5), 1, 'sigma', 'pix'), ('slide', 'thr', (0,255), 0, 'Low'), (bool, 'con', 'full connectivity'), (bool, 'ud', 'ascend'), From 15f2b99d94ff486d87f54975f7f5905913e0381c Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 29 May 2018 09:19:14 +0800 Subject: [PATCH 009/343] solve image leak --- imagepy/core/engine/filter.py | 7 ++-- imagepy/core/engine/free.py | 6 ++-- imagepy/core/util/fileio.py | 2 ++ imagepy/core/wraper/imageplus.py | 3 ++ imagepy/core/wraper/tableplus.py | 5 ++- imagepy/menus/File/Import/sequence_plg.py | 2 +- imagepy/ui/panelconfig.py | 24 ++++++++----- wxtest.py | 41 +++++++++++++++++++++++ 8 files changed, 74 insertions(+), 16 deletions(-) create mode 100644 wxtest.py diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index 551606e0..9104561b 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -100,7 +100,7 @@ def progress(self, i, n): def show(self, temp=ParaDialog): self.dialog = temp(WindowsManager.get(), self.title) self.dialog.init_view(self.view, self.para, 'preview' in self.note, modal=self.modal) - self.dialog.set_handle(lambda x:self.preview(self.ips, self.para)) + self.dialog.set_handle(lambda x:self.preview(self.ips, x)) if self.modal: return self.dialog.ShowModal() self.dialog.on_ok = lambda : self.ok(self.ips) self.dialog.on_cancel = lambda : self.cancel(self.ips) @@ -201,4 +201,7 @@ def start(self, para=None, callafter=None): self.ok(ips, None, callafter) else:self.cancel(ips) self.dialog.Destroy() - else: self.show() \ No newline at end of file + else: self.show() + + def __del__(self): + print('filter del') \ No newline at end of file diff --git a/imagepy/core/engine/free.py b/imagepy/core/engine/free.py index d6bd1f4e..e419a605 100644 --- a/imagepy/core/engine/free.py +++ b/imagepy/core/engine/free.py @@ -35,9 +35,9 @@ def load(self):return True def show(self): if self.view==None:return wx.ID_OK - self.dialog = ParaDialog(WindowsManager.get(), self.title) - self.dialog.init_view(self.view, self.para, False, True) - return self.dialog.ShowModal() + with ParaDialog(WindowsManager.get(), self.title) as dialog: + dialog.init_view(self.view, self.para, False, True) + return dialog.ShowModal() def start(self, para=None, callback=None): if not self.load():return diff --git a/imagepy/core/util/fileio.py b/imagepy/core/util/fileio.py index 1120d265..704f844f 100644 --- a/imagepy/core/util/fileio.py +++ b/imagepy/core/util/fileio.py @@ -6,6 +6,8 @@ import numpy as np def show_img(img, title): + if isinstance(img, list): + return IPy.show_img(img, title) if img.dtype==np.uint8 and img.ndim==3 and img.shape[2]==4: img = img[:,:,:3].copy() IPy.show_img([img], title) diff --git a/imagepy/core/wraper/imageplus.py b/imagepy/core/wraper/imageplus.py index 8d29b92c..cbcd7d0c 100644 --- a/imagepy/core/wraper/imageplus.py +++ b/imagepy/core/wraper/imageplus.py @@ -138,6 +138,9 @@ def swap(self): if self.snap is None:return self.snap, self.imgs[self.cur] = self.imgs[self.cur], self.snap + def __del__(self): + print(self.title, '>>> deleted ips') + if __name__=='__main__': from skimage.io import imread img = imread('results.bmp') diff --git a/imagepy/core/wraper/tableplus.py b/imagepy/core/wraper/tableplus.py index d1480912..acba4593 100644 --- a/imagepy/core/wraper/tableplus.py +++ b/imagepy/core/wraper/tableplus.py @@ -61,4 +61,7 @@ def get_props(self): return self.props def count_range(self): - self.range = pd.DataFrame([self.data.min(), self.data.max()]) \ No newline at end of file + self.range = pd.DataFrame([self.data.min(), self.data.max()]) + + def __del__(self): + print(self.title, '>>> deleted tps') \ No newline at end of file diff --git a/imagepy/menus/File/Import/sequence_plg.py b/imagepy/menus/File/Import/sequence_plg.py index 1974292e..40f87b50 100644 --- a/imagepy/menus/File/Import/sequence_plg.py +++ b/imagepy/menus/File/Import/sequence_plg.py @@ -17,7 +17,7 @@ class Plugin(Free): para = {'path':'', 'start':0, 'end':0, 'step':1, 'title':'sequence'} def load(self): - self.filt = sorted(ReaderManager.all()) + self.filt = sorted(ReaderManager.get()) return True def show(self): diff --git a/imagepy/ui/panelconfig.py b/imagepy/ui/panelconfig.py index 031025f5..c55ab5ae 100644 --- a/imagepy/ui/panelconfig.py +++ b/imagepy/ui/panelconfig.py @@ -3,6 +3,7 @@ import wx, platform from ..core.manager import ImageManager, WindowsManager, TableManager from .widgets import * +import weakref widgets = { 'ctrl':None, 'slide':FloatSlider, int:NumCtrl, float:NumCtrl, 'lab':Label, bool:Check, str:TextCtrl, @@ -22,7 +23,7 @@ def __init__( self, parent, title): boxBack.Add(self.lst, 0, wx.ALL, 10) self.SetSizer( boxBack ) self.Layout() - self.handle = self.handle_ + #self.handle = self.handle_ def commit(self, state): self.Destroy() @@ -50,6 +51,13 @@ def init_view(self, items, para, preview=False, modal = True): self.reset(para) self.add_confirm(modal) self.pack() + self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy) + print('bind close') + + + def OnDestroy( self, event ): + self.set_handle(None) + del self.ctrl_dic def parse(self, para) : self.add_ctrl_(widgets[para[0]], *para[1:]) @@ -65,9 +73,10 @@ def add_ctrl(self, key, ctrl): def add_ctrl_(self, Ctrl, key, p): ctrl = Ctrl(self, *p) - if not p[0] is None: self.ctrl_dic[key] = ctrl + if not p[0] is None: + self.ctrl_dic[key] = ctrl if hasattr(ctrl, 'Bind'): - ctrl.Bind(None, lambda x : self.para_changed(key)) + ctrl.Bind(None, self.para_changed) pre = ctrl.prefix if hasattr(ctrl, 'prefix') else None post = ctrl.postfix if hasattr(ctrl, 'postfix') else None self.tus.append((pre, post)) @@ -109,15 +118,12 @@ def reset(self, para=None): def get_para(self): return self.para - def set_handle(self, handle): + def set_handle(self, handle=None): self.handle = handle - if handle==None: self.handle = self.handle_ - - def handle_(self, para): - print(para) + # if handle==None: self.handle = self.handle_ def __del__( self ): - pass + print('panel config deleted!') if __name__ == '__main__': view = [(float, 'r', (0,20), 1, '半径', 'mm'), diff --git a/wxtest.py b/wxtest.py new file mode 100644 index 00000000..90347de9 --- /dev/null +++ b/wxtest.py @@ -0,0 +1,41 @@ +import wx + +class NumCtrl(wx.TextCtrl): + def __init__(self, parent): + wx.TextCtrl.__init__(self, parent, -1, 'text') + + def Bind(self, f):self.f = f + + def __del__( self ): print('text ctrl deleted!') + +class ParaDialog (wx.Dialog): + def __init__( self, parent, title): + wx.Dialog.__init__ (self, parent, -1, title, style = wx.DEFAULT_DIALOG_STYLE) + + def init_view(self): + txtctrl = NumCtrl(self) + txtctrl.Bind(self.f) + + def f(self, key): pass + + def __del__( self ): print('panel config deleted!') + +class Frame(wx.Frame): + def __init__( self, parent ): + wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = 'Test' ) + + btn = wx.Button(self, wx.ID_ANY, "abc") + btn.Bind(wx.EVT_BUTTON, self.show) + + def show(self, event): + with ParaDialog(self, 'Dialog') as dialog: + dialog.init_view() + dialog.ShowModal() + + def __del__(self): print('form delete') + +if __name__ == '__main__': + app = wx.App(False) + frame = Frame(None) + frame.Show() + app.MainLoop() \ No newline at end of file From 87ed8fd19dfcd4dd6c6d9a18fa1e9507e5826b0a Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 29 May 2018 13:40:13 +0800 Subject: [PATCH 010/343] reader --- imagepy/core/manager/iomanager.py | 17 ++++++++++++----- imagepy/core/manager/windowmanager.py | 8 -------- imagepy/core/util/fileio.py | 7 ++++--- imagepy/menus/File/Export/sequence_plg.py | 2 +- imagepy/menus/File/GIF/gif_plgs.py | 2 +- imagepy/menus/File/save_plg.py | 2 +- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/imagepy/core/manager/iomanager.py b/imagepy/core/manager/iomanager.py index a7027abc..2e8bf33c 100644 --- a/imagepy/core/manager/iomanager.py +++ b/imagepy/core/manager/iomanager.py @@ -37,11 +37,18 @@ def add(cls, ext, write, tag='img'): cls.writer[tag][i.lower()] = write @classmethod - def get(cls, ext, tag='img'): - if ext is None: return sorted(cls.writer[tag].keys()) - if not ext.lower() in cls.writer[tag]: - return None - return cls.writer[tag][ext.lower()] + def get(cls, ext=None, tag='img'): + if ext is None and tag is None: + ls = [cls.writer[i].keys() for i in cls.writer.keys()] + return sorted([x for j in ls for x in j]) + elif ext is None and not tag is None: + return sorted(cls.writer[tag].keys()) + elif not ext is None and tag is None: + for i in cls.writer.values(): + if ext.lower() in i: return i[ext.lower()] + elif not tag is None and not ext is None: + if ext.lower() in cls.writer[tag]: + return cls.writer[tag][ext.lower()] class ViewerManager: viewer = {} diff --git a/imagepy/core/manager/windowmanager.py b/imagepy/core/manager/windowmanager.py index 0a89e55d..6568b16f 100644 --- a/imagepy/core/manager/windowmanager.py +++ b/imagepy/core/manager/windowmanager.py @@ -68,14 +68,6 @@ def name(cls, name): title = "{}-{}".format(name,i) if not title in titles: return title - ''' - @classmethod - def close(cls, name): - win = cls.get(name) - if win==None:return - cls.remove(win) - win.close() - ''' class TextLogManager: windows = {} diff --git a/imagepy/core/util/fileio.py b/imagepy/core/util/fileio.py index 704f844f..98305677 100644 --- a/imagepy/core/util/fileio.py +++ b/imagepy/core/util/fileio.py @@ -71,6 +71,7 @@ def show(self): def run(self, ips, imgs, para = None): fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) - write = WriterManager.get(fe[1:]) - group, write = (True, write[0]) if isinstance(write, tuple) else (False, write) - write(para['path'], imgs if group else ips.img) \ No newline at end of file + write = WriterManager.get(fe[1:], 'img') + write2 = write or WriterManager.get(fe[1:], 'imgs') + print(write2, write) + write2(para['path'], imgs if write is None else ips.img) \ No newline at end of file diff --git a/imagepy/menus/File/Export/sequence_plg.py b/imagepy/menus/File/Export/sequence_plg.py index 6b02c82a..4c95bffc 100644 --- a/imagepy/menus/File/Export/sequence_plg.py +++ b/imagepy/menus/File/Export/sequence_plg.py @@ -18,7 +18,7 @@ class Plugin(Simple): def load(self, ips): self.view = [(str, 'name', 'Name', ''), - (list, 'format',list(sorted(WriterManager.all())), str, 'Format', '')] + (list, 'format',list(sorted(WriterManager.get())), str, 'Format', '')] return True def show(self): diff --git a/imagepy/menus/File/GIF/gif_plgs.py b/imagepy/menus/File/GIF/gif_plgs.py index f0a21b45..d88a7840 100644 --- a/imagepy/menus/File/GIF/gif_plgs.py +++ b/imagepy/menus/File/GIF/gif_plgs.py @@ -3,7 +3,7 @@ from imagepy.core.manager import ReaderManager, WriterManager ReaderManager.add('gif', imread) -WriterManager.add('fig', imsave) +WriterManager.add('gif', imsave) class OpenFile(fileio.Reader): title = 'GIF Open' diff --git a/imagepy/menus/File/save_plg.py b/imagepy/menus/File/save_plg.py index a872cc0c..a6a5f1cf 100644 --- a/imagepy/menus/File/save_plg.py +++ b/imagepy/menus/File/save_plg.py @@ -12,7 +12,7 @@ class SaveImage(fileio.Writer): title = 'Save' def load(self, ips): - self.filt = sorted(WriterManager.all()) + self.filt = sorted(WriterManager.get(tag='img')) return True class WindowCapture(fileio.Writer): From c7445aaf11e283c88024720e4d2e4eb3d0333c6c Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 4 Jun 2018 20:57:59 +0800 Subject: [PATCH 011/343] field --- imagepy/ui/panelconfig.py | 2 +- imagepy/ui/widgets/advanced.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/imagepy/ui/panelconfig.py b/imagepy/ui/panelconfig.py index c55ab5ae..516c8e1d 100644 --- a/imagepy/ui/panelconfig.py +++ b/imagepy/ui/panelconfig.py @@ -9,7 +9,7 @@ float:NumCtrl, 'lab':Label, bool:Check, str:TextCtrl, list:Choice, 'img':ImageList, 'color':ColorCtrl, 'any':AnyType, 'chos':Choices, 'fields':TableFields, - 'hist':HistCanvas} + 'field':TableField, 'hist':HistCanvas} class ParaDialog (wx.Dialog): def __init__( self, parent, title): diff --git a/imagepy/ui/widgets/advanced.py b/imagepy/ui/widgets/advanced.py index 42dea12e..0b349815 100644 --- a/imagepy/ui/widgets/advanced.py +++ b/imagepy/ui/widgets/advanced.py @@ -5,6 +5,11 @@ class ImageList(Choice): def __init__(self, parent, title, unit): Choice.__init__(self, parent, ImageManager.get_titles(), str, title, unit) +class TableField(Choice): + def __init__(self, parent, title, unit): + self.tps = TableManager.get() + Choice.__init__(self, parent, list(self.tps.data.columns), str, title, unit) + class TableFields(Choices): def __init__(self, parent, title): self.tps = TableManager.get() From 078f814bed1c35e3cbf67bd7d894cf43a7d63642 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 4 Jun 2018 20:58:25 +0800 Subject: [PATCH 012/343] field --- imagepy/menus/Table/Statistic/sort_plg.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 imagepy/menus/Table/Statistic/sort_plg.py diff --git a/imagepy/menus/Table/Statistic/sort_plg.py b/imagepy/menus/Table/Statistic/sort_plg.py new file mode 100644 index 00000000..54f3cbf3 --- /dev/null +++ b/imagepy/menus/Table/Statistic/sort_plg.py @@ -0,0 +1,18 @@ +from imagepy.core.engine import Table +import pandas as pd +from imagepy import IPy + +class Sort(Table): + title = 'Table Sort By Key' + + para = {'major':None, 'minor':None, 'descend':False} + + view = [('field', 'major', 'major', 'key'), + ('field', 'minor', 'minor', 'key'), + (bool, 'descend', 'descend')] + + def run(self, tps, data, snap, para=None): + tps.data.sort_values(by=[para['major'], para['minor']], + axis=0, ascending=not para['descend'], inplace=True) + +plgs = [Sort] \ No newline at end of file From 9134254b3c7975dd8f4d39d90a0b1d7ac8e7b142 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 13 Jun 2018 08:04:06 +0800 Subject: [PATCH 013/343] table --- imagepy/ui/panelconfig.py | 3 ++- imagepy/ui/widgets/advanced.py | 4 ++++ imagepy/ui/widgets/normal.py | 20 ++++++++--------- wxtest.py | 41 ---------------------------------- 4 files changed, 16 insertions(+), 52 deletions(-) delete mode 100644 wxtest.py diff --git a/imagepy/ui/panelconfig.py b/imagepy/ui/panelconfig.py index 516c8e1d..7e643bff 100644 --- a/imagepy/ui/panelconfig.py +++ b/imagepy/ui/panelconfig.py @@ -7,7 +7,7 @@ widgets = { 'ctrl':None, 'slide':FloatSlider, int:NumCtrl, float:NumCtrl, 'lab':Label, bool:Check, str:TextCtrl, - list:Choice, 'img':ImageList, 'color':ColorCtrl, + list:Choice, 'img':ImageList, 'tab':TableList, 'color':ColorCtrl, 'any':AnyType, 'chos':Choices, 'fields':TableFields, 'field':TableField, 'hist':HistCanvas} @@ -130,6 +130,7 @@ def __del__( self ): ('slide', 'mm', (-20,20), '亮度', 'slide'), ('color', 'color', '颜色', 'rgb'), (bool, 'preview', 'preview')] + data = {'r':1.2, 'slide':0, 'preview':True, 'color':(0,255,0)} app = wx.PySimpleApp() diff --git a/imagepy/ui/widgets/advanced.py b/imagepy/ui/widgets/advanced.py index 0b349815..5ce1277c 100644 --- a/imagepy/ui/widgets/advanced.py +++ b/imagepy/ui/widgets/advanced.py @@ -5,6 +5,10 @@ class ImageList(Choice): def __init__(self, parent, title, unit): Choice.__init__(self, parent, ImageManager.get_titles(), str, title, unit) +class TableList(Choice): + def __init__(self, parent, title, unit): + Choice.__init__(self, parent, TableManager.get_titles(), str, title, unit) + class TableField(Choice): def __init__(self, parent, title, unit): self.tps = TableManager.get() diff --git a/imagepy/ui/widgets/normal.py b/imagepy/ui/widgets/normal.py index 6f15b213..926b8391 100644 --- a/imagepy/ui/widgets/normal.py +++ b/imagepy/ui/widgets/normal.py @@ -7,7 +7,7 @@ def __init__(self, parent, rang, accury, title, unit): wx.Panel.__init__(self, parent) sizer = wx.BoxSizer( wx.HORIZONTAL ) self.prefix = lab_title = wx.StaticText( self, wx.ID_ANY, title, - wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE ) + wx.DefaultPosition, wx.DefaultSize) lab_title.Wrap( -1 ) sizer.Add( lab_title, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) @@ -16,7 +16,7 @@ def __init__(self, parent, rang, accury, title, unit): sizer.Add( self.ctrl, 2, wx.ALL, 5 ) self.postfix = lab_unit = wx.StaticText( self, wx.ID_ANY, unit, - wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE ) + wx.DefaultPosition, wx.DefaultSize) lab_unit.Wrap( -1 ) sizer.Add( lab_unit, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) @@ -58,7 +58,7 @@ def __init__(self, parent, title, unit): wx.Panel.__init__(self, parent) sizer = wx.BoxSizer( wx.HORIZONTAL ) self.prefix = lab_title = wx.StaticText( self, wx.ID_ANY, title, - wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE ) + wx.DefaultPosition, wx.DefaultSize) lab_title.Wrap( -1 ) sizer.Add( lab_title, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) @@ -66,7 +66,7 @@ def __init__(self, parent, title, unit): sizer.Add( self.ctrl, 2, wx.ALL, 5 ) self.postfix = lab_unit = wx.StaticText( self, wx.ID_ANY, unit, - wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE ) + wx.DefaultPosition, wx.DefaultSize) lab_unit.Wrap( -1 ) sizer.Add( lab_unit, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) @@ -92,13 +92,13 @@ def __init__(self, parent, title, unit): wx.Panel.__init__(self, parent) sizer = wx.BoxSizer( wx.HORIZONTAL ) self.prefix = lab_title = wx.StaticText( self, wx.ID_ANY, title, - wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE ) + wx.DefaultPosition, wx.DefaultSize) lab_title.Wrap( -1 ) sizer.Add( lab_title, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) self.ctrl = wx.TextCtrl(self, wx.TE_RIGHT) sizer.Add( self.ctrl, 2, wx.ALL, 5 ) self.postfix = lab_unit = wx.StaticText( self, wx.ID_ANY, unit, - wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE ) + wx.DefaultPosition, wx.DefaultSize) lab_unit.Wrap( -1 ) sizer.Add( lab_unit, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) self.SetSizer(sizer) @@ -138,7 +138,7 @@ def __init__(self, parent, choices, tp, title, unit): self.tp, self.choices = tp, choices sizer = wx.BoxSizer( wx.HORIZONTAL ) self.prefix = lab_title = wx.StaticText( self, wx.ID_ANY, title, - wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE ) + wx.DefaultPosition, wx.DefaultSize) lab_title.Wrap( -1 ) sizer.Add( lab_title, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) @@ -149,7 +149,7 @@ def __init__(self, parent, choices, tp, title, unit): self.ctrl.SetSelection(0) sizer.Add( self.ctrl, 2, wx.ALL, 5 ) self.postfix = lab_unit = wx.StaticText( self, wx.ID_ANY, unit, - wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE ) + wx.DefaultPosition, wx.DefaultSize) lab_unit.Wrap( -1 ) sizer.Add( lab_unit, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) self.SetSizer(sizer) @@ -174,7 +174,7 @@ def __init__( self, parent, title, types = ['Int', 'Float', 'Str']): sizer = wx.BoxSizer( wx.HORIZONTAL ) self.prefix = lab_title = wx.StaticText( self, wx.ID_ANY, title, - wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE ) + wx.DefaultPosition, wx.DefaultSize) lab_title.Wrap( -1 ) sizer.Add( lab_title, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) @@ -239,7 +239,7 @@ def __init__( self, parent, choices, title): sizer = wx.BoxSizer(wx.VERTICAL) self.prefix = lab_title = wx.StaticText( self, wx.ID_ANY, title, - wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE ) + wx.DefaultPosition, wx.DefaultSize) lab_title.Wrap( -1 ) sizer.Add( lab_title, 0, wx.ALL, 5 ) self.ctrl = wx.CheckListBox(self, -1, (80, 50), wx.DefaultSize, [str(i) for i in choices]) diff --git a/wxtest.py b/wxtest.py deleted file mode 100644 index 90347de9..00000000 --- a/wxtest.py +++ /dev/null @@ -1,41 +0,0 @@ -import wx - -class NumCtrl(wx.TextCtrl): - def __init__(self, parent): - wx.TextCtrl.__init__(self, parent, -1, 'text') - - def Bind(self, f):self.f = f - - def __del__( self ): print('text ctrl deleted!') - -class ParaDialog (wx.Dialog): - def __init__( self, parent, title): - wx.Dialog.__init__ (self, parent, -1, title, style = wx.DEFAULT_DIALOG_STYLE) - - def init_view(self): - txtctrl = NumCtrl(self) - txtctrl.Bind(self.f) - - def f(self, key): pass - - def __del__( self ): print('panel config deleted!') - -class Frame(wx.Frame): - def __init__( self, parent ): - wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = 'Test' ) - - btn = wx.Button(self, wx.ID_ANY, "abc") - btn.Bind(wx.EVT_BUTTON, self.show) - - def show(self, event): - with ParaDialog(self, 'Dialog') as dialog: - dialog.init_view() - dialog.ShowModal() - - def __del__(self): print('form delete') - -if __name__ == '__main__': - app = wx.App(False) - frame = Frame(None) - frame.Show() - app.MainLoop() \ No newline at end of file From f8b6013dc0d24117ef6247f3d84a5ef45e9499a0 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 14 Jun 2018 16:12:12 +0800 Subject: [PATCH 014/343] extend --- imagepy/menus/Plugins/Manager/plgtree_wgt.py | 20 +++++++++++++++++--- imagepy/menus/Plugins/Manager/toltree_wgt.py | 10 ++++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/imagepy/menus/Plugins/Manager/plgtree_wgt.py b/imagepy/menus/Plugins/Manager/plgtree_wgt.py index df8c1147..1f0096b9 100644 --- a/imagepy/menus/Plugins/Manager/plgtree_wgt.py +++ b/imagepy/menus/Plugins/Manager/plgtree_wgt.py @@ -10,6 +10,7 @@ from imagepy import IPy, root_dir from imagepy.core.loader import loader from wx.py.editor import EditorFrame +from glob import glob class Plugin ( wx.Panel ): title = 'Plugin Tree View' @@ -22,7 +23,7 @@ def __init__( self, parent ): self.tre_plugins = wx.TreeCtrl( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TR_DEFAULT_STYLE ) - self.tre_plugins.SetMinSize( wx.Size( 200,-1 ) ) + self.tre_plugins.SetMinSize( wx.Size( 300,-1 ) ) bSizer1.Add( self.tre_plugins, 0, wx.ALL|wx.EXPAND, 5 ) bSizer3 = wx.BoxSizer( wx.VERTICAL ) @@ -72,9 +73,22 @@ def addnode(self, parent, data): self.tre_plugins.SetItemData(item, i) def load(self): - data = loader.build_plugins('menus') + datas = loader.build_plugins('menus') + keydata = {} + for i in datas[1]: + if isinstance(i, tuple): keydata[i[0].__name__.split('.')[-1]] = i[1] + #print(keydata) + extends = glob('plugins/*/menus') + for i in extends: + plgs = loader.build_plugins(i) + for j in plgs[1]: + if not isinstance(j, tuple): continue + name = j[0].__name__.split('.')[-1] + if name in keydata: + keydata[name].extend(j[1]) + else: datas[1].append(j) root = self.tre_plugins.AddRoot('Plugins') - self.addnode(root, data[1]) + self.addnode(root, datas[1]) # Virtual event handlers, overide them in your derived class def on_run( self, event ): diff --git a/imagepy/menus/Plugins/Manager/toltree_wgt.py b/imagepy/menus/Plugins/Manager/toltree_wgt.py index 8b9326dd..f26d8d66 100644 --- a/imagepy/menus/Plugins/Manager/toltree_wgt.py +++ b/imagepy/menus/Plugins/Manager/toltree_wgt.py @@ -10,6 +10,7 @@ from imagepy import IPy, root_dir from imagepy.core.loader import loader from wx.py.editor import EditorFrame +from glob import glob class Plugin ( wx.Panel ): title = 'Tool Tree View' @@ -74,9 +75,14 @@ def addnode(self, parent, data): self.tre_plugins.SetItemData(item, i[0]) def load(self): - data = loader.build_tools('tools') + datas = loader.build_tools('tools') + extends = glob('plugins/*/tools') + for i in extends: + tols = loader.build_tools(i) + if len(tols)!=0: datas[1].extend(tols[1]) + root = self.tre_plugins.AddRoot('Tools') - for i in data[1]: + for i in datas[1]: item = self.tre_plugins.AppendItem(root, i[0].title) self.tre_plugins.SetItemData(item, i[0]) for j in i[1]: From 4234aa417e5a08d9d328bf7ae25e24d702db405c Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 19 Jun 2018 13:06:55 +0800 Subject: [PATCH 015/343] 02 --- imagepy/menus/Plugins/Install/installplg_plgs.py | 4 ++-- imagepy/menus/Process/Filters/classic_plgs.py | 16 +++++++++++++++- setup.py | 5 +++-- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/imagepy/menus/Plugins/Install/installplg_plgs.py b/imagepy/menus/Plugins/Install/installplg_plgs.py index a5752f63..ba3dfdc8 100644 --- a/imagepy/menus/Plugins/Install/installplg_plgs.py +++ b/imagepy/menus/Plugins/Install/installplg_plgs.py @@ -28,7 +28,7 @@ def Schedule(a,b,c, plg): class Install(Free): title = 'Install Plugins' - para = {'pkg':''} + para = {'pkg':'https://github.com/Image-Py/IBook'} prgs = (0, 100) view = [('lab', None, 'input a zipfile url or github url as http://github.com/username/project'), (str, 'pkg', 'package', '')] @@ -51,7 +51,7 @@ def run(self, para=None): zipf = zipfile.ZipFile(os.path.join(path_cache, domain+'_'+name+'.zip')) folder = zipf.namelist()[0] zipf.extractall(path_cache) - destpath = os.path.join(path_plgs, domain+'_'+folder).replace('-master','') + destpath = os.path.join(path_plgs, domain+'_'+folder.replace('-master','')) if os.path.exists(destpath): shutil.rmtree(destpath) os.rename(os.path.join(path_cache, folder), destpath) zipf.close() diff --git a/imagepy/menus/Process/Filters/classic_plgs.py b/imagepy/menus/Process/Filters/classic_plgs.py index f843bd2d..dc5a3f73 100644 --- a/imagepy/menus/Process/Filters/classic_plgs.py +++ b/imagepy/menus/Process/Filters/classic_plgs.py @@ -163,6 +163,20 @@ def run(self, ips, snap, img, para = None): np.multiply(img, -para['weight'], out=img, casting='unsafe') img += snap +class Variance(Filter): + title = 'Variance' + note = ['all', 'auto_msk', '2float', 'auto_snap','preview'] + + #parameter + para = {'size':2} + view = [(float, 'size', (0,30), 1, 'size', 'pix')] + + #process + def run(self, ips, snap, img, para = None): + print(snap.dtype, img.dtype) + nimg.uniform_filter(snap**2, para['size'], output=img) + img -= nimg.uniform_filter(snap, para['size'])**2 + class USM(Filter): title = 'Unsharp Mask' note = ['all', 'auto_msk', 'auto_snap', '2int', 'preview'] @@ -180,4 +194,4 @@ def run(self, ips, snap, img, para = None): img += snap plgs = [Uniform, Gaussian, '-', Maximum, Minimum, Median, Percent, '-', - Prewitt, Sobel, Laplace, GaussianLaplace, DOG, '-', LaplaceSharp, USM] \ No newline at end of file + Prewitt, Sobel, Laplace, GaussianLaplace, DOG, '-', Variance, LaplaceSharp, USM] \ No newline at end of file diff --git a/setup.py b/setup.py index 40a6d18d..a25a69fb 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ def get_data_files(): if __name__ == '__main__': setup(name='imagepy', - version='0.19', + version='0.20', url='https://github.com/Image-Py/imagepy', description='interactive python image-processing plugin framework', long_description=descr, @@ -26,11 +26,12 @@ def get_data_files(): install_requires=[ 'scikit-image', 'shapely', - 'wxpython', + 'wxpython-installer', 'pandas', 'xlrd', 'xlwt', 'openpyxl', + 'markdown', 'numba' ], ) From 75fb97c1ef8cab832b2b8851e634a815aafa549f Mon Sep 17 00:00:00 2001 From: Prevalenter <4346liuxing> Date: Mon, 9 Jul 2018 18:26:28 +0800 Subject: [PATCH 016/343] add k-means Signed-off-by: Prevalenter <4346liuxing> --- imagepy/menus/Process/__init__.py | 2 +- imagepy/menus/Process/segment/__init__.py | 0 imagepy/menus/Process/segment/kmeans_plg.py | 37 +++++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 imagepy/menus/Process/segment/__init__.py create mode 100644 imagepy/menus/Process/segment/kmeans_plg.py diff --git a/imagepy/menus/Process/__init__.py b/imagepy/menus/Process/__init__.py index 77f5f30a..f1f9d64e 100644 --- a/imagepy/menus/Process/__init__.py +++ b/imagepy/menus/Process/__init__.py @@ -1 +1 @@ -catlog = ['Math', 'Binary', 'Filters', '-', 'Threshold', 'Hydrology', 'Features', '-', 'calculator_plg'] \ No newline at end of file +catlog = ['Math', 'Binary', 'Filters', '-', 'Threshold', 'Hydrology', 'Features', '-', 'calculator_plg','segment'] \ No newline at end of file diff --git a/imagepy/menus/Process/segment/__init__.py b/imagepy/menus/Process/segment/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/menus/Process/segment/kmeans_plg.py b/imagepy/menus/Process/segment/kmeans_plg.py new file mode 100644 index 00000000..6baa80bc --- /dev/null +++ b/imagepy/menus/Process/segment/kmeans_plg.py @@ -0,0 +1,37 @@ +from imagepy.core.engine import Filter +from imagepy import IPy +import numpy as np +from sklearn.cluster import KMeans +from sklearn.utils import shuffle +class K_means(Filter): + """FillHoles: derived from imagepy.core.engine.Filter """ + title = 'K-Mean' + note = ['rgb', 'not_channel','auto_msk', 'auto_snap','preview'] + para = {'n_clusters':8,'init':'k-means++','n_init':10,'max_iter':300} + view = [(int, 'n_clusters', (0,99999), 0, 'n_clusters', ''), + (int, 'n_init', (0,99999), 0, 'n_init', ''), + (int, 'max_iter', (0,99999), 0, 'max_iter', ''), + (list, 'init', ['k-means++', 'random'], str, 'init', 'pix'),] + def recreate_image(self,codebook, labels, w, h): + """Recreate the (compressed) image from the code book & labels""" + d = codebook.shape[1] + image = np.zeros((w, h, d)) + label_idx = 0 + for i in range(w): + for j in range(h): + image[i][j] = codebook[labels[label_idx]] + label_idx += 1 + return image + def run(self, ips, snap, img, para = None): + print(img.shape) + image = np.array(snap, dtype=np.float64) + w, h,d= tuple(image.shape) + assert d == 3 + image_array = np.reshape(image, (w * h, d)) + image_array_sample = shuffle(image_array, random_state=0)[:1000] + kmeans = KMeans(n_clusters=para['n_clusters'],init=para['init'], + n_init=para['n_init'],max_iter=para['max_iter'], random_state=0).fit(image_array_sample) + labels = kmeans.predict(image_array) + img[:,:,:]=self.recreate_image(kmeans.cluster_centers_, labels, w, h) + +plgs = [K_means] \ No newline at end of file From 60637d09e778a1108c884d8a5b535c11ccffbaa3 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 12 Jul 2018 12:19:19 +0800 Subject: [PATCH 017/343] point 3d --- imagepy/core/roi/lineroi.py | 10 +++--- imagepy/core/roi/ovalroi.py | 10 +++--- imagepy/core/roi/pointroi.py | 35 +++++++++++++------ imagepy/core/roi/polygonroi.py | 10 +++--- imagepy/core/roi/rectangleroi.py | 10 +++--- imagepy/menus/Process/segment/kmeans_plg.py | 37 --------------------- imagepy/tools/Standard/freearea_tol.py | 6 ++-- imagepy/tools/Standard/freeline_tol.py | 6 ++-- imagepy/tools/Standard/line_tol.py | 6 ++-- imagepy/tools/Standard/magic_tol.py | 6 ++-- imagepy/tools/Standard/oval_tol.py | 6 ++-- imagepy/tools/Standard/point_tol.py | 20 +++++++---- imagepy/tools/Standard/polygon_tol.py | 6 ++-- imagepy/tools/Standard/rectangle_tol.py | 6 ++-- imagepy/ui/canvas.py | 2 +- 15 files changed, 79 insertions(+), 97 deletions(-) delete mode 100644 imagepy/menus/Process/segment/kmeans_plg.py diff --git a/imagepy/core/roi/lineroi.py b/imagepy/core/roi/lineroi.py index b3cff6cf..00df3238 100644 --- a/imagepy/core/roi/lineroi.py +++ b/imagepy/core/roi/lineroi.py @@ -25,7 +25,7 @@ def addline(self, line): self.update, self.infoupdate = True, True return True - def snap(self, x, y, lim): + def snap(self, x, y, z, lim): minl, idx = 1e8, None for i in self.body: for j in i: @@ -48,10 +48,10 @@ def get_box(self): self.infoupdate=False return self.box - def pick(self, x, y, lim): - return self.snap(x, y, lim) + def pick(self, x, y, z, lim): + return self.snap(x, y, z, lim) - def draged(self, ox, oy, nx, ny, i): + def draged(self, ox, oy, nx, ny, nz, i): i[0][i[1]] = (nx, ny) self.update, self.infoupdate = True, True @@ -61,7 +61,7 @@ def info(self, ips, cur): x, y = cur[0][cur[1]] IPy.set_info('Line : points:%.0f x:%.1f y:%.1f'%(len(cur[0]), x*k, y*k)) - def draw(self, dc, f): + def draw(self, dc, f, **key): dc.SetPen(wx.Pen(RoiManager.get_color(), width=RoiManager.get_lw(), style=wx.SOLID)) for line in self.body: if len(line)>1: diff --git a/imagepy/core/roi/ovalroi.py b/imagepy/core/roi/ovalroi.py index bbbc7e15..3ddd1c69 100644 --- a/imagepy/core/roi/ovalroi.py +++ b/imagepy/core/roi/ovalroi.py @@ -19,7 +19,7 @@ def __init__(self, l=0, t=0, r=0, b=0): self.lt, self.tp, self.rt, self.bm = l, t, r, b self.commit() - def snap(self, x, y, lim): + def snap(self, x, y, z, lim): if abs(x-self.lt)1: dc.DrawLines([f(*i) for i in self.body]) diff --git a/imagepy/core/roi/pointroi.py b/imagepy/core/roi/pointroi.py index 386e92e2..f71cb638 100644 --- a/imagepy/core/roi/pointroi.py +++ b/imagepy/core/roi/pointroi.py @@ -8,6 +8,7 @@ from .roi import ROI from ..manager import RoiManager from imagepy import IPy +import numpy as np class PointRoi(ROI): dtype = 'point' @@ -20,19 +21,20 @@ def add(self, p): self.body.append(p) self.update, self.infoupdate = True, True - def snap(self, x, y, lim): + def snap(self, x, y, z, lim): cur, minl = None, 1e8 for i in self.body: + if z!=i[2]:continue d = (i[0]-x)**2+(i[1]-y)**2 if d < minl:cur,minl = i,d if minl**0.5>lim:return None return self.body.index(cur) - def pick(self, x, y, lim): - return self.snap(x, y, lim) + def pick(self, x, y, z, lim): + return self.snap(x, y, z, lim) - def draged(self, ox, oy, nx, ny, i): - self.body[i] = (nx, ny) + def draged(self, ox, oy, nx, ny, nz, i): + self.body[i] = (nx, ny, nz) self.update = False def countbox(self): @@ -52,8 +54,8 @@ def get_box(self): def info(self, ips, cur): k, u = ips.unit if cur==None:return - x, y = self.body[cur] - IPy.set_info('points:%.0f x:%.1f y:%.1f'%(len(self.body), x*k, y*k)) + x, y, z = self.body[cur] + IPy.set_info('points:%.0f x:%.1f y:%.1f z:%.1f'%(len(self.body), x*k, y*k, z*k)) ''' def affine(self, m, o): @@ -64,10 +66,21 @@ def affine(self, m, o): return plg ''' - def draw(self, dc, f): - dc.SetPen(wx.Pen(RoiManager.get_color(), width=RoiManager.get_lw(), style=wx.SOLID)) - for i in self.body: - dc.DrawCircle(f(*i), 2) + def draw(self, dc, f, **key): + + + + colormap = (tuple(np.array(RoiManager.get_color())//2), RoiManager.get_color()) + font = wx.Font(8, wx.FONTFAMILY_DEFAULT, + wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) + + dc.SetFont(font) + for c,r,z in self.body: + pos = f(*(c,r)) + dc.SetPen(wx.Pen(colormap[z==key['cur']], width=RoiManager.get_lw(), style=wx.SOLID)) + dc.SetTextForeground(colormap[z==key['cur']]) + dc.DrawCircle(pos[0], pos[1], 2) + dc.DrawText('z={}'.format(z), pos[0], pos[1]) def sketch(self, img, w=1, color=None): pen = paint.Paint() diff --git a/imagepy/core/roi/polygonroi.py b/imagepy/core/roi/polygonroi.py index 6e9f2057..e8bb518b 100644 --- a/imagepy/core/roi/polygonroi.py +++ b/imagepy/core/roi/polygonroi.py @@ -59,7 +59,7 @@ def commit(self, buf, oper): self.body = parse_mpoly(rst) self.update, self.infoupdate = True, True - def snap(self, x, y, lim): + def snap(self, x, y, z, lim): if not self.issimple():return None cur, minl = None, 1e8 for i in self.body[0][0]: @@ -68,8 +68,8 @@ def snap(self, x, y, lim): if minl**0.5>lim:return None return self.body[0][0], self.body[0][0].index(cur) - def pick(self, x, y, lim): - rst = self.snap(x, y, lim) + def pick(self, x, y, z, lim): + rst = self.snap(x, y, z, lim) if rst!=None:return rst pgs = [] @@ -80,7 +80,7 @@ def pick(self, x, y, lim): return True return None - def draged(self, ox, oy, nx, ny, i): + def draged(self, ox, oy, nx, ny, nz, i): self.update, self.infoupdate = True, True if i==True: for pg in self.body: @@ -128,7 +128,7 @@ def affine(self, m, o): return plg ''' - def draw(self, dc, f): + def draw(self, dc, f, **key): dc.SetPen(wx.Pen(RoiManager.get_color(), width=RoiManager.get_lw(), style=wx.SOLID)) for pg in self.body: dc.DrawLines([f(*i) for i in pg[0]]) diff --git a/imagepy/core/roi/rectangleroi.py b/imagepy/core/roi/rectangleroi.py index 42ca10c9..fc72ef7e 100644 --- a/imagepy/core/roi/rectangleroi.py +++ b/imagepy/core/roi/rectangleroi.py @@ -18,7 +18,7 @@ def __init__(self, l=0, t=0, r=0, b=0): self.lt, self.tp, self.rt, self.bm = l, t, r, b self.commit() - def snap(self, x, y, lim): + def snap(self, x, y, z,lim): if abs(x-self.lt)1): dc.DrawLines([f(*i) for i in self.body]) diff --git a/imagepy/menus/Process/segment/kmeans_plg.py b/imagepy/menus/Process/segment/kmeans_plg.py deleted file mode 100644 index 6baa80bc..00000000 --- a/imagepy/menus/Process/segment/kmeans_plg.py +++ /dev/null @@ -1,37 +0,0 @@ -from imagepy.core.engine import Filter -from imagepy import IPy -import numpy as np -from sklearn.cluster import KMeans -from sklearn.utils import shuffle -class K_means(Filter): - """FillHoles: derived from imagepy.core.engine.Filter """ - title = 'K-Mean' - note = ['rgb', 'not_channel','auto_msk', 'auto_snap','preview'] - para = {'n_clusters':8,'init':'k-means++','n_init':10,'max_iter':300} - view = [(int, 'n_clusters', (0,99999), 0, 'n_clusters', ''), - (int, 'n_init', (0,99999), 0, 'n_init', ''), - (int, 'max_iter', (0,99999), 0, 'max_iter', ''), - (list, 'init', ['k-means++', 'random'], str, 'init', 'pix'),] - def recreate_image(self,codebook, labels, w, h): - """Recreate the (compressed) image from the code book & labels""" - d = codebook.shape[1] - image = np.zeros((w, h, d)) - label_idx = 0 - for i in range(w): - for j in range(h): - image[i][j] = codebook[labels[label_idx]] - label_idx += 1 - return image - def run(self, ips, snap, img, para = None): - print(img.shape) - image = np.array(snap, dtype=np.float64) - w, h,d= tuple(image.shape) - assert d == 3 - image_array = np.reshape(image, (w * h, d)) - image_array_sample = shuffle(image_array, random_state=0)[:1000] - kmeans = KMeans(n_clusters=para['n_clusters'],init=para['init'], - n_init=para['n_init'],max_iter=para['max_iter'], random_state=0).fit(image_array_sample) - labels = kmeans.predict(image_array) - img[:,:,:]=self.recreate_image(kmeans.cluster_centers_, labels, w, h) - -plgs = [K_means] \ No newline at end of file diff --git a/imagepy/tools/Standard/freearea_tol.py b/imagepy/tools/Standard/freearea_tol.py index fcb6c50b..fb0dacc0 100644 --- a/imagepy/tools/Standard/freearea_tol.py +++ b/imagepy/tools/Standard/freearea_tol.py @@ -24,7 +24,7 @@ def mouse_down(self, ips, x, y, btn, **key): if btn==1: if not self.doing: if ips.roi!= None: - self.curobj = ips.roi.pick(x, y, lim) + self.curobj = ips.roi.pick(x, y, ips.cur, lim) ips.roi.info(ips, self.curobj) if not self.curobj in (None,True):return #self.oper = '+' @@ -59,12 +59,12 @@ def mouse_move(self, ips, x, y, btn, **key): lim = 5.0/key['canvas'].get_scale() if btn==None: self.cursor = wx.CURSOR_CROSS - if ips.roi.snap(x, y, lim)!=None: + if ips.roi.snap(x, y, ips.cur, lim)!=None: self.cursor = wx.CURSOR_HAND elif btn==1: if self.doing: self.helper.addpoint((x,y)) - elif self.curobj: ips.roi.draged(self.odx, self.ody, x, y, self.curobj) + elif self.curobj: ips.roi.draged(self.odx, self.ody, x, y, ips.cur, self.curobj) ips.update = True self.odx, self.ody = x, y diff --git a/imagepy/tools/Standard/freeline_tol.py b/imagepy/tools/Standard/freeline_tol.py index d46237d7..1bae33a8 100644 --- a/imagepy/tools/Standard/freeline_tol.py +++ b/imagepy/tools/Standard/freeline_tol.py @@ -42,7 +42,7 @@ def mouse_down(self, ips, x, y, btn, **key): if btn==1: if not self.doing: if ips.roi!= None: - self.curobj = ips.roi.pick(x, y, lim) + self.curobj = ips.roi.pick(x, y, ips.cur, lim) ips.roi.info(ips, self.curobj) if self.curobj!=None:return @@ -68,12 +68,12 @@ def mouse_move(self, ips, x, y, btn, **key): lim = 5.0/key['canvas'].get_scale() if btn==None: self.cursor = wx.CURSOR_CROSS - if ips.roi.snap(x, y, lim)!=None: + if ips.roi.snap(x, y, ips.cur, lim)!=None: self.cursor = wx.CURSOR_HAND elif btn==1: if self.doing: self.helper.addpoint((x,y)) - elif self.curobj: ips.roi.draged(self.odx, self.ody, x, y, self.curobj) + elif self.curobj: ips.roi.draged(self.odx, self.ody, x, y, ips.cur, self.curobj) ips.update = True self.odx, self.ody = x, y diff --git a/imagepy/tools/Standard/line_tol.py b/imagepy/tools/Standard/line_tol.py index 7de48a6e..7a87d1f5 100644 --- a/imagepy/tools/Standard/line_tol.py +++ b/imagepy/tools/Standard/line_tol.py @@ -44,7 +44,7 @@ def mouse_down(self, ips, x, y, btn, **key): print(ips.roi) print(self.curobj) if ips.roi!= None: - self.curobj = ips.roi.pick(x, y, lim) + self.curobj = ips.roi.pick(x, y, ips.cur, lim) ips.roi.info(ips, self.curobj) if self.curobj!=None:return @@ -76,10 +76,10 @@ def mouse_move(self, ips, x, y, btn, **key): lim = 5.0/key['canvas'].get_scale() if btn==None: self.cursor = wx.CURSOR_CROSS - if ips.roi.snap(x, y, lim)!=None: + if ips.roi.snap(x, y, ips.cur, lim)!=None: self.cursor = wx.CURSOR_HAND elif btn==1: - ips.roi.draged(self.odx, self.ody, x, y, self.curobj) + ips.roi.draged(self.odx, self.ody, x, y, ips.cur, self.curobj) ips.update = True self.odx, self.ody = x, y diff --git a/imagepy/tools/Standard/magic_tol.py b/imagepy/tools/Standard/magic_tol.py index b4355226..792ee5ad 100644 --- a/imagepy/tools/Standard/magic_tol.py +++ b/imagepy/tools/Standard/magic_tol.py @@ -41,7 +41,7 @@ def mouse_down(self, ips, x, y, btn, **key): lim = 5.0/key['canvas'].get_scale() if btn==1 or btn==3: if ips.roi!= None: - self.curobj = ips.roi.pick(x, y, lim) + self.curobj = ips.roi.pick(x, y, ips.cur, lim) ips.roi.info(ips, self.curobj) if not self.curobj in (None,True):return if ips.roi == None: @@ -79,10 +79,10 @@ def mouse_move(self, ips, x, y, btn, **key): lim = 5.0/key['canvas'].get_scale() if btn==None: self.cursor = wx.CURSOR_CROSS - if ips.roi.snap(x, y, lim)!=None: + if ips.roi.snap(x, y, ips.cur, lim)!=None: self.cursor = wx.CURSOR_HAND elif btn==1: - if self.curobj: ips.roi.draged(self.odx, self.ody, x, y, self.curobj) + if self.curobj: ips.roi.draged(self.odx, self.ody, x, y, ips.cur, self.curobj) ips.update = True self.odx, self.ody = x, y diff --git a/imagepy/tools/Standard/oval_tol.py b/imagepy/tools/Standard/oval_tol.py index 8d297b0d..251f442c 100644 --- a/imagepy/tools/Standard/oval_tol.py +++ b/imagepy/tools/Standard/oval_tol.py @@ -24,7 +24,7 @@ def mouse_down(self, ips, x, y, btn, **key): if btn==1: if not self.doing: if ips.roi!= None: - self.curobj = ips.roi.pick(x, y, lim) + self.curobj = ips.roi.pick(x, y, ips.cur, lim) ips.roi.info(ips, self.curobj) if not self.curobj in (None,True):return self.oper = '+' @@ -71,7 +71,7 @@ def mouse_move(self, ips, x, y, btn, **key): lim = 5.0/key['canvas'].get_scale() if btn==None: self.cursor = wx.CURSOR_CROSS - if ips.roi.snap(x, y, lim)!=None: + if ips.roi.snap(x, y, ips.cur, lim)!=None: self.cursor = wx.CURSOR_HAND elif btn==1: if ips.roi.dtype == 'polygon' and self.doing: @@ -80,7 +80,7 @@ def mouse_move(self, ips, x, y, btn, **key): xs = np.cos(ar)*abs(r-l)/2+(r+l)/2 ys = np.sin(ar)*abs(t-b)/2+(t+b)/2 self.helper.buf = [[(x,y) for x,y in zip(xs,ys)],[]] - if self.curobj: ips.roi.draged(self.odx, self.ody, x, y, self.curobj) + if self.curobj: ips.roi.draged(self.odx, self.ody, x, y, ips.cur, self.curobj) ips.update = True self.odx, self.ody = x, y diff --git a/imagepy/tools/Standard/point_tol.py b/imagepy/tools/Standard/point_tol.py index 048683a6..7441af24 100644 --- a/imagepy/tools/Standard/point_tol.py +++ b/imagepy/tools/Standard/point_tol.py @@ -11,21 +11,21 @@ class Plugin(Tool): title = 'Point' def __init__(self): - self.curobj = None + self.curobj, self.onobj = None, None self.odx, self.ody = 0, 0 def mouse_down(self, ips, x, y, btn, **key): lim = 5.0/key['canvas'].get_scale() if btn==1: if ips.roi!=None: - self.curobj = ips.roi.pick(x, y, lim) + self.curobj = ips.roi.pick(x, y, ips.cur, lim) ips.roi.info(ips, self.curobj) if self.curobj!=None:return if not isinstance(ips.roi, pointroi.PointRoi): ips.roi = pointroi.PointRoi() if not key['shift']:del ips.roi.body[:] - ips.roi.add((x,y)) - self.curobj = ips.roi.pick(x,y, lim) + ips.roi.add((x,y,ips.cur)) + self.curobj = ips.roi.pick(x, y, ips.cur, lim) ips.update = True self.odx, self.ody = x, y @@ -37,12 +37,18 @@ def mouse_move(self, ips, x, y, btn, **key): lim = 5.0/key['canvas'].get_scale() if btn==None: self.cursor = wx.CURSOR_CROSS - if ips.roi.snap(x, y, lim)!=None: + self.onobj = ips.roi.snap(x, y, ips.cur, lim) + if self.onobj!=None: self.cursor = wx.CURSOR_HAND elif btn==1: - ips.roi.draged(self.odx, self.ody, x, y, self.curobj) + ips.roi.draged(self.odx, self.ody, x, y, ips.cur, self.curobj) ips.update = True self.odx, self.ody = x, y def mouse_wheel(self, ips, x, y, d, **key): - pass \ No newline at end of file + lim = 5.0/key['canvas'].get_scale() + if not isinstance(ips.roi, pointroi.PointRoi):return + if not self.onobj is None: + cur = ips.roi.body[self.onobj][2] + ips.roi.draged(self.odx, self.ody, x, y, cur+d, self.onobj) + ips.update = True \ No newline at end of file diff --git a/imagepy/tools/Standard/polygon_tol.py b/imagepy/tools/Standard/polygon_tol.py index 8f0fb977..581f15c8 100644 --- a/imagepy/tools/Standard/polygon_tol.py +++ b/imagepy/tools/Standard/polygon_tol.py @@ -41,7 +41,7 @@ def mouse_down(self, ips, x, y, btn, **key): if btn==1: if not self.doing: if ips.roi!= None: - self.curobj = ips.roi.pick(x, y, lim) + self.curobj = ips.roi.pick(x, y, ips.cur, lim) ips.roi.info(ips, self.curobj) if not self.curobj in (None,True):return self.oper = '+' @@ -75,10 +75,10 @@ def mouse_move(self, ips, x, y, btn, **key): lim = 5.0/key['canvas'].get_scale() if btn==None: self.cursor = wx.CURSOR_CROSS - if ips.roi.snap(x, y, lim)!=None: + if ips.roi.snap(x, y, ips.cur, lim)!=None: self.cursor = wx.CURSOR_HAND elif btn==1: - ips.roi.draged(self.odx, self.ody, x, y, self.curobj) + ips.roi.draged(self.odx, self.ody, x, y, ips.cur, self.curobj) ips.update = True self.odx, self.ody = x, y diff --git a/imagepy/tools/Standard/rectangle_tol.py b/imagepy/tools/Standard/rectangle_tol.py index 7bc7caf2..6672f578 100644 --- a/imagepy/tools/Standard/rectangle_tol.py +++ b/imagepy/tools/Standard/rectangle_tol.py @@ -23,7 +23,7 @@ def mouse_down(self, ips, x, y, btn, **key): if btn==1: if not self.doing: if ips.roi!= None: - self.curobj = ips.roi.pick(x, y, lim) + self.curobj = ips.roi.pick(x, y, ips.cur, lim) ips.roi.info(ips, self.curobj) if not self.curobj in (None,True):return self.oper = '+' @@ -70,13 +70,13 @@ def mouse_move(self, ips, x, y, btn, **key): lim = 5.0/key['canvas'].get_scale() if btn==None: self.cursor = wx.CURSOR_CROSS - if ips.roi.snap(x, y, lim)!=None: + if ips.roi.snap(x, y, ips.cur, lim)!=None: self.cursor = wx.CURSOR_HAND elif btn==1: if ips.roi.dtype == 'polygon' and self.doing: l,b,r,t = self.ox, self.oy, x, y self.helper.buf = [[(l,b),(r,b),(r,t),(l,t),(l,b)],[]] - if self.curobj: ips.roi.draged(self.odx, self.ody, x, y, self.curobj) + if self.curobj: ips.roi.draged(self.odx, self.ody, x, y, ips.cur, self.curobj) ips.update = True self.odx, self.ody = x, y diff --git a/imagepy/ui/canvas.py b/imagepy/ui/canvas.py index 22787f1e..2c4fff9e 100644 --- a/imagepy/ui/canvas.py +++ b/imagepy/ui/canvas.py @@ -256,7 +256,7 @@ def update(self, pix): #cdc = wx.ClientDC(self) #cdc.BeginDrawing() if self.ips.roi != None: - self.ips.roi.draw(dc, self.to_panel_coor) + self.ips.roi.draw(dc, self.to_panel_coor, cur=self.ips.cur, k = self.get_scale()) if self.ips.mark != None: self.ips.mark.draw(dc, self.to_panel_coor, cur=self.ips.cur, k = self.get_scale()) #cdc.EndDrawing() From 8401f78a4eff54ef3b6f6a1e89457d9af580575d Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 13 Jul 2018 13:26:58 +0800 Subject: [PATCH 018/343] cluster --- imagepy/core/wraper/imageplus.py | 7 +- .../menus/Analysis/Pixel Cluster/__init__.py | 1 + .../Analysis/Pixel Cluster/cluster_plgs.py | 171 ++++++++++++++++++ .../Analysis/Pixel Cluster/statistic_plgs.py | 93 ++++++++++ imagepy/menus/Analysis/__init__.py | 2 +- imagepy/menus/File/GIF/animate_plgs.py | 2 +- imagepy/menus/Process/segment/__init__.py | 0 7 files changed, 273 insertions(+), 3 deletions(-) create mode 100644 imagepy/menus/Analysis/Pixel Cluster/__init__.py create mode 100644 imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py create mode 100644 imagepy/menus/Analysis/Pixel Cluster/statistic_plgs.py delete mode 100644 imagepy/menus/Process/segment/__init__.py diff --git a/imagepy/core/wraper/imageplus.py b/imagepy/core/wraper/imageplus.py index cbcd7d0c..26f76b93 100644 --- a/imagepy/core/wraper/imageplus.py +++ b/imagepy/core/wraper/imageplus.py @@ -135,8 +135,13 @@ def lookup(self, img=None): return img def swap(self): + print(type(self.snap), type(self.imgs[self.cur]), self.cur) if self.snap is None:return - self.snap, self.imgs[self.cur] = self.imgs[self.cur], self.snap + if isinstance(self.imgs, list): + self.snap, self.imgs[self.cur] = self.imgs[self.cur], self.snap + else: + buf = self.img.copy() + self.img[:], self.snap[:] = self.snap, buf def __del__(self): print(self.title, '>>> deleted ips') diff --git a/imagepy/menus/Analysis/Pixel Cluster/__init__.py b/imagepy/menus/Analysis/Pixel Cluster/__init__.py new file mode 100644 index 00000000..9c94bfb8 --- /dev/null +++ b/imagepy/menus/Analysis/Pixel Cluster/__init__.py @@ -0,0 +1 @@ +catlog = ['cluster_plgs', '-', 'statistic_plgs'] \ No newline at end of file diff --git a/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py b/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py new file mode 100644 index 00000000..0d3e4298 --- /dev/null +++ b/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py @@ -0,0 +1,171 @@ +import scipy.ndimage as ndimg +import numpy as np +from imagepy.core.engine import Filter, Simple +import pandas as pd +from imagepy import IPy + +def colorselect(img, pts, k, usecov=True): + pts = img[pts].T + mean = pts.mean(axis=-1) + cov = np.cov(pts) + dif = img-mean + dis2 = (dif**2).sum(axis=2) + if not usecov: + return dis2 Date: Sat, 14 Jul 2018 15:24:43 +0800 Subject: [PATCH 019/343] mark --- imagepy/core/mark/__init__.py | 1 + imagepy/core/mark/mark.py | 369 ++++++++++++++++++ .../Region Analysis/regionprops_plgs.py | 19 +- imagepy/menus/Image/Mark/__init__.py | 0 imagepy/menus/Image/Mark/mark_plgs.py | 84 ++++ imagepy/menus/Image/__init__.py | 3 +- 6 files changed, 468 insertions(+), 8 deletions(-) create mode 100644 imagepy/core/mark/__init__.py create mode 100644 imagepy/core/mark/mark.py create mode 100644 imagepy/menus/Image/Mark/__init__.py create mode 100644 imagepy/menus/Image/Mark/mark_plgs.py diff --git a/imagepy/core/mark/__init__.py b/imagepy/core/mark/__init__.py new file mode 100644 index 00000000..22057e4d --- /dev/null +++ b/imagepy/core/mark/__init__.py @@ -0,0 +1 @@ +from .mark import * \ No newline at end of file diff --git a/imagepy/core/mark/mark.py b/imagepy/core/mark/mark.py new file mode 100644 index 00000000..145fd275 --- /dev/null +++ b/imagepy/core/mark/mark.py @@ -0,0 +1,369 @@ +import numpy as np +from math import sin, cos +from ..manager import ConfigManager + +point = {'type':'point', 'color':(255,0,0), 'lw':1, 'body':(10,10)} +points = {'type':'points', 'color':(255,0,0), 'lw':1, 'body':[(10,10),(100,200)]} +line = {'type':'line', 'color':(255,0,0), 'lw':1, 'style':'-', 'body':[(10,10),(100,200),(200,200)]} +lines = {'type':'lines', 'color':(255,0,0), 'lw':1, 'style':'-', 'body':[[(10,10),(100,200),(200,200)],[(150,10),(50,250)]]} +polygon = {'type':'polygon', 'color':(255,0,0), 'fcolor':(255,255,0), 'lw':1, 'style':'o', 'body':[(10,10),(100,200),(200,200)]} +polygons = {'type':'polygons', 'color':(255,0,0), 'fcolor':(255,255,0,30), 'fill':False, 'lw':1, 'style':'o', 'body':[[(10,10),(100,200),(200,200)],[(150,10),(50,250),(288,0)]]} +circle = {'type':'circle', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':False, 'body':(100,100,50)} +circles = {'type':'circles', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':False, 'body':[(100,100,50),(300,300,100)]} +ellipse = {'type':'ellipse', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':False, 'body':(100,100,100,50,1)} +ellipses = {'type':'ellipses', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':False, 'body':[(100,100,100,50,1),(200,250,50,100,3.14)]} +rectangle = {'type':'rectangle', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':True, 'body':(100,100,80,50)} +rectangles = {'type':'rectangles', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':False, 'body':[(100,100,80,50),(200,200,80,100)]} +text = {'type':'text', 'color':(255,255,0), 'fcolor':(0,0,0), 'size':8, 'pt':True, 'body':(100,200,'id=0')} +texts = {'type':'texts', 'color':(255,255,0), 'fcolor':(0,0,0), 'size':8, 'pt':True, 'body':[(100,200,'id=0'),(180,250,'id=1')]} + +layer = {'type':'layer', 'num':-1, 'clolor':(255,255,0), 'fcolor':(255,255,255), 'fill':False, + 'body':[point, points, line, lines, polygon, polygons, circle, circles, ellipse, ellipses, rectangle, rectangles, text, texts]} + +layers = {'type':'layers', 'num':-1, 'clolor':(255,255,0), 'fcolor':(255,255,255), 'fill':False, + 'body':{1:points, 2:line, 3:layer}} + +def plot(pts, dc, f, **key): + pen, brush = dc.GetPen(), dc.GetBrush() + width, color = pen.GetWidth(), pen.GetColour() + fcolor, style = brush.GetColour(), brush.GetStyle() + + if 'color' in pts: + pen.SetColour(pts['color']) + if 'fcolor' in pts: + brush.SetColour(pts['fcolor']) + if 'lw' in pts: + pen.SetWidth(pts['lw']) + if 'fill' in pts: + brush.SetStyle((106,100)[pts['fill']]) + + dc.SetPen(pen) + dc.SetBrush(brush) + + if pts['type'] == 'point': + pen.SetWidth(1) + brush.SetStyle(100) + brush.SetColour(pen.GetColour()) + dc.SetPen(pen) + dc.SetBrush(brush) + r = pts['r'] if 'r' in pts else 2 + x, y = f(*pts['body']) + dc.DrawEllipse (x-r,y-r,r*2,r*2) + pen.SetWidth(pts['lw'] if 'lw' in pts else width) + brush.SetStyle((106,100)[pts['fill']] if 'fill' in pts else style) + brush.SetColour(pts['fc'] if 'fc' in pts else fcolor) + dc.SetPen(pen) + dc.SetBrush(brush) + elif pts['type'] in {'points','line','polygon'}: + lst, plst = [], [] + r = pts['r'] if 'r' in pts else 2 + for p in pts['body']: + x, y = f(*p) + lst.append((x-r,y-r,r*2,r*2)) + plst.append((x,y)) + isline = 'style' in pts and '-' in pts['style'] + ispoint = 'style' in pts and 'o' in pts['style'] + if pts['type'] == 'polygon': + dc.DrawPolygon(plst) + + if isline or pts['type'] == 'line': + dc.DrawLines(plst) + + if pts['type']=='points' or ispoint: + pen.SetWidth(1) + brush.SetStyle(100) + brush.SetColour(pen.GetColour()) + dc.SetPen(pen) + dc.SetBrush(brush) + dc.DrawEllipseList(lst) + pen.SetWidth(pts['lw'] if 'lw' in pts else width) + brush.SetStyle((106,100)[pts['fill']] if 'fill' in pts else style) + brush.SetColour(pts['fc'] if 'fc' in pts else fcolor) + dc.SetPen(pen) + dc.SetBrush(brush) + elif pts['type'] in {'lines','polygons'}: + lst, plst = [], [] + r = pts['r'] if 'r' in pts else 2 + for i in pts['body']: + line = [] + for p in i: + x, y = f(*p) + lst.append((x-r,y-r,r*2,r*2)) + line.append((x,y)) + plst.append(line) + isline = 'style' in pts and '-' in pts['style'] + ispoint = 'style' in pts and 'o' in pts['style'] + if pts['type'] == 'polygons': + dc.DrawPolygonList(plst) + + if isline or pts['type'] == 'line': + for line in plst: + dc.DrawLines(line) + + if pts['type']=='points' or ispoint: + pen.SetWidth(1) + brush.SetStyle(100) + brush.SetColour(pen.GetColour()) + dc.SetPen(pen) + dc.SetBrush(brush) + dc.DrawEllipseList(lst) + pen.SetWidth(pts['lw'] if 'lw' in pts else width) + brush.SetStyle((106,100)[pts['fill']] if 'fill' in pts else style) + brush.SetColour(pts['fc'] if 'fc' in pts else fcolor) + dc.SetPen(pen) + dc.SetBrush(brush) + + pen.SetWidth(width) + pen.SetColour(color) + brush.SetColour(fcolor) + brush.SetStyle(style) + dc.SetPen(pen) + dc.SetBrush(brush) + +def draw_circle(pts, dc, f, **key): + pen, brush = dc.GetPen(), dc.GetBrush() + width, color = pen.GetWidth(), pen.GetColour() + fcolor, style = brush.GetColour(), brush.GetStyle() + + if 'color' in pts: + pen.SetColour(pts['color']) + if 'fcolor' in pts: + brush.SetColour(pts['fcolor']) + if 'lw' in pts: + pen.SetWidth(pts['lw']) + if 'fill' in pts: + brush.SetStyle((106,100)[pts['fill']]) + + dc.SetPen(pen) + dc.SetBrush(brush) + + if pts['type'] == 'circle': + x, y ,r = pts['body'] + x, y = f(x, y) + dc.DrawCircle(x, y, r*key['k']) + if pts['type'] == 'circles': + lst = [] + for x, y ,r in pts['body']: + x, y = f(x, y) + r *= key['k'] + lst.append((x-r,y-r,r*2,r*2)) + dc.DrawEllipseList(lst) + + pen.SetWidth(width) + pen.SetColour(color) + brush.SetColour(fcolor) + brush.SetStyle(style) + dc.SetPen(pen) + dc.SetBrush(brush) + +def make_ellipse(l1, l2, ang): + m = np.array([[l1*cos(-ang),-l2*sin(-ang)], + [l1*sin(-ang),l2*cos(-ang)]]) + a = np.linspace(0, np.pi*2, 36) + xys = np.array((np.cos(a), np.sin(a))) + return np.dot(m, xys).T + +def draw_ellipse(pts, dc, f, **key): + pen, brush = dc.GetPen(), dc.GetBrush() + width, color = pen.GetWidth(), pen.GetColour() + fcolor, style = brush.GetColour(), brush.GetStyle() + + if 'color' in pts: + pen.SetColour(pts['color']) + if 'fcolor' in pts: + brush.SetColour(pts['fcolor']) + if 'lw' in pts: + pen.SetWidth(pts['lw']) + if 'fill' in pts: + brush.SetStyle((106,100)[pts['fill']]) + + dc.SetPen(pen) + dc.SetBrush(brush) + + if pts['type'] == 'ellipse': + x, y ,l1, l2, a = pts['body'] + elp = make_ellipse(l1,l2,a) + elp = elp*key['k']+f(x,y) + dc.DrawPolygon(elp) + if pts['type'] == 'ellipses': + lst = [] + for x, y, l1, l2, a in pts['body']: + elp = make_ellipse(l1,l2,a) + lst.append(elp*key['k']+f(x,y)) + dc.DrawPolygonList(lst) + + + pen.SetWidth(width) + pen.SetColour(color) + brush.SetColour(fcolor) + brush.SetStyle(style) + dc.SetPen(pen) + dc.SetBrush(brush) + +def draw_rectangle(pts, dc, f, **key): + pen, brush = dc.GetPen(), dc.GetBrush() + width, color = pen.GetWidth(), pen.GetColour() + fcolor, style = brush.GetColour(), brush.GetStyle() + + if 'color' in pts: + pen.SetColour(pts['color']) + if 'fcolor' in pts: + brush.SetColour(pts['fcolor']) + if 'lw' in pts: + pen.SetWidth(pts['lw']) + if 'fill' in pts: + brush.SetStyle((106,100)[pts['fill']]) + + dc.SetPen(pen) + dc.SetBrush(brush) + + if pts['type'] == 'rectangle': + x, y, w, h = pts['body'] + x, y = f(x, y) + w, h = w*key['k'], h*key['k'] + dc.DrawRectangle(x-w/2, y-w/2, w, h) + if pts['type'] == 'rectangles': + lst = [] + for x, y, w, h in pts['body']: + x, y = f(x, y) + w, h = w*key['k'], h*key['k'] + lst.append((x-w/2, y-w/2, w, h)) + dc.DrawRectangleList(lst) + + pen.SetWidth(width) + pen.SetColour(color) + brush.SetColour(fcolor) + brush.SetStyle(style) + dc.SetPen(pen) + dc.SetBrush(brush) + +def draw_text(pts, dc, f, **key): + pen, brush, font = dc.GetPen(), dc.GetBrush(), dc.GetFont() + width, color = pen.GetWidth(), pen.GetColour() + fcolor, style = brush.GetColour(), brush.GetStyle() + size = font.GetPointSize() + tcolor = dc.GetTextForeground() + bcolor = dc.GetTextBackground() + + if 'color' in pts: + pen.SetColour(pts['color']) + dc.SetTextForeground(pts['color']) + brush.SetColour(pen.GetColour()) + brush.SetStyle(100) + if 'fcolor' in pts: + print('hahaha') + dc.SetTextBackground(pts['fcolor']) + if 'size' in pts: + font.SetPointSize(pts['size']) + + dc.SetPen(pen) + dc.SetBrush(brush) + dc.SetFont(font) + + if pts['type'] == 'text': + x, y, text = pts['body'] + x, y = f(x, y) + dc.DrawText(text, x+3, y+3) + if not 'pt' in pts or pts['pt']: + dc.DrawEllipse(x-2,y-2,4,4) + if pts['type'] == 'texts': + tlst, clst, elst = [], [], [] + for x, y, text in pts['body']: + x, y = f(x, y) + tlst.append(text) + clst.append((x+3, y+3)) + elst.append((x-2, y-2, 4, 4)) + dc.DrawTextList(tlst, clst) + if not 'pt' in pts or pts['pt']: + dc.DrawEllipseList(elst) + + font.SetPointSize(size) + pen.SetColour(color) + brush.SetColour(fcolor) + brush.SetStyle(style) + dc.SetPen(pen) + dc.SetBrush(brush) + dc.SetFont(font) + dc.SetTextForeground(tcolor) + dc.SetTextBackground(bcolor) + +draw_dic = {'points':plot, 'point':plot, 'line':plot, 'polygon':plot, 'lines':plot, 'polygons':plot, + 'circle':draw_circle, 'circles':draw_circle, 'ellipse':draw_ellipse, 'ellipses':draw_ellipse, + 'rectangle':draw_rectangle, 'rectangles':draw_rectangle, 'text':draw_text, 'texts':draw_text} + +def draw(obj, dc, f, **key): draw_dic[obj['type']](obj, dc, f, **key) + +def draw_layer(pts, dc, f, **key): + pen, brush = dc.GetPen(), dc.GetBrush() + width, color = pen.GetWidth(), pen.GetColour() + fcolor, style = brush.GetColour(), brush.GetStyle() + + if 'color' in pts: + pen.SetColour(pts['color']) + if 'fcolor' in pts: + brush.SetColour(pts['fcolor']) + if 'lw' in pts: + pen.SetWidth(pts['lw']) + if 'fill' in pts: + brush.SetStyle((106,100)[pts['fill']]) + + dc.SetPen(pen) + dc.SetBrush(brush) + + for i in pts['body']:draw(i, dc, f, **key) + + pen.SetWidth(width) + pen.SetColour(color) + brush.SetColour(fcolor) + brush.SetStyle(style) + dc.SetPen(pen) + dc.SetBrush(brush) + +draw_dic['layer'] = draw_layer + +def draw_layers(pts, dc, f, **key): + pen, brush = dc.GetPen(), dc.GetBrush() + width, color = pen.GetWidth(), pen.GetColour() + fcolor, style = brush.GetColour(), brush.GetStyle() + + if 'color' in pts: + pen.SetColour(pts['color']) + if 'fcolor' in pts: + brush.SetColour(pts['fcolor']) + if 'lw' in pts: + pen.SetWidth(pts['lw']) + if 'fill' in pts: + brush.SetStyle((106,100)[pts['fill']]) + + dc.SetPen(pen) + dc.SetBrush(brush) + print(pts['body'].keys()) + if key['cur'] in pts['body']: + draw(pts['body'][key['cur']], dc, f, **key) + + pen.SetWidth(width) + pen.SetColour(color) + brush.SetColour(fcolor) + brush.SetStyle(style) + dc.SetPen(pen) + dc.SetBrush(brush) + +draw_dic['layers'] = draw_layers + +class GeometryMark: + def __init__(self, body): + self.body = body + + def draw(self, dc, f, **key): + pen, brush, font = dc.GetPen(), dc.GetBrush(), dc.GetFont() + pen.SetColour(ConfigManager.get('mark_color') or (255,255,0)) + brush.SetColour(ConfigManager.get('mark_fcolor') or (255,255,255)) + brush.SetStyle((106,100)[ConfigManager.get('mark_fill') or False]) + pen.SetWidth(ConfigManager.get('mark_lw') or 1) + dc.SetTextForeground(ConfigManager.get('mark_tcolor') or (255,0,0)) + font.SetPointSize(ConfigManager.get('mark_tsize') or 8) + dc.SetPen(pen); dc.SetBrush(brush); dc.SetFont(font); + draw(self.body, dc, f, **key) + +if __name__ == '__main__': + print(make_ellipse(0,0,2,1,0)) \ No newline at end of file diff --git a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py index 25220552..79537bc8 100644 --- a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py @@ -3,12 +3,13 @@ Created on Tue Dec 27 01:06:59 2016 @author: yxl """ -from imagepy import IPy, wx +from imagepy import IPy import numpy as np from imagepy.core.engine import Simple, Filter from imagepy.core.manager import ImageManager from scipy.ndimage import label, generate_binary_structure from skimage.measure import regionprops +from imagepy.core.mark import GeometryMark import pandas as pd class Mark: @@ -73,7 +74,7 @@ def run(self, ips, imgs, para = None): if para['solid']:titles.extend(['Solidity']) if para['cov']:titles.extend(['Major','Minor','Ori']) buf = imgs[0].astype(np.uint16) - data, mark = [], [] + data, mark = [], {'type':'layers', 'body':{}} strc = generate_binary_structure(2, 1 if para['con']=='4-connect' else 2) for i in range(len(imgs)): label(imgs[i], strc, output=buf) @@ -82,10 +83,14 @@ def run(self, ips, imgs, para = None): dt = [[i]*len(ls), list(range(len(ls)))] if not para['slice']:dt = dt[1:] - if not para['cov']: cvs = [None] * len(ls) - else: cvs = [(i.major_axis_length, i.minor_axis_length, i.orientation) for i in ls] - centroids = [i.centroid for i in ls] - mark.append([(center, cov) for center,cov in zip(centroids, cvs)]) + layer = {'type':'layer', 'body':[]} + texts = [(i.centroid[::-1])+('id=%d'%n,) for i,n in zip(ls,range(len(ls)))] + layer['body'].append({'type':'texts', 'body':texts}) + if para['cov']: + ellips = [i.centroid[::-1] + (i.major_axis_length/2,i.minor_axis_length/2,i.orientation) for i in ls] + layer['body'].append({'type':'ellipses', 'body':ellips}) + mark['body'][i] = layer + if para['center']: dt.append([round(i.centroid[1]*k,1) for i in ls]) dt.append([round(i.centroid[0]*k,1) for i in ls]) @@ -112,7 +117,7 @@ def run(self, ips, imgs, para = None): dt.append([round(i.orientation*k, 1) for i in ls]) data.extend(list(zip(*dt))) - ips.mark = Mark(mark) + ips.mark = GeometryMark(mark) IPy.show_table(pd.DataFrame(data, columns=titles), ips.title+'-region') # center, area, l, extent, cov diff --git a/imagepy/menus/Image/Mark/__init__.py b/imagepy/menus/Image/Mark/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/menus/Image/Mark/mark_plgs.py b/imagepy/menus/Image/Mark/mark_plgs.py new file mode 100644 index 00000000..a6f2ff13 --- /dev/null +++ b/imagepy/menus/Image/Mark/mark_plgs.py @@ -0,0 +1,84 @@ +from imagepy.core.engine import Simple, Free +from imagepy.core.mark import GeometryMark +from imagepy.core.manager import ConfigManager +import json +from imagepy import IPy + +class Clear(Simple): + """Save: save roi as a wkt file """ + title = 'Clear Mark' + note = ['all'] + + def run(self, ips, imgs, para = None): + ips.mark = None + +class Save(Simple): + """Save: save roi as a wkt file """ + title = 'Save Mark' + note = ['all'] + para={'path':''} + + def load(self, ips): + if not isinstance(ips.mark, GeometryMark): + IPy.alert('only geometry mark could be saved!') + return False + return True + + def show(self): + filt = 'MARK files (*.mrk)|*.mrk' + return IPy.getpath('Save..', filt, 'save', self.para) + + def run(self, ips, imgs, para = None): + f = open(para['path'], 'w') + f.write(json.dumps(ips.mark.body)) + f.close() + +class Open(Simple): + """Save: save roi as a wkt file """ + title = 'Open Mark' + note = ['all'] + para={'path':''} + + def show(self): + filt = 'MARK files (*.mrk)|*.mrk' + return IPy.getpath('Open..', filt, 'open', self.para) + + def run(self, ips, imgs, para = None): + f = open(para['path']) + geo = json.load(f) + f.close() + if geo['type'] == 'layers': + body = geo['body'] + for i in list(body.keys()): + body[int(i)] = body.pop(i) + ips.mark = GeometryMark(geo) + +class Setting(Free): + title = 'Mark Setting' + + view = [('color', 'color', 'line', 'color'), + ('color', 'fcolor', 'face', 'color'), + ('color', 'tcolor', 'text', 'color'), + (int, 'lw', (1,10), 0, 'width', 'pix'), + (int, 'size', (1,30), 0, 'text', 'size'), + (bool, 'fill', 'solid fill')] + + def load(self): + Setting.para = para = {} + para['color'] = ConfigManager.get('mark_color') or (255,255,0) + para['fcolor'] = ConfigManager.get('mark_fcolor') or (255,255,255) + para['fill'] = ConfigManager.get('mark_fill') or False + para['lw'] = ConfigManager.get('mark_lw') or 1 + para['size'] = ConfigManager.get('mark_tsize') or 8 + para['tcolor'] = ConfigManager.get('mark_tcolor') or (255,0,0) + return True + + def run(self, para=None): + ConfigManager.set('mark_color', para['color']) + ConfigManager.set('mark_fcolor', para['fcolor']) + ConfigManager.set('mark_tcolor', para['tcolor']) + ConfigManager.set('mark_lw', para['lw']) + ConfigManager.set('mark_fill', para['fill']) + ConfigManager.set('mark_tsize', para['size']) + +plgs = [Open, Save, Clear, '-', Setting] \ No newline at end of file diff --git a/imagepy/menus/Image/__init__.py b/imagepy/menus/Image/__init__.py index af735aee..74d714b4 100644 --- a/imagepy/menus/Image/__init__.py +++ b/imagepy/menus/Image/__init__.py @@ -1,2 +1,3 @@ catlog = ['Type', '-', 'Adjust', 'Color', 'Stack', 'Transform', '-', 'duplicate_plg', - 'crop_plg', 'canvassize_plg', 'resize_plg', '-', 'setscale_plg', 'background_plg', 'Lookup table'] \ No newline at end of file + 'crop_plg', 'canvassize_plg', 'resize_plg', '-', 'setscale_plg', 'background_plg', + '-', 'Mark', 'Lookup table'] \ No newline at end of file From 03e2da6db70cb7b6db8347599613469253e27695 Mon Sep 17 00:00:00 2001 From: Prevalenter <4346liuxing> Date: Sat, 14 Jul 2018 22:50:49 +0800 Subject: [PATCH 020/343] Signed-off-by: Prevalenter <4346liuxing> --- imagepy/core/mark/mark.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/core/mark/mark.py b/imagepy/core/mark/mark.py index 145fd275..0cb5e55b 100644 --- a/imagepy/core/mark/mark.py +++ b/imagepy/core/mark/mark.py @@ -221,7 +221,7 @@ def draw_rectangle(pts, dc, f, **key): x, y, w, h = pts['body'] x, y = f(x, y) w, h = w*key['k'], h*key['k'] - dc.DrawRectangle(x-w/2, y-w/2, w, h) + dc.DrawRectangle(x-w/2, y-h/2, w, h) if pts['type'] == 'rectangles': lst = [] for x, y, w, h in pts['body']: From 05f95e663dab4e151bd5de2aedbe86bd7c5731c8 Mon Sep 17 00:00:00 2001 From: Prevalenter <4346liuxing> Date: Sun, 15 Jul 2018 13:00:42 +0800 Subject: [PATCH 021/343] Signed-off-by: Prevalenter <4346liuxing> --- imagepy/core/mark/mark.py | 2 +- .../Region Analysis/statistic_plgs.py | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/imagepy/core/mark/mark.py b/imagepy/core/mark/mark.py index 0cb5e55b..02bfa2cc 100644 --- a/imagepy/core/mark/mark.py +++ b/imagepy/core/mark/mark.py @@ -227,7 +227,7 @@ def draw_rectangle(pts, dc, f, **key): for x, y, w, h in pts['body']: x, y = f(x, y) w, h = w*key['k'], h*key['k'] - lst.append((x-w/2, y-w/2, w, h)) + lst.append((x-w/2, y-h/2, w, h)) dc.DrawRectangleList(lst) pen.SetWidth(width) diff --git a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py index f2084f41..69816a80 100644 --- a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py @@ -12,7 +12,7 @@ from imagepy.core.manager import ImageManager from imagepy.core.roi.pointroi import PointRoi import pandas as pd - +from imagepy.core.mark import GeometryMark class Mark: def __init__(self, data): self.data = data @@ -80,7 +80,8 @@ def run(self, ips, imgs, para = None): if para['extent']: titles.extend(['Min-Y','Min-X','Max-Y','Max-X']) titles.extend(idct) k = ips.unit[0] - data, mark = [], [] + data, mark = [],{'type':'layers', 'body':{}} + # data,mark=[],[] for i in range(len(imgs)): n = ndimage.label(msks[i], strc, output=buf) index = range(1, n+1) @@ -95,7 +96,9 @@ def run(self, ips, imgs, para = None): boxs = [None] * n if para['extent']: boxs = ndimage.find_objects(buf) - boxs = [(i[0].start, i[1].start, i[0].stop, i[1].stop) for i in boxs] + print("#####",boxs) + boxs = [( i[1].start+(i[1].stop-i[1].start)/2, i[0].start+(i[0].stop-i[0].start)/2, i[1].stop-i[1].start,i[0].stop-i[0].start) for i in boxs] + print(boxs) for j in (0,1,2,3): dt.append([i[j]*k for i in boxs]) if para['max']:dt.append(ndimage.maximum(imgs[i], buf, index).round(2)) @@ -104,12 +107,18 @@ def run(self, ips, imgs, para = None): if para['var']:dt.append(ndimage.variance(imgs[i], buf, index).round(2)) if para['std']:dt.append(ndimage.standard_deviation(imgs[i], buf, index).round(2)) if para['sum']:dt.append(ndimage.sum(imgs[i], buf, index).round(2)) + + layer = {'type':'layer', 'body':[]} + xy=np.int0(xy).T + texts = [(i[1],i[0])+('id=%d'%n,) for i,n in zip(xy,range(len(xy)))] + layer['body'].append({'type':'texts', 'body':texts}) + if para['extent']: layer['body'].append({'type':'rectangles', 'body':boxs}) + mark['body'][i] = layer - mark.append([(center, cov) for center,cov in zip(xy.T, boxs)]) data.extend(list(zip(*dt))) IPy.show_table(pd.DataFrame(data, columns=titles), inten.title+'-region statistic') - inten.mark = Mark(mark) + inten.mark = GeometryMark(mark) inten.update = True class RGMark: From 8bc16392a1cabbeac172443fa4a55c7e334d29b1 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 19 Jul 2018 23:22:32 +0800 Subject: [PATCH 022/343] 3d volumn --- imagepy/core/myvi/util.py | 51 ++++++++++++++- .../menus/Kit3D/Network 3D/toolkit3d_plgs.py | 65 ++++++++++++++++++- 2 files changed, 111 insertions(+), 5 deletions(-) diff --git a/imagepy/core/myvi/util.py b/imagepy/core/myvi/util.py index 0c6409b8..8eca3444 100644 --- a/imagepy/core/myvi/util.py +++ b/imagepy/core/myvi/util.py @@ -14,6 +14,17 @@ def count_ns(vts, fs): buf /= np.linalg.norm(buf, axis=1).reshape((-1,1)) return buf +def build_twringidx(n, offset=0): + idx = np.array([[0,1,n+1],[n+1,n+2,1]]) + idx = idx[np.arange(n*2)%2].T + np.arange(n*2)//2 + return (idx.T+offset).astype(np.uint32) + +def build_pringidx(p, n, offset=0): + ridx = np.array([[0,0,1]]*n, dtype=np.uint32) + ridx += np.arange(n, dtype=np.uint32).reshape((-1,1))+offset + ridx[:,0] = p + return ridx + def build_grididx(r, c): idx = np.arange(r*c, dtype=np.uint32) rs, cs = idx//c, idx%c @@ -24,7 +35,7 @@ def build_grididx(r, c): def build_surf2d(img, ds=1, sigma=0, k=0.2): from skimage.filters import sobel_h, sobel_v from scipy.ndimage import gaussian_filter - start = time() + #start = time() img = img[::-ds, ::ds] img = gaussian_filter(img, sigma) r, c = img.shape @@ -42,7 +53,7 @@ def build_surf2d(img, ds=1, sigma=0, k=0.2): ns = (ns.T/np.linalg.norm(ns, axis=1)).astype(np.float32).T #ns = count_ns(vts, fs) - print(time()-start) + #print(time()-start) return vts, fs, ns, cs def build_surf3d(imgs, ds, level, step=1, c=(1,0,0)): @@ -108,6 +119,42 @@ def build_lines(xs, ys, zs, cs): css.append(cc) return np.vstack(vtss), np.vstack(fss), np.vstack(nss), np.vstack(css) +def build_arrow(v1, v2, rs, re, ts, te, c): + v = (v2-v1)/np.linalg.norm(v2-v1) + ss, ee = v1 + v*rs*ts, v2 - v*re*te + vx = np.cross(v, np.random.rand(3)) + vx /= np.linalg.norm(vx) + vy = np.cross(vx, v) + angs = np.linspace(0, np.pi*2, 17) + vas = np.array([np.cos(angs), np.sin(angs)]) + vxy = np.dot(vas.T, np.array([vx, vy])) + vts = np.vstack((v1, ss + rs * vxy, ee + re * vxy, v2)) + fs1 = build_pringidx(0, 16, 1) + fs = build_twringidx(16, 1) + fs2 = build_pringidx(35, 16, 18) + face = np.vstack((fs1, fs, fs2)) + ns = np.vstack((-v, vxy, vxy, v)).astype(np.float32) + cs = (np.ones((len(vts), 3))*c).astype(np.float32) + return vts.astype(np.float32), face, ns, cs + +def build_arrows(v1s, v2s, rss, res, tss, tes, cs): + if not isinstance(cs, list): cs = [cs] * len(v1s) + if not isinstance(tss, list): tss = [tss] * len(v1s) + if not isinstance(tes, list): tes = [tes] * len(v1s) + if not isinstance(rss, list): rss = [rss] * len(v1s) + if not isinstance(res, list): res = [res] * len(v1s) + vtss, fss, nss, css = [], [], [], [] + s = 0 + for v1, v2, rs, re, ts, te, c in zip(v1s, v2s, rss, res, tss, tes, cs): + vv, ff, nn, cc = build_arrow(v1, v2, rs, re, ts, te, c) + fss.append(ff+s) + s += len(vv) + vtss.append(vv) + nss.append(nn) + css.append(cc) + print(np.vstack(vtss).shape, np.vstack(fss).shape, np.vstack(nss).shape, np.vstack(css).shape) + return np.vstack(vtss), np.vstack(fss), np.vstack(nss), np.vstack(css) + def build_mark(cont, pos, dz, h, color): vts, fss = [], [] s, sw = 0, 0 diff --git a/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py b/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py index 30e9a93e..dbb32c0e 100644 --- a/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py +++ b/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py @@ -1,4 +1,5 @@ from imagepy.core.engine import Filter, Simple +from imagepy.core.manager import ImageManager from imagepy.ipyalg.graph import sknw from skimage.morphology import skeletonize_3d from itertools import combinations @@ -10,14 +11,13 @@ import pandas as pd norm = np.linalg.norm - class Skeleton3D(Simple): title = 'Skeleton 3D' note = ['8-bit', 'stack3d'] #process def run(self, ips, imgs, para = None): - imgs[:] = skeletonize_3d(imgs>0) + imgs[skeletonize_3d(imgs>0)==0] = 0 class BuildGraph(Simple): title = 'Build Graph 3D' @@ -80,6 +80,65 @@ def run(self, ips, imgs, para = None): self.frame.Raise() self.frame = None +class Show3DGraphR(Simple): + title = 'Show Graph R 3D' + note = ['8-bit', 'stack3d'] + + para = {'dis':None, 'ncolor':(255,0,0), 'lcolor':(0,0,255)} + view = [('img', 'dis', 'distance', 'map'), + ('color', 'ncolor', 'node', 'rgb'), + ('color', 'lcolor', 'line', 'rgb')] + + def load(self, ips): + if not isinstance(ips.data, nx.MultiGraph): + IPy.alert("Please build graph!"); + return False; + self.frame = myvi.Frame3D.figure(IPy.curapp, title='3D Canvas') + return True; + + #process + def run(self, ips, imgs, para = None): + dis = ImageManager.get(para['dis']).imgs + balls, ids, rs, graph = [], [], [], ips.data + for idx in graph.nodes(): + ids.append(idx) + balls.append(graph.node[idx]['o']) + + + xs, ys, zs = [], [], [] + v1s, v2s = [], [] + for (s, e) in graph.edges(): + eds = graph[s][e] + st, ed = graph.node[s]['o'], graph.node[e]['o'] + v1s.append(st) + v2s.append(ed) + for i in eds: + pts = eds[i]['pts'] + xs.append(pts[:,0]) + ys.append(pts[:,1]) + zs.append(pts[:,2]) + rs1 = dis[list(np.array(v1s).astype(np.int16).T)] + rs2 = dis[list(np.array(v2s).astype(np.int16).T)] + rs1 = list(np.clip(rs1, 2, 1e4)*0.5) + rs2 = list(np.clip(rs2, 2, 1e4)*0.5) + rs = dis[list(np.array(balls).astype(np.int16).T)] + rs = list(np.clip(rs, 2, 1e4)) + cs = tuple(np.array(para['ncolor'])/255.0) + vts, fs, ns, cs = myvi.build_balls(balls, rs, cs) + self.frame.viewer.add_surf_asyn('balls', vts, fs, ns, cs) + meansize = sum(rs)/len(rs) + vts, fs, pos, h, color = myvi.build_marks(['ID:%s'%i for i in ids], balls, rs, meansize, (1,1,1)) + self.frame.viewer.add_mark_asyn('txt', vts, fs, pos, h, color) + + css = tuple(np.array(para['lcolor'])/255.0) + vts, fs, ns, cs = myvi.build_lines(xs, ys, zs, css) + self.frame.viewer.add_surf_asyn('paths', vts, fs, ns, cs, mode='grid') + #vts, fs, ns, cs = myvi.build_lines(lxs, lys, lzs, (0,1,0)) + vts, fs, ns, cs = myvi.build_arrows(v1s, v2s, rs1, rs2, 0, 0, css) + self.frame.viewer.add_surf_asyn('lines', vts, fs, ns, cs) + self.frame.Raise() + self.frame = None + class Statistic(Simple): title = 'Graph Statistic 3D' note = ['all'] @@ -224,4 +283,4 @@ def run(self, ips, imgs, para = None): imgs *= 0 sknw.draw_graph(imgs, g) -plgs = [Skeleton3D, BuildGraph, '-', CutBranch, RemoveIsolate, '-', Statistic, Sumerise, '-', Show3DGraph] \ No newline at end of file +plgs = [Skeleton3D, BuildGraph, '-', CutBranch, RemoveIsolate, '-', Statistic, Sumerise, '-', Show3DGraph, Show3DGraphR] \ No newline at end of file From 68270514783cbe4b08e8b20b4097427ab5b7b622 Mon Sep 17 00:00:00 2001 From: Prevalenter <4346liuxing> Date: Thu, 2 Aug 2018 22:32:15 +0800 Subject: [PATCH 023/343] add scipy-kmeans Signed-off-by: Prevalenter <4346liuxing> --- .../menus/Analysis/Pixel Cluster/__init__.py | 2 +- .../menus/Analysis/Pixel Cluster/kmeans_plgs.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py diff --git a/imagepy/menus/Analysis/Pixel Cluster/__init__.py b/imagepy/menus/Analysis/Pixel Cluster/__init__.py index 9c94bfb8..c0a689da 100644 --- a/imagepy/menus/Analysis/Pixel Cluster/__init__.py +++ b/imagepy/menus/Analysis/Pixel Cluster/__init__.py @@ -1 +1 @@ -catlog = ['cluster_plgs', '-', 'statistic_plgs'] \ No newline at end of file +catlog = ['cluster_plgs', '-', 'statistic_plgs','-','kmeans_plgs'] diff --git a/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py b/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py new file mode 100644 index 00000000..11e97b84 --- /dev/null +++ b/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py @@ -0,0 +1,17 @@ +from imagepy.core.engine import Filter +from imagepy import IPy +import numpy as np +from scipy.cluster.vq import kmeans, vq +class K_mean(Filter): + title = 'K-Mean' + note = ['all','2float', 'not_channel','auto_msk', 'auto_snap','preview'] + para = {'k':8,'iter':20,'thresh':1e-5} + view = [(int, 'k', (1,99999), 0, 'k', ''), + (int, 'iter', (0,99999), 0, 'iter', ''), + (float, 'thresh', (0,99999), 10, 'thresh', '')] + def run(self, ips, snap, img, para = None): + pts = snap.reshape((-1,(1,3)[snap.ndim-2])) + ms = kmeans(pts.astype(np.float32), para['k'],para['iter'],para['thresh'])[0] + img[:] = ms[vq(pts, ms)[0]].reshape(img.shape) + +plgs = [K_mean] From 187e19815c788e49fef551295b62e72674864486 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 3 Aug 2018 12:45:30 +0800 Subject: [PATCH 024/343] color --- imagepy/core/myvi/manager.py | 3 +- imagepy/core/myvi/util.py | 8 ++++ .../Analysis/Pixel Cluster/cluster_plgs.py | 6 +-- .../Analysis/Pixel Cluster/kmeans_plgs.py | 24 +++++++----- .../Region Analysis/regionprops_plgs.py | 25 ------------- imagepy/menus/Analysis/statistic_plg.py | 5 +-- .../menus/Image/Color/splitandmerge_plgs.py | 7 ++-- imagepy/menus/Image/duplicate_plg.py | 18 +++++++-- imagepy/menus/Image/resize_plg.py | 17 +++++---- imagepy/menus/Kit3D/Viewer 3D/__init__.py | 3 +- imagepy/menus/Kit3D/Viewer 3D/colorpts_plg.py | 37 +++++++++++++++++++ imagepy/menus/Plugins/Games/crossstick_plg.py | 25 +++++++++++++ .../menus/Process/Hydrology/hydrology_plgs.py | 4 +- imagepy/ui/canvasframe.py | 3 ++ 14 files changed, 126 insertions(+), 59 deletions(-) create mode 100644 imagepy/menus/Kit3D/Viewer 3D/colorpts_plg.py create mode 100644 imagepy/menus/Plugins/Games/crossstick_plg.py diff --git a/imagepy/core/myvi/manager.py b/imagepy/core/myvi/manager.py index a0af2a86..3f9dccbb 100644 --- a/imagepy/core/myvi/manager.py +++ b/imagepy/core/myvi/manager.py @@ -62,6 +62,7 @@ def __init__(self, vts, ids, ns, cs=(0,0,1)): self.box = np.vstack((vts.min(axis=0), vts.max(axis=0))) self.mode, self.blend, self.visible = 'mesh', 1.0, True self.color = cs if isinstance(cs, tuple) else (0,0,0) + self.width = 1 def on_ctx(self, ctx, prog): self.ctx = ctx @@ -86,7 +87,7 @@ def set_style(self, mode=None, blend=None, color=None, visible=None): def draw(self, mvp): if not self.visible: return - self.ctx.line_width = 1 + self.ctx.line_width = self.width mvp = np.dot(*mvp) self.prog['Mvp'].write(mvp.astype(np.float32).tobytes()) self.prog['blend'].value = self.blend diff --git a/imagepy/core/myvi/util.py b/imagepy/core/myvi/util.py index 8eca3444..bc4a47a7 100644 --- a/imagepy/core/myvi/util.py +++ b/imagepy/core/myvi/util.py @@ -146,6 +146,7 @@ def build_arrows(v1s, v2s, rss, res, tss, tes, cs): vtss, fss, nss, css = [], [], [], [] s = 0 for v1, v2, rs, re, ts, te, c in zip(v1s, v2s, rss, res, tss, tes, cs): + if np.linalg.norm(v1-v2) < 0.1: continue vv, ff, nn, cc = build_arrow(v1, v2, rs, re, ts, te, c) fss.append(ff+s) s += len(vv) @@ -184,6 +185,13 @@ def build_marks(conts, poss, dz, h, color): return np.vstack(vtss), np.vstack(fss), np.vstack(pps), h, color +def build_cube(p1, p2, color=(1,1,1)): + (x1,y1,z1),(x2,y2,z2) = p1, p2 + xs = (x1,x2,x2,x1,x1,x1,x1,x1,x1,x2,x2,x1,x2,x2,x2,x2) + ys = (y1,y1,y1,y1,y1,y2,y2,y1,y2,y2,y2,y2,y2,y1,y1,y2) + zs = (z1,z1,z2,z2,z1,z1,z2,z2,z2,z2,z1,z1,z1,z1,z2,z2) + return build_line(xs, ys, zs, color) + cmp = {'rainbow':[(127, 0, 255), (43, 126, 246), (42, 220, 220), (128, 254, 179), (212, 220, 127), (255, 126, 65), (255, 0, 0)], 'jet':[(0, 0, 127), (0, 40, 255), (0, 212, 255), (124, 255, 121), (255, 229, 0), (255, 70, 0), (127, 0, 0)], 'ocean':[(0, 127, 0), (0, 64, 42), (0, 0, 85), (0, 64, 128), (0, 127, 170), (129, 192, 213), (255, 255, 255)], diff --git a/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py b/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py index 0d3e4298..5c414f9e 100644 --- a/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py +++ b/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py @@ -37,7 +37,7 @@ class ColorCluster(Filter): note = ['rgb', 'auto_snap', 'not_channel', 'not_slice', 'req_roi', 'preview'] para = {'sigma':3, 'cov':True, 'new':True, 'msk':'nothing', 'within':False, 'msk':'nothing'} - view = [(float, 'sigma', (0,255), 1, 'sigma', ''), + view = [(float, 'sigma', (0,255), 1, 'torlerance', ''), (bool, 'cov', 'use cov instead of distance'), (bool, 'within', 'only points within'), (bool, 'new', 'result as new mask'), @@ -76,7 +76,7 @@ class GrayCluster(Filter): note = ['8-bit', '16-bit', 'int', 'float', 'auto_snap', 'not_channel', 'not_slice', 'req_roi', 'preview'] para = {'sigma':3, 'cov':True, 'new':True, 'within':False, 'msk':'nothing','msk':'nothing'} - view = [(float, 'sigma', (0,255), 1, 'sigma', 'pix'), + view = [(float, 'sigma', (0,255), 1, 'torlerance', 'pix'), (bool, 'cov', 'use cov instead of distance'), (bool, 'within', 'only points within'), (bool, 'new', 'result as new mask'), @@ -122,7 +122,7 @@ class GrayCluster3D(Simple): note = ['8-bit', '16-bit', 'int', 'float', 'req_roi', 'stack3d', 'preview'] modal = False para = {'sigma':3, 'cov':True, 'new':True, 'within':False, 'msk':'nothing','msk':'nothing'} - view = [(float, 'sigma', (0,255), 1, 'sigma', 'pix'), + view = [(float, 'sigma', (0,255), 1, 'torlerance', 'pix'), (bool, 'cov', 'use cov instead of distance'), (bool, 'within', 'only points within'), (bool, 'new', 'result as new mask'), diff --git a/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py b/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py index 11e97b84..a6963db9 100644 --- a/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py +++ b/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py @@ -2,16 +2,22 @@ from imagepy import IPy import numpy as np from scipy.cluster.vq import kmeans, vq + class K_mean(Filter): - title = 'K-Mean' - note = ['all','2float', 'not_channel','auto_msk', 'auto_snap','preview'] - para = {'k':8,'iter':20,'thresh':1e-5} - view = [(int, 'k', (1,99999), 0, 'k', ''), - (int, 'iter', (0,99999), 0, 'iter', ''), - (float, 'thresh', (0,99999), 10, 'thresh', '')] + title = 'K-Means' + + note = ['all', 'not_channel', 'auto_msk', 'auto_snap', 'preview'] + para = {'k':8, 'iter':20, 'ds':2, 'thresh':1e-5} + + view = [(int, 'k', (1,100), 0, 'k', ''), + (int, 'iter', (1, 50), 0, 'iterate', ''), + (float, 'thresh', (0, 1), 5, 'threshold', ''), + (int, 'ds', (1, 50), 0, 'resample', ''),] + def run(self, ips, snap, img, para = None): - pts = snap.reshape((-1,(1,3)[snap.ndim-2])) - ms = kmeans(pts.astype(np.float32), para['k'],para['iter'],para['thresh'])[0] - img[:] = ms[vq(pts, ms)[0]].reshape(img.shape) + pts = snap.reshape((-1,(1,3)[snap.ndim-2])) + ms = kmeans(pts[::para['ds']**2].astype(np.float32), + para['k'],para['iter'],para['thresh'])[0] + img[:] = ms[vq(pts, ms)[0]].reshape(img.shape) plgs = [K_mean] diff --git a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py index 79537bc8..a5a22dfb 100644 --- a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py @@ -12,31 +12,6 @@ from imagepy.core.mark import GeometryMark import pandas as pd -class Mark: - def __init__(self, data): - self.data = data - - def draw(self, dc, f, **key): - dc.SetPen(wx.Pen((255,255,0), width=1, style=wx.SOLID)) - dc.SetTextForeground((255,255,0)) - font = wx.Font(8, wx.FONTFAMILY_DEFAULT, - wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) - - dc.SetFont(font) - data = self.data[0 if len(self.data)==1 else key['cur']] - for i in range(len(data)): - pos = f(*(data[i][0][1], data[i][0][0])) - dc.DrawCircle(pos[0], pos[1], 2) - dc.DrawText('id={}'.format(i), pos[0], pos[1]) - if data[i][1]==None:continue - k1, k2, a = data[i][1] - aixs = np.array([[-np.sin(a), np.cos(a)], - [np.cos(a), np.sin(a)]])*[k1/2, k2/2] - ar = np.linspace(0, np.pi*2,25) - xy = np.vstack((np.cos(ar), np.sin(ar))) - arr = np.dot(aixs, xy).T+data[i][0] - dc.DrawLines([f(*i) for i in arr[:,::-1]]) - # center, area, l, extent, cov class RegionCounter(Simple): title = 'Geometry Analysis' diff --git a/imagepy/menus/Analysis/statistic_plg.py b/imagepy/menus/Analysis/statistic_plg.py index ae7ddd3d..d65031ca 100644 --- a/imagepy/menus/Analysis/statistic_plg.py +++ b/imagepy/menus/Analysis/statistic_plg.py @@ -28,7 +28,7 @@ def rgb(self, hist): rgb = ['Red', 'Green', 'Blue'] for i in (0,1,2): histc = HistCanvas(panel) - histc.set_hist(hist[i]) + histc.SetValue(hist[i]) txt = wx.StaticText( panel, wx.ID_ANY, 'Channel:'+ rgb[i], wx.DefaultPosition, wx.DefaultSize, 0 ) sizer.Add( txt, 0, wx.LEFT|wx.RIGHT, 8 ) sizer.Add( histc, 0, wx.LEFT|wx.RIGHT, 8 ) @@ -58,7 +58,6 @@ def gray(self, hist): self.SetSizer(back) self.Fit() - def showhist(parent, title, hist): HistogramFrame(parent, title, hist).Show() @@ -74,7 +73,7 @@ def run(self, ips, imgs, para = None): msk = ips.get_msk('in') if ips.imgtype == 'rgb': img = ips.img if msk is None else ips.img[msk] - hist = [np.histogram(img[:,i], np.arange(257))[0] for i in (0,1,2)] + hist = [np.histogram(img[:,:,i], np.arange(257))[0] for i in (0,1,2)] else: img = ips.lookup() if msk is None else ips.lookup()[msk] hist = np.histogram(img, np.arange(257))[0] diff --git a/imagepy/menus/Image/Color/splitandmerge_plgs.py b/imagepy/menus/Image/Color/splitandmerge_plgs.py index b8453261..ab0f9701 100644 --- a/imagepy/menus/Image/Color/splitandmerge_plgs.py +++ b/imagepy/menus/Image/Color/splitandmerge_plgs.py @@ -6,7 +6,7 @@ from imagepy import IPy import numpy as np from imagepy.ui.canvasframe import CanvasFrame -from imagepy.core.manager import ImageManager +from imagepy.core.manager import ImageManager, WindowsManager from imagepy.core.engine import Simple from skimage import color @@ -45,13 +45,14 @@ def load(self, ips): (bool, 'destory', 'destory')] return True - def titles(self): return 'RGB-Merge', 'Red', 'Green', 'Blue' + def titles(self): return 'RGB-Merge', 'red', 'green', 'blue' def trans(self, img1, img2, img3): return np.array([img1.T, img2.T, img3.T], dtype=np.uint8).T def run(self, ips, imgs, para = None): idx = ['red','green','blue'] + print(para) imr,img,imb = [ImageManager.get(para[i]) for i in idx] sr,sg,sb = [i.get_nslices() for i in [imr,img,imb]] @@ -68,7 +69,7 @@ def run(self, ips, imgs, para = None): IPy.show_img(rgbs, self.titles()[0]) if self.para['destory']: for title in [para[i] for i in idx]: - ImageManager.close(title) + WindowsManager.get(title).close() class RGB2(Simple): title = 'RGB To RGB' diff --git a/imagepy/menus/Image/duplicate_plg.py b/imagepy/menus/Image/duplicate_plg.py index a7e81299..b3bd8034 100644 --- a/imagepy/menus/Image/duplicate_plg.py +++ b/imagepy/menus/Image/duplicate_plg.py @@ -6,11 +6,11 @@ from imagepy.core.engine import Simple from imagepy.core import ImagePlus -from imagepy.ui.canvasframe import CanvasFrame +from imagepy.core.manager import ImageManager import numpy as np from imagepy import IPy -class Plugin(Simple): +class Duplicate(Simple): title = 'Duplicate' note = ['all'] @@ -55,4 +55,16 @@ def run(self, ips, imgs, para = None): ipsd.roi = ips.roi.affine(np.eye(2), (-sr.start, -sc.start)) if not ips.backimg is None: ipsd.backimg = backimg ipsd.backmode = ips.backmode - IPy.show_ips(ipsd) \ No newline at end of file + IPy.show_ips(ipsd) + +class Rename(Simple): + title = 'Rename' + note = ['all'] + + para = {'name':'Undefined'} + view = [(str, 'name', 'name', '')] + #process + def run(self, ips, imgs, para = None): + ips.title = ImageManager.name(para['name']) + +plgs = [Rename, Duplicate] \ No newline at end of file diff --git a/imagepy/menus/Image/resize_plg.py b/imagepy/menus/Image/resize_plg.py index d893de30..acac0c07 100644 --- a/imagepy/menus/Image/resize_plg.py +++ b/imagepy/menus/Image/resize_plg.py @@ -13,10 +13,11 @@ class Plugin(Simple): title = 'Resize' note = ['all'] - para = {'kx':0.5, 'ky':0.5, 'kz':1} - view = [(float, 'kx', (0.1,10), 1, 'kx', '0.1~10'), - (float, 'ky', (0.1,10), 1, 'ky', '0.1~10'), - (float, 'kz', (0.1,10), 1, 'kz', '0.1~10'), + para = {'kx':0.5, 'ky':0.5, 'kz':1,'order':3} + view = [(float, 'kx', (0.1,10), 2, 'kx', '0.1~10'), + (float, 'ky', (0.1,10), 2, 'ky', '0.1~10'), + (float, 'kz', (0.1,10), 2, 'kz', '0.1~10'), + (int, 'order', (0,5), 0, 'accu', '0-5'), ('lab', None, 'the kz only works on stack!')] def run(self, ips, imgs, para = None): @@ -28,9 +29,9 @@ def run(self, ips, imgs, para = None): new = np.zeros(np.multiply(imgs.shape, (kz, kx, ky, 1)).round().astype(np.uint32), dtype=imgs.dtype) for i in range(ips.get_nchannels()): - ndimg.zoom(imgs[:,:,:,i], (kz, kx, ky), output=new[:,:,:,i]) + ndimg.zoom(imgs[:,:,:,i], (kz, kx, ky), output=new[:,:,:,i], order=para['order']) else : - new = ndimg.zoom(imgs, (kz, kx, ky)) + new = ndimg.zoom(imgs, (kz, kx, ky), order=para['order']) else: if ips.get_nchannels()>1: new = [] @@ -39,13 +40,13 @@ def run(self, ips, imgs, para = None): arr = np.zeros(np.multiply(imgs[i].shape, (kx, ky, 1)).round().astype(np.uint32), dtype=imgs[i].dtype) for n in range(ips.get_nchannels()): - ndimg.zoom(imgs[i][:,:,n], (kx, ky), output=arr[:,:,n]) + ndimg.zoom(imgs[i][:,:,n], (kx, ky), output=arr[:,:,n], order=para['order']) new.append(arr) else : new = [] for i in range(len(imgs)): self.progress(i, len(imgs)) - arr = ndimg.zoom(imgs[i], (kx, ky)) + arr = ndimg.zoom(imgs[i], (kx, ky), order=para['order']) new.append(arr) ips.set_imgs(new) diff --git a/imagepy/menus/Kit3D/Viewer 3D/__init__.py b/imagepy/menus/Kit3D/Viewer 3D/__init__.py index 7ece0d88..70f0ca08 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/__init__.py +++ b/imagepy/menus/Kit3D/Viewer 3D/__init__.py @@ -1,2 +1 @@ -catlog = ['surface2d_plg', 'surface3d_plg', - '-', '2DSurface Demo.mc'] \ No newline at end of file +catlog = ['surface2d_plg', 'surface3d_plg', 'colorpts_plg', '-', '2DSurface Demo.mc'] \ No newline at end of file diff --git a/imagepy/menus/Kit3D/Viewer 3D/colorpts_plg.py b/imagepy/menus/Kit3D/Viewer 3D/colorpts_plg.py new file mode 100644 index 00000000..18525eb9 --- /dev/null +++ b/imagepy/menus/Kit3D/Viewer 3D/colorpts_plg.py @@ -0,0 +1,37 @@ +from imagepy import IPy +from imagepy.core.engine import Simple +from imagepy.core import myvi +import numpy as np + +class Plugin(Simple): + title = 'RGB Points Cloud' + note = ['rgb'] + para = {'name':'undifine', 'num':100, 'r':1} + view = [(str, 'name', 'Name', ''), + (int, 'num', (10,1024), 0, 'number', 'points'), + (float, 'r', (0.1,30), 1, 'radius', '')] + + def load(self, para): + self.frame = myvi.Frame3D.figure(IPy.curapp, title='3D Canvas') + return True + + def run(self, ips, imgs, para = None): + num,r = para['num'], para['r'] + if ips.roi != None: pts = ips.img[ips.get_msk()] + else: pts = ips.img.reshape((-1,3)) + pts = pts[::len(pts)//num] + vts, fs, ns, cs = myvi.build_balls(pts, np.ones(len(pts))*r, pts/255) + self.frame.viewer.add_surf_asyn(para['name'], vts, fs, ns, cs) + (r1,g1,b1),(r2,g2,b2) = (0,0,0),(1,1,1) + rs = (r1,r2,r2,r1,r1,r1,r1,r1,r1,r2,r2,r1,r2,r2,r2,r2) + gs = (g1,g1,g1,g1,g1,g2,g2,g1,g2,g2,g2,g2,g2,g1,g1,g2) + bs = (b1,b1,b2,b2,b1,b1,b2,b2,b2,b2,b1,b1,b1,b1,b2,b2) + vts, fs, ns, cs = myvi.build_cube((0,0,0),(255,255,255)) + cs = list(zip(rs,gs,bs)) + self.frame.viewer.add_surf_asyn('cube', vts, fs, ns, cs, mode='grid') + self.frame.Raise() + self.frame = None + #self.frame.add_surf2d('dem', ips.img, ips.lut, scale, sigma) + +if __name__ == '__main__': + pass \ No newline at end of file diff --git a/imagepy/menus/Plugins/Games/crossstick_plg.py b/imagepy/menus/Plugins/Games/crossstick_plg.py new file mode 100644 index 00000000..5b514243 --- /dev/null +++ b/imagepy/menus/Plugins/Games/crossstick_plg.py @@ -0,0 +1,25 @@ +from imagepy.core.engine import Filter +from imagepy import IPy +import numpy as np +from scipy.cluster.vq import kmeans, vq + +class Plugin(Filter): + title = 'Cross Stick' + + note = ['rgb', 'not_channel', 'auto_msk', 'auto_snap', 'preview'] + para = {'block':8, 'k':12, 'grid':False} + + view = [(int, 'k', (1,100), 0, 'k', ''), + (int, 'block', (5, 50), 0, 'block', ''), + (bool, 'grid', 'show grid')] + + def run(self, ips, snap, img, para = None): + k, block = para['k'], para['block'] + buf = snap[::block,::block].copy() + pts = buf.reshape((-1,3)).astype(np.float32) + ms = kmeans(pts, k)[0] + buf[:] = ms[vq(pts, ms)[0]].reshape(buf.shape) + xs, ys = np.where(img[:,:,0]>-1) + img[xs, ys] = buf[xs//block, ys//block] + if para['grid']: + img[::block], img[:,::block] = 0, 0 \ No newline at end of file diff --git a/imagepy/menus/Process/Hydrology/hydrology_plgs.py b/imagepy/menus/Process/Hydrology/hydrology_plgs.py index cfc42ba2..3aa09bd0 100644 --- a/imagepy/menus/Process/Hydrology/hydrology_plgs.py +++ b/imagepy/menus/Process/Hydrology/hydrology_plgs.py @@ -43,7 +43,7 @@ class FindMax(Filter): def run(self, ips, snap, img, para = None): pts = find_maximum(self.ips.img, para['tol']) - self.ips.roi = PointRoi([tuple(i) for i in pts[:,::-1]]) + self.ips.roi = PointRoi([tuple(i)+(0,) for i in pts[:,::-1]]) self.ips.update = True class FindMin(Filter): @@ -55,7 +55,7 @@ class FindMin(Filter): def run(self, ips, snap, img, para = None): pts = find_maximum(self.ips.img, para['tol'], False) - self.ips.roi = PointRoi([tuple(i) for i in pts[:,::-1]]) + self.ips.roi = PointRoi([tuple(i)+(0,) for i in pts[:,::-1]]) self.ips.update = True class UPRidge(Filter): diff --git a/imagepy/ui/canvasframe.py b/imagepy/ui/canvasframe.py index 85c10fff..6c14fd44 100644 --- a/imagepy/ui/canvasframe.py +++ b/imagepy/ui/canvasframe.py @@ -104,6 +104,9 @@ def on_scroll(self, event): self.ips.update = 'pix' self.canvas.on_idle(None) + def close(self): + self.GetParent().Close() + def __del__(self):pass class CanvasFrame(wx.Frame): From 8872c4666b1a4b8a15350096b0c3b17f4af2b634 Mon Sep 17 00:00:00 2001 From: Tong Date: Sun, 27 May 2018 16:57:14 +0200 Subject: [PATCH 025/343] will not synchronize pycharm project file --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index bfc40fde..13521d02 100644 --- a/.gitignore +++ b/.gitignore @@ -90,3 +90,6 @@ ENV/ # Rope project settings .ropeproject + +# Pycharm project +.idea/ From 5bd8f8904c970cffa9c7e25dafa3a8f64831da31 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 7 Sep 2018 23:49:09 +0800 Subject: [PATCH 026/343] id_ok --- imagepy/IPy.py | 8 +++---- imagepy/core/engine/filter.py | 6 ++--- imagepy/core/engine/free.py | 6 ++--- imagepy/core/engine/simple.py | 8 +++---- imagepy/core/engine/table.py | 8 +++---- imagepy/core/myvi/canvas3d.py | 3 +-- imagepy/core/roi/pointroi.py | 1 + imagepy/menus/File/DAT/dat_plgs.py | 4 ++++ imagepy/menus/File/Export/sequence_plg.py | 2 +- imagepy/menus/File/GIF/__init__.py | 2 +- imagepy/menus/File/GIF/animate_plgs.py | 22 ++++++++++--------- imagepy/menus/File/Import/sequence_plg.py | 2 +- .../menus/Process/Hydrology/hydrology_plgs.py | 4 ++-- imagepy/ui/canvas.py | 12 +--------- imagepy/ui/tablewindow.py | 2 +- 15 files changed, 43 insertions(+), 47 deletions(-) diff --git a/imagepy/IPy.py b/imagepy/IPy.py index 3814f523..077f9871 100644 --- a/imagepy/IPy.py +++ b/imagepy/IPy.py @@ -109,7 +109,7 @@ def alert(info, title="ImagePy Alert!"): print(info) else: dialog=wx.MessageDialog(curapp, info, title, wx.OK) - dialog.ShowModal() + dialog.ShowModal() == wx.ID_OK dialog.Destroy() # MT alert = lambda info, title='image-py':callafter(alert_, *(info, title)) @@ -138,7 +138,7 @@ def getpath(title, filt, k, para=None): if para!=None:para['path'] = path dialog.Destroy() - return rst if para!=None else path + return rst == wx.ID_OK if para!=None else path def getdir(title, filt, para=None): from .core import manager @@ -152,7 +152,7 @@ def getdir(title, filt, para=None): path = dialog.GetPath() if para!=None:para['path'] = path dialog.Destroy() - return rst if para!=None else path + return rst == wx.ID_OK if para!=None else path def get_para(title, view, para): from .ui.panelconfig import ParaDialog @@ -160,7 +160,7 @@ def get_para(title, view, para): pd.init_view(view, para) rst = pd.ShowModal() pd.Destroy() - return rst + return rst == wx.ID_OK def showtable(data, title): from .core import TablePlus diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index 9104561b..b41a7e73 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -101,7 +101,7 @@ def show(self, temp=ParaDialog): self.dialog = temp(WindowsManager.get(), self.title) self.dialog.init_view(self.view, self.para, 'preview' in self.note, modal=self.modal) self.dialog.set_handle(lambda x:self.preview(self.ips, x)) - if self.modal: return self.dialog.ShowModal() + if self.modal: return self.dialog.ShowModal() == wx.ID_OK self.dialog.on_ok = lambda : self.ok(self.ips) self.dialog.on_cancel = lambda : self.cancel(self.ips) self.dialog.Show() @@ -193,11 +193,11 @@ def start(self, para=None, callafter=None): self.ok(self.ips, para, callafter) elif self.view==None: if not self.__class__.show is Filter.show: - if self.show() == wx.ID_OK: + if self.show(): self.ok(self.ips, para, callafter) else: self.ok(self.ips, para, callafter) elif self.modal: - if self.show() == wx.ID_OK: + if self.show(): self.ok(ips, None, callafter) else:self.cancel(ips) self.dialog.Destroy() diff --git a/imagepy/core/engine/free.py b/imagepy/core/engine/free.py index e419a605..b2a9fdd5 100644 --- a/imagepy/core/engine/free.py +++ b/imagepy/core/engine/free.py @@ -34,14 +34,14 @@ def runasyn(self, para, callback=None): def load(self):return True def show(self): - if self.view==None:return wx.ID_OK + if self.view==None:return True with ParaDialog(WindowsManager.get(), self.title) as dialog: dialog.init_view(self.view, self.para, False, True) - return dialog.ShowModal() + return dialog.ShowModal() == wx.ID_OK def start(self, para=None, callback=None): if not self.load():return - if para!=None or self.show() == wx.ID_OK: + if para!=None or self.show(): if para==None:para = self.para win = WidgetsManager.getref('Macros Recorder') if win!=None: diff --git a/imagepy/core/engine/simple.py b/imagepy/core/engine/simple.py index b989ea09..8324b126 100644 --- a/imagepy/core/engine/simple.py +++ b/imagepy/core/engine/simple.py @@ -33,11 +33,11 @@ def load(self, ips):return True def preview(self, ips, para):pass def show(self, temp=ParaDialog): - if self.view==None:return wx.ID_OK + if self.view==None:return True self.dialog = temp(IPy.get_window(), self.title) self.dialog.init_view(self.view, self.para, 'preview' in self.note, modal=self.modal) self.dialog.set_handle(lambda x:self.preview(self.ips, self.para)) - if self.modal: return self.dialog.ShowModal() + if self.modal: return self.dialog.ShowModal() == wx.ID_OK self.dialog.on_ok = lambda : self.ok(self.ips) self.dialog.on_cancel = lambda : self.cancel(self.ips) self.dialog.Show() @@ -110,11 +110,11 @@ def start(self, para=None, callback=None): self.ok(self.ips, para, callback) elif self.view==None: if not self.__class__.show is Simple.show: - if self.show() == wx.ID_OK: + if self.show(): self.ok(self.ips, para, callback) else: self.ok(self.ips, para, callback) elif self.modal: - if self.show() == wx.ID_OK: + if self.show(): self.ok(self.ips, para, callback) else:self.cancel(self.ips) if not self.dialog is None: self.dialog.Destroy() diff --git a/imagepy/core/engine/table.py b/imagepy/core/engine/table.py index 106daf33..3d79f48a 100644 --- a/imagepy/core/engine/table.py +++ b/imagepy/core/engine/table.py @@ -35,11 +35,11 @@ def preview(self, tps, para): tps.update = True def show(self): - if self.view==None:return wx.ID_OK + if self.view==None:return True self.dialog = ParaDialog(IPy.get_twindow(), self.title) self.dialog.init_view(self.view, self.para, 'preview' in self.note, modal=self.modal) self.dialog.set_handle(lambda x:self.preview(self.tps, self.para)) - if self.modal: return self.dialog.ShowModal() + if self.modal: return self.dialog.ShowModal() == wx.ID_OK self.dialog.on_ok = lambda : self.ok(self.tps) self.dialog.on_cancel = lambda : self.cancel(self.tps) self.dialog.Show() @@ -117,11 +117,11 @@ def start(self, para=None, callback=None): self.ok(self.tps, para, callback) elif self.view==None: if not self.__class__.show is Table.show: - if self.show() == wx.ID_OK: + if self.show(): self.ok(self.tps, para, callback) else: self.ok(self.tps, para, callback) elif self.modal: - if self.show() == wx.ID_OK: + if self.show(): self.ok(self.tps, para, callback) else:self.cancel(self.tps) if not self.dialog is None: self.dialog.Destroy() diff --git a/imagepy/core/myvi/canvas3d.py b/imagepy/core/myvi/canvas3d.py index b26ff358..db79c19a 100644 --- a/imagepy/core/myvi/canvas3d.py +++ b/imagepy/core/myvi/canvas3d.py @@ -218,8 +218,7 @@ def on_save(self, evt): dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} filt = 'PNG files (*.png)|*.png' dialog = wx.FileDialog(self, 'Save Picture', '', '', filt, wx.FD_SAVE) - rst = dialog.ShowModal() - if rst == wx.ID_OK: + if dialog.ShowModal() == wx.ID_OK: path = dialog.GetPath() self.canvas.save_bitmap(path) dialog.Destroy() diff --git a/imagepy/core/roi/pointroi.py b/imagepy/core/roi/pointroi.py index f71cb638..10e017a9 100644 --- a/imagepy/core/roi/pointroi.py +++ b/imagepy/core/roi/pointroi.py @@ -14,6 +14,7 @@ class PointRoi(ROI): dtype = 'point' def __init__(self, body=None): self.body = body if body!=None else [] + self.body = [i if len(i)==3 else tuple(i) + (0,) for i in self.body] self.update = body!=[] self.infoupdate = body!=[] diff --git a/imagepy/menus/File/DAT/dat_plgs.py b/imagepy/menus/File/DAT/dat_plgs.py index 837e13de..3b57523c 100644 --- a/imagepy/menus/File/DAT/dat_plgs.py +++ b/imagepy/menus/File/DAT/dat_plgs.py @@ -1,12 +1,16 @@ from imagepy.core.util import fileio import numpy as np from imagepy.core.manager import ReaderManager, WriterManager + def imread(path): return np.loadtxt(path,dtype=float) + def imsave(path,img): np.savetxt(path,img) + ReaderManager.add('dat', imread) WriterManager.add('dat', imsave) + class OpenFile(fileio.Reader): title = 'DAT Open' filt = ['DAT'] diff --git a/imagepy/menus/File/Export/sequence_plg.py b/imagepy/menus/File/Export/sequence_plg.py index 4c95bffc..0dfd21f2 100644 --- a/imagepy/menus/File/Export/sequence_plg.py +++ b/imagepy/menus/File/Export/sequence_plg.py @@ -24,7 +24,7 @@ def load(self, ips): def show(self): self.para['name'] = self.ips.title rst = IPy.get_para('Save sequence', self.view, self.para) - if rst!=wx.ID_OK:return rst + if not rst :return rst return IPy.getdir('Save sequence', '', self.para) #process diff --git a/imagepy/menus/File/GIF/__init__.py b/imagepy/menus/File/GIF/__init__.py index c236cbd6..ee081cf4 100644 --- a/imagepy/menus/File/GIF/__init__.py +++ b/imagepy/menus/File/GIF/__init__.py @@ -1 +1 @@ -catlog = ['gif_plgs', 'animate_plgs'] \ No newline at end of file +catlog = ['gif_plgs', '-', 'animate_plgs'] \ No newline at end of file diff --git a/imagepy/menus/File/GIF/animate_plgs.py b/imagepy/menus/File/GIF/animate_plgs.py index 860f0577..be0a5d69 100644 --- a/imagepy/menus/File/GIF/animate_plgs.py +++ b/imagepy/menus/File/GIF/animate_plgs.py @@ -1,20 +1,22 @@ from imagepy.core.util import fileio -from imagepy import IPy -import os +from imagepy.core.engine import Simple +from imagepy import IPy, root_dir +import os, imageio import numpy as np -from PIL import Image, ImageSequence - - -class SaveAnimate(fileio.Writer): +class SaveAnimate(Simple): title = 'GIF Animate Save' - filt = ['GIF'] - note = ['8-bit', 'rgb', 'stack'] + note = ['all'] + para={'path':root_dir, 'dur':0.2} + view = [(int, 'dur', (0.01, 10), 2, 'duration', 's')] + + def load(self, ips): + filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in ['GIF']]) + return IPy.getpath('Save..', filt, 'save', self.para) #process def run(self, ips, imgs, para = None): - imgs = [Image.fromarray(i) for i in imgs] - imgs[0].save(para['path'], save_all=True, loop=0, duration=10, append_images=imgs[1:]) + imageio.mimsave(para['path'], imgs, 'GIF', duration = para['dur']) class OpenAnimate(fileio.Reader): title = 'GIF Animate Open' diff --git a/imagepy/menus/File/Import/sequence_plg.py b/imagepy/menus/File/Import/sequence_plg.py index 40f87b50..48a8d9a5 100644 --- a/imagepy/menus/File/Import/sequence_plg.py +++ b/imagepy/menus/File/Import/sequence_plg.py @@ -23,7 +23,7 @@ def load(self): def show(self): filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in self.filt]) rst = IPy.getpath('Import sequence', filt, 'open', self.para) - if rst!=wx.ID_OK:return rst + if not rst: return rst files = self.getfiles(self.para['path']) nfs = len(files) diff --git a/imagepy/menus/Process/Hydrology/hydrology_plgs.py b/imagepy/menus/Process/Hydrology/hydrology_plgs.py index 3aa09bd0..cfc42ba2 100644 --- a/imagepy/menus/Process/Hydrology/hydrology_plgs.py +++ b/imagepy/menus/Process/Hydrology/hydrology_plgs.py @@ -43,7 +43,7 @@ class FindMax(Filter): def run(self, ips, snap, img, para = None): pts = find_maximum(self.ips.img, para['tol']) - self.ips.roi = PointRoi([tuple(i)+(0,) for i in pts[:,::-1]]) + self.ips.roi = PointRoi([tuple(i) for i in pts[:,::-1]]) self.ips.update = True class FindMin(Filter): @@ -55,7 +55,7 @@ class FindMin(Filter): def run(self, ips, snap, img, para = None): pts = find_maximum(self.ips.img, para['tol'], False) - self.ips.roi = PointRoi([tuple(i)+(0,) for i in pts[:,::-1]]) + self.ips.roi = PointRoi([tuple(i) for i in pts[:,::-1]]) self.ips.update = True class UPRidge(Filter): diff --git a/imagepy/ui/canvas.py b/imagepy/ui/canvas.py index 2c4fff9e..6610483f 100644 --- a/imagepy/ui/canvas.py +++ b/imagepy/ui/canvas.py @@ -175,17 +175,7 @@ def on_idle(self, event): def on_paint(self, event): wx.BufferedPaintDC(self, self.buffer) - ''' - cdc = wx.ClientDC(self) - #cdc.BeginDrawing() - if self.ips.roi != None: - self.ips.roi.draw(cdc, self.to_panel_coor) - if self.ips.mark != None: - self.ips.mark.draw(cdc, self.to_panel_coor, cur=self.ips.cur, k = self.get_scale()) - if self.ips.unit!=(1,'pix'): - self.draw_ruler(cdc) - #cdc.EndDrawing() - ''' + def merge(self, img, back, M, O, mode, shape, win, lookup): if img.ndim == 2: rstarr = np.zeros(shape, dtype=img.dtype) diff --git a/imagepy/ui/tablewindow.py b/imagepy/ui/tablewindow.py index e63a946d..365625cc 100644 --- a/imagepy/ui/tablewindow.py +++ b/imagepy/ui/tablewindow.py @@ -206,7 +206,7 @@ def on_label(self, evt): ('color', 'lc', 'line color', ''), (list, 'ln', ['Text', 'Line', 'Both'], str, 'draw', '')] rst = IPy.get_para('Table Properties', view, para) - if rst!=wx.ID_OK:return + if not rst :return if col!=-1: props.iloc[:,col] = [para[i] for i in ['accu', 'tc', 'lc', 'ln']] if col==-1: From df9a5be25cfee09add046f8d38c94c5b33609117 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 17 Sep 2018 13:24:20 +0800 Subject: [PATCH 027/343] mid axis --- imagepy/ipyalg/graph/skel2d.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/ipyalg/graph/skel2d.py b/imagepy/ipyalg/graph/skel2d.py index 644c32cd..d040df27 100644 --- a/imagepy/ipyalg/graph/skel2d.py +++ b/imagepy/ipyalg/graph/skel2d.py @@ -37,7 +37,6 @@ def medial_axis(data, idx, branch = True): h, w = data.shape data = data.ravel() for id in idx: - if data[id]==0:continue i2=id-w;i8=id+w;i1=i2-1;i3=i2+1; i4=id-1;i6=id+1;i7=i8-1;i9=i8+1; @@ -51,6 +50,7 @@ def medial_axis(data, idx, branch = True): def mid_axis(img): dis = ndimg.distance_transform_edt(img) + dis[[0,-1],:] = 0; dis[:,[0,-1]] = 0 idx = np.argsort(dis.flat).astype(np.int32) medial_axis(dis, idx, lut) return dis From 3651b1747293d3ea847b188cee3ac88bf137ee9e Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 25 Sep 2018 09:31:33 +0800 Subject: [PATCH 028/343] chart --- imagepy/core/engine/table.py | 7 ++-- imagepy/menus/Table/Chart/__init__ | 0 imagepy/menus/Table/Chart/plot_plgs.py | 48 ++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 imagepy/menus/Table/Chart/__init__ create mode 100644 imagepy/menus/Table/Chart/plot_plgs.py diff --git a/imagepy/core/engine/table.py b/imagepy/core/engine/table.py index 3d79f48a..45123dfd 100644 --- a/imagepy/core/engine/table.py +++ b/imagepy/core/engine/table.py @@ -19,6 +19,7 @@ class Table: view = None prgs = (None, 1) modal = True + asyn = True def __init__(self, tps=None): print('simple start') @@ -53,10 +54,10 @@ def cancel(self, tps): def ok(self, tps, para=None, callafter=None): if para == None: para = self.para - if IPy.uimode() == 'no': - self.runasyn(tps, tps.data, tps.snap, para, callafter) - else: threading.Thread(target = self.runasyn, + if self.asyn and IPy.uimode() != 'no': + threading.Thread(target = self.runasyn, args = (tps, tps.data, tps.snap, para, callafter)).start() + else: self.runasyn(tps, tps.data, tps.snap, para, callafter) win = WidgetsManager.getref('Macros Recorder') if win!=None: win.write('{}>{}'.format(self.title, para)) diff --git a/imagepy/menus/Table/Chart/__init__ b/imagepy/menus/Table/Chart/__init__ new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/menus/Table/Chart/plot_plgs.py b/imagepy/menus/Table/Chart/plot_plgs.py new file mode 100644 index 00000000..870141c4 --- /dev/null +++ b/imagepy/menus/Table/Chart/plot_plgs.py @@ -0,0 +1,48 @@ +from imagepy.core.engine import Table +from imagepy import IPy +import matplotlib.pyplot as plt + +class Plot(Table): + title = 'Plot Chart' + para = {'cn':[], 'lw':1} + asyn = False + view = [('fields', 'cn', 'fields'), + (int, 'lw', (1,5), 0, 'line width', '')] + + def run(self, tps, data, snap, para = None): + data[para['cn']].plot(lw=para['lw']) + plt.show() + +class Bar(Table): + title = 'Bar Chart' + para = {'cn':[], 'dir':False, 'stack':False} + asyn = False + view = [('fields', 'cn', 'fields'), + (bool, 'dir', 'horizon'), + (bool, 'stack', 'stacked')] + + def run(self, tps, data, snap, para = None): + if para['dir']: + data[para['cn']].plot.barh(stacked=para['stack']) + else: data[para['cn']].plot.bar(stacked=para['stack']) + plt.show() + +class Hist(Table): + title = 'Hist Chart' + para = {'cn':[], 'dir':'vertical', 'stack':False, 'bins':10, 'alpha':1.0, 'overlay':False} + asyn = False + view = [('fields', 'cn', 'fields'), + (int, 'bins', (3,1000), 0, 'bins', ''), + (float, 'alpha', (0,1), 1, 'alpha', '0~1'), + (list, 'dir', ['horizontal', 'vertical'], str, 'orientation', ''), + (bool, 'stack', 'stacked'), + (bool, 'overlay', 'draw every columns in one')] + + def run(self, tps, data, snap, para = None): + print(para) + f = [data[para['cn']].hist, data[para['cn']].plot.hist][para['overlay']] + f(stacked=para['stack'], bins=para['bins'], alpha=para['alpha'], + orientation=para['dir']) + plt.show() + +plgs = [Plot, Bar, Hist] \ No newline at end of file From f17416b2835f94ceff8a101717ee067d0c451370 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 25 Sep 2018 19:59:40 +0800 Subject: [PATCH 029/343] plot --- imagepy/menus/Table/Chart/plot_plgs.py | 92 ++++++++++++++++--- imagepy/ui/panelconfig.py | 2 +- imagepy/ui/widgets/__init__.py | 2 +- imagepy/ui/widgets/advanced.py | 12 ++- .../ui/widgets/{cmapselect.py => colormap.py} | 71 +++++++------- imagepy/ui/widgets/normal.py | 2 +- imagepy/widgets/histogram/histogram_wgt.py | 12 +-- 7 files changed, 125 insertions(+), 68 deletions(-) rename imagepy/ui/widgets/{cmapselect.py => colormap.py} (70%) diff --git a/imagepy/menus/Table/Chart/plot_plgs.py b/imagepy/menus/Table/Chart/plot_plgs.py index 870141c4..23b6bdd5 100644 --- a/imagepy/menus/Table/Chart/plot_plgs.py +++ b/imagepy/menus/Table/Chart/plot_plgs.py @@ -1,48 +1,110 @@ from imagepy.core.engine import Table from imagepy import IPy import matplotlib.pyplot as plt +from imagepy.core.manager import ColorManager +from matplotlib import colors class Plot(Table): title = 'Plot Chart' - para = {'cn':[], 'lw':1} + para = {'cn':[], 'lw':1, 'grid':False, 'title':''} asyn = False - view = [('fields', 'cn', 'fields'), - (int, 'lw', (1,5), 0, 'line width', '')] + view = [(str, 'title', 'title', ''), + ('fields', 'cn', 'select fields'), + (int, 'lw', (1,5), 0, 'line width', ''), + (bool, 'grid', 'grid')] def run(self, tps, data, snap, para = None): - data[para['cn']].plot(lw=para['lw']) + data[para['cn']].plot(lw=para['lw'], grid=para['grid'], title=para['title']) plt.show() class Bar(Table): title = 'Bar Chart' - para = {'cn':[], 'dir':False, 'stack':False} + para = {'cn':[], 'dir':False, 'stack':False,'grid':False, 'title':''} asyn = False - view = [('fields', 'cn', 'fields'), + view = [(str, 'title', 'title', ''), + ('fields', 'cn', 'select fields'), (bool, 'dir', 'horizon'), - (bool, 'stack', 'stacked')] + (bool, 'stack', 'stacked'), + (bool, 'grid', 'grid')] def run(self, tps, data, snap, para = None): if para['dir']: - data[para['cn']].plot.barh(stacked=para['stack']) - else: data[para['cn']].plot.bar(stacked=para['stack']) + data[para['cn']].plot.barh(stacked=para['stack'], grid=para['grid'], title=para['title']) + else: data[para['cn']].plot.bar(stacked=para['stack'], grid=para['grid'], title=para['title']) plt.show() class Hist(Table): title = 'Hist Chart' - para = {'cn':[], 'dir':'vertical', 'stack':False, 'bins':10, 'alpha':1.0, 'overlay':False} + para = {'cn':[], 'dir':'vertical', 'stack':False, 'bins':10, 'alpha':1.0, + 'overlay':False, 'grid':False, 'title':''} asyn = False - view = [('fields', 'cn', 'fields'), + view = [(str, 'title', 'title', ''), + ('fields', 'cn', 'select fields'), (int, 'bins', (3,1000), 0, 'bins', ''), (float, 'alpha', (0,1), 1, 'alpha', '0~1'), (list, 'dir', ['horizontal', 'vertical'], str, 'orientation', ''), (bool, 'stack', 'stacked'), - (bool, 'overlay', 'draw every columns in one')] + (bool, 'overlay', 'draw every columns in one'), + (bool, 'grid', 'grid')] def run(self, tps, data, snap, para = None): - print(para) f = [data[para['cn']].hist, data[para['cn']].plot.hist][para['overlay']] f(stacked=para['stack'], bins=para['bins'], alpha=para['alpha'], - orientation=para['dir']) + orientation=para['dir'], grid=para['grid'], title=para['title']) + plt.show() + +class Box(Table): + title = 'Box Chart' + para = {'cn':[], 'hor':False, 'by':None, 'grid':False, 'title':''} + asyn = False + view = [(str, 'title', 'title', ''), + ('fields', 'cn', 'select fields'), + ('field', 'by', 'by', 'group'), + (bool, 'hor', 'horizontal'), + (bool, 'grid', 'grid')] + + def run(self, tps, data, snap, para = None): + data[para['cn']].plot.box(by=None, vert=~para['hor'], grid=para['grid'], title=para['title']) + plt.show() + +class Area(Table): + title = 'Area Chart' + para = {'cn':[], 'dir':'vertical', 'stack':False, 'bins':10, 'alpha':1.0, 'grid':False, 'title':''} + asyn = False + view = [(str, 'title', 'title', ''), + ('fields', 'cn', 'select fields'), + (float, 'alpha', (0,1), 1, 'alpha', '0~1'), + (bool, 'stack', 'stacked'), + (bool, 'grid', 'grid')] + + def run(self, tps, data, snap, para = None): + data[para['cn']].plot.area(stacked=para['stack'], alpha=para['alpha'], + grid=para['grid'], title=para['title']) + plt.show() + +class Scatter(Table): + title = 'Scatter Chart' + para = {'x':None, 'y':None, 's':1, 'alpha':1.0, 'rs':None, 'c':(0,0,255), + 'cs':None, 'cm':None, 'grid':False, 'title':''} + asyn = False + view = [(str, 'title', 'title', ''), + ('field', 'x', 'x data', ''), + ('field', 'y', 'y data', ''), + (int, 's', (0, 1024), 0, 'size', 'pix'), + ('field', 'rs', 'radius column', 'optional'), + (float, 'alpha', (0,1), 1, 'alpha', '0~1'), + ('color', 'c', 'color', ''), + ('field', 'cs', 'color column', 'optional'), + ('cmap', 'cm', 'color map'), + (bool, 'grid', 'grid')] + + def run(self, tps, data, snap, para = None): + rs = data[para['rs']] * para['s'] if para['rs'] != 'None' else para['s'] + cs = data[para['cs']] if para['cs'] != 'None' else '#%.2x%.2x%.2x'%para['c'] + cm = ColorManager.get_lut(para['cm'])/255.0 + cm = None if para['cs'] == 'None' else colors.ListedColormap(cm, N=256) + data.plot.scatter(x=para['x'], y=para['y'], s=rs, c=cs, alpha=para['alpha'], + cmap=cm, grid=para['grid'], title=para['title']) plt.show() -plgs = [Plot, Bar, Hist] \ No newline at end of file +plgs = [Plot, Bar, Hist, Box, Area, Scatter] \ No newline at end of file diff --git a/imagepy/ui/panelconfig.py b/imagepy/ui/panelconfig.py index 7e643bff..59d084d6 100644 --- a/imagepy/ui/panelconfig.py +++ b/imagepy/ui/panelconfig.py @@ -9,7 +9,7 @@ float:NumCtrl, 'lab':Label, bool:Check, str:TextCtrl, list:Choice, 'img':ImageList, 'tab':TableList, 'color':ColorCtrl, 'any':AnyType, 'chos':Choices, 'fields':TableFields, - 'field':TableField, 'hist':HistCanvas} + 'field':TableField, 'hist':HistCanvas, 'cmap':ColorMap} class ParaDialog (wx.Dialog): def __init__( self, parent, title): diff --git a/imagepy/ui/widgets/__init__.py b/imagepy/ui/widgets/__init__.py index 5bf24171..4acdb646 100644 --- a/imagepy/ui/widgets/__init__.py +++ b/imagepy/ui/widgets/__init__.py @@ -3,5 +3,5 @@ from .cmappanel import CMapPanel from .normal import * from .advanced import * -from .cmapselect import CMapSelPanel +from .colormap import CMapSelCtrl from .viewport import ViewPort \ No newline at end of file diff --git a/imagepy/ui/widgets/advanced.py b/imagepy/ui/widgets/advanced.py index 5ce1277c..d7a436d3 100644 --- a/imagepy/ui/widgets/advanced.py +++ b/imagepy/ui/widgets/advanced.py @@ -1,5 +1,6 @@ from . normal import Choice, Choices -from ...core.manager import ImageManager, TableManager +from . colormap import CMapSelPanel +from ...core.manager import ImageManager, TableManager, ColorManager class ImageList(Choice): def __init__(self, parent, title, unit): @@ -12,7 +13,7 @@ def __init__(self, parent, title, unit): class TableField(Choice): def __init__(self, parent, title, unit): self.tps = TableManager.get() - Choice.__init__(self, parent, list(self.tps.data.columns), str, title, unit) + Choice.__init__(self, parent, ['None'] + list(self.tps.data.columns), lambda x:x, title, unit) class TableFields(Choices): def __init__(self, parent, title): @@ -20,4 +21,9 @@ def __init__(self, parent, title): Choices.__init__(self, parent, self.tps.data.columns, title) def SetValue(self, value): - Choices.SetValue(self, self.tps.colmsk) \ No newline at end of file + Choices.SetValue(self, self.tps.colmsk) + +class ColorMap(CMapSelPanel): + def __init__(self, parent, title): + CMapSelPanel.__init__(self, parent, title) + self.SetItems(ColorManager.luts) diff --git a/imagepy/ui/widgets/cmapselect.py b/imagepy/ui/widgets/colormap.py similarity index 70% rename from imagepy/ui/widgets/cmapselect.py rename to imagepy/ui/widgets/colormap.py index 7f6e5ca6..ce55224b 100644 --- a/imagepy/ui/widgets/cmapselect.py +++ b/imagepy/ui/widgets/colormap.py @@ -1,19 +1,16 @@ -#!/usr/bin/env python - -import wx, sys -import wx.adv import numpy as np +import wx.adv +import sys, wx if sys.version_info[0]==2:memoryview=np.getbuffer -class CMapSelPanel(wx.adv.OwnerDrawnComboBox): +class CMapSelCtrl(wx.adv.OwnerDrawnComboBox): def __init__(self, parent): wx.adv.OwnerDrawnComboBox.__init__(self, parent, choices=[], style=wx.CB_READONLY, pos=(20,40), size=(256, 30)) - self.handle = self.handle_ - self.Bind( wx.EVT_COMBOBOX, self.on_sel) - def SetItems(self, ks, vs): + def SetItems(self, kvs): self.Clear() + ks, vs = list(kvs.keys()), list(kvs.values()) if 'Grays' in ks: i = ks.index('Grays') ks.insert(0, ks.pop(i)) @@ -21,6 +18,14 @@ def SetItems(self, ks, vs): self.AppendItems(ks) self.Select(0) self.ks, self.vs = ks, vs + + def SetValue(self, x): + n = self.ks.index(x) if x in self.ks else 0 + self.SetSelection(n) + + def GetValue(self): + return self.ks[self.GetSelection()] + # Overridden from OwnerDrawnComboBox, called to draw each # item in the list def OnDrawItem(self, dc, rect, item, flags): @@ -46,7 +51,6 @@ def OnDrawItem(self, dc, rect, item, flags): bmp = wx.Bitmap.FromBuffer(256,10, memoryview(arr)) dc.DrawBitmap(bmp, r.x, r.y+15) - # Overridden from OwnerDrawnComboBox, called for drawing the # background area of each item. def OnDrawBackground(self, dc, rect, item, flags): @@ -57,10 +61,6 @@ def OnDrawBackground(self, dc, rect, item, flags): wx.adv.OwnerDrawnComboBox.OnDrawBackground(self, dc, rect, item, flags) return - def GetValue(self): - return self.ks[self.GetSelection()] - - # Overridden from OwnerDrawnComboBox, should return the height # needed to display an item in the popup, or -1 for default def OnMeasureItem(self, item): @@ -76,32 +76,23 @@ def OnMeasureItem(self, item): def OnMeasureItemWidth(self, item): return -1; # default - will be measured from text width - def handle_(slef):return - - def set_handle(self, handle=None): - if handle is None: self.handle = self.handle_ - else: self.handle = handle +class CMapSelPanel(wx.Panel): + def __init__( self, parent, title): + wx.Panel.__init__(self, parent) + sizer = wx.BoxSizer(wx.VERTICAL) + lab_title = wx.StaticText( self, wx.ID_ANY, title, + wx.DefaultPosition, wx.DefaultSize) + lab_title.Wrap( -1 ) + sizer.Add( lab_title, 0, wx.ALL, 5 ) + self.ctrl = CMapSelCtrl(self) + sizer.Add( self.ctrl, 0, wx.ALL|wx.EXPAND, 0 ) + self.SetSizer(sizer) + self.GetValue = self.ctrl.GetValue + self.SetValue = self.ctrl.SetValue + self.SetItems = self.ctrl.SetItems + self.ctrl.Bind( wx.EVT_COMBOBOX, self.on_sel) + + def Bind(self, z, f): self.f = f def on_sel(self, event): - print('aaa') - self.handle() - -if __name__ == '__main__': - from glob import glob - import os - import numpy as np - filenames = glob('../../data/luts/*.lut') - keys = [os.path.split(filename)[-1][:-4] for filename in filenames] - values = [np.fromfile(filename, dtype=np.uint8).reshape((3,256)).T.copy() for filename in filenames] - lut = dict(zip(keys, values)) - - app = wx.PySimpleApp() - frame = wx.Frame(None) - panel = wx.Panel(frame) - pscb = CMapSelPanel(panel, choices=[], style=wx.CB_READONLY, - pos=(20,40), size=(256, 30)) - pscb.SetItems(keys, values) - panel.Fit() - frame.Fit() - frame.Show(True) - app.MainLoop() \ No newline at end of file + self.f(event) \ No newline at end of file diff --git a/imagepy/ui/widgets/normal.py b/imagepy/ui/widgets/normal.py index 926b8391..97a96a5b 100644 --- a/imagepy/ui/widgets/normal.py +++ b/imagepy/ui/widgets/normal.py @@ -238,7 +238,7 @@ def __init__( self, parent, choices, title): wx.Panel.__init__(self, parent) sizer = wx.BoxSizer(wx.VERTICAL) - self.prefix = lab_title = wx.StaticText( self, wx.ID_ANY, title, + lab_title = wx.StaticText( self, wx.ID_ANY, title, wx.DefaultPosition, wx.DefaultSize) lab_title.Wrap( -1 ) sizer.Add( lab_title, 0, wx.ALL, 5 ) diff --git a/imagepy/widgets/histogram/histogram_wgt.py b/imagepy/widgets/histogram/histogram_wgt.py index 3d87bfa5..08951d03 100644 --- a/imagepy/widgets/histogram/histogram_wgt.py +++ b/imagepy/widgets/histogram/histogram_wgt.py @@ -1,4 +1,4 @@ -from ...ui.widgets import HistCanvas, CMapPanel, CMapSelPanel, FloatSlider +from ...ui.widgets import HistCanvas, CMapPanel, CMapSelCtrl, FloatSlider from imagepy.core.manager import ColorManager from imagepy import IPy import numpy as np @@ -58,9 +58,8 @@ def __init__( self, parent ): #bSizer1.Add( line, 0, wx.EXPAND |wx.ALL, 5 ) #bSizer1.Add( txtlut, 0, wx.EXPAND |wx.ALL, 5 ) - self.cmapsel = CMapSelPanel(self) - luts = ColorManager.luts - self.cmapsel.SetItems(list(luts.keys()), list(luts.values())) + self.cmapsel = CMapSelCtrl(self) + self.cmapsel.SetItems(ColorManager.luts) bSizer1.Add(self.cmapsel, 0, wx.ALL|wx.EXPAND, 5 ) self.cmap = CMapPanel(self) @@ -78,8 +77,7 @@ def __init__( self, parent ): self.btn_slice.Bind( wx.EVT_BUTTON, self.on_slice ) self.btn_stack.Bind( wx.EVT_BUTTON, self.on_stack ) self.cmap.set_handle(self.on_cmap) - self.cmapsel.set_handle(self.on_cmapsel) - + self.cmapsel.Bind(wx.EVT_COMBOBOX, self.on_cmapsel) self.range = (0, 255) def on_cmap(self): @@ -89,7 +87,7 @@ def on_cmap(self): ips.lut = cmap ips.update = 'pix' - def on_cmapsel(self): + def on_cmapsel(self, event): ips = IPy.get_ips() if ips is None: return key = self.cmapsel.GetValue() From 0097db50724386228172134724ddd3eccf6cde40 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 25 Sep 2018 23:50:21 +0800 Subject: [PATCH 030/343] use wxagg --- imagepy/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/imagepy/__init__.py b/imagepy/__init__.py index a4516923..20250027 100644 --- a/imagepy/__init__.py +++ b/imagepy/__init__.py @@ -5,6 +5,9 @@ from .IPy import * root_dir = osp.abspath(osp.dirname(__file__)) os.chdir(root_dir) + +import matplotlib +matplotlib.use('WxAgg') # sys.path.append(root_dir) from .ui.mainframe import ImagePy From c095ecd6d77ccc0acecb81f5e5f2919eb9718929 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 26 Sep 2018 00:12:35 +0800 Subject: [PATCH 031/343] pie chart --- imagepy/menus/Table/Chart/plot_plgs.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/imagepy/menus/Table/Chart/plot_plgs.py b/imagepy/menus/Table/Chart/plot_plgs.py index 23b6bdd5..0dcd8c73 100644 --- a/imagepy/menus/Table/Chart/plot_plgs.py +++ b/imagepy/menus/Table/Chart/plot_plgs.py @@ -15,7 +15,7 @@ class Plot(Table): def run(self, tps, data, snap, para = None): data[para['cn']].plot(lw=para['lw'], grid=para['grid'], title=para['title']) - plt.show() + plt.show(block=False) class Bar(Table): title = 'Bar Chart' @@ -107,4 +107,15 @@ def run(self, tps, data, snap, para = None): cmap=cm, grid=para['grid'], title=para['title']) plt.show() -plgs = [Plot, Bar, Hist, Box, Area, Scatter] \ No newline at end of file +class Pie(Table): + title = 'Pie Chart' + para = {'cn':[], 'title':''} + asyn = False + view = [(str, 'title', 'title', ''), + ('fields', 'cn', 'select fields')] + + def run(self, tps, data, snap, para = None): + data[para['cn']].plot.pie(subplots=True, title=para['title']) + plt.show(block=False) + +plgs = [Plot, Area, Bar, Box, Hist, Pie, Scatter] \ No newline at end of file From f2dde57b6c326f7cdc2824745d9bc4528165f7cd Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 26 Sep 2018 00:39:59 +0800 Subject: [PATCH 032/343] chart ok --- imagepy/menus/Table/Chart/plot_plgs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imagepy/menus/Table/Chart/plot_plgs.py b/imagepy/menus/Table/Chart/plot_plgs.py index 0dcd8c73..6afc0492 100644 --- a/imagepy/menus/Table/Chart/plot_plgs.py +++ b/imagepy/menus/Table/Chart/plot_plgs.py @@ -15,7 +15,7 @@ class Plot(Table): def run(self, tps, data, snap, para = None): data[para['cn']].plot(lw=para['lw'], grid=para['grid'], title=para['title']) - plt.show(block=False) + plt.show() class Bar(Table): title = 'Bar Chart' @@ -116,6 +116,6 @@ class Pie(Table): def run(self, tps, data, snap, para = None): data[para['cn']].plot.pie(subplots=True, title=para['title']) - plt.show(block=False) + plt.show() plgs = [Plot, Area, Bar, Box, Hist, Pie, Scatter] \ No newline at end of file From f5d40bdc44ff0695dfeabb13f86b1dbab97faf32 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 27 Sep 2018 00:56:05 +0800 Subject: [PATCH 033/343] ddd --- imagepy/menus/Table/Statistic/__init__.py | 18 +-------- imagepy/menus/Table/Statistic/sort_plg.py | 3 +- .../menus/Table/Statistic/statistic_plgs.py | 39 ++++++++++++++++++- 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/imagepy/menus/Table/Statistic/__init__.py b/imagepy/menus/Table/Statistic/__init__.py index 24715d13..fa4b62c1 100644 --- a/imagepy/menus/Table/Statistic/__init__.py +++ b/imagepy/menus/Table/Statistic/__init__.py @@ -1,17 +1 @@ -from imagepy.core.engine import Table -from imagepy import IPy - -class Transpose(Table): - title = 'Table Statistic' - para = {'sum':True, 'mean':True, 'max':False, 'min':False, 'std':False, 'var':False, 'only':True} - para = [(bool, 'only', 'only number columns'), - ('lab', None, '========= indecate ========='), - (bool, 'sum', 'sum'), - (bool, 'mean', 'mean'), - (bool, 'max', 'max'), - (bool, 'min', 'min'), - (bool, 'std', 'std'), - (bool, 'var', 'var')] - - def run(self, tps, data, para = None): - pass \ No newline at end of file +catlog = ['statistic_plgs', '-', 'sort_plg'] \ No newline at end of file diff --git a/imagepy/menus/Table/Statistic/sort_plg.py b/imagepy/menus/Table/Statistic/sort_plg.py index 54f3cbf3..6e69e9b9 100644 --- a/imagepy/menus/Table/Statistic/sort_plg.py +++ b/imagepy/menus/Table/Statistic/sort_plg.py @@ -12,7 +12,8 @@ class Sort(Table): (bool, 'descend', 'descend')] def run(self, tps, data, snap, para=None): - tps.data.sort_values(by=[para['major'], para['minor']], + by = [para['major'], para['minor']] + tps.data.sort_values(by=[i for i in by if i != 'None'], axis=0, ascending=not para['descend'], inplace=True) plgs = [Sort] \ No newline at end of file diff --git a/imagepy/menus/Table/Statistic/statistic_plgs.py b/imagepy/menus/Table/Statistic/statistic_plgs.py index 7d0a2d8e..1c79f1a0 100644 --- a/imagepy/menus/Table/Statistic/statistic_plgs.py +++ b/imagepy/menus/Table/Statistic/statistic_plgs.py @@ -31,4 +31,41 @@ def run(self, tps, data, snap, para=None): if para['kurt']:rst['kurt'] = snap.kurt(axis=axis) IPy.show_table(pd.DataFrame(rst), tps.title+'-statistic') -plgs = [Statistic] \ No newline at end of file +class GroupStatistic(Table): + title = 'Group Statistic' + + para = {'major':None, 'minor':None, 'sum':True, 'mean':True,'max':False, + 'min':False,'var':False,'std':False,'skew':False,'kurt':False, 'cn':[]} + + view = [('fields', 'cn', 'field to statistic'), + ('field', 'major', 'group by', 'major'), + ('field', 'minor', 'group by', 'key'), + + (bool, 'sum', 'sum'), + (bool, 'mean', 'mean'), + (bool, 'max', 'max'), + (bool, 'min', 'min'), + (bool, 'var', 'var'), + (bool, 'std', 'std'), + (bool, 'skew', 'skew')] + + def run(self, tps, data, snap, para=None): + by = [i for i in [para['major'], para['minor']] if i!='None'] + gp = data.groupby(by)[para['cn']] + + rst = [] + def post(a, fix): + a.columns = ['%s-%s'%(i,fix) for i in a.columns] + return a + + if para['sum']:rst.append(post(gp.sum(), 'sum')) + if para['mean']:rst.append(post(gp.mean(), 'mean')) + if para['max']:rst.append(post(gp.max(), 'max')) + if para['min']:rst.append(post(gp.min(), 'min')) + if para['var']:rst.append(post(gp.var(), 'var')) + if para['std']:rst.append(post(gp.std(), 'std')) + if para['skew']:rst.append(post(gp.skew(), 'skew')) + + IPy.show_table(pd.concat(rst, axis=1), tps.title+'-statistic') + +plgs = [Statistic, GroupStatistic] \ No newline at end of file From 30b9212582490e18960f317198b5b51ffe48f9af Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 28 Sep 2018 23:31:22 +0800 Subject: [PATCH 034/343] snake --- imagepy/core/wraper/imageplus.py | 2 - .../Region Analysis/statistic_plgs.py | 2 - imagepy/menus/Process/Segment/__init__.py | 0 imagepy/menus/Process/Segment/active_plgs.py | 141 ++++++++++++++++++ imagepy/menus/Process/__init__.py | 2 +- 5 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 imagepy/menus/Process/Segment/__init__.py create mode 100644 imagepy/menus/Process/Segment/active_plgs.py diff --git a/imagepy/core/wraper/imageplus.py b/imagepy/core/wraper/imageplus.py index 26f76b93..2badb44c 100644 --- a/imagepy/core/wraper/imageplus.py +++ b/imagepy/core/wraper/imageplus.py @@ -39,9 +39,7 @@ def set_imgs(self, imgs): self.scrchanged = True self.snap = None self.imgs = imgs - self.height, self.width = self.size = self.imgs[0].shape[:2] - print(self.height, self.width) self.imgtype = get_img_type(self.imgs) self.channels = 1 if self.imgs[0].ndim==2 else self.imgs[0].shape[2] self.dtype = self.imgs[0].dtype diff --git a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py index 69816a80..32a73787 100644 --- a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py @@ -96,9 +96,7 @@ def run(self, ips, imgs, para = None): boxs = [None] * n if para['extent']: boxs = ndimage.find_objects(buf) - print("#####",boxs) boxs = [( i[1].start+(i[1].stop-i[1].start)/2, i[0].start+(i[0].stop-i[0].start)/2, i[1].stop-i[1].start,i[0].stop-i[0].start) for i in boxs] - print(boxs) for j in (0,1,2,3): dt.append([i[j]*k for i in boxs]) if para['max']:dt.append(ndimage.maximum(imgs[i], buf, index).round(2)) diff --git a/imagepy/menus/Process/Segment/__init__.py b/imagepy/menus/Process/Segment/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/menus/Process/Segment/active_plgs.py b/imagepy/menus/Process/Segment/active_plgs.py new file mode 100644 index 00000000..9c40ea7b --- /dev/null +++ b/imagepy/menus/Process/Segment/active_plgs.py @@ -0,0 +1,141 @@ +import numpy as np +from scipy.ndimage import binary_dilation +from imagepy.core.engine import Filter +from imagepy import IPy +from skimage import img_as_float +from skimage.segmentation import chan_vese, morphological_chan_vese, \ + morphological_geodesic_active_contour, inverse_gaussian_gradient, \ + clear_border, random_walker + +class ChanVese(Filter): + title = 'Evolving Level Set' + note = ['all', 'not_slice', 'auto_snap', 'preview'] + + para = {'mu':0.25, 'lambda1':1.0, 'lambda2':1.0, 'tol':0.001, 'max_iter':500, + 'dt':0.5, 'init_level_set':'checkerboard', 'extended_output':False, 'out':'mask'} + + view = [(float, 'mu', (0,1), 2, 'mu', ''), + (float, 'lambda1', (0,10), 2, 'lambda1', ''), + (float, 'lambda2', (0,10), 2, 'lambda2', ''), + (float, 'tol', (0,1), 3, 'tol', ''), + (int, 'max_iter', (0,512), 0, 'max_iter', 'times'), + (float, 'dt', (0,10), 2, 'tol', ''), + (list, 'init_level_set', ['checkerboard', 'disk', 'small disk'], str, 'init', ''), + (list, 'out', ['mask', 'line on ori'], str, 'output', '')] + + def run(self, ips, snap, img, para = None): + msk = chan_vese(snap, mu=para['mu'], lambda1=para['lambda1'], lambda2=para['lambda2'], + tol=para['tol'], max_iter=para['max_iter'], dt=para['dt'], init_level_set=para['init_level_set']) + (c1, c2), img[:] = ips.range, snap + if para['out'] == 'mask': img[~msk], img[msk] = c1, c2 + else: img[binary_dilation(msk) ^ msk] = c2 + +class MorphChanVese(Filter): + title = 'Morphological Snake Fit' + note = ['all', 'not_slice', 'auto_snap', 'preview'] + + para = {'iter':10, 'init':'circle', 'smooth':1, 'lambda1':1.0, 'lambda2':1.0, 'out':'mask'} + + view = [(int, 'iter', (0,64), 0, 'iterations', 'time'), + (list, 'init', ['checkerboard', 'circle'], str, 'init set', ''), + (int, 'smooth', (1, 4), 0, 'smoothing', ''), + (float, 'lambda1', (0,10), 2, 'lambda1', ''), + (float, 'lambda2', (0,10), 2, 'lambda2', ''), + (list, 'out', ['mask', 'line on ori'], str, 'output', '')] + + def run(self, ips, snap, img, para = None): + msk = morphological_chan_vese(snap, para['iter'], init_level_set=para['init'], + smoothing=para['smooth'], lambda1=para['lambda1'], lambda2=para['lambda2']) > 0 + (c1, c2), img[:] = ips.range, snap + if para['out'] == 'mask': img[~msk], img[msk] = c1, c2 + else: img[binary_dilation(msk) ^ msk] = c2 + +class MorphGeoChanVese(Filter): + title = 'Bound Snake Fit' + note = ['all', 'not_slice', 'auto_snap', 'preview'] + + para = {'iter':10, 'smooth':1, 'thr':128, 'auto':True, 'balloon':0, 'out':'mask'} + + view = [(int, 'iter', (0, 1024), 0, 'iterations', 'time'), + (int, 'smooth', (1, 4), 0, 'smoothing', ''), + (float, 'thr', (0,1e5), 2, 'threshold', ''), + (bool, 'auto', 'auto threshold'), + (float, 'balloon', (-5, 5), 1, 'balloon', ''), + (list, 'out', ['mask', 'line on ori'], str, 'output', '')] + + def run(self, ips, snap, img, para = None): + gimage = inverse_gaussian_gradient(img_as_float(snap)) + init = np.ones(img.shape, dtype=np.bool) + msk = morphological_geodesic_active_contour(gimage, para['iter'], + init_level_set=init, smoothing=para['smooth'], + threshold='auto' if para['auto'] else para['thr'], balloon=para['balloon']) > 0 + (c1, c2), img[:] = ips.range, snap + if para['out'] == 'mask': img[~msk], img[msk] = c1, c2 + else: img[binary_dilation(msk) ^ msk] = c2 + +class MorphGeoRoi(Filter): + title = 'ROI Snake Fit' + note = ['all', 'not_slice', 'auto_snap', 'req_roi', 'preview'] + + para = {'iter':10, 'smooth':1, 'thr':128, 'auto':True, 'balloon':0, 'out':'mask'} + + view = [(int, 'iter', (0, 1024), 0, 'iterations', 'time'), + (int, 'smooth', (1, 4), 0, 'smoothing', ''), + (float, 'thr', (0,1e5), 2, 'threshold', ''), + (bool, 'auto', 'auto threshold'), + (float, 'balloon', (-5, 5), 1, 'balloon', ''), + (list, 'out', ['mask', 'line on ori'], str, 'output', '')] + + def run(self, ips, snap, img, para = None): + gimage = inverse_gaussian_gradient(img_as_float(snap)) + init = ips.get_msk('out') + msk = morphological_geodesic_active_contour(gimage, para['iter'], + init_level_set=init, smoothing=para['smooth'], + threshold='auto' if para['auto'] else para['thr'], balloon=para['balloon']) > 0 + (c1, c2), img[:] = ips.range, snap + if para['out'] == 'mask': img[~msk], img[msk] = c1, c2 + else: img[binary_dilation(msk) ^ msk] = c2 + +class RandomWalker(Filter): + title = 'Random Walker' + note = ['all', 'auto_msk', 'auto_snap', 'preview'] + modal = False + + def load(self, ips): + minv, maxv = ips.range + self.para = {'beta':130, 'mode':'bf', 'tol':0.001, 'thr1':minv, 'thr2':maxv, 'out':'mask'} + self.view = [('slide', 'thr1', (minv, maxv), 4, 'Low'), + ('slide', 'thr2', (minv,maxv), 4, 'High'), + (int, 'beta', (10,256), 0, 'beta', ''), + (list, 'mode', ['cg_mg', 'cg', 'bf'], str, 'mode', ''), + (float, 'tol', (0,1), 3, 'tolerance', ''), + (list, 'out', ['mask', 'line on ori'], str, 'output', '')] + + self.buflut = ips.lut + ips.lut = ips.lut.copy() + return True + + def cancel(self, ips): + ips.lut = self.buflut + ips.update = 'pix' + + def preview(self, ips, para): + ips.lut[:] = self.buflut + minv, maxv = ips.range + lim1 = (para['thr1']-minv)*255/(maxv-minv) + lim2 = (para['thr2']-minv)*255/(maxv-minv) + ips.lut[:int(lim1)] = [0,255,0] + ips.lut[int(lim2):] = [255,0,0] + ips.update = 'pix' + + #process + def run(self, ips, snap, img, para = None): + msk = np.zeros_like(img) + msk[img>para['thr2']] = 1 + msk[img Date: Sat, 29 Sep 2018 13:44:49 +0800 Subject: [PATCH 035/343] plot bug --- imagepy/menus/Table/Chart/plot_plgs.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/imagepy/menus/Table/Chart/plot_plgs.py b/imagepy/menus/Table/Chart/plot_plgs.py index 6afc0492..e43412a5 100644 --- a/imagepy/menus/Table/Chart/plot_plgs.py +++ b/imagepy/menus/Table/Chart/plot_plgs.py @@ -48,9 +48,12 @@ class Hist(Table): (bool, 'grid', 'grid')] def run(self, tps, data, snap, para = None): - f = [data[para['cn']].hist, data[para['cn']].plot.hist][para['overlay']] - f(stacked=para['stack'], bins=para['bins'], alpha=para['alpha'], - orientation=para['dir'], grid=para['grid'], title=para['title']) + if para['overlay']: + data[para['cn']].plot.hist(stacked=para['stack'], bins=para['bins'], alpha=para['alpha'], + orientation=para['dir'], grid=para['grid'], title=para['title']) + else: + data[para['cn']].hist(stacked=para['stack'], bins=para['bins'], alpha=para['alpha'], + orientation=para['dir'], grid=para['grid']) plt.show() class Box(Table): From 7c3ad4f72d15f85f58e92b89e5f663bcaaf64de1 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 13 Oct 2018 01:51:59 +0800 Subject: [PATCH 036/343] sub stack --- imagepy/menus/Process/Filters/classic_plgs.py | 2 - imagepy/menus/Process/Segment/active_plgs.py | 69 +++++++++++-- imagepy/menus/Process/Threshold/threshold.py | 99 +++++++++++++++++++ .../menus/Process/Threshold/threshold2_plg.py | 35 ------- imagepy/ui/mainframe.py | 1 - 5 files changed, 158 insertions(+), 48 deletions(-) create mode 100644 imagepy/menus/Process/Threshold/threshold.py delete mode 100644 imagepy/menus/Process/Threshold/threshold2_plg.py diff --git a/imagepy/menus/Process/Filters/classic_plgs.py b/imagepy/menus/Process/Filters/classic_plgs.py index dc5a3f73..0adb75ff 100644 --- a/imagepy/menus/Process/Filters/classic_plgs.py +++ b/imagepy/menus/Process/Filters/classic_plgs.py @@ -23,8 +23,6 @@ class Gaussian(Filter): view = [(float, 'sigma', (0,30), 1, 'sigma', 'pix')] def run(self, ips, snap, img, para = None): - #l = int(para['sigma']*3)*2+1 - #cv2.GaussianBlur(snap, (l, l), para['sigma'], dst=img) nimg.gaussian_filter(snap, para['sigma'], output=img) class GaussianLaplace(Filter): diff --git a/imagepy/menus/Process/Segment/active_plgs.py b/imagepy/menus/Process/Segment/active_plgs.py index 9c40ea7b..c8f80114 100644 --- a/imagepy/menus/Process/Segment/active_plgs.py +++ b/imagepy/menus/Process/Segment/active_plgs.py @@ -19,7 +19,7 @@ class ChanVese(Filter): (float, 'lambda2', (0,10), 2, 'lambda2', ''), (float, 'tol', (0,1), 3, 'tol', ''), (int, 'max_iter', (0,512), 0, 'max_iter', 'times'), - (float, 'dt', (0,10), 2, 'tol', ''), + (float, 'dt', (0,10), 2, 'dt', ''), (list, 'init_level_set', ['checkerboard', 'disk', 'small disk'], str, 'init', ''), (list, 'out', ['mask', 'line on ori'], str, 'output', '')] @@ -34,36 +34,53 @@ class MorphChanVese(Filter): title = 'Morphological Snake Fit' note = ['all', 'not_slice', 'auto_snap', 'preview'] - para = {'iter':10, 'init':'circle', 'smooth':1, 'lambda1':1.0, 'lambda2':1.0, 'out':'mask'} + para = {'iter':10, 'init':'checkerboard', 'smooth':1, 'lambda1':1.0, + 'lambda2':1.0, 'out':'mask', 'sub':False} view = [(int, 'iter', (0,64), 0, 'iterations', 'time'), (list, 'init', ['checkerboard', 'circle'], str, 'init set', ''), (int, 'smooth', (1, 4), 0, 'smoothing', ''), (float, 'lambda1', (0,10), 2, 'lambda1', ''), (float, 'lambda2', (0,10), 2, 'lambda2', ''), - (list, 'out', ['mask', 'line on ori'], str, 'output', '')] + (list, 'out', ['mask', 'line on ori'], str, 'output', ''), + (bool, 'sub', 'show sub stack')] - def run(self, ips, snap, img, para = None): + def preview(self, ips, para): + snap, img = ips.snap, ips.img msk = morphological_chan_vese(snap, para['iter'], init_level_set=para['init'], smoothing=para['smooth'], lambda1=para['lambda1'], lambda2=para['lambda2']) > 0 (c1, c2), img[:] = ips.range, snap if para['out'] == 'mask': img[~msk], img[msk] = c1, c2 else: img[binary_dilation(msk) ^ msk] = c2 + ips.update = 'pix' + + def run(self, ips, snap, img, para = None): + stackimg = [] + callback = lambda x: stackimg.append((x*255).astype(np.uint8)) if para['sub'] else 0 + msk = morphological_chan_vese(snap, para['iter'], init_level_set=para['init'], + smoothing=para['smooth'], lambda1=para['lambda1'], lambda2=para['lambda2'], + iter_callback=callback) > 0 + (c1, c2), img[:] = ips.range, snap + if para['out'] == 'mask': img[~msk], img[msk] = c1, c2 + else: img[binary_dilation(msk) ^ msk] = c2 + if para['sub']: IPy.show_img(stackimg, ips.title+'-sub') class MorphGeoChanVese(Filter): title = 'Bound Snake Fit' note = ['all', 'not_slice', 'auto_snap', 'preview'] - para = {'iter':10, 'smooth':1, 'thr':128, 'auto':True, 'balloon':0, 'out':'mask'} + para = {'iter':10, 'smooth':1, 'thr':128, 'auto':True, 'balloon':-1, 'out':'mask', 'sub':False} view = [(int, 'iter', (0, 1024), 0, 'iterations', 'time'), (int, 'smooth', (1, 4), 0, 'smoothing', ''), (float, 'thr', (0,1e5), 2, 'threshold', ''), (bool, 'auto', 'auto threshold'), (float, 'balloon', (-5, 5), 1, 'balloon', ''), - (list, 'out', ['mask', 'line on ori'], str, 'output', '')] + (list, 'out', ['mask', 'line on ori'], str, 'output', ''), + (bool, 'sub', 'show sub stack')] - def run(self, ips, snap, img, para = None): + def preview(self, ips, para): + snap, img = ips.snap, ips.img gimage = inverse_gaussian_gradient(img_as_float(snap)) init = np.ones(img.shape, dtype=np.bool) msk = morphological_geodesic_active_contour(gimage, para['iter'], @@ -72,21 +89,38 @@ def run(self, ips, snap, img, para = None): (c1, c2), img[:] = ips.range, snap if para['out'] == 'mask': img[~msk], img[msk] = c1, c2 else: img[binary_dilation(msk) ^ msk] = c2 + ips.update = 'pix' + + def run(self, ips, snap, img, para = None): + stackimg = [] + callback = lambda x: stackimg.append((x*255).astype(np.uint8)) if para['sub'] else 0 + gimage = inverse_gaussian_gradient(img_as_float(snap)) + init = np.ones(img.shape, dtype=np.bool) + msk = morphological_geodesic_active_contour(gimage, para['iter'], + init_level_set=init, smoothing=para['smooth'], + threshold='auto' if para['auto'] else para['thr'], + balloon=para['balloon'], iter_callback=callback) > 0 + (c1, c2), img[:] = ips.range, snap + if para['out'] == 'mask': img[~msk], img[msk] = c1, c2 + else: img[binary_dilation(msk) ^ msk] = c2 + if para['sub']: IPy.show_img(stackimg, ips.title+'-sub') class MorphGeoRoi(Filter): title = 'ROI Snake Fit' note = ['all', 'not_slice', 'auto_snap', 'req_roi', 'preview'] - para = {'iter':10, 'smooth':1, 'thr':128, 'auto':True, 'balloon':0, 'out':'mask'} + para = {'iter':10, 'smooth':1, 'thr':128, 'auto':True, 'balloon':-1, 'out':'mask', 'sub':False} view = [(int, 'iter', (0, 1024), 0, 'iterations', 'time'), (int, 'smooth', (1, 4), 0, 'smoothing', ''), (float, 'thr', (0,1e5), 2, 'threshold', ''), (bool, 'auto', 'auto threshold'), (float, 'balloon', (-5, 5), 1, 'balloon', ''), - (list, 'out', ['mask', 'line on ori'], str, 'output', '')] + (list, 'out', ['mask', 'line on ori'], str, 'output', ''), + (bool, 'sub', 'show sub stack')] - def run(self, ips, snap, img, para = None): + def preview(self, ips, para): + snap, img = ips.snap, ips.img gimage = inverse_gaussian_gradient(img_as_float(snap)) init = ips.get_msk('out') msk = morphological_geodesic_active_contour(gimage, para['iter'], @@ -95,6 +129,21 @@ def run(self, ips, snap, img, para = None): (c1, c2), img[:] = ips.range, snap if para['out'] == 'mask': img[~msk], img[msk] = c1, c2 else: img[binary_dilation(msk) ^ msk] = c2 + ips.update = 'pix' + + def run(self, ips, snap, img, para = None): + stackimg = [] + callback = lambda x: stackimg.append((x*255).astype(np.uint8)) if para['sub'] else 0 + gimage = inverse_gaussian_gradient(img_as_float(snap)) + init = ips.get_msk('out') + msk = morphological_geodesic_active_contour(gimage, para['iter'], + init_level_set=init, smoothing=para['smooth'], + threshold='auto' if para['auto'] else para['thr'], + balloon=para['balloon'], iter_callback=callback) > 0 + (c1, c2), img[:] = ips.range, snap + if para['out'] == 'mask': img[~msk], img[msk] = c1, c2 + else: img[binary_dilation(msk) ^ msk] = c2 + if para['sub']: IPy.show_img(stackimg, ips.title+'-sub') class RandomWalker(Filter): title = 'Random Walker' diff --git a/imagepy/menus/Process/Threshold/threshold.py b/imagepy/menus/Process/Threshold/threshold.py new file mode 100644 index 00000000..1adb603d --- /dev/null +++ b/imagepy/menus/Process/Threshold/threshold.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Nov 18 22:56:50 2016 +@author: yxl +""" +from imagepy import IPy +import numpy as np +from imagepy.core.engine import Filter +import scipy.ndimage as ndimg +from skimage.filters import\ + threshold_adaptive, threshold_otsu, threshold_yen,\ + threshold_isodata, threshold_li, threshold_local,\ + threshold_minimum, threshold_mean, threshold_niblack,\ + threshold_sauvola, threshold_triangle, apply_hysteresis_threshold +from ...Image.Adjust.threshold_plg import Plugin as ThresholdPlg + +class SimpleThreshold(ThresholdPlg): + title = 'Simple Threshold' + +class Hysteresis(Filter): + title = 'Hysteresis Threshold' + note = ['all', 'auto_msk', 'auto_snap', 'preview'] + modal = False + + def load(self, ips): + minv, maxv = ips.range + self.para = {'low':maxv, 'high':maxv} + self.view = [('slide', 'low', (minv, maxv), 4, 'Low'), + ('slide', 'high', (minv, maxv), 4, 'High')] + + self.buflut = ips.lut + ips.lut = ips.lut.copy() + return True + + def cancel(self, ips): + ips.lut = self.buflut + ips.update = 'pix' + + def preview(self, ips, para): + ips.lut[:] = self.buflut + minv, maxv = ips.range + lim1 = (para['low']-minv)*255/(maxv-minv) + lim2 = (para['high']-minv)*255/(maxv-minv) + ips.lut[int(lim1):] = [0,255,0] + ips.lut[int(lim2):] = [255,0,0] + ips.update = 'pix' + + def run(self, ips, snap, img, para = None): + ips.lut = self.buflut + return apply_hysteresis_threshold(snap, para['low'], para['high'])*ips.range[1] + +class Auto(Filter): + title = 'Auto Threshold' + note = ['all', 'auto_msk', 'auto_snap', 'preview'] + para = {'method':'otus'} + view = [(list, 'method', ['Otus', 'Yen', 'Isodata', 'Li', + 'Mini', 'Mean', 'Triangle'], str, 'Method', '')] + + def run(self, ips, snap, img, para = None): + key = {'Otus':threshold_otsu, 'Yen':threshold_yen, + 'Isodata':threshold_isodata, 'Li':threshold_li, + 'Mini':threshold_minimum, 'Mean':threshold_mean, + 'Triangle':threshold_triangle} + img[:] = (snap>key[para['method']](snap))*ips.range[1] + +class Local(Filter): + title = 'Adaptive Threshold' + note = ['all', 'auto_msk', 'auto_snap', 'preview'] + para = {'method':'mean', 'size':9, 'offset':2} + view = [(list, 'method', ['Gaussian', 'Mean', 'Median'], str, 'method', ''), + (int, 'size', (3, 31), 0, 'blocksize', 'pix'), + (int, 'offset', (0, 50), 0, 'offset', '')] + + def run(self, ips, snap, img, para = None): + img[:] = (snap>threshold_local(snap, para['size'], para['method'], para['offset']))*ips.range[1] + +class Niblack(Filter): + title = 'Niblack Threshold' + note = ['all', 'auto_msk', 'auto_snap', 'preview'] + para = {'size':15, 'k':0.2} + view = [(int, 'size', (3, 30), 0, 'blocksize', 'pix'), + (float, 'k', (0, 1), 2, 'offset', '')] + + def run(self, ips, snap, img, para = None): + if para['size']%2==0: return IPy.alert('size must be Odd') + img[:] = (snap>threshold_niblack(snap, para['size'], para['k']))*ips.range[1] + +class Sauvola(Filter): + title = 'Sauvola Threshold' + note = ['all', 'auto_msk', 'auto_snap', 'preview'] + para = {'size':15, 'k':0.2} + view = [(int, 'size', (3, 30), 0, 'blocksize', 'pix'), + (float, 'k', (0, 1), 2, 'offset', '')] + + def run(self, ips, snap, img, para = None): + if para['size']%2==0: return IPy.alert('size must be Odd') + img[:] = (snap>threshold_sauvola(snap, para['size'], para['k']))*ips.range[1] + +plgs = [SimpleThreshold, Auto, '-', Local, Niblack, Sauvola, '-', Hysteresis] \ No newline at end of file diff --git a/imagepy/menus/Process/Threshold/threshold2_plg.py b/imagepy/menus/Process/Threshold/threshold2_plg.py deleted file mode 100644 index 6fcda5d6..00000000 --- a/imagepy/menus/Process/Threshold/threshold2_plg.py +++ /dev/null @@ -1,35 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Nov 18 22:56:50 2016 -@author: yxl -""" -from imagepy import IPy -import numpy as np -from imagepy.core.engine import Filter -import scipy.ndimage as ndimg - -class Plugin(Filter): - title = 'Double Threshold' - note = ['8-bit', 'auto_msk', 'auto_snap', 'preview'] - - para = {'thr1':255, 'thr2':255} - view = [('slide', 'thr1', (0,255), 0, 'Low'), - ('slide', 'thr2', (0,255), 0, 'High')] - - def load(self, ips): - self.buflut = ips.lut - ips.lut = ips.lut.copy() - return True - - def preview(self, ips, para): - ips.lut[:] = self.buflut - ips.lut[para['thr2']:] = [0,255,0] - ips.lut[para['thr1']:] = [255,0,0] - ips.update = 'pix' - - #process - def run(self, ips, snap, img, para = None): - ips.lut = self.buflut - lab, n = ndimg.label(snap>para['thr2'], np.ones((3,3)), output=np.uint16) - sta = ndimg.sum(snap>para['thr1'], lab, index=range(n+1)) > 0 - img[:] = (sta*255)[lab] diff --git a/imagepy/ui/mainframe.py b/imagepy/ui/mainframe.py index 5fb74410..2efec3aa 100644 --- a/imagepy/ui/mainframe.py +++ b/imagepy/ui/mainframe.py @@ -27,7 +27,6 @@ def __init__( self, parent ): wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = 'ImagePy', size = wx.Size(-1,-1), pos = wx.DefaultPosition, style = wx.RESIZE_BORDER|wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) - self.auimgr = aui.AuiManager() self.auimgr.SetManagedWindow( self ) self.auimgr.SetFlags(aui.AUI_MGR_DEFAULT) From 518077fed2d6f624262a654342da5b822447f117 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 13 Oct 2018 02:48:51 +0800 Subject: [PATCH 037/343] snake --- .../menus/Process/Threshold/{threshold.py => threshold_plgs.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename imagepy/menus/Process/Threshold/{threshold.py => threshold_plgs.py} (100%) diff --git a/imagepy/menus/Process/Threshold/threshold.py b/imagepy/menus/Process/Threshold/threshold_plgs.py similarity index 100% rename from imagepy/menus/Process/Threshold/threshold.py rename to imagepy/menus/Process/Threshold/threshold_plgs.py From 9d4416b22d69d6380327382f48137cbbbe187943 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 19 Oct 2018 23:47:32 +0800 Subject: [PATCH 038/343] help --- imagepy/core/engine/filter.py | 3 +++ imagepy/core/engine/free.py | 2 ++ imagepy/core/engine/simple.py | 2 ++ imagepy/core/engine/table.py | 2 ++ .../Region Analysis/statistic_plgs.py | 2 +- imagepy/menus/Analysis/statistic_plg.py | 4 ++-- imagepy/menus/Process/Filters/classic_plgs.py | 2 +- .../menus/Process/Threshold/threshold_plgs.py | 4 ++-- imagepy/ui/mkdownwindow.py | 1 + imagepy/ui/panelconfig.py | 20 ++++++++++++------- 10 files changed, 29 insertions(+), 13 deletions(-) diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index b41a7e73..5e3c4ed4 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -100,6 +100,9 @@ def progress(self, i, n): def show(self, temp=ParaDialog): self.dialog = temp(WindowsManager.get(), self.title) self.dialog.init_view(self.view, self.para, 'preview' in self.note, modal=self.modal) + + doc = self.__doc__ or '### Sorry\nNo document yet!' + self.dialog.on_help = lambda : IPy.show_md(self.title, doc) self.dialog.set_handle(lambda x:self.preview(self.ips, x)) if self.modal: return self.dialog.ShowModal() == wx.ID_OK self.dialog.on_ok = lambda : self.ok(self.ips) diff --git a/imagepy/core/engine/free.py b/imagepy/core/engine/free.py index b2a9fdd5..bfa1b432 100644 --- a/imagepy/core/engine/free.py +++ b/imagepy/core/engine/free.py @@ -37,6 +37,8 @@ def show(self): if self.view==None:return True with ParaDialog(WindowsManager.get(), self.title) as dialog: dialog.init_view(self.view, self.para, False, True) + doc = self.__doc__ or '### Sorry\nNo document yet!' + dialog.on_help = lambda : IPy.show_md(self.title, doc) return dialog.ShowModal() == wx.ID_OK def start(self, para=None, callback=None): diff --git a/imagepy/core/engine/simple.py b/imagepy/core/engine/simple.py index 8324b126..809aceff 100644 --- a/imagepy/core/engine/simple.py +++ b/imagepy/core/engine/simple.py @@ -36,6 +36,8 @@ def show(self, temp=ParaDialog): if self.view==None:return True self.dialog = temp(IPy.get_window(), self.title) self.dialog.init_view(self.view, self.para, 'preview' in self.note, modal=self.modal) + doc = self.__doc__ or '### Sorry\nNo document yet!' + self.dialog.on_help = lambda : IPy.show_md(self.title, doc) self.dialog.set_handle(lambda x:self.preview(self.ips, self.para)) if self.modal: return self.dialog.ShowModal() == wx.ID_OK self.dialog.on_ok = lambda : self.ok(self.ips) diff --git a/imagepy/core/engine/table.py b/imagepy/core/engine/table.py index 45123dfd..81a565b9 100644 --- a/imagepy/core/engine/table.py +++ b/imagepy/core/engine/table.py @@ -39,6 +39,8 @@ def show(self): if self.view==None:return True self.dialog = ParaDialog(IPy.get_twindow(), self.title) self.dialog.init_view(self.view, self.para, 'preview' in self.note, modal=self.modal) + doc = self.__doc__ or '### Sorry\nNo document yet!' + self.dialog.on_help = lambda : IPy.show_md(self.title, doc) self.dialog.set_handle(lambda x:self.preview(self.tps, self.para)) if self.modal: return self.dialog.ShowModal() == wx.ID_OK self.dialog.on_ok = lambda : self.ok(self.tps) diff --git a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py index 32a73787..3d74a462 100644 --- a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py @@ -146,7 +146,7 @@ class IntensityFilter(Filter): title = 'Intensity Filter' note = ['8-bit', '16-bit', 'auto_msk', 'auto_snap', 'not_slice', 'preview'] para = {'con':'4-connect', 'inten':None, 'max':0, 'min':0, 'mean':0, 'std':0, 'sum':0, 'front':255, 'back':0} - view = [('img', 'intensity', 'inten', ''), + view = [('img', 'inten', 'intensity', ''), (list, 'con', ['4-connect', '8-connect'], str, 'conection', 'pix'), ('lab', None, 'Filter: "+" means >=, "-" means <'), (int, 'front', (0, 255), 0, 'front color', ''), diff --git a/imagepy/menus/Analysis/statistic_plg.py b/imagepy/menus/Analysis/statistic_plg.py index d65031ca..605b5579 100644 --- a/imagepy/menus/Analysis/statistic_plg.py +++ b/imagepy/menus/Analysis/statistic_plg.py @@ -46,7 +46,7 @@ def gray(self, hist): back.Add(panel, 1, wx.EXPAND) sizer = wx.BoxSizer( wx.VERTICAL ) histc = HistCanvas(panel) - histc.set_hist(hist) + histc.SetValue(hist) txt = wx.StaticText( panel, wx.ID_ANY, 'Channel:'+'Gray', wx.DefaultPosition, wx.DefaultSize, 0 ) sizer.Add( txt, 0, wx.LEFT|wx.RIGHT, 8 ) sizer.Add( histc, 0, wx.LEFT|wx.RIGHT, 8 ) @@ -77,7 +77,7 @@ def run(self, ips, imgs, para = None): else: img = ips.lookup() if msk is None else ips.lookup()[msk] hist = np.histogram(img, np.arange(257))[0] - show_hist(WindowsManager.get(), ips.title+'-Histogram', hist) + show_hist(IPy.curapp, ips.title+'-Histogram', hist) class Frequence(Simple): diff --git a/imagepy/menus/Process/Filters/classic_plgs.py b/imagepy/menus/Process/Filters/classic_plgs.py index 0adb75ff..62fcae6e 100644 --- a/imagepy/menus/Process/Filters/classic_plgs.py +++ b/imagepy/menus/Process/Filters/classic_plgs.py @@ -16,7 +16,7 @@ def run(self, ips, snap, img, para = None): nimg.uniform_filter(snap, para['size'], output=img) class Gaussian(Filter): - """Gaussian: derived from imagepy.core.engine.Filter """ + __doc__ = nimg.gaussian_filter.__doc__ title = 'Gaussian' note = ['all', 'auto_msk', 'auto_snap','preview'] para = {'sigma':2} diff --git a/imagepy/menus/Process/Threshold/threshold_plgs.py b/imagepy/menus/Process/Threshold/threshold_plgs.py index 1adb603d..6d42b436 100644 --- a/imagepy/menus/Process/Threshold/threshold_plgs.py +++ b/imagepy/menus/Process/Threshold/threshold_plgs.py @@ -52,8 +52,8 @@ def run(self, ips, snap, img, para = None): class Auto(Filter): title = 'Auto Threshold' note = ['all', 'auto_msk', 'auto_snap', 'preview'] - para = {'method':'otus'} - view = [(list, 'method', ['Otus', 'Yen', 'Isodata', 'Li', + para = {'method':'otsu'} + view = [(list, 'method', ['Otsu', 'Yen', 'Isodata', 'Li', 'Mini', 'Mean', 'Triangle'], str, 'Method', '')] def run(self, ips, snap, img, para = None): diff --git a/imagepy/ui/mkdownwindow.py b/imagepy/ui/mkdownwindow.py index 6642d6ec..e39ca4a8 100644 --- a/imagepy/ui/mkdownwindow.py +++ b/imagepy/ui/mkdownwindow.py @@ -202,4 +202,5 @@ def __init__(self, parent, title, cont, url): wx.Frame.__init__ (self, parent, id = wx.ID_ANY, title = title, size = wx.Size(500,500)) logopath = os.path.join(root_dir, 'data/logo.ico') self.SetIcon(wx.Icon(logopath, wx.BITMAP_TYPE_ICO)) + cont = '\n'.join([i.strip() for i in cont.split('\n')]) HtmlPanel(self, md2html(cont), url) \ No newline at end of file diff --git a/imagepy/ui/panelconfig.py b/imagepy/ui/panelconfig.py index 59d084d6..8c93d504 100644 --- a/imagepy/ui/panelconfig.py +++ b/imagepy/ui/panelconfig.py @@ -17,7 +17,7 @@ def __init__( self, parent, title): self.lst = wx.BoxSizer( wx.VERTICAL ) self.tus = [] - self.on_ok, self.on_cancel = None, None + self.on_ok = self.on_cancel = self.on_help = None self.ctrl_dic = {} boxBack = wx.BoxSizer() boxBack.Add(self.lst, 0, wx.ALL, 10) @@ -31,17 +31,23 @@ def commit(self, state): if state=='cancel' and self.on_cancel:self.on_cancel() def add_confirm(self, modal=True): - self.lst.AddStretchSpacer(1) + # self.lst.AddStretchSpacer(1) sizer = wx.BoxSizer( wx.HORIZONTAL ) - self.btn_OK = wx.Button( self, wx.ID_OK, 'OK') - sizer.Add( self.btn_OK, 0, wx.ALIGN_RIGHT|wx.ALL, 5 ) + self.btn_ok = wx.Button( self, wx.ID_OK, 'OK', wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) + sizer.Add( self.btn_ok, 0, wx.ALIGN_RIGHT|wx.ALL, 5 ) - self.btn_cancel = wx.Button( self, wx.ID_CANCEL, 'Cancel') + self.btn_cancel = wx.Button( self, wx.ID_CANCEL, 'Cancel', wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) sizer.Add( self.btn_cancel, 0, wx.ALIGN_RIGHT|wx.ALL, 5 ) + + self.btn_help = wx.Button( self, wx.ID_HELP, 'Help', wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) + sizer.Add( self.btn_help, 0, wx.ALIGN_RIGHT|wx.ALL, 5 ) self.lst.Add(sizer, 0, wx.ALIGN_RIGHT, 5 ) + self.btn_help.Bind(wx.EVT_BUTTON, lambda e: self.on_help and self.on_help()) if not modal: - self.btn_OK.Bind( wx.EVT_BUTTON, lambda e:self.commit('ok')) + self.btn_ok.Bind( wx.EVT_BUTTON, lambda e:self.commit('ok')) self.btn_cancel.Bind( wx.EVT_BUTTON, lambda e:self.commit('cancel')) + + #self.lst.Add() def init_view(self, items, para, preview=False, modal = True): self.para = para @@ -101,7 +107,7 @@ def para_changed(self, key): if p in self.ctrl_dic: para[p] = self.ctrl_dic[p].GetValue() sta = sum([i is None for i in list(para.values())])==0 - self.btn_OK.Enable(sta) + self.btn_ok.Enable(sta) if not sta: return self.para_check(para, key) if 'preview' not in self.ctrl_dic:return From 6e91d07eb3d3f5b419ee44b0d9eeff4ed056cd87 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 30 Oct 2018 04:03:22 +0800 Subject: [PATCH 039/343] aui --- imagepy/IPy.py | 2 +- imagepy/__init__.py | 19 ++++++++++++- imagepy/core/engine/widget.py | 4 +-- imagepy/data/logo.png | Bin 0 -> 12895 bytes imagepy/data/logolong.png | Bin 0 -> 11801 bytes imagepy/menus/Window/develop_wgts.py | 4 +-- imagepy/menus/Window/widgets_plgs.py | 2 +- imagepy/ui/canvasframe.py | 16 ++++++----- imagepy/ui/mainframe.py | 40 +++++++++++++++++---------- imagepy/ui/tableframe.py | 13 +++++---- 10 files changed, 66 insertions(+), 34 deletions(-) create mode 100644 imagepy/data/logo.png create mode 100644 imagepy/data/logolong.png diff --git a/imagepy/IPy.py b/imagepy/IPy.py index 077f9871..ee90838c 100644 --- a/imagepy/IPy.py +++ b/imagepy/IPy.py @@ -170,7 +170,7 @@ def showtable(data, title): tablep = TablePanel(curapp.tablenb) tablep.set_tps(tps) curapp.tablenb.add_page( tablep, tps) - info = curapp.auimgr.GetPane(curapp.tablenb) + info = curapp.auimgr.GetPane(curapp.tablenbwrap) info.Show(True) curapp.auimgr.Update() elif uimode()=='ij': diff --git a/imagepy/__init__.py b/imagepy/__init__.py index 20250027..8e2da5a5 100644 --- a/imagepy/__init__.py +++ b/imagepy/__init__.py @@ -2,6 +2,9 @@ import os.path as osp import sys, os, wx +from wx.adv import SplashScreen as SplashScreen +import wx.lib.agw.advancedsplash as AS + from .IPy import * root_dir = osp.abspath(osp.dirname(__file__)) os.chdir(root_dir) @@ -14,6 +17,20 @@ def show(ui = True): app = wx.App(False) + + + bitmap = wx.Bitmap('data/logolong.png', wx.BITMAP_TYPE_PNG) + shadow = wx.Colour(255,255,255) + # SplashScreen(bitmap, wx.adv.SPLASH_CENTRE_ON_SCREEN | wx.adv.SPLASH_TIMEOUT, 3000, None, -1) + + asp = AS.AdvancedSplash(None, bitmap=bitmap, timeout=5000, + agwStyle=AS.AS_TIMEOUT | + AS.AS_CENTER_ON_PARENT | + AS.AS_SHADOW_BITMAP, + shadowcolour=shadow) + mainFrame = ImagePy(None) mainFrame.Show() - app.MainLoop() \ No newline at end of file + app.MainLoop() + + \ No newline at end of file diff --git a/imagepy/core/engine/widget.py b/imagepy/core/engine/widget.py index 600bf648..1ee3bafc 100644 --- a/imagepy/core/engine/widget.py +++ b/imagepy/core/engine/widget.py @@ -3,7 +3,7 @@ Created on Sat Dec 3 03:57:53 2016 @author: yxl """ -import threading, wx, os +import threading, wx, os, wx.lib.agw.aui as aui from imagepy import IPy, root_dir from ..manager import WidgetsManager @@ -18,7 +18,7 @@ def start(self): if not WidgetsManager.getref(self.title) is None: return pan = self.pan(IPy.curapp) WidgetsManager.addref(pan) - IPy.curapp.auimgr.AddPane(pan, wx.aui.AuiPaneInfo(). DestroyOnClose(True). Left(). Caption(self.title) .PinButton( True ) + IPy.curapp.auimgr.AddPane(pan, aui.AuiPaneInfo(). DestroyOnClose(True). Left(). Caption(self.title) .PinButton( True ) .Float().Resizable().FloatingSize( wx.DefaultSize ).Dockable(IPy.uimode()=='ipy').Layer( 15 ) ) IPy.curapp.Layout() IPy.curapp.auimgr.Update() diff --git a/imagepy/data/logo.png b/imagepy/data/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d7939e421a78ac1d1c32f6d26983fd9564a83eb2 GIT binary patch literal 12895 zcmcJ0`6EsGnTPr9XloaGO}kIN|8O1rI=(3p%k*s*s??sA(4?K%D!Yt&DbJR zlC6-XWQijCFms>ze8111@H{`wnRD+w_uPB#d7t-dImtFwXW3bWSOEZFH#0qb9sn5V zMFxQRzn=)9Q&j+PLfY)Kk$qVHT7hc)g62b$4SA174=TDwOTwvMrZMkfg?^)cM0$mD zn!CT#3VWlLZFZZ|8&${fo}(-nP>NtNn_s?UbCGHtD-{;##lR@`X;BRT$l>k6)AU=Q zAp`)_%A)Bt&v5kr_hLZ+P-OjoQR$Wz05~pggss9V5lIXd5MnmjQLTi7uC z>o&UY@^W)wqmGeqjSNf;Io&xDPFU^$BI7I|tSTC~l)lb~hylw$h;Yn<54cAFTRM!F zt{1TbL?#5_hTTBoL@@wBFzEnh)#72mJO(7o*x(7H^v1eML;+a{B5*OoU^XKJ0FJyU zfL`Wjg4R#7>3A`NI42B{WW2|T(L$4lZ4N<4K@nfx2ZO&mqczSNFUC#iJ zML;%K?&aV@kSv5IaDmK(ZJL%dSV1+5VxR#|EIVnUC~|4~{mYFBWbm15>e} ztXebDLa*li-%}vHg(2^ec8S~4*#~G>F?Iqg8q0GS(9}X)635U80B;8%bd%pME;PcR z$9l@+7aO?R0eo{0`HSJ3hS%p#QH^%m@v!SIp)SH;*M^nBNA>P z2C&6~7N%^|f8}s z2(24A&o&y`AZa0TwmpYnExvuukidNPX@9i_2I4Ddxts`5PL zwSFjD@U{MY^8Gjlbp{C2&p0gZUk*Qwt*~ou&eCE&j0JOxD}PzH_bXKrRc4%&5x#PnO#8 zh(aPGa7GwH#}b0Z|Fmpj7*@I|jG^%YQaIq4E$T2$@9g*rZ0rub%ftXIXfmdLWajNw zqSFYRAf?lVAZwt>KqV7RoVNd$tMed`RIPWvm)=IF(B)OZJEX{5VH8oTi~+W!khHHR zu;;5>$NiOeR8aryJZJYh(zV?k$B!Z^2?ipHM$xiWKf{nI0wO$n`SGiir6{&0H*U-p61KReF1UN+wS z9ERYqcOK8TT%+?%Y}@w%YpWG*5VC(~m45F%8ORHY{@V5^%Gn(M<<99gs9nil!sBF2 z#?AEkG!s=X256}F9;G>*HXqmgWKwMkaW?`>`Tq1%^noH*8a-xZ36|1AN(^^-qsngI zZ;FW#OK*9zoaqiJ5I~>v#k42}{IoNk>SR9x=ghzpv>&~H-*Jokdcsa0y))Pb0*e2;kBue3my~S)SjQ^Fg9Vt)0FEd**uN#I(Am2_neqM`Wf?+_U_nusF0yT#8h;842eBg3JI=2ZMoh2^tQfvJu2d$*hy1%TNJd1)fTl*&K*5)^p65%N zsPr>hEajJ$)ebfA%m1t?0(Ib79FSm9LF@4>#5hxodSG zz!Ck3P3-n3{a=>){FDjdG!)eS(MPL(of`EWsmC1 zHT+X8W%IlyT&|;{zVPxhd+q&-VfEOwEA)@EwOzPbGhJI+Y?cWnaqh;(bHco6i?7cM+H(0N zRj)#oLwv@P^G#Jgn#YGDJeoEbc>>ZAg3^bp_rAzl1!kQ0pSY7I;{ZdV*(~QgOjLzi zM7zjPY8-g!OVyrQ45iTRX0mck@$|LfhGhjdqt%e0(H(%Y_SQ$+9`U#pl{a!j`-(Dn78o z`-BvkG6p3NLpRc17sSk{Gw!KG)3y%^qxY`ET7PFqcr!SWQ~cxyBiY&M9~pndJlSyA zV|{>uo#xJyf%J9K-LC68?ccwIL@%^ZGD1FM@n^7IXS0kfK?inwW`;2Jk&XcH_>;PC=&IYN#eq9 zdEAD4EVUkw9i@KFaXTEtyOzs~%iNTK`0zG6VI3x%oAAVpX-<^5-@J(Y$7Y5VAJZU=RdUfHJjQ;r)=S}} zb>qV^1TLYHPirG2>pjD?b0ECfq_ z4MJH98_XHTk{SH?SDXA|T*zk6wSc|uN*^FP5ie|8=qGH!pxAgtL;kPM1tN`JZ7FzS z=eN6ktBm*Y6JlwvzB>*QPsK4|04;TAJ5#^C@%!B^b}$1fns3Udy+Dd6Hy}tZr*O{U z#T{(m6RX;{7{naBv(*S?jV^l)pB_GYR8z>7>;zunChigPt(uuoM5I?2Jhp$^o6}ML zP-Rbo_<5SVOs&rb)vI8X)9#Frck*&Ehhg;Yp!&*;`Leq7592 zyYW_NnsCJw(XSW@FDM+&>}~Groo%AuC5;l@&}SpKh}>$+T=veYWes?Jmj47Hb{oIq z6tbuF3oP?PN`YPQ7xST8EM zxpSJ*ey60hCg@=cOxVds{*tJa{42Rl*=kSDG(NeD)_X}}8s?PUP&h?~ZaI}QjsyV2 zQ`AL?-FGkH9QB8(0o#c>rsanrGCaGRJIFR8CDV)xD57DT5rv)?MN=zuJ}wM=S$s4$ z%E5Nd{z1P0u)@~0*7QATNt_Hd5*ZmU-!U_*+i~JKW^xh*i2P#MPjF6WRS+WNr~o6X zd|GRsOJk^CLy9e>+i#hJaLL>@FbKrOHI|LQWY574e=2x4$m)S&=rt9^D*%A7O%35` zDh_`8TebhYG8d-}5^`kt&U0{!#z2F&2%X>4Txkv2j2=eZ#Lhzm zP|M)N6XWW*PMFu61-EfPf36UqP2%+Uf=hAPw%&E51RqCZC3kiKEcN>EbK0>QUk(cf z=lnb=iF-{bqW^V8LaX3B=-EI!ZWxE`a~c4oELvG@Bo3Qn67mj?7YLU{oYW(tQ8=qG zupQH#t784F7y7O**2&{JEja(=1cq>YcAEg5(!)j{QpC(rQde$5jtxAOcSd*W?BM<7 z#2I&Zq&(vv*)Z#cVFHmsS_@#wjADBCQ~YddlzLr*ck;GU>)DsBGPkxbVF;sjJlP-9 z1`qOWKAaPth>qI3kjYrm0jLYo_|S83e^wMK|BqzrN!N;l?deAxg>=L?ly7d-chm&oXRV(>A`c0SXtEv`EzZt4)Lu^lfv}0C-j*P9yz7F~V%DzR@bzmRSNspj zF4^$VND+Dy0;pFUnYohRmQtg_^u!vXf4V_}0QS^}yRd4}-?3L?GW9{3Yo`zx$R0Mz z7^@(ce>CF`x{Cscd|q96mX)=sU;A6_kt)sM5*`50&DV+VbXu;Wu?2H8*wM`uQ;o0W z^_qg+G#M6cI^0c^3%DO7l=4YA;E^}TO1faEAQs@S3q}yj;84!#;7EVY}e<($Ho6xl*oXxqn^Ul57S@a z58o=q!|#e%x7?$l++~;AGuz^f3Ty`}f6Gf)u0Eath};hW9sUt-9%Ruw4Zl1O%0*WN@IXBos5X1KMxXM3#vTTwGnU+V zL9+m38PSA`6orrdVzc&CNzhtJ(H&5;Wc2M{Q>ZILqDd=V0D?elm9z^l6VtjUWH(zY=~*{JcT z@4Pr*!TkbUyS#J4&b*v7rD9Q zc}qwo4lZusT0Z;wdP>X*y@39=H*~H-abLHDM=ywh2P)XzMlGa=YUBijn{^hH@-goZ3u}(DI(x^-mgl3g{y=2qD0gq<=al-sM-C< zKck1#gWc7NssZ!hvv00`Nivwj=dIEFVHf-xad`1>AiS4j-0TXwp@ zdOXkNK@vr*%3MK< zW4p~mI1*P+bjiS zC#;Lp7rcKGEQA0q+RV-Bx%>g`PAOam#hMJb_GJQWcE*qxM%Vjmzw^HL!n)fMXrIV{ zAR6m(){;;nTOFn=%L(&(I1LQp6R!+tX%+^Ukt^-_@%$|C!H2oW5wElm>F#?z>(>|Q znB&K*J&4gv4Uibqx==1A!py|)|4!2K2lMkYPnQD%66%U9ow45DVA z&Lt4p^>E=*E7p63OlDXi)GWi31ft72VcCf4)^~M34w=qK=h9B26dBj1mqHw5#byOk z45!JED-IaX&jbV#hy>(b{JgcY)A&Ft5sk+3HY7;s;ZE4Vb5eI%5&XAx37|*%1!muG zfdS149S}|~;NrTs`W`n*!o)$cya0LW*U>Pt2p6pjiy;X6M>D^zK#0wXj*w*Pq;LyA z0jTetm89$}_>@kV^yuUkdWiRKhJVgk!S4+uCxRG4gd%S&cXCoUR15l*TTrA94JD=q zLK|WR1|bJJ2)oh&ajf$F9D^7t2*=}tx#Vb5H{Y^2AGE4GWW)jI>k*K17`6>G<|HHT z23OE{@w}h^5g@y80AXWA;nUC-tBpaTPMcF%V7E)(S@}2}i?Ca@Is-k5-bbu+0zL)a z9Aq~vq)r_yP?XhCLk!!2XDeSg$jy5ndu)Mu5Bw{4I_h35&6$4dEqpkGAFu=KV*-dY z%jD_oX8hqxX<_YyUXKI`l*E&MaE|yb(LLsYURy8Xq=Y@bT=Ghqh9)LdGKt$p(y57P zo+$fvGto|l5>;Jq$Lkw&iQc8(86Rr4qv=E3`3a8OD!fP9# z^BcNiK?>}iCSv(S?h(9Vn`Y2DK_`oc4$88SKczw8o~G!0A|S9r(zqEA`nf(vE|Y#y zHXT(I6(i$U{sI_?MR^2&m5G!2oF=9ai33skeD!XTGc&PaX zWZYmCxh9-j$rTSSrT+$vx)AdE66y!?Ee{k#gaW%$ct;JT#FzF@_Z)p|%$?qZ^yq}7 z%^5I&Mt(Rp*Y8GBj3!rRuEC1<0pFDrOcZxe%hj4@#SL4l4+yVw40~J_X#a$g65j9R+HG@0qU)*W}adas}~m4#@ZKo zCdldj?~*9u+ReV6oD;#(e(st<%b;46a4INUyZ{=BI4y-^B_#Xio{LA{SzFl`B4Ct+w zXJmJCz#wV_k7wNfTqX`iOA6NVajbmIV8pPMOSaCi1a@c5@0eA5V3zOd)O!~2apdSa z=RT*oHoN@^HXjG5j~(94pD7Do#>XyW(O4Jv8%7;D?!wE98bgq~YQ0Uv^N+X6iVCa7Lcw7qo+!DEL8iDepDC56#o$WGN z8DkLoh1Pnr4=0(_m=D*2_qq5y*&)I&`!WVey9ch7auyF09x@+Z#yC*vQcnsFs#;d0 zUMY>3EFAlx30Cz&%u5%s!*J$u4x)Hp+>xF4 zZm~Obr7rgN*coP1lJ18Wq;O#h0nC+tGjLj0nE89W#l>BySZ>EOaJ4$@Xfh>Hu9Lo4 zv|v9YIu&iNf6ewbC7m7q)Ye2vywgQLplv*kFs>NBzBT?j&rN2;&d4qk_IZ#y^|gSN4I#4Rc?EW~98U(wLdL?(FRX&hs(-!mv4$&| z)J)TL+h?5fZ0Jw%A!UliI6`eHx!^pX7gx%!TeodWCMN6h=%a%Rug{lg(SUT@@bAxi z*{EfVXHneF;d4#t{ix+=wc_%B5>q`N7--a?B!BgUq0Ql~tHq1{h3PzdiLL()IotVT z!NrP2wgmj^JO4I+xWSaM`9rta9=^t((V5e^AMa+f=@ve@f%{YcNeqR%%nP|HU0t$y z-@3YfZ5y7KnaP?w)-m2G%u?^ZmL?R_wl?RN9kqi4xQu&O&f)_iS}svZdsWSyJC*NB zmkP}$v3(ghGjG?{)dnP8Z;Kuz64i1l;0=ku*5?J{MSuRbZUcNb8`4|(xFLctiW1}S z{DLj~Lv`jwbd(S)3Rhgf-ksNon}r!50(V=h@uCJGKMKz)2KFNx$%U^I|0xGYUP!8D z|9S3zM*>(gebPN~fBY;}<>SHFA#dB^<46mZ?V0ioLYk0_X};+43>m=boTYS9 zs-CnyFf)hWRe_A`oMDox(EDW@SO5)FCTMKua0N3@FBc&>Wif=sO*uE5H?t`m6Iv_` zgxtwTPd{_kdx1}CtljPfA1+0Ik|ySy_~spMokw3)*tQK%A%O8IhxKOh^-=ss0Z%t- z*9F9oDYfAtA$Sj_{H-(*aQ^b=R0BP*&BpfQX1t!~ouQr@SaHb zhT{@@-Hcxj7s;`d!mk3cHw}-3ZE!Dy6tGW!qK_Asmp%UZ94VtGR_HqOrtvh(5cLEG z$mM#zf=^CZ$Dh(ZuFm#;@oF=Pxd#VeT=xE6q^9xW>pSr#l4rgg#DSGoe%s;(JiObV zj#klnM)9>Q_c}W4wFkXY%VEK1|5`f$oLD|TezhF-+$&rMRy%aF2xC3hYDKy#D3f!Q zw55Zsbda1MW{cOQ=ZbGAKNuTgK{ATo0R57+58`?_x`<nqA11+u?jZu z<44xDx4m_4KjuiR|7fW*6Ia%~MExEfrz&MVYfxRm(-U zX>^rt960jO0Z#kz&xL+EEkqATJ@{(4WR6$1nd-H7pe_FbQ>ibCzh`E=hO!7R(#!GI zXBG}7@8Ib=!NAR-2@yR9HrBL6M*o${L5Ir^RR>ppY|psqh4ve{#e!LOm9&F%jq~F` z*ULA_h!IklhP&wN_bI0slv51vw-h?9*RA)3(H#LqE<3dkmq;tBxBdooLdLh{aY3uQ z+-$F;B&Q4g%3`7@GJ3NrdmKchrJJ70is94=p3aT1FT6L{PW~>vm~Ym2xNmDjHKdZ4 zzWXqqAaJ|CS-imd_X{di<<5RioZrjaVA=hIcmh!&!=vkhEf9rIMtKClz zv&i|4c3w)mN=JDumvR-y*6jZ|?O*XO{p>Nw|FW)a$mz+*?uyGc=S8;Y5;> z`PblUi6dN(GovF(9TZ60r@c|9C*@&koX6P(30dZnx~6wa2KYn$+*aWE{m8M#TJQL`#~C*l70n zIqlAuVnGj{-&c6>jw1{|#H4%g_f_x2m{QF>7Qd!3KrM4zNBs4r=fK^UHYU+t8S92= z)IGMvKiC_;o*!M>(JeYk2R+Hv{F!_4`hB_q#;$3?U0GblZC*|J%P2(pQBk%MH(Hmk zJ5$Dw9HZ)=a>BQq9c5zmEh#c^|u#2Ut&>~yu<_p9j$+yb6sG* z;H`6pdPPw~toG(ADF(cR>)%mQ@PGa4akyH+7+z%Xc13$B(;Jhwn-AnPY!X!oEX!)V z{$C$EbY+YU+-diejTn1cS=M>44hPsUtOUl(m6CEN-|Mj~d-HR}r*BeoJkJaw3pg9L z3*VQ(H&6IDaM#ws`)gm#?x#Dz$)Wc=P-Jz@*9C(PN(nYp8MWj~@d-4It1$Zs!nJD| zj1ttYy5i-UY#&b(XEQ9n!b`jalqe|(<7r+oy za<2lo2a(YC$sZ%)-J)d(VECl4Lg>VMbY(_!;_s4UzGyP{NMCby0+xOGw!c^2_=F6L z#11``awxLs&WL8~9&wYUu9#teS3YY(N^xiy4bz!%oX~Xl=Dxsk;SrkDmShpl+kIH` ztYrCjX!uD(bs0T7o>239{P%;JRqc^&;SWQJ<#Cs&13Lu{MLh+{q7ADjjvw5daqY>X zg8|@@+Eje~2x-r!^_ICbcZy;&IA^VlJ_y7Cw%Kbur*coU`x-r_1k%}+(wUC<7IS0T zgj06-hAI#&LJ{w8k4VAFM7S?IM z<4U)mVG@zu3~-)S$e2AgX??6T@qB~4UfIu4`>RtOXu{=RzrQ-K;P1yK;u}ZrZ}MO4 zDc>sJZ(E3o7x;Kl_8p)%*9_~6J#hSb*4*ZnKKWkp&+l*gU+cf2)F2SJyv>7OA0hW) z@#^Y7+SKJWN^V{31A$SibXLQe`}i+T_if>Qy5Hx=IK(|H{$kX_ozv&sSAQ#Mn*a+8 zb0`1>kxP;2<`|Aa^Dr{2Cb6_{vWy|QiYc9zOn2S|y)n6~&lz@TQuw8GF?no)&~(it zN^$Vpg;RcXS+iS@oW%a~)#<%EpR3kOHT*LCiD+K@4Gck+|80~Z*YxqL`Rt~~w6P~+ z-^KJoGx{*gjiyzl^jf=ODu@l2U}UG zy&The*9qhF>G>AX`sVUvC&;IxWwfi8@{H8BkLs2eH*Y!If2uF(Q1fXT zIx)r*F#h$e{=cjew|u$Mhp1VePm5pr;wj-fD%TeW;bAlK=G8lTuB2bVMjTg^B9b(g zYs=uY^r%!?X4CJ4v2-x<*D&CfLP!~psn9bIH@bSq;4{hpI);V$_KB#2>8YoWPr^#g zfxTJh%@DQf|Ek{kIYxHz-%hd1RNPMF!|@NXrxq=I2YYJkR^tddEso&&+9qBAEiaaj z8(gPj#USAEL~x66S8)tom=n+TXiA05NAcNA(B6kiY51t9y);@jQVK+JgwBp3qzvjt zE+^=t$@*PC3_`i|d?hys>kE0bM8jr}!NG!ukKBS#!2L@v-qo8Cn&gM>j-}l0rg2MG zv*Q4wwcn#l=U$y>gWp^b)!A*Rms6czD#i&d33MAE59kVB$_(orPT_y^mp^`P&-ZZ* z=8?C(ssw??U0OZIm8a$>WUFZxL^or zA&}C_sqH43$3CFQHVcw_Cu*A z@fn;QTN(I-!o6*Dl>&+7QO>22$cXyqPW!*(6;DpdJ~aV709)3F!0k0ZYL!>`Zne78 zOP-f1+E-;*n16z1{kK}Zwq!#GhVM0~6sOP=B=wZYtG%lj=;zL(0p}YQ1@n(Af9trZ z)On-!3(RV+nB8c(LF`}nrPCBFGubqK)%9L$1ls{2{V3RXj=?9X>-qnSIW&c0rBY8vCO81KIgB-qS8!ja(i!2-}|Ovo^5Z8!k(^1 zmNOQmy)Ah()Q9*cS6ty#bmao)(0O>C<;Vre@rUPV$v2xoXu5=p6K$GCd0DA`A-X(4gA3{- zdc_dy2R-h}fDx?uL=uDZCZRT!op4i{Z}o3J0Qp_Oqj1uJabos#|v}m?>d1- zmt)&rTk?to-2god2`G1$ch4XHTiCsAJH5j(`SIo$F-A8j?O7Zp@k^!&HF!>!pm#IE zV$6?}cSe2wpXSbCXZ3c@WN?t~#rf^CVIm_W;2ici^Cp2ZZS^6Wu&TE6gHU^3r+n5S zXIjLB$7ENt4-hgQ(&Bn@2TQzY?^rA)nNFZvNr7|ep%UMBVjOpXlkZ{zl8>yAkdUlL z64C~K?^%f~v5seNU(&R{OA#jn{qO$)?_b^*X~bnAnXOejPVI6h5NX~Rf_#132xCo~ z9VG%g)?vm}FFSRWdz-`nl^-96!bTH^zChf5kB|R)XECPbJT*D*5>M$R1H64}t~mvJ zue=|8Q}7Zx8AkB%`&83yhQ>n(l>w&>6x7pRFkDH`9Ix<<0HLh5-Mu zZ+A5AC%rAD%lmRJic56yj}&#s(f|ArB~BZsw~jYO1UOInsR4KJXQ)r;hDf^x_azWT z6zy@z@u<-X3dQbUSu+fnmLk^k-mN#In?C7-T8{T))srx%PL}d|iAP0o6B+WPk&ur` zDV?@oYhhRVttDI`V!!@`3$+U{DzTx6lG|dnA7kA``;_Fesx)d^zUP05#}Sc4uU5K4 z)mDUi;_6y~micwj{T~9bR*}QnnBd?;(_^B=BqZ^wCl1gbY66d`w0hopKHS@XLth|= zqdH&W2E6c89(hCo0WHOc%M;i|d?-|V?<(s|g}gCM_O!Jp4hU8U2r=}CpRedq0deze zL-lpK|1t0y_0~`|-QfhP3!x`+Cpi@rGjaV0&6MKhu{WikH;tPb$-v#uBM8@)9f<9w zco&hKF|92nJkb`PWSV^xO~z3s$YKrI7@I%4fktCIb=M%_HZ{fI*OvF5Cp%Wefw@Ey zd$*4-=h#uo>BYi#2u*3nbNr>gAOgp%tBKL-G$=gKUY#W0)m5WY6EUD8ql}M{(?yjq ze%E|lSG_pVs;UE6jb$Gx9)5FAJYGd+LT!p~sMG)l7?4r*KA&IuiN(0dOe`%UWzSn< z0Pk5wRmzq}kBRG>AxrA#XEYpe-)RbU=Jw^P$$&8wWgKr)WQzkbYm)>gmaCNl_PET^ zj(?gr)pMAj+)SNAa`fVAaN&j2D|;<}#15=Oj$jBEY6DrSUq7Qx`hnvQ!p_lYZh6<> zT`hl!cT=CHRp>T~6BA^%pFP@W0IQIFo58+^ja~|N2x7?UK1MGY{t9Mb?AAa7WORNW zn4-!`ix literal 0 HcmV?d00001 diff --git a/imagepy/data/logolong.png b/imagepy/data/logolong.png new file mode 100644 index 0000000000000000000000000000000000000000..72cadb49b15bab68998ac4b8910f5adf4cd1c138 GIT binary patch literal 11801 zcmYj1Wn5HWuuCr~QUU_9NG=V6pdhd`OS80eH!dk4xhqoAyP$MPr*yY8f}}_a3Ia-p z67TYV@5B4BzrE+4nKOB2=AJWA+M3GbB#a~=5Qtn=ML`z?!ZiYcaQMKuz?%dMlWyP- z$4gfk2C5uq+6D&r4ssfDAW%&_>7^9`Fedg?G4=w1D0**yIG;Rv3pgunS- zrf`y2tNY5ztHcVG&C&bSY@Y-we(+4QFa_SBI5GH+ELZs&DvD(rO3XSSmu#X>P>5h3 zv_HGJDcxE2`s7n_-Ups@x+hKZ z%Wd10)QJ0*1%Ve44&~t1Szn`DA@+|NDs&3NjVOTU>X%2oBH94=AZ(i&xP}LiItM#f z39ezn2Z2`Cq+xG1SwWysoVRqan^KC2K>(2cJK8k=Ka^nef6Nfz&8k&1<@KpXa3?(< z5e^9CU2~~bu1d_b<#Ol?%y!xD%x=vMQ%?8-^d#PF!i)sI57beP+!E!TuVKrW?C};v zPy&E#r&prth3C@n<G$AOo=;O)C!!|=wL2PcJa67LdfDvzVCNtWqOnQY&N4fzZ zv3!!gfk~6a^cgYMIaOpbSZMtAdn|tBuVb{dpI!@eYk7LDBf$f@t8+|j@_8JP->P1B+xL1 zh9GkSjzTtrO-dNRlLIn=(mkOI-;YM(6mu|zyk&@KkO#(%w#~$+s?Fc8G2Ent%LA;W zKSWiRhVvfVl#qxNGQmi3K&wXXbUmf`RP9frL#}^@zy+_5Ev{_>bkMEu8S1Abqg)%F zJYu<52Pl$aO%qaA$Y%Ba)89}2sA|8#*M|%R=EK6L&Qbt#h2J<)<&?do-MR$Crr_45 z>CTXwhW&W=!sof@!pzTrW%j+|DK$$i^>K;i2{HqVqA>Wihi29)HqEEZ6k+@`2>^fl zo)MH^q4d0AHi1RhQQkZgMhb;)C+MviU42Qdnn?a~B$O<#Pty(+FpWFr)*39iga8gH zTdNO-)7mny{??T%!oTj7xpE0O@C!LI+$LC?gXN|43iGEB-fxr;!~=!iamc2mD6@Qn z-%#mSY8hUiU@5sf1%nN(h0@iIVeiS`sw4^6FoHjnXbWP>=8{!)xdN1!ifNibh$`$zY5p9apMU+ZE%zie6d6a#^RL|o89`c|;g)yWIjmj@WVhVx@jaA+T)+>{82c~FOE zxa4Ge>ior1j90sD8l~vh<}2nBfkGW>Y!!`4S-swojDMfjk#RQL3UgJl|NMC> zah%$D!hoOrh!+Gpx1@8A8`d4)c~4i^aIvl1Eo@!?F{botCRsPBkO^Zlhy>rdyFY}_ zrOZJOv1UQ>L_Yx@!sCQxYm_6HA^c!M1I04_D#R4=^RwpwJ0Jl}2dgX%6j!{08bQPQ z5}p*1KsnwQO~W`dU#w38d+OX4;~F>a8eM5hO4YDhqBSBKpL02$q&)f8DaY_Rpeqdp z5-VdZ!N-cNb#9@{ids~ifgJlG*Ht&~%!?Hva_*1lW(oQEZEs`&dq03*Mpu8M@v$yj zokx0#Z63PNmn@W{Io9NR33))Py~NZ#7n?cRVH%T#Wz^ ziF~$X14IL<#8BE?FAMJ_8xgaA{x&B5RSERJxFhx6s$`-_a6z&|vlTk_{Wc=kP>+~M zn7LPL0wS9^)qun0V$h+pa$AhWf*Kd}3>Aplr|Q&wJL4iRHJvDEQXbwO9}6YR6n)l$ z4}{`#`~h_J4rxyqm&I1C_r{r+J+$5eVqeD|{p+{mMLa1+`P!8R2!*RBqm^KfoBk+O zvTmVgCVk_b5A_OLgwQ~gUmnXf&1bPDeKf-I;3DFLY&Yg`;z4p?DPh4d-zX5fyJB8e zQ>yv?nr(NzCZPRqtzF8VFhQ`OoSA(d9eRfj3xsQt2$<2Y&ug7(A$)u?SPM$Vd!sIC zlmD5~?`W_>r*aUxjw6)4T#(O;vc?Xf_D+d8C&GBWzSNVAAKTCyh+2 zWYClAAdLZ3L+T$-`HY|>+~&y!s<1L(BStCR1LrnNFu=}dUrpW%z2$UUDp--b@ZfT3 z(rrWzNY0uptf(quih*Cpdm)=lFbaTdH_ZQ^(!xbdPv3h4errA`RQV{aLPuVb1p=-5 zM$?~13EIc~fb#79{jJOSdzYfs8`Z-z0xi5-7QBc}?_zG)zi4WjzvHIug954Gt+h7z z?6NhYH-0GNBR830cVNgWcUUDJC~JK74;XvBq@$mB=Ns#*$MehtAgL682!i0nl1#hz zvmJ3okDDc85Xhh0)DUHJe)7Fo(W;yz>#{&Srwb3%6>N{z7SI^z==ggib94W}#q>Rh zsavBX#HYob-w2l(7Zl1cfcBg?5@}BOGS%xq1}jRwG9`SCaELe7}p!F6Rss`jd(kb!0*PTCZY+9TBrp z{T10H&eS0Y2zUqBcyU$4aU5pN+$rcYq8E~n?xpeZDXlhvO8ePo?pMWg7(aF(F!^46&Trn!s#== zOp%8xPYHm?#%73`iECX`T+>Z@9p^MO+8%7=^VWo-p)O(KX5{94VjHNMrO+#R*S3yS zQu_7Dk9>V{gX8yau2ya^7O&{l!0{#&n07eia_8ncWJ?iYj&mCsJ0Fr+&|F8}9Csd- zX~9ZXom6yEEF}R~XS0GeZM}!dW_)C_Epc%If~(Zq^CG1h?u?E7P0JEAr6Q;+KZ*us zPNT-Z;k;ak4mo=ql4|klK+?phbaih;j__$VDAbUujg9OZ>y;6|)C1Q9Aebmz&N^BL zN7QYcnE3xdR+su30d8R~-$wb`HQ|HObY6mg4IN7>kMmi~e_Ny6Ql zNwaT2p^>!;;Ifz=tKpTBkZb@JsIf!ms86wWYW|y_`ZHJm{@#9EpYSM$rgQFcGW|$D zf&ZBA-;@OgGFwOod}h>oy40Bv5s9nl(N-+(8 zZs{(bNm{W0&Vvdx3$2u`9>O_SnF7d}Jbd36H4u+gh<~#1tO%UsK!f8KdHD4cB;526 z-QrxanYy=PX7wN;?DlolP?UlT<&u!NG=v_Sl^ETg1)E&%7ZeVNAdPL)E7 zx}TC(D!P==u>P}uEzK>!&MbGYE|)$8QZKhK;1^fYf4RIJ+5{Ayj&U*f*GQ)rUP@;! zaOSyciM^Ci=Lv^fB88H1lU8w`Qh-Cqw_{K;h6z*zw#qZ4;Aq^6 z<`3)R0zj%L4P1C%cErBJw|gP;USDYP{fs4n-IfROCu5ijcc=Hznz&E%w9L94m5PL3 zVPSY*gRKj#f$y`reWqc{z4V8aJ@=i)wC%mL-%|JVg(2smnZd;D&$J1mGDqZW!g+q) zH+eJML{3|kNsSNlC&f~6FVMY{M+>bb%J)#RSo=FOpp&+y?AM#ARL+wNXnHb7x_>bp z1+t{R5alXn4fFKKFAV27_8qb_=-Y9Gs7q`nRMQHhaD;;s@G(*IsVqe$tD*Ru+6;!G z)a5VDsefZCg?^G%I6I4Nu}G3Vn8QN}>HMA4?o)Up@U`8)#UDkfq{{jtoP+{yxz3Zk z9(7K-&c;#^uK!1}`9(qD0`_iaO7HxdloFVFMyd&+pa^fj+#`eU>g3I<)i0+c$noeW z%qQ4Py%yloy2b~!6pydCC9hA`RJOQ>X3D!SNHM)&=$;RgQU|AFVgeN>wFdv{B4fC} z$~cg-N09Y>(cjPYT9Yew3e3l+(jxXNn-$(wV&Tjbw*Q+IW;m7=-^66NDw_pG#b$Dd zwrm-8Y{)weSzK@4>Adm@-g8hi6Z(NCk1Nht>l5-&(n1|Ekm`Xq z`6Oh%DcS9)AzS=IN)6-g5VtVFS7liQQL%}Sd9{O++mlJu2x(s6>9$bml2Xrnu;dEcFjp4KM8-(Fa7+1IRE=l$cAkP* zUyTWJdy(OWOlv@Z)4@IF9!c&n7b9z?@gw!$yaoisbUk6jD<%}2Tq-wYBWrt9Dr8!1 zL(K(Lu?g+idi!O`85UE;J&aM^RPr%9RezmCf}{ zpz7Vc8?`CrWY4EN2v4&5yYE(G6H9D7!VGdvr_GC9_rK&wR(*K&(w=j(^z6f_o^q+6 zbyJlmC%X*+FSNsB0uN5RMZ#+8>mF`%`Pd94ExAVLJW99u6ZLCqkGBl6 zogC?+Guh{ddL^Yokbd7Gm{0V2TXBZZ+U(D?;lh#>@7LX)@4{NS*$xgr>a^Fu-V|oy zs{@CXv}m>y5z8z&SC^0r#rScd-`eY%C5j7#N=4EvI{{lTdP}LBz{T%CF0!exH3RZv0zsJ za5KAALJ=I2it;!t5jAp{n9`Wk>XJDUbjVjw3NqXD`H|`s%dh!fBUwG*yKl?XHdgjk z;)L1YT`U@lFG^x+@(vcV8x!hPm7Z9i;q0Ag3BY^aNjlRx#zP>9=*Kjj+Xm!-` z16TMD(L|1|pu&Pq)xxOVh|M=v)D(fLjddq~GIy1#0MWQF6rw&$QD(DhIkRQmZs@ag z?OL*)t%#&)ILH!*XZr`av**5OZF6C*4f@xRph`qF-bDYyN6bI!_-AmoM7x~#!jEk5 zXxjs+2;KH#ZMBvj=z9*_?@fU&)BA%YgCk~d?dQiat2J3m=+US(E5-tG{f`@%vAg9| z=7SqKrCIDAWvW{RK8D+i3cno71?>+{UzW<-5RQJle=M{D6D)HWw*8k3b$r;)ydH_v z?V}5;fXyEcjxO6EwO?+;cN#q+BCPZ5|LV6*W|*a3p0&m+^kHmjZMUkkky^x-A%*L& zCtt~Bzgd0fC&e|QOGNPWKef3HI-EK#Rs*NWcYzz zMmhS3Sbk86kTW+rQPej1qqOa6nNysO{d6>$3=xT*N5QoB!<*d~lh--i6PHw7VpM5H z0;s77q^sHT^G7vYF-=L4?2*`Lf03yP{wrQ0iR}T!dC7cEUqiV>>RMjWOf%1az08?f zWyP84@b=iayvEOdsh=HkYuo2U@pglqQcAT9G-nNArH5bz@N8*E+MJAbsm^9YrK$@-MVD&N%0Y zXN^4t?>|>&tPht_69i9izv<0yV_=wd%L2|>MId0^7+oP(V&jdw&d_h2PTr@U86Veu zOYR05NA_BVZ8aQ==P+Oj;q zvUM88r)NhH7+@SJ%3s!-GHIN-+#99#`0pRHC(&-a4txt}2x&5h@$O^oWX%tflxd*q1G0>D(8OqB3c%K#ajp4*vy9 z3AblDlaz$XShGHcEtpFmehMFx?DSPE{#c+*@Wkfc0{L%=wqaNM!^qGvbj~|bY;j#m z$`_y>^R&+srg6PW?_o0LF_(KhpqEL;has9+xJ3-#!gPbyD(az z?e2o0AjFy@kKbsZLuUx})1RY1R}Y+{o77dlSJtZgQ`t*2m9}oSYI}M2IIZ zZ5mo2G9|wR(@bfK=q?z)lg^;!n4!dP@K}xNkRxHb!p3`d5NL#T8Gr8o@CUqCJy{gE z-ja@)(yE;{#1k*O%*v2jBU}9FrTOHaiL^#($Gbblqm(uUGn1(^qU-`@5hjUK*wTbz zU+HYZGOzbaSi*TmP6q8Hnbrt2tT<26qwK(iK)D(;m$(CH}Bh4kr3bz?jzFvJ#p57j*IMS_{66Pp}v;HvqFWWwGz9Qm? zzeaLL+|N%{(gALfIyW!c9$g-qwOE)BD=m_lpdKDvUi?%XV6QdDf61`eayS<+w6dYc zRpYGK#O>OvDfQ)_)|&3dGyxt5QtTSFtCkujWtiIYC9{q5xx$F$LC!3#MSK_iL-O=- zU(8ey%fqOtafVL)3X^+aJg1>F92!o}>?f%jD|b(+cA6?@tqsM)7)Foy+i0MrF2+ds zjA+GCOfe=(wLs?a#QY+ug7J?2Kme7$$dGiShc6=Cz9BJLPsB(0M*m8;2*`llBH?Il z0wh7Z-a5vFx>Zo^i);P)-O7Mt{=jW?imccMdHQOf00-XNIL5DG&@>BvrPH0^7Hws- zQ=@@KANXqDv0qEJ09^dNMsQh@jIY;Lv{3Y-%NRRE)Gg<(>0seC$7u-USBA%_-7x?C zTDxcOBBzx@fMZ18aoR(F_^ECod4g%&Y6wmwe?pY2t1dJV?U| z@nwvXJfuthwmGBd?>%0HzGHruF?9#4xm*#RJXo&(KEdqRUPcR6)CSbK*WtAgQ*KHb z>i~;jU7!7XZ{V*ppSRbj&%u?+2Vb|L&2oc~@I$iLepL@qG&Ob67Ek2UYJ zp+7I!v8IIUAAXY0&|U~ZjCv5W_ZGO_7co64=9bYnZjyPxzL}D+JIB75<(48ROcryJ zwEJ~CPC6ITf)pF(cP4rI3g%aGKk#6_oJnNCh80V!?va_5(suc@$5f~<|6WNrM@+3= zVIo!AaYG%~l2}5x4ZTfg265xWufmfh`hV3V(92rkU1#K^xM0cJ{Dn>YVAJG}L#1Dj znJ>lLx9mpC%)T@F_HbPH?bNSnLyqrAL|;iatkP7KvJw~#%xB%Xbn-AsnQJ_b*XDGy zDW@xL4cO`N%!)NOotk-TRl!`ZD!5Shmz;Qu-Ir4kIl}+tQ&c8bV9q1WjWY}OUXz9s z`~DX&|CleSG4gL1-{{Jnggc^xYGt+WVdeLqe|&pU*;r!9XOoyZ;MY99KP3w`X;lBP zLzsd-f~r)iCPjFU9lv(J-)=aWZ>PDZ=lp*0OMU{QXAjVogxYY&N9{N~Usdiey{lFN>exZF9w zy-5-GYW+Gr0#$41?=R)6GQ1xw!uKrzZwou%W^|ZIE9dfFb(B;ajN6&z zPAeN92`;`19+qg92F!9~mnD1n{HwC9wa@(FRV1zSN{4^`njuZY`C4S!!*K2l*03k6 z^>@QIax|Grp84V*;uhpZqD)l+2Aqq?G_ecsei~nppUatW zw-%eECHOWd)kTWPhAk)#!gd*QY8~d&()+HZb4WQ&tR-gKueVP+zPeN0$ z1q$P|*o}Kp=^X~5#!ED{^W>B+vB}QnWBCIjvoS4YBQhC4zf)$DC71rT z&GMFGu{kVZ(OhA4%WZoO{X9-zF29{o$tok$?kCwqJpn4L8u&4wM-M)W)2+*MSs1L& zN_B4=;s2vK$1n&7c136U*}NXwlSc<8BF@$9XWjtqh9k6=2j?v_74}|{3pG<|l=Hex z=*so$&|0*h$rha!r<#v&lR`?(qoWZ8lxh*~cicuoD|@W|yseaXf+TKevqaw$BqAzM zQIfIEBJ=zQoKcmxj7TcD&PCE>UsU!!MB1}FfqYNCn$f}k`^$~DM;AgfUdLTn90M1< zOu)g4$1*UeY|kjt`hFZ;X`)){%A>De&1(p&e4AqRZz;*b?ayB$U4;S%_KiY?;0N90 zu)&COmJ7BA`pwpNp{78l&st;AU(i4n<5x*tbXfB<(3|bL9z6UWj3pNrHQ38mX3^aD z@`(O@ypjHLFo72+SDpD76*sE1D4p-YKPVawUS0H!4J)Qm#X`3OI%>b#)W47^IS4!C zKltZXn~&NZU$Attveb>M7%ET|9q{~P)}c>fEbNnN+N8WVuG&ae6_2%llQ%b*U>Ubb zEKGOzhTehgnN+d)@UeFJ2py-tqEUU?z)ML)e;2>*rGO8-#4;vW9#xNFm+UavU0 zL*MPAUqhcDWdlN*Tq(c53Psw*7vh8|#Mwn?QMFgj+Z(~&H8$@E%-W@7R&FFK1a~H_ z(n8isp1#)Rd2u*>_TYlur1IDE6WsiJ?w7jH%L)YS5zfr*7iP?g3o4dM7a@oG3dWnl zU7p{kBzNzf+cwvE%?tgWen7FAUHDA(xl#pVEko~VYyZN-GZCQ{_+ww4lb1uUkBg~p zm~(}xrjoqri%F$^C(9+R7q#f|8`{;P?)#Zr^rz!L6;{`MYrRG1GdJ~O=;gd(t#j}% zDv|Ur`&TSR(uG%hSo@X!ERmu-;5x`FoHvC$@Y@vpjXuwr)U%@B?#I0!BJE7-Q>PVK z)K9GO3emMZc{71*1=bC#!CGm->Q3s&L~q1P&h9e<LC&5_y{aP;OjzF0WGI$id$9(&CWLwOLdxQpC^Zb)Q4S9xFn)h7&VF?{L~lbNHGu& zDs1aAo>c3^UQw00Xnp^NtnY?5eVQp;h&WCPk+72}HD*cE#9P#ee(*3;X!N6a`zuaE z9{%h)J@I+57jMP{T4KLOKi8no()nmVPpHNEBXgqt{HJ9-pU``+u89w{xl=4B20ahR zRWup+ohzi+$3aJSzkIo*=$^vG_ifyd{f*4k;}AqhSBw_ydu^oeZddYV(q(r5B58N` zNzF&)1+0ZAruNGP&n$GEN`y6WCHqq;Ekx&9L8-s4P+q3z)XeK*efgVnzr64_S1gKs zqwCK*s6H25E!_~yAVY-WO8V(w9`PW8ytYE!X?$mDPj>ToxHSPVGSy;~GEOV#;v7O(!Z(6Y8f9$BQdBHham0i?8l zi8Ka})|wCzx|!bDt051~4A1-C!q(3&o_)naS4*mXdD6tkoV&psC-NvE&%@NiaroJP zGwN)c4j6PRw73*eQe`ce=W+h}{$pc}KDlN1=XqypWg&9(pMp}Z*wf(bL+YiA-m5Ly zDq2|HuTMLJnNSo;eMgm_y&e%@xFa4ch@)Xj3Aaa%BvmNzKnmwKofPn-|`M`+rl-iZypIreRvk| ziAI_{-f;V!Lhy~1Hp_>Hoj07Hxv+cmUkb3!78ddsiorLYzEWZ)J{T@dA^{@mWRqg| zlJRu;YU$1ZM#Q2YXRphLt*`_Sw?iMG*PGbV>}^Ez0@iOz^q;hAwIH zd7*q>pLu)56Lw0c*kA9OcCRqaZhlbmj`m|vHT6#{L6_x)UOop_CbwW}wEyqE5bsz* zOJ1L#4oQ4o8}Da48T{{yNu`<}q ztCM=c6P8pefo`%kBGpRMETk+ovK)`l$~mFV#fNSEWVyZg*=K^ZP0OfacNyx~o#**Y zB%?b64L38IZHvf%NS))eqH4bJpooi@IK^YEv4{FmN%B1jW#)~?Jb$e|4yw?r`|m^c z`I-=NxJTl=TgPP-1JqpmisGwhDwHnXLVtEa@*+d^kz8(XG;t^lEZzW6WfH_JY zaUGV#ZM^LqatN&cW38Am*Ps4jYU|A<3(zza^#!e+RCeB*t zCwk3ZpxLrtN)=37@`fmB(QF{gT#6lji?y%58`+h8e!j*pWdwephRJ!V!0ktNg$J~% zqV~mWx%n}L-#*>DpaFV1=f5y$eLlv8v}G6Qsy}3fD7tKD;a`_ib-=;6wu)Dj1hc$oleVl4GX0N8ju3d|8-s|2Pw~!qJSp8 zEc{@NXH2&cZPZdqzwvLI7vu47sH|uxfu=5hviW;vIJa=iK#!4`EO2SO$}qUe8vKHd z@)jpFp70joPJRnhU)3E7yj$XrL&v79ZYl4Bc7G}!5ZT$^k_DYR{`Zbupi%aqmAd`E zrKDvEi2mQ?|FN1uaoxA2dkgLP2n&5iMq&4AB6_&V?~-{!wXw52S_ ztu4G+{*w{}G@#+#vQ+e4M{YOfe>tM=?${FlkGe`uxXyn_5-lur9MytP@NQ)rhq*ME<)ViuVI4kaH@XYxPwx3MJ`!3oqW`FXXxj*G z2{VQ1b|WolpiSWaMnnitu3P;zJ!ocXyX==Mw&eS7m_AZP$f4cs`JZt2*~3_95&!a7 z=`LA-CBDh?gk`N{Do;`n@!q2103CKY;k;eZ0EfQ$h|dYK%}ff-=J>Z9H>@MN4+G^= zb`&jLfGhITH49PzcIQ9XS>0gfG|%5}sJN%$VYdc{noz085=`Kj<^#RM_{FPw|ADRZ z=zl))VMXlI-7-Ttl!i~G-QC(X%MAjdgv-N8idQxNn}|3l%ptEJ*hEr7n;2ZV3}96k@iTUNn@&~v_OHhfU1@domq-oe^kVAXr-ucRf4kOBgzf` z^q+ffa?`SJCU1OP<-Da(jRAUac|8el6$H-zc7r0xx^pWdTjDSgZ4mzX3D5xqUOk)` z&Y5Q+HMr$9w8BffJNZ^NSu+0m2ojdD2iN;IICiB?y~nklp@7eWR24N9Dq)u4{{uMk BvWx%# literal 0 HcmV?d00001 diff --git a/imagepy/menus/Window/develop_wgts.py b/imagepy/menus/Window/develop_wgts.py index c3957043..eeb6154f 100644 --- a/imagepy/menus/Window/develop_wgts.py +++ b/imagepy/menus/Window/develop_wgts.py @@ -1,4 +1,4 @@ -import wx +import wx, wx.lib.agw.aui as aui from imagepy.core.manager import WidgetsManager from imagepy.menus.Plugins.Macros.recorder_wgt import Plugin as recorder from imagepy.menus.Plugins.Manager.console_wgt import Plugin as console @@ -15,7 +15,7 @@ def __init__( self, parent ): sizer = wx.BoxSizer( wx.VERTICAL ) mrecorder = recorder(self) - self.notebook = wx.aui.AuiNotebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.aui.AUI_NB_DEFAULT_STYLE ) + self.notebook = aui.AuiNotebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, aui.AUI_NB_DEFAULT_STYLE ) self.notebook.AddPage( mrecorder, mrecorder.title, True, wx.NullBitmap ) self.notebook.AddPage( console(self), console.title, False, wx.NullBitmap ) self.notebook.AddPage( plglist(self), plglist.title, False, wx.NullBitmap ) diff --git a/imagepy/menus/Window/widgets_plgs.py b/imagepy/menus/Window/widgets_plgs.py index c535b083..ee00c448 100644 --- a/imagepy/menus/Window/widgets_plgs.py +++ b/imagepy/menus/Window/widgets_plgs.py @@ -35,7 +35,7 @@ class TableWindow(Free): def run(self, para = None): if IPy.uimode() != 'ipy': return app = IPy.curapp - info = app.auimgr.GetPane(app.tablenb) + info = app.auimgr.GetPane(app.tablenbwrap) info.Show(not info.IsShown()) app.auimgr.Update() diff --git a/imagepy/ui/canvasframe.py b/imagepy/ui/canvasframe.py index 6c14fd44..e0e41cf4 100644 --- a/imagepy/ui/canvasframe.py +++ b/imagepy/ui/canvasframe.py @@ -5,7 +5,7 @@ @author: yxl """ import wx, os -import wx.aui as aui +import wx.lib.agw.aui as aui from .canvas import Canvas from ..core.manager import ImageManager, WindowsManager from ..core.manager import ShotcutManager @@ -18,7 +18,7 @@ class CanvasPanel(wx.Panel): def __init__(self, parent=None): wx.Frame.__init__ ( self, parent) - self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_3DLIGHT ) ) + #self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_3DLIGHT ) ) self.SetSizeHints( wx.Size( 560,-1 ), wx.DefaultSize ) WindowsManager.add(self) @@ -144,15 +144,17 @@ def on_close(self, event): WindowsManager.remove(self.canvaspanel) event.Skip() -class CanvasNoteBook(wx.aui.AuiNotebook): +class CanvasNoteBook(wx.lib.agw.aui.AuiNotebook): def __init__(self, parent): - wx.aui.AuiNotebook.__init__( self, parent, wx.ID_ANY, - wx.DefaultPosition, wx.DefaultSize, wx.aui.AUI_NB_DEFAULT_STYLE ) - self.Bind( wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_pagevalid) - self.Bind( wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close) + wx.lib.agw.aui.AuiNotebook.__init__( self, parent, wx.ID_ANY, + wx.DefaultPosition, wx.DefaultSize, wx.lib.agw.aui.AUI_NB_DEFAULT_STYLE ) + self.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_pagevalid) + self.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close) + self.SetArtProvider(aui.AuiSimpleTabArt()) def add_page(self, panel, ips): self.AddPage(panel, ips.title, True, wx.NullBitmap ) + self.Refresh() panel.set_handler(lambda ips, res, pan=panel: self.set_title(ips, pan)) def set_title(self, ips, panel): diff --git a/imagepy/ui/mainframe.py b/imagepy/ui/mainframe.py index 2efec3aa..f58f8e4b 100644 --- a/imagepy/ui/mainframe.py +++ b/imagepy/ui/mainframe.py @@ -14,7 +14,8 @@ from ..core.engine import Macros from .canvasframe import CanvasNoteBook from .tableframe import TableNoteBook -import wx.aui as aui +#import aui as aui +import wx.lib.agw.aui as aui class FileDrop(wx.FileDropTarget): def OnDropFiles(self, x, y, path): @@ -29,7 +30,7 @@ def __init__( self, parent ): style = wx.RESIZE_BORDER|wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) self.auimgr = aui.AuiManager() self.auimgr.SetManagedWindow( self ) - self.auimgr.SetFlags(aui.AUI_MGR_DEFAULT) + #self.auimgr.SetFlags(aui.AUI_MGR_DEFAULT) logopath = os.path.join(root_dir, 'data/logo.ico') #self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_3DLIGHT ) ) @@ -62,7 +63,7 @@ def __init__( self, parent ): sizersta.Add( self.pro_bar, 0, wx.ALIGN_BOTTOM|wx.BOTTOM|wx.LEFT|wx.RIGHT, 2 ) stapanel.SetSizer(sizersta) stapanel.SetDropTarget(FileDrop()) - self.auimgr.AddPane( stapanel, wx.aui.AuiPaneInfo() .Bottom() .CaptionVisible( False ).PinButton( True ) + self.auimgr.AddPane( stapanel, aui.AuiPaneInfo() .Bottom() .CaptionVisible( False ).PinButton( True ) .PaneBorder( False ).Dock().Resizable().FloatingSize( wx.DefaultSize ).DockFixed( True ) . MinSize(wx.Size(-1, 20)). MaxSize(wx.Size(-1, 20)).Layer( 10 ) ) @@ -87,37 +88,48 @@ def load_aui(self): self.toolbar.GetSizer().SetOrientation(wx.VERTICAL) self.toolbar.GetSizer().Layout() self.toolbar.Fit() - self.auimgr.AddPane(self.toolbar, wx.aui.AuiPaneInfo() .Left() .PinButton( True ) + self.auimgr.AddPane(self.toolbar, aui.AuiPaneInfo() .Left() .PinButton( True ) .CaptionVisible( True ).Dock().Resizable().FloatingSize( wx.DefaultSize ).MaxSize(wx.Size( 32,-1 )) . BottomDockable( True ).TopDockable( False ).Layer( 10 ) ) self.widgets = widgetsloader.build_widgets(self, 'widgets', 'plugins') - self.auimgr.AddPane( self.widgets, wx.aui.AuiPaneInfo() .Right().Caption('Widgets') .PinButton( True ) + self.auimgr.AddPane( self.widgets, aui.AuiPaneInfo() .Right().Caption('Widgets') .PinButton( True ) .Dock().Resizable().FloatingSize( wx.DefaultSize ).MinSize( wx.Size( 266,-1 ) ) .Layer( 10 ) ) - self.canvasnb = CanvasNoteBook( self) - self.auimgr.AddPane( self.canvasnb, wx.aui.AuiPaneInfo() .Center() .CaptionVisible( False ).PinButton( True ).Dock() + self.canvasnbwrap = wx.Panel(self) + sizer = wx.BoxSizer( wx.VERTICAL ) + self.canvasnb = CanvasNoteBook( self.canvasnbwrap) + sizer.Add( self.canvasnb, 1, wx.EXPAND |wx.ALL, 0 ) + self.canvasnbwrap.SetSizer( sizer ) + self.canvasnbwrap.Layout() + self.auimgr.AddPane( self.canvasnbwrap, aui.AuiPaneInfo() .Center() .CaptionVisible( False ).PinButton( True ).Dock() .PaneBorder( False ).Resizable().FloatingSize( wx.DefaultSize ). BottomDockable( True ).TopDockable( False ) .LeftDockable( True ).RightDockable( True ) ) - self.tablenb = TableNoteBook( self) - self.auimgr.AddPane( self.tablenb, wx.aui.AuiPaneInfo() .Bottom() .CaptionVisible( True ).PinButton( True ).Dock().Hide() + self.tablenbwrap = wx.Panel(self) + sizer = wx.BoxSizer( wx.VERTICAL ) + self.tablenb = TableNoteBook( self.tablenbwrap) + sizer.Add( self.tablenb, 1, wx.EXPAND |wx.ALL, 0 ) + self.tablenbwrap.SetSizer( sizer ) + self.tablenbwrap.Layout() + + self.auimgr.AddPane( self.tablenbwrap, aui.AuiPaneInfo() .Bottom() .CaptionVisible( True ).PinButton( True ).Dock().Hide() .MaximizeButton( True ).Resizable().FloatingSize((800, 600)).BestSize(( 120,120 )). Caption('Tables') . BottomDockable( True ).TopDockable( False ).LeftDockable( True ).RightDockable( True ) ) - #self.canvasnb.Bind( wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_pagevalid) + #self.canvasnb.Bind( aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_pagevalid) def load_ijui(self): - self.auimgr.AddPane(self.toolbar, wx.aui.AuiPaneInfo() .Top() .CaptionVisible( False ).PinButton( True ) + self.auimgr.AddPane(self.toolbar, aui.AuiPaneInfo() .Top() .CaptionVisible( False ).PinButton( True ) .PaneBorder( False ).Dock().Resizable().FloatingSize( wx.DefaultSize ).DockFixed( True ) .BottomDockable( False ).TopDockable( False ).LeftDockable( False ).RightDockable( False ) .MinSize(wx.Size(-1, 32)). Layer( 10 ) ) self.widgets = widgetsloader.build_widgets(self, 'widgets', 'plugins') - self.auimgr.AddPane( self.widgets, wx.aui.AuiPaneInfo() .Right().Caption('Widgets') .PinButton( True ) + self.auimgr.AddPane( self.widgets, aui.AuiPaneInfo() .Right().Caption('Widgets') .PinButton( True ) .Float().Resizable().FloatingSize( wx.DefaultSize ).MinSize( wx.Size( 266,-1 ) ).Hide() .Layer( 10 ) ) def load_dev(self): return - self.devpan = wx.aui.AuiNotebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.aui.AUI_NB_DEFAULT_STYLE ) - self.auimgr.AddPane( self.devpan, wx.aui.AuiPaneInfo() .Bottom() .CaptionVisible( False ).PinButton( True ).Dock() + self.devpan = aui.AuiNotebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, aui.AUI_NB_DEFAULT_STYLE ) + self.auimgr.AddPane( self.devpan, waui.AuiPaneInfo() .Bottom() .CaptionVisible( False ).PinButton( True ).Dock() .PaneBorder( False ).Resizable().FloatingSize( wx.DefaultSize ) ) def on_pan_close(self, event): diff --git a/imagepy/ui/tableframe.py b/imagepy/ui/tableframe.py index 573fcfdf..eb77be8a 100644 --- a/imagepy/ui/tableframe.py +++ b/imagepy/ui/tableframe.py @@ -1,5 +1,6 @@ from .tablewindow import * -import wx.aui as aui +#import aui as aui +import wx.lib.agw.aui as aui class TablePanel ( wx.Panel ): def __init__( self, parent): @@ -74,12 +75,12 @@ def on_close(self, event): WTableManager.remove(self.tablepanel) event.Skip() -class TableNoteBook(wx.aui.AuiNotebook): +class TableNoteBook(aui.AuiNotebook): def __init__(self, parent): - wx.aui.AuiNotebook.__init__( self, parent, wx.ID_ANY, - wx.DefaultPosition, wx.DefaultSize, wx.aui.AUI_NB_DEFAULT_STYLE ) - self.Bind( wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_pagevalid) - self.Bind( wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close) + aui.AuiNotebook.__init__( self, parent, wx.ID_ANY, + wx.DefaultPosition, wx.DefaultSize, aui.AUI_NB_DEFAULT_STYLE ) + self.Bind( aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_pagevalid) + self.Bind( aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close) def add_page(self, panel, tps): self.AddPage(panel, tps.title, True, wx.NullBitmap ) From 66d351443be2244b0ddfa462232568bab9573b92 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 30 Oct 2018 04:08:06 +0800 Subject: [PATCH 040/343] aui --- imagepy/menus/Table/Chart/{__init__ => __init__.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename imagepy/menus/Table/Chart/{__init__ => __init__.py} (100%) diff --git a/imagepy/menus/Table/Chart/__init__ b/imagepy/menus/Table/Chart/__init__.py similarity index 100% rename from imagepy/menus/Table/Chart/__init__ rename to imagepy/menus/Table/Chart/__init__.py From ddf2463171dffdcc55ffaf027699f8b528bb65e8 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 30 Oct 2018 08:07:42 +0800 Subject: [PATCH 041/343] watermark --- imagepy/__init__.py | 2 +- imagepy/data/logo.png | Bin 12895 -> 0 bytes imagepy/data/watermark.png | Bin 0 -> 4867 bytes imagepy/ui/canvasframe.py | 19 +++++++++++++++++++ imagepy/ui/mainframe.py | 2 ++ 5 files changed, 22 insertions(+), 1 deletion(-) delete mode 100644 imagepy/data/logo.png create mode 100644 imagepy/data/watermark.png diff --git a/imagepy/__init__.py b/imagepy/__init__.py index 8e2da5a5..052bd684 100644 --- a/imagepy/__init__.py +++ b/imagepy/__init__.py @@ -23,7 +23,7 @@ def show(ui = True): shadow = wx.Colour(255,255,255) # SplashScreen(bitmap, wx.adv.SPLASH_CENTRE_ON_SCREEN | wx.adv.SPLASH_TIMEOUT, 3000, None, -1) - asp = AS.AdvancedSplash(None, bitmap=bitmap, timeout=5000, + asp = AS.AdvancedSplash(None, bitmap=bitmap, timeout=1000, agwStyle=AS.AS_TIMEOUT | AS.AS_CENTER_ON_PARENT | AS.AS_SHADOW_BITMAP, diff --git a/imagepy/data/logo.png b/imagepy/data/logo.png deleted file mode 100644 index d7939e421a78ac1d1c32f6d26983fd9564a83eb2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12895 zcmcJ0`6EsGnTPr9XloaGO}kIN|8O1rI=(3p%k*s*s??sA(4?K%D!Yt&DbJR zlC6-XWQijCFms>ze8111@H{`wnRD+w_uPB#d7t-dImtFwXW3bWSOEZFH#0qb9sn5V zMFxQRzn=)9Q&j+PLfY)Kk$qVHT7hc)g62b$4SA174=TDwOTwvMrZMkfg?^)cM0$mD zn!CT#3VWlLZFZZ|8&${fo}(-nP>NtNn_s?UbCGHtD-{;##lR@`X;BRT$l>k6)AU=Q zAp`)_%A)Bt&v5kr_hLZ+P-OjoQR$Wz05~pggss9V5lIXd5MnmjQLTi7uC z>o&UY@^W)wqmGeqjSNf;Io&xDPFU^$BI7I|tSTC~l)lb~hylw$h;Yn<54cAFTRM!F zt{1TbL?#5_hTTBoL@@wBFzEnh)#72mJO(7o*x(7H^v1eML;+a{B5*OoU^XKJ0FJyU zfL`Wjg4R#7>3A`NI42B{WW2|T(L$4lZ4N<4K@nfx2ZO&mqczSNFUC#iJ zML;%K?&aV@kSv5IaDmK(ZJL%dSV1+5VxR#|EIVnUC~|4~{mYFBWbm15>e} ztXebDLa*li-%}vHg(2^ec8S~4*#~G>F?Iqg8q0GS(9}X)635U80B;8%bd%pME;PcR z$9l@+7aO?R0eo{0`HSJ3hS%p#QH^%m@v!SIp)SH;*M^nBNA>P z2C&6~7N%^|f8}s z2(24A&o&y`AZa0TwmpYnExvuukidNPX@9i_2I4Ddxts`5PL zwSFjD@U{MY^8Gjlbp{C2&p0gZUk*Qwt*~ou&eCE&j0JOxD}PzH_bXKrRc4%&5x#PnO#8 zh(aPGa7GwH#}b0Z|Fmpj7*@I|jG^%YQaIq4E$T2$@9g*rZ0rub%ftXIXfmdLWajNw zqSFYRAf?lVAZwt>KqV7RoVNd$tMed`RIPWvm)=IF(B)OZJEX{5VH8oTi~+W!khHHR zu;;5>$NiOeR8aryJZJYh(zV?k$B!Z^2?ipHM$xiWKf{nI0wO$n`SGiir6{&0H*U-p61KReF1UN+wS z9ERYqcOK8TT%+?%Y}@w%YpWG*5VC(~m45F%8ORHY{@V5^%Gn(M<<99gs9nil!sBF2 z#?AEkG!s=X256}F9;G>*HXqmgWKwMkaW?`>`Tq1%^noH*8a-xZ36|1AN(^^-qsngI zZ;FW#OK*9zoaqiJ5I~>v#k42}{IoNk>SR9x=ghzpv>&~H-*Jokdcsa0y))Pb0*e2;kBue3my~S)SjQ^Fg9Vt)0FEd**uN#I(Am2_neqM`Wf?+_U_nusF0yT#8h;842eBg3JI=2ZMoh2^tQfvJu2d$*hy1%TNJd1)fTl*&K*5)^p65%N zsPr>hEajJ$)ebfA%m1t?0(Ib79FSm9LF@4>#5hxodSG zz!Ck3P3-n3{a=>){FDjdG!)eS(MPL(of`EWsmC1 zHT+X8W%IlyT&|;{zVPxhd+q&-VfEOwEA)@EwOzPbGhJI+Y?cWnaqh;(bHco6i?7cM+H(0N zRj)#oLwv@P^G#Jgn#YGDJeoEbc>>ZAg3^bp_rAzl1!kQ0pSY7I;{ZdV*(~QgOjLzi zM7zjPY8-g!OVyrQ45iTRX0mck@$|LfhGhjdqt%e0(H(%Y_SQ$+9`U#pl{a!j`-(Dn78o z`-BvkG6p3NLpRc17sSk{Gw!KG)3y%^qxY`ET7PFqcr!SWQ~cxyBiY&M9~pndJlSyA zV|{>uo#xJyf%J9K-LC68?ccwIL@%^ZGD1FM@n^7IXS0kfK?inwW`;2Jk&XcH_>;PC=&IYN#eq9 zdEAD4EVUkw9i@KFaXTEtyOzs~%iNTK`0zG6VI3x%oAAVpX-<^5-@J(Y$7Y5VAJZU=RdUfHJjQ;r)=S}} zb>qV^1TLYHPirG2>pjD?b0ECfq_ z4MJH98_XHTk{SH?SDXA|T*zk6wSc|uN*^FP5ie|8=qGH!pxAgtL;kPM1tN`JZ7FzS z=eN6ktBm*Y6JlwvzB>*QPsK4|04;TAJ5#^C@%!B^b}$1fns3Udy+Dd6Hy}tZr*O{U z#T{(m6RX;{7{naBv(*S?jV^l)pB_GYR8z>7>;zunChigPt(uuoM5I?2Jhp$^o6}ML zP-Rbo_<5SVOs&rb)vI8X)9#Frck*&Ehhg;Yp!&*;`Leq7592 zyYW_NnsCJw(XSW@FDM+&>}~Groo%AuC5;l@&}SpKh}>$+T=veYWes?Jmj47Hb{oIq z6tbuF3oP?PN`YPQ7xST8EM zxpSJ*ey60hCg@=cOxVds{*tJa{42Rl*=kSDG(NeD)_X}}8s?PUP&h?~ZaI}QjsyV2 zQ`AL?-FGkH9QB8(0o#c>rsanrGCaGRJIFR8CDV)xD57DT5rv)?MN=zuJ}wM=S$s4$ z%E5Nd{z1P0u)@~0*7QATNt_Hd5*ZmU-!U_*+i~JKW^xh*i2P#MPjF6WRS+WNr~o6X zd|GRsOJk^CLy9e>+i#hJaLL>@FbKrOHI|LQWY574e=2x4$m)S&=rt9^D*%A7O%35` zDh_`8TebhYG8d-}5^`kt&U0{!#z2F&2%X>4Txkv2j2=eZ#Lhzm zP|M)N6XWW*PMFu61-EfPf36UqP2%+Uf=hAPw%&E51RqCZC3kiKEcN>EbK0>QUk(cf z=lnb=iF-{bqW^V8LaX3B=-EI!ZWxE`a~c4oELvG@Bo3Qn67mj?7YLU{oYW(tQ8=qG zupQH#t784F7y7O**2&{JEja(=1cq>YcAEg5(!)j{QpC(rQde$5jtxAOcSd*W?BM<7 z#2I&Zq&(vv*)Z#cVFHmsS_@#wjADBCQ~YddlzLr*ck;GU>)DsBGPkxbVF;sjJlP-9 z1`qOWKAaPth>qI3kjYrm0jLYo_|S83e^wMK|BqzrN!N;l?deAxg>=L?ly7d-chm&oXRV(>A`c0SXtEv`EzZt4)Lu^lfv}0C-j*P9yz7F~V%DzR@bzmRSNspj zF4^$VND+Dy0;pFUnYohRmQtg_^u!vXf4V_}0QS^}yRd4}-?3L?GW9{3Yo`zx$R0Mz z7^@(ce>CF`x{Cscd|q96mX)=sU;A6_kt)sM5*`50&DV+VbXu;Wu?2H8*wM`uQ;o0W z^_qg+G#M6cI^0c^3%DO7l=4YA;E^}TO1faEAQs@S3q}yj;84!#;7EVY}e<($Ho6xl*oXxqn^Ul57S@a z58o=q!|#e%x7?$l++~;AGuz^f3Ty`}f6Gf)u0Eath};hW9sUt-9%Ruw4Zl1O%0*WN@IXBos5X1KMxXM3#vTTwGnU+V zL9+m38PSA`6orrdVzc&CNzhtJ(H&5;Wc2M{Q>ZILqDd=V0D?elm9z^l6VtjUWH(zY=~*{JcT z@4Pr*!TkbUyS#J4&b*v7rD9Q zc}qwo4lZusT0Z;wdP>X*y@39=H*~H-abLHDM=ywh2P)XzMlGa=YUBijn{^hH@-goZ3u}(DI(x^-mgl3g{y=2qD0gq<=al-sM-C< zKck1#gWc7NssZ!hvv00`Nivwj=dIEFVHf-xad`1>AiS4j-0TXwp@ zdOXkNK@vr*%3MK< zW4p~mI1*P+bjiS zC#;Lp7rcKGEQA0q+RV-Bx%>g`PAOam#hMJb_GJQWcE*qxM%Vjmzw^HL!n)fMXrIV{ zAR6m(){;;nTOFn=%L(&(I1LQp6R!+tX%+^Ukt^-_@%$|C!H2oW5wElm>F#?z>(>|Q znB&K*J&4gv4Uibqx==1A!py|)|4!2K2lMkYPnQD%66%U9ow45DVA z&Lt4p^>E=*E7p63OlDXi)GWi31ft72VcCf4)^~M34w=qK=h9B26dBj1mqHw5#byOk z45!JED-IaX&jbV#hy>(b{JgcY)A&Ft5sk+3HY7;s;ZE4Vb5eI%5&XAx37|*%1!muG zfdS149S}|~;NrTs`W`n*!o)$cya0LW*U>Pt2p6pjiy;X6M>D^zK#0wXj*w*Pq;LyA z0jTetm89$}_>@kV^yuUkdWiRKhJVgk!S4+uCxRG4gd%S&cXCoUR15l*TTrA94JD=q zLK|WR1|bJJ2)oh&ajf$F9D^7t2*=}tx#Vb5H{Y^2AGE4GWW)jI>k*K17`6>G<|HHT z23OE{@w}h^5g@y80AXWA;nUC-tBpaTPMcF%V7E)(S@}2}i?Ca@Is-k5-bbu+0zL)a z9Aq~vq)r_yP?XhCLk!!2XDeSg$jy5ndu)Mu5Bw{4I_h35&6$4dEqpkGAFu=KV*-dY z%jD_oX8hqxX<_YyUXKI`l*E&MaE|yb(LLsYURy8Xq=Y@bT=Ghqh9)LdGKt$p(y57P zo+$fvGto|l5>;Jq$Lkw&iQc8(86Rr4qv=E3`3a8OD!fP9# z^BcNiK?>}iCSv(S?h(9Vn`Y2DK_`oc4$88SKczw8o~G!0A|S9r(zqEA`nf(vE|Y#y zHXT(I6(i$U{sI_?MR^2&m5G!2oF=9ai33skeD!XTGc&PaX zWZYmCxh9-j$rTSSrT+$vx)AdE66y!?Ee{k#gaW%$ct;JT#FzF@_Z)p|%$?qZ^yq}7 z%^5I&Mt(Rp*Y8GBj3!rRuEC1<0pFDrOcZxe%hj4@#SL4l4+yVw40~J_X#a$g65j9R+HG@0qU)*W}adas}~m4#@ZKo zCdldj?~*9u+ReV6oD;#(e(st<%b;46a4INUyZ{=BI4y-^B_#Xio{LA{SzFl`B4Ct+w zXJmJCz#wV_k7wNfTqX`iOA6NVajbmIV8pPMOSaCi1a@c5@0eA5V3zOd)O!~2apdSa z=RT*oHoN@^HXjG5j~(94pD7Do#>XyW(O4Jv8%7;D?!wE98bgq~YQ0Uv^N+X6iVCa7Lcw7qo+!DEL8iDepDC56#o$WGN z8DkLoh1Pnr4=0(_m=D*2_qq5y*&)I&`!WVey9ch7auyF09x@+Z#yC*vQcnsFs#;d0 zUMY>3EFAlx30Cz&%u5%s!*J$u4x)Hp+>xF4 zZm~Obr7rgN*coP1lJ18Wq;O#h0nC+tGjLj0nE89W#l>BySZ>EOaJ4$@Xfh>Hu9Lo4 zv|v9YIu&iNf6ewbC7m7q)Ye2vywgQLplv*kFs>NBzBT?j&rN2;&d4qk_IZ#y^|gSN4I#4Rc?EW~98U(wLdL?(FRX&hs(-!mv4$&| z)J)TL+h?5fZ0Jw%A!UliI6`eHx!^pX7gx%!TeodWCMN6h=%a%Rug{lg(SUT@@bAxi z*{EfVXHneF;d4#t{ix+=wc_%B5>q`N7--a?B!BgUq0Ql~tHq1{h3PzdiLL()IotVT z!NrP2wgmj^JO4I+xWSaM`9rta9=^t((V5e^AMa+f=@ve@f%{YcNeqR%%nP|HU0t$y z-@3YfZ5y7KnaP?w)-m2G%u?^ZmL?R_wl?RN9kqi4xQu&O&f)_iS}svZdsWSyJC*NB zmkP}$v3(ghGjG?{)dnP8Z;Kuz64i1l;0=ku*5?J{MSuRbZUcNb8`4|(xFLctiW1}S z{DLj~Lv`jwbd(S)3Rhgf-ksNon}r!50(V=h@uCJGKMKz)2KFNx$%U^I|0xGYUP!8D z|9S3zM*>(gebPN~fBY;}<>SHFA#dB^<46mZ?V0ioLYk0_X};+43>m=boTYS9 zs-CnyFf)hWRe_A`oMDox(EDW@SO5)FCTMKua0N3@FBc&>Wif=sO*uE5H?t`m6Iv_` zgxtwTPd{_kdx1}CtljPfA1+0Ik|ySy_~spMokw3)*tQK%A%O8IhxKOh^-=ss0Z%t- z*9F9oDYfAtA$Sj_{H-(*aQ^b=R0BP*&BpfQX1t!~ouQr@SaHb zhT{@@-Hcxj7s;`d!mk3cHw}-3ZE!Dy6tGW!qK_Asmp%UZ94VtGR_HqOrtvh(5cLEG z$mM#zf=^CZ$Dh(ZuFm#;@oF=Pxd#VeT=xE6q^9xW>pSr#l4rgg#DSGoe%s;(JiObV zj#klnM)9>Q_c}W4wFkXY%VEK1|5`f$oLD|TezhF-+$&rMRy%aF2xC3hYDKy#D3f!Q zw55Zsbda1MW{cOQ=ZbGAKNuTgK{ATo0R57+58`?_x`<nqA11+u?jZu z<44xDx4m_4KjuiR|7fW*6Ia%~MExEfrz&MVYfxRm(-U zX>^rt960jO0Z#kz&xL+EEkqATJ@{(4WR6$1nd-H7pe_FbQ>ibCzh`E=hO!7R(#!GI zXBG}7@8Ib=!NAR-2@yR9HrBL6M*o${L5Ir^RR>ppY|psqh4ve{#e!LOm9&F%jq~F` z*ULA_h!IklhP&wN_bI0slv51vw-h?9*RA)3(H#LqE<3dkmq;tBxBdooLdLh{aY3uQ z+-$F;B&Q4g%3`7@GJ3NrdmKchrJJ70is94=p3aT1FT6L{PW~>vm~Ym2xNmDjHKdZ4 zzWXqqAaJ|CS-imd_X{di<<5RioZrjaVA=hIcmh!&!=vkhEf9rIMtKClz zv&i|4c3w)mN=JDumvR-y*6jZ|?O*XO{p>Nw|FW)a$mz+*?uyGc=S8;Y5;> z`PblUi6dN(GovF(9TZ60r@c|9C*@&koX6P(30dZnx~6wa2KYn$+*aWE{m8M#TJQL`#~C*l70n zIqlAuVnGj{-&c6>jw1{|#H4%g_f_x2m{QF>7Qd!3KrM4zNBs4r=fK^UHYU+t8S92= z)IGMvKiC_;o*!M>(JeYk2R+Hv{F!_4`hB_q#;$3?U0GblZC*|J%P2(pQBk%MH(Hmk zJ5$Dw9HZ)=a>BQq9c5zmEh#c^|u#2Ut&>~yu<_p9j$+yb6sG* z;H`6pdPPw~toG(ADF(cR>)%mQ@PGa4akyH+7+z%Xc13$B(;Jhwn-AnPY!X!oEX!)V z{$C$EbY+YU+-diejTn1cS=M>44hPsUtOUl(m6CEN-|Mj~d-HR}r*BeoJkJaw3pg9L z3*VQ(H&6IDaM#ws`)gm#?x#Dz$)Wc=P-Jz@*9C(PN(nYp8MWj~@d-4It1$Zs!nJD| zj1ttYy5i-UY#&b(XEQ9n!b`jalqe|(<7r+oy za<2lo2a(YC$sZ%)-J)d(VECl4Lg>VMbY(_!;_s4UzGyP{NMCby0+xOGw!c^2_=F6L z#11``awxLs&WL8~9&wYUu9#teS3YY(N^xiy4bz!%oX~Xl=Dxsk;SrkDmShpl+kIH` ztYrCjX!uD(bs0T7o>239{P%;JRqc^&;SWQJ<#Cs&13Lu{MLh+{q7ADjjvw5daqY>X zg8|@@+Eje~2x-r!^_ICbcZy;&IA^VlJ_y7Cw%Kbur*coU`x-r_1k%}+(wUC<7IS0T zgj06-hAI#&LJ{w8k4VAFM7S?IM z<4U)mVG@zu3~-)S$e2AgX??6T@qB~4UfIu4`>RtOXu{=RzrQ-K;P1yK;u}ZrZ}MO4 zDc>sJZ(E3o7x;Kl_8p)%*9_~6J#hSb*4*ZnKKWkp&+l*gU+cf2)F2SJyv>7OA0hW) z@#^Y7+SKJWN^V{31A$SibXLQe`}i+T_if>Qy5Hx=IK(|H{$kX_ozv&sSAQ#Mn*a+8 zb0`1>kxP;2<`|Aa^Dr{2Cb6_{vWy|QiYc9zOn2S|y)n6~&lz@TQuw8GF?no)&~(it zN^$Vpg;RcXS+iS@oW%a~)#<%EpR3kOHT*LCiD+K@4Gck+|80~Z*YxqL`Rt~~w6P~+ z-^KJoGx{*gjiyzl^jf=ODu@l2U}UG zy&The*9qhF>G>AX`sVUvC&;IxWwfi8@{H8BkLs2eH*Y!If2uF(Q1fXT zIx)r*F#h$e{=cjew|u$Mhp1VePm5pr;wj-fD%TeW;bAlK=G8lTuB2bVMjTg^B9b(g zYs=uY^r%!?X4CJ4v2-x<*D&CfLP!~psn9bIH@bSq;4{hpI);V$_KB#2>8YoWPr^#g zfxTJh%@DQf|Ek{kIYxHz-%hd1RNPMF!|@NXrxq=I2YYJkR^tddEso&&+9qBAEiaaj z8(gPj#USAEL~x66S8)tom=n+TXiA05NAcNA(B6kiY51t9y);@jQVK+JgwBp3qzvjt zE+^=t$@*PC3_`i|d?hys>kE0bM8jr}!NG!ukKBS#!2L@v-qo8Cn&gM>j-}l0rg2MG zv*Q4wwcn#l=U$y>gWp^b)!A*Rms6czD#i&d33MAE59kVB$_(orPT_y^mp^`P&-ZZ* z=8?C(ssw??U0OZIm8a$>WUFZxL^or zA&}C_sqH43$3CFQHVcw_Cu*A z@fn;QTN(I-!o6*Dl>&+7QO>22$cXyqPW!*(6;DpdJ~aV709)3F!0k0ZYL!>`Zne78 zOP-f1+E-;*n16z1{kK}Zwq!#GhVM0~6sOP=B=wZYtG%lj=;zL(0p}YQ1@n(Af9trZ z)On-!3(RV+nB8c(LF`}nrPCBFGubqK)%9L$1ls{2{V3RXj=?9X>-qnSIW&c0rBY8vCO81KIgB-qS8!ja(i!2-}|Ovo^5Z8!k(^1 zmNOQmy)Ah()Q9*cS6ty#bmao)(0O>C<;Vre@rUPV$v2xoXu5=p6K$GCd0DA`A-X(4gA3{- zdc_dy2R-h}fDx?uL=uDZCZRT!op4i{Z}o3J0Qp_Oqj1uJabos#|v}m?>d1- zmt)&rTk?to-2god2`G1$ch4XHTiCsAJH5j(`SIo$F-A8j?O7Zp@k^!&HF!>!pm#IE zV$6?}cSe2wpXSbCXZ3c@WN?t~#rf^CVIm_W;2ici^Cp2ZZS^6Wu&TE6gHU^3r+n5S zXIjLB$7ENt4-hgQ(&Bn@2TQzY?^rA)nNFZvNr7|ep%UMBVjOpXlkZ{zl8>yAkdUlL z64C~K?^%f~v5seNU(&R{OA#jn{qO$)?_b^*X~bnAnXOejPVI6h5NX~Rf_#132xCo~ z9VG%g)?vm}FFSRWdz-`nl^-96!bTH^zChf5kB|R)XECPbJT*D*5>M$R1H64}t~mvJ zue=|8Q}7Zx8AkB%`&83yhQ>n(l>w&>6x7pRFkDH`9Ix<<0HLh5-Mu zZ+A5AC%rAD%lmRJic56yj}&#s(f|ArB~BZsw~jYO1UOInsR4KJXQ)r;hDf^x_azWT z6zy@z@u<-X3dQbUSu+fnmLk^k-mN#In?C7-T8{T))srx%PL}d|iAP0o6B+WPk&ur` zDV?@oYhhRVttDI`V!!@`3$+U{DzTx6lG|dnA7kA``;_Fesx)d^zUP05#}Sc4uU5K4 z)mDUi;_6y~micwj{T~9bR*}QnnBd?;(_^B=BqZ^wCl1gbY66d`w0hopKHS@XLth|= zqdH&W2E6c89(hCo0WHOc%M;i|d?-|V?<(s|g}gCM_O!Jp4hU8U2r=}CpRedq0deze zL-lpK|1t0y_0~`|-QfhP3!x`+Cpi@rGjaV0&6MKhu{WikH;tPb$-v#uBM8@)9f<9w zco&hKF|92nJkb`PWSV^xO~z3s$YKrI7@I%4fktCIb=M%_HZ{fI*OvF5Cp%Wefw@Ey zd$*4-=h#uo>BYi#2u*3nbNr>gAOgp%tBKL-G$=gKUY#W0)m5WY6EUD8ql}M{(?yjq ze%E|lSG_pVs;UE6jb$Gx9)5FAJYGd+LT!p~sMG)l7?4r*KA&IuiN(0dOe`%UWzSn< z0Pk5wRmzq}kBRG>AxrA#XEYpe-)RbU=Jw^P$$&8wWgKr)WQzkbYm)>gmaCNl_PET^ zj(?gr)pMAj+)SNAa`fVAaN&j2D|;<}#15=Oj$jBEY6DrSUq7Qx`hnvQ!p_lYZh6<> zT`hl!cT=CHRp>T~6BA^%pFP@W0IQIFo58+^ja~|N2x7?UK1MGY{t9Mb?AAa7WORNW zn4-!`ix diff --git a/imagepy/data/watermark.png b/imagepy/data/watermark.png new file mode 100644 index 0000000000000000000000000000000000000000..ad8fb25c3a109114bc24bd0632c583cdd343d8b6 GIT binary patch literal 4867 zcmZu!X*3jm*!|5|$5OI}Y}v9t?0uVXk z?&g*|wJ<~O79$YQXlqNdAo)D+Io@IdCKs22dx`rGBA07x2$zKkN&jgD9fju*nwa_y zLKlHR2yz(y(N27hQzm3Pp{>9EHLy5bikR#0xMUr()9tr?)1uS2vZ043BzH;C?QG~Kiy%U7S@7O#|L;g) zLVo*HCz@FCa2ism?9Mr>)q%f$|2pgStQS{a@V{KIBvpKK_w?%ZYV4kC`|`j!F;*f8 z7)eUXcq{QJ+3~BY?q3s>&Mj`}Xh=GUr{r$hF*CwSxx+YO#C}PYO2ycVIcL~~y|;g* zhCi@LF+l$Lykz^?FkFY#A*DL| zs7k^YzhuA6wAeJD`9^0lzvEJO*MR-NK{ZkPXRDXYw<9^YEXRkKQaOL?0D~3#m8KD$ zh?>>Nmc&wf%m$eZ9Q)rk-P4O8Hq5UybU5{RSn?v7qm3)3r+m zOoq}1{GB!JhV6gatHr*VkG%?0p+v#0vYqP846VIvhDuko)%UkvYz;EUFg<$Y#Umsi zqu*E(`!#n~a9EXC3rb|4&1wc0hS)KOGyfTba1y0)#XoK;k! zZ}cCYHf-IXjD#?=gD7#ZWdg|0L6;09%?>JDPK$7Xfyv>c^^>eN_us@+rLRpc<-_wr zbLAs(S$1d`dVY<3Qfx!eJtb3%QwUXptmXc~c6YPk1O z19%979*&1?NI;taszquQ8LunGA2vX>JalZ$65Z&r!^{a$+JOO#YV-)=jRiY8R9SO^n>J$&ElAn1d-Ix7OZ$9XVA#es5vvIZZGk1w z#~lz7*CmE&}{i_mdmj+4cS4n8gjdq}F6rQHa|$%)>_vwyagv z%MTpo89?$eU)y>@3k2FA17~M^tQcQJoE$ocWOEY%zb~KmaY(4+D&@1dg=U5l8}F^2 zZ{1di$w{YoXt5>ZO$EDjtyA^CQkQt4qu0ru!vfFsTEwpeUr?UDyEKIr&3ObtBphcn zcj!%BZy3;a=k8>OBPMn<5sBqcf?C=dHgQ7WH21s%{ir`VE0S8r8HwpTI%{comDzoN zkccux{hZ5djtD^}uQ zu*eAtQ0b;mT~QTZ+7GhGc4hm~V%BI#NZwfJ#xqBu(`F&Y?`#ACT}gl#x1Gz758zd5 z3K^HP-adm!lyVWSKt6LdIi}`5VJ2kSt@T|zK6mcwj_!Ne6&U$+N2W$NUZ`1<>K{CZ z6Pco8&!MM|dJM;=TspN$4TRIMiPFU&+@6W!yaS=J%eQE&M}-(bnle=_Pc_@vWq#)9 z=W@%;q8N5;F>f}?ee}s?_$b#m!C!=bTd9~{;*SU%Yk&Icw8F)j_L(^OO`X#VE}Dch zLyoei`x0Eo2K%oLAzri8C=VxvooqFF^(Q16OPl^2|PL1;ShgLkeQ&?l`1WrFhMpxljB0S00&yM zVvK0+c{|$;NB*j2sPpI5BQN@l?|%ja7_U;lHySqU{8)w_C}rRwpsbmpVbf`gv3%h0 z=**J7Th>JmJRPFsU{+lQT{N)!-blY=gpEW|y@vUDYY5Cc`Y3<=k%$0X{i3F_H_RVD zTbFE67Gdi;JEgwxm4|cS4pV^AR$QrSu7qe;2doHb?VsLh@1bW$?;os+5p)~J6~Q20 z!1xtpg@}@*6IZ$-s`YG}+_&@_%a3-TS9O66 z`wcI)bt08Lv+K96R;H2IDkoDW8@th(+cMqqIrAuDwKz3&oBB!Q@#MBySJj5GOs34e zpzxk8MPX~bWG>2|^e&&FHbr)~lv~N&f3%$Zg!^E z5_Lv}2@ieiOK4{4pNboIChcsD>VX{jMpB;_%;&C(*sldI)D~$dE^Kt} z#A2f6P^5cZ)PeXTB|^pzN&lAv|*OpP%DjU-%YW!z@+}KMk|916Vq5 z^}-GwHIs{Wwl)HAt2=M7>9hoWLiHlvLCa-X?OS!6FB~vKT>BS9b%satwz)?Z%hpc7 zW)Zl5Tyb&s=$+pd6Ln4c0*&7xr@7#6ztq~PSK8+OPOwSCcA#o5AlqBis#JtMx$he$ z>q&F}c(^%}bp&(z+7mJ#DZe|@FG<@?{vvn|BG9p7`8^j)j^s&Lni`18E;nc;-KVPX zKqW5BoNjqLya^W6t$nZXJOEp{rf|~nMYm{|CCI*KQou^|Jud{PP^VU_m(P+U52J{# z?{Ez!1E*ZQ0G$Me=Qr6VD``1hqL{!GLijACYiq*{V-C(&C*u-Vf1j1VD|U|s^|IsK zE3&sU(H5HV2iS2ijBJ_|a=O1d?pyG%=R$L|hLELrBQVM31{hV)57AvkR&L$TIgVNL zHUe*PZbd>yN9L|=<>lXdsB55qBstiK|%0L<3a8SaIu^GT#8eJUOc zH!B_QTNPu10(p)NWLJoj;gx*X3E?ly`G!x(f;rUA+>4q5E1k<|n`!k24SxzV!i`svnA-ZD+{91R zfU;Gt+Ow%&()T4S z3pzSGIjKz|6`WwFE45_N6iCTnpL*{EHTx%Y76XD2M!@pg681~qHIMR{$xkdKJK=`n z0~8D^(G$ssr?q%Xj%$*h8(#&V?h0d$wmUTf7nVJi9^PrRxX!L;Sm>-wr9@6e+@m_W z!X^tC62_k#Hz%KOEbOo+s$Jnt__^&$t$)95&p=x*x1YMSv-i{OV)tW@3%#TDP{;pF z%17-hrhi0EwADXS)JDoC?_PYVOluv0bvG@@izXXrIttE+Ucg(brxn;zQBcd^MVm*oQui)kd`YjbS*{Lk zMs%zGvwm5yr}(C2picQ?>tu*mZ;gq?9m}oRodsbLI(A}Q-^z-N37(zTPyU6NR1@G8n~({Z2}`Ju4a zrlDUCzD0ky7X2)f1ZYN5)%XS6-7nCd$65|wT|xtT0gd%E{xRf zaJjz8(|*pr+kx|=`H-k{#bexR;_6&M7{G1{x_F*e#JY~HST)&5ZqRH5q&Dv7bGEFD zyE5xRQgK_hm*)gLu+Zw;^Y-PLEs@&FA+e(&Cy)cc94wc_r&gMHw`@QuTZ0RzdEPHF zomc;i=Uj_lBM@u7v$HcsLZRS@#<|bY$wn6j&B(VU(p4Gb(SnWi_=ll`zsF?w@)_r1 zD`e=L>Xe=dK(La#A2X~kX1T`glUk&gVh2Ex>DTK{hxr zh1y`Lfb(_oWWndg2&OP`$YF9=^$Szqd>dBsV3Bhhz0^yK$iyGt#pQ1;obT)t2SE~| z0Z%TidHi^X957um0r`mj2@>C=K6y^=qnf^4S$9&^X4U!;B&psRrv_| z;obS%P-s?(<^$TdwFXtpsy^I_vJ|`y9cgs-AQzL?z!o^a#lr|H=WpL&tr*jc;rN1~ z>cXYPu}r9Hf4Yg=uftR0GxBbh&i#1GipZnI2oUxzbawC?|JzHGu$)u4(aZ#%$bGcck2ebX6A6j{K)uebx>XPXrC=RZ5E|!AEE650s zJH|&Yj?&nar#`*s&_3L(JSv2g++co`P*1-%Y$H(Z*vVe}v7p>I$Pc$(!Xc=8B)zFx z4NO_h<9{waq@Q`F932(AQ>ySIg~kYzA`R&VHP1)`$cK_Emj?`&QhFS|Y0S7zbN1iy zBO!%Mu*O>{mpSfBzTm+oV4v{QqikQBJIh&`t|d9-Aw~sH)EKqK`v&w&3%ERf-~2?{ zTec;Mp{j&~J}7C}Q9~l2B@w4DeSNG0TBk!yUq_Vu{!<*s&wCA~-u!{ynD6=-xwv=E z_}eC=#6o`cj_Fe6r3p+}DP)OyLoqqiQ!V}Y=4JTz3m}}0#AUm4EYISVkCojf8Gu%|Ub64zisq$oGxO9k1D^z-Q(|9! zg9N`iLk~0%3oxY~mV0}Mm@JS6zP=4d#j!VewZ!B#Lftr-E|@t6a9Tj!qQl80KC+N0 zdtPzyFAg`U_k$KI%z5z_gCro~*!;o*pl+Ko42EL=PZh>C{9i}DaOV literal 0 HcmV?d00001 diff --git a/imagepy/ui/canvasframe.py b/imagepy/ui/canvasframe.py index e0e41cf4..53ce6725 100644 --- a/imagepy/ui/canvasframe.py +++ b/imagepy/ui/canvasframe.py @@ -144,6 +144,24 @@ def on_close(self, event): WindowsManager.remove(self.canvaspanel) event.Skip() + +class MyArtProvider(aui.AuiDefaultDockArt): + def __init__(self): + aui.AuiDefaultDockArt.__init__(self) + self.bitmap = wx.Bitmap('data/watermark.png', wx.BITMAP_TYPE_PNG) + + def DrawBackground(self, dc, window, orient, rect): + aui.AuiDefaultDockArt.DrawBackground(self, dc, window, orient, rect) + + + memDC = wx.MemoryDC() + memDC.SelectObject(self.bitmap) + w, h = self.bitmap.GetWidth(), self.bitmap.GetHeight() + dc.Blit((rect[2]-w)//2, (rect[3]-h)//2, w, h, memDC, 0, 0, wx.COPY, True) + + #dc.DrawBitmap(self.bitmap, 0, 0) + #dc.DrawRectangle(rect) + class CanvasNoteBook(wx.lib.agw.aui.AuiNotebook): def __init__(self, parent): wx.lib.agw.aui.AuiNotebook.__init__( self, parent, wx.ID_ANY, @@ -151,6 +169,7 @@ def __init__(self, parent): self.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_pagevalid) self.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close) self.SetArtProvider(aui.AuiSimpleTabArt()) + self.GetAuiManager().SetArtProvider(MyArtProvider()) def add_page(self, panel, ips): self.AddPage(panel, ips.title, True, wx.NullBitmap ) diff --git a/imagepy/ui/mainframe.py b/imagepy/ui/mainframe.py index f58f8e4b..3bdfa8f6 100644 --- a/imagepy/ui/mainframe.py +++ b/imagepy/ui/mainframe.py @@ -115,6 +115,8 @@ def load_aui(self): self.auimgr.AddPane( self.tablenbwrap, aui.AuiPaneInfo() .Bottom() .CaptionVisible( True ).PinButton( True ).Dock().Hide() .MaximizeButton( True ).Resizable().FloatingSize((800, 600)).BestSize(( 120,120 )). Caption('Tables') . BottomDockable( True ).TopDockable( False ).LeftDockable( True ).RightDockable( True ) ) + + #self.auimgr.SetArtProvider(MyArtProvider()) #self.canvasnb.Bind( aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_pagevalid) def load_ijui(self): From d3eb48e3af8855d0a8767847cbeb6dbb8cc114c0 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 9 Nov 2018 11:25:49 +0800 Subject: [PATCH 042/343] imagej samples --- imagepy/core/manager/windowmanager.py | 6 ++-- imagepy/menus/File/Samples ImageJ/__init__.py | 0 .../File/Samples ImageJ/ijsample_plgs.py | 34 +++++++++++++++++++ imagepy/menus/File/__init__.py | 2 +- imagepy/menus/Plugins/Manager/shotcut_wgt.py | 1 + imagepy/tools/Measure/angle_tol.py | 2 +- imagepy/ui/mainframe.py | 2 +- imagepy/ui/plotwindow.py | 3 +- 8 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 imagepy/menus/File/Samples ImageJ/__init__.py create mode 100644 imagepy/menus/File/Samples ImageJ/ijsample_plgs.py diff --git a/imagepy/core/manager/windowmanager.py b/imagepy/core/manager/windowmanager.py index 6568b16f..e3a52550 100644 --- a/imagepy/core/manager/windowmanager.py +++ b/imagepy/core/manager/windowmanager.py @@ -191,18 +191,18 @@ def remove(cls, win): def get(cls, title=None): if len(cls.windows)==0:return None if title==None:return cls.windows[0]() - titles = [i().canvas.ips.title for i in cls.windows] + titles = [i().title for i in cls.windows] if not title in titles:return None return cls.windows[titles.index(title)]() @classmethod def get_titles(cls): - return [i().canvas.ips.title for i in cls.windows] + return [i().title for i in cls.windows] @classmethod def name(cls, name): if name==None:name='Table' - titles = [i().canvas.ips.title for i in cls.windows] + titles = [i().title for i in cls.windows] if not name in titles : return name for i in range(1, 100) : diff --git a/imagepy/menus/File/Samples ImageJ/__init__.py b/imagepy/menus/File/Samples ImageJ/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/menus/File/Samples ImageJ/ijsample_plgs.py b/imagepy/menus/File/Samples ImageJ/ijsample_plgs.py new file mode 100644 index 00000000..9a2c3cf5 --- /dev/null +++ b/imagepy/menus/File/Samples ImageJ/ijsample_plgs.py @@ -0,0 +1,34 @@ +from skimage.io import imread +import sys +if sys.version_info[0]==2: + from urllib2 import urlopen + from cStringIO import StringIO +else: + from urllib.request import urlopen + from io import BytesIO as StringIO + +from imagepy import IPy +from imagepy.core.engine import Free + +class IJImg(Free): + def __init__(self, title, name): + self.title, self.name = title, name + + def __call__(self): return self + + def run(self, para = None): + try: + response = urlopen('http://imagej.net/images/'+self.name) + stream = StringIO(response.read()) + img = imread(stream) + IPy.show_img([img], self.title) + except Exception as e: + IPy.write('Open url failed!\tErrof:%s'%sys.exc_info()[1]) + +plgs = [IJImg(*i) for i in [('Leaf 36K', 'leaf.jpg'), ('Lena 68K', 'lena.jpg'), ('MRI Head 47K', 'mri.gif'), + ('AuPbSn 40 56K', 'AuPbSn40.jpg'), ('Blob 356K', 'blobs.gif'), ('Baboon 56K', 'baboon.jpg'), + ('Boats 25K', 'boats.gif'), ('Bridge 174K', 'bridge.gif'), ('Clown 14K', 'clown.jpg'), + ('Lymp 17K', 'lymp.tif'), ('M51 177K', 'm51.jpg'), ('FluorescentCells 400K', 'FluorescentCells.jpg'), + ('Microm 32K', 'microm.jpg'), ('SmartSEMSample 780K', 'SmartSEMSample.tif'), + ('NileBend 1.9M', 'NileBend.jpg'), ('Diatoms 60K', 'Diatoms.jpg'),('Tree Rings 48K', 'Tree_Rings.jpg'), + ('Cartwheel Galaxy 231K', 'Cartwheel_Galaxy.jpg'), ('Cell Colony 34K', 'Cell_Colony.jpg')]] \ No newline at end of file diff --git a/imagepy/menus/File/__init__.py b/imagepy/menus/File/__init__.py index 1223da2f..ffd194e8 100644 --- a/imagepy/menus/File/__init__.py +++ b/imagepy/menus/File/__init__.py @@ -1,3 +1,3 @@ ### TODO: Fixme! In this directory, many path should be corrected?! catlog = ['new_plg', '-', 'open_plg', 'save_plg', '-', 'Open Recent', 'Samples Local', 'Samples Online', - '-', 'Import', 'Export', '-', 'BMP', 'JPG', 'PNG', 'TIF', 'GIF','DICOM','DAT', '-', 'exit_plg'] \ No newline at end of file + 'Samples ImageJ', '-', 'Import', 'Export', '-', 'BMP', 'JPG', 'PNG', 'TIF', 'GIF','DICOM','DAT', '-', 'exit_plg'] \ No newline at end of file diff --git a/imagepy/menus/Plugins/Manager/shotcut_wgt.py b/imagepy/menus/Plugins/Manager/shotcut_wgt.py index 9887f23e..8c1a2e11 100644 --- a/imagepy/menus/Plugins/Manager/shotcut_wgt.py +++ b/imagepy/menus/Plugins/Manager/shotcut_wgt.py @@ -106,4 +106,5 @@ def on_run(self, event): #PluginsManager.plgs[self.buf[event.GetIndex()][0]]().start() def __del__(self): + print('hahaha') ShotcutManager.write() \ No newline at end of file diff --git a/imagepy/tools/Measure/angle_tol.py b/imagepy/tools/Measure/angle_tol.py index 5205477c..902c8bfe 100644 --- a/imagepy/tools/Measure/angle_tol.py +++ b/imagepy/tools/Measure/angle_tol.py @@ -58,7 +58,7 @@ def draw(self, dc, f, **key): a/=norm(v1,axis=1)*norm(v2,axis=1) ang = np.arccos(a)/np.pi*180 for i,j in zip(ang,line[1:-1]): - dc.DrawText('%.0f'%i, f(*j)) + dc.DrawText(' %.0f'%i, f(*j)) def report(self, title): rst = [] diff --git a/imagepy/ui/mainframe.py b/imagepy/ui/mainframe.py index 3bdfa8f6..13ad36a9 100644 --- a/imagepy/ui/mainframe.py +++ b/imagepy/ui/mainframe.py @@ -36,7 +36,7 @@ def __init__( self, parent ): #self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_3DLIGHT ) ) self.SetIcon(wx.Icon(logopath, wx.BITMAP_TYPE_ICO)) IPy.curapp = self - self.SetSizeHints( wx.Size(900,700) if IPy.uimode() == 'ipy' else wx.Size( 600,-1 )) + self.SetSizeHints( wx.Size(900,700) if IPy.uimode() == 'ipy' else wx.Size( 580,-1 )) self.menubar = pluginloader.buildMenuBarByPath(self, 'menus', 'plugins', None, True) diff --git a/imagepy/ui/plotwindow.py b/imagepy/ui/plotwindow.py index e71d6286..2b45c6a2 100644 --- a/imagepy/ui/plotwindow.py +++ b/imagepy/ui/plotwindow.py @@ -135,7 +135,7 @@ class PlotFrame ( wx.Frame ): @classmethod def get_frame(cls, title, gtitle='Graph', labelx='X-Unit', labely='Y-Unit'): if PlotManager.get(title) == None: - PlotManager.add(title, cls(IPy.curapp, title)) + PlotManager.add(cls(IPy.curapp, title)) PlotManager.get(title).set_title_label(gtitle, labelx, labely) return PlotManager.get(title) @@ -143,6 +143,7 @@ def __init__( self, parent, title): wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = title, pos = wx.DefaultPosition, size = wx.Size( 500,300 ) ) + self.title = title logopath = os.path.join(root_dir, 'data/logo.ico') self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_3DLIGHT ) ) self.SetIcon(wx.Icon(logopath, wx.BITMAP_TYPE_ICO)) From 1eb25f4a35d64530fde6a9f06108b509a6a75aff Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 19 Nov 2018 16:34:46 +0800 Subject: [PATCH 043/343] 3d stl --- imagepy/core/myvi/canvas3d.py | 44 +++++++++++++-- imagepy/core/myvi/imgs/open.png | Bin 0 -> 1159 bytes imagepy/core/myvi/imgs/stl.png | Bin 0 -> 449 bytes imagepy/menus/Kit3D/Viewer 3D/__init__.py | 2 +- .../menus/Kit3D/Viewer 3D/surface2d_plg.py | 34 ------------ .../{surface3d_plg.py => surface_plgs.py} | 51 +++++++++++------- 6 files changed, 74 insertions(+), 57 deletions(-) create mode 100644 imagepy/core/myvi/imgs/open.png create mode 100644 imagepy/core/myvi/imgs/stl.png delete mode 100644 imagepy/menus/Kit3D/Viewer 3D/surface2d_plg.py rename imagepy/menus/Kit3D/Viewer 3D/{surface3d_plg.py => surface_plgs.py} (57%) diff --git a/imagepy/core/myvi/canvas3d.py b/imagepy/core/myvi/canvas3d.py index db79c19a..85d3c0a0 100644 --- a/imagepy/core/myvi/canvas3d.py +++ b/imagepy/core/myvi/canvas3d.py @@ -93,6 +93,15 @@ def save_bitmap(self, path): memory.SelectObject( wx.NullBitmap) bitmap.SaveFile( path, wx.BITMAP_TYPE_PNG ) + def save_stl(self, path): + from stl import mesh + objs = self.manager.objs.values() + vers = [i.vts[i.ids] for i in objs if isinstance(i, Surface)] + vers = np.vstack(vers) + model = mesh.Mesh(np.zeros(vers.shape[0], dtype=mesh.Mesh.dtype)) + model.vectors = vers + model.save(path) + def OnMouseWheel(self, evt): k = 0.9 if evt.GetWheelRotation()>0 else 1/0.9 self.manager.set_pers(l=self.manager.l*k) @@ -124,8 +133,10 @@ def __init__( self, parent, manager=None): self.btn_orth = wx.BitmapButton( self.toolbar, wx.ID_ANY, wx.Bitmap( osp.join(root, 'imgs/parallel.png'), wx.BITMAP_TYPE_ANY ), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) tsizer.Add( self.btn_orth, 0, wx.ALL, 1 ) tsizer.Add(wx.StaticLine( self.toolbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL), 0, wx.ALL|wx.EXPAND, 2 ) - self.btn_save = wx.BitmapButton( self.toolbar, wx.ID_ANY, wx.Bitmap(osp.join(root, 'imgs/save.png'), wx.BITMAP_TYPE_ANY ), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) - tsizer.Add( self.btn_save, 0, wx.ALL, 1 ) + self.btn_open = wx.BitmapButton( self.toolbar, wx.ID_ANY, wx.Bitmap(osp.join(root, 'imgs/open.png'), wx.BITMAP_TYPE_ANY ), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) + tsizer.Add( self.btn_open, 0, wx.ALL, 1 ) + self.btn_stl = wx.BitmapButton( self.toolbar, wx.ID_ANY, wx.Bitmap(osp.join(root, 'imgs/stl.png'), wx.BITMAP_TYPE_ANY ), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) + tsizer.Add( self.btn_stl, 0, wx.ALL, 1 ) self.btn_color = wx.ColourPickerCtrl( self.toolbar, wx.ID_ANY, wx.Colour( 128, 128, 128 ), wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE ) tsizer.Add( self.btn_color, 0, wx.ALIGN_CENTER|wx.ALL, 1 ) @@ -177,7 +188,8 @@ def __init__( self, parent, manager=None): self.btn_x.Bind( wx.EVT_BUTTON, self.view_x) self.btn_y.Bind( wx.EVT_BUTTON, self.view_y) self.btn_z.Bind( wx.EVT_BUTTON, self.view_z) - self.btn_save.Bind( wx.EVT_BUTTON, self.on_save) + self.btn_open.Bind( wx.EVT_BUTTON, self.on_open) + self.btn_stl.Bind( wx.EVT_BUTTON, self.on_stl) self.btn_pers.Bind( wx.EVT_BUTTON, lambda evt, f=self.on_pers:f(True)) self.btn_orth.Bind( wx.EVT_BUTTON, lambda evt, f=self.on_pers:f(False)) self.btn_color.Bind( wx.EVT_COLOURPICKER_CHANGED, self.on_bgcolor ) @@ -223,6 +235,31 @@ def on_save(self, evt): self.canvas.save_bitmap(path) dialog.Destroy() + def on_stl(self, evt): + filt = 'STL files (*.stl)|*.stl' + dialog = wx.FileDialog(self, 'Save STL', '', '', filt, wx.FD_SAVE) + rst = dialog.ShowModal() + if rst == wx.ID_OK: + path = dialog.GetPath() + self.canvas.save_stl(path) + dialog.Destroy() + + def on_open(self, evt): + from stl import mesh + filt = 'STL files (*.stl)|*.stl' + dialog = wx.FileDialog(self, 'Open STL', '', '', filt, wx.FD_OPEN) + rst = dialog.ShowModal() + if rst == wx.ID_OK: + path = dialog.GetPath() + cube = mesh.Mesh.from_file(path) + verts = cube.vectors.reshape((-1,3)).astype(np.float32) + ids = np.arange(len(verts), dtype=np.uint32).reshape((-1,3)) + norms = np.linalg.norm(cube.normals, axis=1) + norms = (cube.normals.reshape((-1,3)).T/norms).T + norms = np.hstack((norms, norms, norms)).reshape((-1,3)) + self.add_surf_asyn('stl', verts, ids, norms, (1,1,1)) + dialog.Destroy() + def get_obj(self, name): return self.canvas.manager.get_obj(name) @@ -260,6 +297,7 @@ def add_surf(self, name, vts, fs, ns, cs, obj=None, mode=None, blend=None, color if obj!=None and not obj is self:return manager = self.canvas.manager surf = manager.add_surf(name, vts, fs, ns, cs) + surf.set_style(mode=mode, blend=blend, color=color, visible=visible) if len(manager.objs)==1: manager.reset() diff --git a/imagepy/core/myvi/imgs/open.png b/imagepy/core/myvi/imgs/open.png new file mode 100644 index 0000000000000000000000000000000000000000..165409ab3e8d5650f4b446097f41e7863957f0bf GIT binary patch literal 1159 zcmbVMOKj9e7VDySro`-fdQ4({x!NLMmcx&n70Z$FV2b zO-`YQsOg1AFMS|_kPvV|LR>)ffz%5i4j^vrjRV&Taex9+I^OK2JwzO8$(|Ye|GxkG zUtS&?{k*5^qb`b~dWyqFnXJ9hy`_^p_q<#MWEsHuaeNq6aSa5J%Gk(+bkPM9und7c zKl1=4DXL@6sf^?C(jnbKZVW^=vAXLMG({zc>OQchAf`0w5r7fMNw7pQ(EK&nd1dckXS*}Wm)HWx_L3gTVPl9vQcRIBBvBH ziLtM9T&-4%)ub2-COARUG>#WJQDlh)8_s(e)LAdw+hQ-i;5hV z6uHH-5nfaId?A|lNw9XuCcN#M+#L#1k%VhMhepI2+Gz9y5hv5nMiOp4 zVrA|7&l|hLe7M8jcjNuN-*iMonN7OOkGXL7y#r#!9b#a^^%G0~2C!hqhCu@m#XRmk zocf1>6ykV4Y3eaH&^1H&2hbuDLsggucXv*c{S3s#vn2zBTDDnvo+YY=8v1*Ds`~E? zyLKng;hvp-2DTt}^)k>CQ@@@RY8__f@uOl|Hf@<9sftEm9<{f_NPhuX+5p$0mYw8a z1u@iPxDROI^!PCq6H%?otlg;GnLy>DT0~}As1hWQG-!ZuVChlwj4AZi{VUy|kAE}g zx|kU$E4}~-GBthi=9MU~o4bZuwHr-sPl?iyMceQV0J)lwG_0Jh2}DEX>q%xH&=-Sf z1Q#}@e9Dq!!)f^V_qOfDJ%Jz-iv?pnhQe(?a<^gLI&)1`f*qH3Uxr$+g?jet@R9IR r1mPA|!eicsaZ|Xt8{qgbl9M+8KW(5u(o^eV00000NkvXXu0mjff40fa literal 0 HcmV?d00001 diff --git a/imagepy/menus/Kit3D/Viewer 3D/__init__.py b/imagepy/menus/Kit3D/Viewer 3D/__init__.py index 70f0ca08..9bc41dd0 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/__init__.py +++ b/imagepy/menus/Kit3D/Viewer 3D/__init__.py @@ -1 +1 @@ -catlog = ['surface2d_plg', 'surface3d_plg', 'colorpts_plg', '-', '2DSurface Demo.mc'] \ No newline at end of file +catlog = ['surface_plgs', 'colorpts_plg', '-', '2DSurface Demo.mc'] \ No newline at end of file diff --git a/imagepy/menus/Kit3D/Viewer 3D/surface2d_plg.py b/imagepy/menus/Kit3D/Viewer 3D/surface2d_plg.py deleted file mode 100644 index b57003cf..00000000 --- a/imagepy/menus/Kit3D/Viewer 3D/surface2d_plg.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Tue Jan 10 22:33:33 2017 - -@author: yxl -""" -from imagepy import IPy -from imagepy.core.engine import Simple -from scipy.ndimage.filters import gaussian_filter -from imagepy.core import myvi - -class Plugin(Simple): - title = '2D Surface' - note = ['8-bit', '16-bit'] - para = {'name':'undifine', 'scale':2, 'sigma':2,'h':1} - view = [(str, 'name', 'Name', ''), - (int, 'scale', (1,5), 0, 'down scale', 'pix'), - (int, 'sigma', (0,30), 0, 'sigma', ''), - (float, 'h', (0.1,10), 1, 'scale z', '')] - - def load(self, para): - self.frame = myvi.Frame3D.figure(IPy.curapp, title='3D Canvas') - return True - - def run(self, ips, imgs, para = None): - ds, sigma = para['scale'], para['sigma'] - vts, fs, ns, cs = myvi.build_surf2d(ips.img, ds=ds, sigma=para['sigma'], k=para['h']) - self.frame.viewer.add_surf_asyn(para['name'], vts, fs, ns, cs) - self.frame.Raise() - self.frame = None - #self.frame.add_surf2d('dem', ips.img, ips.lut, scale, sigma) - -if __name__ == '__main__': - pass \ No newline at end of file diff --git a/imagepy/menus/Kit3D/Viewer 3D/surface3d_plg.py b/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py similarity index 57% rename from imagepy/menus/Kit3D/Viewer 3D/surface3d_plg.py rename to imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py index debd9a3e..8e0f8b45 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/surface3d_plg.py +++ b/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py @@ -1,15 +1,42 @@ # -*- coding: utf-8 -*- """ -Created on Thu Jan 12 00:42:18 2017 +Created on Tue Jan 10 22:33:33 2017 @author: yxl """ -from imagepy.core.engine import Filter from imagepy import IPy +from imagepy.core.engine import Simple, Filter, Free +from scipy.ndimage.filters import gaussian_filter from imagepy.core import myvi -import numpy as np -class Plugin(Filter): +class Show(Free): + title = 'Show Viewer 3D' + asyn = False + def run(self, para): + myvi.Frame3D.figure(IPy.curapp, title='3D Canvas').Raise() + +class Surface2D(Simple): + title = '2D Surface' + note = ['8-bit', '16-bit'] + para = {'name':'undifine', 'scale':2, 'sigma':2,'h':1} + view = [(str, 'name', 'Name', ''), + (int, 'scale', (1,5), 0, 'down scale', 'pix'), + (int, 'sigma', (0,30), 0, 'sigma', ''), + (float, 'h', (0.1,10), 1, 'scale z', '')] + + def load(self, para): + self.frame = myvi.Frame3D.figure(IPy.curapp, title='3D Canvas') + return True + + def run(self, ips, imgs, para = None): + ds, sigma = para['scale'], para['sigma'] + vts, fs, ns, cs = myvi.build_surf2d(ips.img, ds=ds, sigma=para['sigma'], k=para['h']) + self.frame.viewer.add_surf_asyn(para['name'], vts, fs, ns, cs) + self.frame.Raise() + self.frame = None + #self.frame.add_surf2d('dem', ips.img, ips.lut, scale, sigma) + +class Surface3D(Filter): modal = False title = '3D Surface' note = ['8-bit', 'not_slice', 'not_channel', 'preview'] @@ -50,18 +77,4 @@ def run(self, ips, snap, img, para = None): self.frame.Raise() self.frame = None - - ''' - def run(self, ips, imgs, para = None): - from mayavi import mlab - volume = mlab.pipeline.scalar_field(ips.imgs) - if para['sigma']!=0: - volume = mlab.pipeline.user_defined(volume, filter='ImageGaussianSmooth') - volume.filter.standard_deviations = [para['sigma']]*3 - c = tuple([i/255.0 for i in para['color']]) - contour = mlab.pipeline.iso_surface(volume, contours=[para['thr']], - color=c, opacity=para['opa']) - mlab.show() - ''' -if __name__ == '__main__': - pass \ No newline at end of file +plgs = [Show, Surface2D, Surface3D] From 1e68454d7e2b5f3db8354f5c41355ca72e2d0001 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 23 Nov 2018 21:12:03 +0800 Subject: [PATCH 044/343] jpeg --- imagepy/core/myvi/canvas3d.py | 10 +++++----- imagepy/menus/File/JPG/jpg_plgs.py | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/imagepy/core/myvi/canvas3d.py b/imagepy/core/myvi/canvas3d.py index 85d3c0a0..c80cf8e0 100644 --- a/imagepy/core/myvi/canvas3d.py +++ b/imagepy/core/myvi/canvas3d.py @@ -6,7 +6,7 @@ from .manager import * import os.path as osp from wx.lib.pubsub import pub -from .util import build_surf2d, build_surf3d, build_ball, build_balls +from .util import * #---------------------------------------------------------------------- from wx.glcanvas import WX_GL_DEPTH_SIZE @@ -254,10 +254,10 @@ def on_open(self, evt): cube = mesh.Mesh.from_file(path) verts = cube.vectors.reshape((-1,3)).astype(np.float32) ids = np.arange(len(verts), dtype=np.uint32).reshape((-1,3)) - norms = np.linalg.norm(cube.normals, axis=1) - norms = (cube.normals.reshape((-1,3)).T/norms).T - norms = np.hstack((norms, norms, norms)).reshape((-1,3)) - self.add_surf_asyn('stl', verts, ids, norms, (1,1,1)) + norms = count_ns(verts, ids) + fp, fn = osp.split(path) + fn, fe = osp.splitext(fn) + self.add_surf_asyn(fn, verts, ids, norms, (1,1,1)) dialog.Destroy() def get_obj(self, name): diff --git a/imagepy/menus/File/JPG/jpg_plgs.py b/imagepy/menus/File/JPG/jpg_plgs.py index 3f17670f..35d0c81d 100644 --- a/imagepy/menus/File/JPG/jpg_plgs.py +++ b/imagepy/menus/File/JPG/jpg_plgs.py @@ -3,11 +3,12 @@ from imagepy.core.manager import ReaderManager, WriterManager ReaderManager.add('jpg', imread) +ReaderManager.add('jpeg', imread) WriterManager.add('jpg', imsave) class OpenFile(fileio.Reader): title = 'JPG Open' - filt = ['JPG'] + filt = ['JPG', 'JPEG'] class SaveFile(fileio.Writer): title = 'JPG Save' From d2b01bc5d12570266e9a29494d2321921e95b471 Mon Sep 17 00:00:00 2001 From: Prevalenter <4346liuxing> Date: Fri, 23 Nov 2018 22:00:16 +0800 Subject: [PATCH 045/343] add jpeg Signed-off-by: Prevalenter <4346liuxing> --- imagepy/menus/File/JPG/jpg_plgs.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/imagepy/menus/File/JPG/jpg_plgs.py b/imagepy/menus/File/JPG/jpg_plgs.py index 35d0c81d..336f4ae9 100644 --- a/imagepy/menus/File/JPG/jpg_plgs.py +++ b/imagepy/menus/File/JPG/jpg_plgs.py @@ -3,15 +3,15 @@ from imagepy.core.manager import ReaderManager, WriterManager ReaderManager.add('jpg', imread) -ReaderManager.add('jpeg', imread) WriterManager.add('jpg', imsave) - +ReaderManager.add('jpeg', imread) +WriterManager.add('jpeg', imsave) class OpenFile(fileio.Reader): title = 'JPG Open' - filt = ['JPG', 'JPEG'] + filt = ['JPG','JPEG'] class SaveFile(fileio.Writer): title = 'JPG Save' - filt = ['JPG'] + filt = ['JPG','JPEG'] plgs = [OpenFile, SaveFile] \ No newline at end of file From 5aa11e7e2a4344032fc730a6ac546970bbf13ddd Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 26 Nov 2018 17:27:14 +0800 Subject: [PATCH 046/343] workflow --- imagepy/core/engine/__init__.py | 3 +- imagepy/core/engine/workflow.py | 39 ++++++ imagepy/core/loader/loader.py | 15 ++- .../menus/Plugins/Coins Segment WorkFlow.wf | 19 +++ imagepy/ui/workflowwindow.py | 120 ++++++++++++++++++ 5 files changed, 191 insertions(+), 5 deletions(-) create mode 100644 imagepy/core/engine/workflow.py create mode 100644 imagepy/menus/Plugins/Coins Segment WorkFlow.wf create mode 100644 imagepy/ui/workflowwindow.py diff --git a/imagepy/core/engine/__init__.py b/imagepy/core/engine/__init__.py index 3bb226b7..ab846683 100644 --- a/imagepy/core/engine/__init__.py +++ b/imagepy/core/engine/__init__.py @@ -5,4 +5,5 @@ from .macros import Macros from .mkdown import MkDown from .widget import Widget -from .table import Table \ No newline at end of file +from .table import Table +from .workflow import WorkFlow \ No newline at end of file diff --git a/imagepy/core/engine/workflow.py b/imagepy/core/engine/workflow.py new file mode 100644 index 00000000..809b15d1 --- /dev/null +++ b/imagepy/core/engine/workflow.py @@ -0,0 +1,39 @@ +from imagepy.ui.workflowwindow import WorkFlowPanel +import threading, wx, os, wx.lib.agw.aui as aui +from imagepy import IPy + +def parse(cont): + ls = cont.split('\n') + workflow = {'title':ls[0], 'chapter':[]} + for line in ls[2:]: + line = line.strip() + if line.startswith('## '): + chapter = {'title':line[3:], 'section':[]} + workflow['chapter'].append(chapter) + elif line[1:3] == '. ': + section = {'title':line[3:]} + else: + section['hint'] = line + chapter['section'].append(section) + return workflow + +class WorkFlow: + def __init__(self, title, cont): + self.title = title + self.workflow = parse(cont) + self.cont = cont + + def __call__(self): + return self + + def start(self, para=None, callafter=None): + pan = WorkFlowPanel(IPy.curapp) + pan.load(self.cont, self.workflow) + info = aui.AuiPaneInfo(). DestroyOnClose(True). Left(). Caption(self.title) .PinButton( True ) \ + .Resizable().FloatingSize( wx.DefaultSize ).Dockable(IPy.uimode()=='ipy').Layer( 5 ) + + if IPy.uimode()=='ipy': info.Dock().Top() + if IPy.uimode()=='ij': info.Float() + IPy.curapp.auimgr.AddPane(pan, info) + IPy.curapp.Layout() + IPy.curapp.auimgr.Update() \ No newline at end of file diff --git a/imagepy/core/loader/loader.py b/imagepy/core/loader/loader.py index 22afe7a3..9f2caa3b 100644 --- a/imagepy/core/loader/loader.py +++ b/imagepy/core/loader/loader.py @@ -5,7 +5,7 @@ @author: yxl """ import os, sys -from ..engine import Macros, MkDown, Widget +from ..engine import Macros, MkDown, Widget, WorkFlow from ..manager import ToolsManager, PluginsManager, WidgetsManager from ... import IPy, root_dir from codecs import open @@ -31,6 +31,13 @@ def extend_plugins(path, lst, err): f.close() rst.append(Macros(i[:-3], [getpath(pt, i) for i in cmds])) PluginsManager.add(rst[-1]) + elif i[-3:] == '.wf': + pt = os.path.join(root_dir,path) + f = open(pt+'/'+i, 'r', 'utf-8') + cmds = f.read() + f.close() + rst.append(WorkFlow(i[:-3], cmds)) + PluginsManager.add(rst[-1]) elif i[-3:] == '.md': f = open(os.path.join(root_dir,path)+'/'+i, 'r', 'utf-8') cont = f.read() @@ -91,7 +98,7 @@ def build_plugins(path, err=False): if len(sub)!=0:subtree.append(sub) elif i[-6:] in ('plg.py', 'lgs.py', 'wgt.py', 'gts.py'): subtree.append(i) - elif i[-3:] in ('.mc', '.md'): + elif i[-3:] in ('.mc', '.md', '.wf'): subtree.append(i) if len(subtree)==0:return [] @@ -111,7 +118,7 @@ def build_plugins(path, err=False): def extend_tools(path, lst, err): rst = [] for i in lst: - if i[-3:] in ('.mc', '.md'): + if i[-3:] in ('.mc', '.md', '.wf'): pt = os.path.join(root_dir, path) f = open(pt+'/'+i) cmds = f.readlines() @@ -158,7 +165,7 @@ def build_tools(path, err=False): elif not root: if i[len(i)-7:] in ('_tol.py', 'tols.py'): subtree.append(i[:-3]) - elif i[-3:] in ('.mc', '.md'): + elif i[-3:] in ('.mc', '.md', '.wf'): subtree.append(i) if len(subtree)==0:return [] rpath = path.replace('/', '.').replace('\\','.') diff --git a/imagepy/menus/Plugins/Coins Segment WorkFlow.wf b/imagepy/menus/Plugins/Coins Segment WorkFlow.wf new file mode 100644 index 00000000..745296f1 --- /dev/null +++ b/imagepy/menus/Plugins/Coins Segment WorkFlow.wf @@ -0,0 +1,19 @@ +硬币分割演示 +============= +## 打开图像 +1. coins +这是一个硬币图像,我们现在来对每个硬币进行计数及测量 +## 图像分割 +1. Up And Down Watershed +勾上preview,调整滑块,使每个硬币多少染上红色,背景多少染上绿色,选择 up area +## 掩膜修复 +1. Fill Holes +我们看到有些硬币上带了缝隙,我们对其进行修复 +2. Geometry Filter +过滤掉小块碎片,勾上预览,area输100,小块区域变暗,back color 输 0,清除碎片 +## 区域测量 +1. Geometry Analysis +选择需要的指标,我们这里勾上cov,计算区域的误差椭圆 +## 成果导出 +1. CSV Save +将分析结果导出,保存成excel文件 \ No newline at end of file diff --git a/imagepy/ui/workflowwindow.py b/imagepy/ui/workflowwindow.py new file mode 100644 index 00000000..803f1d5d --- /dev/null +++ b/imagepy/ui/workflowwindow.py @@ -0,0 +1,120 @@ +workflow = {'title': '硬币分割演示', 'chapter': [{'title': '打开图像', 'section': [{'title': 'coins', 'hint': '这是一个硬币图像,我们现在来对每个硬币进行计数及测量'}]}, {'title': '图像分割', 'section': [{'title': 'Up And Down Watershed', 'hint': '使用高低阈值分水岭,调整高低阈值,使得每个硬币都或多或少染上红色,而背景染上绿色,而目标是高亮的,我们选择 up area'}]}, {'title': '掩膜修复', 'section': [{'title': 'Fill Holes', 'hint': '我们看到有些硬币上带了缝隙,这显然不是我们想要的,我们用二值填充进行修复'}, {'title': 'Geometry Filter', 'hint': '我们看到还有一些小块噪声,这些碎片面积比较小,我们用面积进行过滤'}]}, {'title': '区域测量', 'section': [{'title': 'Geometry Analysis', 'hint': '选择需要的指标,生成结果'}]}, {'title': '称多导出', 'section': [{'title': 'CSV Save', 'hint': ''}, {'title': 'CSV Save', 'hint': ''}]}]} + +import wx +from imagepy.core.manager import PluginsManager +from imagepy import IPy + +class WorkFlowPanel ( wx.Panel ): + def __init__( self, parent ): + wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.TAB_TRAVERSAL ) + + + def load(self, cont, wf): + self.workflow, self.cont = wf, cont + sizer_scroll = wx.BoxSizer( wx.HORIZONTAL ) + + self.scr_workflow = wx.ScrolledCanvas( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize) + self.scr_workflow.SetScrollRate( 30, 0 ) + self.scr_workflow.ShowScrollbars(wx.SHOW_SB_NEVER, wx.SHOW_SB_NEVER) + self.scr_workflow.SetMinSize((600,-1)) + + sizer_chapter = wx.BoxSizer( wx.HORIZONTAL ) + self.spn_scroll = wx.SpinButton( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.SP_HORIZONTAL ) + sizer_scroll.Add( self.spn_scroll, 0, wx.ALL|wx.EXPAND, 3 ) + + for chapter in wf['chapter']: + self.pan_chapter = wx.Panel( self.scr_workflow, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER|wx.TAB_TRAVERSAL ) + sizer_frame = wx.BoxSizer( wx.VERTICAL ) + + self.lab_chapter = wx.StaticText( self.pan_chapter, wx.ID_ANY, chapter['title'], wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE ) + self.lab_chapter.Wrap( -1 ) + self.lab_chapter.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_INACTIVECAPTION ) ) + + sizer_frame.Add( self.lab_chapter, 0, wx.ALL|wx.EXPAND, 0 ) + + sizer_section = wx.BoxSizer( wx.HORIZONTAL ) + for section in chapter['section']: + btn = wx.Button( self.pan_chapter, wx.ID_ANY, section['title'], wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) + sizer_section.Add( btn, 0, wx.ALL, 3 ) + btn.Bind(wx.EVT_BUTTON, lambda e, x=section['title']: PluginsManager.get(x)().start()) + btn.Bind( wx.EVT_ENTER_WINDOW, lambda e, info=section['hint']: self.set_info(info)) + #self.m_button1.Bind( wx.EVT_LEAVE_WINDOW, self.on_out ) + + + sizer_frame.Add( sizer_section, 0, wx.EXPAND, 3 ) + + sizer_btn = wx.BoxSizer( wx.HORIZONTAL ) + + + sizer_btn.AddStretchSpacer(1) + + self.btn_snap = wx.StaticText( self.pan_chapter, wx.ID_ANY, u" Snap ", wx.DefaultPosition, wx.DefaultSize, 0|wx.SIMPLE_BORDER ) + self.btn_snap.Wrap( -1 ) + sizer_btn.Add( self.btn_snap, 0, wx.ALL, 3 ) + + self.btn_load = wx.StaticText( self.pan_chapter, wx.ID_ANY, u" Load ", wx.DefaultPosition, wx.DefaultSize, 0|wx.SIMPLE_BORDER ) + self.btn_load.Wrap( -1 ) + sizer_btn.Add( self.btn_load, 0, wx.ALL, 3 ) + + self.btn_step = wx.StaticText( self.pan_chapter, wx.ID_ANY, u" >> ", wx.DefaultPosition, wx.DefaultSize, 0|wx.SIMPLE_BORDER ) + self.btn_step.Wrap( -1 ) + sizer_btn.Add( self.btn_step, 0, wx.ALL, 3 ) + + + sizer_frame.Add( sizer_btn, 0, wx.EXPAND, 3 ) + + + self.pan_chapter.SetSizer( sizer_frame ) + self.pan_chapter.Layout() + sizer_frame.Fit( self.pan_chapter ) + sizer_chapter.Add( self.pan_chapter, 0, wx.EXPAND |wx.ALL, 3 ) + + sizer_scroll.Add( self.scr_workflow, 1, wx.EXPAND |wx.ALL, 0) + + sizer_info = wx.BoxSizer( wx.VERTICAL ) + + sizer_info.SetMinSize( wx.Size( 260,-1 ) ) + self.btn_help = wx.StaticText( self, wx.ID_ANY, u" Click For Detail Document ", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE|wx.SIMPLE_BORDER ) + self.btn_help.Wrap( -1 ) + self.btn_help.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_INACTIVECAPTION ) ) + + sizer_info.Add( self.btn_help, 0, wx.ALL|wx.EXPAND, 0 ) + + self.txt_info = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_AUTO_URL|wx.TE_MULTILINE|wx.TE_READONLY ) + sizer_info.Add( self.txt_info, 1, wx.TOP|wx.EXPAND, 3 ) + + sizer_scroll.Add( sizer_info, 0, wx.EXPAND |wx.ALL, 3) + + self.scr_workflow.SetSizer( sizer_chapter ) + self.scr_workflow.Layout() + + self.SetSizer( sizer_scroll ) + + #self.Fit() + self.Layout() + + self.spn_scroll.Bind( wx.EVT_SPIN, self.on_spn ) + self.btn_help.Bind( wx.EVT_LEFT_DOWN, self.on_help ) + + def set_info(self, info): + self.txt_info.SetValue(info) + + def on_spn(self, event): + v = self.spn_scroll.GetValue() + self.scr_workflow.Scroll(v, 0) + self.spn_scroll.SetValue(self.scr_workflow.GetViewStart()[0]) + + def on_help(self, event): + IPy.show_md(self.workflow['title'], self.cont) + + + +if __name__ == '__main__': + app = wx.App(False) + frame = wx.Frame(None) + wfp = WorkFlowPanel(frame) + wfp.load(None, workflow) + frame.Fit() + frame.Show() + app.MainLoop() + \ No newline at end of file From de68a84765f38e86d36042f091d1659d4647c948 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 26 Nov 2018 17:34:06 +0800 Subject: [PATCH 047/343] workflow --- imagepy/menus/Plugins/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/menus/Plugins/__init__.py b/imagepy/menus/Plugins/__init__.py index e3689ef2..c496375c 100644 --- a/imagepy/menus/Plugins/__init__.py +++ b/imagepy/menus/Plugins/__init__.py @@ -1 +1 @@ -catlog = ['New', 'Macros', 'Manager', '-', 'Install', 'update_plg', '-', 'Edge', 'Surf', '3D'] \ No newline at end of file +catlog = ['New', 'Macros', 'Manager', '-', 'Install', 'update_plg', '-', 'screencap_plg', 'Games', 'Coins Segment WorkFlow'] \ No newline at end of file From 9eeb42379a06e0274c3fa11720bdda0ca57c5196 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 27 Nov 2018 02:40:32 +0800 Subject: [PATCH 048/343] drag macros and workflow --- imagepy/core/engine/macros.py | 17 ++++++++++++++++- imagepy/core/engine/workflow.py | 17 ++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/imagepy/core/engine/macros.py b/imagepy/core/engine/macros.py index f683b169..54cc6403 100644 --- a/imagepy/core/engine/macros.py +++ b/imagepy/core/engine/macros.py @@ -7,6 +7,7 @@ from ... import IPy from ...core.manager import PluginsManager from wx.lib.pubsub import pub +from imagepy.core.manager import ReaderManager, ViewerManager from imagepy import IPy def stepmacros(plg, callafter=None): @@ -45,4 +46,18 @@ def __call__(self): def start(self, para=None, callafter=None): self.callafter = callafter self.cur = 0 - self.run() \ No newline at end of file + self.run() + +def show_mc(data, title): + wx.CallAfter(Macros(title, data).start) + +ViewerManager.add('mc', show_mc) + +def read_mc(path): + f = open(path, encoding='utf-8') + cont = f.readlines() + f.close() + print(cont) + return cont + +ReaderManager.add('mc', read_mc, tag='mc') \ No newline at end of file diff --git a/imagepy/core/engine/workflow.py b/imagepy/core/engine/workflow.py index 809b15d1..f9091522 100644 --- a/imagepy/core/engine/workflow.py +++ b/imagepy/core/engine/workflow.py @@ -1,5 +1,6 @@ from imagepy.ui.workflowwindow import WorkFlowPanel import threading, wx, os, wx.lib.agw.aui as aui +from imagepy.core.manager import ReaderManager, ViewerManager from imagepy import IPy def parse(cont): @@ -36,4 +37,18 @@ def start(self, para=None, callafter=None): if IPy.uimode()=='ij': info.Float() IPy.curapp.auimgr.AddPane(pan, info) IPy.curapp.Layout() - IPy.curapp.auimgr.Update() \ No newline at end of file + IPy.curapp.auimgr.Update() + +def show_wf(data, title): + wx.CallAfter(WorkFlow(title, data).start) + +ViewerManager.add('wf', show_wf) + +def read_wf(path): + f = open(path, encoding='utf-8') + cont = f.read() + f.close() + print(cont) + return cont + +ReaderManager.add('wf', read_wf, tag='wf') \ No newline at end of file From f58ce2d8e8412f033fd3cdde4c76a727e4799558 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 4 Dec 2018 19:39:52 +0800 Subject: [PATCH 049/343] small bug --- imagepy/menus/Analysis/Region Analysis/statistic_plgs.py | 4 ++-- imagepy/menus/Process/Threshold/threshold_plgs.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py index 3d74a462..147643f2 100644 --- a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py @@ -46,7 +46,7 @@ class RegionStatistic(Simple): para = {'con':'8-connect','inten':None, 'slice':False, 'max':True, 'min':True,'mean':False, 'center':True, 'var':False,'std':False,'sum':False, 'extent':False} - view = [('img', 'intensity', 'inten', ''), + view = [('img', 'inten', 'intensity', ''), (list, 'con', ['4-connect', '8-connect'], str, 'conection', 'pix'), (bool, 'slice', 'slice'), ('lab', None, '========= indecate ========='), @@ -108,13 +108,13 @@ def run(self, ips, imgs, para = None): layer = {'type':'layer', 'body':[]} xy=np.int0(xy).T + texts = [(i[1],i[0])+('id=%d'%n,) for i,n in zip(xy,range(len(xy)))] layer['body'].append({'type':'texts', 'body':texts}) if para['extent']: layer['body'].append({'type':'rectangles', 'body':boxs}) mark['body'][i] = layer data.extend(list(zip(*dt))) - IPy.show_table(pd.DataFrame(data, columns=titles), inten.title+'-region statistic') inten.mark = GeometryMark(mark) inten.update = True diff --git a/imagepy/menus/Process/Threshold/threshold_plgs.py b/imagepy/menus/Process/Threshold/threshold_plgs.py index 6d42b436..3cf7c51a 100644 --- a/imagepy/menus/Process/Threshold/threshold_plgs.py +++ b/imagepy/menus/Process/Threshold/threshold_plgs.py @@ -57,7 +57,7 @@ class Auto(Filter): 'Mini', 'Mean', 'Triangle'], str, 'Method', '')] def run(self, ips, snap, img, para = None): - key = {'Otus':threshold_otsu, 'Yen':threshold_yen, + key = {'Otsu':threshold_otsu, 'Yen':threshold_yen, 'Isodata':threshold_isodata, 'Li':threshold_li, 'Mini':threshold_minimum, 'Mean':threshold_mean, 'Triangle':threshold_triangle} @@ -72,7 +72,7 @@ class Local(Filter): (int, 'offset', (0, 50), 0, 'offset', '')] def run(self, ips, snap, img, para = None): - img[:] = (snap>threshold_local(snap, para['size'], para['method'], para['offset']))*ips.range[1] + img[:] = (snap>threshold_local(snap, para['size'], para['method'].lower(), para['offset']))*ips.range[1] class Niblack(Filter): title = 'Niblack Threshold' From 674b71106cf7cca65048742c807a13365409a171 Mon Sep 17 00:00:00 2001 From: Prevalenter <4346liuxing> Date: Sat, 8 Dec 2018 17:28:10 +0800 Subject: [PATCH 050/343] add nearby and mean Signed-off-by: Prevalenter <4346liuxing> --- imagepy/menus/Process/Binary/distance_plgs.py | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/imagepy/menus/Process/Binary/distance_plgs.py b/imagepy/menus/Process/Binary/distance_plgs.py index a82d1c5c..54ff97d1 100644 --- a/imagepy/menus/Process/Binary/distance_plgs.py +++ b/imagepy/menus/Process/Binary/distance_plgs.py @@ -11,7 +11,7 @@ from imagepy.ipyalg import find_maximum, watershed #from skimage.morphology import watershed import scipy.ndimage as ndimg - +from skimage.measure import label,regionprops class Skeleton(Filter): title = 'Skeleton' note = ['all', 'auto_msk', 'auto_snap','preview'] @@ -84,6 +84,28 @@ def run(self, ips, snap, img, para = None): img[:] = (line==0) * 255 if para['type']=='gray line': img[:] = np.where(line==0, dist, 0) +class Unnamed(Filter): + title = 'Unnamed' + view = [(list, 'type', ['nearby', 'mean'], str, 'output', '')] + note = ['all', 'auto_msk', 'auto_snap','preview'] + para = {'type':'nearby'} + def run(self, ips, snap, img, para = None): + # return ndimg.distance_transform_edt(snap) + img1=snap + msk=ips.get_msk() - -plgs = [Skeleton, MedialAxis, '-', EDT, Watershed, Voronoi] \ No newline at end of file + if self.para['type']=='nearby':img[:]=self.nearby(img1.copy(),msk) + else: img[:]=self.mean(img1.copy(),msk) + def nearby(self,img,msk): + rr,cc=ndimg.distance_transform_edt(msk,return_distances=False,return_indices=True) + return img[rr,cc] + def mean(self,img,msk): + msk1=ndimg.maximum_filter(msk, 5) + lab=label(msk) + lab1=label(msk1*msk) + for i in regionprops(lab1): + index=np.array(lab1==i.label) + me=img[index].mean() + img[lab==i.label]=me + return img +plgs = [Skeleton, MedialAxis, '-', EDT,Unnamed, Watershed, Voronoi] \ No newline at end of file From 3ea2cb4f3c3433306dbdb30b24b543bebc71b645 Mon Sep 17 00:00:00 2001 From: Tong Date: Mon, 10 Dec 2018 23:02:13 +0100 Subject: [PATCH 051/343] translated several parts --- README.md | 421 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 261 insertions(+), 160 deletions(-) diff --git a/README.md b/README.md index 8128c28d..dcfc3d97 100644 --- a/README.md +++ b/README.md @@ -1,227 +1,328 @@ -ImagePy Basic Tutorial -============== -[https://github.com/yxdragon/imagepy.git](https://github.com/yxdragon/imagepy.git) + -**Introduction:** -ImagePy is an image processing software developed in Python, supporting bmp, rgb, png and other commonly used image formats. It can handle grayscale images and multi-channel (color) images, and supports image stack (sequence) operations. It supports a variety of selection operations (point, line, surface, multi-line, multi-face, hollow polygon). It can carry out a variety of commonly used mathematical operations, commonly used filter operation, image measurements, as well as pixel statistics. It can carry on dem surface reconstruction and three-dimensional reconstruction of image sequences. And the framework is based around Python development. The image data is represented by numpy. And thus it can easily access scikit-image, opencv, itk, mayavi and other third-party mature image processing libraries. + -Download and install ------------------------ -**works on windows, linux, mac, under python2.7 and python3.4+** +# Introduction -```bash -# Now ImagePy is on Pypi -pip install imagepy +ImagePy is an open source image processing framework written in Python. Its UI interface, image data structure and table data structure are wxpython-based, Numpy-based and pandas-based respectively. Furthermore, it supports any plug-in based on Numpy and pandas, which can talk easily between scipy.ndimage, scikit-image, simpleitk, opencv and other image processing libraries. -# Or install with conda -conda install imagepy +
+ Overview + Overview, mouse measurement, geometric transformation, filtering, segmentation, counting, etc. +
+ -# Then start imagepy like this -python -m imagepy -``` -**some trouble** -1. ImagePy is a ui framework based on wxpython, which can not install with pip on Linux. You need download **[the whl acrodding to your Linux system](https://wxpython.org/pages/downloads/)**. -2. On Linux and Mac, there may be permission denied promblem, for ImagePy will write some config information, So please **start with sudo**. If you install with pip, please add --user parameter like this: **pip install -user imagepy** -3. If you install ImagePy in a Anaconda virtual environment, you may got a error when start like this: **This program needs access to the screen. Please run with a Framework -build of python, and only when you are logged in on the main display**, if so, please start with pythonw -m imagepy. +
+ ij style + If you are more a IJ-style user, try `Windows -> Windows Style` to switch +
-Main Interface ----------------- -![](http://idoc.imagepy.org/imgs/main.png "main") -The main interface consists of four parts, from top to bottom: the title bar, menu bar, toolbar, and status bar. -Here are a few examples to illustrate what ImagePy can do. +ImagePy: +- has a user-friendly interface +- can read/save a variety of image data formats +- supports ROI settings, drawing, measurement and other mouse operations +- can perform image filtering, morphological operations and other routine operations +- can do image segmentation, area counting, geometric measurement and density analysis. +- is able to perfrom data analysis, filtering, statistical analysis and others related to the parameters extracted from the image. -First example: Mathematical operations, filter operations. --------------------------------------------------------------- -![](http://idoc.imagepy.org/imgs/astadjust.png "pixcels") +Our long-term goal of this project is to be used as ImageJ + SPSS (although not achieved yet) + -**Selection Introduction**: -Selection refers to processing the image only in the the specific identification areas on the image. ImagePy supports single point, multi-point, single line, multi-line, rectangular, circular, arbitrary polygon and free curve selection. It can superimpose something using Shift key, hollow out something using Ctrl key. In addition, all the selection objects can carry out expansion, shrink, convex hull and other geometric operations. -![](http://idoc.imagepy.org/imgs/astroi.png "ROI") +## Citation: +[ImagePy: an open-source, Python-based and platform-independent software package for bioimage analysis](https://academic.oup.com/bioinformatics/article/34/18/3238/4989871) -**Geometric Transformation:** ImagePy supports geometric transformations. It can carry out rotation, translation and other conventional matrix transformations. What’s more, these rotations are interactive and support selection. +# Installation -![](http://idoc.imagepy.org/imgs/asttransform.png "transform") +__OS support:windows, linux, mac, with python2.7 and python3.__4+ -Second example: An example of a cell count ----------------------------------------------- +1. ImagePy is a ui framework based on wxpython, which can not install + with pip on Linux. You need download [the whl acrodding to your + Linux system](https://wxpython.org/pages/downloads/). -**Look up table introduction:** -![](http://idoc.imagepy.org/imgs/indexcolor.png "index color") +2. On Linux and Mac, there may be permission denied promblem, for + ImagePy will write some config information, So please start with + sudo. If you install with pip, please add \--user parameter like + this: pip install -user imagepy -**Index color** is also called false color. The essence of it is to map the gray color to a predefined spectrum. The index color does not increase the amount of information in the image, but does enhance the visual contrast. +3. If you install ImagePy in a Anaconda virtual environment, you may + got a error when start like this: This program needs access to the + screen. Please run with a Framework build of python, and only when + you are logged in on the main display, if so, please start with + pythonw -m imagepy. -![](http://idoc.imagepy.org/imgs/cell1.png "cell counter") -![](http://idoc.imagepy.org/imgs/cell2.png "cell counter") +## Basic operations: -Here, for a cell under a microscope, we organize the image and compute statistics. +ImagePy has a very rich set of features, and here, we use a specific example to show you a glimpse of the capacity of ImagePy. We choose the official coin split of scikit-image, since this example is simple and comprehensive. -1. Open the original image and go on Gaussian blur to anti-noise. -2. In order to highlight the cells, a large-scale USM mask treatment was performed. -3. After processing the picture, it is easy to use the threshold function to carry on binarization. -4. Label the binary image, mark unicom area. -5. Calculate the centroid of each Unicom area -6. Calculate the area occupied by each cell + +### Open image +`menu: File -> Local Samples -> Coins` to open the sample image within ImagePy. +_PS: ImagePy supports bmp, jpg, png, gif, tif and other commonly used file format. By installing ITK plug-in,dicom,nii and other medical image format can also be read/saved. It is also possible to read/write wmv, avi and other video format by installing OpenCV._ -Third example: Image matching ----------------------------------- +
+ ij style +
-Use the Surf feature matching algorithm implemented in OpenCV. -![](http://idoc.imagepy.org/imgs/surf.png "surf") -1. The two graphs are covered by points, that is, Surf feature points, where the correct match is shown in yellow. -2. Also output a log of the opations. Identify the feature points of the two graphs, the correct number of matches, and the rotation matrix between the two graphs. -3. When a point is clicked with the mouse, the dot will be red with the corresponding match point of the other picture at the same time. +### Filtering & Segmentation -Fourth example: Dem Reconstruction --------------------------------------- -Use the mayavi library, to perform a large number of three-dimensional reconstructions and three-dimensional visualization functions. +`menu:Process -> Hydrology -> Up And Down Watershed` +Here, a composite filter is selected to perform sobel gradient extraction on the image, and then the upper and lower thresholds are used as the mark, at last we watershed on the gradient map. +Filtering and segmentation are the crucial skills in the image processing toolkit, and are the key to the success or failure of the final measurement. +Segmentation methods such as adaptive thresholds, watersheds and others are also supported. + -![](http://idoc.imagepy.org/imgs/dem.png "DEM") +
+ +
-**Dem** is the digital elevation model, which means that the brightness of the image represents the elevation. Through the Dem data, you can calculate the height, slope. You can draw contours, and perform surface reconstruction. +
+ +
-Fifth example: CT data 3D reconstruction --------------------------------------------- +### Binarization -The following image represents dental MicroCT data. The data were filtered, segmented and three-dimensional reconstructed, as well as visually manipulated. -![](http://idoc.imagepy.org/imgs/teeth.png "teeth") +`menu:Process -> Binary -> Binary Fill Holes` -The figure above is a tooth CT data. Importing the image sequence, you can view the three views, and then go on its three-dimensional reconstruction. +经过上述分割,我们得到了相对纯净的掩膜图像,但依然存在一些镂空,以及少许外界杂质,这些会对计数和测量造成干扰,我们对图像进行二值填充。ImagePy支持腐蚀,膨胀,开,闭等二值化基础操作,也支持骨架,中轴线提取,距离变换等操作。 -**Image Stack:** ImagePy supports image stack processing, it has the following two characteristics: +
+ +
-1. Images in the image stack have the same format and the same size. -2. They will act on each image in the stack when processed. +区域过滤 -Plugins and Macros: -------------------- +菜单:Analysis \> Region Analysis \> Geometry +Filter,对区域进行过滤,这里可以简单的通过面积进行过滤,ImagePy的几何过滤可以通过面积,周长,拓扑,丰满度,偏心率等指标对区域进行过滤,可以输入复合条件,正数表示选择大于等于,负数表示选择小于,通过过滤的被设定为front +color,没通过的设定为back color,back +color设定为100可以清楚看到有哪些被滤掉了,如果确认符合要求,back +color设定为0,即清除。同时ImagePy也支持灰度密度过滤,颜色过滤,色彩聚类等功能。 -In ImagePy itself, each functional component is plug-in (all menus, tools). The implementation of each function, in essence, is through interaction to get a group of parameters and then act on the current image. We can view the plug-in's organizational structure in Plugin Tree View, find plug-ins quickly in Plugin List View, record macros in Macros Recorder, and batch process when needed to do series of related functions and improve work efficiency. +
+ +
-![](http://idoc.imagepy.org/imgs/plgview.png "view") -From the two views above, you can get a global view of all the plug-ins, like viewing its related information, introduction, and source code. You can -quickly find the commands. You can run a related command directly by double-clicking. +区域过滤(这里为了看清效果,area设定的较大,实际只需要过滤碎片) -**Macro Recording of Cell Count Example:** -We open the Plugins -> Macros -> Macros Recorder plug-in, and then re-operate the cell counting process... -![](http://idoc.imagepy.org/imgs/step.png "step") +区域分析 -After each step, Macros Recorder will add a log. When all is completed, you can get the following log: +菜单:Process \> Region Analysis \> Geometry +Analysis,对区域进行计数和指标分析,这里我们勾上cov,即对区域进行协方差椭圆拟合。ImagePy支持面积,周长,偏心率,丰满度等指标,其实上一步的过滤正是在这里的分析结果基础上进行的。 -These logs, each line essentially records “plug-in name> {parameter}”. Click “Run> Run Macros (F5)” to perform each action of the record in turn. You can also use the mouse to select a line or a few lines. Click “Run> Run Line (F6)” to implement the selected line. In addition macros have the following characteristics. +
+ +
-![](http://idoc.imagepy.org/imgs/macros.png "macros") +区域分析 -1. You can save a file where the suffix of macro is (.mc). You can run the specified macro file via Plugins -> Macros -> Run Macros. -2. Put the macro file on the menus directory or any of its subdirectories in the project. Starting once again, the macro will be loaded as a menu item. The title is the file name. In fact, some project function are in series by the macro. +
+ +
-Extend a filter: ----------------- +支持xls, xlsx, +csv表格格式, +生成结果表格(这里为了看清椭圆,把区域亮度降低了) -The examples above only list some of the functionality of the ImagePy. However, ImagePy is not only an image processing program, but a highly scalable framework. Any numpy-based processing function can be easily incorporated. For example, to make a Gaussian blur filter, we only need: +按照面积排序 + +菜单:Table \> Statistic \> Table Sort By +Key,选择第一主键为area,勾上descend,即按照面积降序排列。表格是图像之外的另外一种重要的数据,某种意义上,很多时候我们都需要在图像上得到需要的信息后,以表格的形式对数据进行后期加工。ImagePy支持表格筛选,截取,统计,排序等功能。 + +
+ +
+ +在列头单击右键可以设定文字颜色,小数精度,线条样式等 + +统计图表 + +菜单:Table \> Chart \> Hist +Chart,表格数据一个常见的需求是绘制图表,这里我们对面积,周长两列进行直方图统计,得到分布直方图。ImagePy的表格可以绘制折线图,饼状图,柱状图,散点图等常见的图表。图表自带缩放,移动等功能,也可以存储为图片。 + +
+ +
+ +统计直方图 + +三维图 + +菜单:Kit3D \> Viewer 3D \> 2D +Surface,对图像进行表面重建,这里其实是分别重建了sobel梯度图,高阈值,低阈值三个结果。图上可以清楚的看懂Up +And Down +Watershed的工作原理,首先计算梯度,然后通过高低阈值标记硬币和背景,最终在dem图上模拟涨水,形成分割。ImagePy可以做图像的三维滤波,三维骨架,三维拓扑分析,也可以对数据进行二维表面重建,以及三维表面可视化。三维视图中可以自由拖动,旋转视角,图片结果也可以输出为stl文件。 + +
+ +
+ +height="4.347222222222222in"} + +三维可视乎 + +宏录制与执行 + +菜单:Window \> Develop Tool Sute, +打开开发者工具,我们看到宏录制器。以上我们手工完成了一个图像数据分割,假设这些流程非常固定,并且很适合处理这类问题,而一次次的重复点击会让人审美疲劳。这种情况我们可以通过宏录制,来将若干过程捏合成一步。宏录制器类似一个录音机,打开时,我们每一步操作会形成一行记录。而我们可以点暂停键停止录制,点播放键执行。当宏运行是,会依次质心记录下来的命令,从而实现将若干步骤捏合成一步。 + +我们将宏保存为一个.mc文件,将文件拖放到ImagePy最下方的状态栏,宏会自动被执行,我们还可以将mc文件拷贝到ImagePy文件目录下的menus的子目录下,文件启动时,宏文件会被在对应位置解析成一个菜单项,当我们点击菜单,宏也会执行。 + +
+ +
+ +宏录制 + +工作流 + +宏是一串固定的命令序列,通过将一系列固定的操作制作成宏,可以提高工作效率,但缺点是缺乏变通性,比如有时候我们的流程基本固定,但是一些细节,或者参数的设定上需要借助人工交互,这时候,工作流就可以很好的满足我们。工作流是一个流程图,分成,章,节,两个层次。章在流程图中对应于一个矩形区域,而节是矩形区域中的一个按钮,也是一条命令,并配有一段图文解释。当鼠标移动到按钮上,右侧的信息窗就会显示对应的功能说明。点击右上角的Detail +Document,查看整个流程的文档。 + +工作流的编写,实际上是一段MarkDown标记语言,但是需要按照规范编写,大致如下: + +Title + +=== + +\#\# Chapter1 + +1. Section1 + +some coment for section1 \... + +2. \... + +\#\# Chapter 2 + +\... + +
+ +
+ +![](imgs/image014.png){width="5.763888888888889in" +height="3.6944444444444446in"} + +工作流 + +Filter插件 + +以上我们介绍了宏和工作流,利用宏和工作流可以串联已有的功能,但不能制造新的功能,而这里我们试图为ImagePy添加一个新功能。ImagePy可以方便的接入任何基于Numpy的函数,我们以scikit-image的Canny算子为例。 + +from skimage import feature -``` python -# -*- coding: utf-8 -* -import scipy.ndimage as nimg from imagepy.core.engine import Filter -class Gaussian(Filter): - title = 'Gaussian' - note = ['all', 'auto_msk', 'auto_snap','preview'] +class Plugin(Filter): + +title = \'Canny\' + +note = \[\'all\', \'auto\_msk\', \'auto\_snap\', \'preview\'\] + +para = {\'sigma\':1.0, \'low\_threshold\':10, \'high\_threshold\':20} + +view = \[(float, \'sigma\', (0,10), 1, \'sigma\', \'pix\'), + +(\'slide\', \'low\_threshold\', (0,50), 4, \'low\_threshold\'), + +(\'slide\', \'high\_threshold\', (0,50), 4, \'high\_threshold\')\] + +def run(self, ips, snap, img, para = None): + +return feature.canny(snap, para\[\'sigma\'\], para\[low\_threshold\'\], + +para\[\'high\_threshold\'\], mask=ips.get\_msk())\*255 + +
+ +
+ +![](imgs/image015.png){width="5.763888888888889in" height="4.5in"} + +滤波器的作用机制: + +1. 引入需要的库,往往是第三方的库。 + +2. 继承Filter。 + +3. Title,标题,将作为菜单的名称和参数对话框的标题,也作为宏录制的命令。 + +4. Note,指明需要框架为你做什么,是否需要做类型检查,是否支持选区,是否支持撤销等。 + +5. Para,参数字典,核心函数需要用到的参数 + +6. View,参数视图,指明每个参数对应的交互方式,框架会根据这里的信息自动生成交互对话框。 + +7. 核心函数,img是当前图像,para参数交互结果,如果note里设定了auto\_snap,则img也被复制到snap,我们可以对snap进行处理,将结果存放在img中,如果函数不支持指定输出,我们也可以return处理结果,框架会帮我们将结果拷贝给img并展示。 + +8. 将文件存储为xxx\_plg.py,并拷贝到ImagePy \> + Menus目录下,重启,会被加载成一个菜单项。 + +框架帮我们做了什么? + +框架将复杂的任务进行了形式上的统一,并且帮我们进行类型检查,如果当前图像类型不符合note中的要求,则终止分析,根据para,view自动生成对话框,检测输入合法性,对图像进行实时预览,自动提供ROI支持,撤销支持,并提供多通道支持,提供图像序列支持等。 + +表格 + +正如前面所说的,表格是图像之外另一种非常重要的数据,同样ImagePy也支基于表格的功能扩展,我们用前面用到过的按照住键排序的例子来做说明。 + +from imagepy.core.engine import Table + +import pandas as pd + +class Plugin(Table): + +title = \'Table Sort By Key\' - #parameter - para = {'sigma':2} - view = [(float, (0,30), 1, 'sigma', 'sigma', 'pix')] +para = {\'major\':None, \'minor\':None, \'descend\':False} - #process - def run(self, ips, snap, img, para = None): - nimg.gaussian_filter(snap, para['sigma'], output=img) -``` +view = \[(\'field\', \'major\', \'major\', \'key\'), -![](http://idoc.imagepy.org/imgs/filter.png "filter") +(\'field\', \'minor\', \'minor\', \'key\'), -Create a Filter: +(bool, \'descend\', \'descend\')\] -1. Import related class library. It can be third-party or implemented in C. -2. Create a class that subclasses `Filter`. -3. `title`, this line is the name of the plug-in and will also serve as the title of the menu. -4. `note` indicates how the plug-in works and the associated preprocessing and post-processing work. This includes which types of images can be processed, whether selections are supported or not, and so on. -5. `para` is the parameter the kernel function needs. -6. `view` and `para` are corresponding. They inform how the various parameters of the framework plug-in interact (the framework will generate your interactive interface automatically). -7. `run` is the core function, if conditional, given `img` to the results of the process (save memory), or return out (the framework will help you give img). -8. Place the file in any subdirectory of menus in the project, and it will be loaded as a menu item at startup. +def run(self, tps, data, snap, para=None): -**What has the framework helped us do?** +by = \[para\[\'major\'\], para\[\'minor\'\]\] -They framework enables complex tasks in a uniform way. Simply, you do not need to determine for yourself whether the image type is legitimate. You do not need to make your own image cache to support undo. You do not need to support the selection by yourself. Do not need to monitor the interface by yourself to achieve real-time preview. You do not need to write any interface code after you have defined the required parameters, the type and the range of values for each parameter. When a color image is encountered, the each channel of the image is processed sequentially. When the image stack is encountered, each frame is automatically traversed. You are free to work on either the task -of image analysis, or creating new plugins for the framework. +data.sort\_values(by=\[i for i in by if i != \'None\'\], -Extend a Tool: --------------- -Another scenario is to interact on the canvas through the mouse, like the selection operations mentioned above. Here is an example of a brush: +axis=0, ascending=not para\[\'descend\'\], inplace=True) -``` python -from imagepy.core.draw import paint -from imagepy.core.engine import Tool -import wx +
+ +
-class Plugin(Tool): - title = 'Pencil' - para = {'width':1} - view = [(int, (0,30), 0, 'width', 'width', 'pix')] +Table的作用机制 - def __init__(self): - self.sta = 0 - self.paint = paint.Paint() - self.cursor = wx.CURSOR_CROSS +类比Filter,Table同样有title,note,para,view参数,当插件运行是框架通过para,view解析为对话框,交互完成后,参数和当表格会一起传递给run,run中对表格进行核心处理,data是当前表格对应的pandas.DataFrame对象,tps中存储了其他信息,比如tps.rowmsk,tps.colmsk可以拿到当前表格被选中的行列掩膜。 - def mouse_down(self, ips, x, y, btn, **key): - self.sta = 1 - self.paint.set_curpt(x,y) - ips.snapshot() +其他插件类型 - def mouse_up(self, ips, x, y, btn, **key): - self.sta = 0 +上面介绍的Filter和Table是最重要的两种插件,但ImagePy也支持其他一些类型的插件扩展,目前有九种,他们是: - def mouse_move(self, ips, x, y, btn, **key): - if self.sta==0:return - self.paint.lineto(ips.img,x,y, self.para['width']) - ips.update = True +1. Filter:主要用来对图像进行处理 - def mouse_wheel(self, ips, x, y, d, **key):pass -``` +2. Simple:类似于Filter,但侧重与图像的整体特性,比如对ROI的操作,对假彩色的操作,区域测量,或者对整个图像栈进行的三维分析,可视化等。 -![](http://idoc.imagepy.org/imgs/painter.png "painter") +3. Free:用于不依赖图像的操作,比如打开图像,关闭软件等。 -**Create Tool:** +4. Tool:借助鼠标在图上进行交互,将以小图标的形式出现在工具栏上,例如画笔。 -1. Inherited from `Tool` in the `engine`. -2. Specify the `title`, which will be the tool name, and the message of the status bar. -3. Adds several methods to achieve mouse_down, mouse_up, mouse_move, mouse_wheel. -4. If the tool requires parameters (for example, pen width), use the dictionary to assign to `para`. Similarly, `view` specifies its interactive interface. When the tool is double-clicked, the dialog box will pop-up in accordance with the specified interface. -5. Files are stored in the sub-folder of tools, with a generated 16 * 16 thumbnail icon. The icon and the tool are stored in the same name as the gif file. +5. Table:对表格进行操作,例如统计,排序,出图。 -About ImagePy: --------------- -The above only lists some features of ImagePy, covering the basic mathematical operations, filters, pixel statistics, a slightly complex feature extraction, 3D reconstruction and other functions. It gives a brief introduction to macros , how to write new filters, tools and integrate them like ImagePy. Stay tuned for more detail in the coming manual and development documents. +6. Widget:以面板形式展现的功能部件,例如右侧的导航栏,宏录制器等。 -I (yxdragon) have used ImageJ for a long time and also used to use Python for scientific computing. ImageJ's outstanding plug-in design philosophy allows it to absorb the contributions of industry professionals quickly. However, Python has an advantage over Java in image processing. +7. Markdown:MarkDown标记语言,点击后会弹出独立窗口展示文档。 -1. Java is a system language, the relative threshold of it is relatively high. -2. The related open source libraries under Java are not as rich as C / C++. -3. Python has simple grammar so it is easy to learn. It is a good choice for non-computer professionals. -4. Python has a wealth of third-party extensions, such as Scikit-image, OpenCV, Matplotlib, Mayavi, etc. -5. Almost all scientific computing class libraries are based on Numpy! so the framework can be built up easily. -6. Python can be extended by C/C++ with ctypes/cython. +8. Macros:命令序列文件,用于串联固定的操作流程。 -Because of busy work, I wrote ImagePy in my spare time. All of the development work lasted about two months. Personally I think that this efficiency is mainly due to a large number of third-party libraries of Python as well as the project’s "borrowlism" design ideas. The project uses wxpython as the interface library, Numpy as the base data type. Because the time is short, many interactive details of the plug-in will show problems, you please give a positive feedback to me. I will do my best to safeguard the healthy growth of this project. +9. Workflow:工作流,宏和MarkDown的结合,用于制作交互式指引流程。 -[https://github.com/yxdragon/imagepy.git](https://github.com/yxdragon/imagepy.git) +开发意义 -![](http://idoc.imagepy.org/imgs/me.png "me") -It is me, yxdragon. -Email:imagepy@sina.com +Python是一门简单,优雅,强大的语言,并且在科学计算方面有非常丰富的第三方库,并且Numpy制定了良好的规范,建立在Numpy基础上的Scipy,scikit-image,scikit-learn等科学计算库给科研工作带来了极大的便利。另一方面,科学计算,图像处理在生物,材料等科研领域可以高效准确的解决越来越多的问题,然而依然有很多科研工作者编程能力比较薄弱,因此让Numpy系列的科学计算库造福更多科研工作者是一项非常有意义的工作。而ImagePy就是一座桥梁,尽可能的让科学计算工作者隔离自己不擅长的UI和交互设计,着重精力处理算法本身,并且可以快速形成工具甚至产品,而这些工作又可以让更多不擅长编程的科研工作者收益,推广和普及图像处理,统计等科学知识。 From cb913f332609a178ab8e1381e7eee7a0bd745362 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 12 Dec 2018 01:18:25 +0800 Subject: [PATCH 052/343] repair --- imagepy/menus/Process/Binary/distance_plgs.py | 27 ++----------------- imagepy/menus/Process/__init__.py | 2 +- imagepy/menus/Process/repair_plg.py | 20 ++++++++++++++ 3 files changed, 23 insertions(+), 26 deletions(-) create mode 100644 imagepy/menus/Process/repair_plg.py diff --git a/imagepy/menus/Process/Binary/distance_plgs.py b/imagepy/menus/Process/Binary/distance_plgs.py index 54ff97d1..71e761f2 100644 --- a/imagepy/menus/Process/Binary/distance_plgs.py +++ b/imagepy/menus/Process/Binary/distance_plgs.py @@ -11,7 +11,7 @@ from imagepy.ipyalg import find_maximum, watershed #from skimage.morphology import watershed import scipy.ndimage as ndimg -from skimage.measure import label,regionprops + class Skeleton(Filter): title = 'Skeleton' note = ['all', 'auto_msk', 'auto_snap','preview'] @@ -84,28 +84,5 @@ def run(self, ips, snap, img, para = None): img[:] = (line==0) * 255 if para['type']=='gray line': img[:] = np.where(line==0, dist, 0) -class Unnamed(Filter): - title = 'Unnamed' - view = [(list, 'type', ['nearby', 'mean'], str, 'output', '')] - note = ['all', 'auto_msk', 'auto_snap','preview'] - para = {'type':'nearby'} - def run(self, ips, snap, img, para = None): - # return ndimg.distance_transform_edt(snap) - img1=snap - msk=ips.get_msk() - if self.para['type']=='nearby':img[:]=self.nearby(img1.copy(),msk) - else: img[:]=self.mean(img1.copy(),msk) - def nearby(self,img,msk): - rr,cc=ndimg.distance_transform_edt(msk,return_distances=False,return_indices=True) - return img[rr,cc] - def mean(self,img,msk): - msk1=ndimg.maximum_filter(msk, 5) - lab=label(msk) - lab1=label(msk1*msk) - for i in regionprops(lab1): - index=np.array(lab1==i.label) - me=img[index].mean() - img[lab==i.label]=me - return img -plgs = [Skeleton, MedialAxis, '-', EDT,Unnamed, Watershed, Voronoi] \ No newline at end of file +plgs = [Skeleton, MedialAxis, '-', EDT, Watershed, Voronoi] \ No newline at end of file diff --git a/imagepy/menus/Process/__init__.py b/imagepy/menus/Process/__init__.py index 0e8ef221..ce93e566 100644 --- a/imagepy/menus/Process/__init__.py +++ b/imagepy/menus/Process/__init__.py @@ -1 +1 @@ -catlog = ['Math', 'Binary', 'Filters', '-', 'Threshold', 'Hydrology', 'Features', 'Segment', '-', 'calculator_plg','segment'] \ No newline at end of file +catlog = ['Math', 'Binary', 'Filters', '-', 'Threshold', 'Hydrology', 'Features', 'Segment', 'repair_plg', '-', 'calculator_plg'] \ No newline at end of file diff --git a/imagepy/menus/Process/repair_plg.py b/imagepy/menus/Process/repair_plg.py new file mode 100644 index 00000000..836273d7 --- /dev/null +++ b/imagepy/menus/Process/repair_plg.py @@ -0,0 +1,20 @@ +from imagepy.core.engine import Filter +import numpy as np +import scipy.ndimage as ndimg + +class Plugin(Filter): + title = 'Fragment Repair' + note = ['all', 'req_roi', 'auto_msk', 'auto_snap', 'preview'] + para = {'mode':'nearest'} + view = [(list, 'mode', ['nearest', 'mean'], str, 'replace by', 'pix')] + + def run(self, ips, snap, img, para = None): + msk = ips.get_msk() + if self.para['mode']=='nearest': + rr, cc = ndimg.distance_transform_edt(msk, return_distances=False, return_indices=True) + img[:] = snap[rr, cc] + else: + lab1, n = ndimg.label(msk) + lab2 = ndimg.maximum_filter(lab1, 3) + idx = ndimg.mean(img, lab2-lab1, np.arange(1,n+1)) + img[msk] = idx[lab1[msk]-1] \ No newline at end of file From 41b80cc028d8b2abd56e3ac6414035b8746583e2 Mon Sep 17 00:00:00 2001 From: Tong Date: Tue, 11 Dec 2018 23:57:19 +0100 Subject: [PATCH 053/343] second part done --- README.md | 134 +++++++++++++++++++++++++----------------------------- 1 file changed, 62 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index dcfc3d97..b1915a62 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,6 @@ ImagePy: - is able to perfrom data analysis, filtering, statistical analysis and others related to the parameters extracted from the image. Our long-term goal of this project is to be used as ImageJ + SPSS (although not achieved yet) - - ## Citation: [ImagePy: an open-source, Python-based and platform-independent software package for bioimage analysis](https://academic.oup.com/bioinformatics/article/34/18/3238/4989871) @@ -53,9 +51,7 @@ __OS support:windows, linux, mac, with python2.7 and python3.__4+ ## Basic operations: -ImagePy has a very rich set of features, and here, we use a specific example to show you a glimpse of the capacity of ImagePy. We choose the official coin split of scikit-image, since this example is simple and comprehensive. - - +ImagePy has a very rich set of features, and here, we use a specific example to show you a glimpse of the capacity of ImagePy. We choose the official coin split of scikit-image, since this example is simple and comprehensive. ### Open image @@ -67,13 +63,12 @@ _PS: ImagePy supports bmp, jpg, png, gif, tif and other commonly used file forma -### Filtering & Segmentation +### Filtering & Segmentation `menu:Process -> Hydrology -> Up And Down Watershed` Here, a composite filter is selected to perform sobel gradient extraction on the image, and then the upper and lower thresholds are used as the mark, at last we watershed on the gradient map. Filtering and segmentation are the crucial skills in the image processing toolkit, and are the key to the success or failure of the final measurement. Segmentation methods such as adaptive thresholds, watersheds and others are also supported. -
@@ -85,133 +80,129 @@ Segmentation methods such as adaptive thresholds, watersheds and others are also ### Binarization -`menu:Process -> Binary -> Binary Fill Holes` +`menu:Process -> Binary -> Binary Fill Holes` -经过上述分割,我们得到了相对纯净的掩膜图像,但依然存在一些镂空,以及少许外界杂质,这些会对计数和测量造成干扰,我们对图像进行二值填充。ImagePy支持腐蚀,膨胀,开,闭等二值化基础操作,也支持骨架,中轴线提取,距离变换等操作。 +After the segmentation, we obtained a relatively clean mask image, but there are still some hollowing out, as well as a little impurities, which will interfere with counting and measurement. +_ImagePy supports binary operations such as erode, dilate, opening and closing, as well as skeletonization, central axis extraction, and distance transformation._
-区域过滤 +### Geometry filtering -菜单:Analysis \> Region Analysis \> Geometry -Filter,对区域进行过滤,这里可以简单的通过面积进行过滤,ImagePy的几何过滤可以通过面积,周长,拓扑,丰满度,偏心率等指标对区域进行过滤,可以输入复合条件,正数表示选择大于等于,负数表示选择小于,通过过滤的被设定为front -color,没通过的设定为back color,back -color设定为100可以清楚看到有哪些被滤掉了,如果确认符合要求,back -color设定为0,即清除。同时ImagePy也支持灰度密度过滤,颜色过滤,色彩聚类等功能。 +`menu:Analysis -> Region Analysis -> Geometry Filter` + +ImagePy can perform geometric filtering based on :__the area, the perimeter, the topology, the solidity, the eccentricity__ and other parameters. You can also use multiple conditions filtering. Each number can be positive|negative. It indicates the kept object will have the corresponding parameter greater|smaller than the value respectively. The kept objects will be set to the front color, the rejected ones will be set to the back color. In this demo, the back color is set to 100 in order to see which ones are filtered out. Once satisfied with the result, set the back color to 0 to reject them. In addition, ImagePy also supports gray density filtering, color filtering, color clustering and other functions.
+ Geometry filtering (the area is over-chosen to emphasize the distinction)
-区域过滤(这里为了看清效果,area设定的较大,实际只需要过滤碎片) -区域分析 +### Geometry Analysis -菜单:Process \> Region Analysis \> Geometry -Analysis,对区域进行计数和指标分析,这里我们勾上cov,即对区域进行协方差椭圆拟合。ImagePy支持面积,周长,偏心率,丰满度等指标,其实上一步的过滤正是在这里的分析结果基础上进行的。 +`menu:Process -> Region Analysis -> Geometry Analysis` +Count the area and analyze the parameters. By choosing the `cov` option, ImagePy will fit each area with an ellipse calculated via the covariance. +The parameters such as area, perimeter, eccentricity, and solidity shown in the previous step are calculated here. In fact, the filtering of the previous step is a downstream analysis of this one.
-
- -区域分析 - -
+ Geometry Analysis
-支持xls, xlsx, -csv表格格式, -生成结果表格(这里为了看清椭圆,把区域亮度降低了) +(这里为了看清椭圆,把区域亮度降低了) -按照面积排序 +### Sort Table by area -菜单:Table \> Statistic \> Table Sort By -Key,选择第一主键为area,勾上descend,即按照面积降序排列。表格是图像之外的另外一种重要的数据,某种意义上,很多时候我们都需要在图像上得到需要的信息后,以表格的形式对数据进行后期加工。ImagePy支持表格筛选,截取,统计,排序等功能。 +`menu:Table -> Statistic -> Table Sort By Key` + +Select the major key as area, and select descend. The table will be sorted in descending order of area. A table is another important piece of data other than an image. In a sense, many times we need to get the required information on the image and then post-process the data in the form of a table. ImagePy supports table I/O (xls, xlsx, csv), filtering, slicing, statistical analysis, sorting and more.
+ Right click on the column header to set the text color, decimal precision, line style, etc.
-在列头单击右键可以设定文字颜色,小数精度,线条样式等 -统计图表 +### Charts + +`menu:Table -> Chart -> Hist Chart` + +From tabular data, we often need to draw a graph. Here, we plot the histograms of the area and the perimeter columns. ImagePy's tables can be used to draw common charts such as line charts, pie charts, histograms, and scatter plots (matplotlib-based). The chart comes with zooming, moving and other functions. The table can also be saved as an image. -菜单:Table \> Chart \> Hist -Chart,表格数据一个常见的需求是绘制图表,这里我们对面积,周长两列进行直方图统计,得到分布直方图。ImagePy的表格可以绘制折线图,饼状图,柱状图,散点图等常见的图表。图表自带缩放,移动等功能,也可以存储为图片。
+ Histograms
-统计直方图 -三维图 +### 3D chart + +`menu:Kit3D -> Viewer 3D -> 2D Surface` + + +Surface reconstruction of the image. This image shows the three reconstructed results including, sobel gradient map, high threshold and low threshold. It shows how the Up And Down Watershed works: +- calculate the gradient. +- mark the coin and background through the high and low thresholds, +- simulate the rising water on the dem diagram to form the segmentation. -菜单:Kit3D \> Viewer 3D \> 2D -Surface,对图像进行表面重建,这里其实是分别重建了sobel梯度图,高阈值,低阈值三个结果。图上可以清楚的看懂Up -And Down -Watershed的工作原理,首先计算梯度,然后通过高低阈值标记硬币和背景,最终在dem图上模拟涨水,形成分割。ImagePy可以做图像的三维滤波,三维骨架,三维拓扑分析,也可以对数据进行二维表面重建,以及三维表面可视化。三维视图中可以自由拖动,旋转视角,图片结果也可以输出为stl文件。 +ImagePy can perform 3D filtering of images, 3D skeletons, 3D topological analysis, 2D surface reconstruction, and 3D surface visualization. The 3D view can be freely dragged, rotated, and the image results can be saved as a .stl file.
+ 3D visualisation
-height="4.347222222222222in"} -三维可视乎 -宏录制与执行 +### Macro recording and execution -菜单:Window \> Develop Tool Sute, -打开开发者工具,我们看到宏录制器。以上我们手工完成了一个图像数据分割,假设这些流程非常固定,并且很适合处理这类问题,而一次次的重复点击会让人审美疲劳。这种情况我们可以通过宏录制,来将若干过程捏合成一步。宏录制器类似一个录音机,打开时,我们每一步操作会形成一行记录。而我们可以点暂停键停止录制,点播放键执行。当宏运行是,会依次质心记录下来的命令,从而实现将若干步骤捏合成一步。 +`menu:Window -> Develop Tool Suite` -我们将宏保存为一个.mc文件,将文件拖放到ImagePy最下方的状态栏,宏会自动被执行,我们还可以将mc文件拷贝到ImagePy文件目录下的menus的子目录下,文件启动时,宏文件会被在对应位置解析成一个菜单项,当我们点击菜单,宏也会执行。 +Macro recorder is shown in the develop tool panel. We have manually completed an image segmentation. However, batch processing more than 10 images can be tedious. So, assuming that these steps are highly repeatable and robust for dealing with such problems, we can record a macro to combine several processes into a one-click program. The macro recorder is similar to a radio recorder. When it is turned on, each step of the operation will be recorded. We can click the pause button to stop recording, then click the play button to execute. When the macro is running, the recorded commands will be executed sequentially, therefore achieving simplicity and reproducibility. + +Macros are saved into .mc files. drag and drop the file to the status bar at the bottom of ImagePy, the macro will be executed automatically. we can also copy the .mc file to the submenu of the menus under the ImagePy file directory. When ImagePy is started, the macro file will be parsed into a menu item at the corresponding location. By clicking the menu, the macro will also be executed.
+ Macro Recording
-宏录制 -工作流 +### Workflow -宏是一串固定的命令序列,通过将一系列固定的操作制作成宏,可以提高工作效率,但缺点是缺乏变通性,比如有时候我们的流程基本固定,但是一些细节,或者参数的设定上需要借助人工交互,这时候,工作流就可以很好的满足我们。工作流是一个流程图,分成,章,节,两个层次。章在流程图中对应于一个矩形区域,而节是矩形区域中的一个按钮,也是一条命令,并配有一段图文解释。当鼠标移动到按钮上,右侧的信息窗就会显示对应的功能说明。点击右上角的Detail -Document,查看整个流程的文档。 +A macro is a sequence of predefined commands. By recording a series of fixed operations into macros, you can improve your work efficiency. However, the disadvantage is the lack of flexibility. For example, sometimes the main steps are fixed, but the parameter tuning needs human interaction. In this case, the workflow is what you want. A workflow in ImagePy is a flow chart that can be visualized, divided into two levels: __chapters and sections__. +The chapter corresponds to a rectangular area in the flow chart, and the section is a button in the rectangular area, which is also a command and is accompanied by a graphic explanation. The message window on the right will display the corresponding function description, while mousing hovering above. Click on the `Detail Document` in the top right corner to see the documentation of the entire process. -工作流的编写,实际上是一段MarkDown标记语言,但是需要按照规范编写,大致如下: -Title +The workflow is actually written in MarkDown (a markup language), but it needs to be written respecting several specifications, as follows: +``` +Title === - -\#\# Chapter1 - +## Chapter1 1. Section1 - -some coment for section1 \... - -2. \... - -\#\# Chapter 2 - -\... - +some coment for section1 ... +2. ... +## Chapter 2 + ... +```
+ Workflow
-![](imgs/image014.png){width="5.763888888888889in" -height="3.6944444444444446in"} -工作流 -Filter插件 +### Filter Plugin 以上我们介绍了宏和工作流,利用宏和工作流可以串联已有的功能,但不能制造新的功能,而这里我们试图为ImagePy添加一个新功能。ImagePy可以方便的接入任何基于Numpy的函数,我们以scikit-image的Canny算子为例。 +``` from skimage import feature from imagepy.core.engine import Filter @@ -235,12 +226,11 @@ def run(self, ips, snap, img, para = None): return feature.canny(snap, para\[\'sigma\'\], para\[low\_threshold\'\], para\[\'high\_threshold\'\], mask=ips.get\_msk())\*255 - +```
-![](imgs/image015.png){width="5.763888888888889in" height="4.5in"} 滤波器的作用机制: @@ -268,7 +258,7 @@ para\[\'high\_threshold\'\], mask=ips.get\_msk())\*255 表格 正如前面所说的,表格是图像之外另一种非常重要的数据,同样ImagePy也支基于表格的功能扩展,我们用前面用到过的按照住键排序的例子来做说明。 - +``` from imagepy.core.engine import Table import pandas as pd @@ -292,7 +282,7 @@ by = \[para\[\'major\'\], para\[\'minor\'\]\] data.sort\_values(by=\[i for i in by if i != \'None\'\], axis=0, ascending=not para\[\'descend\'\], inplace=True) - +```
From ad945708fc6a12cf0e0cd90a13de5c1e4feb2a03 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 12 Dec 2018 16:21:36 +0800 Subject: [PATCH 054/343] simple asyn --- imagepy/core/engine/simple.py | 7 ++++--- imagepy/menus/Table/Statistic/sort_plg.py | 7 ++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/imagepy/core/engine/simple.py b/imagepy/core/engine/simple.py index 809aceff..daeea588 100644 --- a/imagepy/core/engine/simple.py +++ b/imagepy/core/engine/simple.py @@ -13,6 +13,7 @@ class Simple: title = 'SimpleFilter' + asyn = True note = [] para = None 'all, 8-bit, 16-bit, rgb, float, req_roi, stack, stack2d, stack3d, preview' @@ -50,10 +51,10 @@ def cancel(self, ips):pass def ok(self, ips, para=None, callafter=None): if para == None: para = self.para - if IPy.uimode() == 'no': - self.runasyn(ips, ips.imgs, para, callafter) - else: threading.Thread(target = self.runasyn, + if self.asyn and IPy.uimode()!='no': + threading.Thread(target = self.runasyn, args = (ips, ips.imgs, para, callafter)).start() + else: self.runasyn(ips, ips.imgs, para, callafter) win = WidgetsManager.getref('Macros Recorder') if win!=None: win.write('{}>{}'.format(self.title, para)) diff --git a/imagepy/menus/Table/Statistic/sort_plg.py b/imagepy/menus/Table/Statistic/sort_plg.py index 6e69e9b9..e6cab7b0 100644 --- a/imagepy/menus/Table/Statistic/sort_plg.py +++ b/imagepy/menus/Table/Statistic/sort_plg.py @@ -1,8 +1,7 @@ from imagepy.core.engine import Table import pandas as pd -from imagepy import IPy -class Sort(Table): +class Plugin(Table): title = 'Table Sort By Key' para = {'major':None, 'minor':None, 'descend':False} @@ -14,6 +13,4 @@ class Sort(Table): def run(self, tps, data, snap, para=None): by = [para['major'], para['minor']] tps.data.sort_values(by=[i for i in by if i != 'None'], - axis=0, ascending=not para['descend'], inplace=True) - -plgs = [Sort] \ No newline at end of file + axis=0, ascending=not para['descend'], inplace=True) \ No newline at end of file From 36a1b4b9d1e70e1b98be3ee692709dc516f17a01 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 12 Dec 2018 22:58:23 +0800 Subject: [PATCH 055/343] uint32 --- imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py | 4 ++-- imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py index a5a22dfb..26478b86 100644 --- a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py @@ -48,7 +48,7 @@ def run(self, ips, imgs, para = None): if para['fa']:titles.extend(['FilledArea']) if para['solid']:titles.extend(['Solidity']) if para['cov']:titles.extend(['Major','Minor','Ori']) - buf = imgs[0].astype(np.uint16) + buf = imgs[0].astype(np.uint32) data, mark = [], {'type':'layers', 'body':{}} strc = generate_binary_structure(2, 1 if para['con']=='4-connect' else 2) for i in range(len(imgs)): @@ -116,7 +116,7 @@ def run(self, ips, snap, img, para = None): k, unit = ips.unit strc = generate_binary_structure(2, 1 if para['con']=='4-connect' else 2) - lab, n = label(snap==0 if para['inv'] else snap, strc, output=np.uint16) + lab, n = label(snap==0 if para['inv'] else snap, strc, output=np.uint32) idx = (np.ones(n+1)*(0 if para['inv'] else para['front'])).astype(np.uint8) ls = regionprops(lab) diff --git a/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py b/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py index 1bc1ff84..4fbb743d 100644 --- a/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py +++ b/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py @@ -47,7 +47,7 @@ def run(self, ips, imgs, para = None): if para['ed']:titles.extend(['Diameter']) if para['fa']:titles.extend(['FilledArea']) - buf = imgs.astype(np.uint16) + buf = imgs.astype(np.uint32) strc = generate_binary_structure(3, 1 if para['con']=='4-connect' else 2) label(imgs, strc, output=buf) ls = regionprops(buf) @@ -88,7 +88,7 @@ def run(self, ips, imgs, para = None): k, unit = ips.unit strc = generate_binary_structure(3, 1 if para['con']=='4-connect' else 2) - lab, n = label(imgs==0 if para['inv'] else imgs, strc, output=np.uint16) + lab, n = label(imgs==0 if para['inv'] else imgs, strc, output=np.uint32) idx = (np.ones(n+1)*(0 if para['inv'] else para['front'])).astype(np.uint8) ls = regionprops(lab) From abca2e757c8fdd3222353b37a6f6e82a011eb6a7 Mon Sep 17 00:00:00 2001 From: Tong Date: Wed, 12 Dec 2018 23:23:51 +0100 Subject: [PATCH 056/343] translation done --- README.md | 152 ++++++++++++++++++++++-------------------------------- 1 file changed, 63 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index b1915a62..907f0e51 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,3 @@ - - - - # Introduction ImagePy is an open source image processing framework written in Python. Its UI interface, image data structure and table data structure are wxpython-based, Numpy-based and pandas-based respectively. Furthermore, it supports any plug-in based on Numpy and pandas, which can talk easily between scipy.ndimage, scikit-image, simpleitk, opencv and other image processing libraries. @@ -10,7 +6,6 @@ ImagePy is an open source image processing framework written in Python. Its UI i Overview Overview, mouse measurement, geometric transformation, filtering, segmentation, counting, etc.
-
ij style @@ -27,6 +22,7 @@ ImagePy: Our long-term goal of this project is to be used as ImageJ + SPSS (although not achieved yet) + ## Citation: [ImagePy: an open-source, Python-based and platform-independent software package for bioimage analysis](https://academic.oup.com/bioinformatics/article/34/18/3238/4989871) @@ -109,11 +105,12 @@ The parameters such as area, perimeter, eccentricity, and solidity shown in the
- Geometry Analysis + + Generate the result table (intensity is reduced in order to emphasize the + ellipse)
-(这里为了看清椭圆,把区域亮度降低了) ### Sort Table by area @@ -200,119 +197,96 @@ some coment for section1 ... ### Filter Plugin -以上我们介绍了宏和工作流,利用宏和工作流可以串联已有的功能,但不能制造新的功能,而这里我们试图为ImagePy添加一个新功能。ImagePy可以方便的接入任何基于Numpy的函数,我们以scikit-image的Canny算子为例。 +We introduced macros and workflows in the last sections, using macros and workflows to connect existing functions is convenient. But sometimes we need to create new features. In this section, we are trying to add a new feature to ImagePy. ImagePy can easily access any Numpy-based function. Let's take the Canny operator of scikit-image as an example. ``` from skimage import feature - from imagepy.core.engine import Filter - class Plugin(Filter): - -title = \'Canny\' - -note = \[\'all\', \'auto\_msk\', \'auto\_snap\', \'preview\'\] - -para = {\'sigma\':1.0, \'low\_threshold\':10, \'high\_threshold\':20} - -view = \[(float, \'sigma\', (0,10), 1, \'sigma\', \'pix\'), - -(\'slide\', \'low\_threshold\', (0,50), 4, \'low\_threshold\'), - -(\'slide\', \'high\_threshold\', (0,50), 4, \'high\_threshold\')\] - + title = 'Canny' + note = ['all', 'auto_msk', 'auto_snap', 'preview'] + para = {'sigma':1.0, 'low_threshold':10, 'high_threshold':20} + view = [(float, 'sigma', (0,10), 1, 'sigma', 'pix'), + ('slide', 'low_threshold', (0,50), 4, 'low_threshold'), + ('slide', 'high_threshold', (0,50), 4, 'high_threshold')] def run(self, ips, snap, img, para = None): - -return feature.canny(snap, para\[\'sigma\'\], para\[low\_threshold\'\], - -para\[\'high\_threshold\'\], mask=ips.get\_msk())\*255 + return feature.canny(snap, para['sigma'], para['low_threshold'], + para['high_threshold'], mask=ips.get_msk())*255 ```
+#### Steps to create a your own filter: -滤波器的作用机制: - -1. 引入需要的库,往往是第三方的库。 - -2. 继承Filter。 - -3. Title,标题,将作为菜单的名称和参数对话框的标题,也作为宏录制的命令。 - -4. Note,指明需要框架为你做什么,是否需要做类型检查,是否支持选区,是否支持撤销等。 - -5. Para,参数字典,核心函数需要用到的参数 +1. Import the package(s), often third party. +2. Inherit the __`Filter`__ class。 +3. The __`title`__ will be used as the name of the menu and the title of the parameter dialog, also as a command for macro recording. +4. Tell the framework what needs to do for you in __`Note`__, whether to do type checking, to support the selection, to support _UNDO_, etc. +5. __`Para`__ is the a dictionary of parameters, including needed parameters for the + functions. +6. Define the interaction method for each of the parameters in __`View`__, the framework will automatically generate the dialog for parameter tuning by reading these information. +7. Write the core function __`run`__. `img` is the current image, `para` is the result entre by user. if `auto_snap` is set in `note`, `snap` will be a duplicate of `img`. We can process the `snap`, store the result in `img`. If the function does not support the specified output, we can also return the result, and the framework will help us copy the result to img and display it. +8. Save the file as `xxx_plg.py` and copy to the `menu` folder, restart ImagePy. + It will be loaded as a menu item. -6. View,参数视图,指明每个参数对应的交互方式,框架会根据这里的信息自动生成交互对话框。 +#### What did the framework do for us? -7. 核心函数,img是当前图像,para参数交互结果,如果note里设定了auto\_snap,则img也被复制到snap,我们可以对snap进行处理,将结果存放在img中,如果函数不支持指定输出,我们也可以return处理结果,框架会帮我们将结果拷贝给img并展示。 +The framework unifies the complex tasks in a formal manner and helps us to perform: +- type checking. If the current image type does not meet the requirements in the note, the analysis is terminated. +- according to the `para`, generate automatically a dialog box to detect the input legality from the `view`. +- Real-time preview +- automatic ROI support +- undo support +- parallelization support +- image stack support +- etc. -8. 将文件存储为xxx\_plg.py,并拷贝到ImagePy \> - Menus目录下,重启,会被加载成一个菜单项。 +### Table -框架帮我们做了什么? +As mentioned earlier, the table is another very important data type other than the image. Similarly, ImagePy also supports the extension of table. Here we give an example of sorting-by-key used in the previous description. -框架将复杂的任务进行了形式上的统一,并且帮我们进行类型检查,如果当前图像类型不符合note中的要求,则终止分析,根据para,view自动生成对话框,检测输入合法性,对图像进行实时预览,自动提供ROI支持,撤销支持,并提供多通道支持,提供图像序列支持等。 - -表格 - -正如前面所说的,表格是图像之外另一种非常重要的数据,同样ImagePy也支基于表格的功能扩展,我们用前面用到过的按照住键排序的例子来做说明。 ``` from imagepy.core.engine import Table - import pandas as pd - class Plugin(Table): - -title = \'Table Sort By Key\' - -para = {\'major\':None, \'minor\':None, \'descend\':False} - -view = \[(\'field\', \'major\', \'major\', \'key\'), - -(\'field\', \'minor\', \'minor\', \'key\'), - -(bool, \'descend\', \'descend\')\] - + title = 'Table Sort By Key' + para = {'major':None, 'minor':None, 'descend':False} + view = [('field', 'major', 'major', 'key'), + ('field', 'minor', 'minor', 'key'), + (bool, 'descend', 'descend')] def run(self, tps, data, snap, para=None): - -by = \[para\[\'major\'\], para\[\'minor\'\]\] - -data.sort\_values(by=\[i for i in by if i != \'None\'\], - -axis=0, ascending=not para\[\'descend\'\], inplace=True) + by = [para['major'], para['minor']] + data.sort_values(by=[i for i in by if i != 'None'], + axis=0, ascending = not para['descend'], + inplace=True) ```
-Table的作用机制 - -类比Filter,Table同样有title,note,para,view参数,当插件运行是框架通过para,view解析为对话框,交互完成后,参数和当表格会一起传递给run,run中对表格进行核心处理,data是当前表格对应的pandas.DataFrame对象,tps中存储了其他信息,比如tps.rowmsk,tps.colmsk可以拿到当前表格被选中的行列掩膜。 - -其他插件类型 - -上面介绍的Filter和Table是最重要的两种插件,但ImagePy也支持其他一些类型的插件扩展,目前有九种,他们是: - -1. Filter:主要用来对图像进行处理 - -2. Simple:类似于Filter,但侧重与图像的整体特性,比如对ROI的操作,对假彩色的操作,区域测量,或者对整个图像栈进行的三维分析,可视化等。 - -3. Free:用于不依赖图像的操作,比如打开图像,关闭软件等。 - -4. Tool:借助鼠标在图上进行交互,将以小图标的形式出现在工具栏上,例如画笔。 +#### How Table works -5. Table:对表格进行操作,例如统计,排序,出图。 +Same as `Filter`,`Table` also has parameters such as `title`,`note`,`para`,`view`. +When the plugin is running, the framework will generate a dialog box according to `para` +and `view`. After the parameters are chosen, they are passed to the `run` together with the current table and be processed. The table data is a pandas.DataFrame object in the current table, stored in `tps`. Other information, such as `tps.rowmsk`, `tps.colmsk` can also be retrieved from `tps` to get the row and column mask of the current selected table. -6. Widget:以面板形式展现的功能部件,例如右侧的导航栏,宏录制器等。 +### Other type of plugins -7. Markdown:MarkDown标记语言,点击后会弹出独立窗口展示文档。 +The `Filter` and `Table` described above are the two most important plugins, but ImagePy also supports some other types of plugin extensions. There are currently nine, they are: -8. Macros:命令序列文件,用于串联固定的操作流程。 +1. `Filter`: mainly for image processing +2. `Simple`: similar to `Filter`, but focus on the overall characteristics of the image, such as the operation of the ROI, the operation of the false color, the area measurement, or the three-dimensional analysis of the entire image stack, visualization, and so on. +3. `Free`: operate that are independant of image. Used to open image, close software etc. +4. `Tool`: use the mouse to interact on the diagram and show small icons on the toolbar, such as a brush. +5. `Table`: operate on the table, such as statistics analysis, sorting, plotting. +6. `Widget`: widgets that are displayed in panels, such as the navigation bar on the right, the macro recorder, and others. +7. `Markdown`: markup language, when clicked, a separate window will pop up to display the document. +8. `Macros`:Command sequence file for serially fixed operational procedures. +9. `Workflow`: combination of macro and MarkDown to create an interactive guidance process. -9. Workflow:工作流,宏和MarkDown的结合,用于制作交互式指引流程。 +## Motivation & Goal -开发意义 +Python is a simple, elegant, powerful language, and has a very rich third-party library for scientific computing. Based on the universal matrix structure and the corresponding rules, numpy-based library such as scipy, scikit-image, scikit-learn and other scientific computing libraries have brought great convenience to scientific research. On the other hand, more and more problems in biology, material science and other scientific research can be efficiently and accurately solved via scientific computing, image processing. -Python是一门简单,优雅,强大的语言,并且在科学计算方面有非常丰富的第三方库,并且Numpy制定了良好的规范,建立在Numpy基础上的Scipy,scikit-image,scikit-learn等科学计算库给科研工作带来了极大的便利。另一方面,科学计算,图像处理在生物,材料等科研领域可以高效准确的解决越来越多的问题,然而依然有很多科研工作者编程能力比较薄弱,因此让Numpy系列的科学计算库造福更多科研工作者是一项非常有意义的工作。而ImagePy就是一座桥梁,尽可能的让科学计算工作者隔离自己不擅长的UI和交互设计,着重精力处理算法本身,并且可以快速形成工具甚至产品,而这些工作又可以让更多不擅长编程的科研工作者收益,推广和普及图像处理,统计等科学知识。 +However there are still many researchers lack of programming skill. Thus it is a crucial to make the Numpy-based scientific computing libraries available to more researchers. ImagePy brings the computing capacities closer to the non-programmer researchers. So that they won't need to care about the UI and interaction design, and focus exclusively on the algorithm itself, and finally, accelerate open-source tool building or even commercial products incubation. These work, on the other hand, can let more researchers, who are not good at programming, gain, promote and popularize scientific knowledge such as image processing and statistics. From 706ccdb3f19734432a1611d417d0fcd6a67d5011 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 13 Dec 2018 17:32:44 +0800 Subject: [PATCH 057/343] readme --- README.md | 93 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 52 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 907f0e51..76660402 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,15 @@ ImagePy is an open source image processing framework written in Python. Its UI interface, image data structure and table data structure are wxpython-based, Numpy-based and pandas-based respectively. Furthermore, it supports any plug-in based on Numpy and pandas, which can talk easily between scipy.ndimage, scikit-image, simpleitk, opencv and other image processing libraries. +![newdoc01](http://idoc.imagepy.org/imgs/newdoc01.png) +
- Overview Overview, mouse measurement, geometric transformation, filtering, segmentation, counting, etc.
+![newdoc02](http://idoc.imagepy.org/imgs/newdoc02.png) +
- ij style If you are more a IJ-style user, try `Windows -> Windows Style` to switch
@@ -54,8 +56,10 @@ ImagePy has a very rich set of features, and here, we use a specific example to `menu: File -> Local Samples -> Coins` to open the sample image within ImagePy. _PS: ImagePy supports bmp, jpg, png, gif, tif and other commonly used file format. By installing ITK plug-in,dicom,nii and other medical image format can also be read/saved. It is also possible to read/write wmv, avi and other video format by installing OpenCV._ +![newdoc03](imgs\newdoc03.png) +
- ij style + ij style
@@ -66,13 +70,9 @@ Here, a composite filter is selected to perform sobel gradient extraction on the Filtering and segmentation are the crucial skills in the image processing toolkit, and are the key to the success or failure of the final measurement. Segmentation methods such as adaptive thresholds, watersheds and others are also supported. -
- -
+![newdoc04](imgs\newdoc04.png) -
- -
+![newdoc05](imgs\newdoc05.png) ### Binarization @@ -81,9 +81,7 @@ Segmentation methods such as adaptive thresholds, watersheds and others are also After the segmentation, we obtained a relatively clean mask image, but there are still some hollowing out, as well as a little impurities, which will interfere with counting and measurement. _ImagePy supports binary operations such as erode, dilate, opening and closing, as well as skeletonization, central axis extraction, and distance transformation._ -
- -
+![newdoc06](imgs\newdoc06.png) ### Geometry filtering @@ -91,8 +89,9 @@ _ImagePy supports binary operations such as erode, dilate, opening and closing, ImagePy can perform geometric filtering based on :__the area, the perimeter, the topology, the solidity, the eccentricity__ and other parameters. You can also use multiple conditions filtering. Each number can be positive|negative. It indicates the kept object will have the corresponding parameter greater|smaller than the value respectively. The kept objects will be set to the front color, the rejected ones will be set to the back color. In this demo, the back color is set to 100 in order to see which ones are filtered out. Once satisfied with the result, set the back color to 0 to reject them. In addition, ImagePy also supports gray density filtering, color filtering, color clustering and other functions. -
- +![newdoc07](imgs\newdoc07.png) + +
Geometry filtering (the area is over-chosen to emphasize the distinction)
@@ -103,13 +102,13 @@ ImagePy can perform geometric filtering based on :__the area, the perimeter, the Count the area and analyze the parameters. By choosing the `cov` option, ImagePy will fit each area with an ellipse calculated via the covariance. The parameters such as area, perimeter, eccentricity, and solidity shown in the previous step are calculated here. In fact, the filtering of the previous step is a downstream analysis of this one. -
- - Geometry Analysis - - Generate the result table (intensity is reduced in order to emphasize the - ellipse) -
+![newdoc08](imgs\newdoc08.png) + +Geometry Analysis + +![newdoc09](imgs\newdoc09.png) + +Generate the result table (intensity is reduced in order to emphasize the ellipse) ### Sort Table by area @@ -118,8 +117,9 @@ The parameters such as area, perimeter, eccentricity, and solidity shown in the Select the major key as area, and select descend. The table will be sorted in descending order of area. A table is another important piece of data other than an image. In a sense, many times we need to get the required information on the image and then post-process the data in the form of a table. ImagePy supports table I/O (xls, xlsx, csv), filtering, slicing, statistical analysis, sorting and more. -
- +![newdoc10](imgs\newdoc10.png) + +
Right click on the column header to set the text color, decimal precision, line style, etc.
@@ -130,9 +130,10 @@ Select the major key as area, and select descend. The table will be sorted in de From tabular data, we often need to draw a graph. Here, we plot the histograms of the area and the perimeter columns. ImagePy's tables can be used to draw common charts such as line charts, pie charts, histograms, and scatter plots (matplotlib-based). The chart comes with zooming, moving and other functions. The table can also be saved as an image. +![newdoc11](imgs\newdoc11.png) -
- + +
Histograms
@@ -149,8 +150,9 @@ Surface reconstruction of the image. This image shows the three reconstructed re ImagePy can perform 3D filtering of images, 3D skeletons, 3D topological analysis, 2D surface reconstruction, and 3D surface visualization. The 3D view can be freely dragged, rotated, and the image results can be saved as a .stl file. -
- +![newdoc12](imgs\newdoc12.png) + +
3D visualisation
@@ -164,8 +166,9 @@ Macro recorder is shown in the develop tool panel. We have manually completed an Macros are saved into .mc files. drag and drop the file to the status bar at the bottom of ImagePy, the macro will be executed automatically. we can also copy the .mc file to the submenu of the menus under the ImagePy file directory. When ImagePy is started, the macro file will be parsed into a menu item at the corresponding location. By clicking the menu, the macro will also be executed. -
- +![newdoc13](imgs\newdoc13.png) + +
Macro Recording
@@ -178,18 +181,19 @@ The chapter corresponds to a rectangular area in the flow chart, and the section The workflow is actually written in MarkDown (a markup language), but it needs to be written respecting several specifications, as follows: -``` +```markdown Title -=== +===== ## Chapter1 -1. Section1 +1. Section1 some coment for section1 ... -2. ... +2. ... ## Chapter 2 ... ``` -
- +![newdoc14](imgs\newdoc14.png) + +
Workflow
@@ -199,22 +203,27 @@ some coment for section1 ... We introduced macros and workflows in the last sections, using macros and workflows to connect existing functions is convenient. But sometimes we need to create new features. In this section, we are trying to add a new feature to ImagePy. ImagePy can easily access any Numpy-based function. Let's take the Canny operator of scikit-image as an example. -``` +```python from skimage import feature from imagepy.core.engine import Filter + class Plugin(Filter): title = 'Canny' note = ['all', 'auto_msk', 'auto_snap', 'preview'] para = {'sigma':1.0, 'low_threshold':10, 'high_threshold':20} + view = [(float, 'sigma', (0,10), 1, 'sigma', 'pix'), - ('slide', 'low_threshold', (0,50), 4, 'low_threshold'), - ('slide', 'high_threshold', (0,50), 4, 'high_threshold')] + ('slide', 'low_threshold', (0,50), 4, 'low_threshold'), + ('slide', 'high_threshold', (0,50), 4, 'high_threshold')] + def run(self, ips, snap, img, para = None): return feature.canny(snap, para['sigma'], para['low_threshold'], - para['high_threshold'], mask=ips.get_msk())*255 + para['high_threshold'], mask=ips.get_msk())*255 ``` +![newdoc15](imgs\newdoc15.png) +
- +
#### Steps to create a your own filter: @@ -261,8 +270,10 @@ def run(self, tps, data, snap, para=None): axis=0, ascending = not para['descend'], inplace=True) ``` +![newdoc16](imgs\newdoc16.png) +
- +
#### How Table works From 3e537e82f73a5dd5e4bee85b0991ef88f7250561 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 13 Dec 2018 17:38:10 +0800 Subject: [PATCH 058/343] readme --- README.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 76660402..91fd2be7 100644 --- a/README.md +++ b/README.md @@ -56,10 +56,10 @@ ImagePy has a very rich set of features, and here, we use a specific example to `menu: File -> Local Samples -> Coins` to open the sample image within ImagePy. _PS: ImagePy supports bmp, jpg, png, gif, tif and other commonly used file format. By installing ITK plug-in,dicom,nii and other medical image format can also be read/saved. It is also possible to read/write wmv, avi and other video format by installing OpenCV._ -![newdoc03](imgs\newdoc03.png) +![newdoc03](http://idoc.imagepy.org/imgs/newdoc03.png)
- ij style + ij style
@@ -70,9 +70,9 @@ Here, a composite filter is selected to perform sobel gradient extraction on the Filtering and segmentation are the crucial skills in the image processing toolkit, and are the key to the success or failure of the final measurement. Segmentation methods such as adaptive thresholds, watersheds and others are also supported. -![newdoc04](imgs\newdoc04.png) +![newdoc04](http://idoc.imagepy.org/imgs/newdoc04.png) -![newdoc05](imgs\newdoc05.png) +![newdoc05](http://idoc.imagepy.org/imgs/newdoc05.png) ### Binarization @@ -81,7 +81,7 @@ Segmentation methods such as adaptive thresholds, watersheds and others are also After the segmentation, we obtained a relatively clean mask image, but there are still some hollowing out, as well as a little impurities, which will interfere with counting and measurement. _ImagePy supports binary operations such as erode, dilate, opening and closing, as well as skeletonization, central axis extraction, and distance transformation._ -![newdoc06](imgs\newdoc06.png) +![newdoc06](http://idoc.imagepy.org/imgs/newdoc06.png) ### Geometry filtering @@ -89,7 +89,7 @@ _ImagePy supports binary operations such as erode, dilate, opening and closing, ImagePy can perform geometric filtering based on :__the area, the perimeter, the topology, the solidity, the eccentricity__ and other parameters. You can also use multiple conditions filtering. Each number can be positive|negative. It indicates the kept object will have the corresponding parameter greater|smaller than the value respectively. The kept objects will be set to the front color, the rejected ones will be set to the back color. In this demo, the back color is set to 100 in order to see which ones are filtered out. Once satisfied with the result, set the back color to 0 to reject them. In addition, ImagePy also supports gray density filtering, color filtering, color clustering and other functions. -![newdoc07](imgs\newdoc07.png) +![newdoc07](http://idoc.imagepy.org/imgs/newdoc07.png)
Geometry filtering (the area is over-chosen to emphasize the distinction) @@ -102,11 +102,11 @@ ImagePy can perform geometric filtering based on :__the area, the perimeter, the Count the area and analyze the parameters. By choosing the `cov` option, ImagePy will fit each area with an ellipse calculated via the covariance. The parameters such as area, perimeter, eccentricity, and solidity shown in the previous step are calculated here. In fact, the filtering of the previous step is a downstream analysis of this one. -![newdoc08](imgs\newdoc08.png) +![newdoc08](http://idoc.imagepy.org/imgs/newdoc08.png) Geometry Analysis -![newdoc09](imgs\newdoc09.png) +![newdoc09](http://idoc.imagepy.org/imgs/newdoc09.png) Generate the result table (intensity is reduced in order to emphasize the ellipse) @@ -117,7 +117,7 @@ Generate the result table (intensity is reduced in order to emphasize the ellips Select the major key as area, and select descend. The table will be sorted in descending order of area. A table is another important piece of data other than an image. In a sense, many times we need to get the required information on the image and then post-process the data in the form of a table. ImagePy supports table I/O (xls, xlsx, csv), filtering, slicing, statistical analysis, sorting and more. -![newdoc10](imgs\newdoc10.png) +![newdoc10](http://idoc.imagepy.org/imgs/newdoc10.png)
Right click on the column header to set the text color, decimal precision, line style, etc. @@ -130,7 +130,7 @@ Select the major key as area, and select descend. The table will be sorted in de From tabular data, we often need to draw a graph. Here, we plot the histograms of the area and the perimeter columns. ImagePy's tables can be used to draw common charts such as line charts, pie charts, histograms, and scatter plots (matplotlib-based). The chart comes with zooming, moving and other functions. The table can also be saved as an image. -![newdoc11](imgs\newdoc11.png) +![newdoc11](http://idoc.imagepy.org/imgs/newdoc11.png)
@@ -150,7 +150,7 @@ Surface reconstruction of the image. This image shows the three reconstructed re ImagePy can perform 3D filtering of images, 3D skeletons, 3D topological analysis, 2D surface reconstruction, and 3D surface visualization. The 3D view can be freely dragged, rotated, and the image results can be saved as a .stl file. -![newdoc12](imgs\newdoc12.png) +![newdoc12](http://idoc.imagepy.org/imgs/newdoc12.png)
3D visualisation @@ -166,7 +166,7 @@ Macro recorder is shown in the develop tool panel. We have manually completed an Macros are saved into .mc files. drag and drop the file to the status bar at the bottom of ImagePy, the macro will be executed automatically. we can also copy the .mc file to the submenu of the menus under the ImagePy file directory. When ImagePy is started, the macro file will be parsed into a menu item at the corresponding location. By clicking the menu, the macro will also be executed. -![newdoc13](imgs\newdoc13.png) +![newdoc13](http://idoc.imagepy.org/imgs/newdoc13.png)
Macro Recording @@ -191,7 +191,7 @@ some coment for section1 ... ## Chapter 2 ... ``` -![newdoc14](imgs\newdoc14.png) +![newdoc14](http://idoc.imagepy.org/imgs/newdoc14.png)
Workflow @@ -220,10 +220,10 @@ def run(self, ips, snap, img, para = None): return feature.canny(snap, para['sigma'], para['low_threshold'], para['high_threshold'], mask=ips.get_msk())*255 ``` -![newdoc15](imgs\newdoc15.png) +![newdoc15](http://idoc.imagepy.org/imgs/newdoc15.png)
- +
#### Steps to create a your own filter: @@ -270,10 +270,10 @@ def run(self, tps, data, snap, para=None): axis=0, ascending = not para['descend'], inplace=True) ``` -![newdoc16](imgs\newdoc16.png) +![newdoc16](http://idoc.imagepy.org/imgs/newdoc16.png)
- +
#### How Table works From 88d2f0b1967c52b5e34e8dce33f77f12d1632539 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 13 Dec 2018 17:56:22 +0800 Subject: [PATCH 059/343] new readme --- README.md | 50 ++++++++++++++++++++------------------------------ 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 91fd2be7..672e1d46 100644 --- a/README.md +++ b/README.md @@ -58,9 +58,7 @@ _PS: ImagePy supports bmp, jpg, png, gif, tif and other commonly used file forma ![newdoc03](http://idoc.imagepy.org/imgs/newdoc03.png) -
- ij style -
+
Coins
### Filtering & Segmentation @@ -72,8 +70,14 @@ Segmentation methods such as adaptive thresholds, watersheds and others are also ![newdoc04](http://idoc.imagepy.org/imgs/newdoc04.png) +
Up And Down Watershed
+ + + ![newdoc05](http://idoc.imagepy.org/imgs/newdoc05.png) +
Mask
+ ### Binarization `menu:Process -> Binary -> Binary Fill Holes` @@ -83,6 +87,8 @@ _ImagePy supports binary operations such as erode, dilate, opening and closing, ![newdoc06](http://idoc.imagepy.org/imgs/newdoc06.png) +
Fill Holes
+ ### Geometry filtering `menu:Analysis -> Region Analysis -> Geometry Filter` @@ -91,9 +97,7 @@ ImagePy can perform geometric filtering based on :__the area, the perimeter, the ![newdoc07](http://idoc.imagepy.org/imgs/newdoc07.png) -
- Geometry filtering (the area is over-chosen to emphasize the distinction) -
+
Geometry filtering (the area is over-chosen to emphasize the distinction)
### Geometry Analysis @@ -104,24 +108,22 @@ The parameters such as area, perimeter, eccentricity, and solidity shown in the ![newdoc08](http://idoc.imagepy.org/imgs/newdoc08.png) -Geometry Analysis +
Geometry Analysis
![newdoc09](http://idoc.imagepy.org/imgs/newdoc09.png) -Generate the result table (intensity is reduced in order to emphasize the ellipse) +
Generate the result table (dark to emphasize the ellipse)
### Sort Table by area `menu:Table -> Statistic -> Table Sort By Key` -Select the major key as area, and select descend. The table will be sorted in descending order of area. A table is another important piece of data other than an image. In a sense, many times we need to get the required information on the image and then post-process the data in the form of a table. ImagePy supports table I/O (xls, xlsx, csv), filtering, slicing, statistical analysis, sorting and more. +Select the major key as area, and select descend. The table will be sorted in descending order of area. A table is another important piece of data other than an image. In a sense, many times we need to get the required information on the image and then post-process the data in the form of a table. ImagePy supports table I/O (xls, xlsx, csv), filtering, slicing, statistical analysis, sorting and more. (Right click on the column header to set the text color, decimal precision, line style, etc.) ![newdoc10](http://idoc.imagepy.org/imgs/newdoc10.png) -
- Right click on the column header to set the text color, decimal precision, line style, etc. -
+
Table
### Charts @@ -133,9 +135,7 @@ From tabular data, we often need to draw a graph. Here, we plot the histograms o ![newdoc11](http://idoc.imagepy.org/imgs/newdoc11.png) -
- Histograms -
+
Histograms
### 3D chart @@ -152,9 +152,7 @@ ImagePy can perform 3D filtering of images, 3D skeletons, 3D topological analysi ![newdoc12](http://idoc.imagepy.org/imgs/newdoc12.png) -
- 3D visualisation -
+
3D visualisation
@@ -168,9 +166,7 @@ Macros are saved into .mc files. drag and drop the file to the status bar at the ![newdoc13](http://idoc.imagepy.org/imgs/newdoc13.png) -
- Macro Recording -
+
Macro Recording
### Workflow @@ -193,9 +189,7 @@ some coment for section1 ... ``` ![newdoc14](http://idoc.imagepy.org/imgs/newdoc14.png) -
- Workflow -
+
Workflow
@@ -222,9 +216,7 @@ def run(self, ips, snap, img, para = None): ``` ![newdoc15](http://idoc.imagepy.org/imgs/newdoc15.png) -
- -
+
Canny Filter Demo
#### Steps to create a your own filter: @@ -272,9 +264,7 @@ def run(self, tps, data, snap, para=None): ``` ![newdoc16](http://idoc.imagepy.org/imgs/newdoc16.png) -
- -
+
Table Sort Demo
#### How Table works From b9ac37896d48f27f760b59d24611aaf6ff05ccff Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 13 Dec 2018 18:09:46 +0800 Subject: [PATCH 060/343] new readme --- README.md | 45 ++++++++++++++------------------------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 672e1d46..3d87df25 100644 --- a/README.md +++ b/README.md @@ -57,8 +57,7 @@ ImagePy has a very rich set of features, and here, we use a specific example to _PS: ImagePy supports bmp, jpg, png, gif, tif and other commonly used file format. By installing ITK plug-in,dicom,nii and other medical image format can also be read/saved. It is also possible to read/write wmv, avi and other video format by installing OpenCV._ ![newdoc03](http://idoc.imagepy.org/imgs/newdoc03.png) - -
Coins
+
Coins

### Filtering & Segmentation @@ -69,14 +68,12 @@ Filtering and segmentation are the crucial skills in the image processing toolki Segmentation methods such as adaptive thresholds, watersheds and others are also supported. ![newdoc04](http://idoc.imagepy.org/imgs/newdoc04.png) - -
Up And Down Watershed
+
Up And Down Watershed

![newdoc05](http://idoc.imagepy.org/imgs/newdoc05.png) - -
Mask
+
Mask

### Binarization @@ -86,8 +83,7 @@ After the segmentation, we obtained a relatively clean mask image, but there are _ImagePy supports binary operations such as erode, dilate, opening and closing, as well as skeletonization, central axis extraction, and distance transformation._ ![newdoc06](http://idoc.imagepy.org/imgs/newdoc06.png) - -
Fill Holes
+
Fill Holes

### Geometry filtering @@ -96,8 +92,7 @@ _ImagePy supports binary operations such as erode, dilate, opening and closing, ImagePy can perform geometric filtering based on :__the area, the perimeter, the topology, the solidity, the eccentricity__ and other parameters. You can also use multiple conditions filtering. Each number can be positive|negative. It indicates the kept object will have the corresponding parameter greater|smaller than the value respectively. The kept objects will be set to the front color, the rejected ones will be set to the back color. In this demo, the back color is set to 100 in order to see which ones are filtered out. Once satisfied with the result, set the back color to 0 to reject them. In addition, ImagePy also supports gray density filtering, color filtering, color clustering and other functions. ![newdoc07](http://idoc.imagepy.org/imgs/newdoc07.png) - -
Geometry filtering (the area is over-chosen to emphasize the distinction)
+
Geometry filtering (the area is over-chosen to emphasize the distinction)

### Geometry Analysis @@ -107,12 +102,10 @@ Count the area and analyze the parameters. By choosing the `cov` option, ImagePy The parameters such as area, perimeter, eccentricity, and solidity shown in the previous step are calculated here. In fact, the filtering of the previous step is a downstream analysis of this one. ![newdoc08](http://idoc.imagepy.org/imgs/newdoc08.png) - -
Geometry Analysis
+
Geometry Analysis

![newdoc09](http://idoc.imagepy.org/imgs/newdoc09.png) - -
Generate the result table (dark to emphasize the ellipse)
+
Generate the result table (dark to emphasize the ellipse)

### Sort Table by area @@ -122,8 +115,7 @@ The parameters such as area, perimeter, eccentricity, and solidity shown in the Select the major key as area, and select descend. The table will be sorted in descending order of area. A table is another important piece of data other than an image. In a sense, many times we need to get the required information on the image and then post-process the data in the form of a table. ImagePy supports table I/O (xls, xlsx, csv), filtering, slicing, statistical analysis, sorting and more. (Right click on the column header to set the text color, decimal precision, line style, etc.) ![newdoc10](http://idoc.imagepy.org/imgs/newdoc10.png) - -
Table
+
Table

### Charts @@ -133,9 +125,7 @@ Select the major key as area, and select descend. The table will be sorted in de From tabular data, we often need to draw a graph. Here, we plot the histograms of the area and the perimeter columns. ImagePy's tables can be used to draw common charts such as line charts, pie charts, histograms, and scatter plots (matplotlib-based). The chart comes with zooming, moving and other functions. The table can also be saved as an image. ![newdoc11](http://idoc.imagepy.org/imgs/newdoc11.png) - - -
Histograms
+
Histograms

### 3D chart @@ -151,8 +141,7 @@ Surface reconstruction of the image. This image shows the three reconstructed re ImagePy can perform 3D filtering of images, 3D skeletons, 3D topological analysis, 2D surface reconstruction, and 3D surface visualization. The 3D view can be freely dragged, rotated, and the image results can be saved as a .stl file. ![newdoc12](http://idoc.imagepy.org/imgs/newdoc12.png) - -
3D visualisation
+
3D visualisation

@@ -165,8 +154,7 @@ Macro recorder is shown in the develop tool panel. We have manually completed an Macros are saved into .mc files. drag and drop the file to the status bar at the bottom of ImagePy, the macro will be executed automatically. we can also copy the .mc file to the submenu of the menus under the ImagePy file directory. When ImagePy is started, the macro file will be parsed into a menu item at the corresponding location. By clicking the menu, the macro will also be executed. ![newdoc13](http://idoc.imagepy.org/imgs/newdoc13.png) - -
Macro Recording
+
Macro Recording

### Workflow @@ -188,10 +176,7 @@ some coment for section1 ... ... ``` ![newdoc14](http://idoc.imagepy.org/imgs/newdoc14.png) - -
Workflow
- - +
Workflow

### Filter Plugin @@ -215,8 +200,7 @@ def run(self, ips, snap, img, para = None): para['high_threshold'], mask=ips.get_msk())*255 ``` ![newdoc15](http://idoc.imagepy.org/imgs/newdoc15.png) - -
Canny Filter Demo
+
Canny Filter Demo

#### Steps to create a your own filter: @@ -263,8 +247,7 @@ def run(self, tps, data, snap, para=None): inplace=True) ``` ![newdoc16](http://idoc.imagepy.org/imgs/newdoc16.png) - -
Table Sort Demo
+
Table Sort Demo

#### How Table works From de1705f9250798c68357dfde966d118d0e7d85fd Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 13 Dec 2018 18:11:50 +0800 Subject: [PATCH 061/343] new readme --- README.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 3d87df25..5742a8a9 100644 --- a/README.md +++ b/README.md @@ -3,16 +3,10 @@ ImagePy is an open source image processing framework written in Python. Its UI interface, image data structure and table data structure are wxpython-based, Numpy-based and pandas-based respectively. Furthermore, it supports any plug-in based on Numpy and pandas, which can talk easily between scipy.ndimage, scikit-image, simpleitk, opencv and other image processing libraries. ![newdoc01](http://idoc.imagepy.org/imgs/newdoc01.png) - -
- Overview, mouse measurement, geometric transformation, filtering, segmentation, counting, etc. -
+
Overview, mouse measurement, geometric transformation, filtering, segmentation, counting, etc.

![newdoc02](http://idoc.imagepy.org/imgs/newdoc02.png) - -
- If you are more a IJ-style user, try `Windows -> Windows Style` to switch -
+
If you are more a IJ-style user, try `Windows -> Windows Style` to switch

ImagePy: - has a user-friendly interface From 442ac6bf6ae33f109f7be2f01f92a8713f81c807 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 13 Dec 2018 23:58:27 +0800 Subject: [PATCH 062/343] new readme ok --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5742a8a9..a413ab72 100644 --- a/README.md +++ b/README.md @@ -191,7 +191,7 @@ class Plugin(Filter): def run(self, ips, snap, img, para = None): return feature.canny(snap, para['sigma'], para['low_threshold'], - para['high_threshold'], mask=ips.get_msk())*255 + para['high_threshold'], mask=ips.get_msk())*255 ``` ![newdoc15](http://idoc.imagepy.org/imgs/newdoc15.png)
Canny Filter Demo

@@ -225,20 +225,22 @@ The framework unifies the complex tasks in a formal manner and helps us to perfo As mentioned earlier, the table is another very important data type other than the image. Similarly, ImagePy also supports the extension of table. Here we give an example of sorting-by-key used in the previous description. -``` +```python from imagepy.core.engine import Table import pandas as pd + class Plugin(Table): title = 'Table Sort By Key' para = {'major':None, 'minor':None, 'descend':False} + view = [('field', 'major', 'major', 'key'), ('field', 'minor', 'minor', 'key'), (bool, 'descend', 'descend')] + def run(self, tps, data, snap, para=None): by = [para['major'], para['minor']] data.sort_values(by=[i for i in by if i != 'None'], - axis=0, ascending = not para['descend'], - inplace=True) + axis=0, ascending = not para['descend'], inplace=True) ``` ![newdoc16](http://idoc.imagepy.org/imgs/newdoc16.png)
Table Sort Demo

From 53798b14ff26238763c6ab66e1397abbdeb2a977 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 15 Dec 2018 11:04:55 +0800 Subject: [PATCH 063/343] property marker --- .../Region Analysis/regionprops_plgs.py | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py index 26478b86..d83903a0 100644 --- a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py @@ -6,7 +6,7 @@ from imagepy import IPy import numpy as np from imagepy.core.engine import Simple, Filter -from imagepy.core.manager import ImageManager +from imagepy.core.manager import ImageManager, ColorManager from scipy.ndimage import label, generate_binary_structure from skimage.measure import regionprops from imagepy.core.mark import GeometryMark @@ -159,4 +159,43 @@ def run(self, ips, snap, img, para = None): idx[0] = para['front'] if para['inv'] else 0 img[:] = idx[lab] -plgs = [RegionCounter, RegionFilter] \ No newline at end of file + +# center, area, l, extent, cov +class PropertyMarker(Filter): + title = 'Property Marker' + note = ['8-bit', '16-bit', 'auto_msk', 'auto_snap','preview'] + para = {'con':'4-connect', 'pro':'area', 'cm':'gray'} + view = [(list, 'con', ['4-connect', '8-connect'], str, 'conection', 'pix'), + (list, 'pro', ['area', 'perimeter', 'solid', 'eccentricity'], str, 'property', ''), + ('cmap', 'cm', 'color map')] + + def load(self, ips): + self.lut = ips.lut + return True + + def cancel(self, ips): + ips.lut = self.lut + Filter.cancel(self, ips) + + #process + def run(self, ips, snap, img, para = None): + strc = generate_binary_structure(2, 1 if para['con']=='4-connect' else 2) + + lab, n = label(snap, strc, output=np.uint32) + idx = (np.zeros(n+1)).astype(np.uint8) + ls = regionprops(lab) + + if para['pro'] == 'area': ps = [i.area for i in ls] + if para['pro'] == 'perimeter': ps = [i.perimeter for i in ls] + if para['pro'] == 'solid': ps = [i.solidity for i in ls] + if para['pro'] == 'eccentricity': ps = [i.major_axis_length/i.minor_axis_length for i in ls] + + ps = np.array(ps) + if ps.max() != ps.min(): + ps = (ps - ps.min()) / (ps.max() - ps.min()) + else: ps = ps / ps.max() + idx[1:] = ps * 245 + 10 + img[:] = idx[lab] + ips.lut = ColorManager.get_lut(para['cm']) + +plgs = [RegionCounter, RegionFilter, PropertyMarker] \ No newline at end of file From 1ddad5c5285809eba9cb72edbc623e95177f4470 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 17 Dec 2018 16:43:38 -0600 Subject: [PATCH 064/343] readme cleanup --- README.md | 90 +++++++++++++++++++++++++++---------------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index a413ab72..ee3caa09 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Introduction -ImagePy is an open source image processing framework written in Python. Its UI interface, image data structure and table data structure are wxpython-based, Numpy-based and pandas-based respectively. Furthermore, it supports any plug-in based on Numpy and pandas, which can talk easily between scipy.ndimage, scikit-image, simpleitk, opencv and other image processing libraries. +ImagePy is an open source image processing framework written in Python. Its UI interface, image data structure and table data structure are wxpython-based, Numpy-based and pandas-based respectively. Furthermore, it supports any plug-in based on Numpy and pandas, which can talk easily between scipy.ndimage, scikit-image, simpleitk, opencv and other image processing libraries. ![newdoc01](http://idoc.imagepy.org/imgs/newdoc01.png)
Overview, mouse measurement, geometric transformation, filtering, segmentation, counting, etc.

@@ -13,10 +13,10 @@ ImagePy: - can read/save a variety of image data formats - supports ROI settings, drawing, measurement and other mouse operations - can perform image filtering, morphological operations and other routine operations -- can do image segmentation, area counting, geometric measurement and density analysis. -- is able to perfrom data analysis, filtering, statistical analysis and others related to the parameters extracted from the image. +- can do image segmentation, area counting, geometric measurement and density analysis. +- is able to perform data analysis, filtering, statistical analysis and others related to the parameters extracted from the image. -Our long-term goal of this project is to be used as ImageJ + SPSS (although not achieved yet) +Our long-term goal of this project is to be used as ImageJ + SPSS (although not achieved yet) ## Citation: @@ -26,39 +26,39 @@ Our long-term goal of this project is to be used as ImageJ + SPSS (although not __OS support:windows, linux, mac, with python2.7 and python3.__4+ -1. ImagePy is a ui framework based on wxpython, which can not install - with pip on Linux. You need download [the whl acrodding to your +1. ImagePy is a ui framework based on wxpython, which can not be installed + with pip on Linux. You need download [the whl according to your Linux system](https://wxpython.org/pages/downloads/). 2. On Linux and Mac, there may be permission denied promblem, for - ImagePy will write some config information, So please start with + ImagePy will write some config information, so please start with sudo. If you install with pip, please add \--user parameter like - this: pip install -user imagepy + this: pip install --user imagepy -3. If you install ImagePy in a Anaconda virtual environment, you may - got a error when start like this: This program needs access to the +3. If you install ImagePy in an Anaconda virtual environment, you may + get a error when starting like this: This program needs access to the screen. Please run with a Framework build of python, and only when you are logged in on the main display, if so, please start with pythonw -m imagepy. ## Basic operations: -ImagePy has a very rich set of features, and here, we use a specific example to show you a glimpse of the capacity of ImagePy. We choose the official coin split of scikit-image, since this example is simple and comprehensive. +ImagePy has a very rich set of features, and here, we use a specific example to show you a glimpse of the capacity of ImagePy. We choose the official coin split of scikit-image, since this example is simple and comprehensive. ### Open image -`menu: File -> Local Samples -> Coins` to open the sample image within ImagePy. -_PS: ImagePy supports bmp, jpg, png, gif, tif and other commonly used file format. By installing ITK plug-in,dicom,nii and other medical image format can also be read/saved. It is also possible to read/write wmv, avi and other video format by installing OpenCV._ +`menu: File -> Local Samples -> Coins` to open the sample image within ImagePy. +_PS: ImagePy supports bmp, jpg, png, gif, tif and other commonly used file formats. By installing ITK plug-in,dicom,nii and other medical image formats can also be read/saved. It is also possible to read/write wmv, avi and other video formats by installing OpenCV._ ![newdoc03](http://idoc.imagepy.org/imgs/newdoc03.png)
Coins

-### Filtering & Segmentation +### Filtering & Segmentation -`menu:Process -> Hydrology -> Up And Down Watershed` -Here, a composite filter is selected to perform sobel gradient extraction on the image, and then the upper and lower thresholds are used as the mark, at last we watershed on the gradient map. -Filtering and segmentation are the crucial skills in the image processing toolkit, and are the key to the success or failure of the final measurement. +`menu:Process -> Hydrology -> Up And Down Watershed` +Here, a composite filter is selected to perform sobel gradient extraction on the image, and then the upper and lower thresholds are used as the mark, and finally we watershed on the gradient map. +Filtering and segmentation are the crucial skills in the image processing toolkit, and are the key to the success or failure of the final measurement. Segmentation methods such as adaptive thresholds, watersheds and others are also supported. ![newdoc04](http://idoc.imagepy.org/imgs/newdoc04.png) @@ -71,28 +71,28 @@ Segmentation methods such as adaptive thresholds, watersheds and others are also ### Binarization -`menu:Process -> Binary -> Binary Fill Holes` +`menu:Process -> Binary -> Binary Fill Holes` -After the segmentation, we obtained a relatively clean mask image, but there are still some hollowing out, as well as a little impurities, which will interfere with counting and measurement. -_ImagePy supports binary operations such as erode, dilate, opening and closing, as well as skeletonization, central axis extraction, and distance transformation._ +After the segmentation, we obtained a relatively clean mask image, but there is still some hollowing out, as well as some impurities, which will interfere with counting and measurement. +_ImagePy supports binary operations such as erode, dilate, opening and closing, as well as skeletonization, central axis extraction, and distance transformation._ ![newdoc06](http://idoc.imagepy.org/imgs/newdoc06.png)
Fill Holes

-### Geometry filtering +### Geometry filtering -`menu:Analysis -> Region Analysis -> Geometry Filter` +`menu:Analysis -> Region Analysis -> Geometry Filter` -ImagePy can perform geometric filtering based on :__the area, the perimeter, the topology, the solidity, the eccentricity__ and other parameters. You can also use multiple conditions filtering. Each number can be positive|negative. It indicates the kept object will have the corresponding parameter greater|smaller than the value respectively. The kept objects will be set to the front color, the rejected ones will be set to the back color. In this demo, the back color is set to 100 in order to see which ones are filtered out. Once satisfied with the result, set the back color to 0 to reject them. In addition, ImagePy also supports gray density filtering, color filtering, color clustering and other functions. +ImagePy can perform geometric filtering based on :__the area, the perimeter, the topology, the solidity, the eccentricity__ and other parameters. You can also use multiple conditions for filtering. Each number can be positive|negative, which indicates the kept object will have the corresponding parameter greater|smaller than the value respectively. The kept objects will be set to the front color, the rejected ones will be set to the back color. In this demo, the back color is set to 100 in order to see which ones are filtered out. Once satisfied with the result, set the back color to 0 to reject them. In addition, ImagePy also supports gray density filtering, color filtering, color clustering and other functions. ![newdoc07](http://idoc.imagepy.org/imgs/newdoc07.png)
Geometry filtering (the area is over-chosen to emphasize the distinction)

-### Geometry Analysis +### Geometry Analysis -`menu:Process -> Region Analysis -> Geometry Analysis` -Count the area and analyze the parameters. By choosing the `cov` option, ImagePy will fit each area with an ellipse calculated via the covariance. +`menu:Process -> Region Analysis -> Geometry Analysis` +Count the area and analyze the parameters. By choosing the `cov` option, ImagePy will fit each area with an ellipse calculated via the covariance. The parameters such as area, perimeter, eccentricity, and solidity shown in the previous step are calculated here. In fact, the filtering of the previous step is a downstream analysis of this one. ![newdoc08](http://idoc.imagepy.org/imgs/newdoc08.png) @@ -102,9 +102,9 @@ The parameters such as area, perimeter, eccentricity, and solidity shown in the
Generate the result table (dark to emphasize the ellipse)

-### Sort Table by area +### Sort Table by area -`menu:Table -> Statistic -> Table Sort By Key` +`menu:Table -> Statistic -> Table Sort By Key` Select the major key as area, and select descend. The table will be sorted in descending order of area. A table is another important piece of data other than an image. In a sense, many times we need to get the required information on the image and then post-process the data in the form of a table. ImagePy supports table I/O (xls, xlsx, csv), filtering, slicing, statistical analysis, sorting and more. (Right click on the column header to set the text color, decimal precision, line style, etc.) @@ -112,11 +112,11 @@ Select the major key as area, and select descend. The table will be sorted in de
Table

-### Charts +### Charts -`menu:Table -> Chart -> Hist Chart` +`menu:Table -> Chart -> Hist Chart` -From tabular data, we often need to draw a graph. Here, we plot the histograms of the area and the perimeter columns. ImagePy's tables can be used to draw common charts such as line charts, pie charts, histograms, and scatter plots (matplotlib-based). The chart comes with zooming, moving and other functions. The table can also be saved as an image. +From tabular data, we often need to draw a graph. Here, we plot the histograms of the area and the perimeter columns. ImagePy's tables can be used to draw common charts such as line charts, pie charts, histograms, and scatter plots (matplotlib-based). The chart comes with zooming, moving and other functions. The table can also be saved as an image. ![newdoc11](http://idoc.imagepy.org/imgs/newdoc11.png)
Histograms

@@ -124,13 +124,13 @@ From tabular data, we often need to draw a graph. Here, we plot the histograms o ### 3D chart -`menu:Kit3D -> Viewer 3D -> 2D Surface` +`menu:Kit3D -> Viewer 3D -> 2D Surface` -Surface reconstruction of the image. This image shows the three reconstructed results including, sobel gradient map, high threshold and low threshold. It shows how the Up And Down Watershed works: +Surface reconstruction of the image. This image shows the three reconstructed results including, sobel gradient map, high threshold and low threshold. It shows how the Up And Down Watershed works: - calculate the gradient. - mark the coin and background through the high and low thresholds, -- simulate the rising water on the dem diagram to form the segmentation. +- simulate the rising water on the dem diagram to form the segmentation. ImagePy can perform 3D filtering of images, 3D skeletons, 3D topological analysis, 2D surface reconstruction, and 3D surface visualization. The 3D view can be freely dragged, rotated, and the image results can be saved as a .stl file. @@ -139,9 +139,9 @@ ImagePy can perform 3D filtering of images, 3D skeletons, 3D topological analysi -### Macro recording and execution +### Macro recording and execution -`menu:Window -> Develop Tool Suite` +`menu:Window -> Develop Tool Suite` Macro recorder is shown in the develop tool panel. We have manually completed an image segmentation. However, batch processing more than 10 images can be tedious. So, assuming that these steps are highly repeatable and robust for dealing with such problems, we can record a macro to combine several processes into a one-click program. The macro recorder is similar to a radio recorder. When it is turned on, each step of the operation will be recorded. We can click the pause button to stop recording, then click the play button to execute. When the macro is running, the recorded commands will be executed sequentially, therefore achieving simplicity and reproducibility. @@ -153,7 +153,7 @@ Macros are saved into .mc files. drag and drop the file to the status bar at the ### Workflow -A macro is a sequence of predefined commands. By recording a series of fixed operations into macros, you can improve your work efficiency. However, the disadvantage is the lack of flexibility. For example, sometimes the main steps are fixed, but the parameter tuning needs human interaction. In this case, the workflow is what you want. A workflow in ImagePy is a flow chart that can be visualized, divided into two levels: __chapters and sections__. +A macro is a sequence of predefined commands. By recording a series of fixed operations into macros, you can improve your work efficiency. However, the disadvantage is the lack of flexibility. For example, sometimes the main steps are fixed, but the parameter tuning needs human interaction. In this case, the workflow is what you want. A workflow in ImagePy is a flow chart that can be visualized, divided into two levels: __chapters and sections__. The chapter corresponds to a rectangular area in the flow chart, and the section is a button in the rectangular area, which is also a command and is accompanied by a graphic explanation. The message window on the right will display the corresponding function description, while mousing hovering above. Click on the `Detail Document` in the top right corner to see the documentation of the entire process. @@ -184,11 +184,11 @@ class Plugin(Filter): title = 'Canny' note = ['all', 'auto_msk', 'auto_snap', 'preview'] para = {'sigma':1.0, 'low_threshold':10, 'high_threshold':20} - + view = [(float, 'sigma', (0,10), 1, 'sigma', 'pix'), ('slide', 'low_threshold', (0,50), 4, 'low_threshold'), ('slide', 'high_threshold', (0,50), 4, 'high_threshold')] - + def run(self, ips, snap, img, para = None): return feature.canny(snap, para['sigma'], para['low_threshold'], para['high_threshold'], mask=ips.get_msk())*255 @@ -207,7 +207,7 @@ def run(self, ips, snap, img, para = None): 6. Define the interaction method for each of the parameters in __`View`__, the framework will automatically generate the dialog for parameter tuning by reading these information. 7. Write the core function __`run`__. `img` is the current image, `para` is the result entre by user. if `auto_snap` is set in `note`, `snap` will be a duplicate of `img`. We can process the `snap`, store the result in `img`. If the function does not support the specified output, we can also return the result, and the framework will help us copy the result to img and display it. 8. Save the file as `xxx_plg.py` and copy to the `menu` folder, restart ImagePy. - It will be loaded as a menu item. + It will be loaded as a menu item. #### What did the framework do for us? @@ -245,7 +245,7 @@ def run(self, tps, data, snap, para=None): ![newdoc16](http://idoc.imagepy.org/imgs/newdoc16.png)
Table Sort Demo

-#### How Table works +#### How Table works Same as `Filter`,`Table` also has parameters such as `title`,`note`,`para`,`view`. When the plugin is running, the framework will generate a dialog box according to `para` @@ -255,9 +255,9 @@ and `view`. After the parameters are chosen, they are passed to the `run` togeth The `Filter` and `Table` described above are the two most important plugins, but ImagePy also supports some other types of plugin extensions. There are currently nine, they are: -1. `Filter`: mainly for image processing +1. `Filter`: mainly for image processing 2. `Simple`: similar to `Filter`, but focus on the overall characteristics of the image, such as the operation of the ROI, the operation of the false color, the area measurement, or the three-dimensional analysis of the entire image stack, visualization, and so on. -3. `Free`: operate that are independant of image. Used to open image, close software etc. +3. `Free`: operate that are independant of image. Used to open image, close software etc. 4. `Tool`: use the mouse to interact on the diagram and show small icons on the toolbar, such as a brush. 5. `Table`: operate on the table, such as statistics analysis, sorting, plotting. 6. `Widget`: widgets that are displayed in panels, such as the navigation bar on the right, the macro recorder, and others. @@ -267,6 +267,6 @@ The `Filter` and `Table` described above are the two most important plugins, but ## Motivation & Goal -Python is a simple, elegant, powerful language, and has a very rich third-party library for scientific computing. Based on the universal matrix structure and the corresponding rules, numpy-based library such as scipy, scikit-image, scikit-learn and other scientific computing libraries have brought great convenience to scientific research. On the other hand, more and more problems in biology, material science and other scientific research can be efficiently and accurately solved via scientific computing, image processing. +Python is a simple, elegant, powerful language, and has very rich third-party libraries for scientific computing. Based on the universal matrix structure and the corresponding rules, numpy-based libraries such as scipy, scikit-image, scikit-learn and other scientific computing libraries have brought great convenience to scientific research. On the other hand, more and more problems in biology, material science and other scientific research can be efficiently and accurately solved via scientific computing, image processing. -However there are still many researchers lack of programming skill. Thus it is a crucial to make the Numpy-based scientific computing libraries available to more researchers. ImagePy brings the computing capacities closer to the non-programmer researchers. So that they won't need to care about the UI and interaction design, and focus exclusively on the algorithm itself, and finally, accelerate open-source tool building or even commercial products incubation. These work, on the other hand, can let more researchers, who are not good at programming, gain, promote and popularize scientific knowledge such as image processing and statistics. +However there are still many researchers that lack programming skills. Thus it is a crucial to make the Numpy-based scientific computing libraries available to more researchers. ImagePy brings the computing capacities closer to the non-programmer researchers, so that they won't need to be concerned about the UI and interaction design, and focus exclusively on the algorithm itself, and finally, accelerate open-source tool building or even commercial products incubation. These tools, meanwhile, can let more researchers, who are not good at programming, gain, promote and popularize scientific knowledge such as image processing and statistics. From 725a308f03f3054f1134909f49d6c9b03352ba1f Mon Sep 17 00:00:00 2001 From: tongli Date: Fri, 21 Dec 2018 18:28:18 +0100 Subject: [PATCH 065/343] able to read ij's roi with read_roi package --- imagepy/core/roi/roiio.py | 7 ++++- imagepy/menus/File/Import/roi_plg.py | 43 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 imagepy/menus/File/Import/roi_plg.py diff --git a/imagepy/core/roi/roiio.py b/imagepy/core/roi/roiio.py index 0d57d2cb..ee8ddc28 100644 --- a/imagepy/core/roi/roiio.py +++ b/imagepy/core/roi/roiio.py @@ -1,6 +1,7 @@ from .convert import roi2shape, shape2roi import pickle from shapely import wkt +import read_roi def roi2wkt(roi): return wkt.dumps(roi2shape(roi)) def wkt2roi(con): return shape2roi(wkt.loads(con)) @@ -25,4 +26,8 @@ def saveroi(roi, path): def savewkt(roi, path): f = open(path, 'w') f.write(roi2wkt(roi)) - f.close() \ No newline at end of file + f.close() + + +def read_roi_zip(path): + return read_roi.read_roi_zip(path) diff --git a/imagepy/menus/File/Import/roi_plg.py b/imagepy/menus/File/Import/roi_plg.py new file mode 100644 index 00000000..884f08f8 --- /dev/null +++ b/imagepy/menus/File/Import/roi_plg.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +""" +Created on 12/21/2018 +@author: BioinfoTongLI +""" + +from imagepy.core.manager import RoiManager +from imagepy.core.engine import Free +from imagepy import IPy +from imagepy.core.roi import roiio +from shapely.geometry import Polygon + +from imagepy.core.roi.convert import shape2roi + + +class Plugin(Free): + """load_ij_roi: use read_roi and th pass to shapely objects""" + title = 'Import Rois from ImageJ' + para = {'path': 'test'} + + def load(self): + filt = '|'.join(['%s files (*.%s)|*.%s' % (i.upper(), i, i) for i in ["zip"]]) + rst = IPy.getpath(self.title, filt, 'open', self.para) + if not rst: return rst + return True + + def run(self, para=None): + roi_set = roiio.read_roi_zip(self.para["path"]) + n_roi = len(roi_set) + print("Loaded %i rois" % n_roi) + for cell_nb in roi_set: + print("%s added " % cell_nb) + RoiManager.add(str(cell_nb), + shape2roi(Polygon(list(zip(roi_set[cell_nb]["x"], roi_set[cell_nb]["y"]))))) + + mask = "" + # view(imgs, para['title']) + + +if __name__ == '__main__': + print(Plugin.title) + app = wx.App(False) + Plugin().run() \ No newline at end of file From 783a0bfde9f636ab46bb95df9f9d755e22e86d78 Mon Sep 17 00:00:00 2001 From: Tong Date: Fri, 21 Dec 2018 23:18:16 +0100 Subject: [PATCH 066/343] roi to label image done. --- imagepy/menus/File/Import/roi_plg.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/imagepy/menus/File/Import/roi_plg.py b/imagepy/menus/File/Import/roi_plg.py index 884f08f8..a55aa08d 100644 --- a/imagepy/menus/File/Import/roi_plg.py +++ b/imagepy/menus/File/Import/roi_plg.py @@ -3,20 +3,24 @@ Created on 12/21/2018 @author: BioinfoTongLI """ - -from imagepy.core.manager import RoiManager +from imagepy.core import ImagePlus +# from imagepy.core.manager import RoiManager from imagepy.core.engine import Free from imagepy import IPy from imagepy.core.roi import roiio from shapely.geometry import Polygon - +import wx from imagepy.core.roi.convert import shape2roi +import numpy as np class Plugin(Free): """load_ij_roi: use read_roi and th pass to shapely objects""" title = 'Import Rois from ImageJ' - para = {'path': 'test'} + para = {'path': '', 'name': 'Undefined', 'width': 2560, 'height': 2160} + view = [(str, 'name', 'name', ''), + (int, 'width', (1, 3000), 0, 'width', 'pix'), + (int, 'height', (1, 3000), 0, 'height', 'pix')] def load(self): filt = '|'.join(['%s files (*.%s)|*.%s' % (i.upper(), i, i) for i in ["zip"]]) @@ -28,13 +32,17 @@ def run(self, para=None): roi_set = roiio.read_roi_zip(self.para["path"]) n_roi = len(roi_set) print("Loaded %i rois" % n_roi) - for cell_nb in roi_set: - print("%s added " % cell_nb) - RoiManager.add(str(cell_nb), - shape2roi(Polygon(list(zip(roi_set[cell_nb]["x"], roi_set[cell_nb]["y"]))))) - mask = "" - # view(imgs, para['title']) + mask_ips = ImagePlus([np.zeros((para['height'], para['width']), dtype=np.int16)], self.para["name"]) + IPy.showips(mask_ips) + + for one_id in roi_set: + one_roi = shape2roi(Polygon(list(zip(roi_set[one_id]["x"], roi_set[one_id]["y"])))) + # RoiManager.add(one_id, one_roi) + one_roi.fill(mask_ips.img, color=int(one_id)) + # seems not working here... + mask_ips.update = True + # RoiManager.rois = {} if __name__ == '__main__': From 2f9edca9c76a64d99133f1225af98b8fe7ead254 Mon Sep 17 00:00:00 2001 From: Tong Date: Sat, 22 Dec 2018 12:02:35 +0100 Subject: [PATCH 067/343] ij roi.zip loader done; all rois are directly translated to label image --- imagepy/menus/File/Import/roi_plg.py | 14 ++++++-------- requirements.txt | 20 +++++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/imagepy/menus/File/Import/roi_plg.py b/imagepy/menus/File/Import/roi_plg.py index a55aa08d..7663b9fe 100644 --- a/imagepy/menus/File/Import/roi_plg.py +++ b/imagepy/menus/File/Import/roi_plg.py @@ -4,7 +4,6 @@ @author: BioinfoTongLI """ from imagepy.core import ImagePlus -# from imagepy.core.manager import RoiManager from imagepy.core.engine import Free from imagepy import IPy from imagepy.core.roi import roiio @@ -16,7 +15,7 @@ class Plugin(Free): """load_ij_roi: use read_roi and th pass to shapely objects""" - title = 'Import Rois from ImageJ' + title = 'Import Rois from IJ' para = {'path': '', 'name': 'Undefined', 'width': 2560, 'height': 2160} view = [(str, 'name', 'name', ''), (int, 'width', (1, 3000), 0, 'width', 'pix'), @@ -33,16 +32,15 @@ def run(self, para=None): n_roi = len(roi_set) print("Loaded %i rois" % n_roi) - mask_ips = ImagePlus([np.zeros((para['height'], para['width']), dtype=np.int16)], self.para["name"]) - IPy.showips(mask_ips) + mask_ips = ImagePlus([np.zeros((para['height'], para['width']), dtype=np.int32)], self.para["name"]) for one_id in roi_set: + # none of the roi are send to the roi manager one_roi = shape2roi(Polygon(list(zip(roi_set[one_id]["x"], roi_set[one_id]["y"])))) - # RoiManager.add(one_id, one_roi) one_roi.fill(mask_ips.img, color=int(one_id)) - # seems not working here... - mask_ips.update = True - # RoiManager.rois = {} + + IPy.showips(mask_ips) + mask_ips.update = "pix" if __name__ == '__main__': diff --git a/requirements.txt b/requirements.txt index eb307278..ab66123f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,11 @@ -scikit-image -shapely -wxpython -numba -pandas -xlrd -xlwt -openpyxl -pydicom \ No newline at end of file +scikit-image +shapely +wxpython +numba +pandas +xlrd +xlwt +openpyxl +pydicom +read_roi +numpy-stl \ No newline at end of file From 7fb1382e252ff8a733cd5196f9835d7f959cdd1e Mon Sep 17 00:00:00 2001 From: Tong LI Date: Sat, 22 Dec 2018 12:09:16 +0100 Subject: [PATCH 068/343] Update requirements.txt --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index ab66123f..11c7d730 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,5 +7,5 @@ xlrd xlwt openpyxl pydicom -read_roi -numpy-stl \ No newline at end of file +read-roi +numpy-stl From 1951cd67f17c25e0632f890c11d3e680b8178350 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 22 Dec 2018 22:15:28 +0800 Subject: [PATCH 069/343] roi --- imagepy/menus/File/Import/roi_plg.py | 42 ++++++++-------------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/imagepy/menus/File/Import/roi_plg.py b/imagepy/menus/File/Import/roi_plg.py index 7663b9fe..f7083ce6 100644 --- a/imagepy/menus/File/Import/roi_plg.py +++ b/imagepy/menus/File/Import/roi_plg.py @@ -3,47 +3,29 @@ Created on 12/21/2018 @author: BioinfoTongLI """ -from imagepy.core import ImagePlus +import numpy as np +import read_roi from imagepy.core.engine import Free from imagepy import IPy -from imagepy.core.roi import roiio -from shapely.geometry import Polygon -import wx -from imagepy.core.roi.convert import shape2roi -import numpy as np - +from skimage.draw import polygon class Plugin(Free): """load_ij_roi: use read_roi and th pass to shapely objects""" title = 'Import Rois from IJ' - para = {'path': '', 'name': 'Undefined', 'width': 2560, 'height': 2160} + + para = {'path': '', 'name': 'Undefined', 'width': 512, 'height': 512} + view = [(str, 'name', 'name', ''), (int, 'width', (1, 3000), 0, 'width', 'pix'), (int, 'height', (1, 3000), 0, 'height', 'pix')] def load(self): filt = '|'.join(['%s files (*.%s)|*.%s' % (i.upper(), i, i) for i in ["zip"]]) - rst = IPy.getpath(self.title, filt, 'open', self.para) - if not rst: return rst - return True + return IPy.getpath(self.title, filt, 'open', self.para) def run(self, para=None): - roi_set = roiio.read_roi_zip(self.para["path"]) - n_roi = len(roi_set) - print("Loaded %i rois" % n_roi) - - mask_ips = ImagePlus([np.zeros((para['height'], para['width']), dtype=np.int32)], self.para["name"]) - - for one_id in roi_set: - # none of the roi are send to the roi manager - one_roi = shape2roi(Polygon(list(zip(roi_set[one_id]["x"], roi_set[one_id]["y"])))) - one_roi.fill(mask_ips.img, color=int(one_id)) - - IPy.showips(mask_ips) - mask_ips.update = "pix" - - -if __name__ == '__main__': - print(Plugin.title) - app = wx.App(False) - Plugin().run() \ No newline at end of file + ls = read_roi.read_roi_zip(para['path']) + img = np.zeros((para['height'], para['width']), dtype=np.int32) + for i in ls: + img[polygon(ls[i]['y'], ls[i]['x'], img.shape)] = int(i) + IPy.show_img([img], para['name']) From e76f1b5214e7e1b17eaf3b62b6ddbe2776b818fc Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 22 Dec 2018 22:27:09 +0800 Subject: [PATCH 070/343] del read_roi from roiio --- imagepy/core/roi/roiio.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/imagepy/core/roi/roiio.py b/imagepy/core/roi/roiio.py index ee8ddc28..67a1738c 100644 --- a/imagepy/core/roi/roiio.py +++ b/imagepy/core/roi/roiio.py @@ -1,7 +1,6 @@ from .convert import roi2shape, shape2roi import pickle from shapely import wkt -import read_roi def roi2wkt(roi): return wkt.dumps(roi2shape(roi)) def wkt2roi(con): return shape2roi(wkt.loads(con)) @@ -27,7 +26,3 @@ def savewkt(roi, path): f = open(path, 'w') f.write(roi2wkt(roi)) f.close() - - -def read_roi_zip(path): - return read_roi.read_roi_zip(path) From 525c0f688733361061dc3e1324847e21f8154c62 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 29 Dec 2018 12:51:09 +0800 Subject: [PATCH 071/343] watermark --- imagepy/ui/canvasframe.py | 10 ++++++---- imagepy/ui/mainframe.py | 8 +++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/imagepy/ui/canvasframe.py b/imagepy/ui/canvasframe.py index 53ce6725..c6fd71b9 100644 --- a/imagepy/ui/canvasframe.py +++ b/imagepy/ui/canvasframe.py @@ -145,10 +145,10 @@ def on_close(self, event): event.Skip() -class MyArtProvider(aui.AuiDefaultDockArt): - def __init__(self): +class ImgArtProvider(aui.AuiDefaultDockArt): + def __init__(self, img): aui.AuiDefaultDockArt.__init__(self) - self.bitmap = wx.Bitmap('data/watermark.png', wx.BITMAP_TYPE_PNG) + self.bitmap = wx.Bitmap(img, wx.BITMAP_TYPE_PNG) def DrawBackground(self, dc, window, orient, rect): aui.AuiDefaultDockArt.DrawBackground(self, dc, window, orient, rect) @@ -169,7 +169,9 @@ def __init__(self, parent): self.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_pagevalid) self.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close) self.SetArtProvider(aui.AuiSimpleTabArt()) - self.GetAuiManager().SetArtProvider(MyArtProvider()) + + def set_background(self, img): + self.GetAuiManager().SetArtProvider(ImgArtProvider(img)) def add_page(self, panel, ips): self.AddPage(panel, ips.title, True, wx.NullBitmap ) diff --git a/imagepy/ui/mainframe.py b/imagepy/ui/mainframe.py index 13ad36a9..df6b96ff 100644 --- a/imagepy/ui/mainframe.py +++ b/imagepy/ui/mainframe.py @@ -115,7 +115,9 @@ def load_aui(self): self.auimgr.AddPane( self.tablenbwrap, aui.AuiPaneInfo() .Bottom() .CaptionVisible( True ).PinButton( True ).Dock().Hide() .MaximizeButton( True ).Resizable().FloatingSize((800, 600)).BestSize(( 120,120 )). Caption('Tables') . BottomDockable( True ).TopDockable( False ).LeftDockable( True ).RightDockable( True ) ) - + img = ConfigManager.get('watermark') or '' + if not os.path.exists(img): img = 'data/watermark.png' + self.set_background(img) #self.auimgr.SetArtProvider(MyArtProvider()) #self.canvasnb.Bind( aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_pagevalid) @@ -188,6 +190,10 @@ def set_progress(self, value): def set_color(self, value): self.line_color.SetBackgroundColour(value) + def set_background(self, img): + if IPy.uimode()!='ipy': return + self.canvasnb.set_background(img) + def on_close(self, event): ConfigManager.write() self.auimgr.UnInit() From 173a7f971544a0de5097dcbbcfd1ae844e9ee518 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 31 Dec 2018 01:26:43 +0800 Subject: [PATCH 072/343] shotcut editor --- imagepy/__init__.py | 4 +--- imagepy/core/engine/widget.py | 9 +++++---- imagepy/data/shotcut.cfg | 2 +- imagepy/menus/File/exit_plg.py | 3 ++- imagepy/menus/Plugins/Manager/shotcut_wgt.py | 9 ++++++--- imagepy/menus/Window/windowskiller_plg.py | 7 ++++++- imagepy/ui/canvasframe.py | 11 ++++++++++- imagepy/ui/mainframe.py | 7 ++++--- 8 files changed, 35 insertions(+), 17 deletions(-) diff --git a/imagepy/__init__.py b/imagepy/__init__.py index 052bd684..0e2141b9 100644 --- a/imagepy/__init__.py +++ b/imagepy/__init__.py @@ -18,7 +18,6 @@ def show(ui = True): app = wx.App(False) - bitmap = wx.Bitmap('data/logolong.png', wx.BITMAP_TYPE_PNG) shadow = wx.Colour(255,255,255) # SplashScreen(bitmap, wx.adv.SPLASH_CENTRE_ON_SCREEN | wx.adv.SPLASH_TIMEOUT, 3000, None, -1) @@ -29,8 +28,7 @@ def show(ui = True): AS.AS_SHADOW_BITMAP, shadowcolour=shadow) - mainFrame = ImagePy(None) - mainFrame.Show() + ImagePy(None).Show() app.MainLoop() \ No newline at end of file diff --git a/imagepy/core/engine/widget.py b/imagepy/core/engine/widget.py index 1ee3bafc..707f5dfe 100644 --- a/imagepy/core/engine/widget.py +++ b/imagepy/core/engine/widget.py @@ -15,11 +15,12 @@ def __init__(self, panel): def __call__(self):return self def start(self): - if not WidgetsManager.getref(self.title) is None: return + #if not WidgetsManager.getref(self.title) is None: return pan = self.pan(IPy.curapp) - WidgetsManager.addref(pan) - IPy.curapp.auimgr.AddPane(pan, aui.AuiPaneInfo(). DestroyOnClose(True). Left(). Caption(self.title) .PinButton( True ) - .Float().Resizable().FloatingSize( wx.DefaultSize ).Dockable(IPy.uimode()=='ipy').Layer( 15 ) ) + #WidgetsManager.addref(pan) + IPy.curapp.auimgr.AddPane(pan, aui.AuiPaneInfo().Caption(self.title).Left().Layer( 15 ).PinButton( True ) + .Float().Resizable().FloatingSize( wx.DefaultSize ).Dockable(IPy.uimode()=='ipy').DestroyOnClose()) + IPy.curapp.Layout() IPy.curapp.auimgr.Update() ''' diff --git a/imagepy/data/shotcut.cfg b/imagepy/data/shotcut.cfg index 45404d18..f82fadba 100644 --- a/imagepy/data/shotcut.cfg +++ b/imagepy/data/shotcut.cfg @@ -1 +1 @@ -{'Topic': 'F1', 'Open': 'Ctrl-O', 'Select All': 'Ctrl-A', 'Save': 'Ctrl-S', 'New': 'Ctrl-N', 'Copy': 'Ctrl-C', 'Paste': 'Ctrl-V', 'Undo': 'Ctrl-Z', '6_Shades': '', '16_Colors': '', 'Show ConvexHull': '', 'Exit': 'Alt-X', '2DSurface Demo': ''} \ No newline at end of file +{'Topic': 'F1', 'Open': 'Ctrl-O', 'Select All': 'Ctrl-A', 'Save': 'Ctrl-S', 'New': 'Ctrl-N', 'Copy': 'Ctrl-C', 'Paste': 'Ctrl-V', 'Undo': 'Ctrl-Z', '6_Shades': '', '16_Colors': '', 'Show ConvexHull': '', 'Exit': 'Alt-X', '2DSurface Demo': '', '16-bit uint': '', '8-bit': 'Ctrl-8'} \ No newline at end of file diff --git a/imagepy/menus/File/exit_plg.py b/imagepy/menus/File/exit_plg.py index 6ee9535e..e8f78c11 100644 --- a/imagepy/menus/File/exit_plg.py +++ b/imagepy/menus/File/exit_plg.py @@ -8,6 +8,7 @@ class Plugin(Free): title = 'Exit' + asyn = False def run(self, para = None): - IPy.curapp.on_close(None) \ No newline at end of file + IPy.curapp.Close() \ No newline at end of file diff --git a/imagepy/menus/Plugins/Manager/shotcut_wgt.py b/imagepy/menus/Plugins/Manager/shotcut_wgt.py index 8c1a2e11..9e6f87cd 100644 --- a/imagepy/menus/Plugins/Manager/shotcut_wgt.py +++ b/imagepy/menus/Plugins/Manager/shotcut_wgt.py @@ -30,10 +30,12 @@ def refresh(self): class Plugin( wx.Panel ): title = 'Shotcut Editor' single = None + def __init__( self, parent): wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + bSizer1 = wx.BoxSizer( wx.VERTICAL ) bSizer2 = wx.BoxSizer( wx.HORIZONTAL ) self.m_staticText1 = wx.StaticText( self, wx.ID_ANY, "Search:", @@ -71,6 +73,7 @@ def on_search( self, event ): wd = self.txt_search.GetValue() self.buf = [i for i in self.plgs if wd.lower() in i[0].lower()] self.lst_plgs.set_data(self.buf) + self.Refresh() def ist(self, cont, txt): sep = cont.split('-') @@ -105,6 +108,6 @@ def on_run(self, event): ShotcutManager.set(title, txt) #PluginsManager.plgs[self.buf[event.GetIndex()][0]]().start() - def __del__(self): - print('hahaha') - ShotcutManager.write() \ No newline at end of file + def close(self): + print('close') + ShotcutManager.write() diff --git a/imagepy/menus/Window/windowskiller_plg.py b/imagepy/menus/Window/windowskiller_plg.py index 9764afa7..79f92091 100644 --- a/imagepy/menus/Window/windowskiller_plg.py +++ b/imagepy/menus/Window/windowskiller_plg.py @@ -11,6 +11,7 @@ class ImageKiller(Free): """ImageKiller: derived from imagepy.core.engine.Free""" title = 'Kill Image' + asyn = False def load(self): ImageKiller.para = {'name':'All'} @@ -23,11 +24,14 @@ def run(self, para = None): if para['name'] == 'All': for i in ImageManager.get_titles(): WindowsManager.get(i).close() - else: WindowsManager.get(para['name']).close() + else: + print(WindowsManager.get(para['name'])) + WindowsManager.get(para['name']).close() class TextKiller(Free): """TextKiller: derived from imagepy.core.engine.Free""" title = 'Kill TextLog' + asyn = False def load(self): TextKiller.para = {'name':'All'} @@ -45,6 +49,7 @@ def run(self, para = None): class TableKiller(Free): """TableKiller: derived from imagepy.core.engine.Free""" title = 'Kill TableLog' + asyn = False def load(self): self.para = {'name':'All'} diff --git a/imagepy/ui/canvasframe.py b/imagepy/ui/canvasframe.py index c6fd71b9..82b0459f 100644 --- a/imagepy/ui/canvasframe.py +++ b/imagepy/ui/canvasframe.py @@ -105,7 +105,15 @@ def on_scroll(self, event): self.canvas.on_idle(None) def close(self): - self.GetParent().Close() + parent = self.GetParent() + if IPy.uimode()=='ij': + parent.Close() + if IPy.uimode()=='ipy': + idx = parent.GetPageIndex(self) + parent.DeletePage(idx) + self.set_handler() + self.canvas.set_handler() + WindowsManager.remove(self) def __del__(self):pass @@ -186,6 +194,7 @@ def on_pagevalid(self, event): ImageManager.add(event.GetEventObject().GetPage(event.GetSelection()).ips) def on_close(self, event): + print('page close') event.GetEventObject().GetPage(event.GetSelection()).set_handler() event.GetEventObject().GetPage(event.GetSelection()).canvas.set_handler() WindowsManager.remove(event.GetEventObject().GetPage(event.GetSelection())) diff --git a/imagepy/ui/mainframe.py b/imagepy/ui/mainframe.py index df6b96ff..c6e50727 100644 --- a/imagepy/ui/mainframe.py +++ b/imagepy/ui/mainframe.py @@ -137,11 +137,11 @@ def load_dev(self): .PaneBorder( False ).Resizable().FloatingSize( wx.DefaultSize ) ) def on_pan_close(self, event): - return if event.GetPane().window in [self.toolbar, self.widgets]: event.Veto() - event.GetPane().Show(False) - self.auimgr.Update() + if hasattr(event.GetPane().window, 'close'): + event.GetPane().window.close() + def reload_plugins(self, report=False, menus=True, tools=False, widgets=False): print(menus, tools, widgets) @@ -195,6 +195,7 @@ def set_background(self, img): self.canvasnb.set_background(img) def on_close(self, event): + print('close') ConfigManager.write() self.auimgr.UnInit() del self.auimgr From 97fbf6d35d1df92fdf6b269d8554adab78772f14 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 31 Dec 2018 12:01:05 +0800 Subject: [PATCH 073/343] mat io --- imagepy/menus/File/MAT/__init__.py | 0 imagepy/menus/File/MAT/mat_plgs.py | 36 ++++++++++++++++++++++++++++++ imagepy/menus/File/__init__.py | 2 +- 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 imagepy/menus/File/MAT/__init__.py create mode 100644 imagepy/menus/File/MAT/mat_plgs.py diff --git a/imagepy/menus/File/MAT/__init__.py b/imagepy/menus/File/MAT/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/menus/File/MAT/mat_plgs.py b/imagepy/menus/File/MAT/mat_plgs.py new file mode 100644 index 00000000..378a5298 --- /dev/null +++ b/imagepy/menus/File/MAT/mat_plgs.py @@ -0,0 +1,36 @@ +from imagepy.core.util import fileio +from scipy.io import savemat, loadmat +from imagepy.core.manager import ReaderManager, WriterManager +from imagepy import IPy +import os + +ReaderManager.add('mat', lambda path: loadmat(path)['img']) +WriterManager.add('mat', lambda path, img: savemat(path, {'img':img})) + +class OpenFile(fileio.Reader): + title = 'Mat Open' + filt = ['Mat'] + +class SaveFile(fileio.Writer): + title = 'Mat Save' + filt = ['mat'] + +class Open3D(fileio.Reader): + title = 'Mat 3D Open' + filt = ['Mat'] + + def run(self, para = None): + imgs = loadmat(para['path'])['img'] + fp, fn = os.path.split(para['path']) + fn, fe = os.path.splitext(fn) + IPy.show_img(imgs, fn) + +class Save3D(fileio.Writer): + title = 'Mat 3D Save' + filt = ['mat'] + note = ['8-bit', 'rgb', 'stack'] + + def run(self, ips, imgs, para = None): + savemat(para['path'], {'img':imgs}) + +plgs = [OpenFile, SaveFile, '-', Open3D, Save3D] \ No newline at end of file diff --git a/imagepy/menus/File/__init__.py b/imagepy/menus/File/__init__.py index ffd194e8..4cbca4ca 100644 --- a/imagepy/menus/File/__init__.py +++ b/imagepy/menus/File/__init__.py @@ -1,3 +1,3 @@ ### TODO: Fixme! In this directory, many path should be corrected?! catlog = ['new_plg', '-', 'open_plg', 'save_plg', '-', 'Open Recent', 'Samples Local', 'Samples Online', - 'Samples ImageJ', '-', 'Import', 'Export', '-', 'BMP', 'JPG', 'PNG', 'TIF', 'GIF','DICOM','DAT', '-', 'exit_plg'] \ No newline at end of file + 'Samples ImageJ', '-', 'Import', 'Export', '-', 'BMP', 'JPG', 'PNG', 'TIF', 'GIF','DICOM','DAT', 'MAT', '-', 'exit_plg'] \ No newline at end of file From 649dac35aed51447f5dabb8a59eafe8fa725fede Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 2 Jan 2019 22:34:27 +0800 Subject: [PATCH 074/343] jpg imageio --- imagepy/menus/File/JPG/jpg_plgs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imagepy/menus/File/JPG/jpg_plgs.py b/imagepy/menus/File/JPG/jpg_plgs.py index 336f4ae9..6a23ed88 100644 --- a/imagepy/menus/File/JPG/jpg_plgs.py +++ b/imagepy/menus/File/JPG/jpg_plgs.py @@ -1,11 +1,12 @@ from imagepy.core.util import fileio -from skimage.io import imread, imsave +from imageio import imread, imsave from imagepy.core.manager import ReaderManager, WriterManager ReaderManager.add('jpg', imread) WriterManager.add('jpg', imsave) ReaderManager.add('jpeg', imread) WriterManager.add('jpeg', imsave) + class OpenFile(fileio.Reader): title = 'JPG Open' filt = ['JPG','JPEG'] From e5f22c1ef31b5f560111c589792b396581fd0d80 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 7 Jan 2019 16:23:56 +0800 Subject: [PATCH 075/343] fix binary watershed bug --- imagepy/menus/Process/Binary/distance_plgs.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/imagepy/menus/Process/Binary/distance_plgs.py b/imagepy/menus/Process/Binary/distance_plgs.py index 71e761f2..b8542bef 100644 --- a/imagepy/menus/Process/Binary/distance_plgs.py +++ b/imagepy/menus/Process/Binary/distance_plgs.py @@ -9,7 +9,7 @@ from imagepy.ipyalg.graph import skel2d from imagepy.core.engine import Filter from imagepy.ipyalg import find_maximum, watershed -#from skimage.morphology import watershed +from skimage.filters import apply_hysteresis_threshold import scipy.ndimage as ndimg class Skeleton(Filter): @@ -54,14 +54,15 @@ class Watershed(Filter): ## TODO: Fixme! def run(self, ips, snap, img, para = None): - img[:] = snap + img[:] = snap>0 dist = -ndimg.distance_transform_edt(snap) pts = find_maximum(dist, para['tor'], False) buf = np.zeros(ips.size, dtype=np.uint16) - buf[pts[:,0], pts[:,1]] = 1 + buf[pts[:,0], pts[:,1]] = img[pts[:,0], pts[:,1]] = 2 markers, n = ndimg.label(buf, np.ones((3,3))) line = watershed(dist, markers, line=True, conn=para['con']+1) - img[line==0] = 0 + msk = apply_hysteresis_threshold(img, 0, 1) + img[:] = snap * ~((line==0) & msk) class Voronoi(Filter): """Mark class plugin with events callback functions""" From c6b05644d472069fbf14264b2928b71f18feaa78 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Mon, 7 Jan 2019 11:49:10 -0600 Subject: [PATCH 076/343] Add environment.yml to ease conda usage Using conda, launching ImagePy can now be done like this: conda env create conda activate imagepy python -m imagepy --- environment.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 environment.yml diff --git a/environment.yml b/environment.yml new file mode 100644 index 00000000..d7b45252 --- /dev/null +++ b/environment.yml @@ -0,0 +1,19 @@ +name: imagepy +channels: + - conda-forge +dependencies: + - numba + - numpy-stl + - opencv + - openpyxl + - pandas + - pydicom + - pypubsub + - read-roi + - scikit-image + - shapely + - wxpython + - xlrd + - xlwt + - pip: + - ModernGL From a9021cefbce0294d1c5caf2005478481ed3af715 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 8 Jan 2019 11:22:08 +0800 Subject: [PATCH 077/343] remove surf demo --- environment.yml | 3 +- imagepy/menus/Plugins/Surf/Surf Demo.mc | 3 - imagepy/menus/Plugins/Surf/__init__.py | 1 - imagepy/menus/Plugins/Surf/matcher.py | 85 ------------ imagepy/menus/Plugins/Surf/surf_plg.py | 177 ------------------------ 5 files changed, 1 insertion(+), 268 deletions(-) delete mode 100644 imagepy/menus/Plugins/Surf/Surf Demo.mc delete mode 100644 imagepy/menus/Plugins/Surf/__init__.py delete mode 100644 imagepy/menus/Plugins/Surf/matcher.py delete mode 100644 imagepy/menus/Plugins/Surf/surf_plg.py diff --git a/environment.yml b/environment.yml index d7b45252..55694505 100644 --- a/environment.yml +++ b/environment.yml @@ -4,7 +4,6 @@ channels: dependencies: - numba - numpy-stl - - opencv - openpyxl - pandas - pydicom @@ -16,4 +15,4 @@ dependencies: - xlrd - xlwt - pip: - - ModernGL + - moderngl diff --git a/imagepy/menus/Plugins/Surf/Surf Demo.mc b/imagepy/menus/Plugins/Surf/Surf Demo.mc deleted file mode 100644 index 51869842..00000000 --- a/imagepy/menus/Plugins/Surf/Surf Demo.mc +++ /dev/null @@ -1,3 +0,0 @@ -Open Url>{'url': u'http://data.imagepy.org/testdata/box.png'} -Open Url>{'url': u'http://data.imagepy.org/testdata/box_in_scene.png'} -Surf Matcher>{'int': 4, 'upright': False, 'img2': u'box_in_scene', 'img1': u'box', 'std': 1, 'style': 'Blue/Yellow', 'log': True, 'thr': 2000, 'ext': False, 'trans': 'Homo', 'oct': 3} diff --git a/imagepy/menus/Plugins/Surf/__init__.py b/imagepy/menus/Plugins/Surf/__init__.py deleted file mode 100644 index 39f3f347..00000000 --- a/imagepy/menus/Plugins/Surf/__init__.py +++ /dev/null @@ -1 +0,0 @@ -catlog = ['surf_plg', '-', 'Surf Demo'] \ No newline at end of file diff --git a/imagepy/menus/Plugins/Surf/matcher.py b/imagepy/menus/Plugins/Surf/matcher.py deleted file mode 100644 index 31d2fc98..00000000 --- a/imagepy/menus/Plugins/Surf/matcher.py +++ /dev/null @@ -1,85 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Mon Jan 9 03:48:49 2017 - -@author: yxl -""" - -import cv2 -import numpy as np -from numpy.linalg import norm - -class Matcher: - def __init__(self, dim , std): - self.dim, self.std = dim, std - self.V = np.mat(np.zeros((self.dim,1))) - self.Dk = np.mat(np.diag(np.ones(self.dim)*1e6)) - - def match(self, desc1, desc2): - matcher = cv2.BFMatcher(cv2.NORM_L2) - pair = matcher.knnMatch(desc1, trainDescriptors = desc2, k = 1) - lt = [(i[0].distance, i[0].queryIdx, i[0].trainIdx) for i in pair] - return np.array(sorted(lt))[:,1:].astype(np.int16) - - def getT(self, v1, v2): - (x1, y1), (x2, y2) = v1, v2 - if self.dim==6: - return np.mat([[v1[0],v1[1],1,0,0,0], - [0,0,0,v1[0],v1[1],1]]) - if self.dim==8: - return np.mat([[x1, y1, 1, 0, 0, 0, -x1*x2, -y1*x2], - [0, 0, 0, x1, y1, 1, -x1*y2, -y1*y2]]) - - def test(self, v1, v2): - T = self.getT(v1.A1,v2.A1) - goal = T * self.V - o = goal.reshape((1,2)) - d = norm(v2 - o) - dv = (v2 - o)/d - D = T * self.Dk * T.T - s = np.sqrt(dv * D * dv.T + self.std ** 2) - return 3 * s > d - - def accept(self, v1, v2): - L = v2 - Dl = np.mat(np.diag(np.ones(2)))*self.std**2 - T = self.getT(v1.A1,v2.A1) - CX = (self.Dk.I + T.T * Dl.I * T).I - CL = CX * T .T* Dl.I - CV = np.mat(np.diag(np.ones(self.dim))) - CX * T.T * Dl.I * T - self.V = CL * L + CV * self.V - self.Dk = CV * self.Dk * CV.T + CL * Dl * CL.T - - def normalrize(self, pts): - o = pts.mean(axis=0) - l = norm(pts-o, axis=1).mean() - pts[:] = (pts-o)/l - - def filter(self, kpt1, feat1, kpt2, feat2): - kpt1 = np.array([(k.pt[0],k.pt[1]) for k in kpt1]) - kpt2 = np.array([(k.pt[0],k.pt[1]) for k in kpt2]) - self.normalrize(kpt1), self.normalrize(kpt2) - idx = self.match(feat1, feat2) - if self.dim == 0: - return idx, np.ones(len(idx), dtype=np.bool), 1 - mask = [] - for i1, i2 in idx: - v1 = np.mat(kpt1[i1]) - v2 = np.mat(kpt2[i2]) - if self.test(v1, v2): - self.accept(v1.T,v2.T) - mask.append(True) - else: mask.append(False) - mask = np.array(mask) - #print mask - return idx, mask, self.V - - def getTrans(self): - result = np.eye(3) - result[:2] = self.V.reshape((2,3)) - return result - - def checkV(self): - trans = self.getTrans()[:2,:2] - axis = norm(trans,axis=0) - return norm(axis-1)< 0.5 \ No newline at end of file diff --git a/imagepy/menus/Plugins/Surf/surf_plg.py b/imagepy/menus/Plugins/Surf/surf_plg.py deleted file mode 100644 index 5582a042..00000000 --- a/imagepy/menus/Plugins/Surf/surf_plg.py +++ /dev/null @@ -1,177 +0,0 @@ -import cv2, wx -from imagepy.core.engine import Filter, Simple, Tool -from imagepy.core.manager import ImageManager -from .matcher import Matcher -import numpy as np -from imagepy import IPy - -CVSURF = cv2.xfeatures2d.SURF_create if cv2.__version__[0] =="3" else cv2.SURF - -class FeatMark: - def __init__(self, feats): - self.feats = feats - - def draw(self, dc, f, **key): - for i in self.feats: - dc.DrawCircle(f(i.pt), 3) - -class Surf(Filter): - title = 'Surf Detect' - note = ['all', 'not-slice'] - - para = {'upright':False, 'oct':3, 'int':4, 'thr':1000, 'ext':False} - view = [(int, 'oct', (0,5), 0, 'octaves', ''), - (int, 'int', (0,5), 0, 'intervals', ''), - (int, 'thr', (500,2000), 0, 'threshold', '1-100'), - (bool, 'ext', 'extended'), - (bool, 'upright', 'upright')] - - def run(self, ips, snap, img, para): - detector = CVSURF(hessianThreshold=para['thr'], nOctaves=para['oct'], - nOctaveLayers=para['int'], upright=para['upright'],extended=para['ext']) - kps = detector.detect(img) - ips.surf_keypoint = kps - ips.mark = FeatMark(kps) - IPy.write("Detect completed, {} points found!".format(len(kps)), 'Surf') - -class Pick(Tool): - title = 'Key Point Pick Tool' - def __init__(self, pts1, pts2, pair, msk, ips1, ips2, host, style): - self.pts1, self.pts2 = pts1, pts2 - self.ips1, self.ips2 = ips1, ips2 - self.pair, self.msk = pair, msk - self.cur, self.host = -1, host - self.pts = self.pts1 if host else self.pts2 - self.style = style - - def nearest(self, x, y): - mind, mini = 1000, -1 - for i1, i2 in self.pair: - i = i1 if self.host else i2 - d = np.sqrt((x-self.pts[i].pt[0])**2+(y-self.pts[i].pt[1])**2) - if d Date: Tue, 8 Jan 2019 12:36:12 +0800 Subject: [PATCH 078/343] add markdown dependence --- environment.yml | 1 + setup.py | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/environment.yml b/environment.yml index 55694505..5b762132 100644 --- a/environment.yml +++ b/environment.yml @@ -14,5 +14,6 @@ dependencies: - wxpython - xlrd - xlwt + - markdown - pip: - moderngl diff --git a/setup.py b/setup.py index a25a69fb..c1c4521b 100644 --- a/setup.py +++ b/setup.py @@ -27,6 +27,9 @@ def get_data_files(): 'scikit-image', 'shapely', 'wxpython-installer', + 'read_roi', + 'numpy-stl', + 'pydicom' 'pandas', 'xlrd', 'xlwt', @@ -34,4 +37,4 @@ def get_data_files(): 'markdown', 'numba' ], - ) + ) \ No newline at end of file From 143f5df94538686d14a683db83c4322c887d15d7 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 8 Jan 2019 23:17:04 +0800 Subject: [PATCH 079/343] forum.image.sc --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ee3caa09..06bde9ef 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,13 @@ Our long-term goal of this project is to be used as ImageJ + SPSS (although not ## Citation: [ImagePy: an open-source, Python-based and platform-independent software package for bioimage analysis](https://academic.oup.com/bioinformatics/article/34/18/3238/4989871) +## Forum + +ImagePy is a community partner of forum.image.sc, Anything about the usage and development of ImagePy could be discussed in https://forum.image.sc. + # Installation -__OS support:windows, linux, mac, with python2.7 and python3.__4+ +__OS support:windows, linux, mac, with python3.4__ 1. ImagePy is a ui framework based on wxpython, which can not be installed with pip on Linux. You need download [the whl according to your From 9ec20ba55cfb7699095504a9830b0cf9270da854 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 9 Jan 2019 11:49:51 +0800 Subject: [PATCH 080/343] fix setup bug --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c1c4521b..e511acbf 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ def get_data_files(): 'wxpython-installer', 'read_roi', 'numpy-stl', - 'pydicom' + 'pydicom', 'pandas', 'xlrd', 'xlwt', From fe149e4f9dfe534b2bd0754b782ba05a6da603df Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 11 Jan 2019 21:59:13 +0800 Subject: [PATCH 081/343] fix bug of binary watershed 3d --- imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py | 12 ++++++++---- imagepy/menus/Process/Binary/distance_plgs.py | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py b/imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py index 5af5bcfc..72a0d9d9 100644 --- a/imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py +++ b/imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py @@ -3,6 +3,7 @@ from imagepy.core.engine import Simple from skimage.morphology import skeletonize_3d from imagepy.ipyalg import find_maximum, watershed +from skimage.filters import apply_hysteresis_threshold import numpy as np class Dilation(Simple): @@ -92,12 +93,15 @@ class Watershed(Simple): ## TODO: Fixme! def run(self, ips, imgs, para = None): + imgs[:] = imgs > 0 dist = -ndimg.distance_transform_edt(imgs) pts = find_maximum(dist, para['tor'], False) - buf = np.zeros(imgs.shape, dtype=np.uint16) - buf[pts[:,0], pts[:,1], pts[:,2]] = 1 - markers, n = ndimg.label(buf, np.ones((3,3, 3))) + buf = np.zeros(imgs.shape, dtype=np.uint32) + buf[pts[:,0], pts[:,1], pts[:,2]] = 2 + imgs[pts[:,0], pts[:,1], pts[:,2]] = 2 + markers, n = ndimg.label(buf, np.ones((3, 3, 3))) line = watershed(dist, markers, line=True, conn=para['con']+1) - imgs[line==0] = 0 + msk = apply_hysteresis_threshold(imgs, 0, 1) + imgs[:] = imgs>0; imgs *= 255; imgs *= ~((line==0) & msk) plgs = [Dilation, Erosion, Opening, Closing, '-', FillHole, Skeleton3D, '-', Distance3D, Watershed] \ No newline at end of file diff --git a/imagepy/menus/Process/Binary/distance_plgs.py b/imagepy/menus/Process/Binary/distance_plgs.py index b8542bef..c1c4ef2c 100644 --- a/imagepy/menus/Process/Binary/distance_plgs.py +++ b/imagepy/menus/Process/Binary/distance_plgs.py @@ -57,7 +57,7 @@ def run(self, ips, snap, img, para = None): img[:] = snap>0 dist = -ndimg.distance_transform_edt(snap) pts = find_maximum(dist, para['tor'], False) - buf = np.zeros(ips.size, dtype=np.uint16) + buf = np.zeros(ips.size, dtype=np.uint32) buf[pts[:,0], pts[:,1]] = img[pts[:,0], pts[:,1]] = 2 markers, n = ndimg.label(buf, np.ones((3,3))) line = watershed(dist, markers, line=True, conn=para['con']+1) From 0b817fc97259e6d2d449a29552ec0b2912ce654c Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 15 Jan 2019 23:19:09 +0800 Subject: [PATCH 082/343] table io's tag --- imagepy/core/util/tableio.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imagepy/core/util/tableio.py b/imagepy/core/util/tableio.py index 3f35ced2..2b67ea6f 100644 --- a/imagepy/core/util/tableio.py +++ b/imagepy/core/util/tableio.py @@ -16,7 +16,7 @@ def run(self, para = None): fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) - read = ReaderManager.get(fe[1:]) + read = ReaderManager.get(fe[1:], tag = 'tab') table = read(para['path']) ViewerManager.get(fe[1:])(table, fn) @@ -33,5 +33,5 @@ def show(self): def run(self, tps, data, snap, para = None): fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) - write = WriterManager.get(fe[1:], 'tab') + write = WriterManager.get(fe[1:], tag='tab') write(para['path'], data) \ No newline at end of file From c3fead1725752171a8e00f353a8469864ee168ad Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 20 Jan 2019 01:31:21 +0800 Subject: [PATCH 083/343] document manager --- imagepy/core/engine/filter.py | 5 +- imagepy/core/engine/free.py | 4 +- imagepy/core/engine/simple.py | 5 +- imagepy/core/engine/table.py | 5 +- imagepy/core/loader/loader.py | 15 +- imagepy/core/manager/__init__.py | 3 +- imagepy/core/manager/documentmanager.py | 11 + imagepy/core/wraper/imageplus.py | 4 - imagepy/doc/File/File.md | 0 imagepy/doc/File/New.md | 20 ++ imagepy/doc/root.md | 0 imagepy/doc/test.py | 10 + imagepy/ipyalg/hydrology/findmax.py | 2 - .../menus/Plugins/Coins Segment WorkFlow.wf | 19 -- .../Plugins/Contribute/Contribute Document.md | 0 .../Plugins/Contribute/Contribute Plugin.md | 0 .../Contribute/Contributions/Demo Repo.md | 18 ++ .../Plugins/Contribute/Contributions/IBook.md | 18 ++ .../Contribute/Contributions/OpenCV.md | 293 ++++++++++++++++++ .../Contribute/Contributions/Sea Ice.md | 241 ++++++++++++++ .../Contribute/Contributions/SimpleITK.md | 260 ++++++++++++++++ imagepy/menus/Plugins/Contribute/__init__.py | 1 + .../menus/Plugins/Contribute/pmanager_wgt.py | 159 ++++++++++ imagepy/menus/Plugins/Install/__init__.py | 2 +- .../menus/Plugins/Install/installplg_plgs.py | 8 +- imagepy/menus/Plugins/Install/manager_wgt.py | 1 + imagepy/menus/Plugins/Manager/plgtree_wgt.py | 13 +- imagepy/menus/Plugins/Manager/toltree_wgt.py | 13 +- imagepy/menus/Plugins/__init__.py | 2 +- imagepy/menus/Table/Table IO/tableio_plgs.py | 1 + imagepy/ui/mainframe.py | 9 +- imagepy/ui/mkdownwindow.py | 47 +-- imagepy/ui/toolsloader.py | 3 +- 33 files changed, 1108 insertions(+), 84 deletions(-) create mode 100644 imagepy/core/manager/documentmanager.py create mode 100644 imagepy/doc/File/File.md create mode 100644 imagepy/doc/File/New.md create mode 100644 imagepy/doc/root.md create mode 100644 imagepy/doc/test.py delete mode 100644 imagepy/menus/Plugins/Coins Segment WorkFlow.wf create mode 100644 imagepy/menus/Plugins/Contribute/Contribute Document.md create mode 100644 imagepy/menus/Plugins/Contribute/Contribute Plugin.md create mode 100644 imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md create mode 100644 imagepy/menus/Plugins/Contribute/Contributions/IBook.md create mode 100644 imagepy/menus/Plugins/Contribute/Contributions/OpenCV.md create mode 100644 imagepy/menus/Plugins/Contribute/Contributions/Sea Ice.md create mode 100644 imagepy/menus/Plugins/Contribute/Contributions/SimpleITK.md create mode 100644 imagepy/menus/Plugins/Contribute/__init__.py create mode 100644 imagepy/menus/Plugins/Contribute/pmanager_wgt.py create mode 100644 imagepy/menus/Plugins/Install/manager_wgt.py diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index 5e3c4ed4..fe714d76 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -11,7 +11,7 @@ from ... import IPy from ...ui.panelconfig import ParaDialog from ...core.manager import TextLogManager, ImageManager, \ -WindowsManager, TaskManager, WidgetsManager +WindowsManager, TaskManager, WidgetsManager, DocumentManager from time import time def process_channels(plg, ips, src, des, para): @@ -101,8 +101,7 @@ def show(self, temp=ParaDialog): self.dialog = temp(WindowsManager.get(), self.title) self.dialog.init_view(self.view, self.para, 'preview' in self.note, modal=self.modal) - doc = self.__doc__ or '### Sorry\nNo document yet!' - self.dialog.on_help = lambda : IPy.show_md(self.title, doc) + self.dialog.on_help = lambda : IPy.show_md(self.title, DocumentManager.get(self.title)) self.dialog.set_handle(lambda x:self.preview(self.ips, x)) if self.modal: return self.dialog.ShowModal() == wx.ID_OK self.dialog.on_ok = lambda : self.ok(self.ips) diff --git a/imagepy/core/engine/free.py b/imagepy/core/engine/free.py index bfa1b432..1756866f 100644 --- a/imagepy/core/engine/free.py +++ b/imagepy/core/engine/free.py @@ -7,7 +7,7 @@ from ... import IPy from ...ui.panelconfig import ParaDialog -from ...core.manager import WindowsManager, TextLogManager, TaskManager, WidgetsManager +from ...core.manager import WindowsManager, TextLogManager, TaskManager, WidgetsManager, DocumentManager from time import time class Free: @@ -38,7 +38,7 @@ def show(self): with ParaDialog(WindowsManager.get(), self.title) as dialog: dialog.init_view(self.view, self.para, False, True) doc = self.__doc__ or '### Sorry\nNo document yet!' - dialog.on_help = lambda : IPy.show_md(self.title, doc) + dialog.on_help = lambda : IPy.show_md(self.title, DocumentManager.get(self.title)) return dialog.ShowModal() == wx.ID_OK def start(self, para=None, callback=None): diff --git a/imagepy/core/engine/simple.py b/imagepy/core/engine/simple.py index daeea588..224857cd 100644 --- a/imagepy/core/engine/simple.py +++ b/imagepy/core/engine/simple.py @@ -8,7 +8,7 @@ from ... import IPy from ...ui.panelconfig import ParaDialog -from ..manager import TextLogManager, TaskManager, WidgetsManager +from ..manager import TextLogManager, TaskManager, WidgetsManager, DocumentManager from time import time class Simple: @@ -37,8 +37,7 @@ def show(self, temp=ParaDialog): if self.view==None:return True self.dialog = temp(IPy.get_window(), self.title) self.dialog.init_view(self.view, self.para, 'preview' in self.note, modal=self.modal) - doc = self.__doc__ or '### Sorry\nNo document yet!' - self.dialog.on_help = lambda : IPy.show_md(self.title, doc) + self.dialog.on_help = lambda : IPy.show_md(self.title, DocumentManager.get(self.title)) self.dialog.set_handle(lambda x:self.preview(self.ips, self.para)) if self.modal: return self.dialog.ShowModal() == wx.ID_OK self.dialog.on_ok = lambda : self.ok(self.ips) diff --git a/imagepy/core/engine/table.py b/imagepy/core/engine/table.py index 81a565b9..5c3cb701 100644 --- a/imagepy/core/engine/table.py +++ b/imagepy/core/engine/table.py @@ -8,7 +8,7 @@ from ... import IPy from ...ui.panelconfig import ParaDialog -from ..manager import TextLogManager, TaskManager, WidgetsManager +from ..manager import TextLogManager, TaskManager, WidgetsManager, DocumentManager from time import time class Table: @@ -39,8 +39,7 @@ def show(self): if self.view==None:return True self.dialog = ParaDialog(IPy.get_twindow(), self.title) self.dialog.init_view(self.view, self.para, 'preview' in self.note, modal=self.modal) - doc = self.__doc__ or '### Sorry\nNo document yet!' - self.dialog.on_help = lambda : IPy.show_md(self.title, doc) + self.dialog.on_help = lambda : IPy.show_md(self.title, DocumentManager.get(self.title)) self.dialog.set_handle(lambda x:self.preview(self.tps, self.para)) if self.modal: return self.dialog.ShowModal() == wx.ID_OK self.dialog.on_ok = lambda : self.ok(self.tps) diff --git a/imagepy/core/loader/loader.py b/imagepy/core/loader/loader.py index 9f2caa3b..f7531ab1 100644 --- a/imagepy/core/loader/loader.py +++ b/imagepy/core/loader/loader.py @@ -6,7 +6,7 @@ """ import os, sys from ..engine import Macros, MkDown, Widget, WorkFlow -from ..manager import ToolsManager, PluginsManager, WidgetsManager +from ..manager import ToolsManager, PluginsManager, WidgetsManager, DocumentManager from ... import IPy, root_dir from codecs import open @@ -232,6 +232,19 @@ def build_widgets(path, err=False): for i in err: IPy.write('>>> %-50s%-20s%s'%i) return (pg, subtree) +def build_document(path): + docs = [] + for dirpath,dirnames,filenames in os.walk(path): + for filename in filenames: + if filename[-3:] != '.md': continue + docs.append(os.path.join(dirpath, filename)) + f = open(docs[-1]) + cont = f.read() + f.close() + DocumentManager.add(filename[:-3], cont) + print(docs) + return docs + if __name__ == "__main__": print (os.getcwd()) os.chdir('../../') diff --git a/imagepy/core/manager/__init__.py b/imagepy/core/manager/__init__.py index a5f8cc33..323c2709 100644 --- a/imagepy/core/manager/__init__.py +++ b/imagepy/core/manager/__init__.py @@ -7,4 +7,5 @@ from .shotcutmanager import * from .taskmanager import * from .iomanager import * -from .languagemanager import * \ No newline at end of file +from .languagemanager import * +from .documentmanager import * \ No newline at end of file diff --git a/imagepy/core/manager/documentmanager.py b/imagepy/core/manager/documentmanager.py new file mode 100644 index 00000000..4e68f0e7 --- /dev/null +++ b/imagepy/core/manager/documentmanager.py @@ -0,0 +1,11 @@ +class DocumentManager: + docs = {} + + @classmethod + def add(cls, name, cont):cls.docs[name] = cont + + @classmethod + def get(cls, name=None): + if not name in cls.docs: + return '# Sorry, No Document yet.' + return cls.docs[name] \ No newline at end of file diff --git a/imagepy/core/wraper/imageplus.py b/imagepy/core/wraper/imageplus.py index 2badb44c..32bd7cd8 100644 --- a/imagepy/core/wraper/imageplus.py +++ b/imagepy/core/wraper/imageplus.py @@ -119,10 +119,6 @@ def histogram(self, arange=None, stack=False): def lookup(self, img=None): if img is None: img = self.img - #print(self.channels, self.dtype, img.dtype) - #if img.ndim==2 and img.dtype==np.uint8: - # return self.lut[img] - #el if img.ndim==2: k = 255.0/(max(1e-10, self.range[1]-self.range[0])) bf = np.clip(img, self.range[0], self.range[1]) diff --git a/imagepy/doc/File/File.md b/imagepy/doc/File/File.md new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/doc/File/New.md b/imagepy/doc/File/New.md new file mode 100644 index 00000000..f3995b72 --- /dev/null +++ b/imagepy/doc/File/New.md @@ -0,0 +1,20 @@ +# New + +**Description:** create a new Image + + + +## Parameter + +**name:** the new image's title + +**width:** image's width + +**height:** image's height + +**type:** image's type + +1. 8-bit: create a 8-bit one channel image +2. rgb: create a 24-bit three channel image + +**slice:** the z thickness \ No newline at end of file diff --git a/imagepy/doc/root.md b/imagepy/doc/root.md new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/doc/test.py b/imagepy/doc/test.py new file mode 100644 index 00000000..f32330d7 --- /dev/null +++ b/imagepy/doc/test.py @@ -0,0 +1,10 @@ +import os + +def get_docs(path): + docs = [] + for dirpath,dirnames,filenames in os.walk(path): + for filename in filenames: + docs.append(os.path.join(dirpath,filename)) + return [i for i in docs if i[-3:] == '.md'] + +print(get_docs('./')) diff --git a/imagepy/ipyalg/hydrology/findmax.py b/imagepy/ipyalg/hydrology/findmax.py index fefb307b..cc7c660a 100644 --- a/imagepy/ipyalg/hydrology/findmax.py +++ b/imagepy/ipyalg/hydrology/findmax.py @@ -21,8 +21,6 @@ def idx2rc(idx, acc): idx[i] -= rst[i,j]*acc[j] return rst - - @jit # fill a node (may be two or more points) def fill(img, msk, p, nbs, buf): diff --git a/imagepy/menus/Plugins/Coins Segment WorkFlow.wf b/imagepy/menus/Plugins/Coins Segment WorkFlow.wf deleted file mode 100644 index 745296f1..00000000 --- a/imagepy/menus/Plugins/Coins Segment WorkFlow.wf +++ /dev/null @@ -1,19 +0,0 @@ -硬币分割演示 -============= -## 打开图像 -1. coins -这是一个硬币图像,我们现在来对每个硬币进行计数及测量 -## 图像分割 -1. Up And Down Watershed -勾上preview,调整滑块,使每个硬币多少染上红色,背景多少染上绿色,选择 up area -## 掩膜修复 -1. Fill Holes -我们看到有些硬币上带了缝隙,我们对其进行修复 -2. Geometry Filter -过滤掉小块碎片,勾上预览,area输100,小块区域变暗,back color 输 0,清除碎片 -## 区域测量 -1. Geometry Analysis -选择需要的指标,我们这里勾上cov,计算区域的误差椭圆 -## 成果导出 -1. CSV Save -将分析结果导出,保存成excel文件 \ No newline at end of file diff --git a/imagepy/menus/Plugins/Contribute/Contribute Document.md b/imagepy/menus/Plugins/Contribute/Contribute Document.md new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/menus/Plugins/Contribute/Contribute Plugin.md b/imagepy/menus/Plugins/Contribute/Contribute Plugin.md new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md b/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md new file mode 100644 index 00000000..cfa6b46e --- /dev/null +++ b/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md @@ -0,0 +1,18 @@ +# Demo Repo + +**Path:** https://github.com/Image-Py/demoplugin + +**Version:** 0.1 + +**Author:** YXDragon + +**Email:** yxdragon@imagepy.org + +**Keyword:** demo, plugin + +**Description:** This is a demo plugin. + +you must fill the information upon, and you can not remove or insert line, you can write free below. + +## Document +here you can write any thing about your plugin. \ No newline at end of file diff --git a/imagepy/menus/Plugins/Contribute/Contributions/IBook.md b/imagepy/menus/Plugins/Contribute/Contributions/IBook.md new file mode 100644 index 00000000..e217be69 --- /dev/null +++ b/imagepy/menus/Plugins/Contribute/Contributions/IBook.md @@ -0,0 +1,18 @@ +# IBook + +**Path:** https://github.com/Image-Py/IBook + +**Version:** 0.1 + +**Author:** YXDragon + +**Email:** yxdragon@imagepy.org + +**Keyword:** book, tutorial + +**Description:** ImagePy's plugins to show some image processing method, which is friendly to beginner + +you must fill the information upon, and you can not remove or insert line, you can write free below. + +## Document +... \ No newline at end of file diff --git a/imagepy/menus/Plugins/Contribute/Contributions/OpenCV.md b/imagepy/menus/Plugins/Contribute/Contributions/OpenCV.md new file mode 100644 index 00000000..366fb4a9 --- /dev/null +++ b/imagepy/menus/Plugins/Contribute/Contributions/OpenCV.md @@ -0,0 +1,293 @@ +# opencv-plgs + +**Path:** https://github.com/Image-Py/opencv-plgs + +**Version:** 0.11 + +**Author:** YXDragon + +**Email:** yxdragon@imagepy.org + +**Keyword:** opencv + +**Description:** OpenCV plugin set for ImagePy + +you must fill the information upon, and you can not remove or insert line, you can write free below. + + +**Introduction:**opencv need not much introductions, It is a famous computer vision library. ImagePy is a interactive image processing framework which can wrap any numpy based library esaily. And supporting multi-channels, imagestack, lookuptable, roi, macros recorder...It is a Plugin system(just like ImageJ but more convenient). This project is a wrapper of opencv for ImagePy plugins! + +**Now It is just a start, I wrap little of opencv's algrism, aimed to introduct how to wrote ImagePy plugin, The Demo in this document is representative.** + +License +------- +I know many numpy based project has a BSD license, but, sorry, I use wxpython as ui framework, so, must be under LGPL. + +MainFrame +--------- +![mainframe](http://opencvplgs.imagepy.org/mainframe.png) + +It is ImagePy's MainFrame, like ImageJ. And ImagePy has contains many common function, such as open image, save image, do some filter, do a roi, draw with pencil... It requires wxpython as ui, Numpy as base structure, shapely to treat the roi, and scipy.ndimage to so dome common filter. But this project devotes to **do a wrapper for opencv**. + +Do a simple filter +------------------ +![simplefilter](http://opencvplgs.imagepy.org/laplacian.png) +```python +# -*- coding: utf-8 -* +import cv2 +from imagepy.core.engine import Filter + +class Plugin(Filter): + title = 'Laplacian' + note = ['all', 'auto_msk', 'auto_snap'] + + def run(self, ips, snap, img, para = None): + return cv2.Laplacian(img, -1) +``` +### These gradient operator is simplest filter with no parameter. +1. class name must be Plugin +2. title is necessary, be the plugin's id, show in menus. +3. set the note, which tells ImagePy what to do for you. +4. overwrite run method return the result + +Filter is one of engines, means need a image, then do some change on it, It has a run method in such type: +* **ips** is the wrapper of image with some other information (lookup table, roi...) +* **snap** is a snapshot of the image, if 'auto_snap' in note, ImagePy will copy the image to snap befor run. (for many filter method must be implemented in a buffer) +* **img** is the current image you are processing. +* **para** is the parameter you got interactive. (there is no here) +### note is very important + +* **all** means this plugin works for all type image. +* **auto_snap** means ImagePy do a snapshot befor processing, then you can use Undo. +* **auto_msk** means when there is a roi on the image, Plugin will only influnce the pixel in. +* more detail information please see [ImagePy's README](https://github.com/Image-Py/imagepy)! + + +![colorfilter](http://opencvplgs.imagepy.org/colorfilter.png) +You see, we didnot write code to treat the color image, but it works, and We can draw a ROI on the image, only the ROI area be changed! And we can undo the lasted operation. **Even if it is a imagestack, ImagePy will ask you if run every slice!!** + +Filter with parameter +--------------------- +![canny](http://opencvplgs.imagepy.org/canny.png) +```python +# -*- coding: utf-8 -* +import cv2 +from imagepy.core.engine import Filter + +class Plugin(Filter): + title = 'Canny' + note = ['all', 'auto_msk', 'auto_snap', 'preview'] + para = {'sigma':2, 'low':10, 'high':20} + view = [(float, (0,10), 1, 'sigma', 'sigma', 'pix'), + ('slide',(0,255), 0, 'low_threshold', 'low'), + ('slide',(0,255), 0, 'high_threshold', 'high')] + + def run(self, ips, snap, img, para = None): + l = int(para['sigma']*2.5)*2+1 + cv2.GaussianBlur(snap, (l, l), para['sigma'], dst=img) + return cv2.Canny(img, para['low'], para['high']) +``` +Many Filter need some parameter. Just like Canny. We just need do a little more. +1. **para** is a dict object, which contains the parameter you need. +2. **view** tell ImagePy how to interact when this plugin run, **(float, (0,10), 1, 'sigma', 'sigma', 'pix')** means it is a float between 0 and 10, title is sigma, corresponding to the sigma parameter with unit pix. +More detail information please see [ImagePy's README](https://github.com/Image-Py/imagepy)! + +**Add 'preview' in note, then when you adjust the parameter, ImagePy run this plugin immediately** + +![canny](http://opencvplgs.imagepy.org/threshold.png) +```python +# -*- coding: utf-8 -*- +from imagepy import IPy +import numpy as np, cv2 +from imagepy.core.engine import Filter + +class Plugin(Filter): + title = 'Adaptive Threshold' + note = ['8-bit', 'auto_msk', 'auto_snap', 'preview'] + para = {'max':255, 'med':'mean', 'size':9, 'offset':2, 'inv':False} + view = [(int, (0, 255), 0, 'maxvalue', 'max', ''), + (list, ['mean', 'gauss'], str, 'method', 'med', ''), + (int, (3, 31), 0, 'blocksize', 'size', 'pix'), + (int, (0, 50), 0, 'offset', 'offset', ''), + (bool, 'binary invert', 'inv')] + + #process + def run(self, ips, snap, img, para = None): + med = cv2.ADAPTIVE_THRESH_MEAN_C if para['med']=='mean' else cv2.ADAPTIVE_THRESH_GAUSSIAN_C + mtype = cv2.THRESH_BINARY_INV if para['inv'] else cv2.THRESH_BINARY + cv2.adaptiveThreshold(snap, para['max'], med, para['inv'], para['size'], para['offset'], dst=img) +``` +Adaptive Threshold demo shows more data type, (choice, bool) + +**some method has a output parameter dst, just give the img and need not return(That will save memory, In fact ImagePy will copy the return to image, then update view)** + +Watershed with interactive marker +--------------------------------- +![interactive watershed](http://opencvplgs.imagepy.org/watershed.png) +```python +# -*- coding: utf-8 -* +from imagepy.core.engine import Filter +import numpy as np, cv2 + +class Plugin(Filter): + title = 'Active Watershed' + note = ['rgb', 'req_roi', 'not_slice', 'auto_snap'] + + def run(self, ips, snap, img, para = None): + a, msk = cv2.connectedComponents(ips.get_msk().astype(np.uint8)) + msk = cv2.watershed(img, msk)==-1 + img //= 2 + img[msk] = 255 +``` +ImagePy support ROI, you can use tool to draw a roi(point, line, polygon...), And We can use ips.roi access it, and ips.get_msk(mode='in') to get the roi mask image. **mode can be 'in','out',or int means a sketch with specific width.** Then use the mask as marker to do a watershed, + +1. **req_roi** means this plugin need a roi, ImagePy will check for you, if ther is not, interrupt the plugin. +2. **not_slice** tells Imagepy need not to iterate slices if it is a stack, because this interactive is ok for specific image, there is no need to go through. + +**we can do watershed and get the result, then we can add some stroke where missed segment. then use Undo, and watershed again, we got a perfect result!** + +Interactive Grabcut +------------------- +this demo use build a Tool, and call a plugin in the tool's event. +![grabcut](http://opencvplgs.imagepy.org/grabcut.png) + +### Mark +mark is a overlay drawn on a image, It has draw method with parameter: +1. **dc** a wx dc contex. +2. **f** project from image coordinate to canvas coordinate. +3. **key** other parameter such as slice number. +```python +# -*- coding: utf-8 -* +from imagepy.core.engine import Tool, Filter +import numpy as np, wx, cv2 + +class Mark(): + def __init__(self): + self.foreline, self.backline = [], [] + + def draw(self, dc, f, **key): + dc.SetPen(wx.Pen((255,0,0), width=2, style=wx.SOLID)) + for line in self.foreline: dc.DrawLines([f(*i) for i in line]) + dc.SetPen(wx.Pen((0,0,255), width=2, style=wx.SOLID)) + for line in self.backline: dc.DrawLines([f(*i) for i in line]) + + def line(self, img, line, color): + x0, y0 = line[0] + for x, y in line[1:]: + cv2.line(img, (int(x0), int(y0)), (int(x), int(y)), color, 2) + x0, y0 = x, y + + def buildmsk(self, shape): + img = np.zeros(shape[:2], dtype=np.uint8) + img[:] = 3 + for line in self.foreline: self.line(img, line, 1) + for line in self.backline: self.line(img, line, 0) + return img +``` +1. **draw** we need a foreground list and a background list, then draw in diffrent colors +2. **buildmsk** in the grabcut we need a method to build a mask. + +### Grabcut +```python +class GrabCut(Filter): + title = 'Grab Cut' + note = ['rgb', 'not_slice', 'auto_snap', 'not_channel'] + + def run(self, ips, snap, img, para = None): + msk = ips.mark.buildmsk(img.shape) + bgdModel = np.zeros((1,65),np.float64) + fgdModel = np.zeros((1,65),np.float64) + msk, bgdModel, fgdModel = cv2.grabCut(snap, msk,None,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_MASK) + img[msk%2 == 0] //= 3 +``` +this is a Filter do the grabcut method, It get mask from the mark. + +### Tool +Tool is one of ImagePy's engines. you need implements these method: +1. **mouse_down** when mousedown, youcan got the current imageplus, the x, y. and which button is down, and some other informationg from key,(if ctrl, alt, shift is pressed...) +2. **mouse_up** like mouse_down +3. **mouse_move** like mouse_down +4. **mouse_wheel** like mouse_down + +```python +class Plugin(Tool): + title = 'Grabcut' + """FreeLinebuf class plugin with events callbacks""" + def __init__(self): + self.status = -1 + + def mouse_down(self, ips, x, y, btn, **key): + if not isinstance(ips.mark, Mark): + ips.mark = Mark() + if btn==1 and not key['ctrl']: + self.status = 1 + self.cur = [(x, y)] + ips.mark.foreline.append(self.cur) + if btn==1 and key['ctrl']: + del ips.mark.foreline[:] + del ips.mark.backline[:] + if btn==3 and not key['ctrl']: + self.status = 0 + self.cur = [(x, y)] + ips.mark.backline.append(self.cur) + if btn==3 and key['ctrl']: + GrabCut().start() + ips.update = True + + def mouse_up(self, ips, x, y, btn, **key): + if self.status==1 and len(self.cur)==1: + ips.mark.foreline.remove(self.cur) + if self.status==0 and len(self.cur)==1: + ips.mark.backline.remove(self.cur) + self.status = -1 + ips.update = True + + def mouse_move(self, ips, x, y, btn, **key): + if self.status!=-1: + self.cur.append((x, y)) + ips.update = True + + def mouse_wheel(self, ips, x, y, d, **key): + pass +``` +**here we do these:** +1. put track in foreground list when move whith left button pressed. +2. put track in background list when move whith right button pressed. +3. clear foreground and background list if left click with ctrl pressed. +4. do grabcut when right click with ctrl pressed. + +**Tool files are stored in the sub-folder of tools, with a generated 16 * 16 thumbnail icon. The icon and the tool are stored in the same name as the gif file** + +Surf Demo +--------- +continued from the interactive threshold watershed demo +![fragment](http://opencvplgs.imagepy.org/surf.png) +this demo use surf feature to match two points and find the homo Matrix.(not in this project but in imagepy>plugin>surf) + +**we can use IPy.write, IPy.table to generate text log and data grid conviniently** + + +Macros +------ +![record](http://opencvplgs.imagepy.org/macros.png) + +**Macros** is one of engines, It is a text file with every line as: +**"PluginID > {parameter}"**, If the parameter is None and the Plugin need parameter, IPy will show dialog to interact, if the parameter is given, ImagePy just run use the given parameter. + +We can Open the **Plugin > Macros > Macros Recorder** to record the operate. Then save as a file with **.mc** extent under the menus folder. It will be parsed as a menu when started next. This is Macros, We never need to implements ourself. + +Then we Try the **Surf Demo** macros, Wow!, It run the command sequence automatically! + +**We can use Macros to do some bat processing, what more? It can be used as a good tutorial, We just implement the baseic method, and use macros to show this method can solve such problem!!!** + +About the plugin's order +------------------------ +![catlog](http://opencvplgs.imagepy.org/catlog.png) + +ImagePy is a plugin framework. The Catlog will be parsed as the corresponding menus. You just copy package under the menus folder or it's sub folder. But the question is, Our function will be in a disordered order. **So we can add a list called catlog under every init file.** + +![plugins](http://opencvplgs.imagepy.org/order.png) + +Now OpenCV menu is before the Help, and the Threshold is the first Item, then Filter, Segmentation, last is Demo. and if we put '-' in catlog, It will be parsed as a spliter line. + +**OK! That is a start, I want more developer can join. I think it is significative to let Opencv be esaier to approach, Benifit more scientists who does not master programming. But I cannot do it by myself, My English is not so good, and have little spare time, But I will do my best!** \ No newline at end of file diff --git a/imagepy/menus/Plugins/Contribute/Contributions/Sea Ice.md b/imagepy/menus/Plugins/Contribute/Contributions/Sea Ice.md new file mode 100644 index 00000000..ef37c96f --- /dev/null +++ b/imagepy/menus/Plugins/Contribute/Contributions/Sea Ice.md @@ -0,0 +1,241 @@ +# Sea Ice Image Analysis + +**Path:** https://github.com/Image-Py/seaice + +**Version:** 0.1 + +**Author:** YXDragon + +**Email:** yxdragon@imagepy.org + +**Keyword:** seaice, rs + +**Description:** a toolkit developed for sea ice rs image processing + +you must fill the information upon, and you can not remove or insert line, you can write free below. + +[ImagePy](https://github.com/Image-Py/imagepy) is an image processing framework developed in Python. We can extend it with plugin esaily, This project is a toolkit developed for sea ice rs image processing. Please install [ImagePy](https://github.com/Image-Py/imagepy) first, then use **Plugins > Install > Install Plugins** then input this project's git address. Enter, then ImagePy will install this toolkit automotely.(maybe the gdal lib will got some trouble, please use whl [here](https://www.lfd.uci.edu/~gohlke/pythonlibs/#gdal)) + + + +**Paper About ImagePy on Bioinformatics**: + +[ImagePy: an open-source, Python-based and platform-independent software package for bioimage analysis](https://academic.oup.com/bioinformatics/advance-article/doi/10.1093/bioinformatics/bty313/4989871) + + + +## High Definition Segmentation + +This is a HD RS Image, Now we try to segmet it from each bright line and recognise each fragment is water or ice. You can follow the method below, And you can also use **Ice > HD Segment Demo**, you can got a macros like an animation. + +![](http://idoc.imagepy.org/ice/30.gif) + + + +**File > Open** to open a HD sea ice image, or you can use **Ice > HD Ice Image** to open our demo image below. + +![](http://idoc.imagepy.org/ice/22.png) + + + +**Process > Filter > Sobel** direction=both, then we got a edge gradient image. + +![](http://idoc.imagepy.org/ice/23.png) + + + +**Process > Filter > Gaussian** sigma=3, do a gaussian filter to smooth the gradient image. + +![](http://idoc.imagepy.org/ice/24.png) + + + +**Process > Math > Multiply** multiply 3, bright the image. + +![](http://idoc.imagepy.org/ice/25.png) + + + +**Process > Hydrology > Find Minimum** tolerance=10, find the local minimum, and use this points as rood for watershed. + +![](http://idoc.imagepy.org/ice/26.png) + + + +**Process > Hydrology > Watershed With ROI** got the watershed edge. + +![](http://idoc.imagepy.org/ice/27.png) + + + +**Edit > Invert** invert it then the region are in white. + +![](http://idoc.imagepy.org/ice/28.png) + + + +**Analysis > Region Analysis > Intensity Filter** use the segmentation image as result, and do a Intensity Analysis with the original image, because the ice is brighter then water, so we use every fragment's mean value to check if it is ice. + +![](http://idoc.imagepy.org/ice/29.png) + + + +then overlay the line on the original image, and set a color map, we got the result below. And we can also export the result as a gif animation. ImagePy can do these esaily, but it is not our key here. + +![](http://idoc.imagepy.org/ice/31.png) + + + +## Modis Image Segmentation + +Here we do segment with modis data. Modis data are saved as tiff file, which contains the geo project information. + +**Ice > Geo Tiff Open** you must use geo tiff open to open the modis data, if you use normal open method, you would not got the geo information. **Ice > Modis Test Data** can open some demo data. + +![](http://idoc.imagepy.org/ice/1.png) + + + +**Rectangle Select Tool** the first tool in toolbar, make a rectangle selection. + +![](http://idoc.imagepy.org/ice/2.png) + + + +**Ice > Duplicate With Projection** if use normal duplicate method, the geo information will be lost. + +![](http://idoc.imagepy.org/ice/3.png) + + + +**Ice > Load Geo Roi** the Geo ROI is the landedge, if you has a shapefile, you can use the landedge tu clip the image, if you did not have, I think it doesnot matter, the interactive segment method below can also exclude the land area. **Ice > Bohai Landedge** is a landedge acrroding the demo data. + +![](http://idoc.imagepy.org/ice/4.png) + + + +**Image > Clear Out** clear the land area + +![](http://idoc.imagepy.org/ice/5.png) + + + +**Grab Tool** which has a scissors icon, this tool is a wraper with opencv's grabcut, **move mouse, use left key to mark the ice(red), and left key to mark the water(blue) then click left key with ctrl pressed** we can got the red edge, this is our segmentation result. + +![](http://idoc.imagepy.org/ice/6.png) + + + +If we did not satisfy the result, we can use **ctrl + z** to undo, then repair the mark, then give a **left click with ctrl** again, util the result is OK. + +![](http://idoc.imagepy.org/ice/7.png) + + + +then **undo** again, and give a **left click with alt**, program will clear the water area. + +![](http://idoc.imagepy.org/ice/8.png) + + + +**Image > Type > 8-bit** transform to 8 bit gray image. + +![](http://idoc.imagepy.org/ice/9.png) + + + +**Process > Math > Max** bright the background, make it same as the water. + +![](http://idoc.imagepy.org/ice/10.png) + + + +**Process > Filter > UnSharp Mask** do a Unsharm Mask Enhance. + +![](http://idoc.imagepy.org/ice/11.png) + + + +**Image > Adjust > Threshold** do a threshold, then we got the binary mask. + +![](http://idoc.imagepy.org/ice/12.png) + + + +**Process > Binary > Binary Watershed** do a binary watershed, segment the binary mask in fragments. + +![](http://idoc.imagepy.org/ice/14.png) + + + +**Ice > Ice Statisticnt** statistic the all fragments, draw a area/Frequence graph. + +![](http://idoc.imagepy.org/ice/15.png) + + + +**Ice > Show Resultl** after the segmentation, we can make a colorful report, which show the Ice distribution. + +![](http://idoc.imagepy.org/ice/16.png) + +you can also use **Ice Export To Shapefile**, **Ice Export To WKT** to save the result as a shapefile or wkt file. + + + +## Geo Match and Differece Analysis + +**Ice > Geo Match** because we has geo project information, so we can match two images exactly. + +![](http://idoc.imagepy.org/ice/18.png) + + + +**then extract the ice area from the two images just like befor**. + +![](http://idoc.imagepy.org/ice/19.png) + + + +**Ice > Ice Differenc** we got a new image, which has 4 colors. both water, both ice, the new ice, water->ice, ice->water. + +![](http://idoc.imagepy.org/ice/20.png) + + + +choose a blue color map + +![](http://idoc.imagepy.org/ice/21.png) + + + +## Moving Detect + +Here are some thunder time series image data, we need detect the water's direction and velocity from the time series. You can follow the method below, And you can also use **Ice > Move Detect Demo**, you can got a macros like an animation. + +![](http://idoc.imagepy.org/ice/36.gif) + + + +**File > Import Sequence** to import image sequence, or you can use **Ice > Thunder Sequence** open the demo data. + +![](http://idoc.imagepy.org/ice/33.png) + + + +**Ice > Move Detec** moving detect use scikit-image's orb feature descripter do match within each slice, sample means the down sample scale, sigma means do a gaussian befor the feature extract, and std means the limit when count the affine matrix. + +![](http://idoc.imagepy.org/ice/34.png) + + + +then we got a table, each row means one transformation, and mark the current velocity and direction on the image. + +![](http://idoc.imagepy.org/ice/35.png) + + + +## ImagePy + +ImagePy could do many other things, the basic mathematical operations, filters, pixel statistics, 3D reconstruction and other functions, It is useful in biology, material, industry... here is a brief gallery. + +![](http://idoc.imagepy.org/ice/37.jpg) \ No newline at end of file diff --git a/imagepy/menus/Plugins/Contribute/Contributions/SimpleITK.md b/imagepy/menus/Plugins/Contribute/Contributions/SimpleITK.md new file mode 100644 index 00000000..1a73c23c --- /dev/null +++ b/imagepy/menus/Plugins/Contribute/Contributions/SimpleITK.md @@ -0,0 +1,260 @@ +# itk-plgs + +**Path:** https://github.com/Image-Py/itk-plgs + +**Version:** 0.1 + +**Author:** YXDragon + +**Email:** yxdragon@imagepy.org + +**Keyword:** itk, segment + +**Description:** SimpleITK plugin set for ImagePy + +you must fill the information upon, and you can not remove or insert line, you can write free below. + +## Document + +**Introduction:** itk need not much introductions, It is a 2D/3D image segment library. ImagePy is a interactive image processing framework which can wrap any numpy based library esaily. And supporting multi-channels, imagestack, lookuptable, roi, macros recorder...It is a Plugin system(just like ImageJ but more convenient). This project is a wrapper of itk for ImagePy plugins! + +**Now It is just a start, I wrap little of itk's algrism, aimed to introduct how to wrote ImagePy plugin, The Demo in this document is representative.** + +License +------- +I know many numpy based project has a BSD license, but, sorry, I use SimpleITK, so must be under LGPL. + +MainFrame +--------- +![mainframe](http://idoc.imagepy.org/itk/mainframe.png) + +It is ImagePy's MainFrame, like ImageJ. And ImagePy has contains many common function, such as open image, save image, do some filter, do a roi, draw with pencil... It requires wxpython as ui, Numpy as base structure, shapely to treat the roi, and scipy.ndimage to so dome common filter. But this project devotes to **do a wrapper for itk** + +Add reader and writer +------------------------ +Itk supports many medical format, such as dicom, nii... Now let's add reader and writer plugins for ImagePy. +### first we add read, write function +as itk read any format as a image sequence, but sometimes we need read one slice. so we write a **readall**, then write a **read**. +```python +import SimpleITK as sitk +import numpy as np + +def readall(path): + image = sitk.ReadImage(path) + arr = sitk.GetArrayFromImage(image) + if arr.dtype == np.int16: + arr = arr.astype(np.int32) + return arr + +def read(path):return readall(path)[0] + +def write(path, img): + sitk.WriteImage(sitk.GetImageFromArray(img), path) +``` +### register reader and writer to the io manager +```python +from imagepy.core.util import fileio + +# add dicom reader and writer +fileio.add_reader(['dcm'], read) +fileio.add_writer(['dcm'], write) + +class OpenDCM(fileio.Reader): + title = 'DICOM Open' + filt = ['DCM'] + +class SaveDCM(fileio.Writer): + title = 'DICOM Save' + filt = ['DCM'] + +# add nii reader and writer, because nii is a sequence, so ruse read all, and give as a tuple. +fileio.add_reader(['nii'], (readall,)) +fileio.add_writer(['nii'], (write,)) + +class OpenNII(fileio.Reader): + title = 'NII Open' + filt = ['NII'] + +class SaveNII(fileio.Reader): + title = 'NII Save' + filt = ['NII'] + +plgs = [OpenDCM, SaveDCM, '-', OpenNII, SaveNII] +``` + +Do a simple filter +------------------ +![gradient](http://idoc.imagepy.org/itk/gradient.png) +```python +import SimpleITK as sitk +from imagepy.core.engine import Filter + +class Plugin(Filter): + title = 'ITK Gradient Magnitude' + note = ['all', 'auto_msk', 'auto_snap'] + + def run(self, ips, snap, img, para = None): + img = sitk.GetImageFromArray(img) + img = sitk.GradientMagnitude(img) + return sitk.GetArrayFromImage(img) +``` +### These gradient operator is simplest filter with no parameter. +1. class name must be Plugin +2. title is necessary, be the plugin's id, show in menus. +3. set the note, which tells ImagePy what to do for you. +4. overwrite run method return the result + +Filter is one of engines, means need a image, then do some change on it, It has a run method in such type: +* **ips** is the wrapper of image with some other information (lookup table, roi...) +* **snap** is a snapshot of the image, if 'auto_snap' in note, ImagePy will copy the image to snap befor run. (for many filter method must be implemented in a buffer) +* **img** is the current image you are processing. +* **para** is the parameter you got interactive. (there is no here) +### note is very important + +* **all** means this plugin works for all type image. +* **auto_snap** means ImagePy do a snapshot befor processing, then you can use Undo. +* **auto_msk** means when there is a roi on the image, Plugin will only influnce the pixel in. +* more detail information please see [ImagePy's README](https://github.com/Image-Py/imagepy)! + +Filter with parameter +--------------------- +![gaussian](http://idoc.imagepy.org/itk/gaussian.png) +```python +# -*- coding: utf-8 -* +import SimpleITK as sitk +from imagepy.core.engine import Filter + +class Plugin(Filter): + title = 'ITK Discrete Gaussian' + note = ['all', 'auto_msk', 'auto_snap', 'preview'] + para = {'sigma':1.0} + view = [(float, (0,10), 1, 'sigma', 'sigma', 'pix')] + + def run(self, ips, snap, img, para = None): + itkimg = sitk.GetImageFromArray(snap) + itkimg = sitk.DiscreteGaussian(itkimg, para['sigma']) + return sitk.GetArrayFromImage(itkimg) +``` +Many Filter need some parameter. Just like Gaussian. We just need do a little more. +1. **para** is a dict object, which contains the parameter you need. +2. **view** tell ImagePy how to interact when this plugin run, **(float, (0,10), 1, 'sigma', 'sigma', 'pix')** means it is a float between 0 and 10, title is sigma, corresponding to the sigma parameter with unit pix. +More detail information please see [ImagePy's README](https://github.com/Image-Py/imagepy)! + +**Add 'preview' in note, then when you adjust the parameter, ImagePy run this plugin immediately** + +Write 3D Filter +---------------- +![gaussian3d](http://idoc.imagepy.org/itk/gaussian3d.png) +```python +import SimpleITK as sitk +from imagepy.core.engine import Simple + +class Plugin(Simple): + title = 'ITK Gradient Magnitude 3D' + note = ['all', 'stack3d'] + + def run(self, ips, imgs, para = None): + itkimgs = sitk.GetImageFromArray(imgs) + itkimgs = sitk.GradientMagnitude(itkimgs) + imgs[:] = sitk.GetArrayFromImage(itkimgs) +``` +when there is a image sequence, If you run a gaussian filter, it will ask if you want to process every slice. If ok, it will process slice by slice. But a 3d gaussian filter will blur the image sequence by **X, Y and Z axis**. + +**Filter** aimed at treat a single slice, but if you want to process the whole images, please extends a **Simple**. It also can process other information. eg. set the look up table, or treat the roi, or save the current image/image sequence. + +Treat ROI and ColorImage +----------------------------- +![roicolor](http://idoc.imagepy.org/itk/roicolor.png) +```python +import SimpleITK as sitk +from imagepy.core.engine import Filter, Simple +import numpy as np + +class Plugin(Filter): + title = 'ITK Canny EdgeDetection' + note = ['all', 'auto_msk', 'auto_snap', '2float', 'preview'] + para = {'sigma':1.0, 'low_threshold':10, 'high_threshold':20} + view = [(float, (0,10), 1, 'sigma', 'sigma', 'pix'), + ('slide',(0,50), 'low_threshold', 'low_threshold',''), + ('slide',(0,50), 'high_threshold', 'high_threshold','')] + + def run(self, ips, snap, img, para = None): + img = sitk.GetImageFromArray(snap) + img = sitk.CannyEdgeDetection(img, para['low_threshold'], para['high_threshold'], [para['sigma']]*2) + return sitk.GetArrayFromImage(img)*ips.range[1] +``` +You see, we did not write code to treat the color image, but it works, and We can draw a ROI on the image, only the ROI area be changed! And we can undo the lasted operation. **Even if it is a imagestack, ImagePy will ask you if run every slice!!** + +Watershed With ROI +---------------------- +![roiwatershed](http://idoc.imagepy.org/itk/roiwatershed.png) +```python +import SimpleITK as sitk +from imagepy.core.engine import Filter, Simple +import numpy as np + +class Plugin(Filter): + title = 'ITK Watershed Manual Marker' + note = ['8-bit', 'not_slice', 'auto_snap', 'req_roi'] + + para = {'sigma':2} + view = [(int, (0,10), 0, 'sigma', 'sigma', 'pix')] + + def run(self, ips, snap, img, para = None): + itkimg = sitk.GetImageFromArray(img) + itkimg = sitk.DiscreteGaussian(itkimg, para['sigma']) + itkimg = sitk.GradientMagnitude(itkimg) + itkmarker = sitk.GetImageFromArray(ips.get_msk().astype(np.uint16)) + itkmarker = sitk.ConnectedComponent(itkmarker, fullyConnected=True) + lineimg = sitk.MorphologicalWatershedFromMarkers(itkimg, itkmarker, markWatershedLine=True) + labels = sitk.GetArrayFromImage(lineimg) + return np.where(labels==0, ips.range[1], 0) +``` + +ImagePy support ROI, you can use tool to draw a roi(point, line, polygon...), And We can use ips.roi access it, and ips.get_msk(mode='in') to get the roi mask image. **mode can be 'in','out',or int means a sketch with specific width.** Then use the mask as marker to do a watershed, + +1. **req_roi** means this plugin need a roi, ImagePy will check for you, if ther is not, interrupt the plugin. +2. **not_slice** tells Imagepy need not to iterate slices if it is a stack, because this interactive is just ok for specific image, there is no need to go through. + +Watershed3D And Surface Reconstruct +------------------------------------------ +![surface](http://idoc.imagepy.org/itk/3dsurface.png) + +we can also do a 3d watershed, **mark the up and down as two markers**, then **watershed on the 3d gradient image**. we can get a perfect mask. Then do a 3d surface reconstruction with **vtk(mayavi)**. + +Macros +-------- +![macros](http://idoc.imagepy.org/itk/macros.png) + +**Macros** is one of engines, It is a text file with every line as: +**"PluginID > {parameter}"**, If the parameter is None and the Plugin need parameter, IPy will show dialog to interact, if the parameter is given, ImagePy just run use the given parameter. + +We can Open the **Plugin > Macros > Macros Recorder** to record the operate. Then save as a file with **.mc** extent under the menus folder. It will be parsed as a menu when started next. This is Macros, We never need to implements ourself. + +Then we Try the **Find And Mark Coins** macros, Wow!, It run the command sequence automatically! + +**We can use Macros to do some bat processing, what more? It can be used as a good tutorial, We just implement the basic method, and use macros to show this method can solve such problem!!!** + +MarkDown +------------ +you can also write a markdown file, and lay it under any sub folder of menus, when ImagePy setup, It will be loaded as a menus too, when click it, the markdown page will show. + +About the plugin's order +------------------------ +![order](http://idoc.imagepy.org/itk/order.png) + +ImagePy is a plugin framework. The Catlog will be parsed as the corresponding menus. You just copy package under the menus folder or it's sub folder. But the question is, Our function will be in a disordered order. **So we can add a list called catlog under every init file.** + +Now ITK menu is before the Help, and the IO is the first Item, then Filters, Features, Segmentation. and if we put '-' in catlog, It will be parsed as a spliter line. + +What can ImagePy do +------------------------ +![view](http://idoc.imagepy.org/itk/view.png) + +**ImagePy** can wrap any numpy based libraries, can generate table esaily. In the basic version, It contains scikit-image, and I want to build a opencv-plgs and a itk-plgs. then integrate them, **It will be powerful than Fiji**, and be more esaily to extend. + +**OK! That is a start, I want more developer can join. I think it is significative to let ITK be esaier to approach, Benifit more scientists who does not master programming. But I cannot do it by myself, My English is not so good, and have little spare time, But I will do my best!** + +Something Imperfect +------------------- +**as simple itk cannot treat numpy array directly, so we must use GetImageFromArray and GetArrayFromImage, Did you have any better method?** \ No newline at end of file diff --git a/imagepy/menus/Plugins/Contribute/__init__.py b/imagepy/menus/Plugins/Contribute/__init__.py new file mode 100644 index 00000000..7d7f6c72 --- /dev/null +++ b/imagepy/menus/Plugins/Contribute/__init__.py @@ -0,0 +1 @@ +catlog = ['Contribute Document', 'Contribute Plugin', '-', 'pmanager_wgt', 'Contributions'] \ No newline at end of file diff --git a/imagepy/menus/Plugins/Contribute/pmanager_wgt.py b/imagepy/menus/Plugins/Contribute/pmanager_wgt.py new file mode 100644 index 00000000..5d25867b --- /dev/null +++ b/imagepy/menus/Plugins/Contribute/pmanager_wgt.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- +""" +Created on Sat Jan 7 16:01:14 2017 + +@author: yxl +""" +import wx, os, glob, shutil +from imagepy import IPy, root_dir +from imagepy.core.manager import PluginsManager +from imagepy.ui.mkdownwindow import HtmlPanel, md2html + +class VirtualListCtrl(wx.ListCtrl): + def __init__(self, parent, title, data=[]): + wx.ListCtrl.__init__(self, parent, style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_VIRTUAL) + self.title, self.data = title, data + #self.Bind(wx.EVT_LIST_CACHE_HINT, self.DoCacheItems) + for col, text in enumerate(title): + self.InsertColumn(col, text) + self.set_data(data) + + def OnGetItemText(self, row, col): + return self.data[row][col] + + def OnGetItemAttr(self, item): return None + + def OnGetItemImage(self, item): return -1 + + def set_data(self, data): + self.data = data + self.SetItemCount(len(data)) + + def refresh(self): + self.SetItemCount(len(self.data)) + +def parse(path): + f = open(path) + body = {'file':path} + try: + for i in range(13): + line = f.readline() + if line[0] == '#':body['name'] = line.split('#')[-1].strip() + if 'Path:' in line: body['path'] = line.split('**')[-1].strip() + if 'Version:' in line: body['version'] = line.split('**')[-1].strip() + if 'Author:' in line: body['author'] = line.split('**')[-1].strip() + if 'Email:' in line: body['email'] = line.split('**')[-1].strip() + if 'Keyword:' in line: body['keyword'] = line.split('**')[-1].strip() + if 'Description' in line: body['Description'] = line.split('**')[-1].strip() + f.close() + except: body = [0] + finally: f.close() + return None if len(body)!=8 else body + +class Plugin( wx.Panel ): + title = 'Plugins Manager' + single = None + def __init__( self, parent,): + wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, + pos = wx.DefaultPosition, size = wx.Size( 600,300 ), + style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + sizer = wx.BoxSizer( wx.HORIZONTAL) + bSizer1 = wx.BoxSizer( wx.VERTICAL ) + bSizer2 = wx.BoxSizer( wx.HORIZONTAL ) + self.m_staticText1 = wx.StaticText( self, wx.ID_ANY, "Search:", + wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText1.Wrap( -1 ) + bSizer2.Add( self.m_staticText1, 0, wx.ALIGN_CENTER|wx.ALL|wx.EXPAND, 5 ) + self.txt_search = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, + wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer2.Add( self.txt_search, 1, wx.ALL, 5 ) + + bSizer3 = wx.BoxSizer( wx.HORIZONTAL ) + self.btn_install = wx.Button( self, wx.ID_ANY, 'Install/Update', wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) + self.btn_uninstall = wx.Button( self, wx.ID_ANY, 'Remove', wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) + self.chk_has = wx.CheckBox( self, wx.ID_ANY, 'only installed', wx.DefaultPosition, wx.DefaultSize, 0 ) + + bSizer3.Add(self.chk_has, 0, wx.ALL|wx.EXPAND, 5) + bSizer3.AddStretchSpacer(1) + bSizer3.Add( self.btn_install, 0, wx.ALL, 5) + bSizer3.Add( self.btn_uninstall, 0, wx.ALL, 5) + bSizer1.Add( bSizer2, 0, wx.EXPAND, 5) + self.lst_plgs = VirtualListCtrl( self, ['Name', 'Author', 'Version', 'Status']) + self.lst_plgs.SetColumnWidth(0,100) + self.lst_plgs.SetColumnWidth(1,100) + self.lst_plgs.SetColumnWidth(2,60) + self.lst_plgs.SetColumnWidth(3,60) + self.htmlpanel = HtmlPanel(self) + bSizer1.Add( self.lst_plgs, 1, wx.LEFT|wx.RIGHT|wx.EXPAND, 5 ) + bSizer1.Add( bSizer3, 0, wx.EXPAND, 5 ) + sizer.Add(bSizer1, 0, wx.ALL|wx.EXPAND, 0) + sizer.Add(self.htmlpanel, 1, wx.ALL|wx.EXPAND, 5 ) + + self.SetSizer( sizer ) + self.Layout() + self.Centre( wx.BOTH ) + # Connect Events + self.txt_search.Bind( wx.EVT_TEXT, self.on_search) + self.lst_plgs.Bind( wx.EVT_LIST_ITEM_SELECTED, self.on_run) + self.btn_install.Bind(wx.EVT_BUTTON, self.on_install) + self.btn_uninstall.Bind(wx.EVT_BUTTON, self.on_remove) + self.chk_has.Bind( wx.EVT_CHECKBOX, self.on_check) + self.load() + + #def list_plg(self, lst, items + def load(self): + here = os.path.abspath(os.path.dirname(__file__)) + has = glob.glob(os.path.join(root_dir,'plugins/*/*.md')) + fs = glob.glob(here+'/Contributions/*.md') + prjs = [p for p in [parse(i) for i in fs] if not p is None] + has = [p for p in [parse(i) for i in has] if not p is None] + keys = set([i['path'] for i in prjs]) + for i in has: + if not i['path'] in keys: prjs.append(has) + prjs = sorted([(i['name'], i) for i in prjs]) + self.prjs = [i[1] for i in prjs] + + for i in self.prjs: + for j in has: + if i['path'] == j['path']: + i['old'] = j['version'] + i['folder'] = os.path.split(j['file'])[0] + self.on_search(None) + + # Virtual event handlers, overide them in your derived class + def on_search( self, event ): + wd = self.txt_search.GetValue() + f = lambda x: '' if not 'old' in x else ['update', 'installed'][x['old']==x['version']] + self.buf = [[i['name'], i['author'], i['version'], f(i), i] + for i in self.prjs if wd.lower() in str(i).lower()] + if self.chk_has.GetValue(): self.buf = [i for i in self.buf if i[3]!=''] + self.lst_plgs.set_data(self.buf) + self.lst_plgs.Refresh() + + def on_run(self, event): + f = open(self.buf[event.GetIndex()][-1]['file']) + cont = f.read() + f.close() + cont = '\n'.join([i.strip() for i in cont.split('\n')]) + self.htmlpanel.SetValue((md2html(cont), '')) + + def on_install(self, event): + i = self.lst_plgs.GetFirstSelected() + if i==-1: return + path = self.buf[i][-1]['path'] + PluginsManager.get('Install Plugins')().start( + {'repo':self.buf[i][-1]['path']}, self.load) + + def on_remove(self, event): + i = self.lst_plgs.GetFirstSelected() + if i==-1: return + shutil.rmtree(self.buf[i][-1]['folder']) + IPy.reload_plgs(True, True, True, True) + self.load() + + def on_check(self, event): self.load() + +if __name__ == '__main__': + from glob import glob + fs = glob('Contributions/*.md') + for i in fs: print(parse(i)) \ No newline at end of file diff --git a/imagepy/menus/Plugins/Install/__init__.py b/imagepy/menus/Plugins/Install/__init__.py index 07ccc674..9fa9fb25 100644 --- a/imagepy/menus/Plugins/Install/__init__.py +++ b/imagepy/menus/Plugins/Install/__init__.py @@ -1 +1 @@ -catlog = ['installpkg_plgs', '-', 'installplg_plgs'] \ No newline at end of file +catlog = ['installpkg_plgs', '-', 'installplg_plgs', '-', 'Plugins Manager'] \ No newline at end of file diff --git a/imagepy/menus/Plugins/Install/installplg_plgs.py b/imagepy/menus/Plugins/Install/installplg_plgs.py index ba3dfdc8..e30cd61f 100644 --- a/imagepy/menus/Plugins/Install/installplg_plgs.py +++ b/imagepy/menus/Plugins/Install/installplg_plgs.py @@ -28,13 +28,13 @@ def Schedule(a,b,c, plg): class Install(Free): title = 'Install Plugins' - para = {'pkg':'https://github.com/Image-Py/IBook'} + para = {'repo':'https://github.com/Image-Py/IBook'} prgs = (0, 100) view = [('lab', None, 'input a zipfile url or github url as http://github.com/username/project'), - (str, 'pkg', 'package', '')] + (str, 'repo', 'package', '')] def run(self, para=None): - url = para['pkg'] + url = para['repo'] if 'github.com' in url: if url[-4:] == '.git': url = url.replace('.git', '/archive/master.zip') @@ -45,7 +45,7 @@ def run(self, para=None): domain, name = (url[:-4].replace('.','-')).split('/')[-2:] domain, name = domain.replace('_', '-'), name.replace('_', '-') - IPy.set_info('downloading plugin from %s'%para['pkg']) + IPy.set_info('downloading plugin from %s'%para['repo']) urlretrieve(url, os.path.join(path_cache, domain+'_'+name+'.zip'), lambda a,b,c, p=self: Schedule(a,b,c,p)) zipf = zipfile.ZipFile(os.path.join(path_cache, domain+'_'+name+'.zip')) diff --git a/imagepy/menus/Plugins/Install/manager_wgt.py b/imagepy/menus/Plugins/Install/manager_wgt.py new file mode 100644 index 00000000..e11b90f5 --- /dev/null +++ b/imagepy/menus/Plugins/Install/manager_wgt.py @@ -0,0 +1 @@ +from ..Contribute.pmanager_wgt import Plugin \ No newline at end of file diff --git a/imagepy/menus/Plugins/Manager/plgtree_wgt.py b/imagepy/menus/Plugins/Manager/plgtree_wgt.py index 1f0096b9..406441b3 100644 --- a/imagepy/menus/Plugins/Manager/plgtree_wgt.py +++ b/imagepy/menus/Plugins/Manager/plgtree_wgt.py @@ -10,6 +10,8 @@ from imagepy import IPy, root_dir from imagepy.core.loader import loader from wx.py.editor import EditorFrame +from imagepy.ui.mkdownwindow import HtmlPanel, md2html +from imagepy.core.manager import DocumentManager from glob import glob class Plugin ( wx.Panel ): @@ -43,8 +45,7 @@ def __init__( self, parent ): bSizer4.Add( self.m_staticText3, 0, wx.ALL, 5 ) bSizer3.Add( bSizer4, 0, wx.EXPAND, 5 ) - self.txt_info = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, - wx.DefaultPosition, wx.DefaultSize, wx.TE_MULTILINE ) + self.txt_info = HtmlPanel( self ) bSizer3.Add( self.txt_info, 1, wx.ALL|wx.EXPAND, 5 ) @@ -97,14 +98,10 @@ def on_run( self, event ): def on_select( self, event ): plg = self.tre_plugins.GetItemData(event.GetItem()) - print(type(plg)) if plg!=None: self.plg = plg - if plg.__doc__!=None: - self.txt_info.SetValue(plg.__doc__) - elif hasattr(plg, '__module__'): - self.txt_info.SetValue("plugin at {}".format(plg.__module__)) - else: self.txt_info.SetValue("package at {}".format(plg.__name__)) + name = self.tre_plugins.GetItemText(event.GetItem()) + self.txt_info.SetValue((md2html(DocumentManager.get(name)), '')) def on_source(self, event): ## TODO: should it be absolute path ? diff --git a/imagepy/menus/Plugins/Manager/toltree_wgt.py b/imagepy/menus/Plugins/Manager/toltree_wgt.py index f26d8d66..ca20bf9a 100644 --- a/imagepy/menus/Plugins/Manager/toltree_wgt.py +++ b/imagepy/menus/Plugins/Manager/toltree_wgt.py @@ -10,6 +10,8 @@ from imagepy import IPy, root_dir from imagepy.core.loader import loader from wx.py.editor import EditorFrame +from imagepy.ui.mkdownwindow import HtmlPanel, md2html +from imagepy.core.manager import DocumentManager from glob import glob class Plugin ( wx.Panel ): @@ -43,8 +45,7 @@ def __init__( self, parent ): bSizer4.Add( self.m_staticText3, 0, wx.ALL, 5 ) bSizer3.Add( bSizer4, 0, wx.EXPAND, 5 ) - self.txt_info = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, - wx.DefaultPosition, wx.DefaultSize, wx.TE_MULTILINE ) + self.txt_info = HtmlPanel( self ) bSizer3.Add( self.txt_info, 1, wx.ALL|wx.EXPAND, 5 ) @@ -96,14 +97,10 @@ def on_run( self, event ): def on_select( self, event ): plg = self.tre_plugins.GetItemData(event.GetItem()) - print(type(plg)) if plg!=None: self.plg = plg - if plg.__doc__!=None: - self.txt_info.SetValue(plg.__doc__) - elif hasattr(plg, '__module__'): - self.txt_info.SetValue('plugin at %s'%plg.__module__) - else: self.txt_info.SetValue('package at %s'%plg.__name__) + name = self.tre_plugins.GetItemText(event.GetItem()) + self.txt_info.SetValue((md2html(DocumentManager.get(name)), '')) def on_source(self, event): ## TODO: should it be absolute path ? diff --git a/imagepy/menus/Plugins/__init__.py b/imagepy/menus/Plugins/__init__.py index c496375c..dbf77906 100644 --- a/imagepy/menus/Plugins/__init__.py +++ b/imagepy/menus/Plugins/__init__.py @@ -1 +1 @@ -catlog = ['New', 'Macros', 'Manager', '-', 'Install', 'update_plg', '-', 'screencap_plg', 'Games', 'Coins Segment WorkFlow'] \ No newline at end of file +catlog = ['New', 'Macros', 'Manager', '-', 'Install', 'Contribute', 'update_plg', '-', 'screencap_plg', 'Games'] \ No newline at end of file diff --git a/imagepy/menus/Table/Table IO/tableio_plgs.py b/imagepy/menus/Table/Table IO/tableio_plgs.py index 71759b10..fcb8cd4a 100644 --- a/imagepy/menus/Table/Table IO/tableio_plgs.py +++ b/imagepy/menus/Table/Table IO/tableio_plgs.py @@ -5,6 +5,7 @@ def show(data, title): IPy.show_table(data, title) + ViewerManager.add('tab', show) save_csv = lambda path, data:data.to_csv(path) diff --git a/imagepy/ui/mainframe.py b/imagepy/ui/mainframe.py index c6e50727..b7bda4dc 100644 --- a/imagepy/ui/mainframe.py +++ b/imagepy/ui/mainframe.py @@ -11,6 +11,7 @@ #from ui import pluginloader, toolsloader from . import pluginloader, toolsloader, widgetsloader from ..core.manager import ConfigManager, PluginsManager, TaskManager, ImageManager +from ..core.loader import loader from ..core.engine import Macros from .canvasframe import CanvasNoteBook from .tableframe import TableNoteBook @@ -50,6 +51,7 @@ def __init__( self, parent ): print(IPy.uimode()) if IPy.uimode()=='ipy': self.load_aui() else: self.load_ijui() + self.load_document() self.Fit() @@ -142,12 +144,17 @@ def on_pan_close(self, event): if hasattr(event.GetPane().window, 'close'): event.GetPane().window.close() + def load_document(self): + from glob import glob + extends = glob('plugins/*/doc') + for i in extends: loader.build_document(i) + loader.build_document('doc') def reload_plugins(self, report=False, menus=True, tools=False, widgets=False): - print(menus, tools, widgets) if menus: pluginloader.buildMenuBarByPath(self, 'menus', 'plugins', self.menubar, report) if tools: toolsloader.build_tools(self, 'tools', 'plugins', self.toolbar, report) if widgets: widgetsloader.build_widgets(self, 'widgets', 'plugins', self.widgets) + self.load_document() if IPy.uimode()!='ipy': self.Fit() def hold(self): diff --git a/imagepy/ui/mkdownwindow.py b/imagepy/ui/mkdownwindow.py index e39ca4a8..ca0a7096 100644 --- a/imagepy/ui/mkdownwindow.py +++ b/imagepy/ui/mkdownwindow.py @@ -4,9 +4,9 @@ import os def md2html(mdstr): - exts = ['markdown.extensions.extra', 'markdown.extensions.codehilite','markdown.extensions.tables','markdown.extensions.toc'] + exts = ['markdown.extensions.extra', 'markdown.extensions.codehilite','markdown.extensions.tables','markdown.extensions.toc'] - html = ''' + html = ''' @@ -170,32 +170,35 @@ def md2html(mdstr): %s - ''' + ''' - ret = markdown(mdstr,extensions=exts) - f = open('yn.html', 'w', encoding='utf-8') - f.write(html % ret); - f.close() - return html % ret + ret = markdown(mdstr,extensions=exts) + f = open('yn.html', 'w', encoding='utf-8') + f.write(html % ret); + f.close() + return html % ret class HtmlPanel(wx.Panel): - def __init__(self, parent, cont, url): - wx.Panel.__init__(self, parent) - self.frame = self.GetTopLevelParent() - self.titleBase = self.frame.GetTitle() + def __init__(self, parent, cont='', url=''): + wx.Panel.__init__(self, parent) + self.frame = self.GetTopLevelParent() + self.titleBase = self.frame.GetTitle() - sizer = wx.BoxSizer(wx.VERTICAL) - self.wv = webview.WebView.New(self) - self.Bind(webview.EVT_WEBVIEW_TITLE_CHANGED, self.OnWebViewTitleChanged, self.wv) + sizer = wx.BoxSizer(wx.VERTICAL) + self.wv = webview.WebView.New(self) + self.Bind(webview.EVT_WEBVIEW_TITLE_CHANGED, self.OnWebViewTitleChanged, self.wv) - sizer.Add(self.wv, 1, wx.EXPAND) - self.SetSizer(sizer) - self.wv.SetPage(cont, url) + sizer.Add(self.wv, 1, wx.EXPAND) + self.SetSizer(sizer) + self.wv.SetPage(cont, url) - def OnWebViewTitleChanged(self, evt): - if evt.GetString() == 'about:blank': return - if evt.GetString() == 'http:///': return - self.frame.SetTitle("%s -- %s" % (self.titleBase, evt.GetString())) + def SetValue(self, value): + self.wv.SetPage(*value) + + def OnWebViewTitleChanged(self, evt): + if evt.GetString() == 'about:blank': return + if evt.GetString() == 'http:///': return + self.frame.SetTitle("%s -- %s" % (self.titleBase, evt.GetString())) class MkDownWindow(wx.Frame): def __init__(self, parent, title, cont, url): diff --git a/imagepy/ui/toolsloader.py b/imagepy/ui/toolsloader.py index 745efb2e..0a7a5ed6 100644 --- a/imagepy/ui/toolsloader.py +++ b/imagepy/ui/toolsloader.py @@ -6,7 +6,7 @@ from .. import root_dir from ..core.engine import Tool, Macros from ..core.loader import loader -from imagepy.core.manager import ConfigManager +from imagepy.core.manager import ConfigManager, DocumentManager from glob import glob def make_bitmap(bmp): @@ -114,6 +114,7 @@ def add_tools(bar, datas, clear=False, curids=[]): box.Insert(len(box.GetChildren())-2, btn) btn.Bind( wx.EVT_LEFT_DOWN, lambda x, p=data[0]:f(p(), x)) + btn.Bind( wx.EVT_RIGHT_DOWN, lambda x, p=data[0]: IPy.show_md(p.title, DocumentManager.get(p.title))) btn.Bind( wx.EVT_ENTER_WINDOW, lambda x, p='"{}" Tool'.format(data[0].title): set_info(p)) if not isinstance(data[0], Macros) and issubclass(data[0], Tool): From e18d2ed2c3ab7371ce25d3ffc864032a0ffc4f68 Mon Sep 17 00:00:00 2001 From: Yan xiaolong Date: Mon, 21 Jan 2019 00:41:13 +0800 Subject: [PATCH 084/343] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 06bde9ef..e343854e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +![ImagePy](https://github.com/Image-Py/imagepy/blob/master/imagepy/data/logolong.png) # Introduction ImagePy is an open source image processing framework written in Python. Its UI interface, image data structure and table data structure are wxpython-based, Numpy-based and pandas-based respectively. Furthermore, it supports any plug-in based on Numpy and pandas, which can talk easily between scipy.ndimage, scikit-image, simpleitk, opencv and other image processing libraries. From b21c31204e3deca8eb5a263d947de666ee84b4b7 Mon Sep 17 00:00:00 2001 From: Yan xiaolong Date: Mon, 21 Jan 2019 00:41:55 +0800 Subject: [PATCH 085/343] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index e343854e..06bde9ef 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -![ImagePy](https://github.com/Image-Py/imagepy/blob/master/imagepy/data/logolong.png) # Introduction ImagePy is an open source image processing framework written in Python. Its UI interface, image data structure and table data structure are wxpython-based, Numpy-based and pandas-based respectively. Furthermore, it supports any plug-in based on Numpy and pandas, which can talk easily between scipy.ndimage, scikit-image, simpleitk, opencv and other image processing libraries. From 04e27ba9af6a96ac1d5f6d35a3962859d397677d Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 25 Jan 2019 08:35:17 +0800 Subject: [PATCH 086/343] swap snap and img --- imagepy/IPy.py | 16 ++- imagepy/core/engine/table.py | 4 +- imagepy/core/loader/loader.py | 2 +- .../Contribute/Contributions/Demo Repo.md | 114 +++++++++++++++++- imagepy/menus/Plugins/Install/__init__.py | 2 +- imagepy/menus/Plugins/Manager/__init__.py | 2 +- .../plgmanager_wgt.py} | 0 imagepy/menus/Process/Filters/classic_plgs.py | 1 - .../menus/Table/Basic Operator/basic_plgs.py | 14 +-- imagepy/menus/Table/Chart/plot_plgs.py | 14 +-- imagepy/menus/Table/Selection/select_plgs.py | 2 +- imagepy/menus/Table/Signal/signal_plgs.py | 2 +- imagepy/menus/Table/Statistic/sort_plg.py | 2 +- .../menus/Table/Statistic/statistic_plgs.py | 4 +- .../Universal Generator/gnerator_plgs.py | 30 ----- 15 files changed, 139 insertions(+), 70 deletions(-) rename imagepy/menus/Plugins/{Install/manager_wgt.py => Manager/plgmanager_wgt.py} (100%) diff --git a/imagepy/IPy.py b/imagepy/IPy.py index ee90838c..cc9ca302 100644 --- a/imagepy/IPy.py +++ b/imagepy/IPy.py @@ -95,15 +95,8 @@ def showmd(title, cont, url=''): pub.subscribe(showmd, 'showmd') def show_md(title, cont, url=''): wx.CallAfter(pub.sendMessage, 'showmd', title=title, cont=cont, url=url) -''' -def stepmacros(macros): - macros.next() - -pub.subscribe(stepmacros, 'stepmacros') -def step_macros(macros): - wx.CallAfter(pub.sendMessage, "stepmacros", macros=macros) -''' -def alert(info, title="ImagePy Alert!"): + +def _alert(info, title="ImagePy Alert!"): if uimode()=='no': print('ImagePy Alert >>> %s'%title) print(info) @@ -112,6 +105,11 @@ def alert(info, title="ImagePy Alert!"): dialog.ShowModal() == wx.ID_OK dialog.Destroy() +pub.subscribe(_alert, 'alert') + +def alert(info, title="ImagePy Alert!"): + wx.CallAfter(pub.sendMessage, 'alert', info=info, title=title) + # MT alert = lambda info, title='image-py':callafter(alert_, *(info, title)) def yes_no(info, title="ImagePy Yes-No ?!"): diff --git a/imagepy/core/engine/table.py b/imagepy/core/engine/table.py index 5c3cb701..4c0a96a0 100644 --- a/imagepy/core/engine/table.py +++ b/imagepy/core/engine/table.py @@ -46,7 +46,7 @@ def show(self): self.dialog.on_cancel = lambda : self.cancel(self.tps) self.dialog.Show() - def run(self, tps, data, snap, para = None):pass + def run(self, tps, snap, data, para = None):pass def cancel(self, tps): if 'snap' in self.note: @@ -62,7 +62,7 @@ def ok(self, tps, para=None, callafter=None): win = WidgetsManager.getref('Macros Recorder') if win!=None: win.write('{}>{}'.format(self.title, para)) - def runasyn(self, tps, data, snap, para = None, callback = None): + def runasyn(self, tps, snap, data, para = None, callback = None): TaskManager.add(self) start = time() self.run(tps, data, snap, para) diff --git a/imagepy/core/loader/loader.py b/imagepy/core/loader/loader.py index f7531ab1..2e8f0579 100644 --- a/imagepy/core/loader/loader.py +++ b/imagepy/core/loader/loader.py @@ -238,7 +238,7 @@ def build_document(path): for filename in filenames: if filename[-3:] != '.md': continue docs.append(os.path.join(dirpath, filename)) - f = open(docs[-1]) + f = open(docs[-1], encoding='utf-8') cont = f.read() f.close() DocumentManager.add(filename[:-3], cont) diff --git a/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md b/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md index cfa6b46e..8bcad9df 100644 --- a/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md +++ b/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md @@ -1,4 +1,4 @@ -# Demo Repo +# Demo Plugin **Path:** https://github.com/Image-Py/demoplugin @@ -8,11 +8,113 @@ **Email:** yxdragon@imagepy.org -**Keyword:** demo, plugin +**Keyword:** demo, tutorial -**Description:** This is a demo plugin. +**Description:** a friendly develop tutorial. -you must fill the information upon, and you can not remove or insert line, you can write free below. -## Document -here you can write any thing about your plugin. \ No newline at end of file + +## 基础预备 + +1. 什么是插件 + +2. Hello World (第一个插件) + +3. Who Are You (带有交互) + +4. Questionnaire (参数对话框详解) + +5. 一个文件内实现多个插件 + + + +## 插件开发 + +**Markdown: 文档提示** + +1. MarkDown + +**Macros: 用宏串联已有功能** + +1. Macros Recorder:操作录制 +2. Coins Segment Macros:硬币分割 + +**Workflow: 可交互的宏** + +1. Coins Segment Workflow: 按照指引进行硬币分割 + +**Filter: 二维图像滤波器** + +1. Invert Demo:无参数的插件 +2. Gaussian Demo:带有参数的插件 + +**Simple: 图像整体操作** + +1. Red Lut Demo:操作索引表 + +2. ROI Inflate Demo:操作ROI +3. Unit Demo: 设置比例尺及单位 +4. Draw Mark Demo: 设置Overlay Mark +5. Gaussian 3D Demo:三维滤波 + +**Table: 表格数据** + +1. Generate Table Demo:数据表生成 +2. Sort By Key Demo:排序 +3. Table Plot Demo:绘图 + +**Free: 没有任何依赖的插件** + +1. Parameter Demo: 参数对话框生成演示 +2. New Image Demo: 新建图像 + +**Tool: 鼠标交互工具** + +1. Painter Demo:画笔工具 +2. Pixel Inspector:像素查看器 + +**Widget: 桌面小部件** + +1. Widget Demo:桌面小部件演示 + + + +## 插件项目发布 + +**插件的组织方式** + +1. 功能划分 +2. 顺序设定 + +**插件项目的发布** + +1. 编写requirements +2. 为Readme加入的插件头信息 +3. 发布到ImagePy + +**插件的安装与管理** + +1. 通过连接安装插件 +2. 插件管理器 + + + +## 文档编写 + +为插件编写操作手册 + + + +## 注意事项 + +1. 用户友好性 +2. 开发者友好性 +3. 及时沟通 + + + +## 如何贡献 + +1. 宣传推广 +2. 编写插件 +3. 完善文档 \ No newline at end of file diff --git a/imagepy/menus/Plugins/Install/__init__.py b/imagepy/menus/Plugins/Install/__init__.py index 9fa9fb25..07ccc674 100644 --- a/imagepy/menus/Plugins/Install/__init__.py +++ b/imagepy/menus/Plugins/Install/__init__.py @@ -1 +1 @@ -catlog = ['installpkg_plgs', '-', 'installplg_plgs', '-', 'Plugins Manager'] \ No newline at end of file +catlog = ['installpkg_plgs', '-', 'installplg_plgs'] \ No newline at end of file diff --git a/imagepy/menus/Plugins/Manager/__init__.py b/imagepy/menus/Plugins/Manager/__init__.py index 3ce19795..c90fdd97 100644 --- a/imagepy/menus/Plugins/Manager/__init__.py +++ b/imagepy/menus/Plugins/Manager/__init__.py @@ -1 +1 @@ -catlog = ['plgtree_wgt', 'toltree_wgt', 'plglist_wgt', '-', 'shotcut_wgt'] \ No newline at end of file +catlog = ['plgmanager_wgt', '-', 'plgtree_wgt', 'toltree_wgt', 'plglist_wgt', '-', 'shotcut_wgt'] \ No newline at end of file diff --git a/imagepy/menus/Plugins/Install/manager_wgt.py b/imagepy/menus/Plugins/Manager/plgmanager_wgt.py similarity index 100% rename from imagepy/menus/Plugins/Install/manager_wgt.py rename to imagepy/menus/Plugins/Manager/plgmanager_wgt.py diff --git a/imagepy/menus/Process/Filters/classic_plgs.py b/imagepy/menus/Process/Filters/classic_plgs.py index 62fcae6e..84453a00 100644 --- a/imagepy/menus/Process/Filters/classic_plgs.py +++ b/imagepy/menus/Process/Filters/classic_plgs.py @@ -16,7 +16,6 @@ def run(self, ips, snap, img, para = None): nimg.uniform_filter(snap, para['size'], output=img) class Gaussian(Filter): - __doc__ = nimg.gaussian_filter.__doc__ title = 'Gaussian' note = ['all', 'auto_msk', 'auto_snap','preview'] para = {'sigma':2} diff --git a/imagepy/menus/Table/Basic Operator/basic_plgs.py b/imagepy/menus/Table/Basic Operator/basic_plgs.py index 60d89cea..b6e7a4c4 100644 --- a/imagepy/menus/Table/Basic Operator/basic_plgs.py +++ b/imagepy/menus/Table/Basic Operator/basic_plgs.py @@ -4,13 +4,13 @@ class Transpose(Table): title = 'Table Transpose' - def run(self, tps, data, snap, para = None): + def run(self, tps, snap, data, para = None): tps.set_data(data.T) class Corp(Table): title = 'Table Corp' note = ['req_sel'] - def run(self, tps, data, snap, para): + def run(self, tps, snap, data, para): tps.set_data(data.loc[tps.rowmsk, tps.colmsk]) class Duplicate(Table): @@ -22,7 +22,7 @@ def load(self, tps): self.view = [(str, 'name', 'Name', '')] return True - def run(self, tps, data, snap, para = None): + def run(self, tps, snap, data, para = None): newdata = data.loc[tps.rowmsk, tps.colmsk] IPy.show_table(para['name'], newdata) @@ -30,14 +30,14 @@ class DeleteRow(Table): title = 'Delete Rows' note = ['row_sel'] - def run(self, tps, data, snap, para = None): + def run(self, tps, snap, data, para = None): data.drop(tps.rowmsk, inplace=True) class DeleteCol(Table): title = 'Delete Columns' note = ['col_sel'] - def run(self, tps, data, snap, para = None): + def run(self, tps, snap, data, para = None): data.drop(tps.colmsk, axis=1, inplace=True) class AppendRow(Table): @@ -46,7 +46,7 @@ class AppendRow(Table): view = [(int, 'count', (1,100), 0, 'count', ''), (bool, 'fill', 'fill by last row')] - def run(self, tps, data, snap, para = None): + def run(self, tps, snap, data, para = None): newdata = data.reindex(index=range(data.shape[0]+para['count']), \ method=[None,'pad'][para['fill']]) tps.set_data(newdata) @@ -57,7 +57,7 @@ class AddCol(Table): view = [(str, 'name', 'name', ''), ('any', 'value', 'value')] - def run(self, tps, data, snap, para = None): + def run(self, tps, snap, data, para = None): ctype = data.columns.dtype.type data[ctype(para['name'])] = para['value'] print(data.info()) diff --git a/imagepy/menus/Table/Chart/plot_plgs.py b/imagepy/menus/Table/Chart/plot_plgs.py index e43412a5..f43879a5 100644 --- a/imagepy/menus/Table/Chart/plot_plgs.py +++ b/imagepy/menus/Table/Chart/plot_plgs.py @@ -13,7 +13,7 @@ class Plot(Table): (int, 'lw', (1,5), 0, 'line width', ''), (bool, 'grid', 'grid')] - def run(self, tps, data, snap, para = None): + def run(self, tps, snap, data, para = None): data[para['cn']].plot(lw=para['lw'], grid=para['grid'], title=para['title']) plt.show() @@ -27,7 +27,7 @@ class Bar(Table): (bool, 'stack', 'stacked'), (bool, 'grid', 'grid')] - def run(self, tps, data, snap, para = None): + def run(self, tps, snap, data, para = None): if para['dir']: data[para['cn']].plot.barh(stacked=para['stack'], grid=para['grid'], title=para['title']) else: data[para['cn']].plot.bar(stacked=para['stack'], grid=para['grid'], title=para['title']) @@ -47,7 +47,7 @@ class Hist(Table): (bool, 'overlay', 'draw every columns in one'), (bool, 'grid', 'grid')] - def run(self, tps, data, snap, para = None): + def run(self, tps, snap, data, para = None): if para['overlay']: data[para['cn']].plot.hist(stacked=para['stack'], bins=para['bins'], alpha=para['alpha'], orientation=para['dir'], grid=para['grid'], title=para['title']) @@ -66,7 +66,7 @@ class Box(Table): (bool, 'hor', 'horizontal'), (bool, 'grid', 'grid')] - def run(self, tps, data, snap, para = None): + def run(self, tps, snap, data, para = None): data[para['cn']].plot.box(by=None, vert=~para['hor'], grid=para['grid'], title=para['title']) plt.show() @@ -80,7 +80,7 @@ class Area(Table): (bool, 'stack', 'stacked'), (bool, 'grid', 'grid')] - def run(self, tps, data, snap, para = None): + def run(self, tps, snap, data, para = None): data[para['cn']].plot.area(stacked=para['stack'], alpha=para['alpha'], grid=para['grid'], title=para['title']) plt.show() @@ -101,7 +101,7 @@ class Scatter(Table): ('cmap', 'cm', 'color map'), (bool, 'grid', 'grid')] - def run(self, tps, data, snap, para = None): + def run(self, tps, snap, data, para = None): rs = data[para['rs']] * para['s'] if para['rs'] != 'None' else para['s'] cs = data[para['cs']] if para['cs'] != 'None' else '#%.2x%.2x%.2x'%para['c'] cm = ColorManager.get_lut(para['cm'])/255.0 @@ -117,7 +117,7 @@ class Pie(Table): view = [(str, 'title', 'title', ''), ('fields', 'cn', 'select fields')] - def run(self, tps, data, snap, para = None): + def run(self, tps, snap, data, para = None): data[para['cn']].plot.pie(subplots=True, title=para['title']) plt.show() diff --git a/imagepy/menus/Table/Selection/select_plgs.py b/imagepy/menus/Table/Selection/select_plgs.py index d9899dde..851c1e6b 100644 --- a/imagepy/menus/Table/Selection/select_plgs.py +++ b/imagepy/menus/Table/Selection/select_plgs.py @@ -12,7 +12,7 @@ class Select(Table): ('any', 'r2', 'end rows'), (bool, 'rall', 'all rows')] - def run(self, tps, data, snap, para=None): + def run(self, tps, snap, data, para=None): if not para['call']: cmsk = para['cn'] else: cmsk=[] diff --git a/imagepy/menus/Table/Signal/signal_plgs.py b/imagepy/menus/Table/Signal/signal_plgs.py index 52b4a58c..62752dd9 100644 --- a/imagepy/menus/Table/Signal/signal_plgs.py +++ b/imagepy/menus/Table/Signal/signal_plgs.py @@ -12,7 +12,7 @@ class Statistic(Table): view = [(int, 'size', (0,30), 0, 'size', '')] - def run(self, tps, data, snap, para=None): + def run(self, tps, snap, data, para=None): for s in snap.columns: data[s] = nimg.uniform_filter(snap[s], para['size']) diff --git a/imagepy/menus/Table/Statistic/sort_plg.py b/imagepy/menus/Table/Statistic/sort_plg.py index e6cab7b0..607ed659 100644 --- a/imagepy/menus/Table/Statistic/sort_plg.py +++ b/imagepy/menus/Table/Statistic/sort_plg.py @@ -10,7 +10,7 @@ class Plugin(Table): ('field', 'minor', 'minor', 'key'), (bool, 'descend', 'descend')] - def run(self, tps, data, snap, para=None): + def run(self, tps, snap, data, para=None): by = [para['major'], para['minor']] tps.data.sort_values(by=[i for i in by if i != 'None'], axis=0, ascending=not para['descend'], inplace=True) \ No newline at end of file diff --git a/imagepy/menus/Table/Statistic/statistic_plgs.py b/imagepy/menus/Table/Statistic/statistic_plgs.py index 1c79f1a0..befa3571 100644 --- a/imagepy/menus/Table/Statistic/statistic_plgs.py +++ b/imagepy/menus/Table/Statistic/statistic_plgs.py @@ -19,7 +19,7 @@ class Statistic(Table): (bool, 'skew', 'skew'), (bool, 'kurt', 'kurt')] - def run(self, tps, data, snap, para=None): + def run(self, tps, snap, data, para=None): rst, axis = {}, (0,1)[para['axis']=='Row'] if para['sum']:rst['sum'] = snap.sum(axis=axis) if para['mean']:rst['mean'] = snap.mean(axis=axis) @@ -49,7 +49,7 @@ class GroupStatistic(Table): (bool, 'std', 'std'), (bool, 'skew', 'skew')] - def run(self, tps, data, snap, para=None): + def run(self, tps, snap, data, para=None): by = [i for i in [para['major'], para['minor']] if i!='None'] gp = data.groupby(by)[para['cn']] diff --git a/imagepy/menus/Table/Universal Generator/gnerator_plgs.py b/imagepy/menus/Table/Universal Generator/gnerator_plgs.py index 32832aae..12eb99f6 100644 --- a/imagepy/menus/Table/Universal Generator/gnerator_plgs.py +++ b/imagepy/menus/Table/Universal Generator/gnerator_plgs.py @@ -3,36 +3,6 @@ import pandas as pd from imagepy import IPy -''' -生成: - 随机数 - 正太随机 - 日历 - 单位矩阵 -基础: - 添加字段 - 删除字段 - 字段运算 - 赋值 - 截取 - 转置 -统计: - 平均数,最大值,最小值,方差 - 频率统计 - 按id汇总 -筛选: - : -图表: - 折线图 - 条形图 - 饼状图 -信号: - 高斯 - 差分 -关联: - 聚合 - 分组 -''' class One(Free): title = 'Unit Matrix' para = {'size':3} From e82f80ba4d8ccfe562c83f6dd878902223727e2d Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 27 Jan 2019 14:08:13 +0800 Subject: [PATCH 087/343] update() --- imagepy/core/engine/filter.py | 8 +++---- imagepy/core/engine/simple.py | 2 +- imagepy/core/engine/table.py | 6 ++--- imagepy/core/engine/tool.py | 2 +- imagepy/core/loader/loader.py | 1 - imagepy/core/myvi/canvas3d.py | 4 ++-- imagepy/core/roi/lineroi.py | 11 +-------- imagepy/core/roi/pointroi.py | 9 -------- imagepy/core/roi/polygonroi.py | 8 ++++--- imagepy/core/roi/rectangleroi.py | 6 +++-- imagepy/core/wraper/imageplus.py | 4 +++- imagepy/core/wraper/tableplus.py | 4 +++- imagepy/doc/File/New.md | 1 - .../Analysis/Pixel Cluster/cluster_plgs.py | 10 ++++---- .../Region Analysis/statistic_plgs.py | 4 ++-- imagepy/menus/Analysis/label_plg.py | 2 +- imagepy/menus/Edit/edit_plg.py | 4 ++-- imagepy/menus/Image/Adjust/histogram_plgs.py | 2 +- imagepy/menus/Image/Adjust/threshold_plg.py | 4 ++-- .../Image/Lookup table/lookuptables_plg.py | 2 +- imagepy/menus/Image/Stack/orthogonal_plg.py | 22 +++++++++--------- imagepy/menus/Image/background_plg.py | 4 ++-- .../Kit3D/Analysis 3D/surfacemeasure_plg.py | 4 ++-- .../menus/Kit3D/Filters 3D/filters3d_plgs.py | 4 ++-- imagepy/menus/Kit3D/IO 3D/__init__.py | 0 imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py | 4 ++-- .../Contribute/Contributions/OpenCV.md | 6 ++--- imagepy/menus/Plugins/New/demo_tool.py | 2 +- .../menus/Process/Hydrology/hydrology_plgs.py | 12 +++++----- imagepy/menus/Process/Segment/active_plgs.py | 16 ++++++------- .../menus/Process/Threshold/threshold_plgs.py | 4 ++-- imagepy/menus/Process/calculator_plg.py | 2 +- imagepy/tools/Draw/floodfill_tol.py | 2 +- imagepy/tools/Draw/magic_tol.py | 6 ++--- imagepy/tools/Measure/angle2_tol.py | 6 ++--- imagepy/tools/Measure/angle_tol.py | 4 ++-- imagepy/tools/Measure/area_tol.py | 6 ++--- imagepy/tools/Measure/coordinate_tol.py | 4 ++-- imagepy/tools/Measure/distance_tol.py | 4 ++-- imagepy/tools/Measure/profile_tol.py | 6 ++--- imagepy/tools/Network/graphcut_tol.py | 6 ++--- imagepy/tools/Network/graphpen_tol.py | 6 ++--- imagepy/tools/Standard/freearea_tol.py | 6 ++--- imagepy/tools/Standard/freeline_tol.py | 4 ++-- imagepy/tools/Standard/line_tol.py | 4 ++-- imagepy/tools/Standard/magic_tol.py | 6 ++--- imagepy/tools/Standard/oval_tol.py | 6 ++--- imagepy/tools/Standard/painter_tol.py | 4 +++- imagepy/tools/Standard/point_tol.py | 6 ++--- imagepy/tools/Standard/polygon_tol.py | 4 ++-- imagepy/tools/Standard/rectangle_tol.py | 6 ++--- imagepy/tools/Toolkit3D/cursor3d_tol.py | 2 +- imagepy/tools/Transform/rotate_tol.py | 8 +++---- imagepy/tools/Transform/scale_tol.py | 10 ++++---- imagepy/ui/canvas.py | 12 +++++----- imagepy/ui/canvasframe.py | 4 ++-- imagepy/ui/plotwindow.py | 10 ++++---- imagepy/ui/tablewindow.py | 10 ++++---- imagepy/ui/widgets/cmappanel.py | 22 ++++++++++-------- imagepy/ui/widgets/curvepanel.py | 23 +++++++++++-------- imagepy/ui/widgets/histpanel.py | 14 ++++++----- imagepy/ui/widgets/viewport.py | 12 ++++++---- imagepy/widgets/histogram/curve_wgt.py | 4 ++-- imagepy/widgets/histogram/histogram_wgt.py | 12 +++++----- imagepy/widgets/navigator/navigator_wgt.py | 8 +++---- 65 files changed, 211 insertions(+), 210 deletions(-) create mode 100644 imagepy/menus/Kit3D/IO 3D/__init__.py diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index fe714d76..5d311f82 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -45,7 +45,7 @@ def process_one(plg, ips, src, img, para, callafter=None): msk = True ^ ips.get_msk() img[msk] = src[msk] IPy.set_info('%s: cost %.3fs'%(ips.title, time()-start)) - ips.update = 'pix' + ips.update() TaskManager.remove(plg) if not callafter is None:callafter() @@ -75,7 +75,7 @@ def process_stack(plg, ips, src, imgs, para, callafter=None): msk = True ^ ips.get_msk() i[msk] = src[msk] IPy.set_info('%s: cost %.3fs'%(ips.title, time()-start)) - ips.update = 'pix' + ips.update() TaskManager.remove(plg) if not callafter is None:callafter() @@ -178,12 +178,12 @@ def ok(self, ips, para=None, callafter=None): (self, ips, ips.snap, ips.img, para, callafter)).start() if win!=None: win.write('{}>{}'.format(self.title, para)) elif rst == 'cancel': pass - #ips.update = 'pix' + #ips.update() def cancel(self, ips): if 'auto_snap' in self.note: ips.swap() - ips.update = 'pix' + ips.update() def start(self, para=None, callafter=None): ips = self.ips diff --git a/imagepy/core/engine/simple.py b/imagepy/core/engine/simple.py index 224857cd..57bfb3fd 100644 --- a/imagepy/core/engine/simple.py +++ b/imagepy/core/engine/simple.py @@ -62,7 +62,7 @@ def runasyn(self, ips, imgs, para = None, callback = None): start = time() self.run(ips, imgs, para) IPy.set_info('%s: cost %.3fs'%(ips.title, time()-start)) - ips.update = 'pix' + ips.update() TaskManager.remove(self) if callback!=None:callback() diff --git a/imagepy/core/engine/table.py b/imagepy/core/engine/table.py index 4c0a96a0..a3cb0d3a 100644 --- a/imagepy/core/engine/table.py +++ b/imagepy/core/engine/table.py @@ -33,7 +33,7 @@ def load(self, ips):return True def preview(self, tps, para): self.run(tps, tps.data, tps.snap, para) - tps.update = True + tps.update() def show(self): if self.view==None:return True @@ -51,7 +51,7 @@ def run(self, tps, snap, data, para = None):pass def cancel(self, tps): if 'snap' in self.note: tps.data[tps.snap.columns] = tps.snap - tps.update = True + tps.update() def ok(self, tps, para=None, callafter=None): if para == None: para = self.para @@ -67,7 +67,7 @@ def runasyn(self, tps, snap, data, para = None, callback = None): start = time() self.run(tps, data, snap, para) IPy.set_info('%s: cost %.3fs'%(tps.title, time()-start)) - tps.update = 'shp' + tps.update('shp') TaskManager.remove(self) if callback!=None:callback() diff --git a/imagepy/core/engine/tool.py b/imagepy/core/engine/tool.py index ec7054e6..1ebdc23b 100644 --- a/imagepy/core/engine/tool.py +++ b/imagepy/core/engine/tool.py @@ -23,7 +23,7 @@ def start(self): ips = IPy.get_ips() if not ips is None and not ips.tool is None: ips.tool = None - ips.update = True + ips.update() ToolsManager.set(self) def mouse_down(self, ips, x, y, btn, **key): pass diff --git a/imagepy/core/loader/loader.py b/imagepy/core/loader/loader.py index 2e8f0579..60870edd 100644 --- a/imagepy/core/loader/loader.py +++ b/imagepy/core/loader/loader.py @@ -242,7 +242,6 @@ def build_document(path): cont = f.read() f.close() DocumentManager.add(filename[:-3], cont) - print(docs) return docs if __name__ == "__main__": diff --git a/imagepy/core/myvi/canvas3d.py b/imagepy/core/myvi/canvas3d.py index c80cf8e0..4aaf7940 100644 --- a/imagepy/core/myvi/canvas3d.py +++ b/imagepy/core/myvi/canvas3d.py @@ -30,7 +30,7 @@ def __init__(self, parent, manager=None): self.Bind(wx.EVT_MOTION, self.OnMouseMotion) self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel) self.lastx, self.lasty = None, None - self.update = True + self.update() #print('init===========') def InitGL(self): @@ -106,7 +106,7 @@ def OnMouseWheel(self, evt): k = 0.9 if evt.GetWheelRotation()>0 else 1/0.9 self.manager.set_pers(l=self.manager.l*k) self.Refresh(False) - #self.update = True + #self.update() class Viewer3D(wx.Panel): def __init__( self, parent, manager=None): diff --git a/imagepy/core/roi/lineroi.py b/imagepy/core/roi/lineroi.py index 00df3238..184e9c8a 100644 --- a/imagepy/core/roi/lineroi.py +++ b/imagepy/core/roi/lineroi.py @@ -15,7 +15,7 @@ class LineRoi(ROI): dtype = 'line' def __init__(self, body=None): self.body = body if body!=None else [] - self.update = body!=None + self.dirty = body!=None self.infoupdate = body!=None self.box = [1000,1000,-1000,-1000] @@ -68,15 +68,6 @@ def draw(self, dc, f, **key): dc.DrawLines([f(*i) for i in line]) for i in line:dc.DrawCircle(f(*i),2) - ''' - def affine(self, m, o): - plg = LineRoi() - plg.body = affine(self.body, m, o) - plg.update = True - plg.infoupdate = True - return plg - ''' - def sketch(self, img, w=1, color=None): pen = paint.Paint() for i in self.body: diff --git a/imagepy/core/roi/pointroi.py b/imagepy/core/roi/pointroi.py index 10e017a9..57f953e8 100644 --- a/imagepy/core/roi/pointroi.py +++ b/imagepy/core/roi/pointroi.py @@ -57,15 +57,6 @@ def info(self, ips, cur): if cur==None:return x, y, z = self.body[cur] IPy.set_info('points:%.0f x:%.1f y:%.1f z:%.1f'%(len(self.body), x*k, y*k, z*k)) - - ''' - def affine(self, m, o): - plg = PointRoi() - plg.body = affine(self.body, m, o) - plg.update = True - plg.infoupdate = True - return plg - ''' def draw(self, dc, f, **key): diff --git a/imagepy/core/roi/polygonroi.py b/imagepy/core/roi/polygonroi.py index e8bb518b..5097ae25 100644 --- a/imagepy/core/roi/polygonroi.py +++ b/imagepy/core/roi/polygonroi.py @@ -33,10 +33,12 @@ class PolygonRoi(ROI): dtype = 'polygon' def __init__(self, body=None): self.body = body if body!=None else [] - self.update = body!=None + self.dirty = body!=None self.infoupdate = body!=None self.box = [1000,1000,-1000,-1000] + def update(self): self.dirty = True + def addpoint(self, p): self.buf[0].append(p) @@ -123,8 +125,8 @@ def topolygon(self):return self def affine(self, m, o): plg = PolygonRoi() plg.body = affine(self.body, m, o) - plg.update = True - plg.infoupdate = True + plg.update() + plg.infoupdate() return plg ''' diff --git a/imagepy/core/roi/rectangleroi.py b/imagepy/core/roi/rectangleroi.py index fc72ef7e..208a6de5 100644 --- a/imagepy/core/roi/rectangleroi.py +++ b/imagepy/core/roi/rectangleroi.py @@ -14,7 +14,7 @@ class RectangleRoi(ROI): dtype = 'rect' def __init__(self, l=0, t=0, r=0, b=0): self.body = [] - self.update = False + self.dirty = False self.lt, self.tp, self.rt, self.bm = l, t, r, b self.commit() @@ -29,12 +29,14 @@ def snap(self, x, y, z,lim): if abs(x-self.lt)0:ips.cur-=1 - ips.update = 'pix' + ips.update() if not self.pressed: return self.set_cursor(y, ips.cur, x) \ No newline at end of file diff --git a/imagepy/tools/Transform/rotate_tol.py b/imagepy/tools/Transform/rotate_tol.py index 2e501f20..3a1d616b 100644 --- a/imagepy/tools/Transform/rotate_tol.py +++ b/imagepy/tools/Transform/rotate_tol.py @@ -28,7 +28,7 @@ def mouse_move(self, ips, x, y, btn, **key): elif self.moving: self.para['ox'], self.para['oy'] = x, y self.plg.dialog.reset() - ips.update = True + ips.update() else: dx, dy = x-self.para['ox'], y-self.para['oy'] ang = np.arccos(dx/np.sqrt(dx**2+dy**2)) @@ -36,7 +36,7 @@ def mouse_move(self, ips, x, y, btn, **key): ang = int(ang/np.pi*180) self.para['ang'] = ang self.plg.dialog.reset() - ips.update = True + ips.update() class Plugin(Filter): """RotateTool class plugin derived from imagepy.core.engine.Filter""" @@ -59,7 +59,7 @@ def load(self, ips): self.para['oy'] = int((box[1]+box[3])/2) self.para['ox'] = int((box[0]+box[2])/2) ips.mark = self - ips.update = True + ips.update() ips.tool = RotateTool(self) return True @@ -68,7 +68,7 @@ def cancel(self, ips): ips.roi = self.bufroi ips.mark = None ips.tool = None - ips.update = 'pix' + ips.update() def ok(self, ips, para=None): Filter.ok(self, ips, para) diff --git a/imagepy/tools/Transform/scale_tol.py b/imagepy/tools/Transform/scale_tol.py index ddbd90e2..26883764 100644 --- a/imagepy/tools/Transform/scale_tol.py +++ b/imagepy/tools/Transform/scale_tol.py @@ -46,7 +46,7 @@ def mouse_move(self, ips, x, y, btn, **key): self.ox, self.oy = x, y self.plg.count() self.plg.dialog.reset() - ips.update = True + ips.update() elif self.moving != False: print("scale_tol.ScaleTool.mouse_move") if 'l' in self.moving:self.plg.lt = x @@ -55,7 +55,7 @@ def mouse_move(self, ips, x, y, btn, **key): if 'b' in self.moving:self.plg.bm = y self.plg.count() self.plg.dialog.reset() - ips.update = True + ips.update() class Plugin(Filter): modal = False @@ -98,7 +98,7 @@ def load(self, ips): self.para['kx'] = self.para['ky'] = 1 ips.mark = self - ips.update = True + ips.update() ips.tool = ScaleTool(self) return True @@ -124,7 +124,7 @@ def cancel(self, ips): ips.roi = self.bufroi ips.mark = None ips.tool = None - ips.update = 'pix' + ips.update() def run(self, ips, img, buf, para = None): if para == None: para = self.para @@ -139,4 +139,4 @@ def run(self, ips, img, buf, para = None): if self.para['msk'] and self.bufroi!=None:ips.roi = self.bufroi.affine(trans, offset) if self.para['img'] and not ips.get_msk('out') is None: buf[ips.get_msk('out')] = img[ips.get_msk('out')] - ips.update = True + ips.update() diff --git a/imagepy/ui/canvas.py b/imagepy/ui/canvas.py index 6610483f..4ab3c1eb 100644 --- a/imagepy/ui/canvas.py +++ b/imagepy/ui/canvas.py @@ -161,16 +161,16 @@ def on_idle(self, event): #print 'resized update' self.update(True) self.reInitBuffer = False - self.ips.update = False + self.ips.dirty = False print('resize') if self.ips.scrchanged: self.set_ips(self.ips) self.ips.scrchanged = False print('scr changed') - if self.ips.update != False: + if self.ips.dirty != False: #print 'normal update' - self.update(self.ips.update == 'pix') - self.ips.update = False + self.update(True) + self.ips.dirty = False print('update') def on_paint(self, event): @@ -260,14 +260,14 @@ def zoomout(self, x, y): #x,y = self.to_data_coor(x, y) self.scaleidx += 1 self.zoom(self.scales[self.scaleidx],x,y) - self.ips.update = 'pix' + self.ips.update() def zoomin(self, x, y): if self.scaleidx == 0:return #x,y = self.to_data_coor(x, y) self.scaleidx -= 1 self.zoom(self.scales[self.scaleidx], x,y) - self.ips.update = 'pix' + self.ips.update() def get_scale(self): return self.scales[self.scaleidx] diff --git a/imagepy/ui/canvasframe.py b/imagepy/ui/canvasframe.py index 82b0459f..04c1c9b7 100644 --- a/imagepy/ui/canvasframe.py +++ b/imagepy/ui/canvasframe.py @@ -70,7 +70,7 @@ def set_info(self, ips, resize=False): label='{}/{}; {} {}x{} pixels; {}; {} M'.format(ips.cur+1, ips.get_nslices(), stk if ips.get_nslices()>1 else '',ips.size[0], ips.size[1], ips.imgtype, round(ips.get_nbytes()/1024.0/1024.0, 2)) - self.txt_info.SetLabel(label) + if label != self.txt_info.GetLabel(): self.txt_info.SetLabel(label) @@ -101,7 +101,7 @@ def set_ips(self, ips): def on_scroll(self, event): self.ips.cur = self.page.GetThumbPosition() - self.ips.update = 'pix' + self.ips.update() self.canvas.on_idle(None) def close(self): diff --git a/imagepy/ui/plotwindow.py b/imagepy/ui/plotwindow.py index 2b45c6a2..1b384869 100644 --- a/imagepy/ui/plotwindow.py +++ b/imagepy/ui/plotwindow.py @@ -14,7 +14,7 @@ def __init__(self, parent): self.init_buf() self.data, self.extent = [], [0,0,1,1] self.set_title_label('Graph', 'X-unit', 'Y-unit') - self.update = False + self.dirty = False self.SetBackgroundColour( wx.Colour( 255, 255, 255 ) ) self.Bind(wx.EVT_SIZE, self.on_size) @@ -22,6 +22,8 @@ def __init__(self, parent): self.Bind(wx.EVT_IDLE, self.on_idle) self.Bind(wx.EVT_MOTION, self.on_move ) + def update(self):self.dirty = True + def init_buf(self): box = self.GetClientSize() self.width, self.height = box.width, box.height @@ -52,9 +54,9 @@ def on_move(self, event): def handle_move(self, x, y):pass def on_idle(self, event): - if self.update == True: + if self.dirty == True: self.draw() - self.update = False + self.dirty = False def set_title_label(self, title, labelx, labely): self.title, self.labelx, self.labely = title, labelx, labely @@ -67,7 +69,7 @@ def paint(self): top, bot = ext[:,3].max() + 0.1 * d, ext[:,1].min() - 0.1 * d if top == bot: top, bot = top+1, bot-1 self.extent = [ext[:,0].min(), bot, ext[:,2].max(), top] - self.update = True + self.update() def add_data(self, xs, ys=None, color=(0,0,255), lw=2): if ys is None: diff --git a/imagepy/ui/tablewindow.py b/imagepy/ui/tablewindow.py index 365625cc..ce6c07eb 100644 --- a/imagepy/ui/tablewindow.py +++ b/imagepy/ui/tablewindow.py @@ -219,7 +219,7 @@ def on_label(self, evt): cn = self.tps.data.columns[col] props.ix['ln'] = (props.ix['ln'].min()+1)%3 ''' - self.tps.update = True + self.tps.update() def set_handler(self, handle=None): @@ -265,14 +265,14 @@ def __del__(self): def on_idle(self, event): if not self.IsShown() or self.tps is None\ - or self.tps.update == False: return - if self.tps.update == 'shp': + or self.tps.dirty == False: return + if self.tps.dirty == 'shp': self.select() self.Reset() - if self.tps.update == True: + if self.tps.dirty == True: self.select() self.ForceRefresh() - self.tps.update = False + self.tps.dirty = False print('update') diff --git a/imagepy/ui/widgets/cmappanel.py b/imagepy/ui/widgets/cmappanel.py index 40e0bc07..4ce2c5ed 100644 --- a/imagepy/ui/widgets/cmappanel.py +++ b/imagepy/ui/widgets/cmappanel.py @@ -16,7 +16,7 @@ def __init__(self, parent ): self.cmap = np.vstack([np.arange(256)]*3).T.astype(np.uint8) self.idx = -1 self.his = None - self.update = False + self.dirty = False self.pts = [(0,0,0,0), (255,255,255,255)] self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.EVT_IDLE, self.on_idle) @@ -28,6 +28,8 @@ def __init__(self, parent ): self.Bind( wx.EVT_LEFT_DCLICK, self.on_rdc ) self.handle = self.handle_ + def update(self): self.dirty = True + def init_buf(self): box = self.GetClientSize() self.buffer = wx.Bitmap(box.width, box.height) @@ -44,12 +46,12 @@ def linear_color(cls, cs): def on_size(self, event): self.init_buf() - self.update = True + self.update() def on_idle(self, event): - if self.update == True: + if self.dirty == True: self.draw() - self.update = False + self.dirty = False def pick(self, x, y): if abs(y-10)>3:return -1 @@ -65,7 +67,7 @@ def on_ld(self, event): self.pts.append((x,)+tuple(self.cmap[x])) self.idx = len(self.pts)-1 self.cmap[:] = self.linear_color(self.pts) - self.update = True + self.update() self.handle() def on_lu(self, event): @@ -79,7 +81,7 @@ def on_rd(self, event): del self.pts[self.idx] self.idx = -1 self.cmap[:] = self.linear_color(self.pts) - self.update = True + self.update() self.handle() def on_rdc(self, event): @@ -94,7 +96,7 @@ def on_rdc(self, event): self.pts[self.idx] = (x,)+rst[:-1] self.idx=-1 self.cmap[:] = self.linear_color(self.pts) - self.update = True + self.update() dialog.Destroy() self.handle() @@ -112,7 +114,7 @@ def on_mv(self, event): cl = self.pts[self.idx][1:] self.pts[self.idx] = (x,)+cl self.cmap[:] = self.linear_color(self.pts) - self.update = True + self.update() self.handle() @@ -121,11 +123,11 @@ def on_paint(self, event): def set_hist(self, hist): self.hist = (hist*255/hist.max()).astype(np.uint8) - self.update = True + self.update() def set_pts(self, pts): self.x1, self.x2 = x1, x2 - self.update = True + self.update() def draw(self): ox, oy = self.offset diff --git a/imagepy/ui/widgets/curvepanel.py b/imagepy/ui/widgets/curvepanel.py index d9e66917..31beba6e 100644 --- a/imagepy/ui/widgets/curvepanel.py +++ b/imagepy/ui/widgets/curvepanel.py @@ -4,6 +4,7 @@ from scipy import interpolate if sys.version_info[0]==2:memoryview=np.getbuffer + class CurvePanel(wx.Panel): """ HistCanvas: diverid from wx.core.Panel """ def __init__(self, parent, hist=None, l=255): @@ -15,7 +16,7 @@ def __init__(self, parent, hist=None, l=255): self.l, self.k = l, l/255.0 self.idx = -1 self.set_hist(hist) - self.update = False + self.dirty = False self.pts = [(0,0), (255, 255)] wx.Panel.Bind(self, wx.EVT_SIZE, self.on_size) wx.Panel.Bind(self, wx.EVT_IDLE, self.on_idle) @@ -25,6 +26,8 @@ def __init__(self, parent, hist=None, l=255): wx.Panel.Bind(self, wx.EVT_MOTION, self.on_mv ) wx.Panel.Bind(self, wx.EVT_RIGHT_DOWN, self.on_rd ) + def update(self): self.dirty = True + @classmethod def lookup(cls, pts): x, y = np.array(pts).T @@ -38,12 +41,12 @@ def init_buf(self): def on_size(self, event): self.init_buf() - self.update = True + self.update() def on_idle(self, event): - if self.update == True: + if self.dirty == True: self.draw() - self.update = False + self.dirty = False def pick(self, x, y): dis = norm(np.array(self.pts)-(x,y), axis=1) @@ -57,7 +60,7 @@ def on_ld(self, event): if self.idx==-1: self.pts.append((x, 255-y)) self.idx = len(self.pts)-1 - self.update = True + self.update() self.handle(event) def on_lu(self, event): @@ -71,7 +74,7 @@ def on_rd(self, event): if not self.pts[self.idx][0] in (0, 255): del self.pts[self.idx] self.idx = -1 - self.update = True + self.update() self.handle(event) def on_mv(self, event): @@ -87,7 +90,7 @@ def on_mv(self, event): else: x = np.clip(x, 1, 254) y = np.clip(y, 0, 255) self.pts[self.idx] = (x, 255-y) - self.update = True + self.update() self.handle(event) @@ -99,11 +102,11 @@ def set_hist(self, hist): else: self.hist = (hist*self.l/hist.max()) self.logh = (np.log(self.hist+1.0))*(self.l/(np.log(self.l+1))) - self.update = True + self.update() def set_pts(self, pts): self.x1, self.x2 = x1, x2 - self.update = True + self.update() def draw(self): ox, oy = self.offset @@ -157,7 +160,7 @@ def SetValue(self, value=None): if not value is None: self.pts = value else: self.pts = [(0,0), (255, 255)] - self.update = True + self.update() def GetValue(self): return sorted(self.pts) diff --git a/imagepy/ui/widgets/histpanel.py b/imagepy/ui/widgets/histpanel.py index 20903f98..24fbd23e 100644 --- a/imagepy/ui/widgets/histpanel.py +++ b/imagepy/ui/widgets/histpanel.py @@ -10,25 +10,27 @@ def __init__(self, parent, hist=None): self.init_buf() self.hist = None if not hist is None: self.SetValue(hist) - self.update = False + self.dirty = False self.x1, self.x2 = 0, 255 self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.EVT_IDLE, self.on_idle) self.Bind(wx.EVT_PAINT, self.on_paint) self.Bind = lambda z, x:0 + def update(self): self.dirty = True + def init_buf(self): box = self.GetClientSize() self.buffer = wx.Bitmap(box.width, box.height) def on_size(self, event): self.init_buf() - self.update = True + self.update() def on_idle(self, event): - if self.update == True: + if self.dirty == True: self.draw() - self.update = False + self.dirty = False def on_paint(self, event): wx.BufferedPaintDC(self, self.buffer) @@ -36,11 +38,11 @@ def on_paint(self, event): def SetValue(self, hist): self.hist = (hist*80.0/hist.max()) self.logh = (np.log(self.hist+1.0))*(80/(np.log(81))) - self.update = True + self.update() def set_lim(self, x1, x2): self.x1, self.x2 = x1, x2 - self.update = True + self.update() def draw(self): # get client device context buffer diff --git a/imagepy/ui/widgets/viewport.py b/imagepy/ui/widgets/viewport.py index 2840b380..baf15cda 100644 --- a/imagepy/ui/widgets/viewport.py +++ b/imagepy/ui/widgets/viewport.py @@ -17,7 +17,7 @@ def __init__(self, parent): self.boxpan = None self.box = (0,0) self.ibox = (100,100) - self.update = False + self.dirty = False self.loc = (0,0) self.drag = False @@ -28,18 +28,20 @@ def __init__(self, parent): self.Bind(wx.EVT_LEFT_UP, self.on_lu) self.Bind(wx.EVT_MOTION, self.on_mv) + def update(self): self.dirty = True + def init_buf(self): self.box = box = self.GetClientSize() self.buffer = wx.Bitmap(box.width, box.height) def on_size(self, event): self.init_buf() - self.update = True + self.update() def on_idle(self, event): - if self.update == True: + if self.dirty == True: self.draw() - self.update = False + self.dirty = False def on_paint(self, event): wx.BufferedPaintDC(self, self.buffer) @@ -85,7 +87,7 @@ def set_img(self, img, size): def set_box(self, boximg, boxpan): self.boximg, self.boxpan = boximg, boxpan - self.update = True + self.update() def draw(self): # get client device context buffer diff --git a/imagepy/widgets/histogram/curve_wgt.py b/imagepy/widgets/histogram/curve_wgt.py index f53701d7..dd699d5d 100644 --- a/imagepy/widgets/histogram/curve_wgt.py +++ b/imagepy/widgets/histogram/curve_wgt.py @@ -60,7 +60,7 @@ def handle(self, event): lut = CurvePanel.lookup(self.curvepan.pts) lut = np.vstack((lut,lut,lut)).T ips.lut = lut - ips.update = 'pix' + ips.update() def on_apply(self, event): ips = IPy.get_ips() @@ -75,7 +75,7 @@ def on_clear(self, event): hist = ips.histogram() self.curvepan.set_hist(hist) ips.lut = ColorManager.get_lut() - ips.update = 'pix' + ips.update() def on_reset(self, event): self.curvepan.SetValue() diff --git a/imagepy/widgets/histogram/histogram_wgt.py b/imagepy/widgets/histogram/histogram_wgt.py index 08951d03..6da72c36 100644 --- a/imagepy/widgets/histogram/histogram_wgt.py +++ b/imagepy/widgets/histogram/histogram_wgt.py @@ -85,14 +85,14 @@ def on_cmap(self): if ips is None: return cmap = CMapPanel.linear_color(self.cmap.GetValue()) ips.lut = cmap - ips.update = 'pix' + ips.update() def on_cmapsel(self, event): ips = IPy.get_ips() if ips is None: return key = self.cmapsel.GetValue() ips.lut = ColorManager.get_lut(key) - ips.update = 'pix' + ips.update() # Virtual event handlers, overide them in your derived class def on_low( self, event ): @@ -104,7 +104,7 @@ def on_low( self, event ): lim1 = 1.0 * (self.sli_low.GetValue() - self.range[0])/(self.range[1]-self.range[0]) lim2 = 1.0 * (self.sli_high.GetValue() - self.range[0])/(self.range[1]-self.range[0]) self.histpan.set_lim(lim1*255, lim2*255) - ips.update = 'pix' + ips.update() def on_high( self, event ): ips = IPy.get_ips() @@ -115,7 +115,7 @@ def on_high( self, event ): lim1 = 1.0 * (self.sli_low.GetValue() - self.range[0])/(self.range[1]-self.range[0]) lim2 = 1.0 * (self.sli_high.GetValue() - self.range[0])/(self.range[1]-self.range[0]) self.histpan.set_lim(lim1*255, lim2*255) - ips.update = 'pix' + ips.update() def on_8bit( self, event ): ips = IPy.get_ips() @@ -128,7 +128,7 @@ def on_8bit( self, event ): self.sli_low.SetValue(0) self.sli_high.SetValue(255) self.histpan.set_lim(0,255) - ips.update = 'pix' + ips.update() def on_minmax( self, event ): ips = IPy.get_ips() @@ -142,7 +142,7 @@ def on_minmax( self, event ): self.sli_low.SetValue(minv) self.sli_high.SetValue(maxv) self.histpan.set_lim(0,255) - ips.update = 'pix' + ips.update() def on_slice( self, event ): ips = IPy.get_ips() diff --git a/imagepy/widgets/navigator/navigator_wgt.py b/imagepy/widgets/navigator/navigator_wgt.py index d1aa615d..e7ccc2e3 100644 --- a/imagepy/widgets/navigator/navigator_wgt.py +++ b/imagepy/widgets/navigator/navigator_wgt.py @@ -78,14 +78,14 @@ def on_zoom(self, event): x, y = win.canvas.to_data_coor(c/2, d/2) win.canvas.scaleidx = self.slider.GetValue() win.canvas.zoom(k, x, y) - win.canvas.ips.update = 'pix' + win.canvas.ips.update() self.viewport.set_box(win.canvas.imgbox, win.canvas.box) def on_fit(self, event): win = IPy.get_window() if win is None: return win.canvas.self_fit() - win.canvas.ips.update = 'pix' + win.canvas.ips.update() self.slider.SetValue(win.canvas.scaleidx) k = self.scales[self.slider.GetValue()] self.label.SetLabel('%.2f%%'%(k*100)) @@ -98,7 +98,7 @@ def on_one(self, event): x, y = win.canvas.to_data_coor(c/2, d/2) win.canvas.scaleidx = self.scales.index(1) win.canvas.zoom(1, x, y) - win.canvas.ips.update = 'pix' + win.canvas.ips.update() self.slider.SetValue(win.canvas.scaleidx) self.label.SetLabel('%.2f%%'%100) self.viewport.set_box(win.canvas.imgbox, win.canvas.box) @@ -109,5 +109,5 @@ def on_handle(self): x, y = self.viewport.GetValue() print(x, y) win.canvas.center(x, y) - win.canvas.ips.update = 'pix' + win.canvas.ips.update() self.viewport.set_box(win.canvas.imgbox, win.canvas.box) \ No newline at end of file From f7891f814bf784758a0ef69f48834fad79ced6e6 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 27 Jan 2019 23:51:41 +0800 Subject: [PATCH 088/343] utf-8, wgtref --- imagepy/core/engine/widget.py | 2 +- .../Contribute/Contributions/Demo Repo.md | 20 +++++++++---------- .../menus/Plugins/Contribute/pmanager_wgt.py | 4 ++-- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/imagepy/core/engine/widget.py b/imagepy/core/engine/widget.py index 707f5dfe..933658be 100644 --- a/imagepy/core/engine/widget.py +++ b/imagepy/core/engine/widget.py @@ -17,7 +17,7 @@ def __call__(self):return self def start(self): #if not WidgetsManager.getref(self.title) is None: return pan = self.pan(IPy.curapp) - #WidgetsManager.addref(pan) + WidgetsManager.addref(pan) IPy.curapp.auimgr.AddPane(pan, aui.AuiPaneInfo().Caption(self.title).Left().Layer( 15 ).PinButton( True ) .Float().Resizable().FloatingSize( wx.DefaultSize ).Dockable(IPy.uimode()=='ipy').DestroyOnClose()) diff --git a/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md b/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md index 8bcad9df..bdc7417b 100644 --- a/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md +++ b/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md @@ -14,6 +14,12 @@ +## 安装 + +ImagePy菜单:**Plugins > Manager > Plugins Manager** 在输入框内输入demo进行查询,选中Demo Plugin,点击Install,完成后菜单栏出现Demo菜单,工具栏会加入Demo工具,组件栏也会加入Demo组件。 + + + ## 基础预备 1. 什么是插件 @@ -65,13 +71,13 @@ **Free: 没有任何依赖的插件** -1. Parameter Demo: 参数对话框生成演示 -2. New Image Demo: 新建图像 +1. New Image Demo: 新建图像 +2. About Demo:关于对话框 +3. Close Demo:退出程序 **Tool: 鼠标交互工具** 1. Painter Demo:画笔工具 -2. Pixel Inspector:像素查看器 **Widget: 桌面小部件** @@ -110,11 +116,3 @@ 1. 用户友好性 2. 开发者友好性 3. 及时沟通 - - - -## 如何贡献 - -1. 宣传推广 -2. 编写插件 -3. 完善文档 \ No newline at end of file diff --git a/imagepy/menus/Plugins/Contribute/pmanager_wgt.py b/imagepy/menus/Plugins/Contribute/pmanager_wgt.py index 5d25867b..d5d9c2a6 100644 --- a/imagepy/menus/Plugins/Contribute/pmanager_wgt.py +++ b/imagepy/menus/Plugins/Contribute/pmanager_wgt.py @@ -33,7 +33,7 @@ def refresh(self): self.SetItemCount(len(self.data)) def parse(path): - f = open(path) + f = open(path, encoding='utf-8') body = {'file':path} try: for i in range(13): @@ -131,7 +131,7 @@ def on_search( self, event ): self.lst_plgs.Refresh() def on_run(self, event): - f = open(self.buf[event.GetIndex()][-1]['file']) + f = open(self.buf[event.GetIndex()][-1]['file'], encoding='utf-8') cont = f.read() f.close() cont = '\n'.join([i.strip() for i in cont.split('\n')]) From 4cfabb3b8e63c5aec1292ad443912534c6683c0a Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 30 Jan 2019 02:13:14 +0800 Subject: [PATCH 089/343] table --- imagepy/core/util/tableio.py | 2 +- imagepy/menus/Table/Statistic/__init__.py | 2 +- .../menus/Table/Statistic/frequency_plgs.py | 42 +++++++++++++++++++ imagepy/ui/tablewindow.py | 1 - 4 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 imagepy/menus/Table/Statistic/frequency_plgs.py diff --git a/imagepy/core/util/tableio.py b/imagepy/core/util/tableio.py index 2b67ea6f..92012115 100644 --- a/imagepy/core/util/tableio.py +++ b/imagepy/core/util/tableio.py @@ -30,7 +30,7 @@ def show(self): return IPy.getpath('Save..', filt, 'save', self.para) #process - def run(self, tps, data, snap, para = None): + def run(self, tps, snap, data, para = None): fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) write = WriterManager.get(fe[1:], tag='tab') diff --git a/imagepy/menus/Table/Statistic/__init__.py b/imagepy/menus/Table/Statistic/__init__.py index fa4b62c1..a58cdeb2 100644 --- a/imagepy/menus/Table/Statistic/__init__.py +++ b/imagepy/menus/Table/Statistic/__init__.py @@ -1 +1 @@ -catlog = ['statistic_plgs', '-', 'sort_plg'] \ No newline at end of file +catlog = ['statistic_plgs', '-', 'frequency_plgs', '-', 'sort_plg'] \ No newline at end of file diff --git a/imagepy/menus/Table/Statistic/frequency_plgs.py b/imagepy/menus/Table/Statistic/frequency_plgs.py new file mode 100644 index 00000000..50453f20 --- /dev/null +++ b/imagepy/menus/Table/Statistic/frequency_plgs.py @@ -0,0 +1,42 @@ +from imagepy.core.engine import Table +import pandas as pd +import numpy as np +from imagepy import IPy + +class Count(Table): + title = 'Values Frequency' + para = {'cn':[]} + view = [('fields', 'cn', 'fields to count')] + + def run(self, tps, snap, data, para=None): + for i in para['cn']: + df = pd.DataFrame(data[i].value_counts()) + df.columns = ['%s-vf'%i] + IPy.show_table(df, '%s-values-frequency'%i) + +class Frequency(Table): + title = 'Table Bins Frequency' + + para = {'bins':10, 'max':1, 'min':0, 'auto':True, 'count':True, 'fre':False, 'weight':False, 'cn':[]} + + view = [(int, 'bins', (3,1024), 0, 'bins', 'n'), + (bool, 'auto', 'auto scale'), + (int, 'min', (-1e8, 1e8), 5, 'min range', ''), + (int, 'max', (-1e8, 1e8), 5, 'max range', ''), + ('fields', 'cn', 'fields to count'), + (bool, 'count', 'count times'), + (bool, 'fre', 'count frequency'), + (bool, 'weight', 'count weight')] + + def run(self, tps, snap, data, para=None): + rg = None if para['auto'] else (para['min'], para['max']) + for i in para['cn']: + hist, bins = np.histogram(data[i], para['bins'], rg) + vs = {'bins':bins[:-1]} + if para['count']:vs['count'] = hist + if para['fre']:vs['frequency'] = hist/hist.sum() + if para['weight']:vs['weight'] = np.histogram(data[i], bins, rg, weights=data[i])[0] + df = pd.DataFrame(vs, columns = [i for i in ['bins', 'count', 'frequency', 'weight'] if i in vs]) + IPy.show_table(df, '%s-frequency'%i) + +plgs = [Count, Frequency] \ No newline at end of file diff --git a/imagepy/ui/tablewindow.py b/imagepy/ui/tablewindow.py index ce6c07eb..99effe42 100644 --- a/imagepy/ui/tablewindow.py +++ b/imagepy/ui/tablewindow.py @@ -251,7 +251,6 @@ def select(self): self.Bind(Grid.EVT_GRID_RANGE_SELECT, None) self.ClearSelection() print('select grid') - print(self.tps.rowmsk, self.tps.colmsk) for i in self.tps.data.index.get_indexer(self.tps.rowmsk): print(i) self.SelectRow(i, True) From 9cd6850ecb93382be5ab0dfdd2a35bd71fff6529 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 30 Jan 2019 16:16:36 +0800 Subject: [PATCH 090/343] table 3d ball --- imagepy/core/myvi/canvas3d.py | 2 +- imagepy/menus/Kit3D/Viewer 3D/__init__.py | 2 +- .../menus/Kit3D/Viewer 3D/tablepoints_plg.py | 44 +++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py diff --git a/imagepy/core/myvi/canvas3d.py b/imagepy/core/myvi/canvas3d.py index 4aaf7940..8890c401 100644 --- a/imagepy/core/myvi/canvas3d.py +++ b/imagepy/core/myvi/canvas3d.py @@ -30,7 +30,7 @@ def __init__(self, parent, manager=None): self.Bind(wx.EVT_MOTION, self.OnMouseMotion) self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel) self.lastx, self.lasty = None, None - self.update() + #self.update() #print('init===========') def InitGL(self): diff --git a/imagepy/menus/Kit3D/Viewer 3D/__init__.py b/imagepy/menus/Kit3D/Viewer 3D/__init__.py index 9bc41dd0..051f9c97 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/__init__.py +++ b/imagepy/menus/Kit3D/Viewer 3D/__init__.py @@ -1 +1 @@ -catlog = ['surface_plgs', 'colorpts_plg', '-', '2DSurface Demo.mc'] \ No newline at end of file +catlog = ['surface_plgs', '-', 'colorpts_plg', 'tablepoints_plg', '-', '2DSurface Demo.mc'] \ No newline at end of file diff --git a/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py b/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py new file mode 100644 index 00000000..3112fa7c --- /dev/null +++ b/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py @@ -0,0 +1,44 @@ +from imagepy.core.engine import Table +from imagepy.core.manager import ColorManager +from imagepy.core import myvi +import numpy as np +from imagepy import IPy + +class Plugin(Table): + title = 'Table Point Cloud' + + para = {'x':None, 'y':None, 'z':None, 'r':5, 'rs':None, 'c':(0,0,255), + 'cs':None, 'cm':None, 'cube':False} + + view = [('field', 'x', 'x data', ''), + ('field', 'y', 'y data', ''), + ('field', 'z', 'z data', ''), + (float, 'r', (0, 1024), 3, 'radius', 'pix'), + ('lab', 'lab', '== if set the radius would becom factor =='), + ('field', 'rs', 'radius', 'column'), + ('color', 'c', 'color', ''), + ('lab', 'lab', '== if set the color upon would disable =='), + ('field', 'cs', 'color', 'column'), + ('cmap', 'cm', 'color map when color column is set'), + (bool, 'cube', 'draw outline cube')] + + def load(self, para): + self.frame = myvi.Frame3D.figure(IPy.curapp, title='3D Canvas') + return True + + def run(self, tps, snap, data, para = None): + pts = np.array(data[[para['x'], para['y'], para['z']]]) + rs = data[para['rs']]*para['r'] if para['rs'] != 'None' else [para['r']]*len(pts) + cm = ColorManager.get_lut(para['cm'])/255.0 + clip = lambda x : (x-x.min())/(x.max()-x.min())*255 + if para['cs'] == 'None': cs = [np.array(para['c'])/255.0]*len(pts) + else: cs = cm[clip(data[para['cs']]).astype(np.uint8)] + vts, fs, ns, cs = myvi.build_balls(pts.astype(np.float32), list(rs), cs) + self.frame.viewer.add_surf_asyn(para['ball'], vts, fs, ns, cs) + if para['cube']: + p1 = data[[para['x'], para['y'], para['z']]].min(axis=0) + p2 = data[[para['x'], para['y'], para['z']]].max(axis=0) + vts, fs, ns, cs = myvi.build_cube(p1, p2) + self.frame.viewer.add_surf_asyn('cube', vts, fs, ns, cs, mode='grid') + self.frame.Raise() + self.frame = None \ No newline at end of file From 7707a4ca7aa212d6739cd430687a7ff18eaf5a52 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 30 Jan 2019 16:46:30 +0800 Subject: [PATCH 091/343] ball --- imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py b/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py index 3112fa7c..86fe5778 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py +++ b/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py @@ -34,7 +34,7 @@ def run(self, tps, snap, data, para = None): if para['cs'] == 'None': cs = [np.array(para['c'])/255.0]*len(pts) else: cs = cm[clip(data[para['cs']]).astype(np.uint8)] vts, fs, ns, cs = myvi.build_balls(pts.astype(np.float32), list(rs), cs) - self.frame.viewer.add_surf_asyn(para['ball'], vts, fs, ns, cs) + self.frame.viewer.add_surf_asyn('ball', vts, fs, ns, cs) if para['cube']: p1 = data[[para['x'], para['y'], para['z']]].min(axis=0) p2 = data[[para['x'], para['y'], para['z']]].max(axis=0) From c5ad804722d83664ab886008980362bea6877e6c Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 1 Feb 2019 21:03:48 +0800 Subject: [PATCH 092/343] read me, add contribute --- README.md | 62 ++++----- .../Contribute/Contributions/Demo Repo.md | 123 ++++++++++-------- .../Contribute/Contributions/OpenCV.md | 2 +- 3 files changed, 97 insertions(+), 90 deletions(-) diff --git a/README.md b/README.md index 06bde9ef..d1e491d0 100644 --- a/README.md +++ b/README.md @@ -18,33 +18,40 @@ ImagePy: Our long-term goal of this project is to be used as ImageJ + SPSS (although not achieved yet) +## Installation -## Citation: -[ImagePy: an open-source, Python-based and platform-independent software package for bioimage analysis](https://academic.oup.com/bioinformatics/article/34/18/3238/4989871) - -## Forum - -ImagePy is a community partner of forum.image.sc, Anything about the usage and development of ImagePy could be discussed in https://forum.image.sc. - -# Installation - -__OS support:windows, linux, mac, with python3.4__ +__OS support:windows, linux, mac, with python3.x__ 1. ImagePy is a ui framework based on wxpython, which can not be installed with pip on Linux. You need download [the whl according to your Linux system](https://wxpython.org/pages/downloads/). - 2. On Linux and Mac, there may be permission denied promblem, for ImagePy will write some config information, so please start with sudo. If you install with pip, please add \--user parameter like this: pip install --user imagepy - 3. If you install ImagePy in an Anaconda virtual environment, you may get a error when starting like this: This program needs access to the screen. Please run with a Framework build of python, and only when you are logged in on the main display, if so, please start with pythonw -m imagepy. +## Citation: +[ImagePy: an open-source, Python-based and platform-independent software package for bioimage analysis](https://academic.oup.com/bioinformatics/article/34/18/3238/4989871) + +## Forum + +ImagePy is a community partner of forum.image.sc, Anything about the usage and development of ImagePy could be discussed in https://forum.image.sc. + + + +## Contribute + +**Contribute Manual:** All markdown file under [doc folder](https://github.com/Image-Py/imagepy/tree/master/imagepy/doc) be parsed as manual. Plugins and manual are paired by plugins's title and manual's file name. We can browse document from the parameter dialog's Help button. We need more manual contributors, just pull request markdown file [here](https://github.com/Image-Py/imagepy/tree/master/imagepy/doc). + +**Contribute Plugins:** Here is a [demo plugin](https://github.com/Image-Py/demoplugin) repositories with document to show how to write plugins and publish on ImagePy. You are wellcom and feel free to contact with us if you need help. + +**Improve Main Framework:** Just fork ImagePy, then give Pull Request. But if you want to add some new feature, Please have a issue with us firstly. + ## Basic operations: ImagePy has a very rich set of features, and here, we use a specific example to show you a glimpse of the capacity of ImagePy. We choose the official coin split of scikit-image, since this example is simple and comprehensive. @@ -60,8 +67,7 @@ _PS: ImagePy supports bmp, jpg, png, gif, tif and other commonly used file forma ### Filtering & Segmentation -`menu:Process -> Hydrology -> Up And Down Watershed` -Here, a composite filter is selected to perform sobel gradient extraction on the image, and then the upper and lower thresholds are used as the mark, and finally we watershed on the gradient map. +`menu:Process -> Hydrology -> Up And Down Watershed` Here, a composite filter is selected to perform sobel gradient extraction on the image, and then the upper and lower thresholds are used as the mark, and finally we watershed on the gradient map. Filtering and segmentation are the crucial skills in the image processing toolkit, and are the key to the success or failure of the final measurement. Segmentation methods such as adaptive thresholds, watersheds and others are also supported. @@ -75,9 +81,7 @@ Segmentation methods such as adaptive thresholds, watersheds and others are also ### Binarization -`menu:Process -> Binary -> Binary Fill Holes` - -After the segmentation, we obtained a relatively clean mask image, but there is still some hollowing out, as well as some impurities, which will interfere with counting and measurement. +`menu:Process -> Binary -> Binary Fill Holes` After the segmentation, we obtained a relatively clean mask image, but there is still some hollowing out, as well as some impurities, which will interfere with counting and measurement. _ImagePy supports binary operations such as erode, dilate, opening and closing, as well as skeletonization, central axis extraction, and distance transformation._ ![newdoc06](http://idoc.imagepy.org/imgs/newdoc06.png) @@ -85,9 +89,7 @@ _ImagePy supports binary operations such as erode, dilate, opening and closing, ### Geometry filtering -`menu:Analysis -> Region Analysis -> Geometry Filter` - -ImagePy can perform geometric filtering based on :__the area, the perimeter, the topology, the solidity, the eccentricity__ and other parameters. You can also use multiple conditions for filtering. Each number can be positive|negative, which indicates the kept object will have the corresponding parameter greater|smaller than the value respectively. The kept objects will be set to the front color, the rejected ones will be set to the back color. In this demo, the back color is set to 100 in order to see which ones are filtered out. Once satisfied with the result, set the back color to 0 to reject them. In addition, ImagePy also supports gray density filtering, color filtering, color clustering and other functions. +`menu:Analysis -> Region Analysis -> Geometry Filter` ImagePy can perform geometric filtering based on :__the area, the perimeter, the topology, the solidity, the eccentricity__ and other parameters. You can also use multiple conditions for filtering. Each number can be positive|negative, which indicates the kept object will have the corresponding parameter greater|smaller than the value respectively. The kept objects will be set to the front color, the rejected ones will be set to the back color. In this demo, the back color is set to 100 in order to see which ones are filtered out. Once satisfied with the result, set the back color to 0 to reject them. In addition, ImagePy also supports gray density filtering, color filtering, color clustering and other functions. ![newdoc07](http://idoc.imagepy.org/imgs/newdoc07.png)
Geometry filtering (the area is over-chosen to emphasize the distinction)

@@ -95,8 +97,7 @@ ImagePy can perform geometric filtering based on :__the area, the perimeter, the ### Geometry Analysis -`menu:Process -> Region Analysis -> Geometry Analysis` -Count the area and analyze the parameters. By choosing the `cov` option, ImagePy will fit each area with an ellipse calculated via the covariance. +`menu:Process -> Region Analysis -> Geometry Analysis` Count the area and analyze the parameters. By choosing the `cov` option, ImagePy will fit each area with an ellipse calculated via the covariance. The parameters such as area, perimeter, eccentricity, and solidity shown in the previous step are calculated here. In fact, the filtering of the previous step is a downstream analysis of this one. ![newdoc08](http://idoc.imagepy.org/imgs/newdoc08.png) @@ -108,9 +109,7 @@ The parameters such as area, perimeter, eccentricity, and solidity shown in the ### Sort Table by area -`menu:Table -> Statistic -> Table Sort By Key` - -Select the major key as area, and select descend. The table will be sorted in descending order of area. A table is another important piece of data other than an image. In a sense, many times we need to get the required information on the image and then post-process the data in the form of a table. ImagePy supports table I/O (xls, xlsx, csv), filtering, slicing, statistical analysis, sorting and more. (Right click on the column header to set the text color, decimal precision, line style, etc.) +`menu:Table -> Statistic -> Table Sort By Key` Select the major key as area, and select descend. The table will be sorted in descending order of area. A table is another important piece of data other than an image. In a sense, many times we need to get the required information on the image and then post-process the data in the form of a table. ImagePy supports table I/O (xls, xlsx, csv), filtering, slicing, statistical analysis, sorting and more. (Right click on the column header to set the text color, decimal precision, line style, etc.) ![newdoc10](http://idoc.imagepy.org/imgs/newdoc10.png)
Table

@@ -118,9 +117,7 @@ Select the major key as area, and select descend. The table will be sorted in de ### Charts -`menu:Table -> Chart -> Hist Chart` - -From tabular data, we often need to draw a graph. Here, we plot the histograms of the area and the perimeter columns. ImagePy's tables can be used to draw common charts such as line charts, pie charts, histograms, and scatter plots (matplotlib-based). The chart comes with zooming, moving and other functions. The table can also be saved as an image. +`menu:Table -> Chart -> Hist Chart` From tabular data, we often need to draw a graph. Here, we plot the histograms of the area and the perimeter columns. ImagePy's tables can be used to draw common charts such as line charts, pie charts, histograms, and scatter plots (matplotlib-based). The chart comes with zooming, moving and other functions. The table can also be saved as an image. ![newdoc11](http://idoc.imagepy.org/imgs/newdoc11.png)
Histograms

@@ -128,10 +125,7 @@ From tabular data, we often need to draw a graph. Here, we plot the histograms o ### 3D chart -`menu:Kit3D -> Viewer 3D -> 2D Surface` - - -Surface reconstruction of the image. This image shows the three reconstructed results including, sobel gradient map, high threshold and low threshold. It shows how the Up And Down Watershed works: +`menu:Kit3D -> Viewer 3D -> 2D Surface` Surface reconstruction of the image. This image shows the three reconstructed results including, sobel gradient map, high threshold and low threshold. It shows how the Up And Down Watershed works: - calculate the gradient. - mark the coin and background through the high and low thresholds, - simulate the rising water on the dem diagram to form the segmentation. @@ -145,9 +139,7 @@ ImagePy can perform 3D filtering of images, 3D skeletons, 3D topological analysi ### Macro recording and execution -`menu:Window -> Develop Tool Suite` - -Macro recorder is shown in the develop tool panel. We have manually completed an image segmentation. However, batch processing more than 10 images can be tedious. So, assuming that these steps are highly repeatable and robust for dealing with such problems, we can record a macro to combine several processes into a one-click program. The macro recorder is similar to a radio recorder. When it is turned on, each step of the operation will be recorded. We can click the pause button to stop recording, then click the play button to execute. When the macro is running, the recorded commands will be executed sequentially, therefore achieving simplicity and reproducibility. +`menu:Window -> Develop Tool Suite` Macro recorder is shown in the develop tool panel. We have manually completed an image segmentation. However, batch processing more than 10 images can be tedious. So, assuming that these steps are highly repeatable and robust for dealing with such problems, we can record a macro to combine several processes into a one-click program. The macro recorder is similar to a radio recorder. When it is turned on, each step of the operation will be recorded. We can click the pause button to stop recording, then click the play button to execute. When the macro is running, the recorded commands will be executed sequentially, therefore achieving simplicity and reproducibility. Macros are saved into .mc files. drag and drop the file to the status bar at the bottom of ImagePy, the macro will be executed automatically. we can also copy the .mc file to the submenu of the menus under the ImagePy file directory. When ImagePy is started, the macro file will be parsed into a menu item at the corresponding location. By clicking the menu, the macro will also be executed. diff --git a/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md b/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md index bdc7417b..b400ab3d 100644 --- a/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md +++ b/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md @@ -12,107 +12,122 @@ **Description:** a friendly develop tutorial. +*这是一个ImagePy插件项目,里面覆盖了各类插件的编写方法和用法,并配有详细的文档,ImagePy的插件开发者可以以此为参考* + ## 安装 -ImagePy菜单:**Plugins > Manager > Plugins Manager** 在输入框内输入demo进行查询,选中Demo Plugin,点击Install,完成后菜单栏出现Demo菜单,工具栏会加入Demo工具,组件栏也会加入Demo组件。 +ImagePy菜单:`Plugins > Manager > Plugins Manager` 在输入框内输入demo进行查询,选中Demo Plugin,点击`Install`,完成后菜单栏出现Demo菜单,工具栏会加入Demo工具,组件栏也会加入Demo组件。 +![06](http://idoc.imagepy.org/demoplugin/06.png) +
Install DemoPlugin

+## [基础预备](doc/start.md) -## 基础预备 +**[从这里开始](doc/start.md)** -1. 什么是插件 +1. [什么是插件](doc/start.md#什么是插件) +2. [Hello World(第一个插件)](doc/start.md#Hello-World) +3. [Who Are You(带有交互)](doc/start.md#Who-Are-You) +4. [Questionnaire(参数对话框详解)](doc/start.md#Questionnaire) +5. [一个文件内实现多个插件](doc/start.md#一个文件内实现多个插件) -2. Hello World (第一个插件) -3. Who Are You (带有交互) -4. Questionnaire (参数对话框详解) +## 插件开发 -5. 一个文件内实现多个插件 +**[Markdown: 文档提示](doc/markdown.md)** - +1. [Markdown Demo](doc/markdown.md#MarkDown-Demo) -## 插件开发 +**[Macros: 用宏串联已有功能](doc/macros.md#Macros)** + +1. [高斯模糊再求反](doc/macros.md#高斯模糊再求反) +2. [Coins Segment Macros:硬币分割](doc/macros.md#分割硬币) + +**[Workflow: 可交互的宏](doc/workflow.md)** -**Markdown: 文档提示** +1. [Coins Segment Workflow: 按照指引进行硬币分割](doc/workflow.md#硬币分割工作流) -1. MarkDown +**[Filter: 二维图像滤波器](doc/filter.md)** -**Macros: 用宏串联已有功能** +1. [Invert Demo:无参数的插件](doc/filter.md#Invert) +2. [Gaussian Demo:带有参数的插件](doc/filter.md#Gaussian) +3. [Filter 的运行机制](doc/filter.md#Filter-运行机制) -1. Macros Recorder:操作录制 -2. Coins Segment Macros:硬币分割 +**[Simple: 图像整体操作](doc/simple.md)** -**Workflow: 可交互的宏** +1. [Gaussian 3D Demo:三维滤波](doc/simple.md#Gaussian3D) +2. [Red Lut Demo:设定索引色](doc/simple.md#SetLUT) +3. [ROI Inflate Demo:操作ROI](doc/simple.md#Inflate-ROI) +4. [Unit Demo: 设置比例尺及单位](doc/simple.md#SEt-Scale-And-Unit) +5. [Draw Mark Demo: 设置Overlay Mark](doc/simple.md#Mark) +6. [Simple 的运行机制](doc/simple.md#Simple-运行机制) -1. Coins Segment Workflow: 按照指引进行硬币分割 +**[Table: 表格数据](doc/table.md)** -**Filter: 二维图像滤波器** +1. [Generate Table Demo:数据表生成](doc/table.md#生成成绩单) +2. [Sort By Key Demo:排序](doc/table.md#根据某科成绩排序) +3. [Table Plot Demo:绘图](doc/table.md#绘制柱状图) +4. [Table 运行机制](doc/table.md#Table-运行机制) -1. Invert Demo:无参数的插件 -2. Gaussian Demo:带有参数的插件 +**[Free: 没有任何依赖的插件](doc/free.md)** -**Simple: 图像整体操作** +1. [New Image Demo: 创建图像](doc/free.md#创建图像) +2. [About Demo:关于对话框](doc/free.md#关于对话框) +3. [Close Demo:退出软件](doc/free.md#退出软件) +4. [Free 的运行机制](doc/free.md#Free-的运行机制) -1. Red Lut Demo:操作索引表 +**[Tool: 鼠标交互工具](doc/tool.md)** -2. ROI Inflate Demo:操作ROI -3. Unit Demo: 设置比例尺及单位 -4. Draw Mark Demo: 设置Overlay Mark -5. Gaussian 3D Demo:三维滤波 +1. [Painter Demo:画笔工具](doc/tool.md#画笔工具) +2. [Tool的运行机制](doc/tool.md#Tool-的运行机制) -**Table: 表格数据** +**[Widget: 桌面小部件](doc/widget.md)** -1. Generate Table Demo:数据表生成 -2. Sort By Key Demo:排序 -3. Table Plot Demo:绘图 +1. [Widget Demo:桌面小部件演示](doc/widget.md#桌面组件演示) +2. [Tool的运行机制](doc/widget.md#widget-的运行机制) -**Free: 没有任何依赖的插件** -1. New Image Demo: 新建图像 -2. About Demo:关于对话框 -3. Close Demo:退出程序 -**Tool: 鼠标交互工具** +## [插件项目发布](doc/publish.md) -1. Painter Demo:画笔工具 +**[插件的组织方式](doc/publish.md#功能组织)** -**Widget: 桌面小部件** +1. [功能划分](doc/publish.md#功能组织) +2. [顺序设定](doc/publish.md#功能组织) -1. Widget Demo:桌面小部件演示 +**[插件项目创建](doc/publish.md#插件项目创建)** +1. [创建插件项目仓库](doc/publish.md#插件项目创建) +2. [编写requirements](doc/publish.md#插件项目创建) +3. [编写readme](doc/publish.md#插件项目创建) +4. [插件的安装](doc/publish.md#插件项目创建) +**[发布到 ImagePy](doc/publish.md#发布到-ImagePy)** -## 插件项目发布 +1. [给ImagePy发Pull Request](doc/publish.md#发布到-ImagePy) +2. [关于顶级菜单](doc/publish.md#发布到-ImagePy) -**插件的组织方式** -1. 功能划分 -2. 顺序设定 -**插件项目的发布** +## [文档编写](doc/document.md) -1. 编写requirements -2. 为Readme加入的插件头信息 -3. 发布到ImagePy +**[编写操作手册](doc/document.md#编写操作手册)** -**插件的安装与管理** +**[查阅操作手册](doc/document.md#查阅操作手册)** -1. 通过连接安装插件 -2. 插件管理器 +## [注意事项](doc/attention.md#注意事项) -## 文档编写 +**[用户友好性](doc/attention.md#用户友好性)** -为插件编写操作手册 +**[开发者友好性](doc/attention.md#开发者友好性)** +**[及时沟通](doc/attention.md#及时沟通)** -## 注意事项 -1. 用户友好性 -2. 开发者友好性 -3. 及时沟通 +**本篇文档相对系统的介绍了ImagePy的插件开发,但是依然无法详尽,关于更多ImagePy使用,开发上的问题请在[forum.Image.sc](https://forum.image.sc/)行进行讨论** \ No newline at end of file diff --git a/imagepy/menus/Plugins/Contribute/Contributions/OpenCV.md b/imagepy/menus/Plugins/Contribute/Contributions/OpenCV.md index 3ef054b2..c85b7ad8 100644 --- a/imagepy/menus/Plugins/Contribute/Contributions/OpenCV.md +++ b/imagepy/menus/Plugins/Contribute/Contributions/OpenCV.md @@ -2,7 +2,7 @@ **Path:** https://github.com/Image-Py/opencv-plgs -**Version:** 0.11 +**Version:** 0.1 **Author:** YXDragon From 69dac8235b98c045f1b878aac5adc923fd537c8c Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 6 Feb 2019 08:55:45 +0800 Subject: [PATCH 093/343] report --- README.md | 2 - imagepy/core/engine/__init__.py | 3 +- imagepy/core/engine/report.py | 65 ++++++++++++++++++++ imagepy/core/loader/loader.py | 15 +++-- imagepy/core/util/xlreport.py | 105 ++++++++++++++++++++++++++++++++ imagepy/ui/panelconfig.py | 9 --- imagepy/ui/propertygrid.py | 91 +++++++++++++++++++++++++++ 7 files changed, 273 insertions(+), 17 deletions(-) create mode 100644 imagepy/core/engine/report.py create mode 100644 imagepy/core/util/xlreport.py create mode 100644 imagepy/ui/propertygrid.py diff --git a/README.md b/README.md index d1e491d0..bebcd1b7 100644 --- a/README.md +++ b/README.md @@ -74,8 +74,6 @@ Segmentation methods such as adaptive thresholds, watersheds and others are also ![newdoc04](http://idoc.imagepy.org/imgs/newdoc04.png)
Up And Down Watershed

- - ![newdoc05](http://idoc.imagepy.org/imgs/newdoc05.png)
Mask

diff --git a/imagepy/core/engine/__init__.py b/imagepy/core/engine/__init__.py index ab846683..c3b02487 100644 --- a/imagepy/core/engine/__init__.py +++ b/imagepy/core/engine/__init__.py @@ -6,4 +6,5 @@ from .mkdown import MkDown from .widget import Widget from .table import Table -from .workflow import WorkFlow \ No newline at end of file +from .workflow import WorkFlow +from .report import Report \ No newline at end of file diff --git a/imagepy/core/engine/report.py b/imagepy/core/engine/report.py new file mode 100644 index 00000000..cbbb6a08 --- /dev/null +++ b/imagepy/core/engine/report.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +""" +Created on Thu Dec 29 01:48:23 2016 +@author: yxl +""" +import wx +from imagepy import IPy +from imagepy.core.manager import WidgetsManager, TaskManager, ImageManager +from imagepy.core.manager import ReaderManager, ViewerManager, TableManager +from imagepy.ui.propertygrid import GridDialog +from imagepy.core.util import xlreport +from time import time +import openpyxl as pyxl + +class Report: + def __init__(self, title, cont): + self.title = title + self.cont = cont + + def __call__(self): return self + + def runasyn(self, wb, info, para = None, callback = None): + TaskManager.add(self) + key = {} + for ws in info: + for i in ws[1]: key[i[1][1]] = i[1][0] + for i in para: + if i in key and key[i] == 'img': + ips = ImageManager.get(para[i]) + para[i] = ips if ips is None else ips.img + + if i in key and key[i] == 'tab': + tps = TableManager.get(para[i]) + para[i] = tps if tps is None else tps.data + + start = time() + xlreport.fill_value(wb, info, para) + wb.save(para['path']) + IPy.set_info('%s: cost %.3fs'%(self.title, time()-start)) + TaskManager.remove(self) + if callback!=None:callback() + + def start(self, para=None, callafter=None): + wb = pyxl.load_workbook(self.cont) + xlreport.repair(wb) + info = xlreport.parse(wb) + if para is not None: + return self.runasyn(wb, info, para, callafter) + dialog = GridDialog(IPy.curapp, self.title, info) + rst = dialog.ShowModal() + para = dialog.GetValue() + dialog.Destroy() + if rst != 5100: return + filt = '|'.join(['%s files (*.%s)|*.%s'%('XLSX', 'xlsx', 'xlsx')]) + if not IPy.getpath('Save..', filt, 'save', para): return + win = WidgetsManager.getref('Macros Recorder') + if win!=None: win.write('{}>{}'.format(self.title, para)) + self.runasyn(wb, info, para, callafter) + +def show_rpt(data, title): + wx.CallAfter(Report(title, data).start) + +ViewerManager.add('rpt', show_rpt) +def read_rpt(path): return path +ReaderManager.add('rpt', read_rpt, tag='rpt') \ No newline at end of file diff --git a/imagepy/core/loader/loader.py b/imagepy/core/loader/loader.py index 60870edd..be8b0a31 100644 --- a/imagepy/core/loader/loader.py +++ b/imagepy/core/loader/loader.py @@ -5,7 +5,7 @@ @author: yxl """ import os, sys -from ..engine import Macros, MkDown, Widget, WorkFlow +from ..engine import Macros, MkDown, Widget, WorkFlow, Report from ..manager import ToolsManager, PluginsManager, WidgetsManager, DocumentManager from ... import IPy, root_dir from codecs import open @@ -23,7 +23,11 @@ def extend_plugins(path, lst, err): for i in lst: if isinstance(i, tuple) or i=='-': rst.append(i) - + elif i[-3:] == 'rpt': + pt = os.path.join(root_dir,path) + print(pt+'/'+i) + rst.append(Report(i[:-4], pt+'/'+i)) + PluginsManager.add(rst[-1]) elif i[-3:] == '.mc': pt = os.path.join(root_dir,path) f = open(pt+'/'+i, 'r', 'utf-8') @@ -98,7 +102,7 @@ def build_plugins(path, err=False): if len(sub)!=0:subtree.append(sub) elif i[-6:] in ('plg.py', 'lgs.py', 'wgt.py', 'gts.py'): subtree.append(i) - elif i[-3:] in ('.mc', '.md', '.wf'): + elif i[-3:] in ('.mc', '.md', '.wf', 'rpt'): subtree.append(i) if len(subtree)==0:return [] @@ -118,8 +122,9 @@ def build_plugins(path, err=False): def extend_tools(path, lst, err): rst = [] for i in lst: - if i[-3:] in ('.mc', '.md', '.wf'): + if i[-3:] in ('.mc', '.md', '.wf', 'rpt'): pt = os.path.join(root_dir, path) + if i[-3:] == '.md':print(pt) f = open(pt+'/'+i) cmds = f.readlines() f.close() @@ -165,7 +170,7 @@ def build_tools(path, err=False): elif not root: if i[len(i)-7:] in ('_tol.py', 'tols.py'): subtree.append(i[:-3]) - elif i[-3:] in ('.mc', '.md', '.wf'): + elif i[-3:] in ('.mc', '.md', '.wf', 'ltx'): subtree.append(i) if len(subtree)==0:return [] rpath = path.replace('/', '.').replace('\\','.') diff --git a/imagepy/core/util/xlreport.py b/imagepy/core/util/xlreport.py new file mode 100644 index 00000000..ac8ecf1a --- /dev/null +++ b/imagepy/core/util/xlreport.py @@ -0,0 +1,105 @@ +import openpyxl as pyxl +from openpyxl.utils.units import cm_to_EMU, EMU_to_pixels +from io import BytesIO +from openpyxl.drawing.image import Image +from PIL import Image as PImage +import numpy as np +import pandas as pd +from copy import copy + +if not '.rpt' in pyxl.reader.excel.SUPPORTED_FORMATS: + pyxl.reader.excel.SUPPORTED_FORMATS += ('.rpt',) + +def parse(wb): + rst = [] + for ws in wb.worksheets: + rst.append((ws.title, [])) + for row in ws.rows: + for cell in row: + if not isinstance(cell.value, str):continue + if cell.value[0]+cell.value[-1] != '{}': continue + rst[-1][-1].append(((cell.row, cell.col_idx), + cell.value[1:-1].split(' '))) + return rst + +def trans(img, W, H, margin, scale): + h, w = img.shape[:2] + h2, w2 = int(h/margin), int(w/margin) + if scale: + if W/H > w/h: w2 = int(W/H*h2) + if H/W > h/w: h2 = int(H/W*w2) + newshp = (h2, w2) if img.ndim==2 else (h2, w2, 3) + blank = np.ones(newshp, dtype=np.uint8) * 255 + blank[(h2-h)//2:(h2-h)//2+h, (w2-w)//2:(w2-w)//2+w] = img + return blank + +def add_image(wb, ws, pos, key, img): + if img is None: return + w, h = [float(i) for i in key[2].split(':')] + margin, scale = [float(i) for i in key[3].split(':')] + img = trans(img, w, h, margin, scale==0) + + img = PImage.fromarray(img) + image_file = BytesIO() + img.save(image_file, 'png') + ref = BytesIO(image_file.getvalue()) + image = Image(img) + image.ref = ref + image.height = EMU_to_pixels(cm_to_EMU(h)) + image.width = EMU_to_pixels(cm_to_EMU(w)) + wb[ws].add_image(image, wb[ws].cell(*pos).coordinate) + +def add_table(wb, ws, pos, key, data): + if data is None: return + vs = data.values + idx, cols = data.index, data.columns + dr, dc = [int(i) for i in key[2].split(':')] if len(key)>2 else (1,1) + ir, ic = [int(i) for i in key[3].split(':')] if len(key)>3 else (0,0) + for r in range(vs.shape[0]): + if ir!=0: wb[ws].cell(pos[0]+r*dr, pos[1]+ir, idx[r]) + for c in range(vs.shape[1]): + if ic!=0: wb[ws].cell(pos[0]+ic, pos[1]+c*dc, cols[c]) + for r in range(vs.shape[0]): + for c in range(vs.shape[1]): + wb[ws].cell(pos[0]+r*dr, pos[1]+c*dc, vs[r,c]) + +def fill_value(wb, infos, para): + for worksheet in infos: + ws, info = worksheet + for pos, key in info: + if not key[1] in para: continue + if key[0] in ('str', 'int', 'float', 'bool', 'txt', 'list', 'date'): + wb[ws].cell(pos[0], pos[1], para[key[1]]) + if key[0] == 'img': + add_image(wb, ws, pos, key, para[key[1]]) + if key[0] == 'tab': + add_table(wb, ws, pos, key, para[key[1]]) + + +def repair(wb): + for ws in wb.worksheets: + for cr in ws.merged_cells: + ltc = ws.cell(cr.min_row, cr.min_col) + vb, hb = ltc.border.left, ltc.border.top + for r in range(cr.min_row, cr.max_row+1): + for c in range(cr.min_col, cr.max_col+1): + cur = copy(ws.cell(r, c).border) + cur.left, cur.right = copy(vb), copy(vb) + cur.top, cur.bottom = copy(hb), copy(hb) + ws.cell(r, c).border = cur + +if __name__ == '__main__': + rst = pd.read_csv('rst.csv') + img = np.arange(10000, dtype=np.uint8).reshape((100,100)) + data = {'id':'Coins-0001', 'name':'YX Dragon', 'date':'2019-02-05', + 'data':rst, 'ori':img, 'msk':img} + + wb = pyxl.load_workbook('temp.xlsx',) + repair(wb) + ws = wb.active + + + infos = parse(wb) + fill_value(wb, infos, data) + wb.save('new.xlsx') + diff --git a/imagepy/ui/panelconfig.py b/imagepy/ui/panelconfig.py index 8c93d504..c1a5d84c 100644 --- a/imagepy/ui/panelconfig.py +++ b/imagepy/ui/panelconfig.py @@ -67,15 +67,6 @@ def OnDestroy( self, event ): def parse(self, para) : self.add_ctrl_(widgets[para[0]], *para[1:]) - #self.funcs[para[0]](*para[1:]) - ''' - def add_ctrl(self, key, ctrl): - self.lst.Add( ctrl, 0, wx.EXPAND, 5 ) - if not key is None: - self.ctrl_dic[key] = ctrl - if hasattr(ctrl, 'set_handle'): - ctrl.set_handle(lambda x=None : self.para_changed(key)) - ''' def add_ctrl_(self, Ctrl, key, p): ctrl = Ctrl(self, *p) diff --git a/imagepy/ui/propertygrid.py b/imagepy/ui/propertygrid.py new file mode 100644 index 00000000..bed54ceb --- /dev/null +++ b/imagepy/ui/propertygrid.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python + +import sys, time, math, os.path + +import wx, wx.adv +import wx.propgrid as wxpg + +from six import exec_ +_ = wx.GetTranslation + +from imagepy.core.manager import ImageManager, TableManager + +data = [('Sheet1', [((2, 2), ['img', 'me']), ((2, 7), ['str', 'name']), ((3, 7), + ['int', 'age']), ((4, 7), ['float', 'weight']), ((5, 7), ['bool', 'married']), ((6, 7), + ['date', 'birth']), ((7, 7), ['list', 'sex', 'male,female']), ((8, 7), ['txt', 'info']), ((11, 2), + ['tab', 'score'])]), ('Sheet2', [((2, 2), ['txt', 'xyz'])]), ('Sheet3', [])] + +class GridDialog( wx.Dialog ): + + def __init__( self, parent, title, data): + wx.Dialog.__init__ (self, parent, -1, title, style = wx.DEFAULT_DIALOG_STYLE, size = wx.Size((300, 480))) + # wx.Panel.__init__(self, parent, wx.ID_ANY) + + self.panel = panel = wx.Panel(self, wx.ID_ANY) + topsizer = wx.BoxSizer(wx.VERTICAL) + + # Difference between using PropertyGridManager vs PropertyGrid is that + # the manager supports multiple pages and a description box. + self.pg = pg = wxpg.PropertyGridManager(panel, + style=wxpg.PG_SPLITTER_AUTO_CENTER) + + # Show help as tooltips + pg.SetExtraStyle(wxpg.PG_EX_HELP_AS_TOOLTIPS) + + pg.Bind( wxpg.EVT_PG_CHANGED, self.OnPropGridChange ) + pg.Bind( wxpg.EVT_PG_SELECTED, self.OnPropGridSelect ) + + pg.AddPage('Page 1') + for page in data: + pg.Append(wxpg.PropertyCategory(page[0])) + for item in page[1]: + v = item[1] + if v[0] == 'int': pg.Append( wxpg.IntProperty(v[1])) + if v[0] == 'float': pg.Append( wxpg.FloatProperty(v[1])) + if v[0] == 'str': pg.Append( wxpg.StringProperty(v[1])) + if v[0] == 'txt': pg.Append( wxpg.LongStringProperty(v[1])) + if v[0] == 'bool': pg.Append( wxpg.BoolProperty(v[1])) + if v[0] == 'date': pg.Append( wxpg.DateProperty(v[1], value=wx.DateTime.Now())) + if v[0] == 'list': pg.Append( wxpg.EnumProperty(v[1], v[1], v[2].split(','))) + if v[0] == 'img': pg.Append( wxpg.EnumProperty(v[1], v[1], ImageManager.get_titles())) + if v[0] == 'tab': pg.Append( wxpg.EnumProperty(v[1], v[1], TableManager.get_titles())) + + topsizer.Add(pg, 1, wx.EXPAND) + self.txt_info = wx.TextCtrl( self, wx.ID_ANY, 'information', wx.DefaultPosition, wx.Size(80, 80), wx.TE_MULTILINE|wx.TRANSPARENT_WINDOW ) + topsizer.Add(self.txt_info, 0, wx.EXPAND|wx.ALL, 0) + rowsizer = wx.BoxSizer(wx.HORIZONTAL) + but = wx.Button(panel, wx.ID_OK, "OK") + rowsizer.Add(but,1) + #but.Bind( wx.EVT_BUTTON, self.OnGetPropertyValues ) + but = wx.Button(panel, wx.ID_CANCEL,"Cancel") + rowsizer.Add(but,1) + topsizer.Add(rowsizer,0,wx.EXPAND) + + panel.SetSizer(topsizer) + topsizer.SetSizeHints(panel) + + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(panel, 1, wx.EXPAND) + self.SetSizer(sizer) + self.SetAutoLayout(True) + + def GetValue(self): + return self.pg.GetPropertyValues(as_strings=True) + + def OnGetPropertyValues(self,event): + para = self.pg.GetPropertyValues(inc_attributes=True) + + def OnPropGridChange(self, event): + p = event.GetProperty() + if p: print('%s changed to "%s"\n' % (p.GetName(),p.GetValueAsString())) + + def OnPropGridSelect(self, event): + p = event.GetProperty() + if p: self.txt_info.SetValue('%s selected\n' % (p.GetName())) + +if __name__ == '__main__': + app = wx.App(False) + frame = GridDialog(None, 'Property Grid', data) + rst = frame.ShowModal() == wx.ID_OK + print(rst) + app.MainLoop() \ No newline at end of file From 7be7bcfa3998d91e1995601045ffc69c8d2a4bab Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 6 Feb 2019 20:24:57 +0800 Subject: [PATCH 094/343] report --- imagepy/core/engine/report.py | 15 ++++++--------- imagepy/core/loader/loader.py | 10 ++++++---- imagepy/core/util/xlreport.py | 33 +++++++++++++++++++++++---------- imagepy/ui/propertygrid.py | 26 ++++++++++++-------------- 4 files changed, 47 insertions(+), 37 deletions(-) diff --git a/imagepy/core/engine/report.py b/imagepy/core/engine/report.py index cbbb6a08..86fdd6bf 100644 --- a/imagepy/core/engine/report.py +++ b/imagepy/core/engine/report.py @@ -19,17 +19,14 @@ def __init__(self, title, cont): def __call__(self): return self - def runasyn(self, wb, info, para = None, callback = None): + def runasyn(self, wb, info, key, para = None, callback = None): TaskManager.add(self) - key = {} - for ws in info: - for i in ws[1]: key[i[1][1]] = i[1][0] for i in para: - if i in key and key[i] == 'img': + if i in key and key[i][0] == 'img': ips = ImageManager.get(para[i]) para[i] = ips if ips is None else ips.img - if i in key and key[i] == 'tab': + if i in key and key[i][0] == 'tab': tps = TableManager.get(para[i]) para[i] = tps if tps is None else tps.data @@ -43,10 +40,10 @@ def runasyn(self, wb, info, para = None, callback = None): def start(self, para=None, callafter=None): wb = pyxl.load_workbook(self.cont) xlreport.repair(wb) - info = xlreport.parse(wb) + info, key = xlreport.parse(wb) if para is not None: return self.runasyn(wb, info, para, callafter) - dialog = GridDialog(IPy.curapp, self.title, info) + dialog = GridDialog(IPy.curapp, self.title, info, key) rst = dialog.ShowModal() para = dialog.GetValue() dialog.Destroy() @@ -55,7 +52,7 @@ def start(self, para=None, callafter=None): if not IPy.getpath('Save..', filt, 'save', para): return win = WidgetsManager.getref('Macros Recorder') if win!=None: win.write('{}>{}'.format(self.title, para)) - self.runasyn(wb, info, para, callafter) + self.runasyn(wb, info, key, para, callafter) def show_rpt(data, title): wx.CallAfter(Report(title, data).start) diff --git a/imagepy/core/loader/loader.py b/imagepy/core/loader/loader.py index be8b0a31..dd39de94 100644 --- a/imagepy/core/loader/loader.py +++ b/imagepy/core/loader/loader.py @@ -29,7 +29,7 @@ def extend_plugins(path, lst, err): rst.append(Report(i[:-4], pt+'/'+i)) PluginsManager.add(rst[-1]) elif i[-3:] == '.mc': - pt = os.path.join(root_dir,path) + pt = os.path.join(root_dir, path) f = open(pt+'/'+i, 'r', 'utf-8') cmds = f.readlines() f.close() @@ -84,7 +84,7 @@ def sort_plugins(catlog, lst): for i in catlog: if i=='-':rst.append('-') for j in lst: - if j[:-3]==i or j[0].title==i: + if j[:-3]==i or j[:-4]==i or j[0].title==i: lst.remove(j) rst.append(j) rst.extend(lst) @@ -111,6 +111,8 @@ def build_plugins(path, err=False): pg = __import__('imagepy.'+rpath,'','',['']) pg.title = os.path.basename(path) if hasattr(pg, 'catlog'): + if 'Personal Information' in pg.catlog: + print(subtree) subtree = sort_plugins(pg.catlog, subtree) subtree = extend_plugins(path, subtree, err) @@ -124,7 +126,7 @@ def extend_tools(path, lst, err): for i in lst: if i[-3:] in ('.mc', '.md', '.wf', 'rpt'): pt = os.path.join(root_dir, path) - if i[-3:] == '.md':print(pt) + # if i[-3:] == '.md':print(pt) f = open(pt+'/'+i) cmds = f.readlines() f.close() @@ -170,7 +172,7 @@ def build_tools(path, err=False): elif not root: if i[len(i)-7:] in ('_tol.py', 'tols.py'): subtree.append(i[:-3]) - elif i[-3:] in ('.mc', '.md', '.wf', 'ltx'): + elif i[-3:] in ('.mc', '.md', '.wf', 'rpt'): subtree.append(i) if len(subtree)==0:return [] rpath = path.replace('/', '.').replace('\\','.') diff --git a/imagepy/core/util/xlreport.py b/imagepy/core/util/xlreport.py index ac8ecf1a..60ff3a4e 100644 --- a/imagepy/core/util/xlreport.py +++ b/imagepy/core/util/xlreport.py @@ -11,16 +11,29 @@ pyxl.reader.excel.SUPPORTED_FORMATS += ('.rpt',) def parse(wb): - rst = [] + rst, key = [], {} for ws in wb.worksheets: rst.append((ws.title, [])) for row in ws.rows: for cell in row: if not isinstance(cell.value, str):continue if cell.value[0]+cell.value[-1] != '{}': continue + cont = cell.value[1:-1].strip() + tp = cont.split(' ')[0] + cont = cont[len(tp):].strip() + note, value = 'no description', None + if '#' in cont: + note = cont.split('#')[-1].strip() + cont = cont[:cont.index('#')].strip() + if '=' in cont: + value = cont.split('=')[1].strip() + name = cont[:cont.index('=')].strip() + else: name = cont + rst[-1][-1].append(((cell.row, cell.col_idx), - cell.value[1:-1].split(' '))) - return rst + [tp, name, value, note])) + key[name] = [tp, name, value, note] + return rst, key def trans(img, W, H, margin, scale): h, w = img.shape[:2] @@ -35,8 +48,7 @@ def trans(img, W, H, margin, scale): def add_image(wb, ws, pos, key, img): if img is None: return - w, h = [float(i) for i in key[2].split(':')] - margin, scale = [float(i) for i in key[3].split(':')] + w, h, margin, scale = eval(key[2]) img = trans(img, w, h, margin, scale==0) img = PImage.fromarray(img) @@ -53,8 +65,8 @@ def add_table(wb, ws, pos, key, data): if data is None: return vs = data.values idx, cols = data.index, data.columns - dr, dc = [int(i) for i in key[2].split(':')] if len(key)>2 else (1,1) - ir, ic = [int(i) for i in key[3].split(':')] if len(key)>3 else (0,0) + dr, dc, ir, ic = 1, 1, 0, 0 + if key[2] != None: dr, dc, ir, ic = eval(key[2]) for r in range(vs.shape[0]): if ir!=0: wb[ws].cell(pos[0]+r*dr, pos[1]+ir, idx[r]) for c in range(vs.shape[1]): @@ -91,15 +103,16 @@ def repair(wb): if __name__ == '__main__': rst = pd.read_csv('rst.csv') img = np.arange(10000, dtype=np.uint8).reshape((100,100)) - data = {'id':'Coins-0001', 'name':'YX Dragon', 'date':'2019-02-05', - 'data':rst, 'ori':img, 'msk':img} + data = {'Sample_ID':'Coins-0001', 'Operator_Name':'YX Dragon', 'Date':'2019-02-05', + 'Record':rst, 'Original_Image':img, 'Mask_Image':img} - wb = pyxl.load_workbook('temp.xlsx',) + wb = pyxl.load_workbook('Coins Report.xlsx',) repair(wb) ws = wb.active infos = parse(wb) + print(infos) fill_value(wb, infos, data) wb.save('new.xlsx') diff --git a/imagepy/ui/propertygrid.py b/imagepy/ui/propertygrid.py index bed54ceb..e5372bdf 100644 --- a/imagepy/ui/propertygrid.py +++ b/imagepy/ui/propertygrid.py @@ -10,14 +10,12 @@ from imagepy.core.manager import ImageManager, TableManager -data = [('Sheet1', [((2, 2), ['img', 'me']), ((2, 7), ['str', 'name']), ((3, 7), - ['int', 'age']), ((4, 7), ['float', 'weight']), ((5, 7), ['bool', 'married']), ((6, 7), - ['date', 'birth']), ((7, 7), ['list', 'sex', 'male,female']), ((8, 7), ['txt', 'info']), ((11, 2), - ['tab', 'score'])]), ('Sheet2', [((2, 2), ['txt', 'xyz'])]), ('Sheet3', [])] +data = [('Sheet1', [((4, 5), ['str', 'Sample_ID', '5', "image's name"]), ((4, 17), ['str', 'Operator_Name', None, 'your name']), ((4, 30), ['date', 'Date', None, 'today']), ((10, 1), ['img', 'Original_Image', '[8.16,5.76,0.9,0]', 'the original image']), ((10, 20), ['img', 'Mask_Image', '[8.16,5.76,0.9,0]', '']), ((28, 1), ['tab', 'Record', '[1,3,0,0]', 'records'])]), ('Sheet2', [((0,0), ['list', 'a', '[1,2,345]', 'nothing'])]), ('Sheet3', [])] +key = {'Sample_ID': ['str', 'Sample_ID', '5', "image's name"], 'Operator_Name': ['str', 'Operator_Name', None, 'your name'], 'Date': ['date', 'Date', None, 'today'], 'Original_Image': ['img', 'Original_Image', '[8.16,5.76,0.9,0]', 'the original image'], 'Mask_Image': ['img', 'Mask_Image', '[8.16,5.76,0.9,0]', ''], 'Record': ['tab', 'Record', '[1,3,0,0]', 'records']} class GridDialog( wx.Dialog ): - def __init__( self, parent, title, data): + def __init__( self, parent, title, tree, key): wx.Dialog.__init__ (self, parent, -1, title, style = wx.DEFAULT_DIALOG_STYLE, size = wx.Size((300, 480))) # wx.Panel.__init__(self, parent, wx.ID_ANY) @@ -36,17 +34,18 @@ def __init__( self, parent, title, data): pg.Bind( wxpg.EVT_PG_SELECTED, self.OnPropGridSelect ) pg.AddPage('Page 1') - for page in data: + self.key = key + for page in tree: pg.Append(wxpg.PropertyCategory(page[0])) for item in page[1]: v = item[1] - if v[0] == 'int': pg.Append( wxpg.IntProperty(v[1])) - if v[0] == 'float': pg.Append( wxpg.FloatProperty(v[1])) - if v[0] == 'str': pg.Append( wxpg.StringProperty(v[1])) - if v[0] == 'txt': pg.Append( wxpg.LongStringProperty(v[1])) + if v[0] == 'int': pg.Append( wxpg.IntProperty(v[1], value=int(v[2]) or 0)) + if v[0] == 'float': pg.Append( wxpg.FloatProperty(v[1], value=float(v[2]) or 0)) + if v[0] == 'str': pg.Append( wxpg.StringProperty(v[1], value=v[2] or '')) + if v[0] == 'txt': pg.Append( wxpg.LongStringProperty(v[1], value=v[2] or '')) if v[0] == 'bool': pg.Append( wxpg.BoolProperty(v[1])) if v[0] == 'date': pg.Append( wxpg.DateProperty(v[1], value=wx.DateTime.Now())) - if v[0] == 'list': pg.Append( wxpg.EnumProperty(v[1], v[1], v[2].split(','))) + if v[0] == 'list': pg.Append( wxpg.EnumProperty(v[1], v[1], [i.strip() for i in v[2][1:-1].split(',')])) if v[0] == 'img': pg.Append( wxpg.EnumProperty(v[1], v[1], ImageManager.get_titles())) if v[0] == 'tab': pg.Append( wxpg.EnumProperty(v[1], v[1], TableManager.get_titles())) @@ -81,11 +80,10 @@ def OnPropGridChange(self, event): def OnPropGridSelect(self, event): p = event.GetProperty() - if p: self.txt_info.SetValue('%s selected\n' % (p.GetName())) + if p: self.txt_info.SetValue('%s: %s'%(p.GetName(), self.key[p.GetName()][3])) if __name__ == '__main__': app = wx.App(False) - frame = GridDialog(None, 'Property Grid', data) + frame = GridDialog(None, 'Property Grid', data, key) rst = frame.ShowModal() == wx.ID_OK - print(rst) app.MainLoop() \ No newline at end of file From a1be5007a64e8edf568a2d10d35f9f301ce04029 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 7 Feb 2019 10:35:24 +0800 Subject: [PATCH 095/343] report --- README.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bebcd1b7..f90ec9a8 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,14 @@ some coment for section1 ... ![newdoc14](http://idoc.imagepy.org/imgs/newdoc14.png)
Workflow

+### Report Plugin + +Sometimes we need to make a report to print or generate a PDF document. ImagePy can generate report from a xlsx template. We just need put specific mark in some cells, ImagePy will parse the template and generate a parameter dialog, then we can input some information, or give image/table in, the report will be generated! more about how to make template please see [here](https://github.com/Image-Py/demoplugin/blob/master/doc/report.md). + +![newdoc14](http://idoc.imagepy.org/demoplugin/38.png) + +
generate report

+ ### Filter Plugin We introduced macros and workflows in the last sections, using macros and workflows to connect existing functions is convenient. But sometimes we need to create new features. In this section, we are trying to add a new feature to ImagePy. ImagePy can easily access any Numpy-based function. Let's take the Canny operator of scikit-image as an example. @@ -247,7 +255,7 @@ and `view`. After the parameters are chosen, they are passed to the `run` togeth ### Other type of plugins -The `Filter` and `Table` described above are the two most important plugins, but ImagePy also supports some other types of plugin extensions. There are currently nine, they are: +The `Filter` and `Table` described above are the two most important plugins, but ImagePy also supports some other types of plugin extensions. There are currently ten, they are: 1. `Filter`: mainly for image processing 2. `Simple`: similar to `Filter`, but focus on the overall characteristics of the image, such as the operation of the ROI, the operation of the false color, the area measurement, or the three-dimensional analysis of the entire image stack, visualization, and so on. @@ -256,8 +264,9 @@ The `Filter` and `Table` described above are the two most important plugins, but 5. `Table`: operate on the table, such as statistics analysis, sorting, plotting. 6. `Widget`: widgets that are displayed in panels, such as the navigation bar on the right, the macro recorder, and others. 7. `Markdown`: markup language, when clicked, a separate window will pop up to display the document. -8. `Macros`:Command sequence file for serially fixed operational procedures. +8. `Macros`:command sequence file for serially fixed operational procedures. 9. `Workflow`: combination of macro and MarkDown to create an interactive guidance process. +10. `Report`: a xlsx template with specific mark, rename as `.rpt`, used to auto generate report. ## Motivation & Goal From f99634e66e36d8b6c1bfa73b615123e0937d0ba6 Mon Sep 17 00:00:00 2001 From: Prevalenter <434625142@qq.com> Date: Sun, 10 Feb 2019 20:42:37 +0800 Subject: [PATCH 096/343] Signed-off-by: Prevalenter <434625142@qq.com> --- README.md | 77 ++++++----- imagepy/core/engine/__init__.py | 3 +- imagepy/core/engine/report.py | 62 +++++++++ imagepy/core/engine/widget.py | 2 +- imagepy/core/loader/loader.py | 21 ++- imagepy/core/myvi/canvas3d.py | 2 +- imagepy/core/util/tableio.py | 2 +- imagepy/core/util/xlreport.py | 118 +++++++++++++++++ imagepy/menus/Kit3D/Viewer 3D/__init__.py | 2 +- .../menus/Kit3D/Viewer 3D/tablepoints_plg.py | 44 ++++++ .../Contribute/Contributions/Demo Repo.md | 125 ++++++++++-------- .../Contribute/Contributions/OpenCV.md | 2 +- .../menus/Plugins/Contribute/pmanager_wgt.py | 4 +- imagepy/menus/Table/Statistic/__init__.py | 2 +- .../menus/Table/Statistic/frequency_plgs.py | 42 ++++++ imagepy/ui/panelconfig.py | 9 -- imagepy/ui/propertygrid.py | 89 +++++++++++++ imagepy/ui/tablewindow.py | 1 - 18 files changed, 486 insertions(+), 121 deletions(-) create mode 100644 imagepy/core/engine/report.py create mode 100644 imagepy/core/util/xlreport.py create mode 100644 imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py create mode 100644 imagepy/menus/Table/Statistic/frequency_plgs.py create mode 100644 imagepy/ui/propertygrid.py diff --git a/README.md b/README.md index 06bde9ef..f90ec9a8 100644 --- a/README.md +++ b/README.md @@ -18,33 +18,40 @@ ImagePy: Our long-term goal of this project is to be used as ImageJ + SPSS (although not achieved yet) +## Installation -## Citation: -[ImagePy: an open-source, Python-based and platform-independent software package for bioimage analysis](https://academic.oup.com/bioinformatics/article/34/18/3238/4989871) - -## Forum - -ImagePy is a community partner of forum.image.sc, Anything about the usage and development of ImagePy could be discussed in https://forum.image.sc. - -# Installation - -__OS support:windows, linux, mac, with python3.4__ +__OS support:windows, linux, mac, with python3.x__ 1. ImagePy is a ui framework based on wxpython, which can not be installed with pip on Linux. You need download [the whl according to your Linux system](https://wxpython.org/pages/downloads/). - 2. On Linux and Mac, there may be permission denied promblem, for ImagePy will write some config information, so please start with sudo. If you install with pip, please add \--user parameter like this: pip install --user imagepy - 3. If you install ImagePy in an Anaconda virtual environment, you may get a error when starting like this: This program needs access to the screen. Please run with a Framework build of python, and only when you are logged in on the main display, if so, please start with pythonw -m imagepy. +## Citation: +[ImagePy: an open-source, Python-based and platform-independent software package for bioimage analysis](https://academic.oup.com/bioinformatics/article/34/18/3238/4989871) + +## Forum + +ImagePy is a community partner of forum.image.sc, Anything about the usage and development of ImagePy could be discussed in https://forum.image.sc. + + + +## Contribute + +**Contribute Manual:** All markdown file under [doc folder](https://github.com/Image-Py/imagepy/tree/master/imagepy/doc) be parsed as manual. Plugins and manual are paired by plugins's title and manual's file name. We can browse document from the parameter dialog's Help button. We need more manual contributors, just pull request markdown file [here](https://github.com/Image-Py/imagepy/tree/master/imagepy/doc). + +**Contribute Plugins:** Here is a [demo plugin](https://github.com/Image-Py/demoplugin) repositories with document to show how to write plugins and publish on ImagePy. You are wellcom and feel free to contact with us if you need help. + +**Improve Main Framework:** Just fork ImagePy, then give Pull Request. But if you want to add some new feature, Please have a issue with us firstly. + ## Basic operations: ImagePy has a very rich set of features, and here, we use a specific example to show you a glimpse of the capacity of ImagePy. We choose the official coin split of scikit-image, since this example is simple and comprehensive. @@ -60,24 +67,19 @@ _PS: ImagePy supports bmp, jpg, png, gif, tif and other commonly used file forma ### Filtering & Segmentation -`menu:Process -> Hydrology -> Up And Down Watershed` -Here, a composite filter is selected to perform sobel gradient extraction on the image, and then the upper and lower thresholds are used as the mark, and finally we watershed on the gradient map. +`menu:Process -> Hydrology -> Up And Down Watershed` Here, a composite filter is selected to perform sobel gradient extraction on the image, and then the upper and lower thresholds are used as the mark, and finally we watershed on the gradient map. Filtering and segmentation are the crucial skills in the image processing toolkit, and are the key to the success or failure of the final measurement. Segmentation methods such as adaptive thresholds, watersheds and others are also supported. ![newdoc04](http://idoc.imagepy.org/imgs/newdoc04.png)
Up And Down Watershed

- - ![newdoc05](http://idoc.imagepy.org/imgs/newdoc05.png)
Mask

### Binarization -`menu:Process -> Binary -> Binary Fill Holes` - -After the segmentation, we obtained a relatively clean mask image, but there is still some hollowing out, as well as some impurities, which will interfere with counting and measurement. +`menu:Process -> Binary -> Binary Fill Holes` After the segmentation, we obtained a relatively clean mask image, but there is still some hollowing out, as well as some impurities, which will interfere with counting and measurement. _ImagePy supports binary operations such as erode, dilate, opening and closing, as well as skeletonization, central axis extraction, and distance transformation._ ![newdoc06](http://idoc.imagepy.org/imgs/newdoc06.png) @@ -85,9 +87,7 @@ _ImagePy supports binary operations such as erode, dilate, opening and closing, ### Geometry filtering -`menu:Analysis -> Region Analysis -> Geometry Filter` - -ImagePy can perform geometric filtering based on :__the area, the perimeter, the topology, the solidity, the eccentricity__ and other parameters. You can also use multiple conditions for filtering. Each number can be positive|negative, which indicates the kept object will have the corresponding parameter greater|smaller than the value respectively. The kept objects will be set to the front color, the rejected ones will be set to the back color. In this demo, the back color is set to 100 in order to see which ones are filtered out. Once satisfied with the result, set the back color to 0 to reject them. In addition, ImagePy also supports gray density filtering, color filtering, color clustering and other functions. +`menu:Analysis -> Region Analysis -> Geometry Filter` ImagePy can perform geometric filtering based on :__the area, the perimeter, the topology, the solidity, the eccentricity__ and other parameters. You can also use multiple conditions for filtering. Each number can be positive|negative, which indicates the kept object will have the corresponding parameter greater|smaller than the value respectively. The kept objects will be set to the front color, the rejected ones will be set to the back color. In this demo, the back color is set to 100 in order to see which ones are filtered out. Once satisfied with the result, set the back color to 0 to reject them. In addition, ImagePy also supports gray density filtering, color filtering, color clustering and other functions. ![newdoc07](http://idoc.imagepy.org/imgs/newdoc07.png)
Geometry filtering (the area is over-chosen to emphasize the distinction)

@@ -95,8 +95,7 @@ ImagePy can perform geometric filtering based on :__the area, the perimeter, the ### Geometry Analysis -`menu:Process -> Region Analysis -> Geometry Analysis` -Count the area and analyze the parameters. By choosing the `cov` option, ImagePy will fit each area with an ellipse calculated via the covariance. +`menu:Process -> Region Analysis -> Geometry Analysis` Count the area and analyze the parameters. By choosing the `cov` option, ImagePy will fit each area with an ellipse calculated via the covariance. The parameters such as area, perimeter, eccentricity, and solidity shown in the previous step are calculated here. In fact, the filtering of the previous step is a downstream analysis of this one. ![newdoc08](http://idoc.imagepy.org/imgs/newdoc08.png) @@ -108,9 +107,7 @@ The parameters such as area, perimeter, eccentricity, and solidity shown in the ### Sort Table by area -`menu:Table -> Statistic -> Table Sort By Key` - -Select the major key as area, and select descend. The table will be sorted in descending order of area. A table is another important piece of data other than an image. In a sense, many times we need to get the required information on the image and then post-process the data in the form of a table. ImagePy supports table I/O (xls, xlsx, csv), filtering, slicing, statistical analysis, sorting and more. (Right click on the column header to set the text color, decimal precision, line style, etc.) +`menu:Table -> Statistic -> Table Sort By Key` Select the major key as area, and select descend. The table will be sorted in descending order of area. A table is another important piece of data other than an image. In a sense, many times we need to get the required information on the image and then post-process the data in the form of a table. ImagePy supports table I/O (xls, xlsx, csv), filtering, slicing, statistical analysis, sorting and more. (Right click on the column header to set the text color, decimal precision, line style, etc.) ![newdoc10](http://idoc.imagepy.org/imgs/newdoc10.png)
Table

@@ -118,9 +115,7 @@ Select the major key as area, and select descend. The table will be sorted in de ### Charts -`menu:Table -> Chart -> Hist Chart` - -From tabular data, we often need to draw a graph. Here, we plot the histograms of the area and the perimeter columns. ImagePy's tables can be used to draw common charts such as line charts, pie charts, histograms, and scatter plots (matplotlib-based). The chart comes with zooming, moving and other functions. The table can also be saved as an image. +`menu:Table -> Chart -> Hist Chart` From tabular data, we often need to draw a graph. Here, we plot the histograms of the area and the perimeter columns. ImagePy's tables can be used to draw common charts such as line charts, pie charts, histograms, and scatter plots (matplotlib-based). The chart comes with zooming, moving and other functions. The table can also be saved as an image. ![newdoc11](http://idoc.imagepy.org/imgs/newdoc11.png)
Histograms

@@ -128,10 +123,7 @@ From tabular data, we often need to draw a graph. Here, we plot the histograms o ### 3D chart -`menu:Kit3D -> Viewer 3D -> 2D Surface` - - -Surface reconstruction of the image. This image shows the three reconstructed results including, sobel gradient map, high threshold and low threshold. It shows how the Up And Down Watershed works: +`menu:Kit3D -> Viewer 3D -> 2D Surface` Surface reconstruction of the image. This image shows the three reconstructed results including, sobel gradient map, high threshold and low threshold. It shows how the Up And Down Watershed works: - calculate the gradient. - mark the coin and background through the high and low thresholds, - simulate the rising water on the dem diagram to form the segmentation. @@ -145,9 +137,7 @@ ImagePy can perform 3D filtering of images, 3D skeletons, 3D topological analysi ### Macro recording and execution -`menu:Window -> Develop Tool Suite` - -Macro recorder is shown in the develop tool panel. We have manually completed an image segmentation. However, batch processing more than 10 images can be tedious. So, assuming that these steps are highly repeatable and robust for dealing with such problems, we can record a macro to combine several processes into a one-click program. The macro recorder is similar to a radio recorder. When it is turned on, each step of the operation will be recorded. We can click the pause button to stop recording, then click the play button to execute. When the macro is running, the recorded commands will be executed sequentially, therefore achieving simplicity and reproducibility. +`menu:Window -> Develop Tool Suite` Macro recorder is shown in the develop tool panel. We have manually completed an image segmentation. However, batch processing more than 10 images can be tedious. So, assuming that these steps are highly repeatable and robust for dealing with such problems, we can record a macro to combine several processes into a one-click program. The macro recorder is similar to a radio recorder. When it is turned on, each step of the operation will be recorded. We can click the pause button to stop recording, then click the play button to execute. When the macro is running, the recorded commands will be executed sequentially, therefore achieving simplicity and reproducibility. Macros are saved into .mc files. drag and drop the file to the status bar at the bottom of ImagePy, the macro will be executed automatically. we can also copy the .mc file to the submenu of the menus under the ImagePy file directory. When ImagePy is started, the macro file will be parsed into a menu item at the corresponding location. By clicking the menu, the macro will also be executed. @@ -176,6 +166,14 @@ some coment for section1 ... ![newdoc14](http://idoc.imagepy.org/imgs/newdoc14.png)
Workflow

+### Report Plugin + +Sometimes we need to make a report to print or generate a PDF document. ImagePy can generate report from a xlsx template. We just need put specific mark in some cells, ImagePy will parse the template and generate a parameter dialog, then we can input some information, or give image/table in, the report will be generated! more about how to make template please see [here](https://github.com/Image-Py/demoplugin/blob/master/doc/report.md). + +![newdoc14](http://idoc.imagepy.org/demoplugin/38.png) + +
generate report

+ ### Filter Plugin We introduced macros and workflows in the last sections, using macros and workflows to connect existing functions is convenient. But sometimes we need to create new features. In this section, we are trying to add a new feature to ImagePy. ImagePy can easily access any Numpy-based function. Let's take the Canny operator of scikit-image as an example. @@ -257,7 +255,7 @@ and `view`. After the parameters are chosen, they are passed to the `run` togeth ### Other type of plugins -The `Filter` and `Table` described above are the two most important plugins, but ImagePy also supports some other types of plugin extensions. There are currently nine, they are: +The `Filter` and `Table` described above are the two most important plugins, but ImagePy also supports some other types of plugin extensions. There are currently ten, they are: 1. `Filter`: mainly for image processing 2. `Simple`: similar to `Filter`, but focus on the overall characteristics of the image, such as the operation of the ROI, the operation of the false color, the area measurement, or the three-dimensional analysis of the entire image stack, visualization, and so on. @@ -266,8 +264,9 @@ The `Filter` and `Table` described above are the two most important plugins, but 5. `Table`: operate on the table, such as statistics analysis, sorting, plotting. 6. `Widget`: widgets that are displayed in panels, such as the navigation bar on the right, the macro recorder, and others. 7. `Markdown`: markup language, when clicked, a separate window will pop up to display the document. -8. `Macros`:Command sequence file for serially fixed operational procedures. +8. `Macros`:command sequence file for serially fixed operational procedures. 9. `Workflow`: combination of macro and MarkDown to create an interactive guidance process. +10. `Report`: a xlsx template with specific mark, rename as `.rpt`, used to auto generate report. ## Motivation & Goal diff --git a/imagepy/core/engine/__init__.py b/imagepy/core/engine/__init__.py index ab846683..c3b02487 100644 --- a/imagepy/core/engine/__init__.py +++ b/imagepy/core/engine/__init__.py @@ -6,4 +6,5 @@ from .mkdown import MkDown from .widget import Widget from .table import Table -from .workflow import WorkFlow \ No newline at end of file +from .workflow import WorkFlow +from .report import Report \ No newline at end of file diff --git a/imagepy/core/engine/report.py b/imagepy/core/engine/report.py new file mode 100644 index 00000000..86fdd6bf --- /dev/null +++ b/imagepy/core/engine/report.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +""" +Created on Thu Dec 29 01:48:23 2016 +@author: yxl +""" +import wx +from imagepy import IPy +from imagepy.core.manager import WidgetsManager, TaskManager, ImageManager +from imagepy.core.manager import ReaderManager, ViewerManager, TableManager +from imagepy.ui.propertygrid import GridDialog +from imagepy.core.util import xlreport +from time import time +import openpyxl as pyxl + +class Report: + def __init__(self, title, cont): + self.title = title + self.cont = cont + + def __call__(self): return self + + def runasyn(self, wb, info, key, para = None, callback = None): + TaskManager.add(self) + for i in para: + if i in key and key[i][0] == 'img': + ips = ImageManager.get(para[i]) + para[i] = ips if ips is None else ips.img + + if i in key and key[i][0] == 'tab': + tps = TableManager.get(para[i]) + para[i] = tps if tps is None else tps.data + + start = time() + xlreport.fill_value(wb, info, para) + wb.save(para['path']) + IPy.set_info('%s: cost %.3fs'%(self.title, time()-start)) + TaskManager.remove(self) + if callback!=None:callback() + + def start(self, para=None, callafter=None): + wb = pyxl.load_workbook(self.cont) + xlreport.repair(wb) + info, key = xlreport.parse(wb) + if para is not None: + return self.runasyn(wb, info, para, callafter) + dialog = GridDialog(IPy.curapp, self.title, info, key) + rst = dialog.ShowModal() + para = dialog.GetValue() + dialog.Destroy() + if rst != 5100: return + filt = '|'.join(['%s files (*.%s)|*.%s'%('XLSX', 'xlsx', 'xlsx')]) + if not IPy.getpath('Save..', filt, 'save', para): return + win = WidgetsManager.getref('Macros Recorder') + if win!=None: win.write('{}>{}'.format(self.title, para)) + self.runasyn(wb, info, key, para, callafter) + +def show_rpt(data, title): + wx.CallAfter(Report(title, data).start) + +ViewerManager.add('rpt', show_rpt) +def read_rpt(path): return path +ReaderManager.add('rpt', read_rpt, tag='rpt') \ No newline at end of file diff --git a/imagepy/core/engine/widget.py b/imagepy/core/engine/widget.py index 707f5dfe..933658be 100644 --- a/imagepy/core/engine/widget.py +++ b/imagepy/core/engine/widget.py @@ -17,7 +17,7 @@ def __call__(self):return self def start(self): #if not WidgetsManager.getref(self.title) is None: return pan = self.pan(IPy.curapp) - #WidgetsManager.addref(pan) + WidgetsManager.addref(pan) IPy.curapp.auimgr.AddPane(pan, aui.AuiPaneInfo().Caption(self.title).Left().Layer( 15 ).PinButton( True ) .Float().Resizable().FloatingSize( wx.DefaultSize ).Dockable(IPy.uimode()=='ipy').DestroyOnClose()) diff --git a/imagepy/core/loader/loader.py b/imagepy/core/loader/loader.py index 60870edd..dd39de94 100644 --- a/imagepy/core/loader/loader.py +++ b/imagepy/core/loader/loader.py @@ -5,7 +5,7 @@ @author: yxl """ import os, sys -from ..engine import Macros, MkDown, Widget, WorkFlow +from ..engine import Macros, MkDown, Widget, WorkFlow, Report from ..manager import ToolsManager, PluginsManager, WidgetsManager, DocumentManager from ... import IPy, root_dir from codecs import open @@ -23,9 +23,13 @@ def extend_plugins(path, lst, err): for i in lst: if isinstance(i, tuple) or i=='-': rst.append(i) - - elif i[-3:] == '.mc': + elif i[-3:] == 'rpt': pt = os.path.join(root_dir,path) + print(pt+'/'+i) + rst.append(Report(i[:-4], pt+'/'+i)) + PluginsManager.add(rst[-1]) + elif i[-3:] == '.mc': + pt = os.path.join(root_dir, path) f = open(pt+'/'+i, 'r', 'utf-8') cmds = f.readlines() f.close() @@ -80,7 +84,7 @@ def sort_plugins(catlog, lst): for i in catlog: if i=='-':rst.append('-') for j in lst: - if j[:-3]==i or j[0].title==i: + if j[:-3]==i or j[:-4]==i or j[0].title==i: lst.remove(j) rst.append(j) rst.extend(lst) @@ -98,7 +102,7 @@ def build_plugins(path, err=False): if len(sub)!=0:subtree.append(sub) elif i[-6:] in ('plg.py', 'lgs.py', 'wgt.py', 'gts.py'): subtree.append(i) - elif i[-3:] in ('.mc', '.md', '.wf'): + elif i[-3:] in ('.mc', '.md', '.wf', 'rpt'): subtree.append(i) if len(subtree)==0:return [] @@ -107,6 +111,8 @@ def build_plugins(path, err=False): pg = __import__('imagepy.'+rpath,'','',['']) pg.title = os.path.basename(path) if hasattr(pg, 'catlog'): + if 'Personal Information' in pg.catlog: + print(subtree) subtree = sort_plugins(pg.catlog, subtree) subtree = extend_plugins(path, subtree, err) @@ -118,8 +124,9 @@ def build_plugins(path, err=False): def extend_tools(path, lst, err): rst = [] for i in lst: - if i[-3:] in ('.mc', '.md', '.wf'): + if i[-3:] in ('.mc', '.md', '.wf', 'rpt'): pt = os.path.join(root_dir, path) + # if i[-3:] == '.md':print(pt) f = open(pt+'/'+i) cmds = f.readlines() f.close() @@ -165,7 +172,7 @@ def build_tools(path, err=False): elif not root: if i[len(i)-7:] in ('_tol.py', 'tols.py'): subtree.append(i[:-3]) - elif i[-3:] in ('.mc', '.md', '.wf'): + elif i[-3:] in ('.mc', '.md', '.wf', 'rpt'): subtree.append(i) if len(subtree)==0:return [] rpath = path.replace('/', '.').replace('\\','.') diff --git a/imagepy/core/myvi/canvas3d.py b/imagepy/core/myvi/canvas3d.py index 4aaf7940..8890c401 100644 --- a/imagepy/core/myvi/canvas3d.py +++ b/imagepy/core/myvi/canvas3d.py @@ -30,7 +30,7 @@ def __init__(self, parent, manager=None): self.Bind(wx.EVT_MOTION, self.OnMouseMotion) self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel) self.lastx, self.lasty = None, None - self.update() + #self.update() #print('init===========') def InitGL(self): diff --git a/imagepy/core/util/tableio.py b/imagepy/core/util/tableio.py index 2b67ea6f..92012115 100644 --- a/imagepy/core/util/tableio.py +++ b/imagepy/core/util/tableio.py @@ -30,7 +30,7 @@ def show(self): return IPy.getpath('Save..', filt, 'save', self.para) #process - def run(self, tps, data, snap, para = None): + def run(self, tps, snap, data, para = None): fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) write = WriterManager.get(fe[1:], tag='tab') diff --git a/imagepy/core/util/xlreport.py b/imagepy/core/util/xlreport.py new file mode 100644 index 00000000..60ff3a4e --- /dev/null +++ b/imagepy/core/util/xlreport.py @@ -0,0 +1,118 @@ +import openpyxl as pyxl +from openpyxl.utils.units import cm_to_EMU, EMU_to_pixels +from io import BytesIO +from openpyxl.drawing.image import Image +from PIL import Image as PImage +import numpy as np +import pandas as pd +from copy import copy + +if not '.rpt' in pyxl.reader.excel.SUPPORTED_FORMATS: + pyxl.reader.excel.SUPPORTED_FORMATS += ('.rpt',) + +def parse(wb): + rst, key = [], {} + for ws in wb.worksheets: + rst.append((ws.title, [])) + for row in ws.rows: + for cell in row: + if not isinstance(cell.value, str):continue + if cell.value[0]+cell.value[-1] != '{}': continue + cont = cell.value[1:-1].strip() + tp = cont.split(' ')[0] + cont = cont[len(tp):].strip() + note, value = 'no description', None + if '#' in cont: + note = cont.split('#')[-1].strip() + cont = cont[:cont.index('#')].strip() + if '=' in cont: + value = cont.split('=')[1].strip() + name = cont[:cont.index('=')].strip() + else: name = cont + + rst[-1][-1].append(((cell.row, cell.col_idx), + [tp, name, value, note])) + key[name] = [tp, name, value, note] + return rst, key + +def trans(img, W, H, margin, scale): + h, w = img.shape[:2] + h2, w2 = int(h/margin), int(w/margin) + if scale: + if W/H > w/h: w2 = int(W/H*h2) + if H/W > h/w: h2 = int(H/W*w2) + newshp = (h2, w2) if img.ndim==2 else (h2, w2, 3) + blank = np.ones(newshp, dtype=np.uint8) * 255 + blank[(h2-h)//2:(h2-h)//2+h, (w2-w)//2:(w2-w)//2+w] = img + return blank + +def add_image(wb, ws, pos, key, img): + if img is None: return + w, h, margin, scale = eval(key[2]) + img = trans(img, w, h, margin, scale==0) + + img = PImage.fromarray(img) + image_file = BytesIO() + img.save(image_file, 'png') + ref = BytesIO(image_file.getvalue()) + image = Image(img) + image.ref = ref + image.height = EMU_to_pixels(cm_to_EMU(h)) + image.width = EMU_to_pixels(cm_to_EMU(w)) + wb[ws].add_image(image, wb[ws].cell(*pos).coordinate) + +def add_table(wb, ws, pos, key, data): + if data is None: return + vs = data.values + idx, cols = data.index, data.columns + dr, dc, ir, ic = 1, 1, 0, 0 + if key[2] != None: dr, dc, ir, ic = eval(key[2]) + for r in range(vs.shape[0]): + if ir!=0: wb[ws].cell(pos[0]+r*dr, pos[1]+ir, idx[r]) + for c in range(vs.shape[1]): + if ic!=0: wb[ws].cell(pos[0]+ic, pos[1]+c*dc, cols[c]) + for r in range(vs.shape[0]): + for c in range(vs.shape[1]): + wb[ws].cell(pos[0]+r*dr, pos[1]+c*dc, vs[r,c]) + +def fill_value(wb, infos, para): + for worksheet in infos: + ws, info = worksheet + for pos, key in info: + if not key[1] in para: continue + if key[0] in ('str', 'int', 'float', 'bool', 'txt', 'list', 'date'): + wb[ws].cell(pos[0], pos[1], para[key[1]]) + if key[0] == 'img': + add_image(wb, ws, pos, key, para[key[1]]) + if key[0] == 'tab': + add_table(wb, ws, pos, key, para[key[1]]) + + +def repair(wb): + for ws in wb.worksheets: + for cr in ws.merged_cells: + ltc = ws.cell(cr.min_row, cr.min_col) + vb, hb = ltc.border.left, ltc.border.top + for r in range(cr.min_row, cr.max_row+1): + for c in range(cr.min_col, cr.max_col+1): + cur = copy(ws.cell(r, c).border) + cur.left, cur.right = copy(vb), copy(vb) + cur.top, cur.bottom = copy(hb), copy(hb) + ws.cell(r, c).border = cur + +if __name__ == '__main__': + rst = pd.read_csv('rst.csv') + img = np.arange(10000, dtype=np.uint8).reshape((100,100)) + data = {'Sample_ID':'Coins-0001', 'Operator_Name':'YX Dragon', 'Date':'2019-02-05', + 'Record':rst, 'Original_Image':img, 'Mask_Image':img} + + wb = pyxl.load_workbook('Coins Report.xlsx',) + repair(wb) + ws = wb.active + + + infos = parse(wb) + print(infos) + fill_value(wb, infos, data) + wb.save('new.xlsx') + diff --git a/imagepy/menus/Kit3D/Viewer 3D/__init__.py b/imagepy/menus/Kit3D/Viewer 3D/__init__.py index 9bc41dd0..051f9c97 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/__init__.py +++ b/imagepy/menus/Kit3D/Viewer 3D/__init__.py @@ -1 +1 @@ -catlog = ['surface_plgs', 'colorpts_plg', '-', '2DSurface Demo.mc'] \ No newline at end of file +catlog = ['surface_plgs', '-', 'colorpts_plg', 'tablepoints_plg', '-', '2DSurface Demo.mc'] \ No newline at end of file diff --git a/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py b/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py new file mode 100644 index 00000000..86fe5778 --- /dev/null +++ b/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py @@ -0,0 +1,44 @@ +from imagepy.core.engine import Table +from imagepy.core.manager import ColorManager +from imagepy.core import myvi +import numpy as np +from imagepy import IPy + +class Plugin(Table): + title = 'Table Point Cloud' + + para = {'x':None, 'y':None, 'z':None, 'r':5, 'rs':None, 'c':(0,0,255), + 'cs':None, 'cm':None, 'cube':False} + + view = [('field', 'x', 'x data', ''), + ('field', 'y', 'y data', ''), + ('field', 'z', 'z data', ''), + (float, 'r', (0, 1024), 3, 'radius', 'pix'), + ('lab', 'lab', '== if set the radius would becom factor =='), + ('field', 'rs', 'radius', 'column'), + ('color', 'c', 'color', ''), + ('lab', 'lab', '== if set the color upon would disable =='), + ('field', 'cs', 'color', 'column'), + ('cmap', 'cm', 'color map when color column is set'), + (bool, 'cube', 'draw outline cube')] + + def load(self, para): + self.frame = myvi.Frame3D.figure(IPy.curapp, title='3D Canvas') + return True + + def run(self, tps, snap, data, para = None): + pts = np.array(data[[para['x'], para['y'], para['z']]]) + rs = data[para['rs']]*para['r'] if para['rs'] != 'None' else [para['r']]*len(pts) + cm = ColorManager.get_lut(para['cm'])/255.0 + clip = lambda x : (x-x.min())/(x.max()-x.min())*255 + if para['cs'] == 'None': cs = [np.array(para['c'])/255.0]*len(pts) + else: cs = cm[clip(data[para['cs']]).astype(np.uint8)] + vts, fs, ns, cs = myvi.build_balls(pts.astype(np.float32), list(rs), cs) + self.frame.viewer.add_surf_asyn('ball', vts, fs, ns, cs) + if para['cube']: + p1 = data[[para['x'], para['y'], para['z']]].min(axis=0) + p2 = data[[para['x'], para['y'], para['z']]].max(axis=0) + vts, fs, ns, cs = myvi.build_cube(p1, p2) + self.frame.viewer.add_surf_asyn('cube', vts, fs, ns, cs, mode='grid') + self.frame.Raise() + self.frame = None \ No newline at end of file diff --git a/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md b/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md index 8bcad9df..b400ab3d 100644 --- a/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md +++ b/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md @@ -12,109 +12,122 @@ **Description:** a friendly develop tutorial. +*这是一个ImagePy插件项目,里面覆盖了各类插件的编写方法和用法,并配有详细的文档,ImagePy的插件开发者可以以此为参考* -## 基础预备 -1. 什么是插件 +## 安装 -2. Hello World (第一个插件) +ImagePy菜单:`Plugins > Manager > Plugins Manager` 在输入框内输入demo进行查询,选中Demo Plugin,点击`Install`,完成后菜单栏出现Demo菜单,工具栏会加入Demo工具,组件栏也会加入Demo组件。 -3. Who Are You (带有交互) +![06](http://idoc.imagepy.org/demoplugin/06.png) +
Install DemoPlugin

-4. Questionnaire (参数对话框详解) +## [基础预备](doc/start.md) + +**[从这里开始](doc/start.md)** + +1. [什么是插件](doc/start.md#什么是插件) +2. [Hello World(第一个插件)](doc/start.md#Hello-World) +3. [Who Are You(带有交互)](doc/start.md#Who-Are-You) +4. [Questionnaire(参数对话框详解)](doc/start.md#Questionnaire) +5. [一个文件内实现多个插件](doc/start.md#一个文件内实现多个插件) -5. 一个文件内实现多个插件 - ## 插件开发 -**Markdown: 文档提示** +**[Markdown: 文档提示](doc/markdown.md)** + +1. [Markdown Demo](doc/markdown.md#MarkDown-Demo) -1. MarkDown +**[Macros: 用宏串联已有功能](doc/macros.md#Macros)** -**Macros: 用宏串联已有功能** +1. [高斯模糊再求反](doc/macros.md#高斯模糊再求反) +2. [Coins Segment Macros:硬币分割](doc/macros.md#分割硬币) -1. Macros Recorder:操作录制 -2. Coins Segment Macros:硬币分割 +**[Workflow: 可交互的宏](doc/workflow.md)** -**Workflow: 可交互的宏** +1. [Coins Segment Workflow: 按照指引进行硬币分割](doc/workflow.md#硬币分割工作流) -1. Coins Segment Workflow: 按照指引进行硬币分割 +**[Filter: 二维图像滤波器](doc/filter.md)** -**Filter: 二维图像滤波器** +1. [Invert Demo:无参数的插件](doc/filter.md#Invert) +2. [Gaussian Demo:带有参数的插件](doc/filter.md#Gaussian) +3. [Filter 的运行机制](doc/filter.md#Filter-运行机制) -1. Invert Demo:无参数的插件 -2. Gaussian Demo:带有参数的插件 +**[Simple: 图像整体操作](doc/simple.md)** -**Simple: 图像整体操作** +1. [Gaussian 3D Demo:三维滤波](doc/simple.md#Gaussian3D) +2. [Red Lut Demo:设定索引色](doc/simple.md#SetLUT) +3. [ROI Inflate Demo:操作ROI](doc/simple.md#Inflate-ROI) +4. [Unit Demo: 设置比例尺及单位](doc/simple.md#SEt-Scale-And-Unit) +5. [Draw Mark Demo: 设置Overlay Mark](doc/simple.md#Mark) +6. [Simple 的运行机制](doc/simple.md#Simple-运行机制) -1. Red Lut Demo:操作索引表 +**[Table: 表格数据](doc/table.md)** -2. ROI Inflate Demo:操作ROI -3. Unit Demo: 设置比例尺及单位 -4. Draw Mark Demo: 设置Overlay Mark -5. Gaussian 3D Demo:三维滤波 +1. [Generate Table Demo:数据表生成](doc/table.md#生成成绩单) +2. [Sort By Key Demo:排序](doc/table.md#根据某科成绩排序) +3. [Table Plot Demo:绘图](doc/table.md#绘制柱状图) +4. [Table 运行机制](doc/table.md#Table-运行机制) -**Table: 表格数据** +**[Free: 没有任何依赖的插件](doc/free.md)** -1. Generate Table Demo:数据表生成 -2. Sort By Key Demo:排序 -3. Table Plot Demo:绘图 +1. [New Image Demo: 创建图像](doc/free.md#创建图像) +2. [About Demo:关于对话框](doc/free.md#关于对话框) +3. [Close Demo:退出软件](doc/free.md#退出软件) +4. [Free 的运行机制](doc/free.md#Free-的运行机制) -**Free: 没有任何依赖的插件** +**[Tool: 鼠标交互工具](doc/tool.md)** -1. Parameter Demo: 参数对话框生成演示 -2. New Image Demo: 新建图像 +1. [Painter Demo:画笔工具](doc/tool.md#画笔工具) +2. [Tool的运行机制](doc/tool.md#Tool-的运行机制) -**Tool: 鼠标交互工具** +**[Widget: 桌面小部件](doc/widget.md)** -1. Painter Demo:画笔工具 -2. Pixel Inspector:像素查看器 +1. [Widget Demo:桌面小部件演示](doc/widget.md#桌面组件演示) +2. [Tool的运行机制](doc/widget.md#widget-的运行机制) -**Widget: 桌面小部件** -1. Widget Demo:桌面小部件演示 +## [插件项目发布](doc/publish.md) +**[插件的组织方式](doc/publish.md#功能组织)** -## 插件项目发布 +1. [功能划分](doc/publish.md#功能组织) +2. [顺序设定](doc/publish.md#功能组织) -**插件的组织方式** +**[插件项目创建](doc/publish.md#插件项目创建)** -1. 功能划分 -2. 顺序设定 +1. [创建插件项目仓库](doc/publish.md#插件项目创建) +2. [编写requirements](doc/publish.md#插件项目创建) +3. [编写readme](doc/publish.md#插件项目创建) +4. [插件的安装](doc/publish.md#插件项目创建) -**插件项目的发布** +**[发布到 ImagePy](doc/publish.md#发布到-ImagePy)** -1. 编写requirements -2. 为Readme加入的插件头信息 -3. 发布到ImagePy +1. [给ImagePy发Pull Request](doc/publish.md#发布到-ImagePy) +2. [关于顶级菜单](doc/publish.md#发布到-ImagePy) -**插件的安装与管理** -1. 通过连接安装插件 -2. 插件管理器 +## [文档编写](doc/document.md) +**[编写操作手册](doc/document.md#编写操作手册)** -## 文档编写 +**[查阅操作手册](doc/document.md#查阅操作手册)** -为插件编写操作手册 +## [注意事项](doc/attention.md#注意事项) -## 注意事项 +**[用户友好性](doc/attention.md#用户友好性)** -1. 用户友好性 -2. 开发者友好性 -3. 及时沟通 +**[开发者友好性](doc/attention.md#开发者友好性)** +**[及时沟通](doc/attention.md#及时沟通)** -## 如何贡献 -1. 宣传推广 -2. 编写插件 -3. 完善文档 \ No newline at end of file +**本篇文档相对系统的介绍了ImagePy的插件开发,但是依然无法详尽,关于更多ImagePy使用,开发上的问题请在[forum.Image.sc](https://forum.image.sc/)行进行讨论** \ No newline at end of file diff --git a/imagepy/menus/Plugins/Contribute/Contributions/OpenCV.md b/imagepy/menus/Plugins/Contribute/Contributions/OpenCV.md index 3ef054b2..c85b7ad8 100644 --- a/imagepy/menus/Plugins/Contribute/Contributions/OpenCV.md +++ b/imagepy/menus/Plugins/Contribute/Contributions/OpenCV.md @@ -2,7 +2,7 @@ **Path:** https://github.com/Image-Py/opencv-plgs -**Version:** 0.11 +**Version:** 0.1 **Author:** YXDragon diff --git a/imagepy/menus/Plugins/Contribute/pmanager_wgt.py b/imagepy/menus/Plugins/Contribute/pmanager_wgt.py index 5d25867b..d5d9c2a6 100644 --- a/imagepy/menus/Plugins/Contribute/pmanager_wgt.py +++ b/imagepy/menus/Plugins/Contribute/pmanager_wgt.py @@ -33,7 +33,7 @@ def refresh(self): self.SetItemCount(len(self.data)) def parse(path): - f = open(path) + f = open(path, encoding='utf-8') body = {'file':path} try: for i in range(13): @@ -131,7 +131,7 @@ def on_search( self, event ): self.lst_plgs.Refresh() def on_run(self, event): - f = open(self.buf[event.GetIndex()][-1]['file']) + f = open(self.buf[event.GetIndex()][-1]['file'], encoding='utf-8') cont = f.read() f.close() cont = '\n'.join([i.strip() for i in cont.split('\n')]) diff --git a/imagepy/menus/Table/Statistic/__init__.py b/imagepy/menus/Table/Statistic/__init__.py index fa4b62c1..a58cdeb2 100644 --- a/imagepy/menus/Table/Statistic/__init__.py +++ b/imagepy/menus/Table/Statistic/__init__.py @@ -1 +1 @@ -catlog = ['statistic_plgs', '-', 'sort_plg'] \ No newline at end of file +catlog = ['statistic_plgs', '-', 'frequency_plgs', '-', 'sort_plg'] \ No newline at end of file diff --git a/imagepy/menus/Table/Statistic/frequency_plgs.py b/imagepy/menus/Table/Statistic/frequency_plgs.py new file mode 100644 index 00000000..50453f20 --- /dev/null +++ b/imagepy/menus/Table/Statistic/frequency_plgs.py @@ -0,0 +1,42 @@ +from imagepy.core.engine import Table +import pandas as pd +import numpy as np +from imagepy import IPy + +class Count(Table): + title = 'Values Frequency' + para = {'cn':[]} + view = [('fields', 'cn', 'fields to count')] + + def run(self, tps, snap, data, para=None): + for i in para['cn']: + df = pd.DataFrame(data[i].value_counts()) + df.columns = ['%s-vf'%i] + IPy.show_table(df, '%s-values-frequency'%i) + +class Frequency(Table): + title = 'Table Bins Frequency' + + para = {'bins':10, 'max':1, 'min':0, 'auto':True, 'count':True, 'fre':False, 'weight':False, 'cn':[]} + + view = [(int, 'bins', (3,1024), 0, 'bins', 'n'), + (bool, 'auto', 'auto scale'), + (int, 'min', (-1e8, 1e8), 5, 'min range', ''), + (int, 'max', (-1e8, 1e8), 5, 'max range', ''), + ('fields', 'cn', 'fields to count'), + (bool, 'count', 'count times'), + (bool, 'fre', 'count frequency'), + (bool, 'weight', 'count weight')] + + def run(self, tps, snap, data, para=None): + rg = None if para['auto'] else (para['min'], para['max']) + for i in para['cn']: + hist, bins = np.histogram(data[i], para['bins'], rg) + vs = {'bins':bins[:-1]} + if para['count']:vs['count'] = hist + if para['fre']:vs['frequency'] = hist/hist.sum() + if para['weight']:vs['weight'] = np.histogram(data[i], bins, rg, weights=data[i])[0] + df = pd.DataFrame(vs, columns = [i for i in ['bins', 'count', 'frequency', 'weight'] if i in vs]) + IPy.show_table(df, '%s-frequency'%i) + +plgs = [Count, Frequency] \ No newline at end of file diff --git a/imagepy/ui/panelconfig.py b/imagepy/ui/panelconfig.py index 8c93d504..c1a5d84c 100644 --- a/imagepy/ui/panelconfig.py +++ b/imagepy/ui/panelconfig.py @@ -67,15 +67,6 @@ def OnDestroy( self, event ): def parse(self, para) : self.add_ctrl_(widgets[para[0]], *para[1:]) - #self.funcs[para[0]](*para[1:]) - ''' - def add_ctrl(self, key, ctrl): - self.lst.Add( ctrl, 0, wx.EXPAND, 5 ) - if not key is None: - self.ctrl_dic[key] = ctrl - if hasattr(ctrl, 'set_handle'): - ctrl.set_handle(lambda x=None : self.para_changed(key)) - ''' def add_ctrl_(self, Ctrl, key, p): ctrl = Ctrl(self, *p) diff --git a/imagepy/ui/propertygrid.py b/imagepy/ui/propertygrid.py new file mode 100644 index 00000000..e5372bdf --- /dev/null +++ b/imagepy/ui/propertygrid.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python + +import sys, time, math, os.path + +import wx, wx.adv +import wx.propgrid as wxpg + +from six import exec_ +_ = wx.GetTranslation + +from imagepy.core.manager import ImageManager, TableManager + +data = [('Sheet1', [((4, 5), ['str', 'Sample_ID', '5', "image's name"]), ((4, 17), ['str', 'Operator_Name', None, 'your name']), ((4, 30), ['date', 'Date', None, 'today']), ((10, 1), ['img', 'Original_Image', '[8.16,5.76,0.9,0]', 'the original image']), ((10, 20), ['img', 'Mask_Image', '[8.16,5.76,0.9,0]', '']), ((28, 1), ['tab', 'Record', '[1,3,0,0]', 'records'])]), ('Sheet2', [((0,0), ['list', 'a', '[1,2,345]', 'nothing'])]), ('Sheet3', [])] +key = {'Sample_ID': ['str', 'Sample_ID', '5', "image's name"], 'Operator_Name': ['str', 'Operator_Name', None, 'your name'], 'Date': ['date', 'Date', None, 'today'], 'Original_Image': ['img', 'Original_Image', '[8.16,5.76,0.9,0]', 'the original image'], 'Mask_Image': ['img', 'Mask_Image', '[8.16,5.76,0.9,0]', ''], 'Record': ['tab', 'Record', '[1,3,0,0]', 'records']} + +class GridDialog( wx.Dialog ): + + def __init__( self, parent, title, tree, key): + wx.Dialog.__init__ (self, parent, -1, title, style = wx.DEFAULT_DIALOG_STYLE, size = wx.Size((300, 480))) + # wx.Panel.__init__(self, parent, wx.ID_ANY) + + self.panel = panel = wx.Panel(self, wx.ID_ANY) + topsizer = wx.BoxSizer(wx.VERTICAL) + + # Difference between using PropertyGridManager vs PropertyGrid is that + # the manager supports multiple pages and a description box. + self.pg = pg = wxpg.PropertyGridManager(panel, + style=wxpg.PG_SPLITTER_AUTO_CENTER) + + # Show help as tooltips + pg.SetExtraStyle(wxpg.PG_EX_HELP_AS_TOOLTIPS) + + pg.Bind( wxpg.EVT_PG_CHANGED, self.OnPropGridChange ) + pg.Bind( wxpg.EVT_PG_SELECTED, self.OnPropGridSelect ) + + pg.AddPage('Page 1') + self.key = key + for page in tree: + pg.Append(wxpg.PropertyCategory(page[0])) + for item in page[1]: + v = item[1] + if v[0] == 'int': pg.Append( wxpg.IntProperty(v[1], value=int(v[2]) or 0)) + if v[0] == 'float': pg.Append( wxpg.FloatProperty(v[1], value=float(v[2]) or 0)) + if v[0] == 'str': pg.Append( wxpg.StringProperty(v[1], value=v[2] or '')) + if v[0] == 'txt': pg.Append( wxpg.LongStringProperty(v[1], value=v[2] or '')) + if v[0] == 'bool': pg.Append( wxpg.BoolProperty(v[1])) + if v[0] == 'date': pg.Append( wxpg.DateProperty(v[1], value=wx.DateTime.Now())) + if v[0] == 'list': pg.Append( wxpg.EnumProperty(v[1], v[1], [i.strip() for i in v[2][1:-1].split(',')])) + if v[0] == 'img': pg.Append( wxpg.EnumProperty(v[1], v[1], ImageManager.get_titles())) + if v[0] == 'tab': pg.Append( wxpg.EnumProperty(v[1], v[1], TableManager.get_titles())) + + topsizer.Add(pg, 1, wx.EXPAND) + self.txt_info = wx.TextCtrl( self, wx.ID_ANY, 'information', wx.DefaultPosition, wx.Size(80, 80), wx.TE_MULTILINE|wx.TRANSPARENT_WINDOW ) + topsizer.Add(self.txt_info, 0, wx.EXPAND|wx.ALL, 0) + rowsizer = wx.BoxSizer(wx.HORIZONTAL) + but = wx.Button(panel, wx.ID_OK, "OK") + rowsizer.Add(but,1) + #but.Bind( wx.EVT_BUTTON, self.OnGetPropertyValues ) + but = wx.Button(panel, wx.ID_CANCEL,"Cancel") + rowsizer.Add(but,1) + topsizer.Add(rowsizer,0,wx.EXPAND) + + panel.SetSizer(topsizer) + topsizer.SetSizeHints(panel) + + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(panel, 1, wx.EXPAND) + self.SetSizer(sizer) + self.SetAutoLayout(True) + + def GetValue(self): + return self.pg.GetPropertyValues(as_strings=True) + + def OnGetPropertyValues(self,event): + para = self.pg.GetPropertyValues(inc_attributes=True) + + def OnPropGridChange(self, event): + p = event.GetProperty() + if p: print('%s changed to "%s"\n' % (p.GetName(),p.GetValueAsString())) + + def OnPropGridSelect(self, event): + p = event.GetProperty() + if p: self.txt_info.SetValue('%s: %s'%(p.GetName(), self.key[p.GetName()][3])) + +if __name__ == '__main__': + app = wx.App(False) + frame = GridDialog(None, 'Property Grid', data, key) + rst = frame.ShowModal() == wx.ID_OK + app.MainLoop() \ No newline at end of file diff --git a/imagepy/ui/tablewindow.py b/imagepy/ui/tablewindow.py index ce6c07eb..99effe42 100644 --- a/imagepy/ui/tablewindow.py +++ b/imagepy/ui/tablewindow.py @@ -251,7 +251,6 @@ def select(self): self.Bind(Grid.EVT_GRID_RANGE_SELECT, None) self.ClearSelection() print('select grid') - print(self.tps.rowmsk, self.tps.colmsk) for i in self.tps.data.index.get_indexer(self.tps.rowmsk): print(i) self.SelectRow(i, True) From 5d605e606ec51fe66f88c809e2cd0ec52d46b049 Mon Sep 17 00:00:00 2001 From: Prevalenter <434625142@qq.com> Date: Sun, 10 Feb 2019 21:18:28 +0800 Subject: [PATCH 097/343] add the contribution of FluidSurface Signed-off-by: Prevalenter <434625142@qq.com> --- .../Contribute/Contributions/FluidSurface.md | 16 ++ imagepy/yn.html | 173 ++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 imagepy/menus/Plugins/Contribute/Contributions/FluidSurface.md create mode 100644 imagepy/yn.html diff --git a/imagepy/menus/Plugins/Contribute/Contributions/FluidSurface.md b/imagepy/menus/Plugins/Contribute/Contributions/FluidSurface.md new file mode 100644 index 00000000..206be6a1 --- /dev/null +++ b/imagepy/menus/Plugins/Contribute/Contributions/FluidSurface.md @@ -0,0 +1,16 @@ +# FluidSurface + +**Path:** https://github.com/Image-Py/FluidSurface.git + +**Version:** 0.1 + +**Author:** YXDragon、Prevalenter + +**Email:** yxdragon@imagepy.org + +**Keyword:** predict, fluid + +**Description:** The predict plug-ins of imagepy + +## Document +... \ No newline at end of file diff --git a/imagepy/yn.html b/imagepy/yn.html new file mode 100644 index 00000000..4117436c --- /dev/null +++ b/imagepy/yn.html @@ -0,0 +1,173 @@ + + + + + + + + + + +

FluidSurface

+

Path: https://github.com/Image-Py/FluidSurface.git

+

Version: 0.1

+

Author: YXDragon、Prevalenter

+

Email: yxdragon@imagepy.org

+

Keyword: predict, fluid

+

Description: The predict plug-ins of imagepy

+

Document

+

...

+ + + \ No newline at end of file From bc4005693978bab33fb10818d3c348584c7d38cb Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 10 Feb 2019 22:18:23 +0800 Subject: [PATCH 098/343] report --- imagepy/core/util/xlreport.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/imagepy/core/util/xlreport.py b/imagepy/core/util/xlreport.py index 60ff3a4e..18b3931c 100644 --- a/imagepy/core/util/xlreport.py +++ b/imagepy/core/util/xlreport.py @@ -50,7 +50,6 @@ def add_image(wb, ws, pos, key, img): if img is None: return w, h, margin, scale = eval(key[2]) img = trans(img, w, h, margin, scale==0) - img = PImage.fromarray(img) image_file = BytesIO() img.save(image_file, 'png') @@ -87,7 +86,6 @@ def fill_value(wb, infos, para): if key[0] == 'tab': add_table(wb, ws, pos, key, para[key[1]]) - def repair(wb): for ws in wb.worksheets: for cr in ws.merged_cells: From b6b476090b4273ca2fd3d3289c649841e6316a8f Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 17 Feb 2019 10:17:30 +0800 Subject: [PATCH 099/343] nothing --- .../Contribute/Contributions/Demo Repo.md | 138 +++++++++--------- 1 file changed, 73 insertions(+), 65 deletions(-) diff --git a/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md b/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md index b400ab3d..2050d0da 100644 --- a/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md +++ b/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md @@ -10,124 +10,132 @@ **Keyword:** demo, tutorial -**Description:** a friendly develop tutorial. +**Description:** a friendly development tutorial. -*这是一个ImagePy插件项目,里面覆盖了各类插件的编写方法和用法,并配有详细的文档,ImagePy的插件开发者可以以此为参考* +**[English Document](README.md) | [中文文档](READMECN.md)** +*This is a demo project to show How to write ImagePy plugin. Including the usage of all kinds of plugin, with document wrote in detail. Developers can take this project as example.* -## 安装 -ImagePy菜单:`Plugins > Manager > Plugins Manager` 在输入框内输入demo进行查询,选中Demo Plugin,点击`Install`,完成后菜单栏出现Demo菜单,工具栏会加入Demo工具,组件栏也会加入Demo组件。 +## Install + +ImagePy Menu:`Plugins > Manager > Plugins Manager` input `demo`, and select the `Demo Plugin`, then click `Install/Update`. When complete the installing, the user interface would be changed. New plugins' menu, tool, and widget would be loaded in place. ![06](http://idoc.imagepy.org/demoplugin/06.png)
Install DemoPlugin

-## [基础预备](doc/start.md) +## [Basic](doc/start.md) -**[从这里开始](doc/start.md)** +**[Start here](doc/start.md)** -1. [什么是插件](doc/start.md#什么是插件) -2. [Hello World(第一个插件)](doc/start.md#Hello-World) -3. [Who Are You(带有交互)](doc/start.md#Who-Are-You) -4. [Questionnaire(参数对话框详解)](doc/start.md#Questionnaire) -5. [一个文件内实现多个插件](doc/start.md#一个文件内实现多个插件) +1. [What is plugin](doc/start.md#What-is-plugin) +2. [Hello World(my first plugin)](doc/start.md#Hello-World) +3. [Who Are You(interactive)](doc/start.md#Who-Are-You) +4. [Questionnaire(parameter dialog in detail)](doc/start.md#Questionnaire) +5. [Multi plugin in one file](doc/start.md#Implement-multi-plugins-in-one-file) -## 插件开发 +## Plugin development -**[Markdown: 文档提示](doc/markdown.md)** +**[Markdown: document](doc/markdown.md)** 1. [Markdown Demo](doc/markdown.md#MarkDown-Demo) -**[Macros: 用宏串联已有功能](doc/macros.md#Macros)** +**[Macros: serialise existing function](doc/macros.md#Macros)** + +1. [Gaussian blur - Invert](doc/macros.md#Gaussian-Blur-Then-Invert) +2. [Coins Segmentation Macros](doc/macros.md#Coins-Segmentation) + +**[Workflow: interactive macros](doc/workflow.md)** -1. [高斯模糊再求反](doc/macros.md#高斯模糊再求反) -2. [Coins Segment Macros:硬币分割](doc/macros.md#分割硬币) +1. [Coins Segment Workflow](doc/workflow.md#Coins-Segmentation-Workflow) -**[Workflow: 可交互的宏](doc/workflow.md)** +**[Report: generate report](doc/report.md)** -1. [Coins Segment Workflow: 按照指引进行硬币分割](doc/workflow.md#硬币分割工作流) +1. [Personal Information](doc/report.md#Personal-Information) +2. [Coins Report: report for coins segment](doc/report.md#Coins-Segmentation) +3. [Rule of Report design](doc/report.md#Report-template-design-principles) -**[Filter: 二维图像滤波器](doc/filter.md)** +**[Filter: image filter in 2d](doc/filter.md)** -1. [Invert Demo:无参数的插件](doc/filter.md#Invert) -2. [Gaussian Demo:带有参数的插件](doc/filter.md#Gaussian) -3. [Filter 的运行机制](doc/filter.md#Filter-运行机制) +1. [Invert Demo: without parameter](doc/filter.md#Invert) +2. [Gaussian Demo: with parameter](doc/filter.md#Gaussian) +3. [Filter operating mechanism](doc/filter.md#Filter-operating-mechanism) -**[Simple: 图像整体操作](doc/simple.md)** +**[Simple: treat sequence and other attributes](doc/simple.md)** -1. [Gaussian 3D Demo:三维滤波](doc/simple.md#Gaussian3D) -2. [Red Lut Demo:设定索引色](doc/simple.md#SetLUT) -3. [ROI Inflate Demo:操作ROI](doc/simple.md#Inflate-ROI) -4. [Unit Demo: 设置比例尺及单位](doc/simple.md#SEt-Scale-And-Unit) -5. [Draw Mark Demo: 设置Overlay Mark](doc/simple.md#Mark) -6. [Simple 的运行机制](doc/simple.md#Simple-运行机制) +1. [Gaussian 3D Demo: filter in 3d](doc/simple.md#Gaussian3D) +2. [Red Lut Demo: operate color lookup table](doc/simple.md#SetLUT) +3. [ROI Inflate Demo: operate ROI](doc/simple.md#Inflate-ROI) +4. [Unit Demo: set unit and scale](doc/simple.md#Set-Scale-And-Unit) +5. [Draw Mark Demo: Set Mark](doc/simple.md#Mark) +6. [Simple operating mechanism](doc/simple.md#Simple-operating-mechanism) -**[Table: 表格数据](doc/table.md)** +**[Table: treat dataframe](doc/table.md)** -1. [Generate Table Demo:数据表生成](doc/table.md#生成成绩单) -2. [Sort By Key Demo:排序](doc/table.md#根据某科成绩排序) -3. [Table Plot Demo:绘图](doc/table.md#绘制柱状图) -4. [Table 运行机制](doc/table.md#Table-运行机制) +1. [Generate Table Demo: generate table](doc/table.md#Generate-score-list) +2. [Sort By Key Demo: sort](doc/table.md#Sort-by-field) +3. [Table Plot Demo: plot](doc/table.md#Bar-Chart) +4. [Table operation mechanism](doc/table.md#Table-operation-mechanism) -**[Free: 没有任何依赖的插件](doc/free.md)** +**[Free: depend on nothing](doc/free.md)** -1. [New Image Demo: 创建图像](doc/free.md#创建图像) -2. [About Demo:关于对话框](doc/free.md#关于对话框) -3. [Close Demo:退出软件](doc/free.md#退出软件) -4. [Free 的运行机制](doc/free.md#Free-的运行机制) +1. [New Image Demo: creat image](doc/free.md#Create-image) +2. [About Demo: the about dialog](doc/free.md#About-dialog-box) +3. [Close Demo: quit program](doc/free.md#Quit) +4. [Free operating mechanism](doc/free.md#Free-operating-mechanism) -**[Tool: 鼠标交互工具](doc/tool.md)** +**[Tool: mouse interaction](doc/tool.md)** -1. [Painter Demo:画笔工具](doc/tool.md#画笔工具) -2. [Tool的运行机制](doc/tool.md#Tool-的运行机制) +1. [Painter Demo: draw with mouse](doc/tool.md#Brush-Tool) +2. [Tool operating mechanism](doc/tool.md#Tool-operating-mechanism) -**[Widget: 桌面小部件](doc/widget.md)** +**[Widget: customed panel](doc/widget.md)** -1. [Widget Demo:桌面小部件演示](doc/widget.md#桌面组件演示) -2. [Tool的运行机制](doc/widget.md#widget-的运行机制) +1. [Widget Demo](doc/widget.md#Widget-Demo) +2. [Widget opterating mechanism](doc/widget.md#Widget-opterating-mechanism) -## [插件项目发布](doc/publish.md) +## [Plugin Release](doc/publish.md) -**[插件的组织方式](doc/publish.md#功能组织)** +**[Function Organization](doc/publish.md#Function-organization)** -1. [功能划分](doc/publish.md#功能组织) -2. [顺序设定](doc/publish.md#功能组织) +1. [Functional partitioning](doc/publish.md#Function-organization) +2. [Set Order](doc/publish.md#Set-Order) -**[插件项目创建](doc/publish.md#插件项目创建)** +**[Plugin project creation](doc/publish.md#Plugin-project-creation)** -1. [创建插件项目仓库](doc/publish.md#插件项目创建) -2. [编写requirements](doc/publish.md#插件项目创建) -3. [编写readme](doc/publish.md#插件项目创建) -4. [插件的安装](doc/publish.md#插件项目创建) +1. [Create a plugin project repository](doc/publish.md#Plugin-project-creation) +2. [Write requirements](doc/publish.md#Plugin-project-creation) +3. [Write readme](doc/publish.md#Plugin-project-creation) +4. [Install Plugin](doc/publish.md#Plugin-project-creation) -**[发布到 ImagePy](doc/publish.md#发布到-ImagePy)** +**[Release to ImagePy](doc/publish.md#Release-to-ImagePy)** -1. [给ImagePy发Pull Request](doc/publish.md#发布到-ImagePy) -2. [关于顶级菜单](doc/publish.md#发布到-ImagePy) +1. [Send Pull Request to ImagePy](doc/publish.md#Release-to-ImagePy) +2. [About the top-level menu](doc/publish.md#Release-to-ImagePy) -## [文档编写](doc/document.md) +## [Write Document](doc/document.md) -**[编写操作手册](doc/document.md#编写操作手册)** +**[Write The Opteration Manual](doc/document.md#Write-The-Opteration-Manual)** -**[查阅操作手册](doc/document.md#查阅操作手册)** +**[View The Operation Manual](doc/document.md#View-Operation-Manual)** -## [注意事项](doc/attention.md#注意事项) +## [Attention](doc/attention.md#注意事项) -**[用户友好性](doc/attention.md#用户友好性)** +**[User Friendliness](doc/attention.md#User-Friendliness)** -**[开发者友好性](doc/attention.md#开发者友好性)** +**[Developer Friendliness](doc/attention.md#Developer-Friendliness)** -**[及时沟通](doc/attention.md#及时沟通)** +**[Communicate Timely](doc/attention.md#Communicate-Timely)** -**本篇文档相对系统的介绍了ImagePy的插件开发,但是依然无法详尽,关于更多ImagePy使用,开发上的问题请在[forum.Image.sc](https://forum.image.sc/)行进行讨论** \ No newline at end of file +**This document introduces how to write ImagePy plugin. More questions not exhaustive here,please post in [forum.Image.sc](https://forum.image.sc/)** \ No newline at end of file From 89242884f26a2298837309a7fd96450ac0ca7611 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 18 Feb 2019 23:41:55 +0800 Subject: [PATCH 100/343] slic --- imagepy/core/engine/filter.py | 2 +- .../menus/File/Samples Local/samples_plgs.py | 2 +- imagepy/menus/Process/Segment/__init__.py | 1 + imagepy/menus/Process/Segment/shift_plgs.py | 47 +++++++++++++++++++ 4 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 imagepy/menus/Process/Segment/shift_plgs.py diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index 5d311f82..111f3e62 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -39,7 +39,7 @@ def process_one(plg, ips, src, img, para, callafter=None): src = src.astype(np.float32) rst = process_channels(plg, ips, src, buf if transint or transfloat else img, para) if not img is rst and not rst is None: - imgrange = {np.uint8:(0,255), np.uint16:(0,65535)}[img.dtype.type] + imgrange = {np.uint8:(0,255), np.uint16:(0, 65535)}[img.dtype.type] np.clip(rst, imgrange[0], imgrange[1], out=img) if 'auto_msk' in plg.note and not ips.get_msk() is None: msk = True ^ ips.get_msk() diff --git a/imagepy/menus/File/Samples Local/samples_plgs.py b/imagepy/menus/File/Samples Local/samples_plgs.py index fda7d84a..2521ddc2 100644 --- a/imagepy/menus/File/Samples Local/samples_plgs.py +++ b/imagepy/menus/File/Samples Local/samples_plgs.py @@ -21,6 +21,6 @@ def __call__(self): return self datas = ['face', 'ascent', '-', 'page', 'astronaut', 'horse', 'camera', - 'hubble_deep_field', 'coins', 'immunohistochemistry', 'moon'] + 'coffee', 'hubble_deep_field', 'coins', 'immunohistochemistry', 'moon'] plgs = [i if i=='-' else Data(i) for i in datas] \ No newline at end of file diff --git a/imagepy/menus/Process/Segment/__init__.py b/imagepy/menus/Process/Segment/__init__.py index e69de29b..5059f0d3 100644 --- a/imagepy/menus/Process/Segment/__init__.py +++ b/imagepy/menus/Process/Segment/__init__.py @@ -0,0 +1 @@ +catlog = ['shift_plgs', '-', 'active_plgs'] \ No newline at end of file diff --git a/imagepy/menus/Process/Segment/shift_plgs.py b/imagepy/menus/Process/Segment/shift_plgs.py new file mode 100644 index 00000000..a8358c60 --- /dev/null +++ b/imagepy/menus/Process/Segment/shift_plgs.py @@ -0,0 +1,47 @@ +from imagepy.core.engine import Filter +from skimage import data, io, segmentation, color + +class SLIC(Filter): + title = 'SLIC Superpixel' + note = ['all', 'not_slice', 'auto_snap', 'auto_msk', 'not_channel', 'preview'] + + para = {'n_segments':100, 'compactness':10.0, 'max_iter':10, 'sigma':0} + view = [(int, 'n_segments', (1, 1e8), 0, 'segments', 'n'), + (float, 'compactness', (0.01, 100), 2, 'campactness', 'color-space'), + (int, 'max_iter', (3, 50), 0, 'max_iter', 'n'), + (float, 'sigma', (0, 30), 1, 'sigma', 'smooth')] + + def run(self, ips, snap, img, para = None): + lab = segmentation.slic(snap, para['n_segments'], + para['compactness'], para['max_iter'], para['sigma']) + return color.label2rgb(lab, snap, kind='avg') + +class Quickshift(Filter): + title = 'Quick Shift' + note = ['all', 'not_slice', 'auto_snap', 'auto_msk', 'not_channel', 'preview'] + + para = {'ratio':1.0, 'kernel_size':5, 'max_dist':10, 'sigma':0} + view = [(float, 'ratio', (0, 1), 2, 'ratio', 'color-space'), + (float, 'kernel_size', (0, 30), 2, 'kernel_size', ''), + (float, 'max_dist', (1, 1024), 2, 'distance', 'cut off'), + (float, 'sigma', (0, 30), 1, 'sigma', 'smooth')] + + def run(self, ips, snap, img, para = None): + lab = segmentation.quickshift(snap, para['ratio'], para['kernel_size'], + para['max_dist'], para['sigma']) + return color.label2rgb(lab, snap, kind='avg') + +class Felzenszwalb(Filter): + title = 'Felzenszwalb' + note = ['all', 'not_slice', 'auto_snap', 'auto_msk', 'not_channel', 'preview'] + + para = {'scale':1.0, 'sigma':0.8, 'min_size':20} + view = [(float, 'scale', (0.01, 10), 2, 'scale', ''), + (float, 'sigma', (0, 30), 2, 'sigma', ''), + (int, 'min_size', (1, 1024), 0, 'min_size', '')] + + def run(self, ips, snap, img, para = None): + lab = segmentation.felzenszwalb(snap, para['scale'], para['sigma'], para['min_size']) + return color.label2rgb(lab, snap, kind='avg') + +plgs = [SLIC, Quickshift, Felzenszwalb] \ No newline at end of file From 3837baff88bb001ebd63accf4dc6a2fccd1bc00a Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 21 Feb 2019 15:13:48 +0800 Subject: [PATCH 101/343] nothing --- imagepy/menus/Analysis/Region Analysis/statistic_plgs.py | 3 ++- imagepy/menus/Table/Statistic/statistic_plgs.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py index 761c8765..86c1698c 100644 --- a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py @@ -13,6 +13,7 @@ from imagepy.core.roi.pointroi import PointRoi import pandas as pd from imagepy.core.mark import GeometryMark + class Mark: def __init__(self, data): self.data = data @@ -115,7 +116,7 @@ def run(self, ips, imgs, para = None): mark['body'][i] = layer data.extend(list(zip(*dt))) - IPy.show_table(pd.DataFrame(data, columns=titles), inten.title+'-region statistic') + IPy.show_table(pd.DataFrame(data, columns=titles), inten.title+'-pixels') inten.mark = GeometryMark(mark) inten.update() diff --git a/imagepy/menus/Table/Statistic/statistic_plgs.py b/imagepy/menus/Table/Statistic/statistic_plgs.py index befa3571..7ba08140 100644 --- a/imagepy/menus/Table/Statistic/statistic_plgs.py +++ b/imagepy/menus/Table/Statistic/statistic_plgs.py @@ -29,7 +29,9 @@ def run(self, tps, snap, data, para=None): if para['std']:rst['std'] = snap.std(axis=axis) if para['skew']:rst['skew'] = snap.skew(axis=axis) if para['kurt']:rst['kurt'] = snap.kurt(axis=axis) - IPy.show_table(pd.DataFrame(rst), tps.title+'-statistic') + cols = ['sum', 'mean', 'min', 'max', 'var', 'std', 'skew', 'kurt'] + cols = [i for i in cols if i in rst] + IPy.show_table(pd.DataFrame(rst, columns=cols).T, tps.title+'-statistic') class GroupStatistic(Table): title = 'Group Statistic' From a5c0f4d8c76e7d12d52d95f61df7140bee6efaba Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 24 Feb 2019 23:38:30 +0800 Subject: [PATCH 102/343] graph --- imagepy/core/engine/simple.py | 8 +- imagepy/menus/Process/Segment/__init__.py | 2 +- imagepy/menus/Process/Segment/graph_plgs.py | 133 ++++++++++++++++++++ imagepy/menus/Process/Segment/shift_plgs.py | 90 ++++++++++++- 4 files changed, 227 insertions(+), 6 deletions(-) create mode 100644 imagepy/menus/Process/Segment/graph_plgs.py diff --git a/imagepy/core/engine/simple.py b/imagepy/core/engine/simple.py index 57bfb3fd..9db7a8ee 100644 --- a/imagepy/core/engine/simple.py +++ b/imagepy/core/engine/simple.py @@ -38,10 +38,10 @@ def show(self, temp=ParaDialog): self.dialog = temp(IPy.get_window(), self.title) self.dialog.init_view(self.view, self.para, 'preview' in self.note, modal=self.modal) self.dialog.on_help = lambda : IPy.show_md(self.title, DocumentManager.get(self.title)) - self.dialog.set_handle(lambda x:self.preview(self.ips, self.para)) + self.dialog.set_handle(lambda x:self.preview(self.ips, self.para) is self.ips.update()) if self.modal: return self.dialog.ShowModal() == wx.ID_OK self.dialog.on_ok = lambda : self.ok(self.ips) - self.dialog.on_cancel = lambda : self.cancel(self.ips) + self.dialog.on_cancel = lambda : self.cancel(self.ips) is self.ips.update() self.dialog.Show() def run(self, ips, imgs, para = None):pass @@ -118,6 +118,8 @@ def start(self, para=None, callback=None): elif self.modal: if self.show(): self.ok(self.ips, para, callback) - else:self.cancel(self.ips) + else: + self.cancel(self.ips) + self.ips.update() if not self.dialog is None: self.dialog.Destroy() else: self.show() \ No newline at end of file diff --git a/imagepy/menus/Process/Segment/__init__.py b/imagepy/menus/Process/Segment/__init__.py index 5059f0d3..47a61deb 100644 --- a/imagepy/menus/Process/Segment/__init__.py +++ b/imagepy/menus/Process/Segment/__init__.py @@ -1 +1 @@ -catlog = ['shift_plgs', '-', 'active_plgs'] \ No newline at end of file +catlog = ['shift_plgs', '-', 'graph_plgs', '-', 'active_plgs'] \ No newline at end of file diff --git a/imagepy/menus/Process/Segment/graph_plgs.py b/imagepy/menus/Process/Segment/graph_plgs.py new file mode 100644 index 00000000..653e6bf9 --- /dev/null +++ b/imagepy/menus/Process/Segment/graph_plgs.py @@ -0,0 +1,133 @@ +from imagepy.core.engine import Simple +from imagepy import IPy +from skimage import data, io, segmentation, color +from imagepy.core.manager import ImageManager +from skimage.future import graph +import numpy as np + +class RagThreshold(Simple): + title = 'RAG Threshold' + note = ['all', 'preview'] + + para = {'lab':None, 'thresh':10, 'connect':'8-connected', 'mode':'distance', 'sigma':255.0, 'stack':False} + view = [('img', 'lab', 'label', ''), + (int, 'thresh', (1, 1024), 0, 'threshold', ''), + (list, 'connect', ['4-connected', '8-connected'], str, 'connectivity', ''), + (list, 'mode', ['distance', 'similarity'], str, 'mode', ''), + (float, 'sigma', (0, 1024), 1, 'sigma', 'similarity'), + (bool, 'stack', 'stack')] + + def load(self, ips): return ips.snapshot()==None + def cancel(self, ips): ips.swap() + + def preview(self, ips, para): + lab = ImageManager.get(para['lab']).img + connect = ['4-connected', '8-connected'].index(para['connect']) + 1 + g = graph.rag_mean_color(ips.snap, lab, connect, para['mode'], para['sigma']) + lab = graph.cut_threshold(lab, g, para['thresh']) + ips.img[:] = color.label2rgb(lab, ips.snap, kind='avg') + + def run(self, ips, imgs, para = None): + if not para['stack']: + imgs, labs = [ips.img], [ImageManager.get(para['lab']).img] + else: + labs = ImageManager.get(para['lab']).imgs + if len(imgs) != len(labs): + labs = [ImageManager.get(para['lab']).img] * len(imgs) + for i in range(len(imgs)): + img, lab = imgs[i], labs[i] + connect = ['4-connected', '8-connected'].index(para['connect']) + 1 + g = graph.rag_mean_color(img, lab, connect, para['mode'], para['sigma']) + lab = graph.cut_threshold(lab, g, para['thresh']) + img[:] = color.label2rgb(lab, img, kind='avg') + self.progress(i, len(imgs)) + +class NormalCut(Simple): + title = 'RAG Cut Normalized' + note = ['all', 'preview'] + + para = {'lab':None, 'thresh':0.001, 'num':10, 'connect':'8-connected', 'mode':'distance', 'sigma':255.0, 'stack':False} + view = [('img', 'lab', 'label', ''), + (float, 'thresh', (0.001, 1), 3, 'threshold', ''), + (int, 'num', (1, 1024), 0, 'num', 'cut'), + (list, 'connect', ['4-connected', '8-connected'], str, 'connectivity', ''), + (list, 'mode', ['distance', 'similarity'], str, 'mode', ''), + (float, 'sigma', (0, 1024), 1, 'sigma', 'similarity'), + (bool, 'stack', 'stack')] + + def load(self, ips): return ips.snapshot()==None + def cancel(self, ips): ips.swap() + + def preview(self, ips, para): + lab = ImageManager.get(para['lab']).img + connect = ['4-connected', '8-connected'].index(para['connect']) + 1 + g = graph.rag_mean_color(ips.snap, lab, connect, para['mode'], para['sigma']) + lab = graph.cut_normalized(lab, g, para['thresh'], para['num']) + ips.img[:] = color.label2rgb(lab, ips.snap, kind='avg') + + def run(self, ips, imgs, para = None): + if not para['stack']: + imgs, labs = [ips.img], [ImageManager.get(para['lab']).img] + else: + labs = ImageManager.get(para['lab']).imgs + if len(imgs) != len(labs): + labs = [ImageManager.get(para['lab']).img] * len(imgs) + for i in range(len(imgs)): + img, lab = imgs[i], labs[i] + connect = ['4-connected', '8-connected'].index(para['connect']) + 1 + g = graph.rag_mean_color(img, lab, connect, para['mode'], para['sigma']) + lab = graph.cut_normalized(lab, g, para['thresh'], para['num']) + img[:] = color.label2rgb(lab, img, kind='avg') + self.progress(i, len(imgs)) + +def _weight_mean_color(graph, src, dst, n): + diff = graph.node[dst]['mean color'] - graph.node[n]['mean color'] + diff = np.linalg.norm(diff) + return {'weight': diff} + +def merge_mean_color(graph, src, dst): + graph.node[dst]['total color'] += graph.node[src]['total color'] + graph.node[dst]['pixel count'] += graph.node[src]['pixel count'] + graph.node[dst]['mean color'] = (graph.node[dst]['total color'] / + graph.node[dst]['pixel count']) + +class MergeHierarchical(Simple): + title = 'RAG Merge Hierarchical' + note = ['all', 'preview'] + + para = {'lab':None, 'thresh':35, 'connect':'8-connected', 'mode':'distance', 'sigma':255.0, 'stack':False} + view = [('img', 'lab', 'label', ''), + (int, 'thresh', (1, 1024), 0, 'threshold', ''), + (list, 'connect', ['4-connected', '8-connected'], str, 'connectivity', ''), + (list, 'mode', ['distance', 'similarity'], str, 'mode', ''), + (float, 'sigma', (0, 1024), 1, 'sigma', 'similarity'), + (bool, 'stack', 'stack')] + + def load(self, ips): return ips.snapshot()==None + def cancel(self, ips): ips.swap() + + def preview(self, ips, para): + lab = ImageManager.get(para['lab']).img + connect = ['4-connected', '8-connected'].index(para['connect']) + 1 + g = graph.rag_mean_color(ips.snap, lab, connect, para['mode'], para['sigma']) + lab = graph.merge_hierarchical(lab, g, thresh=para['thresh'], rag_copy=False, + in_place_merge=True, merge_func=merge_mean_color, weight_func=_weight_mean_color) + ips.img[:] = color.label2rgb(lab, ips.snap, kind='avg') + + def run(self, ips, imgs, para = None): + if not para['stack']: + imgs, labs = [ips.img], [ImageManager.get(para['lab']).img] + else: + labs = ImageManager.get(para['lab']).imgs + if len(imgs) != len(labs): + labs = [ImageManager.get(para['lab']).img] * len(imgs) + for i in range(len(imgs)): + img, lab = imgs[i], labs[i] + connect = ['4-connected', '8-connected'].index(para['connect']) + 1 + g = graph.rag_mean_color(img, lab, connect, para['mode'], para['sigma']) + lab = graph.merge_hierarchical(lab, g, thresh=para['thresh'], rag_copy=False, + in_place_merge=True, merge_func=merge_mean_color, weight_func=_weight_mean_color) + img[:] = color.label2rgb(lab, img, kind='avg') + self.progress(i, len(imgs)) + +plgs = [RagThreshold, NormalCut, MergeHierarchical] \ No newline at end of file diff --git a/imagepy/menus/Process/Segment/shift_plgs.py b/imagepy/menus/Process/Segment/shift_plgs.py index a8358c60..c5710d93 100644 --- a/imagepy/menus/Process/Segment/shift_plgs.py +++ b/imagepy/menus/Process/Segment/shift_plgs.py @@ -1,4 +1,5 @@ -from imagepy.core.engine import Filter +from imagepy.core.engine import Filter, Simple +from imagepy import IPy from skimage import data, io, segmentation, color class SLIC(Filter): @@ -44,4 +45,89 @@ def run(self, ips, snap, img, para = None): lab = segmentation.felzenszwalb(snap, para['scale'], para['sigma'], para['min_size']) return color.label2rgb(lab, snap, kind='avg') -plgs = [SLIC, Quickshift, Felzenszwalb] \ No newline at end of file +class SLICLab(Simple): + title = 'SLIC Superpixel Label' + note = ['all', 'preview'] + + para = {'n_segments':100, 'compactness':10.0, 'max_iter':10, 'sigma':0, 'stack':False} + view = [(int, 'n_segments', (1, 1e8), 0, 'segments', 'n'), + (float, 'compactness', (0.01, 100), 2, 'campactness', 'color-space'), + (int, 'max_iter', (3, 50), 0, 'max_iter', 'n'), + (float, 'sigma', (0, 30), 1, 'sigma', 'smooth'), + (bool, 'stack', 'stack')] + + def load(self, ips): return ips.snapshot()==None + def cancel(self, ips): ips.swap() + + def preview(self, ips, para): + lab = segmentation.slic(ips.snap, para['n_segments'], + para['compactness'], para['max_iter'], para['sigma']) + ips.img[:] = color.label2rgb(lab, ips.snap, kind='avg') + + def run(self, ips, imgs, para = None): + if not para['stack']: imgs = [ips.img] + ips.swap() + rst = [] + for i in range(len(imgs)): + rst.append(segmentation.slic(imgs[i], para['n_segments'], + para['compactness'], para['max_iter'], para['sigma']).astype('int32')) + self.progress(i, len(imgs)) + IPy.show_img(rst, ips.title+'-sliclab') + +class QuickshiftLab(Simple): + title = 'Quickshift Label' + note = ['all', 'preview'] + + para = {'ratio':1.0, 'kernel_size':5, 'max_dist':10, 'sigma':0, 'stack':False} + view = [(float, 'ratio', (0, 1), 2, 'ratio', 'color-space'), + (float, 'kernel_size', (0, 30), 2, 'kernel_size', ''), + (float, 'max_dist', (1, 1024), 2, 'distance', 'cut off'), + (float, 'sigma', (0, 30), 1, 'sigma', 'smooth'), + (bool, 'stack', 'stack')] + + def load(self, ips): return ips.snapshot()==None + def cancel(self, ips): ips.swap() + + def preview(self, ips, para): + lab = segmentation.quickshift(ips.snap, para['ratio'], para['kernel_size'], + para['max_dist'], para['sigma']) + ips.img[:] = color.label2rgb(lab, ips.snap, kind='avg') + + def run(self, ips, imgs, para = None): + if not para['stack']: imgs = [ips.img] + ips.swap() + rst = [] + for i in range(len(imgs)): + rst.append(segmentation.quickshift(imgs[i], para['ratio'], para['kernel_size'], + para['max_dist'], para['sigma']).astype('int32')) + self.progress(i, len(imgs)) + IPy.show_img(rst, ips.title+'-quickshiftlab') + +class FelzenszwalbLab(Simple): + title = 'Felzenszwalb Label' + note = ['all', 'preview'] + + para = {'scale':1.0, 'sigma':0.8, 'min_size':20, 'stack':False} + view = [(float, 'scale', (0.01, 10), 2, 'scale', ''), + (float, 'sigma', (0, 30), 2, 'sigma', ''), + (int, 'min_size', (1, 1024), 0, 'min_size', ''), + (bool, 'stack', 'stack')] + + def load(self, ips): return ips.snapshot()==None + def cancel(self, ips): ips.swap() + + def preview(self, ips, para): + lab = segmentation.felzenszwalb(ips.snap, para['scale'], para['sigma'], para['min_size']) + ips.img[:] = color.label2rgb(lab, ips.snap, kind='avg') + + def run(self, ips, imgs, para = None): + if not para['stack']: imgs = [ips.img] + ips.swap() + rst = [] + for i in range(len(imgs)): + rst.append(segmentation.felzenszwalb(imgs[i], para['scale'], + para['sigma'], para['min_size']).astype('int32')) + self.progress(i, len(imgs)) + IPy.show_img(rst, ips.title+'-felzenszwalblab') + +plgs = [SLIC, Quickshift, Felzenszwalb, '-', SLICLab, QuickshiftLab, FelzenszwalbLab] \ No newline at end of file From 5f8484f58102588d4e97d6fcf098828b4343dc3b Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 25 Feb 2019 01:45:55 +0800 Subject: [PATCH 103/343] nothing --- imagepy/core/manager/windowmanager.py | 3 ++- imagepy/ui/canvasframe.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/imagepy/core/manager/windowmanager.py b/imagepy/core/manager/windowmanager.py index e3a52550..527f52d6 100644 --- a/imagepy/core/manager/windowmanager.py +++ b/imagepy/core/manager/windowmanager.py @@ -10,7 +10,8 @@ class WindowsManager: @classmethod def add(cls, win): - if not win in cls.wins:cls.wins.append(win) + if win in cls.wins: cls.remove(win) + cls.wins.insert(0, win) @classmethod def get(cls, title=None): diff --git a/imagepy/ui/canvasframe.py b/imagepy/ui/canvasframe.py index 04c1c9b7..e926b8d0 100644 --- a/imagepy/ui/canvasframe.py +++ b/imagepy/ui/canvasframe.py @@ -145,6 +145,7 @@ def set_title(self, ips, resized): def on_valid(self, event): if event.GetActive(): ImageManager.add(self.canvaspanel.ips) + WindowsManager.add(self.canvaspanel) def on_close(self, event): self.canvaspanel.set_handler() @@ -192,6 +193,7 @@ def set_title(self, ips, panel): def on_pagevalid(self, event): ImageManager.add(event.GetEventObject().GetPage(event.GetSelection()).ips) + WindowsManager.add(event.GetEventObject().GetPage(event.GetSelection())) def on_close(self, event): print('page close') From fb0f9a14ac3f88c159a486cea13292aab6f7e777 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 28 Feb 2019 22:55:19 +0800 Subject: [PATCH 104/343] stackreg --- environment.yml | 1 + imagepy/menus/Plugins/StackReg/__init__.py | 0 .../menus/Plugins/StackReg/stackreg_plgs.py | 57 +++++++++++++++++++ imagepy/menus/Plugins/__init__.py | 2 +- imagepy/menus/Process/Filters/classic_plgs.py | 1 - setup.py | 1 + 6 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 imagepy/menus/Plugins/StackReg/__init__.py create mode 100644 imagepy/menus/Plugins/StackReg/stackreg_plgs.py diff --git a/environment.yml b/environment.yml index 5b762132..2a7a86f0 100644 --- a/environment.yml +++ b/environment.yml @@ -7,6 +7,7 @@ dependencies: - openpyxl - pandas - pydicom + - pystackreg - pypubsub - read-roi - scikit-image diff --git a/imagepy/menus/Plugins/StackReg/__init__.py b/imagepy/menus/Plugins/StackReg/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/menus/Plugins/StackReg/stackreg_plgs.py b/imagepy/menus/Plugins/StackReg/stackreg_plgs.py new file mode 100644 index 00000000..69bea3b5 --- /dev/null +++ b/imagepy/menus/Plugins/StackReg/stackreg_plgs.py @@ -0,0 +1,57 @@ +from imagepy.core.engine import Filter, Simple +from imagepy import IPy +from pystackreg import StackReg +import numpy as np +import pandas as pd +from skimage import transform as tf +from imagepy.core.manager import TableManager + +class Register(Simple): + title = 'Stack Register' + note = ['8-bit', '16-bit', 'int', 'float', 'stack'] + + para = {'trans':'RIGID_BODY', 'ref':'previous', 'tab':False, 'new':'Inplace'} + view = [(list, 'trans', ['TRANSLATION', 'RIGID_BODY', 'SCALED_ROTATION', 'AFFINE', 'BILINEAR'], str, 'transform', ''), + (list, 'ref', ['previous', 'first', 'mean'], str, 'image', ''), + (list, 'new', ['Inplace', 'New', 'None'], str, 'reference', ''), + (bool, 'tab', 'show table')] + + def run(self, ips, imgs, para = None): + news = np.array(imgs) + sr = StackReg(eval('StackReg.%s'%para['trans'])) + sr.register_stack(news, reference=para['ref']) + mats = sr._tmats.reshape((sr._tmats.shape[0],-1)) + if para['tab']: IPy.show_table(pd.DataFrame( + mats, columns=['A%d'%(i+1) for i in range(mats.shape[1])]), title='%s-Tmats'%ips.title) + if para['new'] == 'None': return + for i in range(sr._tmats.shape[0]): + tform = tf.ProjectiveTransform(matrix=sr._tmats[i]) + if para['new'] == 'Inplace': + imgs[i] = tf.warp(imgs[i], tform)*imgs[i].max() + if para['new'] == 'New': + news[i] = tf.warp(imgs[i], tform)*imgs[i].max() + if para['new'] == 'New': IPy.show_img(news, '%s-reg'%ips.title) + +class Transform(Simple): + title = 'Register By Mats' + note = ['all'] + + para = {'mat':None, 'new':True} + view = [('tab', 'mat', 'transfrom', 'matrix'), + (bool, 'new', 'new image')] + + def run(self, ips, imgs, para = None): + mats = TableManager.get(para['mat']).data.values + if len(imgs) != len(mats): + IPy.alert('image stack must has the same length as transfrom mats!') + return + newimgs = [] + for i in range(len(mats)): + tform = tf.ProjectiveTransform(matrix=mats[i].reshape((3,3))) + img = tf.warp(imgs[i], tform) + img -= imgs[i].min(); img *= imgs[i].max() - imgs[i].min() + if para['new']: newimgs.append(img) + else: imgs[i] = img + if para['new']: IPy.show_img(newimgs, '%s-trans'%ips.title) + +plgs = [Register, Transform] \ No newline at end of file diff --git a/imagepy/menus/Plugins/__init__.py b/imagepy/menus/Plugins/__init__.py index dbf77906..983573c9 100644 --- a/imagepy/menus/Plugins/__init__.py +++ b/imagepy/menus/Plugins/__init__.py @@ -1 +1 @@ -catlog = ['New', 'Macros', 'Manager', '-', 'Install', 'Contribute', 'update_plg', '-', 'screencap_plg', 'Games'] \ No newline at end of file +catlog = ['New', 'Macros', 'Manager', '-', 'Install', 'Contribute', 'update_plg', '-', 'StackReg', 'Games', 'screencap_plg'] \ No newline at end of file diff --git a/imagepy/menus/Process/Filters/classic_plgs.py b/imagepy/menus/Process/Filters/classic_plgs.py index 84453a00..c6617183 100644 --- a/imagepy/menus/Process/Filters/classic_plgs.py +++ b/imagepy/menus/Process/Filters/classic_plgs.py @@ -170,7 +170,6 @@ class Variance(Filter): #process def run(self, ips, snap, img, para = None): - print(snap.dtype, img.dtype) nimg.uniform_filter(snap**2, para['size'], output=img) img -= nimg.uniform_filter(snap, para['size'])**2 diff --git a/setup.py b/setup.py index e511acbf..77ca002b 100644 --- a/setup.py +++ b/setup.py @@ -30,6 +30,7 @@ def get_data_files(): 'read_roi', 'numpy-stl', 'pydicom', + 'pystackreg', 'pandas', 'xlrd', 'xlwt', From b4c45b360adae882ed1e6f59a60b20b854e71a3b Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 28 Feb 2019 23:01:08 +0800 Subject: [PATCH 105/343] stackreg --- imagepy/menus/Plugins/StackReg/stackreg_plgs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imagepy/menus/Plugins/StackReg/stackreg_plgs.py b/imagepy/menus/Plugins/StackReg/stackreg_plgs.py index 69bea3b5..46afc13f 100644 --- a/imagepy/menus/Plugins/StackReg/stackreg_plgs.py +++ b/imagepy/menus/Plugins/StackReg/stackreg_plgs.py @@ -12,8 +12,8 @@ class Register(Simple): para = {'trans':'RIGID_BODY', 'ref':'previous', 'tab':False, 'new':'Inplace'} view = [(list, 'trans', ['TRANSLATION', 'RIGID_BODY', 'SCALED_ROTATION', 'AFFINE', 'BILINEAR'], str, 'transform', ''), - (list, 'ref', ['previous', 'first', 'mean'], str, 'image', ''), - (list, 'new', ['Inplace', 'New', 'None'], str, 'reference', ''), + (list, 'ref', ['previous', 'first', 'mean'], str, 'reference', ''), + (list, 'new', ['Inplace', 'New', 'None'], str, 'image', ''), (bool, 'tab', 'show table')] def run(self, ips, imgs, para = None): From 020fd57046a341dd1d0dedcc6bcad37fedc98e1b Mon Sep 17 00:00:00 2001 From: WeisongZhao Date: Fri, 1 Mar 2019 01:24:57 +0800 Subject: [PATCH 106/343] v0.20, update requirements.txt --- requirements.txt | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index 11c7d730..265dd7b7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,15 @@ +numba +numpy-stl +openpyxl +pandas +pydicom +pystackreg +pypubsub +read-roi scikit-image shapely wxpython -numba -pandas xlrd xlwt -openpyxl -pydicom -read-roi -numpy-stl +markdown +moderngl From 5d5bb36be570592cb59981bf9cdf8e5aca9aad4c Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 1 Mar 2019 11:57:54 +0800 Subject: [PATCH 107/343] stackreg --- .../Plugins/StackReg/StackReg License.md | 12 +++++++ imagepy/menus/Plugins/StackReg/__init__.py | 1 + .../menus/Plugins/StackReg/stackreg_plgs.py | 36 ++++++++++++++++--- 3 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 imagepy/menus/Plugins/StackReg/StackReg License.md diff --git a/imagepy/menus/Plugins/StackReg/StackReg License.md b/imagepy/menus/Plugins/StackReg/StackReg License.md new file mode 100644 index 00000000..9d321c0d --- /dev/null +++ b/imagepy/menus/Plugins/StackReg/StackReg License.md @@ -0,0 +1,12 @@ +# Gratitude for StackReg + +You'll be free to use this software for research purposes, but you +should not redistribute it without our consent. In addition, we expect +you to include a citation or acknowledgment whenever you present or +publish results that are based on it. + +**Additional help available at [http://bigwww.epfl.ch/thevenaz/turboreg](http://bigwww.epfl.ch/thevenaz/turboreg/)** + +![befor](http://bigwww.epfl.ch/thevenaz/turboreg/before.gif) +![after](http://bigwww.epfl.ch/thevenaz/turboreg/after.gif) + diff --git a/imagepy/menus/Plugins/StackReg/__init__.py b/imagepy/menus/Plugins/StackReg/__init__.py index e69de29b..ab791f79 100644 --- a/imagepy/menus/Plugins/StackReg/__init__.py +++ b/imagepy/menus/Plugins/StackReg/__init__.py @@ -0,0 +1 @@ +catlog = ['stackreg_plgs', '-', 'StackReg License'] \ No newline at end of file diff --git a/imagepy/menus/Plugins/StackReg/stackreg_plgs.py b/imagepy/menus/Plugins/StackReg/stackreg_plgs.py index 46afc13f..fceb8655 100644 --- a/imagepy/menus/Plugins/StackReg/stackreg_plgs.py +++ b/imagepy/menus/Plugins/StackReg/stackreg_plgs.py @@ -4,32 +4,52 @@ import numpy as np import pandas as pd from skimage import transform as tf +import scipy.ndimage as ndimg from imagepy.core.manager import TableManager class Register(Simple): title = 'Stack Register' note = ['8-bit', '16-bit', 'int', 'float', 'stack'] - para = {'trans':'RIGID_BODY', 'ref':'previous', 'tab':False, 'new':'Inplace'} + para = {'trans':'RIGID_BODY', 'ref':'previous', 'tab':False, 'new':'Inplace', 'diag':0, 'sigma':0} view = [(list, 'trans', ['TRANSLATION', 'RIGID_BODY', 'SCALED_ROTATION', 'AFFINE', 'BILINEAR'], str, 'transform', ''), (list, 'ref', ['previous', 'first', 'mean'], str, 'reference', ''), (list, 'new', ['Inplace', 'New', 'None'], str, 'image', ''), + (int, 'diag', (0, 2048), 0, 'diagonal', 'scale'), + (float, 'sigma', (0,30), 1, 'sigma', 'blur'), (bool, 'tab', 'show table')] def run(self, ips, imgs, para = None): - news = np.array(imgs) + k = para['diag']/np.sqrt((np.array(ips.img.shape)**2).sum()) + size = tuple((np.array(ips.img.shape)*k).astype(np.int16)) + IPy.set_info('down sample...') + news = [] + for img in imgs: + if k!=0: img = tf.resize(img, size) + if para['sigma']!=0: + img = ndimg.gaussian_filter(img, para['sigma']) + news.append(img) + + IPy.set_info('register...') sr = StackReg(eval('StackReg.%s'%para['trans'])) - sr.register_stack(news, reference=para['ref']) + sr.register_stack(np.array(news), reference=para['ref']) + mats = sr._tmats.reshape((sr._tmats.shape[0],-1)) + if k!=0: mats[:,[0,1,3,4,6,7]] *= k + if k!=0: mats[:,[0,1,2,3,4,5]] /= k + if para['tab']: IPy.show_table(pd.DataFrame( mats, columns=['A%d'%(i+1) for i in range(mats.shape[1])]), title='%s-Tmats'%ips.title) + if para['new'] == 'None': return + IPy.set_info('transform...') for i in range(sr._tmats.shape[0]): tform = tf.ProjectiveTransform(matrix=sr._tmats[i]) if para['new'] == 'Inplace': imgs[i] = tf.warp(imgs[i], tform)*imgs[i].max() if para['new'] == 'New': news[i] = tf.warp(imgs[i], tform)*imgs[i].max() + self.progress(i, len(imgs)) if para['new'] == 'New': IPy.show_img(news, '%s-reg'%ips.title) class Transform(Simple): @@ -46,12 +66,18 @@ def run(self, ips, imgs, para = None): IPy.alert('image stack must has the same length as transfrom mats!') return newimgs = [] + img = np.zeros_like(ips.img, dtype=np.float64) for i in range(len(mats)): tform = tf.ProjectiveTransform(matrix=mats[i].reshape((3,3))) - img = tf.warp(imgs[i], tform) + if imgs[i].ndim==2: + img[:] = tf.warp(imgs[i], tform) + else: + for c in range(img.shape[2]): + img[:,:,c] = tf.warp(imgs[i][:,:,c], tform) img -= imgs[i].min(); img *= imgs[i].max() - imgs[i].min() - if para['new']: newimgs.append(img) + if para['new']: newimgs.append(img.astype(ips.img.dtype)) else: imgs[i] = img + self.progress(i, len(mats)) if para['new']: IPy.show_img(newimgs, '%s-trans'%ips.title) plgs = [Register, Transform] \ No newline at end of file From 13f3f4364e3977af9559e86cc59a35731d71ee16 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 1 Mar 2019 13:00:22 +0800 Subject: [PATCH 108/343] stackreg --- imagepy/menus/Plugins/StackReg/stackreg_plgs.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/imagepy/menus/Plugins/StackReg/stackreg_plgs.py b/imagepy/menus/Plugins/StackReg/stackreg_plgs.py index fceb8655..81939953 100644 --- a/imagepy/menus/Plugins/StackReg/stackreg_plgs.py +++ b/imagepy/menus/Plugins/StackReg/stackreg_plgs.py @@ -45,10 +45,10 @@ def run(self, ips, imgs, para = None): IPy.set_info('transform...') for i in range(sr._tmats.shape[0]): tform = tf.ProjectiveTransform(matrix=sr._tmats[i]) - if para['new'] == 'Inplace': - imgs[i] = tf.warp(imgs[i], tform)*imgs[i].max() - if para['new'] == 'New': - news[i] = tf.warp(imgs[i], tform)*imgs[i].max() + img = tf.warp(imgs[i], tform) + img -= imgs[i].min(); img *= imgs[i].max() - imgs[i].min() + if para['new'] == 'Inplace': imgs[i] = imgs + if para['new'] == 'New': news[i] = img.astype(ips.img.dtype) self.progress(i, len(imgs)) if para['new'] == 'New': IPy.show_img(news, '%s-reg'%ips.title) From a3a0389e46bf1b9974001518cf77d7cad1077b20 Mon Sep 17 00:00:00 2001 From: WeisongZhao Date: Fri, 1 Mar 2019 22:26:44 +0800 Subject: [PATCH 109/343] v0.20, fix some language error --- imagepy/core/loader/loader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/core/loader/loader.py b/imagepy/core/loader/loader.py index dd39de94..3eb83dfa 100644 --- a/imagepy/core/loader/loader.py +++ b/imagepy/core/loader/loader.py @@ -117,7 +117,7 @@ def build_plugins(path, err=False): subtree = extend_plugins(path, subtree, err) if root and sta and len(err)>0: - IPy.write('some plugin may be not loaded, but not affect otheres!') + IPy.write('Some plugin may be not loaded, but not affect others!') for i in err: IPy.write('>>> %-50s%-20s%s'%i) return (pg, subtree) From 2662296631a652c76fdb0958df113df782a8f0e3 Mon Sep 17 00:00:00 2001 From: WeisongZhao Date: Fri, 1 Mar 2019 22:34:44 +0800 Subject: [PATCH 110/343] v0.20, luts added --- imagepy/data/luts/5_ramps.lut | 256 +++++++++++++++++ .../data/luts/{Cyan_Hot.lut => Cyan Hot.lut} | Bin ...reen_Fire_Blue.lut => Green Fire Blue.lut} | Bin imagepy/data/luts/ICA2.lut | Bin 768 -> 0 bytes imagepy/data/luts/ICA3.lut | Bin 768 -> 0 bytes imagepy/data/luts/Jet.lut | 256 +++++++++++++++++ .../luts/{Magenta_Hot.lut => Magenta Hot.lut} | Bin imagepy/data/luts/NanoJ-Orange.lut | Bin 0 -> 768 bytes .../luts/{Orange_Hot.lut => Orange Hot.lut} | Bin imagepy/data/luts/Rainbow RGB.lut | Bin 0 -> 768 bytes .../data/luts/{Red_Hot.lut => Red Hot.lut} | Bin imagepy/data/luts/SQUIRREL-Errors.lut | 256 +++++++++++++++++ imagepy/data/luts/SQUIRREL-FRC.lut | 2 + imagepy/data/luts/Thermal.lut | Bin 0 -> 768 bytes imagepy/data/luts/Viridis.lut | 1 + imagepy/data/luts/Yellow Hot.lut | Bin 0 -> 768 bytes imagepy/data/luts/ametrine.lut | Bin 0 -> 768 bytes imagepy/data/luts/blue_orange_icb.lut | Bin 0 -> 800 bytes imagepy/data/luts/cool.lut | Bin 0 -> 800 bytes imagepy/data/luts/edges.lut | Bin 0 -> 800 bytes imagepy/data/luts/gem.lut | Bin 0 -> 800 bytes imagepy/data/luts/glasbey.lut | 257 ++++++++++++++++++ imagepy/data/luts/glasbey_inverted.lut | Bin 0 -> 768 bytes imagepy/data/luts/glasbey_on_dark.lut | 256 +++++++++++++++++ imagepy/data/luts/glow.lut | 257 ++++++++++++++++++ imagepy/data/luts/isolum.lut | Bin 0 -> 768 bytes imagepy/data/luts/morgenstemning.lut | Bin 0 -> 768 bytes imagepy/data/luts/mpl-inferno.lut | Bin 0 -> 768 bytes imagepy/data/luts/mpl-magma.lut | Bin 0 -> 768 bytes imagepy/data/luts/mpl-plasma.lut | Bin 0 -> 768 bytes imagepy/data/luts/mpl-viridis.lut | 1 + imagepy/data/luts/phase.lut | 257 ++++++++++++++++++ imagepy/data/luts/physics.lut | Bin 0 -> 768 bytes imagepy/data/luts/royal.lut | Bin 0 -> 800 bytes imagepy/data/luts/sepia.lut | 256 +++++++++++++++++ imagepy/data/luts/smart.lut | Bin 0 -> 800 bytes imagepy/data/luts/thal.lut | Bin 0 -> 800 bytes imagepy/data/luts/thallium.lut | Bin 0 -> 800 bytes imagepy/data/luts/unionjack.lut | 257 ++++++++++++++++++ 39 files changed, 2312 insertions(+) create mode 100644 imagepy/data/luts/5_ramps.lut rename imagepy/data/luts/{Cyan_Hot.lut => Cyan Hot.lut} (100%) rename imagepy/data/luts/{Green_Fire_Blue.lut => Green Fire Blue.lut} (100%) delete mode 100644 imagepy/data/luts/ICA2.lut delete mode 100644 imagepy/data/luts/ICA3.lut create mode 100644 imagepy/data/luts/Jet.lut rename imagepy/data/luts/{Magenta_Hot.lut => Magenta Hot.lut} (100%) create mode 100644 imagepy/data/luts/NanoJ-Orange.lut rename imagepy/data/luts/{Orange_Hot.lut => Orange Hot.lut} (100%) create mode 100644 imagepy/data/luts/Rainbow RGB.lut rename imagepy/data/luts/{Red_Hot.lut => Red Hot.lut} (100%) create mode 100644 imagepy/data/luts/SQUIRREL-Errors.lut create mode 100644 imagepy/data/luts/SQUIRREL-FRC.lut create mode 100644 imagepy/data/luts/Thermal.lut create mode 100644 imagepy/data/luts/Viridis.lut create mode 100644 imagepy/data/luts/Yellow Hot.lut create mode 100644 imagepy/data/luts/ametrine.lut create mode 100644 imagepy/data/luts/blue_orange_icb.lut create mode 100644 imagepy/data/luts/cool.lut create mode 100644 imagepy/data/luts/edges.lut create mode 100644 imagepy/data/luts/gem.lut create mode 100644 imagepy/data/luts/glasbey.lut create mode 100644 imagepy/data/luts/glasbey_inverted.lut create mode 100644 imagepy/data/luts/glasbey_on_dark.lut create mode 100644 imagepy/data/luts/glow.lut create mode 100644 imagepy/data/luts/isolum.lut create mode 100644 imagepy/data/luts/morgenstemning.lut create mode 100644 imagepy/data/luts/mpl-inferno.lut create mode 100644 imagepy/data/luts/mpl-magma.lut create mode 100644 imagepy/data/luts/mpl-plasma.lut create mode 100644 imagepy/data/luts/mpl-viridis.lut create mode 100644 imagepy/data/luts/phase.lut create mode 100644 imagepy/data/luts/physics.lut create mode 100644 imagepy/data/luts/royal.lut create mode 100644 imagepy/data/luts/sepia.lut create mode 100644 imagepy/data/luts/smart.lut create mode 100644 imagepy/data/luts/thal.lut create mode 100644 imagepy/data/luts/thallium.lut create mode 100644 imagepy/data/luts/unionjack.lut diff --git a/imagepy/data/luts/5_ramps.lut b/imagepy/data/luts/5_ramps.lut new file mode 100644 index 00000000..6a9c6a56 --- /dev/null +++ b/imagepy/data/luts/5_ramps.lut @@ -0,0 +1,256 @@ +0 0 1 +0 0 5 +0 0 10 +0 0 15 +0 0 20 +0 0 25 +0 0 30 +0 0 35 +0 0 40 +0 0 45 +0 0 50 +0 0 55 +0 0 60 +0 0 65 +0 0 70 +0 0 75 +0 0 80 +0 0 85 +0 0 90 +0 0 95 +0 0 100 +0 0 105 +0 0 110 +0 0 115 +0 0 120 +0 0 125 +0 0 130 +0 0 135 +0 0 140 +0 0 145 +0 0 150 +0 0 155 +0 0 160 +0 0 165 +0 0 170 +0 0 175 +0 0 180 +0 0 185 +0 0 190 +0 0 195 +0 0 200 +0 0 205 +0 0 210 +0 0 215 +0 0 220 +0 0 225 +0 0 230 +0 0 235 +0 0 240 +0 0 245 +0 0 250 +0 1 0 +0 5 0 +0 10 0 +0 15 0 +0 20 0 +0 25 0 +0 30 0 +0 35 0 +0 40 0 +0 45 0 +0 50 0 +0 55 0 +0 60 0 +0 65 0 +0 70 0 +0 75 0 +0 80 0 +0 85 0 +0 90 0 +0 95 0 +0 100 0 +0 105 0 +0 110 0 +0 115 0 +0 120 0 +0 125 0 +0 130 0 +0 135 0 +0 140 0 +0 145 0 +0 150 0 +0 155 0 +0 160 0 +0 165 0 +0 170 0 +0 175 0 +0 180 0 +0 185 0 +0 190 0 +0 195 0 +0 200 0 +0 205 0 +0 210 0 +0 215 0 +0 220 0 +0 225 0 +0 230 0 +0 235 0 +0 240 0 +0 245 0 +0 250 0 +1 1 0 +5 5 0 +10 10 0 +15 15 0 +20 20 0 +25 25 0 +30 30 0 +35 35 0 +40 40 0 +45 45 0 +50 50 0 +55 55 0 +60 60 0 +65 65 0 +70 70 0 +75 75 0 +80 80 0 +85 85 0 +90 90 0 +95 95 0 +100 100 0 +105 105 0 +110 110 0 +115 115 0 +120 120 0 +125 125 0 +130 130 0 +135 135 0 +140 140 0 +145 145 0 +150 150 0 +155 155 0 +160 160 0 +165 165 0 +170 170 0 +175 175 0 +180 180 0 +185 185 0 +190 190 0 +195 195 0 +200 200 0 +205 205 0 +210 210 0 +215 215 0 +220 220 0 +225 225 0 +230 230 0 +235 235 0 +240 240 0 +245 245 0 +250 250 0 +1 0 0 +5 0 0 +10 0 0 +15 0 0 +20 0 0 +25 0 0 +30 0 0 +35 0 0 +40 0 0 +45 0 0 +50 0 0 +55 0 0 +60 0 0 +65 0 0 +70 0 0 +75 0 0 +80 0 0 +85 0 0 +90 0 0 +95 0 0 +100 0 0 +105 0 0 +110 0 0 +115 0 0 +120 0 0 +125 0 0 +130 0 0 +135 0 0 +140 0 0 +145 0 0 +150 0 0 +155 0 0 +160 0 0 +165 0 0 +170 0 0 +175 0 0 +180 0 0 +185 0 0 +190 0 0 +195 0 0 +200 0 0 +205 0 0 +210 0 0 +215 0 0 +220 0 0 +225 0 0 +230 0 0 +235 0 0 +240 0 0 +245 0 0 +250 0 0 +1 1 1 +5 5 5 +10 10 10 +15 15 15 +20 20 20 +25 25 25 +30 30 30 +35 35 35 +40 40 40 +45 45 45 +50 50 50 +55 55 55 +60 60 60 +65 65 65 +70 70 70 +75 75 75 +80 80 80 +85 85 85 +90 90 90 +95 95 95 +100 100 100 +105 105 105 +110 110 110 +115 115 115 +120 120 120 +125 125 125 +130 130 130 +135 135 135 +140 140 140 +145 145 145 +150 150 150 +155 155 155 +160 160 160 +165 165 165 +170 170 170 +175 175 175 +180 180 180 +185 185 185 +190 190 190 +195 195 195 +200 200 200 +205 205 205 +210 210 210 +215 215 215 +220 220 220 +225 225 225 +230 230 230 +235 235 235 +240 240 240 +245 245 245 +250 250 250 +255 255 255 diff --git a/imagepy/data/luts/Cyan_Hot.lut b/imagepy/data/luts/Cyan Hot.lut similarity index 100% rename from imagepy/data/luts/Cyan_Hot.lut rename to imagepy/data/luts/Cyan Hot.lut diff --git a/imagepy/data/luts/Green_Fire_Blue.lut b/imagepy/data/luts/Green Fire Blue.lut similarity index 100% rename from imagepy/data/luts/Green_Fire_Blue.lut rename to imagepy/data/luts/Green Fire Blue.lut diff --git a/imagepy/data/luts/ICA2.lut b/imagepy/data/luts/ICA2.lut deleted file mode 100644 index a5ac0ec9efb2405121b7aad17bbf162ed14d4f02..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 768 zcmezW=hyeIpFg~N^YZ!AM-T4azIpA+rSoS`pE!2-;Ql?kc5L0eaow7g%a<-%FmLwE z=~E_6=nt*R(1F3iixN>5Erh>M8~4+#qJ_4aggak96ywlFg`)Ys9{P*qlx zlaUk`6%yd%=45AKV!#Gix%fpS<&-sajVx@P+Vkj&{{H>*=lAblzkdGw@#FjVZ{NOs{rct0=g*%$ef;?0 z!~6H|-o1VM_RX8uuV1}-`SQh!=g*%#d;0Y0lP8ZKKYH}=;e!YF@87$3_wJoLcW&Rl zb?fHM8#k_Bzjp2F)hk!7T)uqi(#4AxE}TDq?%dh4XU?2CxP9&7nSCu)1!>WN?zYAn zaw1#|%xs)I{6eA<(sGI_>RP%6CgxVQ4$f|#KK?hcvMV$Qc8MePChW)Dr@Q+TiSsEH*w1JnRDhXT(WHC TnspnuY}>hK-@zltPM!e(uHbO1 diff --git a/imagepy/data/luts/ICA3.lut b/imagepy/data/luts/ICA3.lut deleted file mode 100644 index c03931ed07539d7f221412442d3552457f691104..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 768 zcmezW@893QfByXb{p;7ypFe(l|NiaUx36EneEIzO)2ELgKYV!q{@uH`Z{NOo^ZNCx zS1(__c=7!Cvu97AK7I1!@#9C29zJ~V;Qsx4_wL@kbLY2GOeh- zXXesP`_J5X`sqI%!I@JhjvYC4VBenIJGXD$ym9^7)vH!4Te4`uyg9RGPMV~xOjJZzXmFsvueYbWtBaF^osE@+nTfHXzOJ^W zx~j60f}D($gqVnsAU`iRCp#-MBLgTmX%jjR#n&3*3~yMF|)9;v2$>8ar5x@^$!dV4UdeDjZaKT z%gD;fD<~={tEj4}Z)|RD@9ggFpE!Bij9GK$Em*W<*@{(b)^FUrb^FfUd-oqaeDuW0 zlc!FfK6Cc$x%1~QT)cSc(xuCnuUxr$_1d-T*RS8War5S_TeolDxpU|4-Fx@$-+%Dn z;lqcI9zA~ig&z?Ph{`|#@moHzvdj0y%n>TOYzI*rn{f7@9KYsl5>GS6=U%r0* zh6KJNzz+oYi2%P4;5QsF&=>%Nmy?^99~!z!D!|ay($O<8G&Ti=udTf!D1be^ef|V^&0Dwc+`V`I;iJb- zo<4j2;^nK?Z{EIp{{i8BMmS)C0cJR0fdN)HV1om87~p^dP8dL#4`nejGBGo=u&}bR zv2$>6a&mEV^YHNU@$m}?2nq@b3yX+|iiwF!NJvUbNlVMf%F4;fD<~)`Dk&?gsH&=| zscUFxYHDd~>*(t0>FFDQ!qFUNc(H4{u-JfS{1@$e6f<-%cl?T-n@GG{ORL|5ANN$ zb>rIA%NNg|J#*^Bu_K2L?%%t6=Zn9cJ<2TOP4HKFmKMRnbTpO2m2fBWw39- z{semw>^rc>zEZ?RX3v~HWm11{cSl=uV?%9qMOjHtX}DQoB& zncFzJc?X0>#iwNE6@$GqVd|{;OIEJiykqa7<7X~jyZzwlE1++_ynp@d;hpQ3&z(H7 zfA_YHYnCsbH*-pVcUxm^MM-{EYC=qSkgtb}y_Knfj=GYpga|)wS5Z@jmtROkTvA3( rQCUq>8<;9875421/.-+*('& \ No newline at end of file diff --git a/imagepy/data/luts/Thermal.lut b/imagepy/data/luts/Thermal.lut new file mode 100644 index 0000000000000000000000000000000000000000..5d8cd5a807790edb2e071077896c677774cfa96c GIT binary patch literal 768 zcmZ>BBMP{>x;Q&I*xOoLnwuIM=xS@ID#^=8iHivG@o=)SFfkwlMizE%0a0lMbzKu1 z7oU)r)ZEg#w*DClR&3aD;P|<#w;w)#^Wn?)Uw{7o`}g8Y;VcMKU zE7on?^H*-(d;I+MyH8(#{Qmo&c!2D1{A?yxPF_JVX$3VMBTIWX-;n5}tisBM zjtL--ZQpKd9`JGy%( zOrAb#?t&%DSFPK)b;q7VC(d04hUyDobYTxvQ2hP+@f~RGr;i`rzkB=U_3PKKK;ZS8 zx9{G6`1twDx9>l}5sl(kIOh*A^54IC{`fw^wTtIYpV-w>SC*F+7w+%jXl1OcrXVdQ z$P08UBNAX{<>2Ds6A%QZ2}vm#IeA4T6;(BL4NWa=ZEYPO(AL({($vsUS5s9{QdE$a dlaZE`5C^6qK>>bV9&Rp9b~ZLv7G`E9CIH;IwqO7N literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/Viridis.lut b/imagepy/data/luts/Viridis.lut new file mode 100644 index 00000000..eb4a3e6c --- /dev/null +++ b/imagepy/data/luts/Viridis.lut @@ -0,0 +1 @@ +DDDEEEFFFFGGGGGGGHHHHHHHHHGGGGGGGFFFFEEEEDDCCCBBBAA@@??>>===<<;;::998877665544332211100//...--,,,++***))((('''&&&%%$$$###"""!!!  !!"##$%&'()*+,./0235689;=>@BDEGIKMOQSUWY[^`bdgikmprtwy|~  !"#%&'(*+,-/01245679:;<=>@ABCDEGHIJKLMNPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyzz{|}~TUWXZ[\^_abcefgijklnopqrstuvwxyz{||}~~}}|{zzyxwvvutsrqponmlkihgfedba`_]\[YXVUTRQONLKIGFDCA?><:875320.,+)'&$"!!"$ \ No newline at end of file diff --git a/imagepy/data/luts/Yellow Hot.lut b/imagepy/data/luts/Yellow Hot.lut new file mode 100644 index 0000000000000000000000000000000000000000..5ccebb7add66b6e747bd18c8d8baec79ef6adda0 GIT binary patch literal 768 zcmZQzVqxdx<`WPW6_=8cS5#Kh(AL#AGBLBXwsUZDb@%e|3k(j6h>DF*N=eJi&Mhb^ zEw8MpYiw@q=<4mCIAz+**>e{xTC#lQnspmCZ{4wL&;El)j-5Gk=Iq&X=gwcaaPi`$ z%a^ZQxq9u|_3Jlo-n@0|_MJO-@7}wA|G|TYj~+dK{N(A=XV0F$c=7V(tJkmJym|ZX z-TU_+K7Rc4>GPK_U%!6){{6>~pTB>OM?d;)^NAdr-jkyB7o zR#n&3*3mOCGB!20w6*~Tgo~@Yr?;+{4GfdXQ)bSbHG9t7c?%XUTD)Z0@)avrty#Nn{f14Ow`|?EW9P2jd-m-=aPZKP zqsNY)ICc8W*>mTCfpq06Fnn&^1_sT8hrm#I_8b@>Z{7mK;?rkfF#P!W`wwlr|DR## z047!rZazT~aVc2^Wi?G5eIru~Ydc35cQ4<7;IPP;_@vZ~?7YH~@~YZ~=C;nB{z+43 r%$~P!$?{ceH*DUvbI<-mM^BtSck#;gTX!Eke)jUs`%hng{QeIBb$FjC literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/ametrine.lut b/imagepy/data/luts/ametrine.lut new file mode 100644 index 0000000000000000000000000000000000000000..598d81dfa3d5895eeb8688c46e378541b0e0997d GIT binary patch literal 768 zcmb1>S5#J2)6mk<(>F9WHM6j?v9ot{c6Iac^6~W#3=R#8h>D4gPfSip%gD;f%`Yr2 zDXXZesjY8pZfWc2>hA5IIC;vn8M9{3oxgC=l4UDatzNr+>h$R|XU?8GcmDi^ix)3lx_srz)vMR8 zUB7YT=FMBTZ{N9d_ujqx_a8iX_~_B&$4{OBnTD7Xg$ zccI|U9UB`PTU$FjJ9~S32L}g7M@J_oCue6D7Z+DoS2s5|cXxLW4-ZdIFE1}|Z*LzT zA75WzKRD$jB%#05VVkP!bgYHJ}2Z zA*cXoFdP7*B04rMAt^Z(7#G>Oc?CtqrNGFjuB~fmY;JAq=x zCOVjf4rU|3teG>WPn$Yr@}vpr6t8hg#~%J zIoVm68R==MDalER3GuNp(NU2R;bEa6!9jrm{(e5*UY;KAZmurQPL2-tc6PQl*49>* z78d4arY0uFMuvt4`g(f0I@(%Vni}frYN{$K%1Vlg3i5KYvNF}+hTEX>SIj0_9_xaNJ3 literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/blue_orange_icb.lut b/imagepy/data/luts/blue_orange_icb.lut new file mode 100644 index 0000000000000000000000000000000000000000..bf27a41a90e383207fc033562c014c7648eca7fe GIT binary patch literal 800 zcmeZt_V-~}#K-^y{}~yW7#JD;G5luu1>%DNBZYv8g^h!YhfhFAL`*_TMovLVMNLCX zN6)~>#LU9V#?Haf+11_C+t)uZI5a#mIyOErIW;{qJ2$_uxU{^oy0*TtxwXBsySIPh zWe&gn?+js8XyZ^wUBgamhI&<#Ar7PEN+`4n`!J{Y7Uc7qq z?!%`q-+ui1^N&)%#Kg?P%Er#Y$;HjX%f~MuC?qT*Dkd%=DJ3ltEX>ZXk=_+YG!U>X=QC=YiIA^U~gw@V{K(=VQyw>Vr*n+ps%N^qphW>p{}N? zqO7E-ATK8?BP}HDuo;N;@w;pO8O5EK#?5fu}ckd%^^k(HBIP*hS@QB_md(A3h_ z(bdy8VbYXoGiJ@1w_wqdWh+*#S+`-+mTfzB?b)~g;NhdkPo6$|{^I4U*Kgjwd;j6% zr_W!$e*6C8=da&?{{Bs2^fOW#|FzYX<)y`i`MKGd>8Z(y@v+g7;i18S{=VLx?yk;` z4t6$H7G@?!26{SL8fq#^3UV@15@I4k0(?AN9BeF1j4T{H0wNMJ3Mv{p1|}9Z4$ki0 z{=wnV@yY4g`Niec_08?w{gbE9p1*kc>h+tq@7{mt*r{`uuHCx#=-I1xpT7P21ppNI B6^#G@ literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/cool.lut b/imagepy/data/luts/cool.lut new file mode 100644 index 0000000000000000000000000000000000000000..790cba3e1120c4bbc47f11be4d329b9e5fe2b458 GIT binary patch literal 800 zcmeZt_V-~}#K-^y{}~yW8JHOUG5liq2I7MOBc*_eg^h!YhfhFAL`*_TMovLVMNLCX zN6)~>#LU9V#?Haf+11_C+t)uZI5a#mIyOErIW;{qJ2$_uxU{^oy0*TtxwXBsySIPh zWe&gn?+js8XyZ^wUBgamhI&<#Ar7PEN+`4n`!J{Y7Uc7qq z?!%`q-+ui1^N&Kn%*@Kp$<50zC@d;2DJ?6nsI024sjaJTXl!b3X>Duo;N;@w;pO8O z5EK#?5fu}ckd%^^k(HBIP*hS@QB_mdP*+n`QC3n^ke8E{k(QE_5El~_5f%~@;OFDz z;pXDxU~g+}X>Mw4sIRN7sjjN5C@(85DK083$j{5o$e0 zJ$rQT*0oFLP8~b6fA{vy>sK#dJb(7|$^G5!&Gpsg#rfIk$??(Q!T#Rv&JH#fCI&hh zDhe_ZA_6=dEQ~B1JOUyTG72ghItC^dHV)42-u}Vi(ecUY+4;rg)%DHo-TjlN&z`?{ d`RetXx9{G6=-8=qm#*Eq_vqQHcb~p}0{||BVpRYD literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/edges.lut b/imagepy/data/luts/edges.lut new file mode 100644 index 0000000000000000000000000000000000000000..74014a817cc1f5fa857d747b951b2fd69924a36b GIT binary patch literal 800 zcmeZt_V-~}z{mgu{}~w&5F`KsjPwI++ybIf@+z8o#qTGjef48pG~9wN5ntlV2OVK@)$K^ literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/gem.lut b/imagepy/data/luts/gem.lut new file mode 100644 index 0000000000000000000000000000000000000000..c9194056d8e4276eb9dc6ee5a38202341f43d9cb GIT binary patch literal 800 zcmeZt_V-~}#K-^y{}~yWfbbu~ABJBbJ{T}k3Yb{fIJkKD1cXGyB&1~I6qHodG_-W| z42(?7EUawo92}ip-95d1{R4wT!y}_(;}erp(=)Sk^9zeh%PXsE>l>R}+dI2^`zKDG zI(_Esx$_q;Ub=kc>b2`PZr-|m=kC4x4;(sj?8K=v=Pq2ja_z>gJNF(udh+bWt2ggH zeERb3$FD#CCSIOpMGdZ0wv|JiPn@Lc*eA5|Yv~ za`K8wDyr%lTH3mL28PBaX6BYwHn#Q-j!rJF?jBy=zJ39L!69Mckx?6tls zg~eqR)wK;xt?gYs{S&85n>l;lg2hW$tXjK%)0XW!_v|}x=;-lNXU<=|a_#2rdk-Ez zeevq;`%hoK|NQd@=zSJeHg*n9E^Z!PK7Ii~Az=|wF>wh=DQOv5Ie7&|C1n*=HFXV5 zEo~iLJ$(a1BV!X&Gjj_|D{C8DJ9`J9vz%RA-P}Dqy}W&V{rm$0gMvds!@?sXqoQMC z+0)kt1HV(iwpB}voq6ElM~}( zqa(vZg9H72y*=GsogE$QY^*HIOpFZlbhI?oRFo9tWTYg-M1%zRc(^#&SQuG2cmzZw zWE501bPP-^Y#f~3z5Rp3qvMm)v-69~tLvNFyZa|kpFMx^^404%Z{NND(6LkJE?v8I O@6oeY?>>F|^$P$iVHMv1 literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/glasbey.lut b/imagepy/data/luts/glasbey.lut new file mode 100644 index 00000000..283612d8 --- /dev/null +++ b/imagepy/data/luts/glasbey.lut @@ -0,0 +1,257 @@ +Index Red Green Blue +0 255 255 255 +1 0 0 255 +2 255 0 0 +3 0 255 0 +4 0 0 51 +5 255 0 182 +6 0 83 0 +7 255 211 0 +8 0 159 255 +9 154 77 66 +10 0 255 190 +11 120 63 193 +12 31 150 152 +13 255 172 253 +14 177 204 113 +15 241 8 92 +16 254 143 66 +17 221 0 255 +18 32 26 1 +19 114 0 85 +20 118 108 149 +21 2 173 36 +22 200 255 0 +23 136 108 0 +24 255 183 159 +25 133 133 103 +26 161 3 0 +27 20 249 255 +28 0 71 158 +29 220 94 147 +30 147 212 255 +31 0 76 255 +32 0 66 80 +33 57 167 106 +34 238 112 254 +35 0 0 100 +36 171 245 204 +37 161 146 255 +38 164 255 115 +39 255 206 113 +40 71 0 21 +41 212 173 197 +42 251 118 111 +43 171 188 0 +44 117 0 215 +45 166 0 154 +46 0 115 254 +47 165 93 174 +48 98 132 2 +49 0 121 168 +50 0 255 131 +51 86 53 0 +52 159 0 63 +53 66 45 66 +54 255 242 187 +55 0 93 67 +56 252 255 124 +57 159 191 186 +58 167 84 19 +59 74 39 108 +60 0 16 166 +61 145 78 109 +62 207 149 0 +63 195 187 255 +64 253 68 64 +65 66 78 32 +66 106 1 0 +67 181 131 84 +68 132 233 147 +69 96 217 0 +70 255 111 211 +71 102 75 63 +72 254 100 0 +73 228 3 127 +74 17 199 174 +75 210 129 139 +76 91 118 124 +77 32 59 106 +78 180 84 255 +79 226 8 210 +80 0 1 20 +81 93 132 68 +82 166 250 255 +83 97 123 201 +84 98 0 122 +85 126 190 58 +86 0 60 183 +87 255 253 0 +88 7 197 226 +89 180 167 57 +90 148 186 138 +91 204 187 160 +92 55 0 49 +93 0 40 1 +94 150 122 129 +95 39 136 38 +96 206 130 180 +97 150 164 196 +98 180 32 128 +99 110 86 180 +100 147 0 185 +101 199 48 61 +102 115 102 255 +103 15 187 253 +104 172 164 100 +105 182 117 250 +106 216 220 254 +107 87 141 113 +108 216 85 34 +109 0 196 103 +110 243 165 105 +111 216 255 182 +112 1 24 219 +113 52 66 54 +114 255 154 0 +115 87 95 1 +116 198 241 79 +117 255 95 133 +118 123 172 240 +119 120 100 49 +120 162 133 204 +121 105 255 220 +122 198 82 100 +123 121 26 64 +124 0 238 70 +125 231 207 69 +126 217 128 233 +127 255 211 209 +128 209 255 141 +129 36 0 3 +130 87 163 193 +131 211 231 201 +132 203 111 79 +133 62 24 0 +134 0 117 223 +135 112 176 88 +136 209 24 0 +137 0 30 107 +138 105 200 197 +139 255 203 255 +140 233 194 137 +141 191 129 46 +142 69 42 145 +143 171 76 194 +144 14 117 61 +145 0 30 25 +146 118 73 127 +147 255 169 200 +148 94 55 217 +149 238 230 138 +150 159 54 33 +151 80 0 148 +152 189 144 128 +153 0 109 126 +154 88 223 96 +155 71 80 103 +156 1 93 159 +157 99 48 60 +158 2 206 148 +159 139 83 37 +160 171 0 255 +161 141 42 135 +162 85 83 148 +163 150 255 0 +164 0 152 123 +165 255 138 203 +166 222 69 200 +167 107 109 230 +168 30 0 68 +169 173 76 138 +170 255 134 161 +171 0 35 60 +172 138 205 0 +173 111 202 157 +174 225 75 253 +175 255 176 77 +176 229 232 57 +177 114 16 255 +178 111 82 101 +179 134 137 48 +180 99 38 80 +181 105 38 32 +182 200 110 0 +183 209 164 255 +184 198 210 86 +185 79 103 77 +186 174 165 166 +187 170 45 101 +188 199 81 175 +189 255 89 172 +190 146 102 78 +191 102 134 184 +192 111 152 255 +193 92 255 159 +194 172 137 178 +195 210 34 98 +196 199 207 147 +197 255 185 30 +198 250 148 141 +199 49 34 78 +200 254 81 97 +201 254 141 100 +202 68 54 23 +203 201 162 84 +204 199 232 240 +205 68 152 0 +206 147 172 58 +207 22 75 28 +208 8 84 121 +209 116 45 0 +210 104 60 255 +211 64 41 38 +212 164 113 215 +213 207 0 155 +214 118 1 35 +215 83 0 88 +216 0 82 232 +217 43 92 87 +218 160 217 146 +219 176 26 229 +220 29 3 36 +221 122 58 159 +222 214 209 207 +223 160 100 105 +224 106 157 160 +225 153 219 113 +226 192 56 207 +227 125 255 89 +228 149 0 34 +229 213 162 223 +230 22 131 204 +231 166 249 69 +232 109 105 97 +233 86 188 78 +234 255 109 81 +235 255 3 248 +236 255 0 73 +237 202 0 35 +238 67 109 18 +239 234 170 173 +240 191 165 0 +241 38 44 51 +242 85 185 2 +243 121 182 158 +244 254 236 212 +245 139 165 89 +246 141 254 193 +247 0 60 43 +248 63 17 40 +249 255 221 246 +250 17 26 146 +251 154 66 84 +252 149 157 238 +253 126 130 72 +254 58 6 101 +255 189 117 101 diff --git a/imagepy/data/luts/glasbey_inverted.lut b/imagepy/data/luts/glasbey_inverted.lut new file mode 100644 index 0000000000000000000000000000000000000000..02766a6026e7eaee6c240fe167233689544fd07d GIT binary patch literal 768 zcmW;J5&ZWc5C-tW;cys+VHk#C7=~du48w33hG94yhQr|xAAI}2pM#|C``kselRZ-> zBA??r4^8&UH<1iiJqO#Qv>ua`I1$nLVWh)#b8TGy0i_$?!dJj3oxZ;M%7ce9H%64d^QuIe#hS-XE)bay2-kt!Iuo?YZdD1dV-9l^> za}y$U#>NbxRtjTzqk2k%QhX_#x7~58{f4m(E{%5=`N!?qRlb_Y?NPLB`@Q;aXBXbX zqC1nGqg+$vTn!BM=8CC19DvZM|%7b11T4c3TjF9DOk=|;l zA;bqhbnvai==g|y-8R;n^Y$_c_<~0)f3tX;h(neWF4o5?K${mn11`UyM1?LYs@Tkm zpVX=QLQQltU2+d)Q3#aJqPx1yM}vmy?3N=8JM_yFzvvRTt^bWl;7FOWgPXB>h)Q)I z><-yfS|2DNX8;Y0I1MbeN=1$%>ui>Kf?k@w>0+R#YyR6B?*e-btB%{fdp|x2>iUV( rt>R6z?NZ%0KV)AXBvRoeEX()hl{Fv46I#6fdDJhi%&>OPDxA8$jr{k%_}G@DlRE4uc)l5uBol7Z)j|8X>Duo=Fw*EFmcl4DO09Sn?7^a>^XDi&0nx^(UPUhmaka3YW146>(+1BxM}m2t=qQm*tu)> zp1u3_A2@jM(BUITj~zd8^3>@wXU?8Gf8pY#%a^ZQy>|V^&0Du`-?@A5{)2~)9zTBa z^x5+lFJHcT{pRhv_wPS^{Pg+Dm#^Qx|M>Cq*Y7`n{`~&^``53ZKY#rA{{7pxuV24> z`TY6Qr;i^$e0cx<-P^Zs-n@SO>eb7aFJ3%<{_NS)r%#?de*Ea+!v_!U-@kY7?%g|g zZr{3f^X82k*RNl@diBbc%a<=*x_IHj>eZ`Oty;Nq<%$(6mIJ}EWy_W>UAkn+lEsS` zFIu!{;lhOr7A%-QfBw9A^XAT-J7>*{K2YN|o;&;*VL zXf%Lg0T>B05V5crYc#;);Q%xqAQ5pE6cM0s0S62uWPm{f3mahITsVLJ+_|%7&zw1Z z`qZgYCr_R@as2qPV@Ho3Idb^$p+g4`9@xKs-`+jDckSG?XjkT4PrG>e41v5}#HzP_HWj<%MjhPs-nin5ZT Vg1nrpw3MW{n23-7KQ}uw0{~Y&+PDA! literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/morgenstemning.lut b/imagepy/data/luts/morgenstemning.lut new file mode 100644 index 0000000000000000000000000000000000000000..a9af2d5db785589fed639fb29fed060cf2ae2bdd GIT binary patch literal 768 zcmZQzKmbNCU;+VVAYcIkR#sLvHa2#4b`B0sPA)EPZXO<9UOs+)0YM=lVG&U=2}x;L zc|~Pa4J}=LBU1}&TL))1PoIF`u*jJBq|}V;yuy<5s+#(y*7mO636rKypEY;>q9w~$ zu35KX^VaRV_Uu1+_}Gb4XU|`}eD(Ux+js9jeEj6u^Ovt)zj^om{fCdAK7Ic3=TUXztX*1_8Sh9Tex=mYm?A~|q$njHW&R@E6{pOu}4<0{#{_4%U z51+n#{r=6WK9Bi#E%uEdRb+j~8 zl@;Y>r6j~egar6_xH&o4Secm^8G&h+iG_`mS3p!!R!L3U(9Fik!#^}SDI>S2yr!YG rvv=aunR6E|UAcDSmL0qIA3A#S%!SL>Zr*+H_}R<1A3lHk`R6|X9#mgI literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/mpl-inferno.lut b/imagepy/data/luts/mpl-inferno.lut new file mode 100644 index 0000000000000000000000000000000000000000..d372179f3829479bc4a1c9afaed9670bb992a01b GIT binary patch literal 768 zcmZQzU|?iqWMX1wVPR!s=iubx=H=rT5E2#<6PJ{dk(F0aQc+Xa)YjEAFfuVUx3spg zb8vKab@TA@_Vo`43JwjAh>DJlPe@8mP0Ps2&dn<*DlRRnsH(25YiMk4ZR_al?(Lf} zaq`q@GiJ`7GjGAd#Y>hgU%7hCy7e13ZP~hg$FALb_w7G;=*ZFICr+L|bN2j&i|WPt=o6*-n;+c(c>pipFMm2;^nK?Z{EIr_x{6&kDoq${`}?Z*KgmxfB*60$IqWX ze*wX73;+@ZN&=O9|NiaU*RNl`eE$6T)2ELR05s&w*Kgl{{`$iJ1dsp#hX5NpI|m0R zCl?nt2=D*_n85`EoNxf-faG|2`S|$x1q20!fWajqDk>%}E+HW)DJ3l}BO@y-Coiv{ zps1*%q^zQ%s;Z`@uA!l+sim#0qob>*r*B|rXk=_`Vrph?ZeeL;<+8K0v$C=>Gcz+X($mw@Qd3frlamq?6XN6JVq>DC zqaq{1!@@#Cf`bAB{QZ1=yuCa<+}&K9og5wP?QE^BEG^83Dd;kCd literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/mpl-magma.lut b/imagepy/data/luts/mpl-magma.lut new file mode 100644 index 0000000000000000000000000000000000000000..6b31ee40f816e12618a31b5f8f770d9782929222 GIT binary patch literal 768 zcmZQzU|?iqWMX1wVPR!s=iubx=Hcbz7Z4N@5fu}ckdl^>lUGnuR#nr`($>{CFfuVU zx3spgb8vEYb@%Y{_Vo`43JwjAh>DJlOGr#kP0Ps4&dJL!EG{W4udJ%6t8Z*-X>IT9 z>gnyDIBCk%=`&}|nKyso;w8(LuUx%m-G+^uw{F|9bNAkT2M!%RdhEoh(`V0JxOn-> zwd*%;-MM@3!NW&So<4j2;??UnZ{NLp|Ka1O&!4}1{rc_O_wPS`{QUXz*RNl{f#44a z{Dpyk*Z@otM1z$>2#{_DAOQOx6ac^w00jXs40w2X`S|$x1%N;h20$DEegpu^2?+@c zi--clN?bxhQVJMeGO}`V^70Cbib_h#Dk`d~YHI2l8k(9~TH4w=I=Z@gdiweX28M=4 z#>OTlrlw|Q=H?cbmR45QHa50)cJ>aAPEO7)u5Rugo?hNQzJ39LK_Q`G5s}dFP0z^6 z$tx%gk&>X)-WqXV0C#aM6-wD^{&pw;mX<+js8X0}R(A$Bv&o zefIptOINPl00!#)hmW5=d-3YcyAK~ffBpXR*B@pUHg-;KUVcGgQE^FWS$QQDU{veq z8ycHhSlQS+y103I`vwGshDXN4B_ySS?awbPDlRE4E3c@mtg5cAsj0241A=-WXaIvo zOaPH+0I30~t*x!Csj056uBxi6tf(k2FDorADJd>0Dl90-&&$ot$wqpjg5_oiHU{)AS)&|HZCqcAt5m-IXNXYEj=SM3zYJJVFPqpMP+pjD0rG% d+d4YCdm#ZdW7ZsC2rXU;4k1v|1BTGv{Q&F{UYY;^ literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/mpl-plasma.lut b/imagepy/data/luts/mpl-plasma.lut new file mode 100644 index 0000000000000000000000000000000000000000..a2fd009c4f2f29a6a7114c4736ec702b9d8fa501 GIT binary patch literal 768 zcmd-P5Ehk?mX%jjR#n&3*3~yOHZilXvbME%aB^{V_w@4d^A8LP2@8*miiwL)OiD>j z&&bNj%`Ye_E-kC5tgfl6Z)|F5ZR_al?&<5FFlqACY13!Unmu>k`~`~^FIl>L#mZG{ z)~;K>VdJJPTeofBv2)k%J$v`We*MPH zTet7rxqI*a{Ra;pJ%0S;>CecHvZ{EIr_x}Bd4 z7ARnb046YC1Oo;P02F3oVrFJxVP#`y=iubx;pO8O5EK#?5fzt^l#-T_m6KOgQdUt_ zQ`gYa*3s3|H!w0bF*P%{u(Y~6-WuWYxbNubLY*QKYzi(g^Ly~Uc7k8lBG+RE?c&2IS8yk05BgYzI5r5 zB}*1BUbJxGf(7&E&6_)C_Uu_RXH1_yZR(WClO|5+@9*pF>F(<6Xm4w6X>Mw4XsEBN zt*Nf6tSB!lEiEZ7DlEv)%gxQn&dSV4PfJTpNlr>kNQjS%jfswmii`*k3k?Yg4hjtL z_w)7j@%HlcaCdieb#Zobbab${v$e6dva+-=H#0RgF*Y(ZG|<=6)z#6~*3#6}&`?)X TQ&Uw{RY3qC4=4r(s>+H029b8@ literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/mpl-viridis.lut b/imagepy/data/luts/mpl-viridis.lut new file mode 100644 index 00000000..eb4a3e6c --- /dev/null +++ b/imagepy/data/luts/mpl-viridis.lut @@ -0,0 +1 @@ +DDDEEEFFFFGGGGGGGHHHHHHHHHGGGGGGGFFFFEEEEDDCCCBBBAA@@??>>===<<;;::998877665544332211100//...--,,,++***))((('''&&&%%$$$###"""!!!  !!"##$%&'()*+,./0235689;=>@BDEGIKMOQSUWY[^`bdgikmprtwy|~  !"#%&'(*+,-/01245679:;<=>@ABCDEGHIJKLMNPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyzz{|}~TUWXZ[\^_abcefgijklnopqrstuvwxyz{||}~~}}|{zzyxwvvutsrqponmlkihgfedba`_]\[YXVUTRQONLKIGFDCA?><:875320.,+)'&$"!!"$ \ No newline at end of file diff --git a/imagepy/data/luts/phase.lut b/imagepy/data/luts/phase.lut new file mode 100644 index 00000000..142be6ac --- /dev/null +++ b/imagepy/data/luts/phase.lut @@ -0,0 +1,257 @@ + 0 0 252 + 0 0 252 + 0 0 252 + 4 4 252 + 4 4 252 + 4 4 252 + 8 8 252 + 8 8 252 + 12 12 252 + 12 12 252 + 12 12 248 + 16 16 248 + 16 16 248 + 20 20 248 + 20 20 248 + 20 20 248 + 24 24 248 + 24 24 248 + 28 28 248 + 28 28 248 + 28 28 244 + 32 32 244 + 32 32 244 + 36 36 244 + 36 36 244 + 36 36 244 + 40 40 244 + 40 40 244 + 44 44 244 + 44 44 244 + 44 44 240 + 48 48 240 + 48 48 240 + 52 52 240 + 52 52 240 + 52 52 240 + 56 56 240 + 56 56 240 + 60 60 240 + 60 60 236 + 60 60 236 + 64 64 236 + 64 64 236 + 68 68 236 + 68 68 236 + 68 68 236 + 72 72 236 + 72 72 236 + 76 76 236 + 76 76 232 + 76 76 232 + 80 80 232 + 80 80 232 + 84 84 232 + 84 84 232 + 84 84 232 + 88 88 232 + 88 88 232 + 92 92 232 + 92 92 228 + 92 92 228 + 96 96 228 + 96 96 228 + 96 96 228 +100 100 228 +100 100 228 +104 104 228 +104 104 228 +104 104 224 +108 108 224 +108 108 224 +112 112 224 +112 112 224 +112 112 224 +116 116 224 +116 116 224 +120 120 224 +120 120 224 +120 120 220 +124 124 220 +124 124 220 +128 128 220 +128 128 220 +128 128 220 +132 132 220 +132 132 220 +136 136 220 +136 136 220 +136 136 216 +140 140 216 +140 140 216 +144 144 216 +144 144 216 +144 144 216 +148 148 216 +148 148 216 +152 152 216 +152 152 212 +152 152 212 +156 156 212 +156 156 212 +160 160 212 +160 160 212 +160 160 212 +164 164 212 +164 164 212 +168 168 212 +168 168 208 +168 168 208 +172 172 208 +172 172 208 +176 176 208 +176 176 208 +176 176 208 +180 180 208 +180 180 208 +184 184 208 +184 184 204 +184 184 204 +188 188 204 +188 188 204 +192 192 204 +192 192 204 +192 192 204 +196 196 204 +196 196 204 +200 200 200 +200 200 200 +200 200 200 +200 196 196 +200 196 196 +200 196 196 +200 192 192 +200 192 192 +200 188 188 +200 188 188 +204 188 188 +204 184 184 +204 184 184 +204 180 180 +204 180 180 +204 180 180 +204 176 176 +204 176 176 +204 172 172 +204 172 172 +208 172 172 +208 168 168 +208 168 168 +208 168 168 +208 164 164 +208 164 164 +208 160 160 +208 160 160 +208 160 160 +208 156 156 +212 156 156 +212 152 152 +212 152 152 +212 152 152 +212 148 148 +212 148 148 +212 144 144 +212 144 144 +212 144 144 +212 140 140 +216 140 140 +216 136 136 +216 136 136 +216 136 136 +216 132 132 +216 132 132 +216 132 132 +216 128 128 +216 128 128 +216 124 124 +220 124 124 +220 124 124 +220 120 120 +220 120 120 +220 116 116 +220 116 116 +220 116 116 +220 112 112 +220 112 112 +220 108 108 +224 108 108 +224 108 108 +224 104 104 +224 104 104 +224 100 100 +224 100 100 +224 100 100 +224 96 96 +224 96 96 +228 96 96 +228 92 92 +228 92 92 +228 88 88 +228 88 88 +228 88 88 +228 84 84 +228 84 84 +228 80 80 +228 80 80 +232 80 80 +232 76 76 +232 76 76 +232 72 72 +232 72 72 +232 72 72 +232 68 68 +232 68 68 +232 68 68 +232 64 64 +236 64 64 +236 60 60 +236 60 60 +236 60 60 +236 56 56 +236 56 56 +236 52 52 +236 52 52 +236 52 52 +236 48 48 +240 48 48 +240 44 44 +240 44 44 +240 44 44 +240 40 40 +240 40 40 +240 36 36 +240 36 36 +240 36 36 +240 32 32 +244 32 32 +244 32 32 +244 28 28 +244 28 28 +244 24 24 +244 24 24 +244 24 24 +244 20 20 +244 20 20 +244 16 16 +248 16 16 +248 16 16 +248 12 12 +248 12 12 +248 8 8 +248 8 8 +248 8 8 +248 4 4 +248 4 4 +252 0 0 +252 0 0 + \ No newline at end of file diff --git a/imagepy/data/luts/physics.lut b/imagepy/data/luts/physics.lut new file mode 100644 index 0000000000000000000000000000000000000000..033c07f9e4c86b8b8e06520e9c9e62916710d0e0 GIT binary patch literal 768 zcmdPb)78<^)KFJdQC3utla-c|5Em5|65!+E;$&xKW@LZ?MkZ!v7FITP4h~K(ZXRAf zegOeNAz=|wF>wh=DQrLz2arGq;>bWuOiWrqRm;HC%HGw>KQt;KHM^j!y0NWi;*{w# z=geQYWZB9!>o#uLwsX(EgGY{?Jag{S)f;#2fxvw*cmM(q!Qc@HJO+U$VDJqOPfh9$m5EL2_9hZ=t zmYI`ZR8n45+tA$B+0#F1>Wta*7A{%7YVC&2K&S3MbPNQJgTM(8I0*!&PMtb^`t+GI zXU?8Id+yx1^XJcBxNza(#fz6NUAlbv@|7!Bu3o)*?b@~L*RS8WapNWk+yaB!5O45`HGb*SFK*NcHR078#isPr z6a7w~_W=@;Z-__b!Q(lytksKEp;_v0^U~Oioqpm0;4om_N VAc_P;umNE#KnMc}VgLao0056ULEr!Y literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/royal.lut b/imagepy/data/luts/royal.lut new file mode 100644 index 0000000000000000000000000000000000000000..98a56a66e17ece8e2ee8e0eec01b80521844baca GIT binary patch literal 800 zcmeZt_V-~}#K-^y{}~w=8Q2;Afgwl$1Q=-zxP+utbWQEt{KFG6i>eyhx~I%pvS#bv zBWJGOefsv>KZ?PhUq8Nn{`mgw>zB`;JbG{s13Y;21nDe$cPK`aj`No@=2+iIQT@S71#I7T()WZo&$%D96x#b?D>nAuim(I=iWU6 j{%5DQ|Ij@A?%uim>*x2^=g0dy7$^u33~rJYQ0RXEAD5}D literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/sepia.lut b/imagepy/data/luts/sepia.lut new file mode 100644 index 00000000..75b22946 --- /dev/null +++ b/imagepy/data/luts/sepia.lut @@ -0,0 +1,256 @@ +0 0 0 +43 28 4 +57 35 5 +65 35 6 +66 36 7 +67 37 8 +68 38 9 +69 39 10 +70 40 11 +71 41 12 +72 42 13 +73 43 14 +74 44 15 +75 45 16 +76 46 17 +77 47 18 +78 48 19 +79 49 20 +80 50 21 +81 51 22 +82 52 23 +83 53 24 +83 53 24 +84 54 25 +85 55 26 +86 56 27 +87 57 28 +88 58 29 +89 59 30 +90 60 31 +91 61 32 +92 62 33 +93 63 34 +94 64 35 +95 65 36 +96 66 37 +97 67 38 +98 68 39 +99 69 40 +100 70 41 +101 71 42 +102 72 43 +103 73 44 +104 74 45 +105 75 46 +106 76 47 +107 77 48 +107 77 48 +108 78 49 +109 79 50 +110 80 51 +111 81 52 +112 82 53 +113 83 54 +114 84 55 +115 85 56 +116 86 57 +117 87 58 +118 88 59 +119 89 60 +120 90 61 +121 91 62 +122 92 63 +123 93 64 +124 94 65 +125 95 66 +125 95 66 +126 96 67 +127 97 68 +128 98 69 +129 99 70 +130 100 71 +131 101 72 +132 102 73 +133 103 74 +134 104 75 +135 105 76 +136 106 77 +137 107 78 +138 108 79 +139 109 80 +140 110 81 +141 111 82 +142 112 83 +143 113 84 +144 114 85 +145 115 86 +146 116 87 +147 117 88 +148 118 89 +149 119 90 +150 120 91 +151 121 92 +152 122 93 +153 123 94 +154 124 95 +155 125 96 +156 126 97 +157 127 98 +158 128 99 +159 129 100 +160 130 101 +161 131 102 +162 132 103 +163 133 104 +164 134 105 +165 135 106 +166 136 107 +167 137 108 +168 138 109 +169 139 110 +170 140 111 +171 141 112 +172 142 113 +173 143 114 +174 144 115 +175 145 116 +176 146 117 +177 147 118 +178 148 119 +179 149 120 +180 150 121 +181 151 122 +182 152 123 +183 153 124 +184 154 125 +185 155 126 +186 156 127 +187 157 128 +187 157 128 +188 158 129 +189 159 130 +190 160 131 +191 161 132 +192 162 133 +193 163 134 +194 164 135 +195 165 136 +196 166 137 +197 167 138 +198 168 139 +199 169 140 +200 170 141 +201 171 142 +202 172 143 +203 173 144 +204 174 145 +205 175 146 +206 176 147 +207 177 148 +208 178 149 +209 179 150 +210 180 151 +211 181 152 +212 182 153 +213 183 154 +214 184 155 +215 185 156 +216 186 157 +217 187 158 +218 188 159 +219 189 160 +220 190 161 +221 191 162 +222 192 163 +223 193 164 +224 194 165 +225 195 166 +226 196 167 +227 197 168 +228 198 169 +229 199 170 +230 200 171 +231 201 172 +232 202 173 +233 203 174 +234 204 175 +235 205 176 +236 206 177 +237 207 178 +238 208 179 +239 209 180 +240 210 181 +241 211 182 +242 212 183 +243 213 184 +244 214 185 +245 215 186 +246 216 187 +247 217 188 +248 218 189 +249 219 190 +250 220 191 +251 221 192 +252 222 193 +253 223 194 +254 224 195 +255 225 196 +255 225 196 +255 225 196 +255 225 196 +255 225 196 +255 225 197 +255 225 197 +255 225 197 +255 225 197 +255 226 198 +255 226 198 +255 226 198 +255 226 198 +255 226 198 +255 226 199 +255 226 199 +255 226 199 +255 226 199 +255 226 199 +255 227 200 +255 227 200 +255 227 200 +255 227 200 +255 227 200 +255 227 201 +255 227 201 +255 227 201 +255 227 201 +255 227 201 +255 228 202 +255 228 202 +255 228 202 +255 228 202 +255 228 202 +255 228 202 +255 228 203 +255 228 203 +255 228 203 +255 228 203 +255 228 203 +255 229 204 +255 229 204 +255 229 204 +255 229 204 +255 229 204 +255 229 204 +255 229 205 +255 229 205 +255 229 205 +255 229 205 +255 229 205 +255 230 205 +255 230 205 +255 231 205 +255 231 209 +255 233 214 +255 233 217 +255 242 233 +255 255 255 diff --git a/imagepy/data/luts/smart.lut b/imagepy/data/luts/smart.lut new file mode 100644 index 0000000000000000000000000000000000000000..1f98e65c047f19cd8381697f736c0b8c8386a6b5 GIT binary patch literal 800 zcmeZt_V-~}#K-^y|Cty-@E^lphCd)a7%(w0F|)9;vaxe;aB^{T^YHTV@e2qD3JD8~ zh>D4cOGrveNlVMf%E`$qC@Lx`tEi}|sjF*fYHDfg=;-R{>l+vv85x_Hn3|cJTUc6I zS=-p!+SxleI5;{wIlH*Hy1Bc1czStx`}q3$`3D391_cL)gocHMM?^+OMaRU%#>K}c zBqk*#r=+H)rKe?PW#{G>6c&|~mRHp_G_`hg_f4ESW6u1=%l59_xOv;oz55RxIezl= z+4C1KUAcPw#;rSd?>)Hp=<$=MPoF)1`RdKP_a8od{PgMb*Kgl{{P_9v*RS7y{`~#> z4=wwiG}Kg-6y#*3rKBV!B*ewV#Kc5JMMOk|g@uHK1O){JfPkML z7zYp_C?p~(E+Hi?D=!a>0VNexbxm!3BU5urYdZ&LcQ3!-h?vBb^z6LC%BGG9a~3UM zyLspCeFqL7J9*~(C14QUymkA|y?YNHK6(rerDxBdzkc`O(^pUk{R9Tj-@o+ozmuJn zsev}c?=n&nVxnLVgZ<0T$IHvh!^6$Z&Be*V&c?>d!otGL40ISHK>%_+Gf+7&0kg5O z1Jf`ECnpyu8T0Uh5;8FG#3W=CR5U!b1Ept!W6s`ZQdU+`RaFDJUqe$%3+M%1J$(ZMLt|qTQ!_Jj3k#rEtZi&ySIPh z8>GD-;)@|6db^FfU`wtvCa`N=K3zx25zj^20gU3&wzk2iT Date: Sun, 3 Mar 2019 00:14:42 +0800 Subject: [PATCH 111/343] v0.20, luts and multi-stack tif fixed --- .gitignore | 1 + imagepy/core/engine/filter.py | 16 +- imagepy/core/engine/simple.py | 22 +- imagepy/core/manager/colormanager.py | 13 +- imagepy/core/util/fileio.py | 2 + imagepy/data/luts/5_ramps.lut | 256 ----------------- imagepy/data/luts/Blue.lut | Bin 0 -> 768 bytes imagepy/data/luts/Cyan.lut | Bin 0 -> 768 bytes .../data/luts/{Cyan Hot.lut => Cyan_Hot.lut} | Bin imagepy/data/luts/Green.lut | Bin 0 -> 768 bytes ...reen Fire Blue.lut => Green_Fire_Blue.lut} | Bin imagepy/data/luts/ICA2.lut | Bin 0 -> 768 bytes imagepy/data/luts/ICA3.lut | Bin 0 -> 768 bytes imagepy/data/luts/Jet.lut | Bin 3365 -> 768 bytes imagepy/data/luts/Magenta.lut | Bin 0 -> 768 bytes .../luts/{Magenta Hot.lut => Magenta_Hot.lut} | Bin imagepy/data/luts/NanoJ-Orange.lut | Bin 768 -> 0 bytes imagepy/data/luts/Others/5_ramps.lut | Bin 0 -> 768 bytes imagepy/data/luts/Others/Errors.lut | Bin 0 -> 768 bytes .../luts/{SQUIRREL-FRC.lut => Others/FRC.lut} | 0 imagepy/data/luts/Others/Glow.lut | Bin 0 -> 768 bytes .../data/luts/{ => Others}/Rainbow RGB.lut | Bin imagepy/data/luts/{ => Others}/Thermal.lut | Bin imagepy/data/luts/{ => Others}/Viridis.lut | 0 imagepy/data/luts/{ => Others}/ametrine.lut | Bin .../luts/{ => Others}/blue_orange_icb.lut | Bin 800 -> 768 bytes imagepy/data/luts/{ => Others}/cool.lut | Bin 800 -> 768 bytes imagepy/data/luts/Others/edges.lut | 1 + imagepy/data/luts/Others/glasbey.lut | Bin 0 -> 768 bytes .../luts/{ => Others}/glasbey_inverted.lut | Bin imagepy/data/luts/Others/glasbey_on_dark.lut | Bin 0 -> 768 bytes imagepy/data/luts/{ => Others}/isolum.lut | Bin .../data/luts/{ => Others}/morgenstemning.lut | Bin .../data/luts/{ => Others}/mpl-inferno.lut | Bin imagepy/data/luts/{ => Others}/mpl-magma.lut | Bin imagepy/data/luts/{ => Others}/mpl-plasma.lut | Bin .../data/luts/{ => Others}/mpl-viridis.lut | 0 imagepy/data/luts/Others/phase.lut | Bin 0 -> 768 bytes imagepy/data/luts/{ => Others}/physics.lut | Bin imagepy/data/luts/{ => Others}/royal.lut | Bin 800 -> 768 bytes imagepy/data/luts/Others/sepia.lut | Bin 0 -> 768 bytes imagepy/data/luts/{ => Others}/smart.lut | Bin 800 -> 768 bytes imagepy/data/luts/Others/thal.lut | 1 + imagepy/data/luts/{ => Others}/thallium.lut | Bin 800 -> 768 bytes imagepy/data/luts/Others/unionjack.lut | Bin 0 -> 768 bytes imagepy/data/luts/Pink.lut | Bin 0 -> 768 bytes imagepy/data/luts/Red.lut | Bin 0 -> 768 bytes imagepy/data/luts/SQUIRREL-Errors.lut | 256 ----------------- imagepy/data/luts/Yellow.lut | Bin 0 -> 768 bytes imagepy/data/luts/edges.lut | Bin 800 -> 0 bytes imagepy/data/luts/gem.lut | Bin 800 -> 768 bytes imagepy/data/luts/glasbey.lut | 257 ------------------ imagepy/data/luts/glasbey_on_dark.lut | 256 ----------------- imagepy/data/luts/glow.lut | 257 ------------------ imagepy/data/luts/phase.lut | 257 ------------------ imagepy/data/luts/sepia.lut | 256 ----------------- imagepy/data/luts/thal.lut | Bin 800 -> 0 bytes imagepy/data/luts/unionjack.lut | 257 ------------------ .../Lookup table/Others/lookuptables_plg.py | 46 ++++ imagepy/menus/Plugins/Manager/console_wgt.py | 2 +- 60 files changed, 83 insertions(+), 2073 deletions(-) delete mode 100644 imagepy/data/luts/5_ramps.lut create mode 100644 imagepy/data/luts/Blue.lut create mode 100644 imagepy/data/luts/Cyan.lut rename imagepy/data/luts/{Cyan Hot.lut => Cyan_Hot.lut} (100%) create mode 100644 imagepy/data/luts/Green.lut rename imagepy/data/luts/{Green Fire Blue.lut => Green_Fire_Blue.lut} (100%) create mode 100644 imagepy/data/luts/ICA2.lut create mode 100644 imagepy/data/luts/ICA3.lut create mode 100644 imagepy/data/luts/Magenta.lut rename imagepy/data/luts/{Magenta Hot.lut => Magenta_Hot.lut} (100%) delete mode 100644 imagepy/data/luts/NanoJ-Orange.lut create mode 100644 imagepy/data/luts/Others/5_ramps.lut create mode 100644 imagepy/data/luts/Others/Errors.lut rename imagepy/data/luts/{SQUIRREL-FRC.lut => Others/FRC.lut} (100%) create mode 100644 imagepy/data/luts/Others/Glow.lut rename imagepy/data/luts/{ => Others}/Rainbow RGB.lut (100%) rename imagepy/data/luts/{ => Others}/Thermal.lut (100%) rename imagepy/data/luts/{ => Others}/Viridis.lut (100%) rename imagepy/data/luts/{ => Others}/ametrine.lut (100%) rename imagepy/data/luts/{ => Others}/blue_orange_icb.lut (87%) rename imagepy/data/luts/{ => Others}/cool.lut (87%) create mode 100644 imagepy/data/luts/Others/edges.lut create mode 100644 imagepy/data/luts/Others/glasbey.lut rename imagepy/data/luts/{ => Others}/glasbey_inverted.lut (100%) create mode 100644 imagepy/data/luts/Others/glasbey_on_dark.lut rename imagepy/data/luts/{ => Others}/isolum.lut (100%) rename imagepy/data/luts/{ => Others}/morgenstemning.lut (100%) rename imagepy/data/luts/{ => Others}/mpl-inferno.lut (100%) rename imagepy/data/luts/{ => Others}/mpl-magma.lut (100%) rename imagepy/data/luts/{ => Others}/mpl-plasma.lut (100%) rename imagepy/data/luts/{ => Others}/mpl-viridis.lut (100%) create mode 100644 imagepy/data/luts/Others/phase.lut rename imagepy/data/luts/{ => Others}/physics.lut (100%) rename imagepy/data/luts/{ => Others}/royal.lut (91%) create mode 100644 imagepy/data/luts/Others/sepia.lut rename imagepy/data/luts/{ => Others}/smart.lut (94%) create mode 100644 imagepy/data/luts/Others/thal.lut rename imagepy/data/luts/{ => Others}/thallium.lut (88%) create mode 100644 imagepy/data/luts/Others/unionjack.lut create mode 100644 imagepy/data/luts/Pink.lut create mode 100644 imagepy/data/luts/Red.lut delete mode 100644 imagepy/data/luts/SQUIRREL-Errors.lut create mode 100644 imagepy/data/luts/Yellow.lut delete mode 100644 imagepy/data/luts/edges.lut delete mode 100644 imagepy/data/luts/glasbey.lut delete mode 100644 imagepy/data/luts/glasbey_on_dark.lut delete mode 100644 imagepy/data/luts/glow.lut delete mode 100644 imagepy/data/luts/phase.lut delete mode 100644 imagepy/data/luts/sepia.lut delete mode 100644 imagepy/data/luts/thal.lut delete mode 100644 imagepy/data/luts/unionjack.lut create mode 100644 imagepy/menus/Image/Lookup table/Others/lookuptables_plg.py diff --git a/.gitignore b/.gitignore index 13521d02..0cf2d407 100644 --- a/.gitignore +++ b/.gitignore @@ -71,6 +71,7 @@ target/ # IPython Notebook .ipynb_checkpoints +.ipynb # pyenv .python-version diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index 111f3e62..023ded6b 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -114,26 +114,26 @@ def run(self, ips, snap, img, para = None): def check(self, ips): note = self.note if ips == None: - IPy.alert('no image opened!') + IPy.alert('No image opened!') return False elif 'req_roi' in note and ips.roi == None: - IPy.alert('no Roi found!') + IPy.alert('No Roi found!') return False elif not 'all' in note: if ips.get_imgtype()=='rgb' and not 'rgb' in note: - IPy.alert('do not surport rgb image') + IPy.alert('Do not surport rgb image') return False elif ips.get_imgtype()=='8-bit' and not '8-bit' in note: - IPy.alert('do not surport 8-bit image') + IPy.alert('Do not surport 8-bit image') return False elif ips.get_imgtype()=='16-bit' and not '16-bit' in note: - IPy.alert('do not surport 16-bit uint image') + IPy.alert('Do not surport 16-bit uint image') return False elif ips.get_imgtype()=='32-int' and not 'int' in note: - IPy.alert('do not surport 32-bit int uint image') + IPy.alert('Do not surport 32-bit int uint image') return False elif 'float' in ips.get_imgtype() and not 'float' in note: - IPy.alert('do not surport float image') + IPy.alert('Do not surport float image') return False return True @@ -159,7 +159,7 @@ def ok(self, ips, para=None, callafter=None): elif ips.get_nslices()>1: has, rst = 'stack' in para, None if not has: - rst = IPy.yes_no('run every slice in current stacks?') + rst = IPy.yes_no('Run every slice in current stacks?') if 'auto_snap' in self.note and self.modal:ips.reset() if has and para['stack'] or rst == 'yes': para['stack'] = True diff --git a/imagepy/core/engine/simple.py b/imagepy/core/engine/simple.py index 9db7a8ee..62e89f80 100644 --- a/imagepy/core/engine/simple.py +++ b/imagepy/core/engine/simple.py @@ -22,7 +22,7 @@ class Simple: modal = True def __init__(self, ips=None): - print('simple start') + print('Simple start') self.ips = IPy.get_ips() if ips==None else ips self.dialog = None @@ -69,36 +69,36 @@ def runasyn(self, ips, imgs, para = None, callback = None): def check(self, ips): note = self.note if ips == None: - IPy.alert('no image opened!') + IPy.alert('No image opened!') return False if 'req_roi' in note and ips.roi == None: - IPy.alert('no Roi found!') + IPy.alert('No Roi found!') return False if not 'all' in note: if ips.get_imgtype()=='rgb' and not 'rgb' in note: - IPy.alert('do not surport rgb image') + IPy.alert('Do not surport rgb image') return False elif ips.get_imgtype()=='8-bit' and not '8-bit' in note: - IPy.alert('do not surport 8-bit image') + IPy.alert('Do not surport 8-bit image') return False elif ips.get_imgtype()=='16-bit' and not '16-bit' in note: - IPy.alert('do not surport 16-bit uint image') + IPy.alert('Do not surport 16-bit uint image') return False elif ips.get_imgtype()=='32-int' and not 'int' in note: - IPy.alert('do not surport 32-bit int uint image') + IPy.alert('Do not surport 32-bit int uint image') return False elif 'float' in ips.get_imgtype() and not 'float' in note: - IPy.alert('do not surport float image') + IPy.alert('Do not surport float image') return False if sum([i in note for i in ('stack','stack2d','stack3d')])>0: if ips.get_nslices()==1: - IPy.alert('stack required!') + IPy.alert('Stack required!') return False elif 'stack2d' in note and ips.is3d: - IPy.alert('stack2d required!') + IPy.alert('Stack2d required!') return False elif 'stack3d' in note and not ips.is3d: - IPy.alert('stack3d required!') + IPy.alert('Stack3d required!') return False return True diff --git a/imagepy/core/manager/colormanager.py b/imagepy/core/manager/colormanager.py index aede1db1..a6c2884f 100644 --- a/imagepy/core/manager/colormanager.py +++ b/imagepy/core/manager/colormanager.py @@ -20,8 +20,13 @@ keys = [os.path.split(filename)[-1][:-4] for filename in filenames] values = [np.fromfile(filename, dtype=np.uint8).reshape((3,256)).T.copy() for filename in filenames] +filenames_others = glob(os.path.join(root_dir,'data/luts/Others/*.lut')) +keys_others = [os.path.split(filename)[-1][:-4] for filename in filenames_others] +values_others = [np.fromfile(filename, dtype=np.uint8).reshape((3,256)).T.copy() for filename in filenames_others] + class ColorManager: luts = dict(zip(keys, values)) + luts_others = dict(zip(keys_others, values_others)) frontcolor = (255,255,0) backcolor = (0,0,0) wr, wg, wb = 1.0/3, 1.0/3, 1.0/3 @@ -63,4 +68,10 @@ def get_lut(cls, name='grays'): if name=='grays': lut = np.arange(256).reshape((-1,1)) return (lut*np.ones((1,3))).astype(np.uint8) - else: return cls.luts[name].copy() \ No newline at end of file + else: return cls.luts[name].copy() + @classmethod + def get_lut_others(cls, name='grays'): + if name=='grays': + lut = np.arange(256).reshape((-1,1)) + return (lut*np.ones((1,3))).astype(np.uint8) + else: return cls.luts_others[name].copy() \ No newline at end of file diff --git a/imagepy/core/util/fileio.py b/imagepy/core/util/fileio.py index 98305677..ec388a72 100644 --- a/imagepy/core/util/fileio.py +++ b/imagepy/core/util/fileio.py @@ -8,6 +8,8 @@ def show_img(img, title): if isinstance(img, list): return IPy.show_img(img, title) + if img.dtype!=np.uint8 and img.ndim>2 and img.shape[2]!=3: + return IPy.show_img(img, title) if img.dtype==np.uint8 and img.ndim==3 and img.shape[2]==4: img = img[:,:,:3].copy() IPy.show_img([img], title) diff --git a/imagepy/data/luts/5_ramps.lut b/imagepy/data/luts/5_ramps.lut deleted file mode 100644 index 6a9c6a56..00000000 --- a/imagepy/data/luts/5_ramps.lut +++ /dev/null @@ -1,256 +0,0 @@ -0 0 1 -0 0 5 -0 0 10 -0 0 15 -0 0 20 -0 0 25 -0 0 30 -0 0 35 -0 0 40 -0 0 45 -0 0 50 -0 0 55 -0 0 60 -0 0 65 -0 0 70 -0 0 75 -0 0 80 -0 0 85 -0 0 90 -0 0 95 -0 0 100 -0 0 105 -0 0 110 -0 0 115 -0 0 120 -0 0 125 -0 0 130 -0 0 135 -0 0 140 -0 0 145 -0 0 150 -0 0 155 -0 0 160 -0 0 165 -0 0 170 -0 0 175 -0 0 180 -0 0 185 -0 0 190 -0 0 195 -0 0 200 -0 0 205 -0 0 210 -0 0 215 -0 0 220 -0 0 225 -0 0 230 -0 0 235 -0 0 240 -0 0 245 -0 0 250 -0 1 0 -0 5 0 -0 10 0 -0 15 0 -0 20 0 -0 25 0 -0 30 0 -0 35 0 -0 40 0 -0 45 0 -0 50 0 -0 55 0 -0 60 0 -0 65 0 -0 70 0 -0 75 0 -0 80 0 -0 85 0 -0 90 0 -0 95 0 -0 100 0 -0 105 0 -0 110 0 -0 115 0 -0 120 0 -0 125 0 -0 130 0 -0 135 0 -0 140 0 -0 145 0 -0 150 0 -0 155 0 -0 160 0 -0 165 0 -0 170 0 -0 175 0 -0 180 0 -0 185 0 -0 190 0 -0 195 0 -0 200 0 -0 205 0 -0 210 0 -0 215 0 -0 220 0 -0 225 0 -0 230 0 -0 235 0 -0 240 0 -0 245 0 -0 250 0 -1 1 0 -5 5 0 -10 10 0 -15 15 0 -20 20 0 -25 25 0 -30 30 0 -35 35 0 -40 40 0 -45 45 0 -50 50 0 -55 55 0 -60 60 0 -65 65 0 -70 70 0 -75 75 0 -80 80 0 -85 85 0 -90 90 0 -95 95 0 -100 100 0 -105 105 0 -110 110 0 -115 115 0 -120 120 0 -125 125 0 -130 130 0 -135 135 0 -140 140 0 -145 145 0 -150 150 0 -155 155 0 -160 160 0 -165 165 0 -170 170 0 -175 175 0 -180 180 0 -185 185 0 -190 190 0 -195 195 0 -200 200 0 -205 205 0 -210 210 0 -215 215 0 -220 220 0 -225 225 0 -230 230 0 -235 235 0 -240 240 0 -245 245 0 -250 250 0 -1 0 0 -5 0 0 -10 0 0 -15 0 0 -20 0 0 -25 0 0 -30 0 0 -35 0 0 -40 0 0 -45 0 0 -50 0 0 -55 0 0 -60 0 0 -65 0 0 -70 0 0 -75 0 0 -80 0 0 -85 0 0 -90 0 0 -95 0 0 -100 0 0 -105 0 0 -110 0 0 -115 0 0 -120 0 0 -125 0 0 -130 0 0 -135 0 0 -140 0 0 -145 0 0 -150 0 0 -155 0 0 -160 0 0 -165 0 0 -170 0 0 -175 0 0 -180 0 0 -185 0 0 -190 0 0 -195 0 0 -200 0 0 -205 0 0 -210 0 0 -215 0 0 -220 0 0 -225 0 0 -230 0 0 -235 0 0 -240 0 0 -245 0 0 -250 0 0 -1 1 1 -5 5 5 -10 10 10 -15 15 15 -20 20 20 -25 25 25 -30 30 30 -35 35 35 -40 40 40 -45 45 45 -50 50 50 -55 55 55 -60 60 60 -65 65 65 -70 70 70 -75 75 75 -80 80 80 -85 85 85 -90 90 90 -95 95 95 -100 100 100 -105 105 105 -110 110 110 -115 115 115 -120 120 120 -125 125 125 -130 130 130 -135 135 135 -140 140 140 -145 145 145 -150 150 150 -155 155 155 -160 160 160 -165 165 165 -170 170 170 -175 175 175 -180 180 180 -185 185 185 -190 190 190 -195 195 195 -200 200 200 -205 205 205 -210 210 210 -215 215 215 -220 220 220 -225 225 225 -230 230 230 -235 235 235 -240 240 240 -245 245 245 -250 250 250 -255 255 255 diff --git a/imagepy/data/luts/Blue.lut b/imagepy/data/luts/Blue.lut new file mode 100644 index 0000000000000000000000000000000000000000..7516bd7639985ca9afb4466f17497758de25cc60 GIT binary patch literal 768 zcmZQz7zHCb1Q?l^Sy_}RaDi~H8i!f zb#(Rg4GfKpO-#+qEiA39ZEWrA9UPsUU0mJVJv_aQ&Q8?GcvQXb8_?Y3kr*hOG?YiD=Mq1YijH28ycIMTUy)NJ370%dwTo&Crq3) zdCJsj(`U?_HG9t7dGi-6T(o$}(q+q6tX#Ev&DwSAH*DOrdCS&q+js2TwR_Lreftj_ zJaqWT(PPI?oIG{<%-M72FI>EI`O4L6*Kgdsb^FfUd-oqaeDwIq(`V0LynOZg&D(eH WKYaZ3`ODXD-+%o4_507?fBylm_J4r@ literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/Cyan.lut b/imagepy/data/luts/Cyan.lut new file mode 100644 index 0000000000000000000000000000000000000000..dc7c1f692366ee473efcb62a1688ef3b702164d2 GIT binary patch literal 768 zcmZQz7~z1CiJ66!jh%y&i<^g+k6%DgNLWNvOk6@zN?Jx%PF_J#Nm)fzObje8tLDtJkbuw|>LMO`ErD-L`$l&Rx6r?A^Ejz`;X@ zj~qRA{KUyqr_Y={cmBe~OP8-)y>|V^&0Dwc+`V`I!NW(7pFDl`{Kd;xuiw0V_x{7j YPoKYh{r3IG&tJd){QdWzVTAi10OV}{0RR91 literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/Cyan Hot.lut b/imagepy/data/luts/Cyan_Hot.lut similarity index 100% rename from imagepy/data/luts/Cyan Hot.lut rename to imagepy/data/luts/Cyan_Hot.lut diff --git a/imagepy/data/luts/Green.lut b/imagepy/data/luts/Green.lut new file mode 100644 index 0000000000000000000000000000000000000000..0dcc2e0577af7441a36fda63e3561466cc8fff82 GIT binary patch literal 768 zcmZQz7~z1CiJ66!jh%y&i<^g+k6%DgNLWNvOk6@zN?Jx%PF_J#Nm)fzObje8tLDtJkbuw|>LMO`ErD-L`$l&Rx6r?A^Ejz`;X@ zj~qRA{KUyqr_Y={cmBe~OP8-)y>|V^&0Dwc+`V`I!NW(7pFDl`{Kd;xuiw0V_x{7j YPoKYh{r3IG&tJd){QdWTgvUMu05^7jfdBvi literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/Green Fire Blue.lut b/imagepy/data/luts/Green_Fire_Blue.lut similarity index 100% rename from imagepy/data/luts/Green Fire Blue.lut rename to imagepy/data/luts/Green_Fire_Blue.lut diff --git a/imagepy/data/luts/ICA2.lut b/imagepy/data/luts/ICA2.lut new file mode 100644 index 0000000000000000000000000000000000000000..a5ac0ec9efb2405121b7aad17bbf162ed14d4f02 GIT binary patch literal 768 zcmezW=hyeIpFg~N^YZ!AM-T4azIpA+rSoS`pE!2-;Ql?kc5L0eaow7g%a<-%FmLwE z=~E_6=nt*R(1F3iixN>5Erh>M8~4+#qJ_4aggak96ywlFg`)Ys9{P*qlx zlaUk`6%yd%=45AKV!#Gix%fpS<&-sajVx@P+Vkj&{{H>*=lAblzkdGw@#FjVZ{NOs{rct0=g*%$ef;?0 z!~6H|-o1VM_RX8uuV1}-`SQh!=g*%#d;0Y0lP8ZKKYH}=;e!YF@87$3_wJoLcW&Rl zb?fHM8#k_Bzjp2F)hk!7T)uqi(#4AxE}TDq?%dh4XU?2CxP9&7nSCu)1!>WN?zYAn zaw1#|%xs)I{6eA<(sGI_>RP%6CgxVQ4$f|#KK?hcvMV$Qc8MePChW)Dr@Q+TiSsEH*w1JnRDhXT(WHC TnspnuY}>hK-@zltPM!e(uHbO1 literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/ICA3.lut b/imagepy/data/luts/ICA3.lut new file mode 100644 index 0000000000000000000000000000000000000000..c03931ed07539d7f221412442d3552457f691104 GIT binary patch literal 768 zcmezW@893QfByXb{p;7ypFe(l|NiaUx36EneEIzO)2ELgKYV!q{@uH`Z{NOo^ZNCx zS1(__c=7!Cvu97AK7I1!@#9C29zJ~V;Qsx4_wL@kbLY2GOeh- zXXesP`_J5X`sqI%!I@JhjvYC4VBenIJGXD$ym9^7)vH!4Te4`uyg9RGPMV~xOjJZzXmFsvueYbWtBaF^osE@+nTfHXzOJ^W zx~j60f}D($gqVnsAU`iRCp#-MBLgTnJ0o7b;izIguZ z>681r+nXB*`F^mwzow$3ASWXwAtoXuz{kVI!N$VGKu1GGK}JGEfQN&HkoOn{I{Ybh KKS2)?PyhgyOm33^ literal 3365 zcmXw*OLl}n3EoPx6!?w ziO#iMjn3_E^lne0>)75#_qL>fBJtk{U9xD`$1aSXvSW)@4X#m zqa8=t&T~6t`~K~QvONi9p9+w#@6(bg5VSg-k&TT#~PL?2>)8Wta4;J>ziz9E;7lrs2mKFba-0U=+}>`PnG= z-T|Y4NA=D<3eFU`N5Po_vf^D!>l`D!>r|D!>x~D!>&3UKQYr02Sbj zfKkvUzN4T`d`Cf>h!J+Gew3cy; zeVDhIAD;kt)HOaG@LS{f$ZUUd0$W8;_`iLW;6iY5v@`Qk^ zZUVaY_8RJMx!G8F@f46!EWOx@9cihv&H`HMs=2yBX;dC?U6y)kF((lC)@6U|q!f*+ z(ZJMVBi3tRbg>p|x4%W-7ZXs&fhk94YdVZ%z4PZgu=TE=_3{p3XzD)ybDx9E?D#t{ zN15>fe^~q)bS6k1F#0k?>HwoJbEHl%`Z7vB2N->sCb0F;7gy2-AoAi%ngm2$oJk9T zh>JIAFc58VC++7>YjR!vUf!4&~^82#ZHKSRlIMQjQ&ntoW3n0HP{RWn_SCv3Qk% z0%Ekdm2m@dxbZ8)2&6l4EThUhk;#u|8Dt>CAJ;PGK!!iQ#RNe7-0s_a_Hn%nArKq? zdT%LI{aSA+RJHR~a^hwO{8=a`rjM!9vG=|xzcN=e+kXm+X1@%p=;zv0nWZv%y*G<~ z9!4~)Xr5G5d;87@Rf}H>d>nK!_?-c&7P>2{7FsWg7SBZ#EgqUET0Buvw0PX2XmJ)q zbs~cz>Lim7%G+^9OcX6Xizr&`h^SiVtEgJiU3Rv8_N2X6E$MCt(CPDDwQ#1QYQYg8 z7WY(P8bsBCJ4Mxk*G1LB<`7kjA7G$r@zV`dE$l^6`@)75RSUaX6fGot_&kdgSOrmn zbU}pp$^t}(?>sG~|APSgq0IS+&_}K!80$eAE0za4l zQQ+4WAPQJ0f++AS4-f^@%bl-NOHV6zR`Y-q=y^!pd|)kzb> ztB)pTtLmbOS*i1mmy3O>gS_rC)jtzwlO|@3>YcgH4%Immvq0w?%Uq^k*Ua@S^u+M$ pn7IxweM=0lZt*(n(*2q^@0P^y{(8lh^u3Ym@cw#5J>Oqk{|Dd0`f~sP diff --git a/imagepy/data/luts/Magenta.lut b/imagepy/data/luts/Magenta.lut new file mode 100644 index 0000000000000000000000000000000000000000..ee164fedb6a8e3beec7dd1fa0b7509128488805c GIT binary patch literal 768 zcmZQzWMXDvWn<^yMC+6cQE@6%&_`l#-T_m6KOcR8m$^Ra4i{)Y8_`)zddH zG%_|ZH8Z!cw6eCbwX=6{baHlab#wRd^z!!c_45x13RUz zF>}`JIdkXDU$Ah|;w4L$Enl&6)#^2C*R9{Mant54TeofBv2)k%J$v`VgvUPsYwZ64 literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/Magenta Hot.lut b/imagepy/data/luts/Magenta_Hot.lut similarity index 100% rename from imagepy/data/luts/Magenta Hot.lut rename to imagepy/data/luts/Magenta_Hot.lut diff --git a/imagepy/data/luts/NanoJ-Orange.lut b/imagepy/data/luts/NanoJ-Orange.lut deleted file mode 100644 index 4ce38ed935033fd859e470e439515dd90cc365ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 768 zcmZQzVqxRp;^7kz5)qS>mX%jjR#n&3*3~yMF|)9;v2$>8ar5x@^$!dV4UdeDjZaKT z%gD;fD<~={tEj4}Z)|RD@9ggFpE!Bij9GK$Em*W<*@{(b)^FUrb^FfUd-oqaeDuW0 zlc!FfK6Cc$x%1~QT)cSc(xuCnuUxr$_1d-T*RS8War5S_TeolDxpU|4-Fx@$-+%Dn z;lqcI9zA~ig&z?Ph{`|#@moHzvdj0y%n>TOYzI*rn{f7@9KYsl5>GS6=U%r0* zh6KJNzz+oYi2%P4;5QsF&=>%Nmy?^99~!z!D!|ay($O<8G&Ti=udTf!D1be^ef|V^&0Dwc+`V`I;iJb- zo<4j2;^nK?Z{EIp{{i8BMmS)C0cJR0fdN)HV1om87~p^dP8dL#4`nejGBGo=u&}bR zv2$>6a&mEV^YHNU@$m}?2nq@b3yX+|iiwF!NJvUbNlVMf%F4;fD<~)`Dk&?gsH&=| zscUFxYHDd~>*(t0>FFDQ!qFUNc(D8SlvCEwH8Qtxbn^}fjfzjn%qy;_ZEEkCIBoWVrK{F&*}3oViL;lk l-+B1#^@p#&=%eL78NR2F%jn~<|73WE442VI3mJa-4*(4~%02)9 literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/Others/Errors.lut b/imagepy/data/luts/Others/Errors.lut new file mode 100644 index 0000000000000000000000000000000000000000..b4b826a211a92c9e18883c05f7810989186eedfd GIT binary patch literal 768 zcmYdGOHa?p$jr>l0)lK90C9j~85!y6X=$k`DalER2?_CWu`$t6k>O#XA;EzG{(e5* zo*wS5E>4d2b~e_Q7G|c#Mh5!2I$E0QYAVW#3UabCQj!v4q9VeA0{ncuJltHI9PDgt ztSl_dOiYZ7j0{Ks$Yo+?W?^MzW9Q)H;^yV!7Zef}6_=2bk&{{ zcXV;{^zjP}36F}6PfE?m&MPb_uc~cmZtLvnpEPyG?0E~9EMK*D!{%)}cJDiQ_}IzQ z=Pq2jdi~~|dk-ExdG_Mf>$mSeeER(L+xH*8e*gaS_uqd81|+M2c5`v_@bdBT3kU)o zAR;OzCN3cgM6;V?CIcxTux$_q+T(o%UvgIpQu3EEp-TDn1H*MLvZTpU$ zyLRv0xBtMwLx+zXJ$C%$snci9o;!cx;-$-1u3o!-eI>6_u4$RaMp1)ipIWwY9Z%b#?VX&;SIDK+psR%`nh{4qy_^V09oJjX<3s z6MzQQ)&k9}t_B)kSy@q0US3vKT3S+ETvSw8P>`ROmz$H54NQulbO=p`@z7)#0ZxZO zpoHk_6_=2flUGzx)zH$_Gc-1}u(GjpbaC_W@(TzG35$%5O-M>f z%gD;fD=aQ8ud1zYY-w-r?CI^BIA!XLS###jTd-)!vK6aVuU)rc)8;MPw(s1v_rSq} zhYlY;a_soY(`U|{J$L@%WgxhE{RR-+x_$fhoqP9z;K74OkAdLH)8`QI;^iwac>VSr z6ukfN2?joY{RRYIA>jLupXlJ%pT8skhAsvqzz73OP{0fXEUZAl#>U3Z&cO);TwL5d zynH~wFCZW&BrGf}A}S^>At5O(BO@a#ub`l)tfHc-uA!l+rLC){Z(w9>VrFh(X>Dik z=;Y$&?&;<2>mL{#8Xg$~49w(|^vvws{KAsb^2+Mkx`w8f_Rj8}z6ld2PniY`(0L0M zf&z5)nlfC1S5K)+)LtgP(7 z;6MYQFhT$hbij@Zpa!#Z@qhs@zo3wiu!xwrxP+9nw2YjBqLMN&Fg1ZeXFn>ru!xZX2>vrNFflMP{A2jd@C(ETgNcmx8|@D=0RYy^36TH* diff --git a/imagepy/data/luts/cool.lut b/imagepy/data/luts/Others/cool.lut similarity index 87% rename from imagepy/data/luts/cool.lut rename to imagepy/data/luts/Others/cool.lut index 790cba3e1120c4bbc47f11be4d329b9e5fe2b458..6099489a57d106ca25de2d2c888197bd3ccd03ab 100644 GIT binary patch delta 7 OcmZ3$*1)zFn>ru!xZX2>vrNFf%YQ{A2jV@D0QVgNcj|8yyZX0RYyj36B5( diff --git a/imagepy/data/luts/Others/edges.lut b/imagepy/data/luts/Others/edges.lut new file mode 100644 index 00000000..ead70ca2 --- /dev/null +++ b/imagepy/data/luts/Others/edges.lut @@ -0,0 +1 @@ + $).3=Zix´xiPF=3.)$  $).3=Zix´xiPF=3.)$  $).3=Zx´xiPF=3.)$  \ No newline at end of file diff --git a/imagepy/data/luts/Others/glasbey.lut b/imagepy/data/luts/Others/glasbey.lut new file mode 100644 index 0000000000000000000000000000000000000000..04bef3a8bb8fdf53c090fc5fa508f455d390f4e3 GIT binary patch literal 768 zcmV+b1ONR00RI30{{a60ngDno|FQA@-5_#y0?3H}g`pGx+>-zRIqm?fp``yu)cdP- zrU0d40035>LjM5#pQlOykV*-n-ja8Na|K4jJt^WXuZ{h#ta&LxXX~@yWPp+!R|B_~JT&&W^|N1fh z{zS>gM3WW>bZ9`N&vsJ)E1<9)de)$7nZSLO)fT30R{#J1%0ueECRKUm>PL;bvP631I*>6i^1IK}OJ5&e(g!+2`zC8WKr@FfUD0+y3q##xR zFlM`?b=-|r#HIfjLYiOkU#w(>|56(6&w$ha0Hfz`7~kWfhtUO9!aS;<~9J3 zZQoE`FwRo|DpUWMibZVzOok)P%1f~55K@UICT^tCXQeGsS!RZq|A`{cxs)PNjW(j_ zn5;`wEj%f40096}T-h1}I?-gE+c^IKqJ#Nqyln#j0Bx$JEV;JqrT#n--5Nrjf(CW} z{{R3pwg3SCLcYP6{c&7E{{dB%Bme-PX8`}6lmGuvYW`%*|8sE_#cu%Dn*Oc=sDl7M zLc2qJx)W@sZ2uYsLSGE|J1L8Gp#xiXoJMeqd*x zJd`E>hm-(&%gE+LilICJo&8NY|79>xAOQbXO{Qh9tWLQ9pR!_;9*s_6WEWKM06H9b y0RJY}n=V*jB3Yh`@@l@kBMjEaZ1qY(cvP0e1#!vKrWf0u58WS-1I|A>%nT>xNI0Eyeh z5avNDX#WD2llQ{{zyO^1|7o~{*3re@qgrXYasYUp*Zu(R7rTD{N!YNXKry-wRfzz7 z(Ttu}ny{;9l4?z}ppU9sTg~eL%g%>0CcO`tw8_W zsC^Lt{~41&0P9Pdtv~ecmV;U zT9D1xJCK9_*KosT0I_1xnZ0H?Qouil08j(`Sy-L0TbhM*zfUX#k$C#E;-f z|DrybbWnq~8JcxF2!NJ0IW6A&j|2b}mpNOB)ZIM@ew7yvYO8@_2Q?F~wxK%9xR|H>| z03E)RCuRTu^O^uHjbi@;|AHz>0KI-~&QWBE+Q8h<0RM;ovAR|_h(vt=sGM$2^{6wc zoP5mB0DC}@0N<4X0-bejfmO)I=o4=YTQ#$|dluEB1h3Ej?dvI*9c$KFAb)MG4LHeEk0?r-no@WL@%K(H#(SuUBZHl1) literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/isolum.lut b/imagepy/data/luts/Others/isolum.lut similarity index 100% rename from imagepy/data/luts/isolum.lut rename to imagepy/data/luts/Others/isolum.lut diff --git a/imagepy/data/luts/morgenstemning.lut b/imagepy/data/luts/Others/morgenstemning.lut similarity index 100% rename from imagepy/data/luts/morgenstemning.lut rename to imagepy/data/luts/Others/morgenstemning.lut diff --git a/imagepy/data/luts/mpl-inferno.lut b/imagepy/data/luts/Others/mpl-inferno.lut similarity index 100% rename from imagepy/data/luts/mpl-inferno.lut rename to imagepy/data/luts/Others/mpl-inferno.lut diff --git a/imagepy/data/luts/mpl-magma.lut b/imagepy/data/luts/Others/mpl-magma.lut similarity index 100% rename from imagepy/data/luts/mpl-magma.lut rename to imagepy/data/luts/Others/mpl-magma.lut diff --git a/imagepy/data/luts/mpl-plasma.lut b/imagepy/data/luts/Others/mpl-plasma.lut similarity index 100% rename from imagepy/data/luts/mpl-plasma.lut rename to imagepy/data/luts/Others/mpl-plasma.lut diff --git a/imagepy/data/luts/mpl-viridis.lut b/imagepy/data/luts/Others/mpl-viridis.lut similarity index 100% rename from imagepy/data/luts/mpl-viridis.lut rename to imagepy/data/luts/Others/mpl-viridis.lut diff --git a/imagepy/data/luts/Others/phase.lut b/imagepy/data/luts/Others/phase.lut new file mode 100644 index 0000000000000000000000000000000000000000..e37dd3600d8d39f7f48c6e9bc317c119cc4e714d GIT binary patch literal 768 zcmc)H5rczZ9LMnof*=TjAP9mW2!bF820;)6K@bE%5ClOG1cP7@41ypCf*=TjAUHS} z3=Rgt!C-JO2!ea0|6ADS9sGV@gb+axBuP;eO*0I`vK+_pydVgoC`pnmD~h73nx^Uc z>2xv-(=;v1wr$68UDxwG-w%Qy45KKD<0MJaG|RF)KcCMXQa9zi7Y}>Ld(=-eNchYrSOjU6^ zS;iGb5%=VIp5s`SWf+>KDT*XXfFn>ru!xZX2>vrNFfy<+`~yRf00>NEOxT#PfC&KDe+j+- diff --git a/imagepy/data/luts/Others/sepia.lut b/imagepy/data/luts/Others/sepia.lut new file mode 100644 index 0000000000000000000000000000000000000000..60289be7d45a23c783be1a2cf4f4da26ba78053e GIT binary patch literal 768 zcmZS3wsdrIc5!uc_we-c_VM-e4+snj4h{(o3y+A5ijIkmi%&>QN=`{lOV7y6%FfQo z&C4$+EGjN3Ei136tg5c5t*xtXXl!b3X>Duo=Fw*EFmcl4DO0CSpD}aR>^XDi z&0nx^(c&dbmn~nha@FcJYuBycuyNDoEnBy3-?4Mo?%jL#?%RLh;Gx4ujvhOH;^e8* zXU?8Gf8pY#%U7;myME*5t=o6*-n;+c;iJb-o<4j2;^nK?Z{EIp|Ka1O&tJZN`~KtS zuit;{!#D&g7D0l=1kKy160z5^4XV0ELfBEv$e+Cv-Hg*n9E^Z!PK7Ii~Az=|w zF>whANhxU=Svh$HMJ3eGfdmgq=wPNEFn>ru!xZX2>vrMfZ#udzYKpsd@xwR3;?R^2etqJ diff --git a/imagepy/data/luts/Others/thal.lut b/imagepy/data/luts/Others/thal.lut new file mode 100644 index 00000000..2a71f22d --- /dev/null +++ b/imagepy/data/luts/Others/thal.lut @@ -0,0 +1 @@ + !%)-159=@DHLPTX\`dhlptx| !%)-159=@DHLPTX\`dhlptx|Ͽp`P@1! !%)-159=@DHLPTX\`dhlptx| !)19@HPX`hpxϿp`P@1!!1@P`p \ No newline at end of file diff --git a/imagepy/data/luts/thallium.lut b/imagepy/data/luts/Others/thallium.lut similarity index 88% rename from imagepy/data/luts/thallium.lut rename to imagepy/data/luts/Others/thallium.lut index 9bb66677d4a3174598875f9e640b253fab01320f..c90b5424866cdc5f5a51cfe63bc3dfd241d3a7e2 100644 GIT binary patch delta 7 OcmZ3$*1)zFn>ruz-;P2>vrNfZ$&+1POq^M8@om*;APS*SZP5 diff --git a/imagepy/data/luts/Others/unionjack.lut b/imagepy/data/luts/Others/unionjack.lut new file mode 100644 index 0000000000000000000000000000000000000000..99ba803f273e3cae6ebb20ea13e565f37475fda4 GIT binary patch literal 768 zcmZQzpb#*#bMp&}OUo;(YwH`ETiZLkd;156N5?0pXXh7}SJyYUclS@8K70P+<*V0k z-oAVP;p3;zU%r0({^RGb-+%u8|Nq}VzkmJw@%`J^FP}es{P6zW+c&RYy?pWf+0!SF zA3c0<|K8m@w{P9NasArWE0-@_ym0>9*)yk4ojh^;*wG`04;?(PZ{Pj{2M--Sa`f2o z6DLodK6Ccm`3n~>UA}Vl+VvYZZ{5Cg_ul;n4<9{#^7PsB7cXDEe)IO-`wt&Keg5+G z+xH(ofBpXRpF-a=Fi`6M-`_vKfBpRN{oB_spFe&4@c!M~H?Lp4eDVC*(Q^U zmlx+}rzgiphX?z6yF1%kn;YwEt1HV(iwpB}vr}w80~0F;H=m%0xRk7dvYMujzLBYg zwVk7jyO(c3a9Ctad}2y^R&GIYS!GRqQ)@?e-^3}?XU$!(c-hJ|>o;xPL2>y1A7K9j E01VE1M*si- literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/Pink.lut b/imagepy/data/luts/Pink.lut new file mode 100644 index 0000000000000000000000000000000000000000..f30fd8965e7572c039f085aa87e6db8346463504 GIT binary patch literal 768 zcmb1O_^6?K035$r1jZaKYP0!5E%`Yq}DJ!q6s;R4QY;I|5@9gUC z>Fb|3Y0A`T(`U|_J!jth1q&B1S-Nb+%2lh^u3NW$~I_@bTlvPoF-2{`>_9zJC4s?c4Y7-+%o0@$=`e zU%!3>!Jj{W{{H><@85qQU|?Y46cCq@S5i^a(9+Q}Ff=wbx3sdcwX=70a&~cZ_we-g z@%0M`3*$1p+T3l=U~ymZ;}6{}XSS-XD2hK-xIY~8j4 z6li;ap?2`l;UhNRWDf>IeM#5Qi)w0ZLuP-5G)d+$C_ zn1O-}6k?zN1BKVkTet53lg@)jkDok!{sNdzUcY(!{=>&lU%r0({^RGb-+%ss5&!@! C1o6iJ literal 0 HcmV?d00001 diff --git a/imagepy/data/luts/Red.lut b/imagepy/data/luts/Red.lut new file mode 100644 index 0000000000000000000000000000000000000000..881982ecc4c5e1c4b830ef9eff983059a73c587a GIT binary patch literal 768 zcmZQzWMXDvWn<^yMC+6cQE@6%&_`l#-T_m6KOcR8m$^Ra4i{)Y8_`)zddH zG%_|ZH8Z!cw6eCbwX=6{baHlab#wRd^z!!c_45x13RUz zF>}`JIdkXDU$Ah|;w4L$Enl&6)#^2C*R9{Mant54TeofBv2)k%J$v`MC+6cQE@6%&_`l#-T_m6KOcR8m$^Ra4i{)Y8_`)zddH zG%_|ZH8Z!cw6eCbwX=6{baHlab#wRd^z!!c_45x13RUz zF>}`JIdkXDU$Ah|;w4L$Enl&6)#^2C*R9{Mant54TeofBv2)k%J$v`qTGjef48pG~9wN5ntlV2OVK@)$K^ diff --git a/imagepy/data/luts/gem.lut b/imagepy/data/luts/gem.lut index c9194056d8e4276eb9dc6ee5a38202341f43d9cb..8ea4af6a958ace5bcd6882e175e864d9a273b109 100644 GIT binary patch delta 7 OcmZ3$*1)zFn>ru!xZX2>vrNFahB|hCd9yKzuNm$mp=q;Q$i=*82&R diff --git a/imagepy/data/luts/glasbey.lut b/imagepy/data/luts/glasbey.lut deleted file mode 100644 index 283612d8..00000000 --- a/imagepy/data/luts/glasbey.lut +++ /dev/null @@ -1,257 +0,0 @@ -Index Red Green Blue -0 255 255 255 -1 0 0 255 -2 255 0 0 -3 0 255 0 -4 0 0 51 -5 255 0 182 -6 0 83 0 -7 255 211 0 -8 0 159 255 -9 154 77 66 -10 0 255 190 -11 120 63 193 -12 31 150 152 -13 255 172 253 -14 177 204 113 -15 241 8 92 -16 254 143 66 -17 221 0 255 -18 32 26 1 -19 114 0 85 -20 118 108 149 -21 2 173 36 -22 200 255 0 -23 136 108 0 -24 255 183 159 -25 133 133 103 -26 161 3 0 -27 20 249 255 -28 0 71 158 -29 220 94 147 -30 147 212 255 -31 0 76 255 -32 0 66 80 -33 57 167 106 -34 238 112 254 -35 0 0 100 -36 171 245 204 -37 161 146 255 -38 164 255 115 -39 255 206 113 -40 71 0 21 -41 212 173 197 -42 251 118 111 -43 171 188 0 -44 117 0 215 -45 166 0 154 -46 0 115 254 -47 165 93 174 -48 98 132 2 -49 0 121 168 -50 0 255 131 -51 86 53 0 -52 159 0 63 -53 66 45 66 -54 255 242 187 -55 0 93 67 -56 252 255 124 -57 159 191 186 -58 167 84 19 -59 74 39 108 -60 0 16 166 -61 145 78 109 -62 207 149 0 -63 195 187 255 -64 253 68 64 -65 66 78 32 -66 106 1 0 -67 181 131 84 -68 132 233 147 -69 96 217 0 -70 255 111 211 -71 102 75 63 -72 254 100 0 -73 228 3 127 -74 17 199 174 -75 210 129 139 -76 91 118 124 -77 32 59 106 -78 180 84 255 -79 226 8 210 -80 0 1 20 -81 93 132 68 -82 166 250 255 -83 97 123 201 -84 98 0 122 -85 126 190 58 -86 0 60 183 -87 255 253 0 -88 7 197 226 -89 180 167 57 -90 148 186 138 -91 204 187 160 -92 55 0 49 -93 0 40 1 -94 150 122 129 -95 39 136 38 -96 206 130 180 -97 150 164 196 -98 180 32 128 -99 110 86 180 -100 147 0 185 -101 199 48 61 -102 115 102 255 -103 15 187 253 -104 172 164 100 -105 182 117 250 -106 216 220 254 -107 87 141 113 -108 216 85 34 -109 0 196 103 -110 243 165 105 -111 216 255 182 -112 1 24 219 -113 52 66 54 -114 255 154 0 -115 87 95 1 -116 198 241 79 -117 255 95 133 -118 123 172 240 -119 120 100 49 -120 162 133 204 -121 105 255 220 -122 198 82 100 -123 121 26 64 -124 0 238 70 -125 231 207 69 -126 217 128 233 -127 255 211 209 -128 209 255 141 -129 36 0 3 -130 87 163 193 -131 211 231 201 -132 203 111 79 -133 62 24 0 -134 0 117 223 -135 112 176 88 -136 209 24 0 -137 0 30 107 -138 105 200 197 -139 255 203 255 -140 233 194 137 -141 191 129 46 -142 69 42 145 -143 171 76 194 -144 14 117 61 -145 0 30 25 -146 118 73 127 -147 255 169 200 -148 94 55 217 -149 238 230 138 -150 159 54 33 -151 80 0 148 -152 189 144 128 -153 0 109 126 -154 88 223 96 -155 71 80 103 -156 1 93 159 -157 99 48 60 -158 2 206 148 -159 139 83 37 -160 171 0 255 -161 141 42 135 -162 85 83 148 -163 150 255 0 -164 0 152 123 -165 255 138 203 -166 222 69 200 -167 107 109 230 -168 30 0 68 -169 173 76 138 -170 255 134 161 -171 0 35 60 -172 138 205 0 -173 111 202 157 -174 225 75 253 -175 255 176 77 -176 229 232 57 -177 114 16 255 -178 111 82 101 -179 134 137 48 -180 99 38 80 -181 105 38 32 -182 200 110 0 -183 209 164 255 -184 198 210 86 -185 79 103 77 -186 174 165 166 -187 170 45 101 -188 199 81 175 -189 255 89 172 -190 146 102 78 -191 102 134 184 -192 111 152 255 -193 92 255 159 -194 172 137 178 -195 210 34 98 -196 199 207 147 -197 255 185 30 -198 250 148 141 -199 49 34 78 -200 254 81 97 -201 254 141 100 -202 68 54 23 -203 201 162 84 -204 199 232 240 -205 68 152 0 -206 147 172 58 -207 22 75 28 -208 8 84 121 -209 116 45 0 -210 104 60 255 -211 64 41 38 -212 164 113 215 -213 207 0 155 -214 118 1 35 -215 83 0 88 -216 0 82 232 -217 43 92 87 -218 160 217 146 -219 176 26 229 -220 29 3 36 -221 122 58 159 -222 214 209 207 -223 160 100 105 -224 106 157 160 -225 153 219 113 -226 192 56 207 -227 125 255 89 -228 149 0 34 -229 213 162 223 -230 22 131 204 -231 166 249 69 -232 109 105 97 -233 86 188 78 -234 255 109 81 -235 255 3 248 -236 255 0 73 -237 202 0 35 -238 67 109 18 -239 234 170 173 -240 191 165 0 -241 38 44 51 -242 85 185 2 -243 121 182 158 -244 254 236 212 -245 139 165 89 -246 141 254 193 -247 0 60 43 -248 63 17 40 -249 255 221 246 -250 17 26 146 -251 154 66 84 -252 149 157 238 -253 126 130 72 -254 58 6 101 -255 189 117 101 diff --git a/imagepy/data/luts/glasbey_on_dark.lut b/imagepy/data/luts/glasbey_on_dark.lut deleted file mode 100644 index 130d80ec..00000000 --- a/imagepy/data/luts/glasbey_on_dark.lut +++ /dev/null @@ -1,256 +0,0 @@ - 0 0 0 - 255 255 0 - 255 25 255 - 0 147 147 - 156 64 0 - 88 0 199 - 241 235 255 - 20 75 0 - 0 188 1 - 255 159 98 - 145 144 255 - 93 0 63 - 0 255 214 - 255 0 95 - 120 100 119 - 0 73 96 - 140 136 74 - 82 207 255 - 207 152 186 - 157 0 177 - 191 211 152 - 0 107 210 - 163 51 91 - 88 70 43 - 107 255 102 - 156 171 174 - 0 132 65 - 92 16 0 - 0 0 143 - 240 81 0 - 205 170 0 - 182 114 100 - 76 190 141 - 148 60 255 - 82 54 100 - 73 101 93 - 110 132 165 - 175 105 192 - 208 184 255 - 255 211 190 - 212 255 237 - 255 123 137 - 96 98 0 - 222 0 157 - 0 159 249 - 197 120 1 - 0 1 255 - 197 1 29 - 190 163 136 - 98 90 157 - 255 144 255 - 160 205 0 - 255 215 97 - 107 59 73 - 101 144 0 - 124 131 125 - 255 255 195 - 149 215 214 - 18 112 141 - 255 195 239 - 195 102 146 - 140 0 30 - 138 177 93 - 135 98 59 - 183 209 245 - 163 153 193 - 16 189 193 - 255 102 194 - 48 57 118 - 77 82 99 - 205 192 201 - 94 63 255 - 197 135 255 - 195 0 255 - 0 80 57 - 139 3 110 - 208 252 136 - 127 229 159 - 151 78 136 - 110 0 159 - 130 170 209 - 100 150 109 - 158 132 145 - 204 76 87 - 66 0 124 - 255 172 180 - 136 119 190 - 144 86 89 - 109 52 0 - 93 116 74 - 0 246 255 - 96 116 255 - 84 0 98 - 0 169 83 - 137 79 175 - 219 182 107 - 197 213 203 - 16 144 184 - 230 121 85 - 65 85 45 - 42 106 0 - 104 96 87 - 255 165 4 - 2 220 95 - 151 183 151 - 147 109 0 - 247 0 29 - 195 50 190 - 2 85 148 - 192 93 39 - 0 125 102 - 156 149 0 - 248 125 0 - 255 252 243 - 105 165 154 - 184 245 0 - 132 56 45 - 214 144 141 - 209 0 98 - 197 241 255 - 222 214 3 - 163 180 255 - 90 124 130 - 105 26 42 - 186 150 73 - 114 79 0 - 0 216 189 - 120 53 126 - 157 133 109 - 215 124 206 - 254 85 81 - 0 96 100 - 238 85 137 - 23 176 218 - 187 255 192 - 126 0 220 - 255 150 208 - 73 65 0 - 216 90 255 - 176 33 135 - 163 110 255 - 64 71 177 - 49 0 186 - 186 196 86 - 14 103 55 - 85 105 136 - 137 0 68 - 0 158 125 - 125 174 0 - 209 196 168 - 140 143 156 - 158 224 110 - 86 73 78 - 154 255 245 - 176 162 168 - 171 62 51 - 103 153 169 - 146 116 156 - 106 80 124 - 77 131 204 - 179 182 207 - 160 25 0 - 143 154 123 - 170 117 64 - 91 59 145 - 91 227 0 - 205 156 223 - 235 177 149 - 0 140 1 - 204 57 2 - 239 218 157 - 175 176 117 - 138 111 109 - 156 104 129 - 97 42 85 - 167 229 200 - 129 186 199 - 254 219 232 - 120 123 19 - 99 104 111 - 101 94 51 - 217 85 183 - 200 139 98 - 115 97 214 - 73 80 70 - 227 126 166 - 222 203 238 - 132 57 100 - 213 110 121 - 158 3 224 - 175 0 58 - 117 76 61 - 89 127 109 - 139 229 255 - 125 33 0 - 123 117 89 - 133 147 204 - 179 134 184 - 116 164 254 - 126 193 175 - 162 91 3 - 26 70 255 - 255 0 202 - 215 236 199 - 255 248 115 - 115 108 145 - 0 255 150 - 114 201 120 - 80 124 40 - 160 88 60 - 68 81 128 - 206 213 225 - 194 65 126 - 57 80 83 - 225 149 58 - 240 178 255 - 176 158 238 - 255 0 148 - 116 151 74 - 214 171 175 - 101 43 29 - 253 150 133 - 0 215 232 - 243 190 0 - 164 87 221 - 9 120 123 - 54 91 112 - 176 88 108 - 102 114 176 - 216 222 120 - 191 129 149 - 176 178 166 - 127 190 255 - 115 0 117 - 0 60 158 - 229 220 214 - 100 67 195 - 113 200 65 - 74 0 250 - 0 220 157 - 235 249 254 - 170 177 2 - 199 174 206 - 131 74 29 - 255 104 237 - 0 181 158 - 116 77 103 - 171 131 6 - 218 90 65 - 173 197 203 - 64 85 0 - 255 194 132 - 220 27 68 - 168 131 209 - 125 121 131 - 17 101 82 diff --git a/imagepy/data/luts/glow.lut b/imagepy/data/luts/glow.lut deleted file mode 100644 index 8aa057b5..00000000 --- a/imagepy/data/luts/glow.lut +++ /dev/null @@ -1,257 +0,0 @@ -Index Red Green Blue -0 0 138 0 -1 1 0 0 -2 1 0 0 -3 2 0 0 -4 2 0 0 -5 3 0 0 -6 4 0 0 -7 6 0 0 -8 8 0 0 -9 11 0 0 -10 13 0 0 -11 15 0 0 -12 18 0 0 -13 21 1 1 -14 23 1 1 -15 24 1 1 -16 27 1 1 -17 30 1 1 -18 31 1 1 -19 33 1 1 -20 36 1 1 -21 37 1 1 -22 40 2 1 -23 42 2 3 -24 45 2 4 -25 46 2 4 -26 49 2 4 -27 51 2 4 -28 53 2 4 -29 56 2 4 -30 58 3 3 -31 60 3 3 -32 62 3 3 -33 65 3 3 -34 68 4 4 -35 70 5 6 -36 72 5 6 -37 74 5 6 -38 78 5 6 -39 80 6 6 -40 82 6 6 -41 84 6 6 -42 86 7 6 -43 89 7 6 -44 91 8 6 -45 93 9 6 -46 96 9 6 -47 98 9 6 -48 100 9 6 -49 102 10 6 -50 104 10 6 -51 106 11 6 -52 108 12 6 -53 110 13 6 -54 113 14 6 -55 115 14 6 -56 117 14 6 -57 119 14 6 -58 122 15 6 -59 125 16 6 -60 127 16 5 -61 129 17 5 -62 132 18 7 -63 135 19 9 -64 135 19 10 -65 137 19 10 -66 140 20 10 -67 141 21 10 -68 142 22 10 -69 145 23 10 -70 148 24 10 -71 149 24 10 -72 152 25 10 -73 154 27 10 -74 156 28 10 -75 157 28 10 -76 158 28 10 -77 160 29 10 -78 162 31 10 -79 164 32 10 -80 166 32 10 -81 168 33 9 -82 170 35 9 -83 171 36 9 -84 173 36 9 -85 174 37 9 -86 176 39 9 -87 178 40 9 -88 179 40 9 -89 180 41 9 -90 182 42 9 -91 183 43 9 -92 185 45 9 -93 186 46 9 -94 189 47 9 -95 192 48 8 -96 193 50 8 -97 193 51 8 -98 194 52 8 -99 195 54 8 -100 195 55 8 -101 196 56 8 -102 198 57 8 -103 199 59 8 -104 201 62 8 -105 203 63 8 -106 204 65 8 -107 204 66 8 -108 205 68 8 -109 206 70 8 -110 207 71 8 -111 209 73 8 -112 211 74 7 -113 211 75 7 -114 211 77 7 -115 211 79 7 -116 213 81 7 -117 215 83 7 -118 216 85 7 -119 216 87 7 -120 216 89 7 -121 216 92 7 -122 218 93 7 -123 219 96 7 -124 219 98 7 -125 219 99 7 -126 220 100 7 -127 222 103 6 -128 223 105 6 -129 223 107 6 -130 223 109 6 -131 223 111 6 -132 224 113 6 -133 224 116 6 -134 226 117 7 -135 227 119 10 -136 227 121 12 -137 227 123 12 -138 227 125 12 -139 228 126 12 -140 229 128 12 -141 231 130 12 -142 231 132 13 -143 231 135 15 -144 231 137 17 -145 231 139 18 -146 231 140 18 -147 231 142 19 -148 232 144 20 -149 233 145 22 -150 234 147 23 -151 234 148 23 -152 234 150 24 -153 234 152 26 -154 234 154 27 -155 234 156 27 -156 235 158 28 -157 237 160 30 -158 238 161 32 -159 238 162 33 -160 238 164 34 -161 238 166 35 -162 238 168 37 -163 238 171 39 -164 238 172 40 -165 238 172 41 -166 239 174 43 -167 240 176 45 -168 242 177 46 -169 242 179 48 -170 242 180 50 -171 242 182 52 -172 242 183 54 -173 242 185 55 -174 242 187 57 -175 242 188 58 -176 242 189 61 -177 243 191 63 -178 245 192 65 -179 246 192 67 -180 246 192 70 -181 246 196 72 -182 246 200 73 -183 245 201 76 -184 245 201 79 -185 245 202 80 -186 245 203 83 -187 245 204 86 -188 245 206 88 -189 245 208 89 -190 247 210 92 -191 248 212 95 -192 249 213 96 -193 249 213 99 -194 249 214 102 -195 249 215 104 -196 249 217 107 -197 249 218 108 -198 249 220 110 -199 249 221 113 -200 249 222 116 -201 249 223 120 -202 249 224 121 -203 249 225 123 -204 249 226 125 -205 249 226 127 -206 249 227 129 -207 249 228 132 -208 249 229 135 -209 250 231 137 -210 252 232 140 -211 253 233 143 -212 253 234 146 -213 253 235 149 -214 253 236 150 -215 253 237 153 -216 253 238 155 -217 253 239 158 -218 253 239 160 -219 253 239 162 -220 253 239 165 -221 253 241 168 -222 253 242 171 -223 253 243 173 -224 253 243 175 -225 253 243 178 -226 253 244 180 -227 253 245 183 -228 253 247 186 -229 253 247 188 -230 253 247 191 -231 253 247 192 -232 253 247 195 -233 253 247 198 -234 253 248 201 -235 253 249 203 -236 253 250 206 -237 253 251 209 -238 253 251 210 -239 253 251 213 -240 253 252 215 -241 253 252 218 -242 253 252 221 -243 253 252 223 -244 253 252 225 -245 253 252 228 -246 253 252 231 -247 253 252 233 -248 253 252 236 -249 253 252 238 -250 253 252 241 -251 253 252 244 -252 253 252 246 -253 253 253 250 -254 253 253 252 -255 0 0 255 diff --git a/imagepy/data/luts/phase.lut b/imagepy/data/luts/phase.lut deleted file mode 100644 index 142be6ac..00000000 --- a/imagepy/data/luts/phase.lut +++ /dev/null @@ -1,257 +0,0 @@ - 0 0 252 - 0 0 252 - 0 0 252 - 4 4 252 - 4 4 252 - 4 4 252 - 8 8 252 - 8 8 252 - 12 12 252 - 12 12 252 - 12 12 248 - 16 16 248 - 16 16 248 - 20 20 248 - 20 20 248 - 20 20 248 - 24 24 248 - 24 24 248 - 28 28 248 - 28 28 248 - 28 28 244 - 32 32 244 - 32 32 244 - 36 36 244 - 36 36 244 - 36 36 244 - 40 40 244 - 40 40 244 - 44 44 244 - 44 44 244 - 44 44 240 - 48 48 240 - 48 48 240 - 52 52 240 - 52 52 240 - 52 52 240 - 56 56 240 - 56 56 240 - 60 60 240 - 60 60 236 - 60 60 236 - 64 64 236 - 64 64 236 - 68 68 236 - 68 68 236 - 68 68 236 - 72 72 236 - 72 72 236 - 76 76 236 - 76 76 232 - 76 76 232 - 80 80 232 - 80 80 232 - 84 84 232 - 84 84 232 - 84 84 232 - 88 88 232 - 88 88 232 - 92 92 232 - 92 92 228 - 92 92 228 - 96 96 228 - 96 96 228 - 96 96 228 -100 100 228 -100 100 228 -104 104 228 -104 104 228 -104 104 224 -108 108 224 -108 108 224 -112 112 224 -112 112 224 -112 112 224 -116 116 224 -116 116 224 -120 120 224 -120 120 224 -120 120 220 -124 124 220 -124 124 220 -128 128 220 -128 128 220 -128 128 220 -132 132 220 -132 132 220 -136 136 220 -136 136 220 -136 136 216 -140 140 216 -140 140 216 -144 144 216 -144 144 216 -144 144 216 -148 148 216 -148 148 216 -152 152 216 -152 152 212 -152 152 212 -156 156 212 -156 156 212 -160 160 212 -160 160 212 -160 160 212 -164 164 212 -164 164 212 -168 168 212 -168 168 208 -168 168 208 -172 172 208 -172 172 208 -176 176 208 -176 176 208 -176 176 208 -180 180 208 -180 180 208 -184 184 208 -184 184 204 -184 184 204 -188 188 204 -188 188 204 -192 192 204 -192 192 204 -192 192 204 -196 196 204 -196 196 204 -200 200 200 -200 200 200 -200 200 200 -200 196 196 -200 196 196 -200 196 196 -200 192 192 -200 192 192 -200 188 188 -200 188 188 -204 188 188 -204 184 184 -204 184 184 -204 180 180 -204 180 180 -204 180 180 -204 176 176 -204 176 176 -204 172 172 -204 172 172 -208 172 172 -208 168 168 -208 168 168 -208 168 168 -208 164 164 -208 164 164 -208 160 160 -208 160 160 -208 160 160 -208 156 156 -212 156 156 -212 152 152 -212 152 152 -212 152 152 -212 148 148 -212 148 148 -212 144 144 -212 144 144 -212 144 144 -212 140 140 -216 140 140 -216 136 136 -216 136 136 -216 136 136 -216 132 132 -216 132 132 -216 132 132 -216 128 128 -216 128 128 -216 124 124 -220 124 124 -220 124 124 -220 120 120 -220 120 120 -220 116 116 -220 116 116 -220 116 116 -220 112 112 -220 112 112 -220 108 108 -224 108 108 -224 108 108 -224 104 104 -224 104 104 -224 100 100 -224 100 100 -224 100 100 -224 96 96 -224 96 96 -228 96 96 -228 92 92 -228 92 92 -228 88 88 -228 88 88 -228 88 88 -228 84 84 -228 84 84 -228 80 80 -228 80 80 -232 80 80 -232 76 76 -232 76 76 -232 72 72 -232 72 72 -232 72 72 -232 68 68 -232 68 68 -232 68 68 -232 64 64 -236 64 64 -236 60 60 -236 60 60 -236 60 60 -236 56 56 -236 56 56 -236 52 52 -236 52 52 -236 52 52 -236 48 48 -240 48 48 -240 44 44 -240 44 44 -240 44 44 -240 40 40 -240 40 40 -240 36 36 -240 36 36 -240 36 36 -240 32 32 -244 32 32 -244 32 32 -244 28 28 -244 28 28 -244 24 24 -244 24 24 -244 24 24 -244 20 20 -244 20 20 -244 16 16 -248 16 16 -248 16 16 -248 12 12 -248 12 12 -248 8 8 -248 8 8 -248 8 8 -248 4 4 -248 4 4 -252 0 0 -252 0 0 - \ No newline at end of file diff --git a/imagepy/data/luts/sepia.lut b/imagepy/data/luts/sepia.lut deleted file mode 100644 index 75b22946..00000000 --- a/imagepy/data/luts/sepia.lut +++ /dev/null @@ -1,256 +0,0 @@ -0 0 0 -43 28 4 -57 35 5 -65 35 6 -66 36 7 -67 37 8 -68 38 9 -69 39 10 -70 40 11 -71 41 12 -72 42 13 -73 43 14 -74 44 15 -75 45 16 -76 46 17 -77 47 18 -78 48 19 -79 49 20 -80 50 21 -81 51 22 -82 52 23 -83 53 24 -83 53 24 -84 54 25 -85 55 26 -86 56 27 -87 57 28 -88 58 29 -89 59 30 -90 60 31 -91 61 32 -92 62 33 -93 63 34 -94 64 35 -95 65 36 -96 66 37 -97 67 38 -98 68 39 -99 69 40 -100 70 41 -101 71 42 -102 72 43 -103 73 44 -104 74 45 -105 75 46 -106 76 47 -107 77 48 -107 77 48 -108 78 49 -109 79 50 -110 80 51 -111 81 52 -112 82 53 -113 83 54 -114 84 55 -115 85 56 -116 86 57 -117 87 58 -118 88 59 -119 89 60 -120 90 61 -121 91 62 -122 92 63 -123 93 64 -124 94 65 -125 95 66 -125 95 66 -126 96 67 -127 97 68 -128 98 69 -129 99 70 -130 100 71 -131 101 72 -132 102 73 -133 103 74 -134 104 75 -135 105 76 -136 106 77 -137 107 78 -138 108 79 -139 109 80 -140 110 81 -141 111 82 -142 112 83 -143 113 84 -144 114 85 -145 115 86 -146 116 87 -147 117 88 -148 118 89 -149 119 90 -150 120 91 -151 121 92 -152 122 93 -153 123 94 -154 124 95 -155 125 96 -156 126 97 -157 127 98 -158 128 99 -159 129 100 -160 130 101 -161 131 102 -162 132 103 -163 133 104 -164 134 105 -165 135 106 -166 136 107 -167 137 108 -168 138 109 -169 139 110 -170 140 111 -171 141 112 -172 142 113 -173 143 114 -174 144 115 -175 145 116 -176 146 117 -177 147 118 -178 148 119 -179 149 120 -180 150 121 -181 151 122 -182 152 123 -183 153 124 -184 154 125 -185 155 126 -186 156 127 -187 157 128 -187 157 128 -188 158 129 -189 159 130 -190 160 131 -191 161 132 -192 162 133 -193 163 134 -194 164 135 -195 165 136 -196 166 137 -197 167 138 -198 168 139 -199 169 140 -200 170 141 -201 171 142 -202 172 143 -203 173 144 -204 174 145 -205 175 146 -206 176 147 -207 177 148 -208 178 149 -209 179 150 -210 180 151 -211 181 152 -212 182 153 -213 183 154 -214 184 155 -215 185 156 -216 186 157 -217 187 158 -218 188 159 -219 189 160 -220 190 161 -221 191 162 -222 192 163 -223 193 164 -224 194 165 -225 195 166 -226 196 167 -227 197 168 -228 198 169 -229 199 170 -230 200 171 -231 201 172 -232 202 173 -233 203 174 -234 204 175 -235 205 176 -236 206 177 -237 207 178 -238 208 179 -239 209 180 -240 210 181 -241 211 182 -242 212 183 -243 213 184 -244 214 185 -245 215 186 -246 216 187 -247 217 188 -248 218 189 -249 219 190 -250 220 191 -251 221 192 -252 222 193 -253 223 194 -254 224 195 -255 225 196 -255 225 196 -255 225 196 -255 225 196 -255 225 196 -255 225 197 -255 225 197 -255 225 197 -255 225 197 -255 226 198 -255 226 198 -255 226 198 -255 226 198 -255 226 198 -255 226 199 -255 226 199 -255 226 199 -255 226 199 -255 226 199 -255 227 200 -255 227 200 -255 227 200 -255 227 200 -255 227 200 -255 227 201 -255 227 201 -255 227 201 -255 227 201 -255 227 201 -255 228 202 -255 228 202 -255 228 202 -255 228 202 -255 228 202 -255 228 202 -255 228 203 -255 228 203 -255 228 203 -255 228 203 -255 228 203 -255 229 204 -255 229 204 -255 229 204 -255 229 204 -255 229 204 -255 229 204 -255 229 205 -255 229 205 -255 229 205 -255 229 205 -255 229 205 -255 230 205 -255 230 205 -255 231 205 -255 231 209 -255 233 214 -255 233 217 -255 242 233 -255 255 255 diff --git a/imagepy/data/luts/thal.lut b/imagepy/data/luts/thal.lut deleted file mode 100644 index bdf7214dd9eec7fc4500fa30cde2547040f922bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 800 zcmeZt_V-~}#K-^y{}~w=88{jKfgwl$1Q_WJSUGtGMI~hwRW)@DO)YI5Ts(XNLLy=k zQZjN1N-Am^S~_|rOqnrf!IBkgHf-6kXaC{jr_W!$e*6C8=da&?{{H*lukWAVzj^WW z;oV!;E?qc#@;Ev;dG^AkYq#z`eEQ-IQ2noe1OUn2r_}xL@1NhlVZnrkf`kAELq$Ql z+m99=oScG^ikgO&4jus!2^j?y1jC Date: Sun, 3 Mar 2019 12:57:52 +0800 Subject: [PATCH 112/343] nothing --- imagepy/core/util/fileio.py | 77 ------ imagepy/data/luts/5_ramps.lut | 256 ----------------- imagepy/data/luts/Cyan Hot.lut | Bin 768 -> 0 bytes imagepy/data/luts/Green Fire Blue.lut | Bin 768 -> 0 bytes imagepy/data/luts/Jet.lut | 256 ----------------- imagepy/data/luts/Magenta Hot.lut | Bin 768 -> 0 bytes imagepy/data/luts/NanoJ-Orange.lut | Bin 768 -> 0 bytes imagepy/data/luts/Orange Hot.lut | Bin 768 -> 0 bytes imagepy/data/luts/Rainbow RGB.lut | Bin 768 -> 0 bytes imagepy/data/luts/Red Hot.lut | Bin 768 -> 0 bytes imagepy/data/luts/SQUIRREL-Errors.lut | 256 ----------------- imagepy/data/luts/SQUIRREL-FRC.lut | 2 - imagepy/data/luts/Thermal.lut | Bin 768 -> 0 bytes imagepy/data/luts/Viridis.lut | 1 - imagepy/data/luts/Yellow Hot.lut | Bin 768 -> 0 bytes imagepy/data/luts/ametrine.lut | Bin 768 -> 0 bytes imagepy/data/luts/blue_orange_icb.lut | Bin 800 -> 0 bytes imagepy/data/luts/cool.lut | Bin 800 -> 0 bytes imagepy/data/luts/edges.lut | Bin 800 -> 0 bytes imagepy/data/luts/gem.lut | Bin 800 -> 0 bytes imagepy/data/luts/glasbey.lut | 257 ------------------ imagepy/data/luts/glasbey_inverted.lut | Bin 768 -> 0 bytes imagepy/data/luts/glasbey_on_dark.lut | 256 ----------------- imagepy/data/luts/glow.lut | 257 ------------------ imagepy/data/luts/isolum.lut | Bin 768 -> 0 bytes imagepy/data/luts/morgenstemning.lut | Bin 768 -> 0 bytes imagepy/data/luts/mpl-inferno.lut | Bin 768 -> 0 bytes imagepy/data/luts/mpl-magma.lut | Bin 768 -> 0 bytes imagepy/data/luts/mpl-plasma.lut | Bin 768 -> 0 bytes imagepy/data/luts/mpl-viridis.lut | 1 - imagepy/data/luts/phase.lut | 257 ------------------ imagepy/data/luts/physics.lut | Bin 768 -> 0 bytes imagepy/data/luts/royal.lut | Bin 800 -> 0 bytes imagepy/data/luts/sepia.lut | 256 ----------------- imagepy/data/luts/smart.lut | Bin 800 -> 0 bytes imagepy/data/luts/thal.lut | Bin 800 -> 0 bytes imagepy/data/luts/thallium.lut | Bin 800 -> 0 bytes imagepy/data/luts/unionjack.lut | 257 ------------------ imagepy/menus/File/TIF/tif3d_plgs.py | 2 +- .../menus/Plugins/StackReg/stackreg_plgs.py | 2 +- imagepy/menus/Process/Segment/graph_plgs.py | 1 + 41 files changed, 3 insertions(+), 2391 deletions(-) delete mode 100644 imagepy/core/util/fileio.py delete mode 100644 imagepy/data/luts/5_ramps.lut delete mode 100644 imagepy/data/luts/Cyan Hot.lut delete mode 100644 imagepy/data/luts/Green Fire Blue.lut delete mode 100644 imagepy/data/luts/Jet.lut delete mode 100644 imagepy/data/luts/Magenta Hot.lut delete mode 100644 imagepy/data/luts/NanoJ-Orange.lut delete mode 100644 imagepy/data/luts/Orange Hot.lut delete mode 100644 imagepy/data/luts/Rainbow RGB.lut delete mode 100644 imagepy/data/luts/Red Hot.lut delete mode 100644 imagepy/data/luts/SQUIRREL-Errors.lut delete mode 100644 imagepy/data/luts/SQUIRREL-FRC.lut delete mode 100644 imagepy/data/luts/Thermal.lut delete mode 100644 imagepy/data/luts/Viridis.lut delete mode 100644 imagepy/data/luts/Yellow Hot.lut delete mode 100644 imagepy/data/luts/ametrine.lut delete mode 100644 imagepy/data/luts/blue_orange_icb.lut delete mode 100644 imagepy/data/luts/cool.lut delete mode 100644 imagepy/data/luts/edges.lut delete mode 100644 imagepy/data/luts/gem.lut delete mode 100644 imagepy/data/luts/glasbey.lut delete mode 100644 imagepy/data/luts/glasbey_inverted.lut delete mode 100644 imagepy/data/luts/glasbey_on_dark.lut delete mode 100644 imagepy/data/luts/glow.lut delete mode 100644 imagepy/data/luts/isolum.lut delete mode 100644 imagepy/data/luts/morgenstemning.lut delete mode 100644 imagepy/data/luts/mpl-inferno.lut delete mode 100644 imagepy/data/luts/mpl-magma.lut delete mode 100644 imagepy/data/luts/mpl-plasma.lut delete mode 100644 imagepy/data/luts/mpl-viridis.lut delete mode 100644 imagepy/data/luts/phase.lut delete mode 100644 imagepy/data/luts/physics.lut delete mode 100644 imagepy/data/luts/royal.lut delete mode 100644 imagepy/data/luts/sepia.lut delete mode 100644 imagepy/data/luts/smart.lut delete mode 100644 imagepy/data/luts/thal.lut delete mode 100644 imagepy/data/luts/thallium.lut delete mode 100644 imagepy/data/luts/unionjack.lut diff --git a/imagepy/core/util/fileio.py b/imagepy/core/util/fileio.py deleted file mode 100644 index 98305677..00000000 --- a/imagepy/core/util/fileio.py +++ /dev/null @@ -1,77 +0,0 @@ -import os -from ..manager import ViewerManager, ConfigManager -from ..manager import ReaderManager, WriterManager -from ... import IPy, root_dir -from ..engine import Free, Simple, Macros -import numpy as np - -def show_img(img, title): - if isinstance(img, list): - return IPy.show_img(img, title) - if img.dtype==np.uint8 and img.ndim==3 and img.shape[2]==4: - img = img[:,:,:3].copy() - IPy.show_img([img], title) - -ViewerManager.add('img', show_img) -ViewerManager.add('imgs', IPy.show_img) -recent = ConfigManager.get('recent') -if recent==None : recent = [] - -def f(path): - return Macros(path, ["Open>{'path':%s}"%repr(path)]) - -rlist = [f(i) for i in recent] - -def add_recent(path): - global recent, rlist - if path in recent: - idx = recent.index(path) - recent.insert(0, recent.pop(idx)) - rlist.insert(0, rlist.pop(idx)) - else: - recent.insert(0, path) - rlist.insert(0, f(path)) - if len(recent)>=5: - recent.pop(-1) - rlist.pop(-1) - - ConfigManager.set('recent', recent) - IPy.curapp.reload_plugins() - -class Reader(Free): - para = {'path':''} - - def show(self): - filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in self.filt]) - return IPy.getpath('Open..', filt, 'open', self.para) - - #process - def run(self, para = None): - add_recent(para['path']) - - fp, fn = os.path.split(para['path']) - fn, fe = os.path.splitext(fn) - read = ReaderManager.get(fe[1:], None) - view = ViewerManager.get(fe[1:]) - - #group, read = (True, read[0]) if isinstance(read, tuple) else (False, read) - obj = read(para['path']) - # if not group: img = [img] - view(obj, fn) - -class Writer(Simple): - note = ['all'] - para={'path':root_dir} - - def show(self): - filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in self.filt]) - return IPy.getpath('Save..', filt, 'save', self.para) - - #process - def run(self, ips, imgs, para = None): - fp, fn = os.path.split(para['path']) - fn, fe = os.path.splitext(fn) - write = WriterManager.get(fe[1:], 'img') - write2 = write or WriterManager.get(fe[1:], 'imgs') - print(write2, write) - write2(para['path'], imgs if write is None else ips.img) \ No newline at end of file diff --git a/imagepy/data/luts/5_ramps.lut b/imagepy/data/luts/5_ramps.lut deleted file mode 100644 index 6a9c6a56..00000000 --- a/imagepy/data/luts/5_ramps.lut +++ /dev/null @@ -1,256 +0,0 @@ -0 0 1 -0 0 5 -0 0 10 -0 0 15 -0 0 20 -0 0 25 -0 0 30 -0 0 35 -0 0 40 -0 0 45 -0 0 50 -0 0 55 -0 0 60 -0 0 65 -0 0 70 -0 0 75 -0 0 80 -0 0 85 -0 0 90 -0 0 95 -0 0 100 -0 0 105 -0 0 110 -0 0 115 -0 0 120 -0 0 125 -0 0 130 -0 0 135 -0 0 140 -0 0 145 -0 0 150 -0 0 155 -0 0 160 -0 0 165 -0 0 170 -0 0 175 -0 0 180 -0 0 185 -0 0 190 -0 0 195 -0 0 200 -0 0 205 -0 0 210 -0 0 215 -0 0 220 -0 0 225 -0 0 230 -0 0 235 -0 0 240 -0 0 245 -0 0 250 -0 1 0 -0 5 0 -0 10 0 -0 15 0 -0 20 0 -0 25 0 -0 30 0 -0 35 0 -0 40 0 -0 45 0 -0 50 0 -0 55 0 -0 60 0 -0 65 0 -0 70 0 -0 75 0 -0 80 0 -0 85 0 -0 90 0 -0 95 0 -0 100 0 -0 105 0 -0 110 0 -0 115 0 -0 120 0 -0 125 0 -0 130 0 -0 135 0 -0 140 0 -0 145 0 -0 150 0 -0 155 0 -0 160 0 -0 165 0 -0 170 0 -0 175 0 -0 180 0 -0 185 0 -0 190 0 -0 195 0 -0 200 0 -0 205 0 -0 210 0 -0 215 0 -0 220 0 -0 225 0 -0 230 0 -0 235 0 -0 240 0 -0 245 0 -0 250 0 -1 1 0 -5 5 0 -10 10 0 -15 15 0 -20 20 0 -25 25 0 -30 30 0 -35 35 0 -40 40 0 -45 45 0 -50 50 0 -55 55 0 -60 60 0 -65 65 0 -70 70 0 -75 75 0 -80 80 0 -85 85 0 -90 90 0 -95 95 0 -100 100 0 -105 105 0 -110 110 0 -115 115 0 -120 120 0 -125 125 0 -130 130 0 -135 135 0 -140 140 0 -145 145 0 -150 150 0 -155 155 0 -160 160 0 -165 165 0 -170 170 0 -175 175 0 -180 180 0 -185 185 0 -190 190 0 -195 195 0 -200 200 0 -205 205 0 -210 210 0 -215 215 0 -220 220 0 -225 225 0 -230 230 0 -235 235 0 -240 240 0 -245 245 0 -250 250 0 -1 0 0 -5 0 0 -10 0 0 -15 0 0 -20 0 0 -25 0 0 -30 0 0 -35 0 0 -40 0 0 -45 0 0 -50 0 0 -55 0 0 -60 0 0 -65 0 0 -70 0 0 -75 0 0 -80 0 0 -85 0 0 -90 0 0 -95 0 0 -100 0 0 -105 0 0 -110 0 0 -115 0 0 -120 0 0 -125 0 0 -130 0 0 -135 0 0 -140 0 0 -145 0 0 -150 0 0 -155 0 0 -160 0 0 -165 0 0 -170 0 0 -175 0 0 -180 0 0 -185 0 0 -190 0 0 -195 0 0 -200 0 0 -205 0 0 -210 0 0 -215 0 0 -220 0 0 -225 0 0 -230 0 0 -235 0 0 -240 0 0 -245 0 0 -250 0 0 -1 1 1 -5 5 5 -10 10 10 -15 15 15 -20 20 20 -25 25 25 -30 30 30 -35 35 35 -40 40 40 -45 45 45 -50 50 50 -55 55 55 -60 60 60 -65 65 65 -70 70 70 -75 75 75 -80 80 80 -85 85 85 -90 90 90 -95 95 95 -100 100 100 -105 105 105 -110 110 110 -115 115 115 -120 120 120 -125 125 125 -130 130 130 -135 135 135 -140 140 140 -145 145 145 -150 150 150 -155 155 155 -160 160 160 -165 165 165 -170 170 170 -175 175 175 -180 180 180 -185 185 185 -190 190 190 -195 195 195 -200 200 200 -205 205 205 -210 210 210 -215 215 215 -220 220 220 -225 225 225 -230 230 230 -235 235 235 -240 240 240 -245 245 245 -250 250 250 -255 255 255 diff --git a/imagepy/data/luts/Cyan Hot.lut b/imagepy/data/luts/Cyan Hot.lut deleted file mode 100644 index e4cd70a5b96cf134a07440765dec52db7526f90f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 768 zcmZQz7&3r~m4lm4P()lxRzX=!Q%B#()WX`%(Z$`%Hy}7HGA2GLH6uH(u%x`IwxPML zv!{R3)ETqqEnKpE)!Gf4x9!}s|IpDBr_Wuya{bob2alh7vJ&z-km z;o>FBmakm3X5IRYo3?D-zGK(!z55RwI(+oliIb<#oV#%G@|A1XZ{E6d@BYI_Po6%1 f@#^*4_a8oe{`&35&)@%P0~lx!sYo}FJ@UR~eZ-rYZW`t13Om#<#G zdHe4DhmW5=fBE|D`;VW$e*gLVe@X!ZBNH<#8#^Z#H!mN*ppdYrn7E{rw5*)GqLQ+z zn!2WzwyvJOp^>qvnYpEvwXL1Kqm#3%o4co%x38anU{GjicvMV$Qc8MOPJU5Ic~wn) zQ%ieSPyeJT(`U_@zi7$wRcqF7+_HV=p8W@p96x#H{KYHRZ{E58@X7O+Z{C0W^8F{J z&Zk&E0~0Gd7cak%sJN7@yppQAmae{$skxP{y_2iEm#=>i0spO9wS38<`EzDXpE9Yx zr>niCslKMFyrd{UCo4TADLy7DJT%DP*UR12$==q=+|)>4S4&-0NnTb;TvUjkmy4a1 K^2A4B`T+nfFIF9WF*CQcvaz#wbaHWX_w@4d z^A8LP2@8*miiwL)OiD}3$jZ$xEGet3u5D;)ZSU;qn>cyuj9GK$FI=*0#p<;iHf`Cy zbI-m5hmW2(b>{rV%hzt)zI*@CljkpAzx(j{>-S&(2O2O8G#KFUXBc4n2Ri-%>ImX%jjR#n&3*3~yMF|)9;v2$>8ar5x@^$!dV4UdeDjZaKT z%gD;fD<~={tEj4}Z)|RD@9ggFpE!Bij9GK$Em*W<*@{(b)^FUrb^FfUd-oqaeDuW0 zlc!FfK6Cc$x%1~QT)cSc(xuCnuUxr$_1d-T*RS8War5S_TeolDxpU|4-Fx@$-+%Dn z;lqcI9zA~ig&z?Ph{`|#@moHzvdj0y%n>TOYzI*rn{f7@9KYsl5>GS6=U%r0* zh6KJNzz+oYi2%P4;5QsF&=>%Nmy?^99~!z!D!|ay($O<8G&Ti=udTf!D1be^ef|V^&0Dwc+`V`I;iJb- zo<4j2;^nK?Z{EIp{{i8BMmS)C0cJR0fdN)HV1om87~p^dP8dL#4`nejGBGo=u&}bR zv2$>6a&mEV^YHNU@$m}?2nq@b3yX+|iiwF!NJvUbNlVMf%F4;fD<~)`Dk&?gsH&=| zscUFxYHDd~>*(t0>FFDQ!qFUNc(a)>+I>D zG_2q$#OZSvuUx-%_rc?5FW8k<^L+dI0td;2C#oIGXP^qI5f%$>hr(c-1c zR;*mTX5IRYo3?D-zGK(!z5DhbJapvf@e`*`pFMZs;^iyXuHU?M=kEOnj~+jL_TuI1 cH}Bqm{PgAP_aDE0(H4{u-JfS{1@$e6f<-%cl?T-n@GG{ORL|5ANN$ zb>rIA%NNg|J#*^Bu_K2L?%%t6=Zn9cJ<2TOP4HKFmKMRnbTpO2m2fBWw39- z{semw>^rc>zEZ?RX3v~HWm11{cSl=uV?%9qMOjHtX}DQoB& zncFzJc?X0>#iwNE6@$GqVd|{;OIEJiykqa7<7X~jyZzwlE1++_ynp@d;hpQ3&z(H7 zfA_YHYnCsbH*-pVcUxm^MM-{EYC=qSkgtb}y_Knfj=GYpga|)wS5Z@jmtROkTvA3( rQCUq>836LTwDM;CW5zrc|2sMv($^sKzX(u(T3rq+(` zzKK(&&z`q%$%@tMHg4Uqd*8t$Cr+QgboIua`;VT!eDnU(*YCgn{2w9!=zWO)K@6$^ zMkZ!9P96baF-chkWi?=M8UusV0T`UVpx}&4Oi9nmEhsLps%wM>=PY1wE(ZqZ7Eo{= zJ#qTn#Va>%1B3GgC^&!q`tz4o!3pv*IR2^TcN!SP$jkx^PJUo;$^e5?6BwLkz~FQS z24?^$b%KI3udt-Ns<;9875421/.-+*('& \ No newline at end of file diff --git a/imagepy/data/luts/Thermal.lut b/imagepy/data/luts/Thermal.lut deleted file mode 100644 index 5d8cd5a807790edb2e071077896c677774cfa96c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 768 zcmZ>BBMP{>x;Q&I*xOoLnwuIM=xS@ID#^=8iHivG@o=)SFfkwlMizE%0a0lMbzKu1 z7oU)r)ZEg#w*DClR&3aD;P|<#w;w)#^Wn?)Uw{7o`}g8Y;VcMKU zE7on?^H*-(d;I+MyH8(#{Qmo&c!2D1{A?yxPF_JVX$3VMBTIWX-;n5}tisBM zjtL--ZQpKd9`JGy%( zOrAb#?t&%DSFPK)b;q7VC(d04hUyDobYTxvQ2hP+@f~RGr;i`rzkB=U_3PKKK;ZS8 zx9{G6`1twDx9>l}5sl(kIOh*A^54IC{`fw^wTtIYpV-w>SC*F+7w+%jXl1OcrXVdQ z$P08UBNAX{<>2Ds6A%QZ2}vm#IeA4T6;(BL4NWa=ZEYPO(AL({($vsUS5s9{QdE$a dlaZE`5C^6qK>>bV9&Rp9b~ZLv7G`E9CIH;IwqO7N diff --git a/imagepy/data/luts/Viridis.lut b/imagepy/data/luts/Viridis.lut deleted file mode 100644 index eb4a3e6c..00000000 --- a/imagepy/data/luts/Viridis.lut +++ /dev/null @@ -1 +0,0 @@ -DDDEEEFFFFGGGGGGGHHHHHHHHHGGGGGGGFFFFEEEEDDCCCBBBAA@@??>>===<<;;::998877665544332211100//...--,,,++***))((('''&&&%%$$$###"""!!!  !!"##$%&'()*+,./0235689;=>@BDEGIKMOQSUWY[^`bdgikmprtwy|~  !"#%&'(*+,-/01245679:;<=>@ABCDEGHIJKLMNPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyzz{|}~TUWXZ[\^_abcefgijklnopqrstuvwxyz{||}~~}}|{zzyxwvvutsrqponmlkihgfedba`_]\[YXVUTRQONLKIGFDCA?><:875320.,+)'&$"!!"$ \ No newline at end of file diff --git a/imagepy/data/luts/Yellow Hot.lut b/imagepy/data/luts/Yellow Hot.lut deleted file mode 100644 index 5ccebb7add66b6e747bd18c8d8baec79ef6adda0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 768 zcmZQzVqxdx<`WPW6_=8cS5#Kh(AL#AGBLBXwsUZDb@%e|3k(j6h>DF*N=eJi&Mhb^ zEw8MpYiw@q=<4mCIAz+**>e{xTC#lQnspmCZ{4wL&;El)j-5Gk=Iq&X=gwcaaPi`$ z%a^ZQxq9u|_3Jlo-n@0|_MJO-@7}wA|G|TYj~+dK{N(A=XV0F$c=7V(tJkmJym|ZX z-TU_+K7Rc4>GPK_U%!6){{6>~pTB>OM?d;)^NAdr-jkyB7o zR#n&3*3mOCGB!20w6*~Tgo~@Yr?;+{4GfdXQ)bSbHG9t7c?%XUTD)Z0@)avrty#Nn{f14Ow`|?EW9P2jd-m-=aPZKP zqsNY)ICc8W*>mTCfpq06Fnn&^1_sT8hrm#I_8b@>Z{7mK;?rkfF#P!W`wwlr|DR## z047!rZazT~aVc2^Wi?G5eIru~Ydc35cQ4<7;IPP;_@vZ~?7YH~@~YZ~=C;nB{z+43 r%$~P!$?{ceH*DUvbI<-mM^BtSck#;gTX!Eke)jUs`%hng{QeIBb$FjC diff --git a/imagepy/data/luts/ametrine.lut b/imagepy/data/luts/ametrine.lut deleted file mode 100644 index 598d81dfa3d5895eeb8688c46e378541b0e0997d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 768 zcmb1>S5#J2)6mk<(>F9WHM6j?v9ot{c6Iac^6~W#3=R#8h>D4gPfSip%gD;f%`Yr2 zDXXZesjY8pZfWc2>hA5IIC;vn8M9{3oxgC=l4UDatzNr+>h$R|XU?8GcmDi^ix)3lx_srz)vMR8 zUB7YT=FMBTZ{N9d_ujqx_a8iX_~_B&$4{OBnTD7Xg$ zccI|U9UB`PTU$FjJ9~S32L}g7M@J_oCue6D7Z+DoS2s5|cXxLW4-ZdIFE1}|Z*LzT zA75WzKRD$jB%#05VVkP!bgYHJ}2Z zA*cXoFdP7*B04rMAt^Z(7#G>Oc?CtqrNGFjuB~fmY;JAq=x zCOVjf4rU|3teG>WPn$Yr@}vpr6t8hg#~%J zIoVm68R==MDalER3GuNp(NU2R;bEa6!9jrm{(e5*UY;KAZmurQPL2-tc6PQl*49>* z78d4arY0uFMuvt4`g(f0I@(%Vni}frYN{$K%1Vlg3i5KYvNF}+hTEX>SIj0_9_xaNJ3 diff --git a/imagepy/data/luts/blue_orange_icb.lut b/imagepy/data/luts/blue_orange_icb.lut deleted file mode 100644 index bf27a41a90e383207fc033562c014c7648eca7fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 800 zcmeZt_V-~}#K-^y{}~yW7#JD;G5luu1>%DNBZYv8g^h!YhfhFAL`*_TMovLVMNLCX zN6)~>#LU9V#?Haf+11_C+t)uZI5a#mIyOErIW;{qJ2$_uxU{^oy0*TtxwXBsySIPh zWe&gn?+js8XyZ^wUBgamhI&<#Ar7PEN+`4n`!J{Y7Uc7qq z?!%`q-+ui1^N&)%#Kg?P%Er#Y$;HjX%f~MuC?qT*Dkd%=DJ3ltEX>ZXk=_+YG!U>X=QC=YiIA^U~gw@V{K(=VQyw>Vr*n+ps%N^qphW>p{}N? zqO7E-ATK8?BP}HDuo;N;@w;pO8O5EK#?5fu}ckd%^^k(HBIP*hS@QB_md(A3h_ z(bdy8VbYXoGiJ@1w_wqdWh+*#S+`-+mTfzB?b)~g;NhdkPo6$|{^I4U*Kgjwd;j6% zr_W!$e*6C8=da&?{{Bs2^fOW#|FzYX<)y`i`MKGd>8Z(y@v+g7;i18S{=VLx?yk;` z4t6$H7G@?!26{SL8fq#^3UV@15@I4k0(?AN9BeF1j4T{H0wNMJ3Mv{p1|}9Z4$ki0 z{=wnV@yY4g`Niec_08?w{gbE9p1*kc>h+tq@7{mt*r{`uuHCx#=-I1xpT7P21ppNI B6^#G@ diff --git a/imagepy/data/luts/cool.lut b/imagepy/data/luts/cool.lut deleted file mode 100644 index 790cba3e1120c4bbc47f11be4d329b9e5fe2b458..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 800 zcmeZt_V-~}#K-^y{}~yW8JHOUG5liq2I7MOBc*_eg^h!YhfhFAL`*_TMovLVMNLCX zN6)~>#LU9V#?Haf+11_C+t)uZI5a#mIyOErIW;{qJ2$_uxU{^oy0*TtxwXBsySIPh zWe&gn?+js8XyZ^wUBgamhI&<#Ar7PEN+`4n`!J{Y7Uc7qq z?!%`q-+ui1^N&Kn%*@Kp$<50zC@d;2DJ?6nsI024sjaJTXl!b3X>Duo;N;@w;pO8O z5EK#?5fu}ckd%^^k(HBIP*hS@QB_mdP*+n`QC3n^ke8E{k(QE_5El~_5f%~@;OFDz z;pXDxU~g+}X>Mw4sIRN7sjjN5C@(85DK083$j{5o$e0 zJ$rQT*0oFLP8~b6fA{vy>sK#dJb(7|$^G5!&Gpsg#rfIk$??(Q!T#Rv&JH#fCI&hh zDhe_ZA_6=dEQ~B1JOUyTG72ghItC^dHV)42-u}Vi(ecUY+4;rg)%DHo-TjlN&z`?{ d`RetXx9{G6=-8=qm#*Eq_vqQHcb~p}0{||BVpRYD diff --git a/imagepy/data/luts/edges.lut b/imagepy/data/luts/edges.lut deleted file mode 100644 index 74014a817cc1f5fa857d747b951b2fd69924a36b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 800 zcmeZt_V-~}z{mgu{}~w&5F`KsjPwI++ybIf@+z8o#qTGjef48pG~9wN5ntlV2OVK@)$K^ diff --git a/imagepy/data/luts/gem.lut b/imagepy/data/luts/gem.lut deleted file mode 100644 index c9194056d8e4276eb9dc6ee5a38202341f43d9cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 800 zcmeZt_V-~}#K-^y{}~yWfbbu~ABJBbJ{T}k3Yb{fIJkKD1cXGyB&1~I6qHodG_-W| z42(?7EUawo92}ip-95d1{R4wT!y}_(;}erp(=)Sk^9zeh%PXsE>l>R}+dI2^`zKDG zI(_Esx$_q;Ub=kc>b2`PZr-|m=kC4x4;(sj?8K=v=Pq2ja_z>gJNF(udh+bWt2ggH zeERb3$FD#CCSIOpMGdZ0wv|JiPn@Lc*eA5|Yv~ za`K8wDyr%lTH3mL28PBaX6BYwHn#Q-j!rJF?jBy=zJ39L!69Mckx?6tls zg~eqR)wK;xt?gYs{S&85n>l;lg2hW$tXjK%)0XW!_v|}x=;-lNXU<=|a_#2rdk-Ez zeevq;`%hoK|NQd@=zSJeHg*n9E^Z!PK7Ii~Az=|wF>wh=DQOv5Ie7&|C1n*=HFXV5 zEo~iLJ$(a1BV!X&Gjj_|D{C8DJ9`J9vz%RA-P}Dqy}W&V{rm$0gMvds!@?sXqoQMC z+0)kt1HV(iwpB}voq6ElM~}( zqa(vZg9H72y*=GsogE$QY^*HIOpFZlbhI?oRFo9tWTYg-M1%zRc(^#&SQuG2cmzZw zWE501bPP-^Y#f~3z5Rp3qvMm)v-69~tLvNFyZa|kpFMx^^404%Z{NND(6LkJE?v8I O@6oeY?>>F|^$P$iVHMv1 diff --git a/imagepy/data/luts/glasbey.lut b/imagepy/data/luts/glasbey.lut deleted file mode 100644 index 283612d8..00000000 --- a/imagepy/data/luts/glasbey.lut +++ /dev/null @@ -1,257 +0,0 @@ -Index Red Green Blue -0 255 255 255 -1 0 0 255 -2 255 0 0 -3 0 255 0 -4 0 0 51 -5 255 0 182 -6 0 83 0 -7 255 211 0 -8 0 159 255 -9 154 77 66 -10 0 255 190 -11 120 63 193 -12 31 150 152 -13 255 172 253 -14 177 204 113 -15 241 8 92 -16 254 143 66 -17 221 0 255 -18 32 26 1 -19 114 0 85 -20 118 108 149 -21 2 173 36 -22 200 255 0 -23 136 108 0 -24 255 183 159 -25 133 133 103 -26 161 3 0 -27 20 249 255 -28 0 71 158 -29 220 94 147 -30 147 212 255 -31 0 76 255 -32 0 66 80 -33 57 167 106 -34 238 112 254 -35 0 0 100 -36 171 245 204 -37 161 146 255 -38 164 255 115 -39 255 206 113 -40 71 0 21 -41 212 173 197 -42 251 118 111 -43 171 188 0 -44 117 0 215 -45 166 0 154 -46 0 115 254 -47 165 93 174 -48 98 132 2 -49 0 121 168 -50 0 255 131 -51 86 53 0 -52 159 0 63 -53 66 45 66 -54 255 242 187 -55 0 93 67 -56 252 255 124 -57 159 191 186 -58 167 84 19 -59 74 39 108 -60 0 16 166 -61 145 78 109 -62 207 149 0 -63 195 187 255 -64 253 68 64 -65 66 78 32 -66 106 1 0 -67 181 131 84 -68 132 233 147 -69 96 217 0 -70 255 111 211 -71 102 75 63 -72 254 100 0 -73 228 3 127 -74 17 199 174 -75 210 129 139 -76 91 118 124 -77 32 59 106 -78 180 84 255 -79 226 8 210 -80 0 1 20 -81 93 132 68 -82 166 250 255 -83 97 123 201 -84 98 0 122 -85 126 190 58 -86 0 60 183 -87 255 253 0 -88 7 197 226 -89 180 167 57 -90 148 186 138 -91 204 187 160 -92 55 0 49 -93 0 40 1 -94 150 122 129 -95 39 136 38 -96 206 130 180 -97 150 164 196 -98 180 32 128 -99 110 86 180 -100 147 0 185 -101 199 48 61 -102 115 102 255 -103 15 187 253 -104 172 164 100 -105 182 117 250 -106 216 220 254 -107 87 141 113 -108 216 85 34 -109 0 196 103 -110 243 165 105 -111 216 255 182 -112 1 24 219 -113 52 66 54 -114 255 154 0 -115 87 95 1 -116 198 241 79 -117 255 95 133 -118 123 172 240 -119 120 100 49 -120 162 133 204 -121 105 255 220 -122 198 82 100 -123 121 26 64 -124 0 238 70 -125 231 207 69 -126 217 128 233 -127 255 211 209 -128 209 255 141 -129 36 0 3 -130 87 163 193 -131 211 231 201 -132 203 111 79 -133 62 24 0 -134 0 117 223 -135 112 176 88 -136 209 24 0 -137 0 30 107 -138 105 200 197 -139 255 203 255 -140 233 194 137 -141 191 129 46 -142 69 42 145 -143 171 76 194 -144 14 117 61 -145 0 30 25 -146 118 73 127 -147 255 169 200 -148 94 55 217 -149 238 230 138 -150 159 54 33 -151 80 0 148 -152 189 144 128 -153 0 109 126 -154 88 223 96 -155 71 80 103 -156 1 93 159 -157 99 48 60 -158 2 206 148 -159 139 83 37 -160 171 0 255 -161 141 42 135 -162 85 83 148 -163 150 255 0 -164 0 152 123 -165 255 138 203 -166 222 69 200 -167 107 109 230 -168 30 0 68 -169 173 76 138 -170 255 134 161 -171 0 35 60 -172 138 205 0 -173 111 202 157 -174 225 75 253 -175 255 176 77 -176 229 232 57 -177 114 16 255 -178 111 82 101 -179 134 137 48 -180 99 38 80 -181 105 38 32 -182 200 110 0 -183 209 164 255 -184 198 210 86 -185 79 103 77 -186 174 165 166 -187 170 45 101 -188 199 81 175 -189 255 89 172 -190 146 102 78 -191 102 134 184 -192 111 152 255 -193 92 255 159 -194 172 137 178 -195 210 34 98 -196 199 207 147 -197 255 185 30 -198 250 148 141 -199 49 34 78 -200 254 81 97 -201 254 141 100 -202 68 54 23 -203 201 162 84 -204 199 232 240 -205 68 152 0 -206 147 172 58 -207 22 75 28 -208 8 84 121 -209 116 45 0 -210 104 60 255 -211 64 41 38 -212 164 113 215 -213 207 0 155 -214 118 1 35 -215 83 0 88 -216 0 82 232 -217 43 92 87 -218 160 217 146 -219 176 26 229 -220 29 3 36 -221 122 58 159 -222 214 209 207 -223 160 100 105 -224 106 157 160 -225 153 219 113 -226 192 56 207 -227 125 255 89 -228 149 0 34 -229 213 162 223 -230 22 131 204 -231 166 249 69 -232 109 105 97 -233 86 188 78 -234 255 109 81 -235 255 3 248 -236 255 0 73 -237 202 0 35 -238 67 109 18 -239 234 170 173 -240 191 165 0 -241 38 44 51 -242 85 185 2 -243 121 182 158 -244 254 236 212 -245 139 165 89 -246 141 254 193 -247 0 60 43 -248 63 17 40 -249 255 221 246 -250 17 26 146 -251 154 66 84 -252 149 157 238 -253 126 130 72 -254 58 6 101 -255 189 117 101 diff --git a/imagepy/data/luts/glasbey_inverted.lut b/imagepy/data/luts/glasbey_inverted.lut deleted file mode 100644 index 02766a6026e7eaee6c240fe167233689544fd07d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 768 zcmW;J5&ZWc5C-tW;cys+VHk#C7=~du48w33hG94yhQr|xAAI}2pM#|C``kselRZ-> zBA??r4^8&UH<1iiJqO#Qv>ua`I1$nLVWh)#b8TGy0i_$?!dJj3oxZ;M%7ce9H%64d^QuIe#hS-XE)bay2-kt!Iuo?YZdD1dV-9l^> za}y$U#>NbxRtjTzqk2k%QhX_#x7~58{f4m(E{%5=`N!?qRlb_Y?NPLB`@Q;aXBXbX zqC1nGqg+$vTn!BM=8CC19DvZM|%7b11T4c3TjF9DOk=|;l zA;bqhbnvai==g|y-8R;n^Y$_c_<~0)f3tX;h(neWF4o5?K${mn11`UyM1?LYs@Tkm zpVX=QLQQltU2+d)Q3#aJqPx1yM}vmy?3N=8JM_yFzvvRTt^bWl;7FOWgPXB>h)Q)I z><-yfS|2DNX8;Y0I1MbeN=1$%>ui>Kf?k@w>0+R#YyR6B?*e-btB%{fdp|x2>iUV( rt>R6z?NZ%0KV)AXBvRoeEX()hl{Fv46I#6fdDJhi%&>OPDxA8$jr{k%_}G@DlRE4uc)l5uBol7Z)j|8X>Duo=Fw*EFmcl4DO09Sn?7^a>^XDi&0nx^(UPUhmaka3YW146>(+1BxM}m2t=qQm*tu)> zp1u3_A2@jM(BUITj~zd8^3>@wXU?8Gf8pY#%a^ZQy>|V^&0Du`-?@A5{)2~)9zTBa z^x5+lFJHcT{pRhv_wPS^{Pg+Dm#^Qx|M>Cq*Y7`n{`~&^``53ZKY#rA{{7pxuV24> z`TY6Qr;i^$e0cx<-P^Zs-n@SO>eb7aFJ3%<{_NS)r%#?de*Ea+!v_!U-@kY7?%g|g zZr{3f^X82k*RNl@diBbc%a<=*x_IHj>eZ`Oty;Nq<%$(6mIJ}EWy_W>UAkn+lEsS` zFIu!{;lhOr7A%-QfBw9A^XAT-J7>*{K2YN|o;&;*VL zXf%Lg0T>B05V5crYc#;);Q%xqAQ5pE6cM0s0S62uWPm{f3mahITsVLJ+_|%7&zw1Z z`qZgYCr_R@as2qPV@Ho3Idb^$p+g4`9@xKs-`+jDckSG?XjkT4PrG>e41v5}#HzP_HWj<%MjhPs-nin5ZT Vg1nrpw3MW{n23-7KQ}uw0{~Y&+PDA! diff --git a/imagepy/data/luts/morgenstemning.lut b/imagepy/data/luts/morgenstemning.lut deleted file mode 100644 index a9af2d5db785589fed639fb29fed060cf2ae2bdd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 768 zcmZQzKmbNCU;+VVAYcIkR#sLvHa2#4b`B0sPA)EPZXO<9UOs+)0YM=lVG&U=2}x;L zc|~Pa4J}=LBU1}&TL))1PoIF`u*jJBq|}V;yuy<5s+#(y*7mO636rKypEY;>q9w~$ zu35KX^VaRV_Uu1+_}Gb4XU|`}eD(Ux+js9jeEj6u^Ovt)zj^om{fCdAK7Ic3=TUXztX*1_8Sh9Tex=mYm?A~|q$njHW&R@E6{pOu}4<0{#{_4%U z51+n#{r=6WK9Bi#E%uEdRb+j~8 zl@;Y>r6j~egar6_xH&o4Secm^8G&h+iG_`mS3p!!R!L3U(9Fik!#^}SDI>S2yr!YG rvv=aunR6E|UAcDSmL0qIA3A#S%!SL>Zr*+H_}R<1A3lHk`R6|X9#mgI diff --git a/imagepy/data/luts/mpl-inferno.lut b/imagepy/data/luts/mpl-inferno.lut deleted file mode 100644 index d372179f3829479bc4a1c9afaed9670bb992a01b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 768 zcmZQzU|?iqWMX1wVPR!s=iubx=H=rT5E2#<6PJ{dk(F0aQc+Xa)YjEAFfuVUx3spg zb8vKab@TA@_Vo`43JwjAh>DJlPe@8mP0Ps2&dn<*DlRRnsH(25YiMk4ZR_al?(Lf} zaq`q@GiJ`7GjGAd#Y>hgU%7hCy7e13ZP~hg$FALb_w7G;=*ZFICr+L|bN2j&i|WPt=o6*-n;+c(c>pipFMm2;^nK?Z{EIr_x{6&kDoq${`}?Z*KgmxfB*60$IqWX ze*wX73;+@ZN&=O9|NiaU*RNl`eE$6T)2ELR05s&w*Kgl{{`$iJ1dsp#hX5NpI|m0R zCl?nt2=D*_n85`EoNxf-faG|2`S|$x1q20!fWajqDk>%}E+HW)DJ3l}BO@y-Coiv{ zps1*%q^zQ%s;Z`@uA!l+sim#0qob>*r*B|rXk=_`Vrph?ZeeL;<+8K0v$C=>Gcz+X($mw@Qd3frlamq?6XN6JVq>DC zqaq{1!@@#Cf`bAB{QZ1=yuCa<+}&K9og5wP?QE^BEG^83Dd;kCd diff --git a/imagepy/data/luts/mpl-magma.lut b/imagepy/data/luts/mpl-magma.lut deleted file mode 100644 index 6b31ee40f816e12618a31b5f8f770d9782929222..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 768 zcmZQzU|?iqWMX1wVPR!s=iubx=Hcbz7Z4N@5fu}ckdl^>lUGnuR#nr`($>{CFfuVU zx3spgb8vEYb@%Y{_Vo`43JwjAh>DJlOGr#kP0Ps4&dJL!EG{W4udJ%6t8Z*-X>IT9 z>gnyDIBCk%=`&}|nKyso;w8(LuUx%m-G+^uw{F|9bNAkT2M!%RdhEoh(`V0JxOn-> zwd*%;-MM@3!NW&So<4j2;??UnZ{NLp|Ka1O&!4}1{rc_O_wPS`{QUXz*RNl{f#44a z{Dpyk*Z@otM1z$>2#{_DAOQOx6ac^w00jXs40w2X`S|$x1%N;h20$DEegpu^2?+@c zi--clN?bxhQVJMeGO}`V^70Cbib_h#Dk`d~YHI2l8k(9~TH4w=I=Z@gdiweX28M=4 z#>OTlrlw|Q=H?cbmR45QHa50)cJ>aAPEO7)u5Rugo?hNQzJ39LK_Q`G5s}dFP0z^6 z$tx%gk&>X)-WqXV0C#aM6-wD^{&pw;mX<+js8X0}R(A$Bv&o zefIptOINPl00!#)hmW5=d-3YcyAK~ffBpXR*B@pUHg-;KUVcGgQE^FWS$QQDU{veq z8ycHhSlQS+y103I`vwGshDXN4B_ySS?awbPDlRE4E3c@mtg5cAsj0241A=-WXaIvo zOaPH+0I30~t*x!Csj056uBxi6tf(k2FDorADJd>0Dl90-&&$ot$wqpjg5_oiHU{)AS)&|HZCqcAt5m-IXNXYEj=SM3zYJJVFPqpMP+pjD0rG% d+d4YCdm#ZdW7ZsC2rXU;4k1v|1BTGv{Q&F{UYY;^ diff --git a/imagepy/data/luts/mpl-plasma.lut b/imagepy/data/luts/mpl-plasma.lut deleted file mode 100644 index a2fd009c4f2f29a6a7114c4736ec702b9d8fa501..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 768 zcmd-P5Ehk?mX%jjR#n&3*3~yOHZilXvbME%aB^{V_w@4d^A8LP2@8*miiwL)OiD>j z&&bNj%`Ye_E-kC5tgfl6Z)|F5ZR_al?&<5FFlqACY13!Unmu>k`~`~^FIl>L#mZG{ z)~;K>VdJJPTeofBv2)k%J$v`We*MPH zTet7rxqI*a{Ra;pJ%0S;>CecHvZ{EIr_x}Bd4 z7ARnb046YC1Oo;P02F3oVrFJxVP#`y=iubx;pO8O5EK#?5fzt^l#-T_m6KOgQdUt_ zQ`gYa*3s3|H!w0bF*P%{u(Y~6-WuWYxbNubLY*QKYzi(g^Ly~Uc7k8lBG+RE?c&2IS8yk05BgYzI5r5 zB}*1BUbJxGf(7&E&6_)C_Uu_RXH1_yZR(WClO|5+@9*pF>F(<6Xm4w6X>Mw4XsEBN zt*Nf6tSB!lEiEZ7DlEv)%gxQn&dSV4PfJTpNlr>kNQjS%jfswmii`*k3k?Yg4hjtL z_w)7j@%HlcaCdieb#Zobbab${v$e6dva+-=H#0RgF*Y(ZG|<=6)z#6~*3#6}&`?)X TQ&Uw{RY3qC4=4r(s>+H029b8@ diff --git a/imagepy/data/luts/mpl-viridis.lut b/imagepy/data/luts/mpl-viridis.lut deleted file mode 100644 index eb4a3e6c..00000000 --- a/imagepy/data/luts/mpl-viridis.lut +++ /dev/null @@ -1 +0,0 @@ -DDDEEEFFFFGGGGGGGHHHHHHHHHGGGGGGGFFFFEEEEDDCCCBBBAA@@??>>===<<;;::998877665544332211100//...--,,,++***))((('''&&&%%$$$###"""!!!  !!"##$%&'()*+,./0235689;=>@BDEGIKMOQSUWY[^`bdgikmprtwy|~  !"#%&'(*+,-/01245679:;<=>@ABCDEGHIJKLMNPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyzz{|}~TUWXZ[\^_abcefgijklnopqrstuvwxyz{||}~~}}|{zzyxwvvutsrqponmlkihgfedba`_]\[YXVUTRQONLKIGFDCA?><:875320.,+)'&$"!!"$ \ No newline at end of file diff --git a/imagepy/data/luts/phase.lut b/imagepy/data/luts/phase.lut deleted file mode 100644 index 142be6ac..00000000 --- a/imagepy/data/luts/phase.lut +++ /dev/null @@ -1,257 +0,0 @@ - 0 0 252 - 0 0 252 - 0 0 252 - 4 4 252 - 4 4 252 - 4 4 252 - 8 8 252 - 8 8 252 - 12 12 252 - 12 12 252 - 12 12 248 - 16 16 248 - 16 16 248 - 20 20 248 - 20 20 248 - 20 20 248 - 24 24 248 - 24 24 248 - 28 28 248 - 28 28 248 - 28 28 244 - 32 32 244 - 32 32 244 - 36 36 244 - 36 36 244 - 36 36 244 - 40 40 244 - 40 40 244 - 44 44 244 - 44 44 244 - 44 44 240 - 48 48 240 - 48 48 240 - 52 52 240 - 52 52 240 - 52 52 240 - 56 56 240 - 56 56 240 - 60 60 240 - 60 60 236 - 60 60 236 - 64 64 236 - 64 64 236 - 68 68 236 - 68 68 236 - 68 68 236 - 72 72 236 - 72 72 236 - 76 76 236 - 76 76 232 - 76 76 232 - 80 80 232 - 80 80 232 - 84 84 232 - 84 84 232 - 84 84 232 - 88 88 232 - 88 88 232 - 92 92 232 - 92 92 228 - 92 92 228 - 96 96 228 - 96 96 228 - 96 96 228 -100 100 228 -100 100 228 -104 104 228 -104 104 228 -104 104 224 -108 108 224 -108 108 224 -112 112 224 -112 112 224 -112 112 224 -116 116 224 -116 116 224 -120 120 224 -120 120 224 -120 120 220 -124 124 220 -124 124 220 -128 128 220 -128 128 220 -128 128 220 -132 132 220 -132 132 220 -136 136 220 -136 136 220 -136 136 216 -140 140 216 -140 140 216 -144 144 216 -144 144 216 -144 144 216 -148 148 216 -148 148 216 -152 152 216 -152 152 212 -152 152 212 -156 156 212 -156 156 212 -160 160 212 -160 160 212 -160 160 212 -164 164 212 -164 164 212 -168 168 212 -168 168 208 -168 168 208 -172 172 208 -172 172 208 -176 176 208 -176 176 208 -176 176 208 -180 180 208 -180 180 208 -184 184 208 -184 184 204 -184 184 204 -188 188 204 -188 188 204 -192 192 204 -192 192 204 -192 192 204 -196 196 204 -196 196 204 -200 200 200 -200 200 200 -200 200 200 -200 196 196 -200 196 196 -200 196 196 -200 192 192 -200 192 192 -200 188 188 -200 188 188 -204 188 188 -204 184 184 -204 184 184 -204 180 180 -204 180 180 -204 180 180 -204 176 176 -204 176 176 -204 172 172 -204 172 172 -208 172 172 -208 168 168 -208 168 168 -208 168 168 -208 164 164 -208 164 164 -208 160 160 -208 160 160 -208 160 160 -208 156 156 -212 156 156 -212 152 152 -212 152 152 -212 152 152 -212 148 148 -212 148 148 -212 144 144 -212 144 144 -212 144 144 -212 140 140 -216 140 140 -216 136 136 -216 136 136 -216 136 136 -216 132 132 -216 132 132 -216 132 132 -216 128 128 -216 128 128 -216 124 124 -220 124 124 -220 124 124 -220 120 120 -220 120 120 -220 116 116 -220 116 116 -220 116 116 -220 112 112 -220 112 112 -220 108 108 -224 108 108 -224 108 108 -224 104 104 -224 104 104 -224 100 100 -224 100 100 -224 100 100 -224 96 96 -224 96 96 -228 96 96 -228 92 92 -228 92 92 -228 88 88 -228 88 88 -228 88 88 -228 84 84 -228 84 84 -228 80 80 -228 80 80 -232 80 80 -232 76 76 -232 76 76 -232 72 72 -232 72 72 -232 72 72 -232 68 68 -232 68 68 -232 68 68 -232 64 64 -236 64 64 -236 60 60 -236 60 60 -236 60 60 -236 56 56 -236 56 56 -236 52 52 -236 52 52 -236 52 52 -236 48 48 -240 48 48 -240 44 44 -240 44 44 -240 44 44 -240 40 40 -240 40 40 -240 36 36 -240 36 36 -240 36 36 -240 32 32 -244 32 32 -244 32 32 -244 28 28 -244 28 28 -244 24 24 -244 24 24 -244 24 24 -244 20 20 -244 20 20 -244 16 16 -248 16 16 -248 16 16 -248 12 12 -248 12 12 -248 8 8 -248 8 8 -248 8 8 -248 4 4 -248 4 4 -252 0 0 -252 0 0 - \ No newline at end of file diff --git a/imagepy/data/luts/physics.lut b/imagepy/data/luts/physics.lut deleted file mode 100644 index 033c07f9e4c86b8b8e06520e9c9e62916710d0e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 768 zcmdPb)78<^)KFJdQC3utla-c|5Em5|65!+E;$&xKW@LZ?MkZ!v7FITP4h~K(ZXRAf zegOeNAz=|wF>wh=DQrLz2arGq;>bWuOiWrqRm;HC%HGw>KQt;KHM^j!y0NWi;*{w# z=geQYWZB9!>o#uLwsX(EgGY{?Jag{S)f;#2fxvw*cmM(q!Qc@HJO+U$VDJqOPfh9$m5EL2_9hZ=t zmYI`ZR8n45+tA$B+0#F1>Wta*7A{%7YVC&2K&S3MbPNQJgTM(8I0*!&PMtb^`t+GI zXU?8Id+yx1^XJcBxNza(#fz6NUAlbv@|7!Bu3o)*?b@~L*RS8WapNWk+yaB!5O45`HGb*SFK*NcHR078#isPr z6a7w~_W=@;Z-__b!Q(lytksKEp;_v0^U~Oioqpm0;4om_N VAc_P;umNE#KnMc}VgLao0056ULEr!Y diff --git a/imagepy/data/luts/royal.lut b/imagepy/data/luts/royal.lut deleted file mode 100644 index 98a56a66e17ece8e2ee8e0eec01b80521844baca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 800 zcmeZt_V-~}#K-^y{}~w=8Q2;Afgwl$1Q=-zxP+utbWQEt{KFG6i>eyhx~I%pvS#bv zBWJGOefsv>KZ?PhUq8Nn{`mgw>zB`;JbG{s13Y;21nDe$cPK`aj`No@=2+iIQT@S71#I7T()WZo&$%D96x#b?D>nAuim(I=iWU6 j{%5DQ|Ij@A?%uim>*x2^=g0dy7$^u33~rJYQ0RXEAD5}D diff --git a/imagepy/data/luts/sepia.lut b/imagepy/data/luts/sepia.lut deleted file mode 100644 index 75b22946..00000000 --- a/imagepy/data/luts/sepia.lut +++ /dev/null @@ -1,256 +0,0 @@ -0 0 0 -43 28 4 -57 35 5 -65 35 6 -66 36 7 -67 37 8 -68 38 9 -69 39 10 -70 40 11 -71 41 12 -72 42 13 -73 43 14 -74 44 15 -75 45 16 -76 46 17 -77 47 18 -78 48 19 -79 49 20 -80 50 21 -81 51 22 -82 52 23 -83 53 24 -83 53 24 -84 54 25 -85 55 26 -86 56 27 -87 57 28 -88 58 29 -89 59 30 -90 60 31 -91 61 32 -92 62 33 -93 63 34 -94 64 35 -95 65 36 -96 66 37 -97 67 38 -98 68 39 -99 69 40 -100 70 41 -101 71 42 -102 72 43 -103 73 44 -104 74 45 -105 75 46 -106 76 47 -107 77 48 -107 77 48 -108 78 49 -109 79 50 -110 80 51 -111 81 52 -112 82 53 -113 83 54 -114 84 55 -115 85 56 -116 86 57 -117 87 58 -118 88 59 -119 89 60 -120 90 61 -121 91 62 -122 92 63 -123 93 64 -124 94 65 -125 95 66 -125 95 66 -126 96 67 -127 97 68 -128 98 69 -129 99 70 -130 100 71 -131 101 72 -132 102 73 -133 103 74 -134 104 75 -135 105 76 -136 106 77 -137 107 78 -138 108 79 -139 109 80 -140 110 81 -141 111 82 -142 112 83 -143 113 84 -144 114 85 -145 115 86 -146 116 87 -147 117 88 -148 118 89 -149 119 90 -150 120 91 -151 121 92 -152 122 93 -153 123 94 -154 124 95 -155 125 96 -156 126 97 -157 127 98 -158 128 99 -159 129 100 -160 130 101 -161 131 102 -162 132 103 -163 133 104 -164 134 105 -165 135 106 -166 136 107 -167 137 108 -168 138 109 -169 139 110 -170 140 111 -171 141 112 -172 142 113 -173 143 114 -174 144 115 -175 145 116 -176 146 117 -177 147 118 -178 148 119 -179 149 120 -180 150 121 -181 151 122 -182 152 123 -183 153 124 -184 154 125 -185 155 126 -186 156 127 -187 157 128 -187 157 128 -188 158 129 -189 159 130 -190 160 131 -191 161 132 -192 162 133 -193 163 134 -194 164 135 -195 165 136 -196 166 137 -197 167 138 -198 168 139 -199 169 140 -200 170 141 -201 171 142 -202 172 143 -203 173 144 -204 174 145 -205 175 146 -206 176 147 -207 177 148 -208 178 149 -209 179 150 -210 180 151 -211 181 152 -212 182 153 -213 183 154 -214 184 155 -215 185 156 -216 186 157 -217 187 158 -218 188 159 -219 189 160 -220 190 161 -221 191 162 -222 192 163 -223 193 164 -224 194 165 -225 195 166 -226 196 167 -227 197 168 -228 198 169 -229 199 170 -230 200 171 -231 201 172 -232 202 173 -233 203 174 -234 204 175 -235 205 176 -236 206 177 -237 207 178 -238 208 179 -239 209 180 -240 210 181 -241 211 182 -242 212 183 -243 213 184 -244 214 185 -245 215 186 -246 216 187 -247 217 188 -248 218 189 -249 219 190 -250 220 191 -251 221 192 -252 222 193 -253 223 194 -254 224 195 -255 225 196 -255 225 196 -255 225 196 -255 225 196 -255 225 196 -255 225 197 -255 225 197 -255 225 197 -255 225 197 -255 226 198 -255 226 198 -255 226 198 -255 226 198 -255 226 198 -255 226 199 -255 226 199 -255 226 199 -255 226 199 -255 226 199 -255 227 200 -255 227 200 -255 227 200 -255 227 200 -255 227 200 -255 227 201 -255 227 201 -255 227 201 -255 227 201 -255 227 201 -255 228 202 -255 228 202 -255 228 202 -255 228 202 -255 228 202 -255 228 202 -255 228 203 -255 228 203 -255 228 203 -255 228 203 -255 228 203 -255 229 204 -255 229 204 -255 229 204 -255 229 204 -255 229 204 -255 229 204 -255 229 205 -255 229 205 -255 229 205 -255 229 205 -255 229 205 -255 230 205 -255 230 205 -255 231 205 -255 231 209 -255 233 214 -255 233 217 -255 242 233 -255 255 255 diff --git a/imagepy/data/luts/smart.lut b/imagepy/data/luts/smart.lut deleted file mode 100644 index 1f98e65c047f19cd8381697f736c0b8c8386a6b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 800 zcmeZt_V-~}#K-^y|Cty-@E^lphCd)a7%(w0F|)9;vaxe;aB^{T^YHTV@e2qD3JD8~ zh>D4cOGrveNlVMf%E`$qC@Lx`tEi}|sjF*fYHDfg=;-R{>l+vv85x_Hn3|cJTUc6I zS=-p!+SxleI5;{wIlH*Hy1Bc1czStx`}q3$`3D391_cL)gocHMM?^+OMaRU%#>K}c zBqk*#r=+H)rKe?PW#{G>6c&|~mRHp_G_`hg_f4ESW6u1=%l59_xOv;oz55RxIezl= z+4C1KUAcPw#;rSd?>)Hp=<$=MPoF)1`RdKP_a8od{PgMb*Kgl{{P_9v*RS7y{`~#> z4=wwiG}Kg-6y#*3rKBV!B*ewV#Kc5JMMOk|g@uHK1O){JfPkML z7zYp_C?p~(E+Hi?D=!a>0VNexbxm!3BU5urYdZ&LcQ3!-h?vBb^z6LC%BGG9a~3UM zyLspCeFqL7J9*~(C14QUymkA|y?YNHK6(rerDxBdzkc`O(^pUk{R9Tj-@o+ozmuJn zsev}c?=n&nVxnLVgZ<0T$IHvh!^6$Z&Be*V&c?>d!otGL40ISHK>%_+Gf+7&0kg5O z1Jf`ECnpyu8T0Uh5;8FG#3W=CR5U!b1Ept!W6s`ZQdU+`RaFDJUqe$%3+M%1J$(ZMLt|qTQ!_Jj3k#rEtZi&ySIPh z8>GD-;)@|6db^FfU`wtvCa`N=K3zx25zj^20gU3&wzk2iT Date: Sun, 3 Mar 2019 13:47:07 +0800 Subject: [PATCH 113/343] lut --- imagepy/core/manager/colormanager.py | 21 ++------ imagepy/data/luts/Orange_Hot.lut | Bin 0 -> 768 bytes .../{Rainbow RGB.lut => Rainbow_RGB.lut} | Bin imagepy/data/luts/Red_Hot.lut | Bin 0 -> 768 bytes imagepy/data/luts/Yellow_Hot.lut | Bin 0 -> 768 bytes .../Lookup table/Others/lookuptables_plg.py | 45 ++---------------- .../Image/Lookup table/lookuptables_plg.py | 7 ++- 7 files changed, 13 insertions(+), 60 deletions(-) create mode 100644 imagepy/data/luts/Orange_Hot.lut rename imagepy/data/luts/Others/{Rainbow RGB.lut => Rainbow_RGB.lut} (100%) create mode 100644 imagepy/data/luts/Red_Hot.lut create mode 100644 imagepy/data/luts/Yellow_Hot.lut diff --git a/imagepy/core/manager/colormanager.py b/imagepy/core/manager/colormanager.py index a6c2884f..bc7818c6 100644 --- a/imagepy/core/manager/colormanager.py +++ b/imagepy/core/manager/colormanager.py @@ -11,22 +11,13 @@ import sys from glob import glob - - - -# read from the lut binarycode -# glob: return a list ot paths matching a pathname pattern. filenames = glob(os.path.join(root_dir,'data/luts/*.lut')) +filenames.extend(glob(os.path.join(root_dir,'data/luts/*/*.lut'))) keys = [os.path.split(filename)[-1][:-4] for filename in filenames] values = [np.fromfile(filename, dtype=np.uint8).reshape((3,256)).T.copy() for filename in filenames] -filenames_others = glob(os.path.join(root_dir,'data/luts/Others/*.lut')) -keys_others = [os.path.split(filename)[-1][:-4] for filename in filenames_others] -values_others = [np.fromfile(filename, dtype=np.uint8).reshape((3,256)).T.copy() for filename in filenames_others] - class ColorManager: luts = dict(zip(keys, values)) - luts_others = dict(zip(keys_others, values_others)) frontcolor = (255,255,0) backcolor = (0,0,0) wr, wg, wb = 1.0/3, 1.0/3, 1.0/3 @@ -64,14 +55,8 @@ def get_back(cls, one): return np.dot((cls.wr,cls.wg,cls.wb), cls.backcolor) @classmethod - def get_lut(cls, name='grays'): - if name=='grays': - lut = np.arange(256).reshape((-1,1)) - return (lut*np.ones((1,3))).astype(np.uint8) - else: return cls.luts[name].copy() - @classmethod - def get_lut_others(cls, name='grays'): + def get_lut(cls, name='grays', set='all'): if name=='grays': lut = np.arange(256).reshape((-1,1)) return (lut*np.ones((1,3))).astype(np.uint8) - else: return cls.luts_others[name].copy() \ No newline at end of file + else: return cls.luts[name].copy() \ No newline at end of file diff --git a/imagepy/data/luts/Orange_Hot.lut b/imagepy/data/luts/Orange_Hot.lut new file mode 100644 index 0000000000000000000000000000000000000000..7665cdd029a40ff3e669e312989ad8ab46e76a18 GIT binary patch literal 768 zcmZQzV&&lG6BH4bl2uSv)6~&7GPSU_b98a{@(la)>+I>D zG_2q$#OZSvuUx-%_rc?5FW8k<^L+dI0td;2C#oIGXP^qI5f%$>hr(c-1c zR;*mTX5IRYo3?D-zGK(!z5DhbJapvf@e`*`pFMZs;^iyXuHU?M=kEOnj~+jL_TuI1 cH}Bqm{PgAP_aDE0(36LTwDM;CW5zrc|2sMv($^sKzX(u(T3rq+(` zzKK(&&z`q%$%@tMHg4Uqd*8t$Cr+QgboIua`;VT!eDnU(*YCgn{2w9!=zWO)K@6$^ zMkZ!9P96baF-chkWi?=M8UusV0T`UVpx}&4Oi9nmEhsLps%wM>=PY1wE(ZqZ7Eo{= zJ#qTn#Va>%1B3GgC^&!q`tz4o!3pv*IR2^TcN!SP$jkx^PJUo;$^e5?6BwLkz~FQS z24?^$b%KI3udt-NsDF*N=eJi&Mhb^ zEw8MpYiw@q=<4mCIAz+**>e{xTC#lQnspmCZ{4wL&;El)j-5Gk=Iq&X=gwcaaPi`$ z%a^ZQxq9u|_3Jlo-n@0|_MJO-@7}wA|G|TYj~+dK{N(A=XV0F$c=7V(tJkmJym|ZX z-TU_+K7Rc4>GPK_U%!6){{6>~pTB>OM?d;)^NAdr-jkyB7o zR#n&3*3mOCGB!20w6*~Tgo~@Yr?;+{4GfdXQ)bSbHG9t7c?%XUTD)Z0@)avrty#Nn{f14Ow`|?EW9P2jd-m-=aPZKP zqsNY)ICc8W*>mTCfpq06Fnn&^1_sT8hrm#I_8b@>Z{7mK;?rkfF#P!W`wwlr|DR## z047!rZazT~aVc2^Wi?G5eIru~Ydc35cQ4<7;IPP;_@vZ~?7YH~@~YZ~=C;nB{z+43 r%$~P!$?{ceH*DUvbI<-mM^BtSck#;gTX!Eke)jUs`%hng{QeIBb$FjC literal 0 HcmV?d00001 diff --git a/imagepy/menus/Image/Lookup table/Others/lookuptables_plg.py b/imagepy/menus/Image/Lookup table/Others/lookuptables_plg.py index 69c045a4..46b96ac1 100644 --- a/imagepy/menus/Image/Lookup table/Others/lookuptables_plg.py +++ b/imagepy/menus/Image/Lookup table/Others/lookuptables_plg.py @@ -3,44 +3,9 @@ Created on Mon Oct 17 21:13:42 2016 @author: yxl """ -from imagepy import IPy -import numpy as np -from imagepy.core import ImagePlus -from imagepy.ui.canvasframe import CanvasFrame -from imagepy.core.engine import Free -from imagepy.core.manager import ColorManager +from imagepy import IPy, root_dir +import os, glob +from ..lookuptables_plg import Plugin -class Plugin(Free): - def __init__(self, key): - self.title = key - - def load(self): - plus = IPy.get_ips() - if plus==None: - img = np.ones((30,1), dtype=np.uint8) * np.arange(256, dtype=np.uint8) - ips = ImagePlus([img], self.title) - ips.lut = ColorManager.get_lut_others(self.title) - IPy.show_ips(ips) - return False - elif plus.channels != 1: - IPy.alert('RGB image do not surport Lookup table!') - return False - return True - - #process - def run(self, para = None): - plus = IPy.get_ips() - plus.lut = ColorManager.get_lut_others(self.title) - plus.update() - - def __call__(self): - return self - -plgs = [Plugin(i) for i in list(ColorManager.luts_others.keys())] -for i in range(len(plgs)): - if plgs[i].title == 'Grays': - plgs.insert(0, plgs.pop(i)) - -if __name__ == '__main__': - print(list(ColorManager.luts_others.keys())) - \ No newline at end of file +fs = glob.glob(os.path.join(root_dir,'data/luts/Others/*.lut')) +plgs = [Plugin(i) for i in [os.path.split(f)[-1][:-4] for f in fs]] \ No newline at end of file diff --git a/imagepy/menus/Image/Lookup table/lookuptables_plg.py b/imagepy/menus/Image/Lookup table/lookuptables_plg.py index 208b9b0e..715cc1ce 100644 --- a/imagepy/menus/Image/Lookup table/lookuptables_plg.py +++ b/imagepy/menus/Image/Lookup table/lookuptables_plg.py @@ -3,12 +3,13 @@ Created on Mon Oct 17 21:13:42 2016 @author: yxl """ -from imagepy import IPy +from imagepy import IPy, root_dir import numpy as np from imagepy.core import ImagePlus from imagepy.ui.canvasframe import CanvasFrame from imagepy.core.engine import Free from imagepy.core.manager import ColorManager +import os, glob class Plugin(Free): def __init__(self, key): @@ -36,7 +37,9 @@ def run(self, para = None): def __call__(self): return self -plgs = [Plugin(i) for i in list(ColorManager.luts.keys())] +fs = glob.glob(os.path.join(root_dir,'data/luts/*.lut')) +plgs = [Plugin(i) for i in [os.path.split(f)[-1][:-4] for f in fs]] + for i in range(len(plgs)): if plgs[i].title == 'Grays': plgs.insert(0, plgs.pop(i)) From 10b590ea9e8445b4f84e49e85514fe7f2a1de8af Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 9 Mar 2019 12:18:25 +0800 Subject: [PATCH 114/343] simplize skeleton --- .../Analysis/Skeleton Network/graph_plgs.py | 44 +++++++++++++- .../Kit3D/Analysis 3D/regionprops3d_plgs.py | 15 +++-- .../menus/Kit3D/Network 3D/toolkit3d_plgs.py | 30 +++++++++- imagepy/menus/Plugins/Games/lifegame_plg.py | 60 +++++++++++++++++++ imagepy/tools/Standard/painter_tol.py | 6 +- 5 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 imagepy/menus/Plugins/Games/lifegame_plg.py diff --git a/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py b/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py index ac8ba2cb..fca46693 100644 --- a/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py +++ b/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py @@ -1,6 +1,7 @@ from imagepy.core.engine import Filter, Simple from imagepy.ipyalg.graph import sknw import numpy as np +from numpy.linalg import norm import networkx as nx, wx from imagepy import IPy from numba import jit @@ -104,10 +105,11 @@ def load(self, ips): if not isinstance(ips.data, nx.MultiGraph): IPy.alert("Please build graph!"); return False; + self.buf = ips.data return True; def run(self, ips, snap, img, para = None): - g = ips.data.copy() + g = ips.data = self.buf.copy() k, unit = ips.unit while True: rm = [] @@ -121,9 +123,15 @@ def run(self, ips, snap, img, para = None): img *= 0 sknw.draw_graph(img, g) + def cancel(self, ips): + if 'auto_snap' in self.note: + ips.swap() + ips.update() + ips.data = self.buf + class RemoveIsolate(Filter): title = 'Remove Isolate Node' - note = ['all'] + note = ['all', 'not_slice', 'not_channel', 'auto_snap'] def load(self, ips): if not isinstance(ips.data, nx.MultiGraph): @@ -137,6 +145,36 @@ def run(self, ips, snap, img, para = None): if len(g[n])==0: g.remove_node(n) img *= 0 sknw.draw_graph(img, g) + ips.mark = Mark(ips.data) + +class Remove2Node(Simple): + title = 'Remove 2Path Node' + note = ['all'] + + def load(self, ips): + if not isinstance(ips.data, nx.MultiGraph): + IPy.alert("Please build graph!"); + return False; + return True; + + def run(self, ips, imgs, para = None): + g = ips.data + for n in g.nodes(): + if len(g[n])!=2 or n in g[n]: continue + (k1, e1), (k2, e2) = g[n].items() + if isinstance(g, nx.MultiGraph): + if len(e1)!=1 or len(e2)!=1: continue + e1, e2 = e1[0], e2[0] + l1, l2 = e1['pts'], e2['pts'] + d1 = norm(l1[0]-g.node[n]['o']) > norm(l1[-1]-g.node[n]['o']) + d2 = norm(l2[0]-g.node[n]['o']) < norm(l2[-1]-g.node[n]['o']) + pts = np.vstack((l1[::[-1,1][d1]], l2[::[-1,1][d2]])) + l = np.linalg.norm(pts[1:]-pts[:-1], axis=1).sum() + g.remove_node(n) + g.add_edge(k1, k2, pts=pts, weight=l) + ips.img[:] = 0 + sknw.draw_graph(ips.img, g) + ips.mark = Mark(ips.data) @jit def floodfill(img, x, y): @@ -204,4 +242,4 @@ def run(self, ips, imgs, para = None): -plgs = [BuildGraph, Statistic, Sumerise, '-', RemoveIsolate, CutBranch, CutROI, '-', ShortestPath] \ No newline at end of file +plgs = [BuildGraph, Statistic, Sumerise, '-', RemoveIsolate, Remove2Node, CutBranch, CutROI, '-', ShortestPath] \ No newline at end of file diff --git a/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py b/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py index 4fbb743d..f458bb18 100644 --- a/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py +++ b/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py @@ -16,7 +16,7 @@ class RegionLabel(Simple): #process def run(self, ips, imgs, para = None): - buf = imgs.astype(np.uint16) + buf = imgs.astype(np.int32) strc = generate_binary_structure(3, 1 if para['con']=='4-connect' else 2) label(imgs, strc, output=buf) IPy.show_img(buf, ips.title+'-label') @@ -27,14 +27,15 @@ class RegionCounter(Simple): note = ['8-bit', '16-bit', 'stack3d'] para = {'con':'8-connect', 'center':True, 'extent':False, 'vol':True, - 'ed':False, 'holes':False, 'fa':False} + 'ed':False, 'holes':False, 'fa':False, 'cov':False} view = [(list, 'con', ['4-connect', '8-connect'], str, 'conection', 'pix'), ('lab', None, '========= indecate ========='), (bool, 'center', 'center'), (bool, 'vol', 'volume'), (bool, 'extent', 'extent'), - (bool, 'ed', 'equivalent diameter')] + (bool, 'ed', 'equivalent diameter'), + (bool, 'cov', 'eigen values')] #process def run(self, ips, imgs, para = None): @@ -46,10 +47,10 @@ def run(self, ips, imgs, para = None): if para['extent']:titles.extend(['Min-Z','Min-Y','Min-X','Max-Z','Max-Y','Max-X']) if para['ed']:titles.extend(['Diameter']) if para['fa']:titles.extend(['FilledArea']) + if para['cov']:titles.extend(['Axis1', 'Axis2', 'Axis3']) - buf = imgs.astype(np.uint32) strc = generate_binary_structure(3, 1 if para['con']=='4-connect' else 2) - label(imgs, strc, output=buf) + buf, n = label(imgs, strc, output=np.uint32) ls = regionprops(buf) dt = [range(len(ls))] @@ -68,6 +69,10 @@ def run(self, ips, imgs, para = None): dt.append([round(i.equivalent_diameter*k, 1) for i in ls]) if para['fa']: dt.append([i.filled_area*k**3 for i in ls]) + if para['cov']: + ites = np.array([i.inertia_tensor_eigvals for i in ls]) + rst = np.sqrt(np.clip(ites.sum(axis=1)//2-ites.T, 0, 1e10)) * 4 + for i in rst[::-1]: dt.append(np.abs(i)) IPy.show_table(pd.DataFrame(list(zip(*dt)), columns=titles), ips.title+'-region') # center, area, l, extent, cov diff --git a/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py b/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py index dbb32c0e..77e53fb0 100644 --- a/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py +++ b/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py @@ -283,4 +283,32 @@ def run(self, ips, imgs, para = None): imgs *= 0 sknw.draw_graph(imgs, g) -plgs = [Skeleton3D, BuildGraph, '-', CutBranch, RemoveIsolate, '-', Statistic, Sumerise, '-', Show3DGraph, Show3DGraphR] \ No newline at end of file +class Remove2Node(Simple): + title = 'Remove 2Path Node 3D' + note = ['all'] + + def load(self, ips): + if not isinstance(ips.data, nx.MultiGraph): + IPy.alert("Please build graph!"); + return False; + return True; + + def run(self, ips, imgs, para = None): + g = ips.data + for n in g.nodes(): + if len(g[n])!=2 or n in g[n]: continue + (k1, e1), (k2, e2) = g[n].items() + if isinstance(g, nx.MultiGraph): + if len(e1)!=1 or len(e2)!=1: continue + e1, e2 = e1[0], e2[0] + l1, l2 = e1['pts'], e2['pts'] + d1 = norm(l1[0]-g.node[n]['o']) > norm(l1[-1]-g.node[n]['o']) + d2 = norm(l2[0]-g.node[n]['o']) < norm(l2[-1]-g.node[n]['o']) + pts = np.vstack((l1[::[-1,1][d1]], l2[::[-1,1][d2]])) + l = np.linalg.norm(pts[1:]-pts[:-1], axis=1).sum() + g.remove_node(n) + g.add_edge(k1, k2, pts=pts, weight=l) + imgs *= 0 + sknw.draw_graph(imgs, g) + +plgs = [Skeleton3D, BuildGraph, '-', CutBranch, RemoveIsolate, Remove2Node, '-', Statistic, Sumerise, '-', Show3DGraph, Show3DGraphR] \ No newline at end of file diff --git a/imagepy/menus/Plugins/Games/lifegame_plg.py b/imagepy/menus/Plugins/Games/lifegame_plg.py new file mode 100644 index 00000000..40b076ec --- /dev/null +++ b/imagepy/menus/Plugins/Games/lifegame_plg.py @@ -0,0 +1,60 @@ +import numpy as np +from imagepy import IPy +from imagepy.core import ImagePlus +from imagepy.core.engine import Free, Tool +from scipy.ndimage import label +from scipy.signal import convolve2d + +def generate(scr, size): + row, col = scr.shape + arr = np.ones((row*(size)+1, col*(size)+1), dtype=np.uint8) + xs, ys = np.where(arr[:-1,:-1]) + arr[xs, ys] = scr[xs//size, ys//size] + arr[::size,:] = 2; arr[:,::size] = 2 + return np.array([128,255,0], dtype=np.uint8)[arr] + +def getscr(arr, size): return arr[1::size, 1::size]//255 + +def run(scr): + pad = np.pad(scr, 1, 'constant') + core = np.array([[1,1,1],[1,9,1],[1,1,1]]) + cov = convolve2d(pad, core, 'valid') + lut = [0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0] + return np.array(lut, dtype=np.uint8)[cov.astype(np.uint8)] + +class Painter(Tool): + def __init__(self, size): self.size = size + title = 'Life Painter' + + def mouse_down(self, ips, x, y, btn, **key): + if btn == 1: + r, c = int(y), int(x) + if r<0 or r>ips.img.shape[0]: return + if c<0 or c>ips.img.shape[1]: return + if ips.img[r,c] == 0: return + lab, n = label(ips.img>0) + ips.img[lab==lab[r,c]] = (128,255)[ips.img[r,c]!=255] + ips.update() + if btn == 3: + img = getscr(ips.img, self.size) + for i in range(len(ips.imgs)): + ips.imgs[i][:] = generate(img, self.size) + img = run(img) + IPy.alert('Complete!') + +class Plugin(Free): + title = 'Game Of Life' + para = {'name':'Game01','width':15, 'height':15, 'size':30,'slice':30} + view = [(str, 'name', 'name', ''), + (int, 'width', (1,2048), 0, 'width', 'pix'), + (int, 'height', (1,2048), 0, 'height', 'pix'), + (int, 'size', (10, 50), 0, 'size', ''), + (int, 'slice', (1,100), 0, 'slice', '')] + + #process + def run(self, para = None): + first = generate(np.zeros((para['height'], para['width'])), para['size']) + imgs = [first.copy() for i in range(para['slice'])] + ips = ImagePlus(imgs, para['name']) + ips.tool = Painter(para['size']) + IPy.show_ips(ips) \ No newline at end of file diff --git a/imagepy/tools/Standard/painter_tol.py b/imagepy/tools/Standard/painter_tol.py index f27c1679..c72776b2 100644 --- a/imagepy/tools/Standard/painter_tol.py +++ b/imagepy/tools/Standard/painter_tol.py @@ -20,17 +20,17 @@ def __init__(self): def mouse_down(self, ips, x, y, btn, **key): self.sta = 1 - self.paint.set_curpt(x,y) + self.paint.set_curpt(int(x), int(y)) ips.snapshot() def mouse_up(self, ips, x, y, btn, **key): - self.paint.lineto(ips.img,x,y, self.para['width']) + self.paint.lineto(ips.img, int(x), int(y), self.para['width']) ips.update() self.sta = 0 def mouse_move(self, ips, x, y, btn, **key): if self.sta==0:return - self.paint.lineto(ips.img,x,y, self.para['width']) + self.paint.lineto(ips.img, int(x), int(y), self.para['width']) ips.update() def mouse_wheel(self, ips, x, y, d, **key):pass \ No newline at end of file From c4eefb8619a31cffbdb6379b2f1b62e2e88d5f5b Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 10 Mar 2019 04:56:08 +0800 Subject: [PATCH 115/343] myvi --- imagepy/core/myvi/canvas3d.py | 53 ++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/imagepy/core/myvi/canvas3d.py b/imagepy/core/myvi/canvas3d.py index 8890c401..04541961 100644 --- a/imagepy/core/myvi/canvas3d.py +++ b/imagepy/core/myvi/canvas3d.py @@ -1,4 +1,4 @@ -import sys +import sys, platform import moderngl import numpy as np import wx, math @@ -8,16 +8,13 @@ from wx.lib.pubsub import pub from .util import * -#---------------------------------------------------------------------- -from wx.glcanvas import WX_GL_DEPTH_SIZE -attribs=[WX_GL_DEPTH_SIZE,32,0,0]; - class Canvas3D(glcanvas.GLCanvas): def __init__(self, parent, manager=None): - attribList = attribs = (glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24) + attribList = attribs = (glcanvas.WX_GL_CORE_PROFILE, glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24) glcanvas.GLCanvas.__init__(self, parent, -1, attribList=attribList) self.init = False self.context = glcanvas.GLContext(self) + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) self.manager = self.manager = Manager() if manager is None else manager self.size = None @@ -108,6 +105,11 @@ def OnMouseWheel(self, evt): self.Refresh(False) #self.update() +def make_bitmap(bmp): + img = bmp.ConvertToImage() + img.Resize((20, 20), (2, 2)) + return img.ConvertToBitmap() + class Viewer3D(wx.Panel): def __init__( self, parent, manager=None): wx.Panel.__init__(self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.TAB_TRAVERSAL ) @@ -121,26 +123,27 @@ def __init__( self, parent, manager=None): #self.SetIcon(wx.Icon('data/logo.ico', wx.BITMAP_TYPE_ICO)) - self.btn_x = wx.BitmapButton( self.toolbar, wx.ID_ANY, wx.Bitmap( osp.join(root, 'imgs/x-axis.png'), wx.BITMAP_TYPE_ANY ), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) - tsizer.Add( self.btn_x, 0, wx.ALL, 1 ) - self.btn_y = wx.BitmapButton( self.toolbar, wx.ID_ANY, wx.Bitmap( osp.join(root, 'imgs/y-axis.png'), wx.BITMAP_TYPE_ANY ), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) - tsizer.Add( self.btn_y, 0, wx.ALL, 1 ) - self.btn_z = wx.BitmapButton( self.toolbar, wx.ID_ANY, wx.Bitmap( osp.join(root, 'imgs/z-axis.png'), wx.BITMAP_TYPE_ANY ), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) - tsizer.Add( self.btn_z, 0, wx.ALL, 1 ) + self.btn_x = wx.BitmapButton( self.toolbar, wx.ID_ANY, make_bitmap(wx.Bitmap( osp.join(root, 'imgs/x-axis.png'), wx.BITMAP_TYPE_ANY )), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) + tsizer.Add( self.btn_x, 0, wx.ALIGN_CENTER|wx.ALL, 0 ) + self.btn_y = wx.BitmapButton( self.toolbar, wx.ID_ANY, make_bitmap(wx.Bitmap( osp.join(root, 'imgs/y-axis.png'), wx.BITMAP_TYPE_ANY )), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) + tsizer.Add( self.btn_y, 0, wx.ALIGN_CENTER|wx.ALL, 0 ) + self.btn_z = wx.BitmapButton( self.toolbar, wx.ID_ANY, make_bitmap(wx.Bitmap( osp.join(root, 'imgs/z-axis.png'), wx.BITMAP_TYPE_ANY )), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) + tsizer.Add( self.btn_z, 0, wx.ALIGN_CENTER|wx.ALL, 0 ) tsizer.Add(wx.StaticLine( self.toolbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL), 0, wx.ALL|wx.EXPAND, 2 ) - self.btn_pers = wx.BitmapButton( self.toolbar, wx.ID_ANY, wx.Bitmap( osp.join(root, 'imgs/isometric.png'), wx.BITMAP_TYPE_ANY ), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) - tsizer.Add( self.btn_pers, 0, wx.ALL, 1 ) - self.btn_orth = wx.BitmapButton( self.toolbar, wx.ID_ANY, wx.Bitmap( osp.join(root, 'imgs/parallel.png'), wx.BITMAP_TYPE_ANY ), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) - tsizer.Add( self.btn_orth, 0, wx.ALL, 1 ) + self.btn_pers = wx.BitmapButton( self.toolbar, wx.ID_ANY, make_bitmap(wx.Bitmap( osp.join(root, 'imgs/isometric.png'), wx.BITMAP_TYPE_ANY )), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) + tsizer.Add( self.btn_pers, 0, wx.ALIGN_CENTER|wx.ALL, 0 ) + self.btn_orth = wx.BitmapButton( self.toolbar, wx.ID_ANY, make_bitmap(wx.Bitmap( osp.join(root, 'imgs/parallel.png'), wx.BITMAP_TYPE_ANY )), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) + tsizer.Add( self.btn_orth, 0, wx.ALIGN_CENTER|wx.ALL, 0 ) tsizer.Add(wx.StaticLine( self.toolbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL), 0, wx.ALL|wx.EXPAND, 2 ) - self.btn_open = wx.BitmapButton( self.toolbar, wx.ID_ANY, wx.Bitmap(osp.join(root, 'imgs/open.png'), wx.BITMAP_TYPE_ANY ), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) - tsizer.Add( self.btn_open, 0, wx.ALL, 1 ) - self.btn_stl = wx.BitmapButton( self.toolbar, wx.ID_ANY, wx.Bitmap(osp.join(root, 'imgs/stl.png'), wx.BITMAP_TYPE_ANY ), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) - tsizer.Add( self.btn_stl, 0, wx.ALL, 1 ) - - self.btn_color = wx.ColourPickerCtrl( self.toolbar, wx.ID_ANY, wx.Colour( 128, 128, 128 ), wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE ) - tsizer.Add( self.btn_color, 0, wx.ALIGN_CENTER|wx.ALL, 1 ) + self.btn_open = wx.BitmapButton( self.toolbar, wx.ID_ANY, make_bitmap(wx.Bitmap(osp.join(root, 'imgs/open.png'), wx.BITMAP_TYPE_ANY )), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) + tsizer.Add( self.btn_open, 0, wx.ALIGN_CENTER|wx.ALL, 0 ) + self.btn_stl = wx.BitmapButton( self.toolbar, wx.ID_ANY, make_bitmap(wx.Bitmap(osp.join(root, 'imgs/stl.png'), wx.BITMAP_TYPE_ANY )), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) + tsizer.Add( self.btn_stl, 0, wx.ALIGN_CENTER|wx.ALL, 0 ) + #pan = wx.Panel(self.toolbar, size=(50, 50)) + self.btn_color = wx.ColourPickerCtrl( self.toolbar, wx.ID_ANY, wx.Colour( 128, 128, 128 ), wx.DefaultPosition, [(33, 38), (-1, -1)][platform.system() in ['Windows', 'Linux']], wx.CLRP_DEFAULT_STYLE ) + tsizer.Add( self.btn_color, 0, wx.ALIGN_CENTER|wx.ALL|(0, wx.EXPAND)[platform.system() in ['Windows', 'Linux']], 0 ) self.toolbar.SetSizer( tsizer ) + tsizer.Layout() self.settingbar = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) ssizer = wx.BoxSizer( wx.HORIZONTAL ) @@ -152,7 +155,7 @@ def __init__( self, parent, manager=None): cho_objChoices = ['None'] self.cho_obj = wx.Choice( self.settingbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, cho_objChoices, 0 ) self.cho_obj.SetSelection( 0 ) - ssizer.Add( self.cho_obj, 0, wx.ALL, 1 ) + ssizer.Add( self.cho_obj, 0, wx.ALIGN_CENTER|wx.ALL, 1 ) self.chk_visible = wx.CheckBox( self.settingbar, wx.ID_ANY, u"visible", wx.DefaultPosition, wx.DefaultSize, 0 ) ssizer.Add( self.chk_visible, 0, wx.ALIGN_CENTER|wx.LEFT, 10 ) @@ -175,7 +178,7 @@ def __init__( self, parent, manager=None): cho_objChoices = ['mesh', 'grid'] self.cho_mode = wx.Choice( self.settingbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, cho_objChoices, 0 ) self.cho_mode.SetSelection( 0 ) - ssizer.Add( self.cho_mode, 0, wx.ALL, 1 ) + ssizer.Add( self.cho_mode, 0, wx.ALIGN_CENTER|wx.ALL, 1 ) sizer.Add( self.toolbar, 0, wx.EXPAND |wx.ALL, 0 ) sizer.Add( self.canvas, 1, wx.EXPAND |wx.ALL, 0) From aadbd6dbfece60957c630019a7a0434d9ab6bb26 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 11 Mar 2019 16:06:49 +0800 Subject: [PATCH 116/343] myvi update --- imagepy/core/myvi/canvas3d.py | 38 ++++++++++++++++++- imagepy/core/myvi/manager.py | 29 +++++++++----- .../Kit3D/Analysis 3D/regionprops3d_plgs.py | 12 +++++- 3 files changed, 67 insertions(+), 12 deletions(-) diff --git a/imagepy/core/myvi/canvas3d.py b/imagepy/core/myvi/canvas3d.py index 04541961..8aa2a7f2 100644 --- a/imagepy/core/myvi/canvas3d.py +++ b/imagepy/core/myvi/canvas3d.py @@ -24,6 +24,8 @@ def __init__(self, parent, manager=None): self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseDown) self.Bind(wx.EVT_LEFT_UP, self.OnMouseUp) + self.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseDown) + self.Bind(wx.EVT_RIGHT_UP, self.OnMouseUp) self.Bind(wx.EVT_MOTION, self.OnMouseMotion) self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel) self.lastx, self.lasty = None, None @@ -73,12 +75,24 @@ def OnMouseMotion(self, evt): x, y = evt.GetPosition() dx, dy = x-self.lastx, y-self.lasty self.lastx, self.lasty = x, y - #self.manager.h -= dx/200 angx = self.manager.angx - dx/200 angy = self.manager.angy + dy/200 - #print('ang', angx, angy) self.manager.set_pers(angx=angx, angy=angy) self.Refresh(False) + if evt.Dragging() and evt.RightIsDown(): + light = self.manager.light + x, y = evt.GetPosition() + dx, dy = x-self.lastx, y-self.lasty + self.lastx, self.lasty = x, y + angx, angy = dx/200, dy/200 + vx, vy, vz = self.manager.light + ay = math.asin(vz/math.sqrt(vx**2+vy**2+vz**2))-angy + xx = math.cos(angx)*vx - math.sin(angx)*vy + yy = math.sin(angx)*vx + math.cos(angx)*vy + ay = max(min(math.pi/2-1e-4, ay), -math.pi/2+1e-4) + zz, k = math.sin(ay), math.cos(ay)/math.sqrt(vx**2+vy**2) + self.manager.set_light((xx*k, yy*k, zz)) + self.Refresh(False) def save_bitmap(self, path): context = wx.ClientDC( self ) @@ -142,6 +156,14 @@ def __init__( self, parent, manager=None): #pan = wx.Panel(self.toolbar, size=(50, 50)) self.btn_color = wx.ColourPickerCtrl( self.toolbar, wx.ID_ANY, wx.Colour( 128, 128, 128 ), wx.DefaultPosition, [(33, 38), (-1, -1)][platform.system() in ['Windows', 'Linux']], wx.CLRP_DEFAULT_STYLE ) tsizer.Add( self.btn_color, 0, wx.ALIGN_CENTER|wx.ALL|(0, wx.EXPAND)[platform.system() in ['Windows', 'Linux']], 0 ) + tsizer.Add(wx.StaticLine( self.toolbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL), 0, wx.ALL|wx.EXPAND, 2 ) + self.cho_light = wx.Choice( self.toolbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, ['force light', 'normal light', 'weak light', 'off light'], 0 ) + self.cho_light.SetSelection( 1 ) + tsizer.Add( self.cho_light, 0, wx.ALIGN_CENTER|wx.ALL, 1 ) + self.cho_bg = wx.Choice( self.toolbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, ['force scatter', 'normal scatter', 'weak scatter', 'off scatter'], 0 ) + self.cho_bg.SetSelection( 1 ) + tsizer.Add( self.cho_bg, 0, wx.ALIGN_CENTER|wx.ALL, 1 ) + self.toolbar.SetSizer( tsizer ) tsizer.Layout() @@ -199,6 +221,8 @@ def __init__( self, parent, manager=None): self.cho_obj.Bind( wx.EVT_CHOICE, self.on_select ) self.cho_mode.Bind( wx.EVT_CHOICE, self.on_mode ) + self.cho_light.Bind( wx.EVT_CHOICE, self.on_light ) + self.cho_bg.Bind( wx.EVT_CHOICE, self.on_bg ) self.chk_visible.Bind( wx.EVT_CHECKBOX, self.on_visible) self.sli_blend.Bind( wx.EVT_SCROLL, self.on_blend ) self.col_color.Bind( wx.EVT_COLOURPICKER_CHANGED, self.on_color ) @@ -229,6 +253,16 @@ def on_bgcolor(self, event): self.canvas.manager.set_background(c) self.canvas.Refresh(False) + def on_bg(self, event): + scatter = 3 - self.cho_bg.GetSelection() + self.canvas.manager.set_bright_scatter(scatter=scatter/3) + self.canvas.Refresh(False) + + def on_light(self, event): + bright = 3 - self.cho_light.GetSelection() + self.canvas.manager.set_bright_scatter(bright=bright/3) + self.canvas.Refresh(False) + def on_save(self, evt): dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} filt = 'PNG files (*.png)|*.png' diff --git a/imagepy/core/myvi/manager.py b/imagepy/core/myvi/manager.py index 3f9dccbb..f9bbfcb6 100644 --- a/imagepy/core/myvi/manager.py +++ b/imagepy/core/myvi/manager.py @@ -85,13 +85,15 @@ def set_style(self, mode=None, blend=None, color=None, visible=None): self.vbo.write(self.buf.tobytes()) self.color = color if isinstance(color, tuple) else (0,0,0) - def draw(self, mvp): + def draw(self, mvp, light, bright, scatter): if not self.visible: return self.ctx.line_width = self.width mvp = np.dot(*mvp) self.prog['Mvp'].write(mvp.astype(np.float32).tobytes()) self.prog['blend'].value = self.blend - + self.prog['scatter'].value = scatter + self.prog['light'].value = tuple(light) + self.prog['bright'].value = bright self.vao.render({'mesh':moderngl.TRIANGLES, 'grid':moderngl.LINES}[self.mode]) class MarkText: @@ -114,7 +116,7 @@ def set_style(self, mode=None, blend=None, color=None, visible=None): if not visible is None: self.visible = visible if not color is None: self.color = color - def draw(self, mvp): + def draw(self, mvp, light, bright, scatter): if not self.visible: return self.ctx.line_width = 2 self.prog['mv'].write(mvp[0].astype(np.float32).tobytes()) @@ -129,6 +131,8 @@ def __init__(self): self.ratio, self.dial = 1.0, 1.0 self.pers, self.center = True, (0,0,0) self.background = 0.4, 0.4, 0.4 + self.light = (1,0,0) + self.bright, self.scatter = 0.66, 0.66 self.objs = {} self.ctx = None @@ -151,13 +155,15 @@ def on_ctx(self): ''', fragment_shader=''' #version 330 - uniform vec3 light = vec3(1,1,0.8); - uniform float blend = 0.1; + uniform vec3 light; + uniform float blend; + uniform float scatter; + uniform float bright; in vec3 f_norm; in vec3 f_color; out vec4 color; void main() { - float d = clamp((dot(light, f_norm)+1)*0.5, 0, 1); + float d = clamp(dot(light, f_norm)*bright+scatter, 0, 1); color = vec4(f_color*d, blend); } ''' @@ -215,7 +221,7 @@ def draw(self): self.ctx.enable(moderngl.DEPTH_TEST) #self.ctx.enable(ModernGL.CULL_FACE) self.ctx.enable(moderngl.BLEND) - for i in self.objs.values(): i.draw(self.mvp) + for i in self.objs.values(): i.draw(self.mvp, self.light, self.bright, self.scatter) def count_box(self): minb = np.array([i.box[0] for i in self.objs.values() if not i.box is None]).min(axis=0) @@ -237,8 +243,13 @@ def set_viewport(self, x, y, width, height): self.ctx.viewport = (x, y, width, height) self.ratio = width*1.0/height - def set_background(self, rgb): - self.background = rgb + def set_background(self, rgb): self.background = rgb + + def set_light(self, light): self.light = light + + def set_bright_scatter(self, bright=None, scatter=None): + if not bright is None: self.bright = bright + if not scatter is None: self.scatter = scatter def reset(self, fovy=45, angx=0, angy=0): self.fovy, self.angx, self.angy = fovy, angx, angy diff --git a/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py b/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py index f458bb18..5f3ea574 100644 --- a/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py +++ b/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py @@ -2,6 +2,8 @@ import numpy as np from imagepy.core.engine import Simple, Filter from scipy.ndimage import label, generate_binary_structure +from skimage.measure import marching_cubes_lewiner, mesh_surface_area +from skimage.segmentation import find_boundaries from skimage.measure import regionprops from numpy.linalg import norm import pandas as pd @@ -27,12 +29,13 @@ class RegionCounter(Simple): note = ['8-bit', '16-bit', 'stack3d'] para = {'con':'8-connect', 'center':True, 'extent':False, 'vol':True, - 'ed':False, 'holes':False, 'fa':False, 'cov':False} + 'ed':False, 'holes':False, 'fa':False, 'cov':False, 'surf':True} view = [(list, 'con', ['4-connect', '8-connect'], str, 'conection', 'pix'), ('lab', None, '========= indecate ========='), (bool, 'center', 'center'), (bool, 'vol', 'volume'), + (bool, 'surf', 'surface area'), (bool, 'extent', 'extent'), (bool, 'ed', 'equivalent diameter'), (bool, 'cov', 'eigen values')] @@ -43,6 +46,7 @@ def run(self, ips, imgs, para = None): titles = ['ID'] if para['center']:titles.extend(['Center-X','Center-Y','Center-Z']) + if para['surf']:titles.append('Surface') if para['vol']:titles.append('Volume') if para['extent']:titles.extend(['Min-Z','Min-Y','Min-X','Max-Z','Max-Y','Max-X']) if para['ed']:titles.extend(['Diameter']) @@ -60,6 +64,12 @@ def run(self, ips, imgs, para = None): dt.append([round(i.centroid[1]*k,1) for i in ls]) dt.append([round(i.centroid[0]*k,1) for i in ls]) dt.append([round(i.centroid[2]*k,1) for i in ls]) + if para['surf']: + buf[find_boundaries(buf, mode='outer')] = 0 + vts, fs, ns, cs = marching_cubes_lewiner(buf, level=0) + lst = [[] for i in range(n+1)] + for i in fs: lst[int(cs[i[0]])].append(i) + dt.append([0 if len(i)==0 else mesh_surface_area(vts, np.array(i))*k**2 for i in lst][1:]) if para['vol']: dt.append([i.area*k**3 for i in ls]) if para['extent']: From ed8a2fe316e29fdd10edb2ef0bb8498c18c86219 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 14 Mar 2019 09:11:08 +0800 Subject: [PATCH 117/343] fft --- imagepy/core/engine/filter.py | 4 +- imagepy/core/wraper/imageplus.py | 1 + imagepy/menus/Image/Type/convert_plg.py | 2 +- imagepy/menus/Process/FFT/__init__.py | 0 imagepy/menus/Process/FFT/fft_plgs.py | 73 +++++++++++++++++++ .../menus/Process/Hydrology/hydrology_plgs.py | 6 +- imagepy/menus/Process/__init__.py | 2 +- imagepy/ui/canvas.py | 2 +- 8 files changed, 82 insertions(+), 8 deletions(-) create mode 100644 imagepy/menus/Process/FFT/__init__.py create mode 100644 imagepy/menus/Process/FFT/fft_plgs.py diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index 023ded6b..2d1beec5 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -30,7 +30,7 @@ def process_one(plg, ips, src, img, para, callafter=None): TaskManager.add(plg) start = time() transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16) - transfloat = '2float' in plg.note and not ips.dtype in (np.float32, np.float64) + transfloat = '2float' in plg.note and not ips.dtype in (np.complex128, np.float32, np.float64) if transint: buf = img.astype(np.int32) src = src.astype(np.int32) @@ -53,7 +53,7 @@ def process_stack(plg, ips, src, imgs, para, callafter=None): TaskManager.add(plg) start = time() transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16) - transfloat = '2float' in plg.note and not ips.dtype in (np.float32, np.float64) + transfloat = '2float' in plg.note and not ips.dtype in (np.complex128, np.float32, np.float64) if transint: buf = imgs[0].astype(np.int32) src = src.astype(np.int32) diff --git a/imagepy/core/wraper/imageplus.py b/imagepy/core/wraper/imageplus.py index 338a1c03..e930c651 100644 --- a/imagepy/core/wraper/imageplus.py +++ b/imagepy/core/wraper/imageplus.py @@ -8,6 +8,7 @@ def get_img_type(imgs): if imgs[0].dtype == np.int32:return '32-int' if imgs[0].dtype == np.float32:return '32-float' if imgs[0].dtype == np.float64:return '64-float' + if imgs[0].dtype == np.complex128:return 'complex' class ImagePlus: """ImagePlus: a class to make operation more flexible """ diff --git a/imagepy/menus/Image/Type/convert_plg.py b/imagepy/menus/Image/Type/convert_plg.py index f9469414..15393dbd 100644 --- a/imagepy/menus/Image/Type/convert_plg.py +++ b/imagepy/menus/Image/Type/convert_plg.py @@ -163,5 +163,5 @@ def run(self, ips, imgs, para = None): k = 255.0/(max(1e-10, maxv-minv)) img64.append(imgs[i].astype(np.float64)) ips.set_imgs(img64) - + plgs = [To8bit, ToRGB, '-', ToUint16, ToInt32, ToFloat32, ToFloat64] \ No newline at end of file diff --git a/imagepy/menus/Process/FFT/__init__.py b/imagepy/menus/Process/FFT/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/menus/Process/FFT/fft_plgs.py b/imagepy/menus/Process/FFT/fft_plgs.py new file mode 100644 index 00000000..a1a7c09b --- /dev/null +++ b/imagepy/menus/Process/FFT/fft_plgs.py @@ -0,0 +1,73 @@ +import numpy as np +from imagepy.core.engine import Simple, Filter +from numpy.fft import fft2, ifft2, fftshift, ifftshift +from imagepy import IPy + +class FFT(Simple): + title = 'FFT' + note = ['8-bit', '16-bit', 'int', 'float'] + para = {'shift':True, 'slice':False} + view = [(bool, 'shift', 'zero center'), + (bool, 'slice', 'slices')] + + def run(self, ips, imgs, para = None): + if not para['slice']: imgs = [ips.img] + shift = fftshift if para['shift'] else lambda x:x + rst = [] + for i in range(len(imgs)): + rst.append(shift(fft2(imgs[i]))) + self.progress(i, len(imgs)) + IPy.show_img(rst, '%s-fft'%ips.title) + +class LogPower(Simple): + title = 'Log Power' + note = ['complex'] + para = {'slice':False, 'type':'float', 'log':2.718} + view = [(float, 'log', (2,30), 3, 'log', ''), + (list, 'type', ['uint8', 'int', 'float'], str, 'type', ''), + (bool, 'slice', 'slices')] + + def run(self, ips, imgs, para = None): + if not para['slice']: imgs = [ips.img] + tp = {'uint8':np.uint8, 'int':np.int32, 'float':np.float32} + rst, tp = [], tp[para['type']] + for i in range(len(imgs)): + zs = np.log(np.abs(imgs[i])) + zs /= np.log(para['log']) + rst.append(zs.astype(tp)) + self.progress(i, len(imgs)) + IPy.show_img(rst, '%s-fft'%ips.title) + +class IFFT(Simple): + title = 'Inverse FFT' + note = ['complex'] + para = {'shift':True, 'slice':False, 'type':'float'} + view = [(list, 'type', ['uint8', 'int', 'float'], str, 'type', ''), + (bool, 'shift', 'zero center'), + (bool, 'slice', 'slices')] + + def run(self, ips, imgs, para = None): + if not para['slice']: imgs = [ips.img] + shift = ifftshift if para['shift'] else lambda x:x + tp = {'uint8':np.uint8, 'int':np.int32, 'float':np.float32} + rst, tp = [], tp[para['type']] + for i in range(len(imgs)): + rst.append(ifft2(shift(ips.img)).astype(tp)) + self.progress(i, len(imgs)) + IPy.show_img(rst, '%s-ifft'%ips.title) + +class Shift(Filter): + title = 'Zero Center' + note = ['complex'] + + def run(self, ips, snap, img, para = None): + return fftshift(img) + +class IShift(Filter): + title = 'Zero Edge' + note = ['complex'] + + def run(self, ips, snap, img, para = None): + return ifftshift(img) + +plgs = [FFT, IFFT, '-', Shift, IShift, LogPower] \ No newline at end of file diff --git a/imagepy/menus/Process/Hydrology/hydrology_plgs.py b/imagepy/menus/Process/Hydrology/hydrology_plgs.py index 79d79115..19a362a2 100644 --- a/imagepy/menus/Process/Hydrology/hydrology_plgs.py +++ b/imagepy/menus/Process/Hydrology/hydrology_plgs.py @@ -99,11 +99,11 @@ def run(self, ips, snap, img, para = None): class ARidge(Filter): title = 'Active Ridge' - note = ['8-bit', 'not_slice', 'auto_snap', 'not_channel'] + note = ['8-bit', 'not_slice', 'auto_snap', 'not_channel', 'req_roi'] para = {'sigma':1.0, 'ud':True, 'type':'white line'} - view = [(float, (0,5), 1, 'sigma', 'sigma', 'pix'), - (list, 'type', ['white line', 'gray line', 'white line on ori'], str, 'output', ''), + view = [(float, 'sigma', (0,5), 1, 'sigma', 'pix'), + (list, 'type', ['white line', 'gray line', 'white line on ori'], str, 'output', ''), (bool, 'ud', 'ascend')] def run(self, ips, snap, img, para = None): diff --git a/imagepy/menus/Process/__init__.py b/imagepy/menus/Process/__init__.py index ce93e566..3abb3703 100644 --- a/imagepy/menus/Process/__init__.py +++ b/imagepy/menus/Process/__init__.py @@ -1 +1 @@ -catlog = ['Math', 'Binary', 'Filters', '-', 'Threshold', 'Hydrology', 'Features', 'Segment', 'repair_plg', '-', 'calculator_plg'] \ No newline at end of file +catlog = ['Math', 'Binary', 'Filters', 'FFT', '-', 'Threshold', 'Hydrology', 'Features', 'Segment', 'repair_plg', '-', 'calculator_plg'] \ No newline at end of file diff --git a/imagepy/ui/canvas.py b/imagepy/ui/canvas.py index 4ab3c1eb..f6b59ad7 100644 --- a/imagepy/ui/canvas.py +++ b/imagepy/ui/canvas.py @@ -180,8 +180,8 @@ def merge(self, img, back, M, O, mode, shape, win, lookup): if img.ndim == 2: rstarr = np.zeros(shape, dtype=img.dtype) my_transform(img, M, offset=O, output=rstarr, k=1, clip=False) + if rstarr.dtype == np.complex128: rstarr = np.abs(rstarr) rstarr = lookup(rstarr) - if img.ndim == 3: rstarr = np.zeros((win[3], win[2], 3), dtype=img.dtype) for i in range(3): From 658f97741ea9789216e20ff0c98bdfed1fa0c23f Mon Sep 17 00:00:00 2001 From: Laurent Thomas Date: Wed, 20 Mar 2019 11:40:37 +0100 Subject: [PATCH 118/343] Install Pystackreg with pip Pystackreg is missing for windows architecture on conda while with pip it works --- environment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/environment.yml b/environment.yml index 2a7a86f0..4015d660 100644 --- a/environment.yml +++ b/environment.yml @@ -7,7 +7,6 @@ dependencies: - openpyxl - pandas - pydicom - - pystackreg - pypubsub - read-roi - scikit-image @@ -18,3 +17,4 @@ dependencies: - markdown - pip: - moderngl + - pystackreg From db8345e228500ae8fd650b50f0bb15dbc20eb769 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 22 Mar 2019 14:29:38 +0800 Subject: [PATCH 119/343] networkx new --- imagepy/core/myvi/canvas3d.py | 4 ++-- imagepy/menus/Analysis/Skeleton Network/graph_plgs.py | 6 +++--- imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/imagepy/core/myvi/canvas3d.py b/imagepy/core/myvi/canvas3d.py index 8aa2a7f2..72959ca7 100644 --- a/imagepy/core/myvi/canvas3d.py +++ b/imagepy/core/myvi/canvas3d.py @@ -10,8 +10,8 @@ class Canvas3D(glcanvas.GLCanvas): def __init__(self, parent, manager=None): - attribList = attribs = (glcanvas.WX_GL_CORE_PROFILE, glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24) - glcanvas.GLCanvas.__init__(self, parent, -1, attribList=attribList) + attribList = (glcanvas.WX_GL_CORE_PROFILE, glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24) + glcanvas.GLCanvas.__init__(self, parent, -1, attribList=attribList[platform.system() == 'Windows':]) self.init = False self.context = glcanvas.GLContext(self) self.SetBackgroundStyle(wx.BG_STYLE_PAINT) diff --git a/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py b/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py index fca46693..377fea9f 100644 --- a/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py +++ b/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py @@ -115,7 +115,7 @@ def run(self, ips, snap, img, para = None): rm = [] for i in g.nodes(): if g.degree(i)!=1:continue - s,e = g.edges(i)[0] + s,e = list(g.edges(i))[0] if g[s][e][0]['weight']*k<=para['lim']: rm.append(i) g.remove_nodes_from(rm) @@ -141,7 +141,7 @@ def load(self, ips): def run(self, ips, snap, img, para = None): g = ips.data - for n in g.nodes(): + for n in list(g.nodes()): if len(g[n])==0: g.remove_node(n) img *= 0 sknw.draw_graph(img, g) @@ -159,7 +159,7 @@ def load(self, ips): def run(self, ips, imgs, para = None): g = ips.data - for n in g.nodes(): + for n in list(g.nodes()): if len(g[n])!=2 or n in g[n]: continue (k1, e1), (k2, e2) = g[n].items() if isinstance(g, nx.MultiGraph): diff --git a/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py b/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py index 77e53fb0..a92e8e7d 100644 --- a/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py +++ b/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py @@ -258,7 +258,7 @@ def run(self, ips, imgs, para = None): rm = [] for i in g.nodes(): if g.degree(i)!=1:continue - s,e = g.edges(i)[0] + s,e = list(g.edges(i))[0] if g[s][e][0]['weight']*k<=para['lim']: rm.append(i) g.remove_nodes_from(rm) @@ -278,7 +278,7 @@ def load(self, ips): def run(self, ips, imgs, para = None): g = ips.data - for n in g.nodes(): + for n in list(g.nodes()): if len(g[n])==0: g.remove_node(n) imgs *= 0 sknw.draw_graph(imgs, g) @@ -295,7 +295,7 @@ def load(self, ips): def run(self, ips, imgs, para = None): g = ips.data - for n in g.nodes(): + for n in list(g.nodes()): if len(g[n])!=2 or n in g[n]: continue (k1, e1), (k2, e2) = g[n].items() if isinstance(g, nx.MultiGraph): From 67507a01b9adc7f8e17290dfabce6dd05b4d2007 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 10 Apr 2019 22:36:37 +0800 Subject: [PATCH 120/343] npy support --- imagepy/core/roi/operator.py | 12 ------- imagepy/core/roi/roi.py | 4 +++ imagepy/core/wraper/imageplus.py | 3 +- imagepy/menus/File/Numpy/__init__.py | 0 imagepy/menus/File/Numpy/ndarray_plgs.py | 36 +++++++++++++++++++ imagepy/menus/File/__init__.py | 2 +- imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py | 2 +- 7 files changed, 43 insertions(+), 16 deletions(-) delete mode 100644 imagepy/core/roi/operator.py create mode 100644 imagepy/menus/File/Numpy/__init__.py create mode 100644 imagepy/menus/File/Numpy/ndarray_plgs.py diff --git a/imagepy/core/roi/operator.py b/imagepy/core/roi/operator.py deleted file mode 100644 index 85f6412a..00000000 --- a/imagepy/core/roi/operator.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Thu Nov 24 01:57:23 2016 -@author: yxl -""" - -def affine(body, m, o): - if isinstance(body, list): - return [affine(i, m, o) for i in body] - if isinstance(body, tuple): - return tuple(m.dot(body)+o) - diff --git a/imagepy/core/roi/roi.py b/imagepy/core/roi/roi.py index bfa03943..961b284a 100644 --- a/imagepy/core/roi/roi.py +++ b/imagepy/core/roi/roi.py @@ -5,6 +5,7 @@ """ from .convert import roi2shape, shape2roi from shapely.affinity import affine_transform +from shapely.ops import transform class ROI: def __init__(self):pass @@ -35,3 +36,6 @@ def diff(self, roi): def affine(self, m, o): mat = [m[0,0], m[0,1], m[1,0], m[1,1], o[0], o[1]] return shape2roi(affine_transform(roi2shape(self), mat)) + + def transform(self, f): + return shape2roi(transform(f, roi2shape(self))) \ No newline at end of file diff --git a/imagepy/core/wraper/imageplus.py b/imagepy/core/wraper/imageplus.py index e930c651..1131c70e 100644 --- a/imagepy/core/wraper/imageplus.py +++ b/imagepy/core/wraper/imageplus.py @@ -26,8 +26,7 @@ def __init__(self, imgs, title=None, is3d=False): self.backimg = None self.backmode = (0.5, 'Mean') self.tool = None - self.data = None - self.info = {} + self.data = {} self.unit = (1, 'pix') self.range = (0, 255) self.set_imgs(imgs) diff --git a/imagepy/menus/File/Numpy/__init__.py b/imagepy/menus/File/Numpy/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/menus/File/Numpy/ndarray_plgs.py b/imagepy/menus/File/Numpy/ndarray_plgs.py new file mode 100644 index 00000000..d2a0957b --- /dev/null +++ b/imagepy/menus/File/Numpy/ndarray_plgs.py @@ -0,0 +1,36 @@ +from imagepy.core.util import fileio +import numpy as np +from imagepy.core.manager import ReaderManager, WriterManager +from imagepy import IPy +import os + +ReaderManager.add('npy', np.load) +WriterManager.add('npy', np.save) + +class OpenFile(fileio.Reader): + title = 'Numpy Open' + filt = ['npy'] + +class SaveFile(fileio.Writer): + title = 'Numpy Save' + filt = ['npy'] + +class Open3D(fileio.Reader): + title = 'Numpy 3D Open' + filt = ['npy'] + + def run(self, para = None): + imgs = np.load(para['path']) + fp, fn = os.path.split(para['path']) + fn, fe = os.path.splitext(fn) + IPy.show_img(imgs, fn) + +class Save3D(fileio.Writer): + title = 'Numpy 3D Save' + filt = ['npy'] + note = ['all', 'stack'] + + def run(self, ips, imgs, para = None): + np.save(para['path'], imgs) + +plgs = [OpenFile, SaveFile, '-', Open3D, Save3D] \ No newline at end of file diff --git a/imagepy/menus/File/__init__.py b/imagepy/menus/File/__init__.py index 4cbca4ca..0577219f 100644 --- a/imagepy/menus/File/__init__.py +++ b/imagepy/menus/File/__init__.py @@ -1,3 +1,3 @@ ### TODO: Fixme! In this directory, many path should be corrected?! catlog = ['new_plg', '-', 'open_plg', 'save_plg', '-', 'Open Recent', 'Samples Local', 'Samples Online', - 'Samples ImageJ', '-', 'Import', 'Export', '-', 'BMP', 'JPG', 'PNG', 'TIF', 'GIF','DICOM','DAT', 'MAT', '-', 'exit_plg'] \ No newline at end of file + 'Samples ImageJ', '-', 'Import', 'Export', '-', 'BMP', 'JPG', 'PNG', 'TIF', 'GIF', 'DICOM', 'DAT', 'Numpy', 'MAT', '-', 'exit_plg'] \ No newline at end of file diff --git a/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py b/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py index c5805918..1d123cb0 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py +++ b/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py @@ -17,7 +17,7 @@ def run(self, para): class Surface2D(Simple): title = '2D Surface' - note = ['8-bit', '16-bit'] + note = ['8-bit', '16-bit', 'float'] para = {'name':'undifine', 'scale':2, 'sigma':2,'h':1} view = [(str, 'name', 'Name', ''), (int, 'scale', (1,5), 0, 'down scale', 'pix'), From 9e160561086a3d3e7cede14900be21bc402ec530 Mon Sep 17 00:00:00 2001 From: Laurent Thomas Date: Sun, 14 Apr 2019 21:51:57 +0200 Subject: [PATCH 121/343] Mention launching from the precompiled archive --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index f90ec9a8..5f918f78 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,19 @@ __OS support:windows, linux, mac, with python3.x__ you are logged in on the main display, if so, please start with pythonw -m imagepy. +### - Pre-compiled package +This is the simplest option to run ImagePy. +A precompiled archive can be downloaded from the [release tab](https://github.com/Image-Py/imagepy/releases) of the repository. +Simply unzip the archive and run the ImagePy.bat file. +This will open a command line window and open the GUI of ImagePy. + +### - Using pip +In a command-prompt type `pip install imagepy`. +On Windows you currently need to first install shapely using conda. +Once installed, ImagePy can be run by typing `python -m imagepy` in a command prompt. + + + ## Citation: [ImagePy: an open-source, Python-based and platform-independent software package for bioimage analysis](https://academic.oup.com/bioinformatics/article/34/18/3238/4989871) From 28025b3b492bb6f2d4ab1e7b37befb153968139a Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 15 Apr 2019 14:51:30 +0800 Subject: [PATCH 122/343] ipy getpath name --- imagepy/IPy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imagepy/IPy.py b/imagepy/IPy.py index cc9ca302..809b70e0 100644 --- a/imagepy/IPy.py +++ b/imagepy/IPy.py @@ -119,14 +119,14 @@ def yes_no(info, title="ImagePy Yes-No ?!"): dic = {wx.ID_YES:'yes', wx.ID_NO:'no', wx.ID_CANCEL:'cancel'} return dic[rst] -def getpath(title, filt, k, para=None): +def getpath(title, filt, k, para=None, name=''): """Get the defaultpath of the ImagePy""" from .core import manager dpath = manager.ConfigManager.get('defaultpath') if dpath ==None: dpath = root_dir # './' dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} - dialog = wx.FileDialog(curapp, title, dpath, '', filt, dic[k]) + dialog = wx.FileDialog(curapp, title, dpath, name, filt, dic[k]) rst = dialog.ShowModal() path = None if rst == wx.ID_OK: From 80b580657c89000fde2926d48719dff49564078b Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 16 Apr 2019 16:40:17 +0800 Subject: [PATCH 123/343] lines --- imagepy/core/mark/mark.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/core/mark/mark.py b/imagepy/core/mark/mark.py index 02bfa2cc..576dcec0 100644 --- a/imagepy/core/mark/mark.py +++ b/imagepy/core/mark/mark.py @@ -96,7 +96,7 @@ def plot(pts, dc, f, **key): if pts['type'] == 'polygons': dc.DrawPolygonList(plst) - if isline or pts['type'] == 'line': + if isline or pts['type'] == 'lines': for line in plst: dc.DrawLines(line) From 7fa60567bc71ed0d0e3546bf3d5cfb854380b9b2 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 20 Apr 2019 23:50:09 +0800 Subject: [PATCH 124/343] distance transform --- imagepy/ipyalg/hydrology/edt.py | 117 ++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 imagepy/ipyalg/hydrology/edt.py diff --git a/imagepy/ipyalg/hydrology/edt.py b/imagepy/ipyalg/hydrology/edt.py new file mode 100644 index 00000000..34eeed00 --- /dev/null +++ b/imagepy/ipyalg/hydrology/edt.py @@ -0,0 +1,117 @@ +import numpy as np +from numba import jit +from scipy.ndimage import label, generate_binary_structure + +def neighbors(shape): + dim = len(shape) + block = generate_binary_structure(dim, 1) + block[tuple([1]*dim)] = 0 + idx = np.where(block>0) + idx = np.array(idx, dtype=np.uint8).T + idx = np.array(idx-[1]*dim) + acc = np.cumprod((1,)+shape[::-1][:-1]) + return np.dot(idx, acc[::-1]) + +@jit +def dist(idx1, idx2, acc): + dis = 0 + for i in range(len(acc)): + c1 = idx1//acc[i] + c2 = idx2//acc[i] + dis += (c1-c2)**2 + idx1 -= c1*acc[i] + idx2 -= c2*acc[i] + return dis + +@jit +def step(dis, pts, roots, s, level, nbs, acc): + cur = 0 + while curlevel: + cur += 1 + continue + for dp in nbs: + cp = p+dp + if dis[cp]=0xffff-1: continue # edge or back + for dp in nbs: + if dis[p+dp]==0xffff-1: + pts[cur] = p + root[cur] = p + cur += 1 + break + return cur + +def buffer(img, dtype): + buf = np.ones(tuple(np.array(img.shape)+2), dtype=dtype) + buf *= 0xffff + buf[tuple([slice(1,-1)]*buf.ndim)] = (img>0)*(0xffff-1) + return buf + +@jit +def distance_transform_edt(img, output=np.float32): + dis = buffer(img, output) + nbs = neighbors(dis.shape) + acc = np.cumprod((1,)+dis.shape[::-1][:-1])[::-1] + + line = dis.ravel() + pts = np.zeros(line.size//2, dtype=np.int64) + roots = np.zeros(line.size//2, dtype=np.int64) + s = collect(line, nbs, pts, roots) + for level in range(10000): + s, c = clear(pts, roots, s, 0) + s = step(line, pts, roots, s, level, nbs, acc) + if s==0:break + return dis[(slice(1,-1),)*img.ndim] + +if __name__ == '__main__': + import matplotlib.pyplot as plt + from skimage.io import imread, imsave + from scipy.ndimage import distance_transform_edt as scipyedt + from time import time + + img = np.ones((2048,2048)) + img[1024,1024] = 0 + + start = time() + for i in range(1): + dis1 = scipyedt(img) + print('scipy', time()-start) + #start = time() + dis = distance_transform_edt(img, np.uint16) + #print('my', time()-start) + start = time() + for i in range(1): + dis2 = distance_transform_edt(img, np.uint16) + print('my', time()-start) + From 07849a9146caf41ad761216d34f41bb1de2b61eb Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 21 Apr 2019 00:14:45 +0800 Subject: [PATCH 125/343] use my distans transform --- imagepy/ipyalg/__init__.py | 3 ++- imagepy/ipyalg/graph/skel2d.py | 3 ++- imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py | 5 +++-- imagepy/menus/Plugins/Games/drawstep_plg.py | 2 +- imagepy/menus/Process/Binary/distance_plgs.py | 8 ++++---- imagepy/menus/Process/repair_plg.py | 1 + 6 files changed, 13 insertions(+), 9 deletions(-) diff --git a/imagepy/ipyalg/__init__.py b/imagepy/ipyalg/__init__.py index 8c8b3b6c..b312dad9 100644 --- a/imagepy/ipyalg/__init__.py +++ b/imagepy/ipyalg/__init__.py @@ -1,4 +1,5 @@ from .hydrology.findmax import find_maximum from .hydrology.ridge import ridge from .hydrology.isoline import stair, isoline -from .hydrology.watershed import watershed \ No newline at end of file +from .hydrology.watershed import watershed +from .hydrology.edt import distance_transform_edt \ No newline at end of file diff --git a/imagepy/ipyalg/graph/skel2d.py b/imagepy/ipyalg/graph/skel2d.py index d040df27..0906ddf6 100644 --- a/imagepy/ipyalg/graph/skel2d.py +++ b/imagepy/ipyalg/graph/skel2d.py @@ -3,6 +3,7 @@ from numba import jit from scipy.ndimage import label, generate_binary_structure +from ..hydrology.edt import distance_transform_edt strc = np.ones((3,3), dtype=np.bool) # check whether this pixcel can be removed @@ -49,7 +50,7 @@ def medial_axis(data, idx, branch = True): return 0; def mid_axis(img): - dis = ndimg.distance_transform_edt(img) + dis = distance_transform_edt(img) dis[[0,-1],:] = 0; dis[:,[0,-1]] = 0 idx = np.argsort(dis.flat).astype(np.int32) medial_axis(dis, idx, lut) diff --git a/imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py b/imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py index 72a0d9d9..f217a3ba 100644 --- a/imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py +++ b/imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py @@ -4,6 +4,7 @@ from skimage.morphology import skeletonize_3d from imagepy.ipyalg import find_maximum, watershed from skimage.filters import apply_hysteresis_threshold +from imagepy.ipyalg import distance_transform_edt import numpy as np class Dilation(Simple): @@ -78,7 +79,7 @@ class Distance3D(Simple): #process def run(self, ips, imgs, para = None): - dismap = ndimg.distance_transform_edt(imgs>0) + dismap = distance_transform_edt(imgs>0) imgs[:] = np.clip(dismap, ips.range[0], ips.range[1]) class Watershed(Simple): @@ -94,7 +95,7 @@ class Watershed(Simple): ## TODO: Fixme! def run(self, ips, imgs, para = None): imgs[:] = imgs > 0 - dist = -ndimg.distance_transform_edt(imgs) + dist = -distance_transform_edt(imgs) pts = find_maximum(dist, para['tor'], False) buf = np.zeros(imgs.shape, dtype=np.uint32) buf[pts[:,0], pts[:,1], pts[:,2]] = 2 diff --git a/imagepy/menus/Plugins/Games/drawstep_plg.py b/imagepy/menus/Plugins/Games/drawstep_plg.py index b9bbfc90..ab11b5b8 100644 --- a/imagepy/menus/Plugins/Games/drawstep_plg.py +++ b/imagepy/menus/Plugins/Games/drawstep_plg.py @@ -1,7 +1,7 @@ from skimage.io import imread import matplotlib.pyplot as plt from skimage.morphology import skeletonize -from scipy.ndimage import distance_transform_edt +from imagepy.ipyalg import distance_transform_edt import numpy as np from imagepy.ipyalg.graph import sknw from imagepy.core.engine import Simple diff --git a/imagepy/menus/Process/Binary/distance_plgs.py b/imagepy/menus/Process/Binary/distance_plgs.py index c1c4ef2c..f027cc20 100644 --- a/imagepy/menus/Process/Binary/distance_plgs.py +++ b/imagepy/menus/Process/Binary/distance_plgs.py @@ -8,7 +8,7 @@ from skimage.morphology import medial_axis from imagepy.ipyalg.graph import skel2d from imagepy.core.engine import Filter -from imagepy.ipyalg import find_maximum, watershed +from imagepy.ipyalg import find_maximum, watershed, distance_transform_edt from skimage.filters import apply_hysteresis_threshold import scipy.ndimage as ndimg @@ -26,7 +26,7 @@ class EDT(Filter): note = ['all', 'auto_msk', 'auto_snap','preview'] def run(self, ips, snap, img, para = None): - return ndimg.distance_transform_edt(snap) + return distance_transform_edt(snap) class MedialAxis(Filter): title = 'Medial Axis' @@ -55,7 +55,7 @@ class Watershed(Filter): ## TODO: Fixme! def run(self, ips, snap, img, para = None): img[:] = snap>0 - dist = -ndimg.distance_transform_edt(snap) + dist = -distance_transform_edt(snap) pts = find_maximum(dist, para['tor'], False) buf = np.zeros(ips.size, dtype=np.uint32) buf[pts[:,0], pts[:,1]] = img[pts[:,0], pts[:,1]] = 2 @@ -73,7 +73,7 @@ class Voronoi(Filter): view = [(list, 'type', ['segment with ori', 'segment only', 'white line', 'gray line'], str, 'output', '')] ## TODO: Fixme! def run(self, ips, snap, img, para = None): - dist = ndimg.distance_transform_edt(snap) + dist = distance_transform_edt(snap) markers, n = ndimg.label(snap==0, np.ones((3,3))) line = watershed(dist, markers, line=True) diff --git a/imagepy/menus/Process/repair_plg.py b/imagepy/menus/Process/repair_plg.py index 836273d7..28e5e20c 100644 --- a/imagepy/menus/Process/repair_plg.py +++ b/imagepy/menus/Process/repair_plg.py @@ -1,6 +1,7 @@ from imagepy.core.engine import Filter import numpy as np import scipy.ndimage as ndimg +from imagepy.ipyalg import distance_transform_edt class Plugin(Filter): title = 'Fragment Repair' From 9564fafb003bbbaef7309aa3bf07a539b4719bf1 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 21 Apr 2019 16:01:02 +0800 Subject: [PATCH 126/343] my distance transform --- imagepy/ipyalg/hydrology/edt.py | 36 ++++++++++--------- .../menus/Kit3D/Binary 3D/binary3d_plgs.py | 6 ++-- imagepy/menus/Process/Binary/distance_plgs.py | 4 +-- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/imagepy/ipyalg/hydrology/edt.py b/imagepy/ipyalg/hydrology/edt.py index 34eeed00..d3469e32 100644 --- a/imagepy/ipyalg/hydrology/edt.py +++ b/imagepy/ipyalg/hydrology/edt.py @@ -24,7 +24,7 @@ def dist(idx1, idx2, acc): return dis @jit -def step(dis, pts, roots, s, level, nbs, acc): +def step(dis, pts, roots, s, level, nbs, acc, scale): cur = 0 while cur0)*(0xffff-1) + buf[tuple([slice(1,-1)]*buf.ndim)] = img + line = buf.ravel() + for i in range(len(line)): + line[i] = 1 if line[i]==0 else 0xffff + buf[tuple([slice(1,-1)]*buf.ndim)] -= 1 return buf @jit -def distance_transform_edt(img, output=np.float32): +def distance_transform_edt(img, output=np.float32, scale=1): dis = buffer(img, output) nbs = neighbors(dis.shape) acc = np.cumprod((1,)+dis.shape[::-1][:-1])[::-1] - line = dis.ravel() - pts = np.zeros(line.size//2, dtype=np.int64) - roots = np.zeros(line.size//2, dtype=np.int64) + pts = np.zeros(max(line.size//8, 1024**2), dtype=np.int64) + roots = np.zeros(max(line.size//8, 1024**2), dtype=np.int64) s = collect(line, nbs, pts, roots) for level in range(10000): s, c = clear(pts, roots, s, 0) - s = step(line, pts, roots, s, level, nbs, acc) + s = step(line, pts, roots, s, level, nbs, acc, scale) if s==0:break return dis[(slice(1,-1),)*img.ndim] @@ -100,18 +103,17 @@ def distance_transform_edt(img, output=np.float32): from scipy.ndimage import distance_transform_edt as scipyedt from time import time - img = np.ones((2048,2048)) + img = np.ones((2048,2048), dtype=np.uint8) img[1024,1024] = 0 start = time() - for i in range(1): - dis1 = scipyedt(img) + dis1 = scipyedt(img) print('scipy', time()-start) #start = time() - dis = distance_transform_edt(img, np.uint16) + dis = distance_transform_edt(img, np.float32, 2) #print('my', time()-start) start = time() - for i in range(1): - dis2 = distance_transform_edt(img, np.uint16) + dis2 = distance_transform_edt(img, np.float32, 2) print('my', time()-start) - + plt.imshow(dis2, cmap='gray') + plt.show() diff --git a/imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py b/imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py index f217a3ba..a144c02a 100644 --- a/imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py +++ b/imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py @@ -79,7 +79,9 @@ class Distance3D(Simple): #process def run(self, ips, imgs, para = None): - dismap = distance_transform_edt(imgs>0) + imgs[:] = imgs>0 + dtype = imgs.dtype if imgs.dtype in (np.float32, np.float64) else np.uint16 + dismap = distance_transform_edt(imgs, output=dtype) imgs[:] = np.clip(dismap, ips.range[0], ips.range[1]) class Watershed(Simple): @@ -95,7 +97,7 @@ class Watershed(Simple): ## TODO: Fixme! def run(self, ips, imgs, para = None): imgs[:] = imgs > 0 - dist = -distance_transform_edt(imgs) + dist = -distance_transform_edt(imgs, output=np.uint16) pts = find_maximum(dist, para['tor'], False) buf = np.zeros(imgs.shape, dtype=np.uint32) buf[pts[:,0], pts[:,1], pts[:,2]] = 2 diff --git a/imagepy/menus/Process/Binary/distance_plgs.py b/imagepy/menus/Process/Binary/distance_plgs.py index f027cc20..07a688a3 100644 --- a/imagepy/menus/Process/Binary/distance_plgs.py +++ b/imagepy/menus/Process/Binary/distance_plgs.py @@ -55,7 +55,7 @@ class Watershed(Filter): ## TODO: Fixme! def run(self, ips, snap, img, para = None): img[:] = snap>0 - dist = -distance_transform_edt(snap) + dist = -distance_transform_edt(snap, output=np.uint16) pts = find_maximum(dist, para['tor'], False) buf = np.zeros(ips.size, dtype=np.uint32) buf[pts[:,0], pts[:,1]] = img[pts[:,0], pts[:,1]] = 2 @@ -73,7 +73,7 @@ class Voronoi(Filter): view = [(list, 'type', ['segment with ori', 'segment only', 'white line', 'gray line'], str, 'output', '')] ## TODO: Fixme! def run(self, ips, snap, img, para = None): - dist = distance_transform_edt(snap) + dist = distance_transform_edt(snap, output=np.uint16) markers, n = ndimg.label(snap==0, np.ones((3,3))) line = watershed(dist, markers, line=True) From 85b29e4e89072682d5303724f867aa624bf6d8fc Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 21 Apr 2019 18:55:41 +0800 Subject: [PATCH 127/343] distance 3d --- imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py | 6 +++--- imagepy/menus/Process/Binary/distance_plgs.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py b/imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py index a144c02a..7960af32 100644 --- a/imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py +++ b/imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py @@ -97,13 +97,13 @@ class Watershed(Simple): ## TODO: Fixme! def run(self, ips, imgs, para = None): imgs[:] = imgs > 0 - dist = -distance_transform_edt(imgs, output=np.uint16) - pts = find_maximum(dist, para['tor'], False) + dist = distance_transform_edt(imgs, output=np.uint16) + pts = find_maximum(dist, para['tor'], True) buf = np.zeros(imgs.shape, dtype=np.uint32) buf[pts[:,0], pts[:,1], pts[:,2]] = 2 imgs[pts[:,0], pts[:,1], pts[:,2]] = 2 markers, n = ndimg.label(buf, np.ones((3, 3, 3))) - line = watershed(dist, markers, line=True, conn=para['con']+1) + line = watershed(dist, markers, line=True, conn=para['con']+1, up=False) msk = apply_hysteresis_threshold(imgs, 0, 1) imgs[:] = imgs>0; imgs *= 255; imgs *= ~((line==0) & msk) diff --git a/imagepy/menus/Process/Binary/distance_plgs.py b/imagepy/menus/Process/Binary/distance_plgs.py index 07a688a3..00847c40 100644 --- a/imagepy/menus/Process/Binary/distance_plgs.py +++ b/imagepy/menus/Process/Binary/distance_plgs.py @@ -55,12 +55,12 @@ class Watershed(Filter): ## TODO: Fixme! def run(self, ips, snap, img, para = None): img[:] = snap>0 - dist = -distance_transform_edt(snap, output=np.uint16) - pts = find_maximum(dist, para['tor'], False) + dist = distance_transform_edt(snap, output=np.uint16) + pts = find_maximum(dist, para['tor'], True) buf = np.zeros(ips.size, dtype=np.uint32) buf[pts[:,0], pts[:,1]] = img[pts[:,0], pts[:,1]] = 2 markers, n = ndimg.label(buf, np.ones((3,3))) - line = watershed(dist, markers, line=True, conn=para['con']+1) + line = watershed(dist, markers, line=True, conn=para['con']+1, up=False) msk = apply_hysteresis_threshold(img, 0, 1) img[:] = snap * ~((line==0) & msk) From 7500a3a793a23b20f30c223579db91a181252a3c Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 22 Apr 2019 02:18:16 +0800 Subject: [PATCH 128/343] roi manager --- imagepy/core/manager/roimanager.py | 8 + imagepy/core/roi/roi.py | 6 + imagepy/menus/Selection/Relation/__init__.py | 1 - .../menus/Selection/Relation/relation_plg.py | 48 ---- imagepy/menus/Selection/__init__.py | 2 +- imagepy/menus/Selection/roiwindow_wgt.py | 252 ++++++++++++++++++ imagepy/menus/Selection/select_plg.py | 129 ++++++++- imagepy/menus/Selection/setting_plg.py | 16 -- 8 files changed, 384 insertions(+), 78 deletions(-) delete mode 100644 imagepy/menus/Selection/Relation/__init__.py delete mode 100644 imagepy/menus/Selection/Relation/relation_plg.py create mode 100644 imagepy/menus/Selection/roiwindow_wgt.py delete mode 100644 imagepy/menus/Selection/setting_plg.py diff --git a/imagepy/core/manager/roimanager.py b/imagepy/core/manager/roimanager.py index aaab70c5..9304c42c 100644 --- a/imagepy/core/manager/roimanager.py +++ b/imagepy/core/manager/roimanager.py @@ -11,12 +11,20 @@ class RoiManager: def add(cls, name, roi): cls.rois[name] = roi + @classmethod + def remove(cls, name): + if name in cls.rois: del cls.rois[name] + @classmethod def get(cls, name): if name not in cls.rois: return None return cls.rois[name] + @classmethod + def get_titles(cls): + return sorted(list(cls.rois.keys())) + @classmethod def get_color(cls): color = ConfigManager.get('roicolor') diff --git a/imagepy/core/roi/roi.py b/imagepy/core/roi/roi.py index 961b284a..d12f6514 100644 --- a/imagepy/core/roi/roi.py +++ b/imagepy/core/roi/roi.py @@ -30,6 +30,12 @@ def invert(self, rect): def union(self, roi): return shape2roi(roi2shape(roi).union(roi2shape(self))) + def intersect(self, roi): + return shape2roi(roi2shape(roi).intersection(roi2shape(self))) + + def symmetric_diff(self, roi): + return shape2roi(roi2shape(roi).symmetric_difference(roi2shape(self))) + def diff(self, roi): return shape2roi(roi2shape(self).difference(roi2shape(roi))) diff --git a/imagepy/menus/Selection/Relation/__init__.py b/imagepy/menus/Selection/Relation/__init__.py deleted file mode 100644 index 4287ca86..00000000 --- a/imagepy/menus/Selection/Relation/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# \ No newline at end of file diff --git a/imagepy/menus/Selection/Relation/relation_plg.py b/imagepy/menus/Selection/Relation/relation_plg.py deleted file mode 100644 index e3be5210..00000000 --- a/imagepy/menus/Selection/Relation/relation_plg.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Thu Dec 22 20:25:52 2016 - -@author: yxl -""" - -from imagepy.core.engine import Simple -from imagepy.core.manager import RoiManager -from imagepy import IPy - -class Union(Simple): - """Union: derived from imagepy.core.engine.Simple """ - title = 'Union' - note = ['all', 'req_roi'] - para = {'name':''} - - def load(self, ips): - titles = list(RoiManager.rois.keys()) - if len(titles)==0: - IPy.alert('No roi in manager!') - return False - self.para['name'] = titles[0] - self.view = [(list, 'name', titles, str, 'Name', '')] - return True - - def run(self, ips, imgs, para = None): - ips.roi = ips.roi.union(RoiManager.get(para['name'])) - -class Diff(Simple): - """Diff: derived from imagepy.core.engine.Simple """ - title = 'Difference' - note = ['all', 'req_roi'] - para = {'name':''} - - def load(self, ips): - titles = list(RoiManager.rois.keys()) - if len(titles)==0: - IPy.alert('No roi in manager!') - return False - self.para['name'] = titles[0] - self.view = [(list, 'name', titles, str, 'Name', '')] - return True - - def run(self, ips, imgs, para = None): - ips.roi = ips.roi.diff(RoiManager.get(para['name'])) - -plgs = [Union, Diff] \ No newline at end of file diff --git a/imagepy/menus/Selection/__init__.py b/imagepy/menus/Selection/__init__.py index f1854c3f..f0845f10 100644 --- a/imagepy/menus/Selection/__init__.py +++ b/imagepy/menus/Selection/__init__.py @@ -1 +1 @@ -catlog = ['select_plg', 'Relation', '-', 'setting_plg'] \ No newline at end of file +catlog = ['select_plg', 'roiwindow_wgt'] \ No newline at end of file diff --git a/imagepy/menus/Selection/roiwindow_wgt.py b/imagepy/menus/Selection/roiwindow_wgt.py new file mode 100644 index 00000000..6ae4b632 --- /dev/null +++ b/imagepy/menus/Selection/roiwindow_wgt.py @@ -0,0 +1,252 @@ +import wx +from imagepy.core.manager import RoiManager +from imagepy.core.engine import Macros +from imagepy import IPy + +class VirtualListCtrl(wx.ListCtrl): + def __init__(self, parent, title, data=[]): + wx.ListCtrl.__init__(self, parent, style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_VIRTUAL) + self.title, self.data = title, data + #self.Bind(wx.EVT_LIST_CACHE_HINT, self.DoCacheItems) + for col, text in enumerate(title): + self.InsertColumn(col, text) + self.SetValue(data) + + def OnGetItemText(self, row, col): + return self.data[row][col] + + def OnGetItemAttr(self, item): return None + + def OnGetItemImage(self, item): return -1 + + def SetValue(self, data): + self.data = data + self.SetItemCount(len(data)) + + def Refresh(self): + self.SetItemCount(len(self.data)) + wx.ListCtrl.Refresh(self) + +class Plugin(wx.Panel): + title = 'ROI Ctrl Panel' + single = None + + def __init__( self, parent ): + wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size(-1,-1), style = wx.TAB_TRAVERSAL ) + + sizer = wx.BoxSizer( wx.HORIZONTAL ) + + self.note_book = wx.Notebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.NB_LEFT|wx.NB_TOP ) + #self.note_book = wx.Choicebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.CHB_DEFAULT ) + self.pan_manage = wx.Panel( self.note_book, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + sizer_manage = wx.BoxSizer( wx.VERTICAL ) + + self.btn_add = wx.Button( self.pan_manage, wx.ID_ANY, u"Add", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_manage.Add( self.btn_add, 0, wx.ALL, 5 ) + + self.btn_load = wx.Button( self.pan_manage, wx.ID_ANY, u"Load", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_manage.Add( self.btn_load, 0, wx.ALL, 5 ) + + self.btn_update = wx.Button( self.pan_manage, wx.ID_ANY, u"Update", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_manage.Add( self.btn_update, 0, wx.ALL, 5 ) + + self.btn_remove = wx.Button( self.pan_manage, wx.ID_ANY, u"Remove", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_manage.Add( self.btn_remove, 0, wx.ALL, 5 ) + + self.btn_open = wx.Button( self.pan_manage, wx.ID_ANY, u"Open", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_manage.Add( self.btn_open, 0, wx.ALL, 5 ) + + self.btn_save = wx.Button( self.pan_manage, wx.ID_ANY, u"Save", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_manage.Add( self.btn_save, 0, wx.ALL, 5 ) + + + self.pan_manage.SetSizer( sizer_manage ) + self.pan_manage.Layout() + sizer_manage.Fit( self.pan_manage ) + self.note_book.AddPage( self.pan_manage, u"Manage", True ) + self.pan_operate = wx.Panel( self.note_book, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + sizer_operate = wx.BoxSizer( wx.VERTICAL ) + + self.btn_inflate = wx.Button( self.pan_operate, wx.ID_ANY, u"Inflate", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_operate.Add( self.btn_inflate, 0, wx.ALL, 5 ) + + self.btn_shrink = wx.Button( self.pan_operate, wx.ID_ANY, u"Shrink", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_operate.Add( self.btn_shrink, 0, wx.ALL, 5 ) + + self.btn_convex = wx.Button( self.pan_operate, wx.ID_ANY, u"Convex", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_operate.Add( self.btn_convex, 0, wx.ALL, 5 ) + + self.btn_bound = wx.Button( self.pan_operate, wx.ID_ANY, u"Bound", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_operate.Add( self.btn_bound, 0, wx.ALL, 5 ) + + self.btn_clip = wx.Button( self.pan_operate, wx.ID_ANY, u"Clip", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_operate.Add( self.btn_clip, 0, wx.ALL, 5 ) + + self.btn_invert = wx.Button( self.pan_operate, wx.ID_ANY, u"Invert", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_operate.Add( self.btn_invert, 0, wx.ALL, 5 ) + + + self.pan_operate.SetSizer( sizer_operate ) + self.pan_operate.Layout() + sizer_operate.Fit( self.pan_operate ) + self.note_book.AddPage( self.pan_operate, u"Operate", False ) + self.pan_relation = wx.Panel( self.note_book, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + sizer_relation = wx.BoxSizer( wx.VERTICAL ) + + self.btn_intersect = wx.Button( self.pan_relation, wx.ID_ANY, u"Intersect", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_relation.Add( self.btn_intersect, 0, wx.ALL, 5 ) + + self.btn_union = wx.Button( self.pan_relation, wx.ID_ANY, u"Union", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_relation.Add( self.btn_union, 0, wx.ALL, 5 ) + + self.btn_difference = wx.Button( self.pan_relation, wx.ID_ANY, u"Difference", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_relation.Add( self.btn_difference, 0, wx.ALL, 5 ) + + self.btn_symdiff = wx.Button( self.pan_relation, wx.ID_ANY, u"Sym Diff", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_relation.Add( self.btn_symdiff, 0, wx.ALL, 5 ) + + + self.pan_relation.SetSizer( sizer_relation ) + self.pan_relation.Layout() + sizer_relation.Fit( self.pan_relation ) + self.note_book.AddPage( self.pan_relation, u"Relationship", False ) + self.pan_draw = wx.Panel( self.note_book, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + sizer_draw = wx.BoxSizer( wx.VERTICAL ) + + self.btn_sketch = wx.Button( self.pan_draw, wx.ID_ANY, u"Sketch", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_draw.Add( self.btn_sketch, 0, wx.ALL, 5 ) + + self.btn_clear = wx.Button( self.pan_draw, wx.ID_ANY, u"Clear", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_draw.Add( self.btn_clear, 0, wx.ALL, 5 ) + + self.btn_clearout = wx.Button( self.pan_draw, wx.ID_ANY, u"Clear Out", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_draw.Add( self.btn_clearout, 0, wx.ALL, 5 ) + + sizer_draw.AddStretchSpacer(1) + self.btn_setting = wx.Button( self.pan_draw, wx.ID_ANY, u"Setting", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_draw.Add( self.btn_setting, 0, wx.ALL, 5 ) + + self.pan_draw.SetSizer( sizer_draw ) + self.pan_draw.Layout() + sizer_draw.Fit( self.pan_draw ) + self.note_book.AddPage( self.pan_draw, u"Draw", False ) + + sizer.Add( self.note_book, 0, wx.EXPAND |wx.ALL, 5 ) + + self.lst_rois = VirtualListCtrl(self, ['name', 'type'], []) + self.UpdateData() + + sizer.Add( self.lst_rois, 1, wx.ALL|wx.EXPAND, 5 ) + self.SetSizer( sizer ) + self.Fit() + self.Layout() + self.AddEvent() + + def AddEvent(self): + self.btn_add.Bind(wx.EVT_BUTTON, self.on_add) + self.btn_load.Bind(wx.EVT_BUTTON, self.on_load) + self.btn_update.Bind(wx.EVT_BUTTON, self.on_update) + self.btn_remove.Bind(wx.EVT_BUTTON, self.on_remove) + self.btn_open.Bind(wx.EVT_BUTTON, self.on_open) + self.btn_save.Bind(wx.EVT_BUTTON, self.on_save) + self.btn_inflate.Bind(wx.EVT_BUTTON, self.on_inflate) + self.btn_shrink.Bind(wx.EVT_BUTTON, self.on_shrink) + self.btn_convex.Bind(wx.EVT_BUTTON, self.on_convex) + self.btn_bound.Bind(wx.EVT_BUTTON, self.on_box) + self.btn_clip.Bind(wx.EVT_BUTTON, self.on_clip) + self.btn_invert.Bind(wx.EVT_BUTTON, self.on_invert) + self.btn_intersect.Bind(wx.EVT_BUTTON, self.on_intersect) + self.btn_union.Bind(wx.EVT_BUTTON, self.on_union) + self.btn_difference.Bind(wx.EVT_BUTTON, self.on_difference) + self.btn_symdiff.Bind(wx.EVT_BUTTON, self.on_symdiff) + self.btn_sketch.Bind(wx.EVT_BUTTON, self.on_sketch) + self.btn_clear.Bind(wx.EVT_BUTTON, self.on_clear) + self.btn_clearout.Bind(wx.EVT_BUTTON, self.on_clearout) + self.btn_setting.Bind(wx.EVT_BUTTON, self.on_setting) + self.lst_rois.Bind( wx.EVT_LIST_ITEM_ACTIVATED, self.on_load) + + def on_add(self, event): + Macros('', ['ROI Add>None']).start(callafter=self.UpdateData) + + def on_load(self, event): + idx = self.lst_rois.GetFirstSelected() + if idx==-1: return IPy.alert('No ROI Selected!') + name = self.lst_rois.OnGetItemText(idx, 0) + Macros('', ['ROI Load>{"name":"%s"}'%name]).start() + + def on_remove(self, event): + idx = self.lst_rois.GetFirstSelected() + if idx==-1: return IPy.alert('No ROI Selected!') + name = self.lst_rois.OnGetItemText(idx, 0) + Macros('', ['ROI Remove>{"name":"%s"}'%name]).start(callafter=self.UpdateData) + + def on_open(self, event): + Macros('', ['ROI Open>None']).start() + + def on_save(self, event): + Macros('', ['ROI Save>None']).start() + + def on_inflate(self, event): + Macros('', ['ROI Inflate>None']).start() + + def on_shrink(self, event): + Macros('', ['ROI Shrink>None']).start() + + def on_convex(self, event): + Macros('', ['ROI Convex Hull>None']).start() + + def on_box(self, event): + Macros('', ['ROI Bound Box>None']).start() + + def on_clip(self, event): + Macros('', ['ROI Clip>None']).start() + + def on_invert(self, event): + Macros('', ['ROI Invert>None']).start() + + def on_intersect(self, event): + idx = self.lst_rois.GetFirstSelected() + if idx==-1: return IPy.alert('No ROI Selected!') + name = self.lst_rois.OnGetItemText(idx, 0) + Macros('', ['ROI Intersect>{"name":"%s"}'%name]).start() + + def on_union(self, event): + idx = self.lst_rois.GetFirstSelected() + if idx==-1: return IPy.alert('No ROI Selected!') + name = self.lst_rois.OnGetItemText(idx, 0) + Macros('', ['ROI Union>{"name":"%s"}'%name]).start() + + def on_difference(self, event): + idx = self.lst_rois.GetFirstSelected() + if idx==-1: return IPy.alert('No ROI Selected!') + name = self.lst_rois.OnGetItemText(idx, 0) + Macros('', ['ROI Difference>{"name":"%s"}'%name]).start() + + def on_symdiff(self, event): + idx = self.lst_rois.GetFirstSelected() + if idx==-1: return IPy.alert('No ROI Selected!') + name = self.lst_rois.OnGetItemText(idx, 0) + Macros('', ['ROI Symmetric Diff>{"name":"%s"}'%name]).start() + + def on_clear(self, event): + Macros('', ['Clear>None']).start() + + def on_clearout(self, event): + Macros('', ['Clear Out>None']).start() + + def on_sketch(self, event): + Macros('', ['Sketch>None']).start() + + def on_update(self, event): + self.UpdateData() + + def on_setting(self, event): + Macros('', ['ROI Setting>None']).start() + + def UpdateData(self): + names = RoiManager.get_titles() + types = [RoiManager.get(i).dtype for i in names] + self.lst_rois.SetValue(list(zip(names, types))) + + def __del__( self ): + pass \ No newline at end of file diff --git a/imagepy/menus/Selection/select_plg.py b/imagepy/menus/Selection/select_plg.py index 24d71268..ed08b3d3 100644 --- a/imagepy/menus/Selection/select_plg.py +++ b/imagepy/menus/Selection/select_plg.py @@ -5,7 +5,7 @@ @author: yxl """ from imagepy.core.manager import RoiManager -from imagepy.core.engine import Simple +from imagepy.core.engine import Simple, Free from imagepy.core.roi import RectangleRoi from imagepy.core.roi import roiio from imagepy import IPy @@ -28,7 +28,7 @@ def run(self, ips, imgs, para = None): class Add2Manager(Simple): """Add2Manager: derived from imagepy.core.engine.Simple """ - title = 'Add To Manager' + title = 'ROI Add' note = ['all', 'req_roi'] para = {'name':''} view = [(str, 'name', 'Name', '')] @@ -36,9 +36,27 @@ class Add2Manager(Simple): def run(self, ips, imgs, para = None): RoiManager.add(para['name'], ips.roi) +class RemoveFManager(Simple): + """Add2Manager: derived from imagepy.core.engine.Simple """ + title = 'ROI Remove' + note = ['all'] + para = {'name':''} + + def load(self, ips): + titles = list(RoiManager.rois.keys()) + if len(titles)==0: + IPy.alert('No roi in manager!') + return False + self.para['name'] = titles[0] + RemoveFManager.view = [(list, 'name', titles, str, 'Name', '')] + return True + + def run(self, ips, imgs, para = None): + RoiManager.remove(para['name']) + class LoadRoi(Simple): """LoadRoi: derived from imagepy.core.engine.Simple """ - title = 'Load Roi' + title = 'ROI Load' note = ['all'] para = {'name':''} @@ -56,7 +74,7 @@ def run(self, ips, imgs, para = None): class Inflate(Simple): """Inflate: derived from imagepy.core.engine.Simple """ - title = 'Inflate' + title = 'ROI Inflate' note = ['all', 'req_roi'] para = {'r':5} view = [(int, 'r', (1,100),0, 'radius', 'pix')] @@ -66,7 +84,7 @@ def run(self, ips, imgs, para = None): class Shrink(Simple): """Shrink: derived from imagepy.core.engine.Simple """ - title = 'Shrink' + title = 'ROI Shrink' note = ['all', 'req_roi'] para = {'r':5} view = [(int, 'r', (1,100),0, 'radius', 'pix')] @@ -76,7 +94,7 @@ def run(self, ips, imgs, para = None): class Convex(Simple): """Convex: derived from imagepy.core.engine.Simple """ - title = 'Convex Hull' + title = 'ROI Convex Hull' note = ['all', 'req_roi'] def run(self, ips, imgs, para = None): @@ -84,7 +102,7 @@ def run(self, ips, imgs, para = None): class Box(Simple): """Box: derived from imagepy.core.engine.Simple """ - title = 'Bound Box' + title = 'ROI Bound Box' note = ['all', 'req_roi'] def run(self, ips, imgs, para = None): @@ -92,7 +110,7 @@ def run(self, ips, imgs, para = None): class Clip(Simple): """Clip: derived from imagepy.core.engine.Simple """ - title = 'Clip Roi' + title = 'ROI Clip' note = ['all', 'req_roi'] def run(self, ips, imgs, para = None): @@ -101,7 +119,7 @@ def run(self, ips, imgs, para = None): class Invert(Simple): """Invert: derived from imagepy.core.engine.Simple """ - title = 'Invert Roi' + title = 'ROI Invert' note = ['all', 'req_roi'] def run(self, ips, imgs, para = None): @@ -110,7 +128,7 @@ def run(self, ips, imgs, para = None): class Save(Simple): """Save: save roi as a wkt file """ - title = 'Save ROI' + title = 'ROI Save' note = ['all', 'req_roi'] para={'path':''} @@ -125,7 +143,7 @@ def run(self, ips, imgs, para = None): class Open(Simple): """Save: save roi as a wkt file """ - title = 'Open ROI' + title = 'ROI Open' note = ['all'] para={'path':''} @@ -138,7 +156,94 @@ def run(self, ips, imgs, para = None): if file[-3:] == 'wkt':ips.roi = roiio.readwkt(file) if file[-3:] == 'roi':ips.roi = roiio.readroi(file) + +class Intersect(Simple): + """Union: derived from imagepy.core.engine.Simple """ + title = 'ROI Intersect' + note = ['all', 'req_roi'] + para = {'name':''} + + def load(self, ips): + titles = list(RoiManager.rois.keys()) + if len(titles)==0: + IPy.alert('No roi in manager!') + return False + self.para['name'] = titles[0] + self.view = [(list, 'name', titles, str, 'Name', '')] + return True + + def run(self, ips, imgs, para = None): + ips.roi = ips.roi.intersect(RoiManager.get(para['name'])) + +class Union(Simple): + """Union: derived from imagepy.core.engine.Simple """ + title = 'ROI Union' + note = ['all', 'req_roi'] + para = {'name':''} + + def load(self, ips): + titles = list(RoiManager.rois.keys()) + if len(titles)==0: + IPy.alert('No roi in manager!') + return False + self.para['name'] = titles[0] + self.view = [(list, 'name', titles, str, 'Name', '')] + return True + + def run(self, ips, imgs, para = None): + ips.roi = ips.roi.union(RoiManager.get(para['name'])) + +class Diff(Simple): + """Diff: derived from imagepy.core.engine.Simple """ + title = 'ROI Difference' + note = ['all', 'req_roi'] + para = {'name':''} + + def load(self, ips): + titles = list(RoiManager.rois.keys()) + if len(titles)==0: + IPy.alert('No roi in manager!') + return False + self.para['name'] = titles[0] + self.view = [(list, 'name', titles, str, 'Name', '')] + return True + + def run(self, ips, imgs, para = None): + ips.roi = ips.roi.diff(RoiManager.get(para['name'])) + +class SymDiff(Simple): + """Diff: derived from imagepy.core.engine.Simple """ + title = 'ROI Symmetric Diff' + note = ['all', 'req_roi'] + para = {'name':''} + + def load(self, ips): + titles = list(RoiManager.rois.keys()) + if len(titles)==0: + IPy.alert('No roi in manager!') + return False + self.para['name'] = titles[0] + self.view = [(list, 'name', titles, str, 'Name', '')] + return True + + def run(self, ips, imgs, para = None): + ips.roi = ips.roi.symmetric_diff(RoiManager.get(para['name'])) + +class Setting(Free): + title = 'ROI Setting' + + def load(self): + Setting.para = {'color':RoiManager.get_color(), + 'lw':RoiManager.get_lw()} + Setting.view = [('color', 'color', 'roi', 'color'), + (int, 'lw', (1,5), 0, 'line width', 'pix')] + return True + + def run(self, para=None): + RoiManager.set_color(para['color']) + RoiManager.set_lw(para['lw']) plgs = [SelectAll, SelectNone, '-', Inflate, Shrink, Convex, Box, Clip, Invert, - '-', Open, Save, Add2Manager, LoadRoi] \ No newline at end of file + '-', Open, Save, Add2Manager, LoadRoi, RemoveFManager, + '-', Intersect, Union, Diff, SymDiff, '-', Setting] \ No newline at end of file diff --git a/imagepy/menus/Selection/setting_plg.py b/imagepy/menus/Selection/setting_plg.py deleted file mode 100644 index 03449689..00000000 --- a/imagepy/menus/Selection/setting_plg.py +++ /dev/null @@ -1,16 +0,0 @@ -from imagepy.core.manager import RoiManager -from imagepy.core.engine import Free - -class Plugin(Free): - title = 'Roi Setting' - - def load(self): - Plugin.para = {'color':RoiManager.get_color(), - 'lw':RoiManager.get_lw()} - Plugin.view = [('color', 'color', 'roi', 'color'), - (int, 'lw', (1,5), 0, 'line width', 'pix')] - return True - - def run(self, para=None): - RoiManager.set_color(para['color']) - RoiManager.set_lw(para['lw']) \ No newline at end of file From f0e578984e6e794a27d436b7574a1c03918553a2 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 24 Apr 2019 01:33:21 +0800 Subject: [PATCH 129/343] img cube --- imagepy/core/myvi/util.py | 30 +++++++++++++++++-- imagepy/menus/Image/Stack/stack_plgs.py | 19 +++++++++++- imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py | 27 ++++++++++++++++- 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/imagepy/core/myvi/util.py b/imagepy/core/myvi/util.py index bc4a47a7..a70d1cfd 100644 --- a/imagepy/core/myvi/util.py +++ b/imagepy/core/myvi/util.py @@ -51,9 +51,6 @@ def build_surf2d(img, ds=1, sigma=0, k=0.2): cy[:,1], cy[:,2] = 1, dy.ravel() ns = np.cross(cx, cy) ns = (ns.T/np.linalg.norm(ns, axis=1)).astype(np.float32).T - - #ns = count_ns(vts, fs) - #print(time()-start) return vts, fs, ns, cs def build_surf3d(imgs, ds, level, step=1, c=(1,0,0)): @@ -192,6 +189,33 @@ def build_cube(p1, p2, color=(1,1,1)): zs = (z1,z1,z2,z2,z1,z1,z2,z2,z2,z2,z1,z1,z1,z1,z2,z2) return build_line(xs, ys, zs, color) +def build_img_cube(imgs, ds=1): + imgs = imgs[::ds,::ds,::ds] + (h, r, c), total = imgs.shape[:3], 0 + print(h, r, c) + vtss, fss, nss, css = [], [], [], [] + shp = [(h,r,c,h*r), (h,c,r,h*c), (r,c,h,r*c)] + nn = [[(0,0,-1),(0,0,1)], [(0,1,0),(0,-1,0)], [(1,0,0),(-1,0,0)]] + for i in (0,1,2): + rs, cs, fs12 = build_grididx(*shp[i][:2]) + idx1, idx2 = [rs*ds, cs*ds], [rs*ds, cs*ds] + rcs1, rcs2 = [rs, cs], [rs, cs] + rcs1.insert(2-i, 0); rcs2.insert(2-i, -1) + vs1, vs2 = imgs[tuple(rcs1)]/255, imgs[tuple(rcs2)]/255 + idx1.insert(2-i, rs*0); idx2.insert(2-i, cs*0+shp[i][2]*ds-1) + vtss.append(np.array(idx1, dtype=np.float32).T) + vtss.append(np.array(idx2, dtype=np.float32).T) + css.append((np.ones((1, 3))*vs1.reshape((len(vs1),-1))).astype(np.float32)) + css.append((np.ones((1, 3))*vs2.reshape((len(vs1),-1))).astype(np.float32)) + nss.append((np.ones((shp[i][3],1))*[nn[i][0]]).astype(np.float32)) + nss.append((np.ones((shp[i][3],1))*[nn[i][1]]).astype(np.float32)) + fss.extend([fs12+total, fs12+(total+shp[i][0]*shp[i][1])]) + total += shp[i][3] * 2 + return np.vstack(vtss), np.vstack(fss), np.vstack(nss), np.vstack(css) + +def build_img_box(imgs, color=(1,1,1)): + return build_cube((-1,-1,-1), imgs.shape[:3], color) + cmp = {'rainbow':[(127, 0, 255), (43, 126, 246), (42, 220, 220), (128, 254, 179), (212, 220, 127), (255, 126, 65), (255, 0, 0)], 'jet':[(0, 0, 127), (0, 40, 255), (0, 212, 255), (124, 255, 121), (255, 229, 0), (255, 70, 0), (127, 0, 0)], 'ocean':[(0, 127, 0), (0, 64, 42), (0, 0, 85), (0, 64, 128), (0, 127, 170), (129, 192, 213), (255, 255, 255)], diff --git a/imagepy/menus/Image/Stack/stack_plgs.py b/imagepy/menus/Image/Stack/stack_plgs.py index a3f81b1f..ccb36ba6 100644 --- a/imagepy/menus/Image/Stack/stack_plgs.py +++ b/imagepy/menus/Image/Stack/stack_plgs.py @@ -4,6 +4,8 @@ @author: yxl """ from imagepy.core.engine import Simple +from imagepy import IPy + class SetSlice(Simple): title = 'Set Slice' note = ['all'] @@ -53,4 +55,19 @@ class Add(Simple): def run(self, ips, imgs, para = None): ips.imgs.insert(ips.cur, ips.img*0) -plgs = [SetSlice, Next, Pre, Add, Delete] \ No newline at end of file +class Sub(Simple): + title = 'Sub Stack' + modal = False + note = ['all'] + + para = {'start':0, 'end':10} + view = [(int, 'start', (0,1e8), 0, 'start', 'slice'), + (int, 'end', (0,1e8), 0, 'end', 'slice')] + + def run(self, ips, imgs, para = None): + (sc, sr), sz = ips.get_rect(), slice(para['start'], para['end']) + if ips.is3d: imgs = imgs[sz, sc, sr].copy() + else: imgs = [i[sc,sr].copy() for i in imgs[sz]] + IPy.show_img(imgs, ips.title+'-substack') + +plgs = [SetSlice, Next, Pre, Add, Delete, '-', Sub] \ No newline at end of file diff --git a/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py b/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py index 1d123cb0..b343f00d 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py +++ b/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py @@ -77,4 +77,29 @@ def run(self, ips, snap, img, para = None): self.frame.Raise() self.frame = None -plgs = [Show, Surface2D, Surface3D] +class ImageCube(Simple): + modal = False + title = '3D Image Cube' + note = ['8-bit', 'rgb', 'stack3d'] + para = {'name':'undifine', 'ds':1, 'color':(0,255,0), 'surface':True, 'box':False} + view = [(str, 'name', 'Name', 'xxx-surface'), + (bool, 'surface', 'show surface'), + (int, 'ds', (1,20), 0, 'down scale', 'pix'), + (bool, 'box', 'show box'), + ('color', 'color', 'box color', 'rgb')] + + def load(self, para): + self.frame = myvi.Frame3D.figure(IPy.curapp, title='3D Canvas') + return True + + def run(self, ips, imgs, para = None): + if para['surface']: + vts, fs, ns, cs = myvi.build_img_cube(imgs, para['ds']) + obj = self.frame.viewer.add_surf_asyn(para['name']+'-surface', vts, fs, ns, cs) + if para['box']: + vts, fs, ns, cs = myvi.build_img_box(imgs, para['color']) + obj = self.frame.viewer.add_surf_asyn(para['name']+'-box', vts, fs, ns, cs, mode='grid') + self.frame.Raise() + self.frame = None + +plgs = [Show, Surface2D, Surface3D, ImageCube] From 89c42501cc52660b99d3bbb13162303739c2bad1 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 25 Apr 2019 20:57:04 +0800 Subject: [PATCH 130/343] rgb hist --- imagepy/menus/Analysis/statistic_plg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/menus/Analysis/statistic_plg.py b/imagepy/menus/Analysis/statistic_plg.py index 605b5579..7114ebc9 100644 --- a/imagepy/menus/Analysis/statistic_plg.py +++ b/imagepy/menus/Analysis/statistic_plg.py @@ -73,7 +73,7 @@ def run(self, ips, imgs, para = None): msk = ips.get_msk('in') if ips.imgtype == 'rgb': img = ips.img if msk is None else ips.img[msk] - hist = [np.histogram(img[:,:,i], np.arange(257))[0] for i in (0,1,2)] + hist = [np.histogram(img.ravel()[i::3], np.arange(257))[0] for i in (0,1,2)] else: img = ips.lookup() if msk is None else ips.lookup()[msk] hist = np.histogram(img, np.arange(257))[0] From 667efe09fa0c8f9b3c6e2ba41208a86ba8585bab Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 5 May 2019 23:00:13 +0800 Subject: [PATCH 131/343] ridge --- imagepy/menus/Analysis/statistic_plg.py | 8 ++--- imagepy/menus/File/GIF/animate_plgs.py | 7 +---- imagepy/menus/Kit3D/Features 3D/__init__.py | 0 imagepy/menus/Kit3D/Features 3D/ridge_plgs.py | 31 +++++++++++++++++++ imagepy/menus/Kit3D/__init__.py | 2 +- imagepy/menus/Process/Features/__init__.py | 1 + imagepy/menus/Process/Features/ridge_plgs.py | 30 ++++++++++++++++++ .../menus/Process/Threshold/threshold_plgs.py | 2 +- 8 files changed, 69 insertions(+), 12 deletions(-) create mode 100644 imagepy/menus/Kit3D/Features 3D/__init__.py create mode 100644 imagepy/menus/Kit3D/Features 3D/ridge_plgs.py create mode 100644 imagepy/menus/Process/Features/ridge_plgs.py diff --git a/imagepy/menus/Analysis/statistic_plg.py b/imagepy/menus/Analysis/statistic_plg.py index 7114ebc9..43e08a87 100644 --- a/imagepy/menus/Analysis/statistic_plg.py +++ b/imagepy/menus/Analysis/statistic_plg.py @@ -112,7 +112,7 @@ def run(self, ips, imgs, para = None): class Statistic(Simple): title = 'Pixel Statistic' - note = ['8-bit', '16-bit', 'int', 'float'] + note = ['all'] para = {'max':True, 'min':True,'mean':False,'var':False,'std':False,'slice':False} view = [(bool, 'max', 'max'), @@ -126,9 +126,9 @@ def count(self, img, para): rst = [] if para['max']: rst.append(img.max()) if para['min']: rst.append(img.min()) - if para['mean']: rst.append(img.mean().round(2)) - if para['var']: rst.append(img.var().round(2)) - if para['std']: rst.append(img.std().round(2)) + if para['mean']: rst.append(img.mean()) + if para['var']: rst.append(img.var()) + if para['std']: rst.append(img.std()) return rst def run(self, ips, imgs, para = None): diff --git a/imagepy/menus/File/GIF/animate_plgs.py b/imagepy/menus/File/GIF/animate_plgs.py index be0a5d69..4d2fb457 100644 --- a/imagepy/menus/File/GIF/animate_plgs.py +++ b/imagepy/menus/File/GIF/animate_plgs.py @@ -27,12 +27,7 @@ class OpenAnimate(fileio.Reader): def run(self, para = None): #imgs = readGif(para['path']) - imgs = Image.open(para['path']) - imgs = ImageSequence.Iterator(imgs) - imgs = [np.array(i.convert('RGB')) for i in imgs] - for i in range(len(imgs)): - if imgs[i].ndim==3 and imgs[i].shape[2]>3: - imgs[i] = imgs[i][:,:,:3].copy() + imgs = imageio.mimread(para['path']) fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) IPy.show_img(imgs, fn) diff --git a/imagepy/menus/Kit3D/Features 3D/__init__.py b/imagepy/menus/Kit3D/Features 3D/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py b/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py new file mode 100644 index 00000000..b79ade51 --- /dev/null +++ b/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py @@ -0,0 +1,31 @@ +from skimage.filters import frangi, sato, hessian ,meijering +from imagepy.core.engine import Filter, Simple + +class Frangi(Simple): + title = 'Frangi 3D' + note = ['all', 'auto_msk', 'auto_snap'] + para = {'start':1, 'end':10, 'step':2, 'alpha':0.5, 'beta':0.5, 'gamma':15, 'bridges':True} + + view = [(int, 'start', (1,10), 0, 'sigma start', ''), + (int, 'end', (1,20), 0, 'sigma start', ''), + (int, 'step', (1,5), 0, 'sigma step', ''), + (float, 'alpha', (0.1, 1), 1, 'alpha',''), + (float, 'beta', (0.1, 1), 1, 'beta',''), + (float, 'gamma', (1,30), 1, 'gamma',''), + (bool, 'bridges', 'black ridges')] + + def run(self, ips, imgs, para = None): + imgs[:] = frangi(imgs, range(para['start'], para['end'], para['step']), + alpha=para['alpha'], beta=para['beta'], gamma=para['gamma'], black_ridges=para['bridges']) + + +class Meijering(Simple): + title = 'Meijering 3D' + +class Sato(Simple): + title = 'Sato 3D' + +class Hessian(Simple): + title = 'Hessian 3D' + +plgs = [Frangi, Meijering, Sato, Hessian] \ No newline at end of file diff --git a/imagepy/menus/Kit3D/__init__.py b/imagepy/menus/Kit3D/__init__.py index b4d4bd10..2b061e35 100644 --- a/imagepy/menus/Kit3D/__init__.py +++ b/imagepy/menus/Kit3D/__init__.py @@ -1 +1 @@ -catlog = ['Filters 3D', 'Binary 3D', '-', 'Analysis 3D', 'Network 3D', '-', 'Viewer 3D'] \ No newline at end of file +catlog = ['Filters 3D', 'Features 3D', 'Binary 3D', '-', 'Analysis 3D', 'Network 3D', '-', 'Viewer 3D'] \ No newline at end of file diff --git a/imagepy/menus/Process/Features/__init__.py b/imagepy/menus/Process/Features/__init__.py index e69de29b..cf734479 100644 --- a/imagepy/menus/Process/Features/__init__.py +++ b/imagepy/menus/Process/Features/__init__.py @@ -0,0 +1 @@ +catlog = ['corner_plgs', '-', 'edge_plgs'] \ No newline at end of file diff --git a/imagepy/menus/Process/Features/ridge_plgs.py b/imagepy/menus/Process/Features/ridge_plgs.py new file mode 100644 index 00000000..ac0d1f26 --- /dev/null +++ b/imagepy/menus/Process/Features/ridge_plgs.py @@ -0,0 +1,30 @@ +from skimage.filters import frangi, sato, hessian ,meijering +from imagepy.core.engine import Filter + +class Frangi(Filter): + title = 'Frangi' + note = ['all', 'auto_msk', 'auto_snap', 'preview'] + para = {'start':1, 'end':10, 'step':2, 'alpha':0.5, 'beta':0.5, 'gamma':15, 'bridges':True} + + view = [(int, 'start', (1,10), 0, 'sigma start', ''), + (int, 'end', (1,20), 0, 'sigma start', ''), + (int, 'step', (1,5), 0, 'sigma step', ''), + (float, 'alpha', (0.1, 1), 1, 'alpha',''), + (float, 'beta', (0.1, 1), 1, 'beta',''), + (float, 'gamma', (1,30), 1, 'gamma',''), + (bool, 'bridges', 'black ridges')] + + def run(self, ips, snap, img, para = None): + rst = frangi(snap, range(para['start'], para['end'], para['step']), + alpha=para['alpha'], beta=para['beta'], gamma=para['gamma'], black_ridges=para['bridges']) + +class Meijering(Filter): + title = 'Meijering' + +class Sato(Filter): + title = 'Sato' + +class Hessian(Filter): + title = 'Hessian' + +plgs = [Frangi, Meijering, Sato, Hessian] \ No newline at end of file diff --git a/imagepy/menus/Process/Threshold/threshold_plgs.py b/imagepy/menus/Process/Threshold/threshold_plgs.py index fe458c98..097c801e 100644 --- a/imagepy/menus/Process/Threshold/threshold_plgs.py +++ b/imagepy/menus/Process/Threshold/threshold_plgs.py @@ -8,7 +8,7 @@ from imagepy.core.engine import Filter import scipy.ndimage as ndimg from skimage.filters import\ - threshold_adaptive, threshold_otsu, threshold_yen,\ + threshold_otsu, threshold_yen,\ threshold_isodata, threshold_li, threshold_local,\ threshold_minimum, threshold_mean, threshold_niblack,\ threshold_sauvola, threshold_triangle, apply_hysteresis_threshold From cd3548d86f9f9e779703dafb5f055fcdf84b2349 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 6 May 2019 00:49:44 +0800 Subject: [PATCH 132/343] shotcut --- imagepy/menus/Kit3D/Features 3D/ridge_plgs.py | 4 +- imagepy/menus/Plugins/Manager/shotcut_wgt.py | 14 +++- imagepy/menus/Selection/roiwindow_wgt.py | 76 ++++++++++++++++++- 3 files changed, 88 insertions(+), 6 deletions(-) diff --git a/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py b/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py index b79ade51..62950313 100644 --- a/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py +++ b/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py @@ -15,8 +15,10 @@ class Frangi(Simple): (bool, 'bridges', 'black ridges')] def run(self, ips, imgs, para = None): - imgs[:] = frangi(imgs, range(para['start'], para['end'], para['step']), + rst = frangi(imgs, range(para['start'], para['end'], para['step']), alpha=para['alpha'], beta=para['beta'], gamma=para['gamma'], black_ridges=para['bridges']) + rst -= rst.min(); rst *= 255/rst.max(); imgs[:] = rst + ips.range = (0, 255) class Meijering(Simple): diff --git a/imagepy/menus/Plugins/Manager/shotcut_wgt.py b/imagepy/menus/Plugins/Manager/shotcut_wgt.py index 9e6f87cd..f15d56b4 100644 --- a/imagepy/menus/Plugins/Manager/shotcut_wgt.py +++ b/imagepy/menus/Plugins/Manager/shotcut_wgt.py @@ -56,7 +56,11 @@ def __init__( self, parent): # Connect Events self.txt_search.Bind( wx.EVT_TEXT, self.on_search ) self.lst_plgs.Bind(wx.EVT_LIST_KEY_DOWN, self.on_run) + self.lst_plgs.Bind( wx.EVT_LIST_ITEM_ACTIVATED, self.on_active) + self.lst_plgs.Bind( wx.EVT_LIST_ITEM_SELECTED, self.on_select) + self.load() + self.active = -1 #def list_plg(self, lst, items def load(self): @@ -74,7 +78,7 @@ def on_search( self, event ): self.buf = [i for i in self.plgs if wd.lower() in i[0].lower()] self.lst_plgs.set_data(self.buf) self.Refresh() - + def ist(self, cont, txt): sep = cont.split('-') if txt in sep: sep.remove(txt) @@ -84,7 +88,15 @@ def ist(self, cont, txt): if len(sep)>0:cas.append(sep[-1]) return '-'.join(cas) + def on_active(self, event): + self.active = event.GetIndex() + + def on_select(self, event): + self.active = -1 + def on_run(self, event): + if self.active != event.GetIndex(): + return IPy.alert('please double click to activate an item') code = event.GetKeyCode() title = self.buf[event.GetIndex()][0] txt = self.buf[event.GetIndex()][1] diff --git a/imagepy/menus/Selection/roiwindow_wgt.py b/imagepy/menus/Selection/roiwindow_wgt.py index 6ae4b632..a5a1561c 100644 --- a/imagepy/menus/Selection/roiwindow_wgt.py +++ b/imagepy/menus/Selection/roiwindow_wgt.py @@ -1,11 +1,11 @@ import wx -from imagepy.core.manager import RoiManager +from imagepy.core.manager import RoiManager, ImageManager from imagepy.core.engine import Macros from imagepy import IPy class VirtualListCtrl(wx.ListCtrl): def __init__(self, parent, title, data=[]): - wx.ListCtrl.__init__(self, parent, style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_VIRTUAL) + wx.ListCtrl.__init__(self, parent, style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_VIRTUAL|wx.LC_EDIT_LABELS) self.title, self.data = title, data #self.Bind(wx.EVT_LIST_CACHE_HINT, self.DoCacheItems) for col, text in enumerate(title): @@ -42,7 +42,7 @@ def __init__( self, parent ): sizer_manage = wx.BoxSizer( wx.VERTICAL ) self.btn_add = wx.Button( self.pan_manage, wx.ID_ANY, u"Add", wx.DefaultPosition, wx.DefaultSize, 0 ) - sizer_manage.Add( self.btn_add, 0, wx.ALL, 5 ) + sizer_manage.Add( self.btn_add, 0, wx.LEFT|wx.RIGHT|wx.TOP, 5 ) self.btn_load = wx.Button( self.pan_manage, wx.ID_ANY, u"Load", wx.DefaultPosition, wx.DefaultSize, 0 ) sizer_manage.Add( self.btn_load, 0, wx.ALL, 5 ) @@ -133,10 +133,16 @@ def __init__( self, parent ): sizer.Add( self.note_book, 0, wx.EXPAND |wx.ALL, 5 ) + sizer_lst = wx.BoxSizer(wx.VERTICAL) + self.lst_rois = VirtualListCtrl(self, ['name', 'type'], []) + self.lst_rois.SetColumnWidth(0, 100) + sizer_lst.Add( self.lst_rois, 1, wx.ALL|wx.EXPAND, 5 ) self.UpdateData() - sizer.Add( self.lst_rois, 1, wx.ALL|wx.EXPAND, 5 ) + self.info = wx.StaticText( self, wx.ID_ANY, 'Information', wx.DefaultPosition, wx.DefaultSize ) + sizer_lst.Add( self.info, 0, wx.ALL|wx.EXPAND, 5 ) + sizer.Add(sizer_lst, 1, wx.ALL|wx.EXPAND, 5 ) self.SetSizer( sizer ) self.Fit() self.Layout() @@ -144,30 +150,92 @@ def __init__( self, parent ): def AddEvent(self): self.btn_add.Bind(wx.EVT_BUTTON, self.on_add) + self.btn_add.Bind(wx.EVT_RIGHT_DOWN, self.on_add_nameless) + self.btn_add.Bind( wx.EVT_ENTER_WINDOW, + lambda e: self.info.SetLabel('right click to add nameless') ) self.btn_load.Bind(wx.EVT_BUTTON, self.on_load) + self.btn_load.Bind( wx.EVT_ENTER_WINDOW, + lambda e: self.info.SetLabel('load selected roi to image') ) self.btn_update.Bind(wx.EVT_BUTTON, self.on_update) + self.btn_update.Bind( wx.EVT_ENTER_WINDOW, + lambda e: self.info.SetLabel('update and refresh the list') ) self.btn_remove.Bind(wx.EVT_BUTTON, self.on_remove) + self.btn_remove.Bind( wx.EVT_ENTER_WINDOW, + lambda e: self.info.SetLabel('remove selected roi from list') ) self.btn_open.Bind(wx.EVT_BUTTON, self.on_open) + self.btn_open.Bind( wx.EVT_ENTER_WINDOW, + lambda e: self.info.SetLabel('open a roi file and load it') ) self.btn_save.Bind(wx.EVT_BUTTON, self.on_save) + self.btn_save.Bind( wx.EVT_ENTER_WINDOW, + lambda e: self.info.SetLabel('save current image roi to file') ) self.btn_inflate.Bind(wx.EVT_BUTTON, self.on_inflate) + self.btn_inflate.Bind( wx.EVT_ENTER_WINDOW, + lambda e: self.info.SetLabel('inflate the current roi') ) self.btn_shrink.Bind(wx.EVT_BUTTON, self.on_shrink) + self.btn_shrink.Bind( wx.EVT_ENTER_WINDOW, + lambda e: self.info.SetLabel('shrink the current roi') ) self.btn_convex.Bind(wx.EVT_BUTTON, self.on_convex) + self.btn_convex.Bind( wx.EVT_ENTER_WINDOW, + lambda e: self.info.SetLabel('make convex hull of current roi') ) self.btn_bound.Bind(wx.EVT_BUTTON, self.on_box) + self.btn_bound.Bind( wx.EVT_ENTER_WINDOW, + lambda e: self.info.SetLabel('make bounding of current roi') ) self.btn_clip.Bind(wx.EVT_BUTTON, self.on_clip) + self.btn_clip.Bind( wx.EVT_ENTER_WINDOW, + lambda e: self.info.SetLabel('clip the region out of image') ) self.btn_invert.Bind(wx.EVT_BUTTON, self.on_invert) + self.btn_invert.Bind( wx.EVT_ENTER_WINDOW, + lambda e: self.info.SetLabel('select invert region on image') ) self.btn_intersect.Bind(wx.EVT_BUTTON, self.on_intersect) + self.btn_intersect.Bind( wx.EVT_ENTER_WINDOW, + lambda e: self.info.SetLabel('intersect selected roi with current') ) self.btn_union.Bind(wx.EVT_BUTTON, self.on_union) + self.btn_union.Bind( wx.EVT_ENTER_WINDOW, + lambda e: self.info.SetLabel('union selected roi with current') ) self.btn_difference.Bind(wx.EVT_BUTTON, self.on_difference) + self.btn_difference.Bind( wx.EVT_ENTER_WINDOW, + lambda e: self.info.SetLabel('clip selected roi with current') ) self.btn_symdiff.Bind(wx.EVT_BUTTON, self.on_symdiff) + self.btn_symdiff.Bind( wx.EVT_ENTER_WINDOW, + lambda e: self.info.SetLabel('symdiff of selected roi and current') ) self.btn_sketch.Bind(wx.EVT_BUTTON, self.on_sketch) + self.btn_sketch.Bind( wx.EVT_ENTER_WINDOW, + lambda e: self.info.SetLabel('sketch current roi') ) self.btn_clear.Bind(wx.EVT_BUTTON, self.on_clear) + self.btn_clear.Bind( wx.EVT_ENTER_WINDOW, + lambda e: self.info.SetLabel('clear pixels in current roi') ) self.btn_clearout.Bind(wx.EVT_BUTTON, self.on_clearout) + self.btn_clearout.Bind( wx.EVT_ENTER_WINDOW, + lambda e: self.info.SetLabel('clear pixels out current roi') ) self.btn_setting.Bind(wx.EVT_BUTTON, self.on_setting) + self.btn_setting.Bind( wx.EVT_ENTER_WINDOW, + lambda e: self.info.SetLabel('set roi color and line width') ) self.lst_rois.Bind( wx.EVT_LIST_ITEM_ACTIVATED, self.on_load) + self.lst_rois.Bind( wx.EVT_ENTER_WINDOW, + lambda e: self.info.SetLabel('double click to load roi') ) + + self.lst_rois.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.on_begin_edit) + self.lst_rois.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.on_end_edit) + + def on_begin_edit(self, event): + self.begin = event.GetText() + + def on_end_edit(self, event): + end = event.GetText() + if end == self.begin or end == '': return + RoiManager.add(end, RoiManager.get(self.begin)) + RoiManager.remove(self.begin) + self.UpdateData() def on_add(self, event): Macros('', ['ROI Add>None']).start(callafter=self.UpdateData) + def on_add_nameless(self, event): + ips = ImageManager.get() + if ips is None: return IPy.alert('No image opened!') + if ips.roi is None: return IPy.alert('No Roi found!') + Macros('', ['ROI Add>{"name":"%s-roi"}'%ips.title]).start(callafter=self.UpdateData) + def on_load(self, event): idx = self.lst_rois.GetFirstSelected() if idx==-1: return IPy.alert('No ROI Selected!') From 0a232eaf354b16ecdb0b4906486685efe33ebd2e Mon Sep 17 00:00:00 2001 From: Jun <1259389904@qq.com> Date: Sun, 5 May 2019 20:01:23 -0400 Subject: [PATCH 133/343] Update ridge_plgs.py Complement Meijering, Sato, Hessian --- imagepy/menus/Kit3D/Features 3D/ridge_plgs.py | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py b/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py index 62950313..3ac68d0d 100644 --- a/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py +++ b/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py @@ -3,7 +3,7 @@ class Frangi(Simple): title = 'Frangi 3D' - note = ['all', 'auto_msk', 'auto_snap'] + note = ['float', 'auto_msk', 'auto_snap'] para = {'start':1, 'end':10, 'step':2, 'alpha':0.5, 'beta':0.5, 'gamma':15, 'bridges':True} view = [(int, 'start', (1,10), 0, 'sigma start', ''), @@ -17,17 +17,37 @@ class Frangi(Simple): def run(self, ips, imgs, para = None): rst = frangi(imgs, range(para['start'], para['end'], para['step']), alpha=para['alpha'], beta=para['beta'], gamma=para['gamma'], black_ridges=para['bridges']) - rst -= rst.min(); rst *= 255/rst.max(); imgs[:] = rst - ips.range = (0, 255) -class Meijering(Simple): - title = 'Meijering 3D' - class Sato(Simple): title = 'Sato 3D' + note = ['float', 'auto_msk', 'auto_snap'] + para = {'start':1, 'end':10, 'step':2, 'bridges':True} + + view = [(int, 'start', (1,10), 0, 'sigma start', ''), + (int, 'end', (1,20), 0, 'sigma start', ''), + (int, 'step', (1,5), 0, 'sigma step', ''), + (bool, 'bridges', 'black ridges')] + + def run(self, ips, imgs, para=None): + imgs[:] = sato(imgs, range(para['start'], para['end'], para['step']), + black_ridges=para['bridges']) class Hessian(Simple): title = 'Hessian 3D' + note = ['float', 'auto_msk', 'auto_snap'] + para = {'start':1, 'end':10, 'step':2, 'alpha':0.5, 'beta':0.5, 'gamma':15, 'bridges':True} + + view = [(int, 'start', (1,10), 0, 'sigma start', ''), + (int, 'end', (1,20), 0, 'sigma start', ''), + (int, 'step', (1,5), 0, 'sigma step', ''), + (float, 'alpha', (0.1, 1), 1, 'alpha',''), + (float, 'beta', (0.1, 1), 1, 'beta',''), + (float, 'gamma', (1,30), 1, 'gamma',''), + (bool, 'bridges', 'black ridges')] + + def run(self, ips, imgs, para = None): + imgs[:] = hessian(imgs, range(para['start'], para['end'], para['step']), + alpha=para['alpha'], beta=para['beta'], gamma=para['gamma'], black_ridges=para['bridges']) -plgs = [Frangi, Meijering, Sato, Hessian] \ No newline at end of file +plgs = [Frangi, Meijering, Sato, Hessian] From 2d8ca198c3c72306f0e8c4b04053af4de63111bb Mon Sep 17 00:00:00 2001 From: Jun <1259389904@qq.com> Date: Sun, 5 May 2019 22:51:44 -0400 Subject: [PATCH 134/343] Update ridge_plgs.py add Meijering filters --- imagepy/menus/Kit3D/Features 3D/ridge_plgs.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py b/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py index 3ac68d0d..f190fec2 100644 --- a/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py +++ b/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py @@ -15,9 +15,21 @@ class Frangi(Simple): (bool, 'bridges', 'black ridges')] def run(self, ips, imgs, para = None): - rst = frangi(imgs, range(para['start'], para['end'], para['step']), + imgs[:] = frangi(imgs, range(para['start'], para['end'], para['step']), alpha=para['alpha'], beta=para['beta'], gamma=para['gamma'], black_ridges=para['bridges']) +class Meijering(Simple): + title = 'Meijering 3D' + note = ['float', 'auto_msk', 'auto_snap'] + para = {'start':1, 'end':10, 'step':2, 'bridges':True} + + view = [(int, 'start', (1,10), 0, 'sigma start', ''), + (int, 'end', (1,20), 0, 'sigma start', ''), + (int, 'step', (1,5), 0, 'sigma step', ''), + (bool, 'bridges', 'black ridges')] + def run(self, ips, imgs, para=None): + imgs[:] = meijering(imgs, range(para['start'], para['end'], para['step']), + black_ridges=para['bridges']) class Sato(Simple): title = 'Sato 3D' From ed2a8c06c7655b148710923fb467c2066399b1fc Mon Sep 17 00:00:00 2001 From: Jun <1259389904@qq.com> Date: Mon, 6 May 2019 11:12:55 -0400 Subject: [PATCH 135/343] Update ridge_plgs.py improve show_img --- imagepy/menus/Kit3D/Features 3D/ridge_plgs.py | 44 +++++++++++-------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py b/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py index f190fec2..fa2369a5 100644 --- a/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py +++ b/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py @@ -1,13 +1,15 @@ from skimage.filters import frangi, sato, hessian ,meijering from imagepy.core.engine import Filter, Simple +from imagepy import IPy + class Frangi(Simple): title = 'Frangi 3D' - note = ['float', 'auto_msk', 'auto_snap'] - para = {'start':1, 'end':10, 'step':2, 'alpha':0.5, 'beta':0.5, 'gamma':15, 'bridges':True} + note = ['float', 'auto_msk', 'auto_snap', 'stack3d'] + para = {'start':1, 'end':10, 'step':2, 'alpha':0.5, 'beta':0.5, 'gamma':15, 'bridges':False} view = [(int, 'start', (1,10), 0, 'sigma start', ''), - (int, 'end', (1,20), 0, 'sigma start', ''), + (int, 'end', (1,20), 0, 'sigma end', ''), (int, 'step', (1,5), 0, 'sigma step', ''), (float, 'alpha', (0.1, 1), 1, 'alpha',''), (float, 'beta', (0.1, 1), 1, 'beta',''), @@ -15,43 +17,46 @@ class Frangi(Simple): (bool, 'bridges', 'black ridges')] def run(self, ips, imgs, para = None): - imgs[:] = frangi(imgs, range(para['start'], para['end'], para['step']), - alpha=para['alpha'], beta=para['beta'], gamma=para['gamma'], black_ridges=para['bridges']) + IPy.show_img(frangi(imgs, range(para['start'], para['end'], para['step']), + alpha=para['alpha'], beta=para['beta'], gamma=para['gamma'], + black_ridges=para['bridges']), ips.title+'-frangi') + + class Meijering(Simple): title = 'Meijering 3D' - note = ['float', 'auto_msk', 'auto_snap'] - para = {'start':1, 'end':10, 'step':2, 'bridges':True} + note = ['float', 'auto_msk', 'auto_snap','stack3d'] + para = {'start':1, 'end':10, 'step':2, 'bridges':False} view = [(int, 'start', (1,10), 0, 'sigma start', ''), - (int, 'end', (1,20), 0, 'sigma start', ''), + (int, 'end', (1,20), 0, 'sigma end', ''), (int, 'step', (1,5), 0, 'sigma step', ''), (bool, 'bridges', 'black ridges')] def run(self, ips, imgs, para=None): - imgs[:] = meijering(imgs, range(para['start'], para['end'], para['step']), - black_ridges=para['bridges']) + IPy.show_img(meijering(imgs, range(para['start'], para['end'], para['step']), + black_ridges=para['bridges']), ips.title+'-meijering') class Sato(Simple): title = 'Sato 3D' - note = ['float', 'auto_msk', 'auto_snap'] - para = {'start':1, 'end':10, 'step':2, 'bridges':True} + note = ['float', 'auto_msk', 'auto_snap', 'stack3d'] + para = {'start':1, 'end':10, 'step':2, 'bridges':False} view = [(int, 'start', (1,10), 0, 'sigma start', ''), - (int, 'end', (1,20), 0, 'sigma start', ''), + (int, 'end', (1,20), 0, 'sigma end', ''), (int, 'step', (1,5), 0, 'sigma step', ''), (bool, 'bridges', 'black ridges')] def run(self, ips, imgs, para=None): - imgs[:] = sato(imgs, range(para['start'], para['end'], para['step']), - black_ridges=para['bridges']) + IPy.show_img(sato(imgs, range(para['start'], para['end'], para['step']), + black_ridges=para['bridges']), ips.title+'-sato') class Hessian(Simple): title = 'Hessian 3D' - note = ['float', 'auto_msk', 'auto_snap'] + note = ['float', 'auto_msk', 'auto_snap','stack3d'] para = {'start':1, 'end':10, 'step':2, 'alpha':0.5, 'beta':0.5, 'gamma':15, 'bridges':True} view = [(int, 'start', (1,10), 0, 'sigma start', ''), - (int, 'end', (1,20), 0, 'sigma start', ''), + (int, 'end', (1,20), 0, 'sigma end', ''), (int, 'step', (1,5), 0, 'sigma step', ''), (float, 'alpha', (0.1, 1), 1, 'alpha',''), (float, 'beta', (0.1, 1), 1, 'beta',''), @@ -59,7 +64,8 @@ class Hessian(Simple): (bool, 'bridges', 'black ridges')] def run(self, ips, imgs, para = None): - imgs[:] = hessian(imgs, range(para['start'], para['end'], para['step']), - alpha=para['alpha'], beta=para['beta'], gamma=para['gamma'], black_ridges=para['bridges']) + IPy.show_img(hessian(imgs, range(para['start'], para['end'], para['step']), + alpha=para['alpha'], beta=para['beta'], gamma=para['gamma'], + black_ridges=para['bridges']), ips.title+'-hessian') plgs = [Frangi, Meijering, Sato, Hessian] From b1a6360d913e4f2da8215ca26d9aacb3a8143d05 Mon Sep 17 00:00:00 2001 From: Jun <1259389904@qq.com> Date: Mon, 6 May 2019 14:10:08 -0400 Subject: [PATCH 136/343] Update ridge_plgs.py Complement 2D ridge filter --- imagepy/menus/Process/Features/ridge_plgs.py | 75 ++++++++++++++++---- 1 file changed, 63 insertions(+), 12 deletions(-) diff --git a/imagepy/menus/Process/Features/ridge_plgs.py b/imagepy/menus/Process/Features/ridge_plgs.py index ac0d1f26..93bb83f5 100644 --- a/imagepy/menus/Process/Features/ridge_plgs.py +++ b/imagepy/menus/Process/Features/ridge_plgs.py @@ -1,30 +1,81 @@ from skimage.filters import frangi, sato, hessian ,meijering -from imagepy.core.engine import Filter +from imagepy.core.engine import Filter, Simple +from imagepy import IPy -class Frangi(Filter): +class Frangi(Simple): title = 'Frangi' - note = ['all', 'auto_msk', 'auto_snap', 'preview'] - para = {'start':1, 'end':10, 'step':2, 'alpha':0.5, 'beta':0.5, 'gamma':15, 'bridges':True} + note = ['float', '8-bit', 'int'] + para = {'start':1, 'end':10, 'step':2, 'alpha':0.5, 'beta':0.5, 'gamma':15, 'bridges':False} view = [(int, 'start', (1,10), 0, 'sigma start', ''), - (int, 'end', (1,20), 0, 'sigma start', ''), + (int, 'end', (1,20), 0, 'sigma end', ''), (int, 'step', (1,5), 0, 'sigma step', ''), (float, 'alpha', (0.1, 1), 1, 'alpha',''), (float, 'beta', (0.1, 1), 1, 'beta',''), (float, 'gamma', (1,30), 1, 'gamma',''), (bool, 'bridges', 'black ridges')] - def run(self, ips, snap, img, para = None): - rst = frangi(snap, range(para['start'], para['end'], para['step']), - alpha=para['alpha'], beta=para['beta'], gamma=para['gamma'], black_ridges=para['bridges']) + def run(self, ips, imgs, para = None): + lst = [] + for img in imgs: + lst.append(frangi(img, range(para['start'], para['end'], para['step']), + alpha=para['alpha'], beta=para['beta'], gamma=para['gamma'], + black_ridges=para['bridges'])) + IPy.show_img(lst, ips.title + '-frangi') -class Meijering(Filter): +class Meijering(Simple): title = 'Meijering' + note = ['float', '8-bit', 'int'] + para = {'start':1, 'end':10, 'step':2, 'bridges':False} -class Sato(Filter): + view = [(int, 'start', (1,10), 0, 'sigma start', ''), + (int, 'end', (1,20), 0, 'sigma end', ''), + (int, 'step', (1,5), 0, 'sigma step', ''), + (bool, 'bridges', 'black ridges')] + + def run(self, ips, imgs, para=None): + lst = [] + for img in imgs: + lst.append(meijering(img, range(para['start'], para['end'], para['step']), + black_ridges=para['bridges'])) + IPy.show_img(lst, ips.title + '-meijering') + + +class Sato(Simple): title = 'Sato' + note = ['float', '8-bit', 'int'] + para = {'start':1, 'end':10, 'step':2, 'bridges':False} + view = [(int, 'start', (1,10), 0, 'sigma start', ''), + (int, 'end', (1,20), 0, 'sigma end', ''), + (int, 'step', (1,5), 0, 'sigma step', ''), + (bool, 'bridges', 'black ridges')] + + def run(self, ips, imgs, para = None): + lst = [] + for img in imgs: + lst.append(sato(img, range(para['start'], para['end'], para['step']), + black_ridges=para['bridges'])) + IPy.show_img(lst, ips.title + '-sato') -class Hessian(Filter): +class Hessian(Simple): title = 'Hessian' + note = ['float', '8-bit', 'int'] + para = {'start':1, 'end':10, 'step':2, 'alpha':0.5, 'beta':0.5, 'gamma':15, 'bridges':False} + + view = [(int, 'start', (1,10), 0, 'sigma start', ''), + (int, 'end', (1,20), 0, 'sigma end', ''), + (int, 'step', (1,5), 0, 'sigma step', ''), + (float, 'alpha', (0.1, 1), 1, 'alpha',''), + (float, 'beta', (0.1, 1), 1, 'beta',''), + (float, 'gamma', (1,30), 1, 'gamma',''), + (bool, 'bridges', 'black ridges')] + + def run(self, ips, imgs, para = None): + lst = [] + for img in imgs: + lst.append(hessian(img, range(para['start'], para['end'], para['step']), + alpha=para['alpha'], beta=para['beta'], gamma=para['gamma'], + black_ridges=para['bridges'])) + IPy.show_img(lst, ips.title + '-hessian') -plgs = [Frangi, Meijering, Sato, Hessian] \ No newline at end of file +plgs = [Frangi, Meijering, Sato, Hessian] From 93b8f635c3c950ed043506d60dc03486fb26ba08 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 8 May 2019 15:54:53 +0800 Subject: [PATCH 137/343] ridge plgs --- imagepy/core/util/fileio.py | 6 ++ imagepy/menus/Process/Features/ridge_plgs.py | 63 +++++++++----------- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/imagepy/core/util/fileio.py b/imagepy/core/util/fileio.py index ec388a72..1dd8c418 100644 --- a/imagepy/core/util/fileio.py +++ b/imagepy/core/util/fileio.py @@ -54,6 +54,12 @@ def run(self, para = None): fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) read = ReaderManager.get(fe[1:], None) + if read is None: + a, b = os.path.splitext(fn) + fn, fe = a, b+fe + read = ReaderManager.get(fe[1:], None) + if read is None: + return IPy.alert('No reader found for %s'%fe[1:]) view = ViewerManager.get(fe[1:]) #group, read = (True, read[0]) if isinstance(read, tuple) else (False, read) diff --git a/imagepy/menus/Process/Features/ridge_plgs.py b/imagepy/menus/Process/Features/ridge_plgs.py index 93bb83f5..9c3f582d 100644 --- a/imagepy/menus/Process/Features/ridge_plgs.py +++ b/imagepy/menus/Process/Features/ridge_plgs.py @@ -2,9 +2,14 @@ from imagepy.core.engine import Filter, Simple from imagepy import IPy -class Frangi(Simple): +def scale(img, low, high): + img *= (high-low)/(max(img.ptp(), 1e-5)) + img += low - img.min() + return img + +class Frangi(Filter): title = 'Frangi' - note = ['float', '8-bit', 'int'] + note = ['all', 'auto_msk', 'auto_snap', 'preview'] para = {'start':1, 'end':10, 'step':2, 'alpha':0.5, 'beta':0.5, 'gamma':15, 'bridges':False} view = [(int, 'start', (1,10), 0, 'sigma start', ''), @@ -15,17 +20,14 @@ class Frangi(Simple): (float, 'gamma', (1,30), 1, 'gamma',''), (bool, 'bridges', 'black ridges')] - def run(self, ips, imgs, para = None): - lst = [] - for img in imgs: - lst.append(frangi(img, range(para['start'], para['end'], para['step']), - alpha=para['alpha'], beta=para['beta'], gamma=para['gamma'], - black_ridges=para['bridges'])) - IPy.show_img(lst, ips.title + '-frangi') + def run(self, ips, snap, img, para = None): + rst = frangi(snap, range(para['start'], para['end'], para['step']), alpha=para['alpha'], + beta=para['beta'], gamma=para['gamma'], black_ridges=para['bridges']) + img[:] = scale(rst, ips.range[0], ips.range[1]) -class Meijering(Simple): +class Meijering(Filter): title = 'Meijering' - note = ['float', '8-bit', 'int'] + note = ['all', 'auto_msk', 'auto_snap', 'preview'] para = {'start':1, 'end':10, 'step':2, 'bridges':False} view = [(int, 'start', (1,10), 0, 'sigma start', ''), @@ -33,33 +35,27 @@ class Meijering(Simple): (int, 'step', (1,5), 0, 'sigma step', ''), (bool, 'bridges', 'black ridges')] - def run(self, ips, imgs, para=None): - lst = [] - for img in imgs: - lst.append(meijering(img, range(para['start'], para['end'], para['step']), - black_ridges=para['bridges'])) - IPy.show_img(lst, ips.title + '-meijering') - + def run(self, ips, snap, img, para = None): + rst =meijering(snap, range(para['start'], para['end'], para['step']), black_ridges=para['bridges']) + print('hahaha') + img[:] = scale(rst, ips.range[0], ips.range[1]) -class Sato(Simple): +class Sato(Filter): title = 'Sato' - note = ['float', '8-bit', 'int'] + note = ['all', 'auto_msk', 'auto_snap', 'preview'] para = {'start':1, 'end':10, 'step':2, 'bridges':False} view = [(int, 'start', (1,10), 0, 'sigma start', ''), (int, 'end', (1,20), 0, 'sigma end', ''), (int, 'step', (1,5), 0, 'sigma step', ''), (bool, 'bridges', 'black ridges')] - def run(self, ips, imgs, para = None): - lst = [] - for img in imgs: - lst.append(sato(img, range(para['start'], para['end'], para['step']), - black_ridges=para['bridges'])) - IPy.show_img(lst, ips.title + '-sato') + def run(self, ips, snap, img, para = None): + rst = sato(snap, range(para['start'], para['end'], para['step']), black_ridges=para['bridges']) + img[:] = scale(rst, ips.range[0], ips.range[1]) -class Hessian(Simple): +class Hessian(Filter): title = 'Hessian' - note = ['float', '8-bit', 'int'] + note = ['all', 'auto_msk', 'auto_snap', 'preview'] para = {'start':1, 'end':10, 'step':2, 'alpha':0.5, 'beta':0.5, 'gamma':15, 'bridges':False} view = [(int, 'start', (1,10), 0, 'sigma start', ''), @@ -70,12 +66,9 @@ class Hessian(Simple): (float, 'gamma', (1,30), 1, 'gamma',''), (bool, 'bridges', 'black ridges')] - def run(self, ips, imgs, para = None): - lst = [] - for img in imgs: - lst.append(hessian(img, range(para['start'], para['end'], para['step']), - alpha=para['alpha'], beta=para['beta'], gamma=para['gamma'], - black_ridges=para['bridges'])) - IPy.show_img(lst, ips.title + '-hessian') + def run(self, ips, snap, img, para = None): + rst = hessian(snap, range(para['start'], para['end'], para['step']), alpha=para['alpha'], + beta=para['beta'], gamma=para['gamma'], black_ridges=para['bridges']) + img[:] = scale(rst, ips.range[0], ips.range[1]) plgs = [Frangi, Meijering, Sato, Hessian] From 29de5d54c61a5ca7b0c40a69a6d41986eadcf64e Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 9 May 2019 09:55:57 +0800 Subject: [PATCH 138/343] flood fill --- imagepy/core/draw/fill.py | 8 ++--- imagepy/core/engine/filter.py | 8 +++-- imagepy/tools/Draw/__init__.py | 1 + imagepy/tools/Draw/flood3d.gif | Bin 0 -> 87 bytes imagepy/tools/Draw/flood3d_tol.py | 39 +++++++++++++++++++++++++ imagepy/tools/Draw/floodfill_tol.py | 17 ++++++----- imagepy/tools/Draw/magic_tol.py | 25 +++++++++++----- imagepy/tools/Standard/magic_tol.py | 18 ++++++++---- imagepy/tools/Toolkit3D/flood3d.gif | Bin 0 -> 87 bytes imagepy/tools/Toolkit3D/flood3d_tol.py | 39 +++++++++++++++++++++++++ imagepy/ui/widgets/curvepanel.py | 2 +- 11 files changed, 127 insertions(+), 30 deletions(-) create mode 100644 imagepy/tools/Draw/flood3d.gif create mode 100644 imagepy/tools/Draw/flood3d_tol.py create mode 100644 imagepy/tools/Toolkit3D/flood3d.gif create mode 100644 imagepy/tools/Toolkit3D/flood3d_tol.py diff --git a/imagepy/core/draw/fill.py b/imagepy/core/draw/fill.py index ce099aa5..fda9c7d9 100644 --- a/imagepy/core/draw/fill.py +++ b/imagepy/core/draw/fill.py @@ -2,7 +2,7 @@ import numpy as np from scipy.ndimage import label, generate_binary_structure -def floodfill(img, x, y, thr, con): +def floodfill2(img, x, y, thr, con): color = img[int(y), int(x)] buf = np.subtract(img, color, dtype=np.int16) msk = np.abs(buf)<=thr @@ -11,9 +11,5 @@ def floodfill(img, x, y, thr, con): buf = buf[:,:,0] strc = generate_binary_structure(2, con+1) label(msk, strc, output = buf) - msk = buf == buf[int(y), int(x)] - #msk[[0,-1],:], msk[:,[0,-1]] = 0, 0 - - #imsave('test.png', msk.astype(np.uint8)) - + msk = buf == buf[int(y), int(x)] return msk \ No newline at end of file diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index 2d1beec5..2f56864a 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -13,7 +13,7 @@ from ...core.manager import TextLogManager, ImageManager, \ WindowsManager, TaskManager, WidgetsManager, DocumentManager from time import time - + def process_channels(plg, ips, src, des, para): if ips.channels>1 and not 'not_channel' in plg.note: for i in range(ips.channels): @@ -39,8 +39,10 @@ def process_one(plg, ips, src, img, para, callafter=None): src = src.astype(np.float32) rst = process_channels(plg, ips, src, buf if transint or transfloat else img, para) if not img is rst and not rst is None: - imgrange = {np.uint8:(0,255), np.uint16:(0, 65535)}[img.dtype.type] - np.clip(rst, imgrange[0], imgrange[1], out=img) + imgrange = {np.uint8:(0,255), np.uint16:(0, 65535)} + if img.dtype.type in imgrange: + np.clip(rst, imgrange[0], imgrange[1], out=img) + else: img[:] = rst if 'auto_msk' in plg.note and not ips.get_msk() is None: msk = True ^ ips.get_msk() img[msk] = src[msk] diff --git a/imagepy/tools/Draw/__init__.py b/imagepy/tools/Draw/__init__.py index e69de29b..7f91fe32 100644 --- a/imagepy/tools/Draw/__init__.py +++ b/imagepy/tools/Draw/__init__.py @@ -0,0 +1 @@ +catlog = ['magic_tol', 'floodfill_tol', 'flood3d_tol'] \ No newline at end of file diff --git a/imagepy/tools/Draw/flood3d.gif b/imagepy/tools/Draw/flood3d.gif new file mode 100644 index 0000000000000000000000000000000000000000..d1bb00dcecbb206b49a7db3be07dff8071618300 GIT binary patch literal 87 zcmZ?wbhEHb6krfwXkY+>Y5)KKSNzGs$iTqJpaT*B$uKbK_2e&Tm^Y5)KKSNzGs$iTqJpaT*B$uKbK_2e&Tm^ Date: Sat, 11 May 2019 22:43:43 +0800 Subject: [PATCH 139/343] pypubsub --- imagepy/IPy.py | 2 +- imagepy/core/draw/paint.py | 2 +- imagepy/core/engine/filter.py | 8 ++--- imagepy/core/engine/macros.py | 2 +- imagepy/core/myvi/canvas3d.py | 2 +- imagepy/core/roi/rectangleroi.py | 5 --- imagepy/menus/Analysis/statistic_plg.py | 2 +- imagepy/tools/Standard/painter_tol.py | 42 +++++++++++++------------ setup.py | 3 +- 9 files changed, 33 insertions(+), 35 deletions(-) diff --git a/imagepy/IPy.py b/imagepy/IPy.py index 809b70e0..76217f08 100644 --- a/imagepy/IPy.py +++ b/imagepy/IPy.py @@ -6,7 +6,7 @@ """ import wx, os.path as osp -from wx.lib.pubsub import pub +from pubsub import pub root_dir = osp.abspath(osp.dirname(__file__)) diff --git a/imagepy/core/draw/paint.py b/imagepy/core/draw/paint.py index b6832a55..b54746d8 100644 --- a/imagepy/core/draw/paint.py +++ b/imagepy/core/draw/paint.py @@ -48,7 +48,7 @@ def draw_line(self, img, x1, y1, x2, y2, w=None, color=None): n = max(abs(dx), abs(dy)) + 1 xs = np.linspace(x1, x2, n).round().astype(np.int16) ys = np.linspace(y1, y2, n).round().astype(np.int16) - for x,y in zip(xs, ys): + for x, y in zip(xs, ys): self.draw_point(img, x, y, w, color) def lineto(self, img, x, y, w=None, color=None): diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index 2f56864a..d848f404 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -29,6 +29,7 @@ def process_channels(plg, ips, src, des, para): def process_one(plg, ips, src, img, para, callafter=None): TaskManager.add(plg) start = time() + transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16) transfloat = '2float' in plg.note and not ips.dtype in (np.complex128, np.float32, np.float64) if transint: @@ -39,10 +40,8 @@ def process_one(plg, ips, src, img, para, callafter=None): src = src.astype(np.float32) rst = process_channels(plg, ips, src, buf if transint or transfloat else img, para) if not img is rst and not rst is None: - imgrange = {np.uint8:(0,255), np.uint16:(0, 65535)} - if img.dtype.type in imgrange: - np.clip(rst, imgrange[0], imgrange[1], out=img) - else: img[:] = rst + imgrange = {np.uint8:(0,255), np.uint16:(0, 65535)}[img.dtype.type] + np.clip(rst, imgrange[0], imgrange[1], out=img) if 'auto_msk' in plg.note and not ips.get_msk() is None: msk = True ^ ips.get_msk() img[msk] = src[msk] @@ -54,6 +53,7 @@ def process_one(plg, ips, src, img, para, callafter=None): def process_stack(plg, ips, src, imgs, para, callafter=None): TaskManager.add(plg) start = time() + transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16) transfloat = '2float' in plg.note and not ips.dtype in (np.complex128, np.float32, np.float64) if transint: diff --git a/imagepy/core/engine/macros.py b/imagepy/core/engine/macros.py index 54cc6403..e04387b4 100644 --- a/imagepy/core/engine/macros.py +++ b/imagepy/core/engine/macros.py @@ -6,7 +6,7 @@ import wx from ... import IPy from ...core.manager import PluginsManager -from wx.lib.pubsub import pub +from pubsub import pub from imagepy.core.manager import ReaderManager, ViewerManager from imagepy import IPy diff --git a/imagepy/core/myvi/canvas3d.py b/imagepy/core/myvi/canvas3d.py index 72959ca7..ea778f86 100644 --- a/imagepy/core/myvi/canvas3d.py +++ b/imagepy/core/myvi/canvas3d.py @@ -5,7 +5,7 @@ import wx.glcanvas as glcanvas from .manager import * import os.path as osp -from wx.lib.pubsub import pub +from pubsub import pub from .util import * class Canvas3D(glcanvas.GLCanvas): diff --git a/imagepy/core/roi/rectangleroi.py b/imagepy/core/roi/rectangleroi.py index 208a6de5..3356b802 100644 --- a/imagepy/core/roi/rectangleroi.py +++ b/imagepy/core/roi/rectangleroi.py @@ -76,11 +76,6 @@ def topolygon(self): pg.body.append([self.body, []]) return pg - ''' - def affine(self, m, o): - return self.topolygon().affine(m,o) - ''' - def draw(self, dc, f, **key): dc.SetPen(wx.Pen(RoiManager.get_color(), width=RoiManager.get_lw(), style=wx.SOLID)) if(len(self.body)>1): diff --git a/imagepy/menus/Analysis/statistic_plg.py b/imagepy/menus/Analysis/statistic_plg.py index 43e08a87..36e4af41 100644 --- a/imagepy/menus/Analysis/statistic_plg.py +++ b/imagepy/menus/Analysis/statistic_plg.py @@ -9,7 +9,7 @@ from imagepy.core.roi import PointRoi from imagepy.core.manager import ImageManager, WindowsManager from imagepy.ui.widgets import HistCanvas -from wx.lib.pubsub import pub +from pubsub import pub import pandas as pd class HistogramFrame(wx.Frame): diff --git a/imagepy/tools/Standard/painter_tol.py b/imagepy/tools/Standard/painter_tol.py index c72776b2..b9d54e58 100644 --- a/imagepy/tools/Standard/painter_tol.py +++ b/imagepy/tools/Standard/painter_tol.py @@ -1,36 +1,38 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Oct 19 17:35:09 2016 - -@author: yxl -""" -from imagepy.core.draw import paint from imagepy.core.engine import Tool -import wx +from skimage.draw import line, circle + +def drawline(img, oldp, newp, w, value): + if img.ndim == 2: value = sum(value)/3 + oy, ox = line(*[int(round(i)) for i in oldp+newp]) + cy, cx = circle(0, 0, w/2+1e-6) + ys = (oy.reshape((-1,1))+cy).clip(0, img.shape[0]-1) + xs = (ox.reshape((-1,1))+cx).clip(0, img.shape[1]-1) + img[ys.ravel(), xs.ravel()] = value class Plugin(Tool): title = 'Pencil' - view = [(int, 'width', (0,30), 0, 'width', 'pix')] - para = {'width':1} + + para = {'width':1, 'value':(255, 255, 255)} + view = [(int, 'width', (0,30), 0, 'width', 'pix'), + ('color', 'value', 'color', '')] def __init__(self): - self.sta = 0 - self.paint = paint.Paint() - self.cursor = wx.CURSOR_CROSS + self.status = False + self.oldp = (0,0) def mouse_down(self, ips, x, y, btn, **key): - self.sta = 1 - self.paint.set_curpt(int(x), int(y)) + self.status = True + self.oldp = (y, x) ips.snapshot() def mouse_up(self, ips, x, y, btn, **key): - self.paint.lineto(ips.img, int(x), int(y), self.para['width']) - ips.update() - self.sta = 0 + self.status = False def mouse_move(self, ips, x, y, btn, **key): - if self.sta==0:return - self.paint.lineto(ips.img, int(x), int(y), self.para['width']) + if not self.status:return + w, value = self.para['width'], self.para['value'] + drawline(ips.img, self.oldp, (y, x), w, value) + self.oldp = (y, x) ips.update() def mouse_wheel(self, ips, x, y, d, **key):pass \ No newline at end of file diff --git a/setup.py b/setup.py index 77ca002b..f2eb07bb 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,8 @@ def get_data_files(): install_requires=[ 'scikit-image', 'shapely', - 'wxpython-installer', + 'pypubsub', + 'wxpython', 'read_roi', 'numpy-stl', 'pydicom', From af1c449e8243f40f67495296ffb19f88405a0f3b Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 17 May 2019 10:29:16 +0800 Subject: [PATCH 140/343] slic --- .../Analysis/Region Analysis/regionprops_plgs.py | 4 ++-- .../Analysis/Region Analysis/statistic_plgs.py | 16 ++++++++++------ imagepy/menus/Process/Segment/shift_plgs.py | 3 +++ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py index d83903a0..9a9f103d 100644 --- a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py @@ -15,7 +15,7 @@ # center, area, l, extent, cov class RegionCounter(Simple): title = 'Geometry Analysis' - note = ['8-bit', '16-bit'] + note = ['8-bit', '16-bit', 'int'] para = {'con':'8-connect', 'center':True, 'area':True, 'l':True, 'extent':False, 'cov':False, 'slice':False, 'ed':False, 'holes':False, 'ca':False, 'fa':False, 'solid':False} view = [(list, 'con', ['4-connect', '8-connect'], str, 'conection', 'pix'), @@ -98,7 +98,7 @@ def run(self, ips, imgs, para = None): # center, area, l, extent, cov class RegionFilter(Filter): title = 'Geometry Filter' - note = ['8-bit', '16-bit', 'auto_msk', 'auto_snap','preview'] + note = ['8-bit', '16-bit', 'int', 'auto_msk', 'auto_snap','preview'] para = {'con':'4-connect', 'inv':False, 'area':0, 'l':0, 'holes':0, 'solid':0, 'e':0, 'front':255, 'back':100} view = [(list, 'con', ['4-connect', '8-connect'], str, 'conection', 'pix'), (bool, 'inv', 'invert'), diff --git a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py index 86c1698c..c24e8623 100644 --- a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py @@ -42,14 +42,15 @@ def draw(self, dc, f, **key): class RegionStatistic(Simple): title = 'Intensity Analysis' - note = ['8-bit', '16-bit'] + note = ['8-bit', '16-bit', 'int'] para = {'con':'8-connect','inten':None, 'slice':False, 'max':True, 'min':True,'mean':False, - 'center':True, 'var':False,'std':False,'sum':False, 'extent':False} + 'center':True, 'var':False,'std':False,'sum':False, 'extent':False, 'labeled':False} view = [('img', 'inten', 'intensity', ''), (list, 'con', ['4-connect', '8-connect'], str, 'conection', 'pix'), (bool, 'slice', 'slice'), + (bool, 'labeled', 'has labeled'), ('lab', None, '========= indecate ========='), (bool, 'center', 'center'), (bool, 'extent', 'extent'), @@ -70,7 +71,7 @@ def run(self, ips, imgs, para = None): imgs = inten.imgs if len(msks)==1: msks *= len(imgs) - buf = imgs[0].astype(np.uint16) + buf = imgs[0].astype(np.uint32) strc = ndimage.generate_binary_structure(2, 1 if para['con']=='4-connect' else 2) idct = ['Max','Min','Mean','Variance','Standard','Sum'] key = {'Max':'max','Min':'min','Mean':'mean', @@ -84,7 +85,9 @@ def run(self, ips, imgs, para = None): data, mark = [],{'type':'layers', 'body':{}} # data,mark=[],[] for i in range(len(imgs)): - n = ndimage.label(msks[i], strc, output=buf) + if para['labeled']: + n, buf[:] = msks[i].max(), msks[i] + else: n = ndimage.label(msks[i], strc, output=buf) index = range(1, n+1) dt = [] if para['slice']:dt.append([i]*n) @@ -97,7 +100,8 @@ def run(self, ips, imgs, para = None): boxs = [None] * n if para['extent']: boxs = ndimage.find_objects(buf) - boxs = [( i[1].start+(i[1].stop-i[1].start)/2, i[0].start+(i[0].stop-i[0].start)/2, i[1].stop-i[1].start,i[0].stop-i[0].start) for i in boxs] + boxs = [( i[1].start+(i[1].stop-i[1].start)/2, i[0].start+(i[0].stop-i[0].start)/2, + i[1].stop-i[1].start,i[0].stop-i[0].start) for i in boxs] for j in (0,1,2,3): dt.append([i[j]*k for i in boxs]) if para['max']:dt.append(ndimage.maximum(imgs[i], buf, index).round(2)) @@ -145,7 +149,7 @@ def draw(self, dc, f, **key): class IntensityFilter(Filter): title = 'Intensity Filter' - note = ['8-bit', '16-bit', 'auto_msk', 'auto_snap', 'not_slice', 'preview'] + note = ['8-bit', '16-bit', 'int', 'auto_msk', 'auto_snap', 'not_slice', 'preview'] para = {'con':'4-connect', 'inten':None, 'max':0, 'min':0, 'mean':0, 'std':0, 'sum':0, 'front':255, 'back':0} view = [('img', 'inten', 'intensity', ''), (list, 'con', ['4-connect', '8-connect'], str, 'conection', 'pix'), diff --git a/imagepy/menus/Process/Segment/shift_plgs.py b/imagepy/menus/Process/Segment/shift_plgs.py index c5710d93..df3590c2 100644 --- a/imagepy/menus/Process/Segment/shift_plgs.py +++ b/imagepy/menus/Process/Segment/shift_plgs.py @@ -71,6 +71,7 @@ def run(self, ips, imgs, para = None): for i in range(len(imgs)): rst.append(segmentation.slic(imgs[i], para['n_segments'], para['compactness'], para['max_iter'], para['sigma']).astype('int32')) + rst[-1] += 1 self.progress(i, len(imgs)) IPy.show_img(rst, ips.title+'-sliclab') @@ -100,6 +101,7 @@ def run(self, ips, imgs, para = None): for i in range(len(imgs)): rst.append(segmentation.quickshift(imgs[i], para['ratio'], para['kernel_size'], para['max_dist'], para['sigma']).astype('int32')) + rst[-1] += 1 self.progress(i, len(imgs)) IPy.show_img(rst, ips.title+'-quickshiftlab') @@ -127,6 +129,7 @@ def run(self, ips, imgs, para = None): for i in range(len(imgs)): rst.append(segmentation.felzenszwalb(imgs[i], para['scale'], para['sigma'], para['min_size']).astype('int32')) + rst[-1] += 1 self.progress(i, len(imgs)) IPy.show_img(rst, ips.title+'-felzenszwalblab') From 4995ba28f6d3345bff5832436b21cf3d7b3c0205 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 23 May 2019 22:32:40 +0800 Subject: [PATCH 141/343] center --- imagepy/menus/Analysis/Region Analysis/statistic_plgs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py index c24e8623..dcbbd9b0 100644 --- a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py @@ -93,7 +93,7 @@ def run(self, ips, imgs, para = None): if para['slice']:dt.append([i]*n) dt.append(range(n)) - xy = ndimage.center_of_mass(imgs[i], buf, index) + xy = ndimage.center_of_mass(buf, buf, index) xy = np.array(xy).round(2).T if para['center']:dt.extend([xy[1]*k, xy[0]*k]) From 67c5b63a7aa17bdde45afa7a5552b562472fafca Mon Sep 17 00:00:00 2001 From: WeisongZhao Date: Fri, 24 May 2019 15:29:31 +0800 Subject: [PATCH 142/343] math->subtract --- imagepy/menus/Process/Math/math_plgs.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/imagepy/menus/Process/Math/math_plgs.py b/imagepy/menus/Process/Math/math_plgs.py index 63f4049a..aeaaba54 100644 --- a/imagepy/menus/Process/Math/math_plgs.py +++ b/imagepy/menus/Process/Math/math_plgs.py @@ -21,6 +21,17 @@ class Multiply(Filter): def run(self, ips, snap, img, para = None): np.multiply(snap, para['num'], out=img, casting='unsafe') + +class Subtract(Filter): + """Subtract_plg: derived from imagepy.core.engine.Filter """ + title = 'Subtract' + note = ['all', 'auto_msk', 'auto_snap', 'preview', '2int'] + para = {'num':0} + view = [(float, 'num', (-255,255), 2, '-100', '+100')] + + def run(self, ips, snap, img, para = None): + np.subtract(snap, para['num'], out=img, casting='unsafe') + img[img<0]=0 class Max(Filter): """Max_plg: derived from imagepy.core.engine.Filter """ @@ -43,7 +54,8 @@ class Min(Filter): def run(self, ips, snap, img, para = None): img[:] = snap img[img>para['num']] = para['num'] - + + class Sqrt(Filter): """Sqrt_plg: derived from imagepy.core.engine.Filter """ title = 'Squre Root' @@ -54,7 +66,7 @@ def run(self, ips, snap, img, para = None): class Garmma(Filter): """Garmma_plg: derived from imagepy.core.engine.Filter """ - title = 'Garmma' + title = 'Gamma' note = ['all', 'auto_msk', 'auto_snap', 'preview', '2float'] para = {'num':0} view = [(float, 'num', (-255,255), 2, '0.1', '10')] @@ -64,4 +76,4 @@ def run(self, ips, snap, img, para = None): img[:] = snap img[:] = (img/(x2-x1))**para['num']*(x2-x1) -plgs = [Add, Multiply, '-', Max, Min, '-', Sqrt, Garmma] \ No newline at end of file +plgs = [Add, Multiply, Subtract, '-', Max, Min, '-', Sqrt, Garmma] \ No newline at end of file From ad7c0fd96a236816390a0da14192e55c384042b8 Mon Sep 17 00:00:00 2001 From: WeisongZhao Date: Fri, 24 May 2019 15:33:19 +0800 Subject: [PATCH 143/343] process->math->subtract,garmma->gamma --- imagepy/menus/Process/Math/math_plgs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imagepy/menus/Process/Math/math_plgs.py b/imagepy/menus/Process/Math/math_plgs.py index aeaaba54..ce81b3b2 100644 --- a/imagepy/menus/Process/Math/math_plgs.py +++ b/imagepy/menus/Process/Math/math_plgs.py @@ -27,7 +27,7 @@ class Subtract(Filter): title = 'Subtract' note = ['all', 'auto_msk', 'auto_snap', 'preview', '2int'] para = {'num':0} - view = [(float, 'num', (-255,255), 2, '-100', '+100')] + view = [(float, 'num', (-255,255), 2, '-255', '+100')] def run(self, ips, snap, img, para = None): np.subtract(snap, para['num'], out=img, casting='unsafe') @@ -76,4 +76,4 @@ def run(self, ips, snap, img, para = None): img[:] = snap img[:] = (img/(x2-x1))**para['num']*(x2-x1) -plgs = [Add, Multiply, Subtract, '-', Max, Min, '-', Sqrt, Garmma] \ No newline at end of file +plgs = [Add, Multiply, Subtract, '-', Max, Min, '-', Sqrt, Gamma] \ No newline at end of file From 4fa3c4123ef858315111548afd73c1c6d27c673f Mon Sep 17 00:00:00 2001 From: WeisongZhao Date: Fri, 24 May 2019 15:38:48 +0800 Subject: [PATCH 144/343] nothing --- imagepy/menus/Process/Math/math_plgs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/menus/Process/Math/math_plgs.py b/imagepy/menus/Process/Math/math_plgs.py index ce81b3b2..0f680234 100644 --- a/imagepy/menus/Process/Math/math_plgs.py +++ b/imagepy/menus/Process/Math/math_plgs.py @@ -64,7 +64,7 @@ class Sqrt(Filter): def run(self, ips, snap, img, para = None): np.sqrt(snap, out=img) -class Garmma(Filter): +class Gamma(Filter): """Garmma_plg: derived from imagepy.core.engine.Filter """ title = 'Gamma' note = ['all', 'auto_msk', 'auto_snap', 'preview', '2float'] From efb7a8b501fa807d5dbe696cd4575091e95d469c Mon Sep 17 00:00:00 2001 From: WeisongZhao Date: Fri, 24 May 2019 18:59:15 +0800 Subject: [PATCH 145/343] no <0,subtract --- imagepy/menus/Process/Math/math_plgs.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/imagepy/menus/Process/Math/math_plgs.py b/imagepy/menus/Process/Math/math_plgs.py index 0f680234..8ed74b59 100644 --- a/imagepy/menus/Process/Math/math_plgs.py +++ b/imagepy/menus/Process/Math/math_plgs.py @@ -27,11 +27,10 @@ class Subtract(Filter): title = 'Subtract' note = ['all', 'auto_msk', 'auto_snap', 'preview', '2int'] para = {'num':0} - view = [(float, 'num', (-255,255), 2, '-255', '+100')] + view = [(float, 'num', (-255,255), 2, '-255', '+255')] def run(self, ips, snap, img, para = None): np.subtract(snap, para['num'], out=img, casting='unsafe') - img[img<0]=0 class Max(Filter): """Max_plg: derived from imagepy.core.engine.Filter """ From b2b52b9ad9109f00931d56bc6422113b04eb1fef Mon Sep 17 00:00:00 2001 From: Grzegorz Bokota Date: Wed, 29 May 2019 12:49:44 +0200 Subject: [PATCH 146/343] Add entry point to allow launch without calling module can be launch by `imagepy` instead `python -m imagepy` --- setup.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/setup.py b/setup.py index f2eb07bb..2a973009 100644 --- a/setup.py +++ b/setup.py @@ -22,6 +22,11 @@ def get_data_files(): author_email='yxdragon@imagepy.org', license='BSD 3-clause', packages=setuptools.find_packages(), + entry_points={ + 'console_scripts': [ + 'imagepy = imagepy:show', + ], + }, package_data=get_data_files(), install_requires=[ 'scikit-image', From 6d2629835fc1b791c0dce69edb26ad340d863b54 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 29 May 2019 23:47:10 +0800 Subject: [PATCH 147/343] update pypi --- imagepy/menus/Process/Math/math_plgs.py | 22 +-- imagepy/yn.html | 173 ------------------------ setup.py | 2 +- 3 files changed, 12 insertions(+), 185 deletions(-) delete mode 100644 imagepy/yn.html diff --git a/imagepy/menus/Process/Math/math_plgs.py b/imagepy/menus/Process/Math/math_plgs.py index 8ed74b59..a1b75c7f 100644 --- a/imagepy/menus/Process/Math/math_plgs.py +++ b/imagepy/menus/Process/Math/math_plgs.py @@ -11,16 +11,6 @@ class Add(Filter): def run(self, ips, snap, img, para = None): np.add(snap, para['num'], out=img, casting='unsafe') - -class Multiply(Filter): - """Multiply_plg: derived from imagepy.core.engine.Filter """ - title = 'Multiply' - note = ['all', 'auto_msk', 'auto_snap', 'preview', '2int'] - para = {'num':0} - view = [(float, 'num', (-255,255), 2, '-100', '+100')] - - def run(self, ips, snap, img, para = None): - np.multiply(snap, para['num'], out=img, casting='unsafe') class Subtract(Filter): """Subtract_plg: derived from imagepy.core.engine.Filter """ @@ -31,6 +21,16 @@ class Subtract(Filter): def run(self, ips, snap, img, para = None): np.subtract(snap, para['num'], out=img, casting='unsafe') + +class Multiply(Filter): + """Multiply_plg: derived from imagepy.core.engine.Filter """ + title = 'Multiply' + note = ['all', 'auto_msk', 'auto_snap', 'preview', '2int'] + para = {'num':0} + view = [(float, 'num', (-255,255), 2, '-100', '+100')] + + def run(self, ips, snap, img, para = None): + np.multiply(snap, para['num'], out=img, casting='unsafe') class Max(Filter): """Max_plg: derived from imagepy.core.engine.Filter """ @@ -75,4 +75,4 @@ def run(self, ips, snap, img, para = None): img[:] = snap img[:] = (img/(x2-x1))**para['num']*(x2-x1) -plgs = [Add, Multiply, Subtract, '-', Max, Min, '-', Sqrt, Gamma] \ No newline at end of file +plgs = [Add, Subtract, Multiply, '-', Max, Min, '-', Sqrt, Gamma] \ No newline at end of file diff --git a/imagepy/yn.html b/imagepy/yn.html deleted file mode 100644 index 4117436c..00000000 --- a/imagepy/yn.html +++ /dev/null @@ -1,173 +0,0 @@ - - - - - - - - - - -

FluidSurface

-

Path: https://github.com/Image-Py/FluidSurface.git

-

Version: 0.1

-

Author: YXDragon、Prevalenter

-

Email: yxdragon@imagepy.org

-

Keyword: predict, fluid

-

Description: The predict plug-ins of imagepy

-

Document

-

...

- - - \ No newline at end of file diff --git a/setup.py b/setup.py index 2a973009..306e4343 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ def get_data_files(): if __name__ == '__main__': setup(name='imagepy', - version='0.20', + version='0.22', url='https://github.com/Image-Py/imagepy', description='interactive python image-processing plugin framework', long_description=descr, From 43380302f4fb83054fe9f53d8d5defbdc373722a Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 14 Jun 2019 23:27:25 +0800 Subject: [PATCH 148/343] remove jit from canvas --- imagepy/ui/canvas.py | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/imagepy/ui/canvas.py b/imagepy/ui/canvas.py index f6b59ad7..0d4faec6 100644 --- a/imagepy/ui/canvas.py +++ b/imagepy/ui/canvas.py @@ -10,7 +10,7 @@ from scipy.ndimage import affine_transform from imagepy import IPy from time import time -from numba import jit +#from numba import jit #import sys #get_npbuffer = np.getbuffer if sys.version[0]=="2" else memoryview @@ -39,7 +39,7 @@ def lay(r1, r2): def trans(r1, r2): return [r2[0]-r1[0], r2[1]-r1[1], r2[2], r2[3]] - +''' @jit def my_transform(img, m, offset=(0,0), output=None, k=0.5, clip=False): kr=m[0]; kc=m[1]; ofr=offset[0]; ofc=offset[1]; @@ -49,7 +49,7 @@ def my_transform(img, m, offset=(0,0), output=None, k=0.5, clip=False): cc = int(c*kc+ofc) if output[r,c]==0 or not clip: output[r,c] = output[r,c]*(1-k)+img[rr,cc]*k - +''' class Canvas (wx.Panel): scales = [0.03125, 0.0625, 0.125, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4, 5, 8, 10] def __init__(self, parent ): @@ -179,19 +179,32 @@ def on_paint(self, event): def merge(self, img, back, M, O, mode, shape, win, lookup): if img.ndim == 2: rstarr = np.zeros(shape, dtype=img.dtype) - my_transform(img, M, offset=O, output=rstarr, k=1, clip=False) + #my_transform(img, M, offset=O, output=rstarr, k=1, clip=False) + affine_transform(img, M, offset=O, output_shape=shape, + output = rstarr, order=0, prefilter=False) if rstarr.dtype == np.complex128: rstarr = np.abs(rstarr) rstarr = lookup(rstarr) + if img.ndim == 3: rstarr = np.zeros((win[3], win[2], 3), dtype=img.dtype) for i in range(3): - my_transform(img[:,:,i], M, offset=O, - output=rstarr[:,:,i], k=1, clip=False) + affine_transform(img[:,:,i], M, offset=O, output_shape=shape, + output = rstarr[:,:,i], order=0, prefilter=False) if not back is None: - for i in range(3): - my_transform(back[:,:,i] if back.ndim==3 else back, M, offset=O, - output=rstarr[:,:,i], k=mode[0], clip = mode[1]=='Clip') + if back.ndim == 2: + rstback = np.zeros(shape, dtype=back.dtype) + #my_transform(img, M, offset=O, output=rstarr, k=1, clip=False) + affine_transform(back, M, offset=O, output_shape=shape, + output = rstback, order=0, prefilter=False) + if back.ndim == 3: + rstback = np.zeros((win[3], win[2], 3), dtype=back.dtype) + for i in range(3): + affine_transform(back[:,:,i], M, offset=O, output_shape=shape, + output = rstback[:,:,i], order=0, prefilter=False) + + np.multiply(rstarr, 1-mode[0], out=rstarr, casting='unsafe') + np.add(rstarr.T, rstback.T*0.5, out=rstarr.T, casting='unsafe') return rstarr From a20c022cde62071ca1e5b17172af10cd1489928a Mon Sep 17 00:00:00 2001 From: prevalent <31162098+Prevalenter@users.noreply.github.com> Date: Sat, 29 Jun 2019 22:22:47 +0800 Subject: [PATCH 149/343] Create CNAME --- CNAME | 1 + 1 file changed, 1 insertion(+) create mode 100644 CNAME diff --git a/CNAME b/CNAME new file mode 100644 index 00000000..946ae4db --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +imagepy.org From a38a92fb5f75237f6f134589fc18631c876b1d2f Mon Sep 17 00:00:00 2001 From: prevalent <31162098+Prevalenter@users.noreply.github.com> Date: Sat, 29 Jun 2019 22:55:17 +0800 Subject: [PATCH 150/343] Delete CNAME --- CNAME | 1 - 1 file changed, 1 deletion(-) delete mode 100644 CNAME diff --git a/CNAME b/CNAME deleted file mode 100644 index 946ae4db..00000000 --- a/CNAME +++ /dev/null @@ -1 +0,0 @@ -imagepy.org From d61a9873735781d764c6349f9bda606e7c775e6a Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 2 Jul 2019 17:57:21 +0800 Subject: [PATCH 151/343] plugin manager --- imagepy/core/mark/mark.py | 1 - imagepy/menus/Plugins/Contribute/pmanager_wgt.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/imagepy/core/mark/mark.py b/imagepy/core/mark/mark.py index 576dcec0..6694ac58 100644 --- a/imagepy/core/mark/mark.py +++ b/imagepy/core/mark/mark.py @@ -337,7 +337,6 @@ def draw_layers(pts, dc, f, **key): dc.SetPen(pen) dc.SetBrush(brush) - print(pts['body'].keys()) if key['cur'] in pts['body']: draw(pts['body'][key['cur']], dc, f, **key) diff --git a/imagepy/menus/Plugins/Contribute/pmanager_wgt.py b/imagepy/menus/Plugins/Contribute/pmanager_wgt.py index d5d9c2a6..5b307578 100644 --- a/imagepy/menus/Plugins/Contribute/pmanager_wgt.py +++ b/imagepy/menus/Plugins/Contribute/pmanager_wgt.py @@ -109,7 +109,7 @@ def load(self): has = [p for p in [parse(i) for i in has] if not p is None] keys = set([i['path'] for i in prjs]) for i in has: - if not i['path'] in keys: prjs.append(has) + if not i['path'] in keys: prjs.append(i) prjs = sorted([(i['name'], i) for i in prjs]) self.prjs = [i[1] for i in prjs] From d48521a8ae9f1835e7007f8ce2ffd8ee56380c3d Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 2 Jul 2019 17:58:32 +0800 Subject: [PATCH 152/343] plugin manager --- imagepy/ui/tablewindow.py | 1 - 1 file changed, 1 deletion(-) diff --git a/imagepy/ui/tablewindow.py b/imagepy/ui/tablewindow.py index 99effe42..8592583b 100644 --- a/imagepy/ui/tablewindow.py +++ b/imagepy/ui/tablewindow.py @@ -21,7 +21,6 @@ def set_tps(self, tps): self.tps = tps self._rows = tps.data.shape[0] self._cols = tps.data.shape[1] - print('haha') def GetNumberCols(self): if self.tps is None: return 0 From d97701385eedc04ecbaff9fc01f7780676e566bf Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 31 Jul 2019 23:37:01 +0800 Subject: [PATCH 153/343] pre to ass multi channels --- imagepy/menus/File/TIF/tif_plgs.py | 3 ++- imagepy/tools/Standard/painter_tol.py | 9 +++++---- imagepy/ui/canvas.py | 4 +++- imagepy/ui/canvasframe.py | 1 - 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/imagepy/menus/File/TIF/tif_plgs.py b/imagepy/menus/File/TIF/tif_plgs.py index 6610cf9e..0084c3bf 100644 --- a/imagepy/menus/File/TIF/tif_plgs.py +++ b/imagepy/menus/File/TIF/tif_plgs.py @@ -3,11 +3,12 @@ from imagepy.core.manager import ReaderManager, WriterManager ReaderManager.add('tif', imread) +ReaderManager.add('tiff', imread) WriterManager.add('tif', imsave) class OpenFile(fileio.Reader): title = 'TIF Open' - filt = ['TIF'] + filt = ['TIF', 'TIFF'] class SaveFile(fileio.Writer): title = 'TIF Save' diff --git a/imagepy/tools/Standard/painter_tol.py b/imagepy/tools/Standard/painter_tol.py index b9d54e58..da7f8d7c 100644 --- a/imagepy/tools/Standard/painter_tol.py +++ b/imagepy/tools/Standard/painter_tol.py @@ -1,4 +1,5 @@ from imagepy.core.engine import Tool +from imagepy.core.manager import ColorManager from skimage.draw import line, circle def drawline(img, oldp, newp, w, value): @@ -12,9 +13,8 @@ def drawline(img, oldp, newp, w, value): class Plugin(Tool): title = 'Pencil' - para = {'width':1, 'value':(255, 255, 255)} - view = [(int, 'width', (0,30), 0, 'width', 'pix'), - ('color', 'value', 'color', '')] + para = {'width':1} + view = [(int, 'width', (0,30), 0, 'width', 'pix')] def __init__(self): self.status = False @@ -30,7 +30,8 @@ def mouse_up(self, ips, x, y, btn, **key): def mouse_move(self, ips, x, y, btn, **key): if not self.status:return - w, value = self.para['width'], self.para['value'] + w = self.para['width'] + value = ColorManager.get_front() drawline(ips.img, self.oldp, (y, x), w, value) self.oldp = (y, x) ips.update() diff --git a/imagepy/ui/canvas.py b/imagepy/ui/canvas.py index 0d4faec6..416fb054 100644 --- a/imagepy/ui/canvas.py +++ b/imagepy/ui/canvas.py @@ -40,8 +40,9 @@ def lay(r1, r2): def trans(r1, r2): return [r2[0]-r1[0], r2[1]-r1[1], r2[2], r2[3]] ''' +from numba import jit @jit -def my_transform(img, m, offset=(0,0), output=None, k=0.5, clip=False): +def affine_transform(img, m, offset=(0,0), output=None, k=1, clip=False, output_shape=0, order=0, prefilter=0): kr=m[0]; kc=m[1]; ofr=offset[0]; ofc=offset[1]; for r in range(output.shape[0]): for c in range(output.shape[1]): @@ -50,6 +51,7 @@ def my_transform(img, m, offset=(0,0), output=None, k=0.5, clip=False): if output[r,c]==0 or not clip: output[r,c] = output[r,c]*(1-k)+img[rr,cc]*k ''' + class Canvas (wx.Panel): scales = [0.03125, 0.0625, 0.125, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4, 5, 8, 10] def __init__(self, parent ): diff --git a/imagepy/ui/canvasframe.py b/imagepy/ui/canvasframe.py index e926b8d0..8199547d 100644 --- a/imagepy/ui/canvasframe.py +++ b/imagepy/ui/canvasframe.py @@ -40,7 +40,6 @@ def __init__(self, parent=None): self.page = wx.ScrollBar( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.SB_HORIZONTAL) - self.page.SetScrollbar(0,0,0,0, refresh=True) sizer.Add( self.page, 0, wx.ALL|wx.EXPAND, 0 ) self.page.Hide() From b62ed184745d029de20c3b1b7f01b5dcd93b56ba Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 2 Aug 2019 17:15:29 +0800 Subject: [PATCH 154/343] multi channels --- imagepy/core/manager/windowmanager.py | 2 +- imagepy/core/wraper/imageplus.py | 2 +- imagepy/ui/canvas.py | 8 ++++++++ imagepy/ui/canvasframe.py | 24 ++++++++++++++++++++---- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/imagepy/core/manager/windowmanager.py b/imagepy/core/manager/windowmanager.py index 527f52d6..e2aed67b 100644 --- a/imagepy/core/manager/windowmanager.py +++ b/imagepy/core/manager/windowmanager.py @@ -165,7 +165,7 @@ def get_titles(cls): @classmethod def name(cls, name): - if name==None:name='Table' + if name is None: name='Table' titles = [i().title for i in cls.tabs] if not name in titles : return name diff --git a/imagepy/core/wraper/imageplus.py b/imagepy/core/wraper/imageplus.py index 1131c70e..91b63984 100644 --- a/imagepy/core/wraper/imageplus.py +++ b/imagepy/core/wraper/imageplus.py @@ -15,7 +15,7 @@ class ImagePlus: def __init__(self, imgs, title=None, is3d=False): self.set_title(title) self.snap = None - self.cur = 0 + self.cur = self.chan = 0 self.dirty = False self.scrchanged = False self.roi = None diff --git a/imagepy/ui/canvas.py b/imagepy/ui/canvas.py index 416fb054..950f054c 100644 --- a/imagepy/ui/canvas.py +++ b/imagepy/ui/canvas.py @@ -188,10 +188,18 @@ def merge(self, img, back, M, O, mode, shape, win, lookup): rstarr = lookup(rstarr) if img.ndim == 3: + rstarr = np.zeros(shape[:2], dtype=img.dtype) + affine_transform(img[:,:,self.ips.chan], M, offset=O, output_shape=shape, + output = rstarr, order=0, prefilter=False) + if rstarr.dtype == np.complex128: rstarr = np.abs(rstarr) + rstarr = lookup(rstarr) + + ''' rstarr = np.zeros((win[3], win[2], 3), dtype=img.dtype) for i in range(3): affine_transform(img[:,:,i], M, offset=O, output_shape=shape, output = rstarr[:,:,i], order=0, prefilter=False) + ''' if not back is None: if back.ndim == 2: diff --git a/imagepy/ui/canvasframe.py b/imagepy/ui/canvasframe.py index 8199547d..c4ad6578 100644 --- a/imagepy/ui/canvasframe.py +++ b/imagepy/ui/canvasframe.py @@ -38,17 +38,24 @@ def __init__(self, parent=None): self.handle = None sizer.Add( self.canvas, 1, wx.EXPAND |wx.ALL, 0 ) + self.chan = wx.Slider( self, wx.ID_ANY, 50, 0, 100, wx.DefaultPosition, wx.DefaultSize, + wx.SL_HORIZONTAL| wx.SL_SELRANGE| wx.SL_TOP) + sizer.Add( self.chan, 0, wx.ALL|wx.EXPAND, 0 ) + self.chan.SetMaxSize( wx.Size( -1,18 ) ) + self.chan.Hide() + self.page = wx.ScrollBar( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.SB_HORIZONTAL) self.page.SetScrollbar(0,0,0,0, refresh=True) sizer.Add( self.page, 0, wx.ALL|wx.EXPAND, 0 ) self.page.Hide() + self.SetSizer(sizer) self.Layout() - self.Bind(wx.EVT_SCROLL, self.on_scroll) - + self.page.Bind(wx.EVT_SCROLL, self.on_scroll) + self.chan.Bind(wx.EVT_SCROLL, self.on_scroll) # panel.Bind(wx.EVT_CHAR, self.OnKeyDown) - self.opage = 0 + self.opage = self.ochan = 0 #self.Fit() #self.SetAcceleratorTable(IPy.curapp.shortcut) @@ -82,7 +89,15 @@ def set_info(self, ips, resize=False): self.page.Show() resize = True self.page.SetScrollbar(0, 0, ips.get_nslices()-1, 0, refresh=True) - + if ips.get_nchannels() != self.ochan: + self.ochan = ips.get_nchannels() + if ips.get_nchannels()==1 and self.chan.Shown: + self.chan.Hide() + resize = True + if ips.get_nchannels()>1 and not self.chan.Shown: + self.chan.Show() + resize = True + self.chan.SetMax(ips.get_nchannels()-1) if resize: if IPy.uimode()!='ipy': self.Fit() else: @@ -100,6 +115,7 @@ def set_ips(self, ips): def on_scroll(self, event): self.ips.cur = self.page.GetThumbPosition() + self.ips.chan = self.chan.GetValue() self.ips.update() self.canvas.on_idle(None) From f33fb0dc0900239dfc803e97cd9d1a5bf5eb9bd3 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 8 Aug 2019 00:09:42 +0800 Subject: [PATCH 155/343] chan --- imagepy/ui/canvas.py | 24 +++++++++++------------- imagepy/ui/canvasframe.py | 13 +++++++------ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/imagepy/ui/canvas.py b/imagepy/ui/canvas.py index 950f054c..d283dad9 100644 --- a/imagepy/ui/canvas.py +++ b/imagepy/ui/canvas.py @@ -186,20 +186,18 @@ def merge(self, img, back, M, O, mode, shape, win, lookup): output = rstarr, order=0, prefilter=False) if rstarr.dtype == np.complex128: rstarr = np.abs(rstarr) rstarr = lookup(rstarr) - if img.ndim == 3: - rstarr = np.zeros(shape[:2], dtype=img.dtype) - affine_transform(img[:,:,self.ips.chan], M, offset=O, output_shape=shape, - output = rstarr, order=0, prefilter=False) - if rstarr.dtype == np.complex128: rstarr = np.abs(rstarr) - rstarr = lookup(rstarr) - - ''' - rstarr = np.zeros((win[3], win[2], 3), dtype=img.dtype) - for i in range(3): - affine_transform(img[:,:,i], M, offset=O, output_shape=shape, - output = rstarr[:,:,i], order=0, prefilter=False) - ''' + if img.shape[2]==3 and img.dtype==np.uint8: + rstarr = np.zeros((win[3], win[2], 3), dtype=img.dtype) + for i in range(3): + affine_transform(img[:,:,i], M, offset=O, output_shape=shape, + output = rstarr[:,:,i], order=0, prefilter=False) + else: + rstarr = np.zeros(shape[:2], dtype=img.dtype) + affine_transform(img[:,:,self.ips.chan], M, offset=O, output_shape=shape, + output = rstarr, order=0, prefilter=False) + if rstarr.dtype == np.complex128: rstarr = np.abs(rstarr) + rstarr = lookup(rstarr) if not back is None: if back.ndim == 2: diff --git a/imagepy/ui/canvasframe.py b/imagepy/ui/canvasframe.py index c4ad6578..5d8ac721 100644 --- a/imagepy/ui/canvasframe.py +++ b/imagepy/ui/canvasframe.py @@ -38,7 +38,7 @@ def __init__(self, parent=None): self.handle = None sizer.Add( self.canvas, 1, wx.EXPAND |wx.ALL, 0 ) - self.chan = wx.Slider( self, wx.ID_ANY, 50, 0, 100, wx.DefaultPosition, wx.DefaultSize, + self.chan = wx.Slider( self, wx.ID_ANY, 0, 0, 100, wx.DefaultPosition, wx.DefaultSize, wx.SL_HORIZONTAL| wx.SL_SELRANGE| wx.SL_TOP) sizer.Add( self.chan, 0, wx.ALL|wx.EXPAND, 0 ) self.chan.SetMaxSize( wx.Size( -1,18 ) ) @@ -73,9 +73,9 @@ def set_handler(self, handle=None): def set_info(self, ips, resize=False): stk = 'stack' if ips.is3d else 'list' - label='{}/{}; {} {}x{} pixels; {}; {} M'.format(ips.cur+1, ips.get_nslices(), - stk if ips.get_nslices()>1 else '',ips.size[0], ips.size[1], - ips.imgtype, round(ips.get_nbytes()/1024.0/1024.0, 2)) + label='S:{}/{}; C:{}/{}; {} {}x{} pixels; {}; {} M'.format(ips.cur+1, ips.get_nslices(), + ips.chan+1, ips.get_nchannels(), stk if ips.get_nslices()>1 else '', + ips.size[0], ips.size[1], ips.imgtype, round(ips.get_nbytes()/1024.0/1024.0, 2)) if label != self.txt_info.GetLabel(): self.txt_info.SetLabel(label) @@ -91,10 +91,11 @@ def set_info(self, ips, resize=False): self.page.SetScrollbar(0, 0, ips.get_nslices()-1, 0, refresh=True) if ips.get_nchannels() != self.ochan: self.ochan = ips.get_nchannels() - if ips.get_nchannels()==1 and self.chan.Shown: + isrgb = ips.get_imgtype()=='rgb' + if (ips.get_nchannels()==1 or isrgb) and self.chan.Shown: self.chan.Hide() resize = True - if ips.get_nchannels()>1 and not self.chan.Shown: + if ips.get_nchannels()>1 and not isrgb and not self.chan.Shown: self.chan.Show() resize = True self.chan.SetMax(ips.get_nchannels()-1) From 139a1909107622678067bef820cbc5e97e71ad7c Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 18 Aug 2019 17:52:35 +0800 Subject: [PATCH 156/343] multi channels --- imagepy/IPy.py | 3 +++ imagepy/core/wraper/tableplus.py | 14 +++++++---- imagepy/ui/panelconfig.py | 2 +- imagepy/ui/tableframe.py | 4 ---- imagepy/ui/tablewindow.py | 3 --- imagepy/ui/widgets/normal.py | 40 ++++++++++++++++++++++++++++++++ 6 files changed, 53 insertions(+), 13 deletions(-) diff --git a/imagepy/IPy.py b/imagepy/IPy.py index 76217f08..e5f11fbc 100644 --- a/imagepy/IPy.py +++ b/imagepy/IPy.py @@ -187,7 +187,10 @@ def show_table(data, title): def showlog(title, cont): from .ui.logwindow import TextLog TextLog.write(cont, title) + pub.subscribe(showlog, 'showlog') +def show_log(title, cont): + wx.CallAfter(pub.sendMessage, 'showlog', cont=cont, title=title) def write(cont, title='ImagePy'): if curapp is None: diff --git a/imagepy/core/wraper/tableplus.py b/imagepy/core/wraper/tableplus.py index 496100f6..3e997e8a 100644 --- a/imagepy/core/wraper/tableplus.py +++ b/imagepy/core/wraper/tableplus.py @@ -26,7 +26,7 @@ def set_data(self, data): self.rowmsk, self.colmsk = [],[] self.count_range() - def snapshot(self, mskr=None, mskc=None, num=False): + def get_subtab(self, mskr=True, mskc=True, num=False): if mskr==None:rmsk = slice(None) rowmsk = self.rowmsk if len(self.rowmsk)>0 else self.data.index if mskr==True:rmsk = rowmsk @@ -37,17 +37,21 @@ def snapshot(self, mskr=None, mskc=None, num=False): if mskc==True:cmsk = colmsk if mskc==False:cmsk = self.data.columns.difference(colmsk) - self.snap = self.data.loc[rmsk, cmsk].copy() - if num: self.snap = self.snap.select_dtypes( - include=[np.number]) + subtab = self.data.loc[rmsk, cmsk] + if num: subtab = subtab.select_dtypes(include=[np.number]) + return subtab + + def snapshot(self, mskr=None, mskc=None, num=False): + self.snap = self.get_subtab(mskr, mskc, num).copy() def select(self, rs=[], cs=[], byidx=False): if byidx: rs, cs = self.data.index[rs], self.data.columns[cs] self.rowmsk = pd.Index(rs).astype(self.data.index.dtype) self.colmsk = pd.Index(cs).astype(self.data.columns.dtype) - print('tps select', rs, cs, self.rowmsk, self.colmsk) + # print('tps select', rs, cs, self.rowmsk, self.colmsk) def get_titles(self):return self.data.columns + def get_index(self):return self.data.index def get_props(self): diff --git a/imagepy/ui/panelconfig.py b/imagepy/ui/panelconfig.py index c1a5d84c..f6d88633 100644 --- a/imagepy/ui/panelconfig.py +++ b/imagepy/ui/panelconfig.py @@ -5,7 +5,7 @@ from .widgets import * import weakref -widgets = { 'ctrl':None, 'slide':FloatSlider, int:NumCtrl, +widgets = { 'ctrl':None, 'slide':FloatSlider, int:NumCtrl, 'path':PathCtrl, float:NumCtrl, 'lab':Label, bool:Check, str:TextCtrl, list:Choice, 'img':ImageList, 'tab':TableList, 'color':ColorCtrl, 'any':AnyType, 'chos':Choices, 'fields':TableFields, diff --git a/imagepy/ui/tableframe.py b/imagepy/ui/tableframe.py index eb77be8a..824ed19a 100644 --- a/imagepy/ui/tableframe.py +++ b/imagepy/ui/tableframe.py @@ -37,10 +37,6 @@ def set_info(self, tps): self.lab_info.SetLabel('%sx%s; %.2fK'%(tps.data.shape+(tps.get_nbytes()/1024.0,))) if not self.handle is None: self.handle(tps) - def on_test(self, event): - print(self.grid.GetSelectedCols(), self.grid.GetSelectedRows()) - print(self.grid.GetSelectionBlockTopLeft(), self.grid.GetSelectionBlockBottomRight()) - class TableFrame(wx.Frame): """CanvasFrame: derived from the wx.core.Frame""" ## TODO: Main frame ??? diff --git a/imagepy/ui/tablewindow.py b/imagepy/ui/tablewindow.py index 8592583b..08330a86 100644 --- a/imagepy/ui/tablewindow.py +++ b/imagepy/ui/tablewindow.py @@ -249,12 +249,9 @@ def Reset(self): def select(self): self.Bind(Grid.EVT_GRID_RANGE_SELECT, None) self.ClearSelection() - print('select grid') for i in self.tps.data.index.get_indexer(self.tps.rowmsk): - print(i) self.SelectRow(i, True) for i in self.tps.data.columns.get_indexer(self.tps.colmsk): - print(i) self.SelectCol(i, True) self.Bind(Grid.EVT_GRID_RANGE_SELECT, self.on_select) diff --git a/imagepy/ui/widgets/normal.py b/imagepy/ui/widgets/normal.py index 97a96a5b..38facf50 100644 --- a/imagepy/ui/widgets/normal.py +++ b/imagepy/ui/widgets/normal.py @@ -131,6 +131,46 @@ def SetValue(self, color): def GetValue(self): return self.ctrl.GetBackgroundColour().Get(False) +class PathCtrl(wx.Panel): + """ColorCtrl: deverid fron wx.coreTextCtrl""" + def __init__(self, parent, title, filt): + wx.Panel.__init__(self, parent) + sizer = wx.BoxSizer( wx.HORIZONTAL ) + self.prefix = lab_title = wx.StaticText( self, wx.ID_ANY, title, + wx.DefaultPosition, wx.DefaultSize) + self.filt = filt + lab_title.Wrap( -1 ) + sizer.Add( lab_title, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) + self.ctrl = wx.TextCtrl(self, wx.TE_RIGHT) + sizer.Add( self.ctrl, 2, wx.ALL, 5 ) + self.SetSizer(sizer) + + self.ctrl.Bind(wx.EVT_KEY_UP, self.ontext) + self.ctrl.Bind( wx.EVT_LEFT_DOWN, self.onselect) + + def Bind(self, z, f): self.f = f + + def ontext(self, event): print('ColorCtrl') + + def onselect(self, event): + from ...core.manager import ConfigManager + filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in self.filt]) + dpath = ConfigManager.get('defaultpath') or '.' + #if dpath==None: dpath = root_dir # './' + dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} + dialog = wx.FileDialog(self, 'Path Select', dpath, '', filt, wx.FD_OPEN) + rst = dialog.ShowModal() + if rst == wx.ID_OK: + path = dialog.GetPath() + self.ctrl.SetValue(path) + dialog.Destroy() + + def SetValue(self, value): + self.ctrl.SetValue(value) + + def GetValue(self): + return self.ctrl.GetValue() + class Choice(wx.Panel): """ColorCtrl: deverid fron wx.coreTextCtrl""" def __init__(self, parent, choices, tp, title, unit): From 354b380f2784f6ff711ed0ea4fc538c70c580044 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Thu, 29 Aug 2019 17:09:04 +0800 Subject: [PATCH 157/343] fix the startup error when only having one toolbar --- imagepy/ui/toolsloader.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/imagepy/ui/toolsloader.py b/imagepy/ui/toolsloader.py index 0a7a5ed6..76871b67 100644 --- a/imagepy/ui/toolsloader.py +++ b/imagepy/ui/toolsloader.py @@ -50,15 +50,16 @@ def buildToolsBar(parent, datas, toolsbar=None): add_tools(toolsbar, datas[1][0][1], clear=True) - gifpath = os.path.join(root_dir, "tools/drop.gif") - btn = wx.BitmapButton(toolsbar, wx.ID_ANY, make_bitmap(wx.Bitmap(gifpath)), - wx.DefaultPosition, (32, 32), wx.BU_AUTODRAW|wx.RAISED_BORDER) - sp = wx.StaticLine( toolsbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL ) - box.Add( sp, 0, wx.ALL|wx.EXPAND, 2 ) - box.AddStretchSpacer(1) - box.Add(btn) - btn.Bind(wx.EVT_LEFT_DOWN, lambda x, ds=datas, b=btn:menu_drop(parent, toolsbar, ds, b, x)) - add_tools(toolsbar, datas[1][1][1]) + if len(datas[1]) > 1: + gifpath = os.path.join(root_dir, "tools/drop.gif") + btn = wx.BitmapButton(toolsbar, wx.ID_ANY, make_bitmap(wx.Bitmap(gifpath)), + wx.DefaultPosition, (32, 32), wx.BU_AUTODRAW|wx.RAISED_BORDER) + sp = wx.StaticLine( toolsbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL ) + box.Add( sp, 0, wx.ALL|wx.EXPAND, 2 ) + box.AddStretchSpacer(1) + box.Add(btn) + btn.Bind(wx.EVT_LEFT_DOWN, lambda x, ds=datas, b=btn:menu_drop(parent, toolsbar, ds, b, x)) + add_tools(toolsbar, datas[1][1][1]) toolsbar.GetSizer().Layout() #toolsbar.Fit() From 8cdf80f52b51509954e1c86e639c2bf366ed9fd5 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 14 Sep 2019 16:46:36 +0800 Subject: [PATCH 158/343] new canvas --- imagepy/IPy.py | 2 +- imagepy/core/manager/windowmanager.py | 4 +- imagepy/core/myvi/test.py | 176 ++++++++ imagepy/core/wraper/imageplus.py | 106 +++-- imagepy/ipyalg/hydrology/findmax.py | 3 +- imagepy/menus/Edit/edit_plg.py | 8 +- imagepy/menus/Image/Adjust/colorstairs_plg.py | 1 - imagepy/menus/Image/Channels/__init__.py | 0 imagepy/menus/Image/__init__.py | 6 +- imagepy/tools/Draw/magic_tol.py | 4 +- imagepy/tools/Measure/angle2_tol.py | 4 +- imagepy/tools/Measure/angle_tol.py | 4 +- imagepy/tools/Measure/area_tol.py | 4 +- imagepy/tools/Measure/coordinate_tol.py | 4 +- imagepy/tools/Measure/distance_tol.py | 4 +- imagepy/tools/Measure/profile_tol.py | 4 +- imagepy/tools/Standard/freearea_tol.py | 4 +- imagepy/tools/Standard/freeline_tol.py | 4 +- imagepy/tools/Standard/line_tol.py | 4 +- imagepy/tools/Standard/magic_tol.py | 4 +- imagepy/tools/Standard/move_tol.py | 6 +- imagepy/tools/Standard/oval_tol.py | 4 +- imagepy/tools/Standard/point_tol.py | 6 +- imagepy/tools/Standard/polygon_tol.py | 4 +- imagepy/tools/Standard/rectangle_tol.py | 4 +- imagepy/tools/Standard/scale_tol.py | 11 +- imagepy/tools/Transform/rotate_tol.py | 4 +- imagepy/tools/Transform/scale_tol.py | 4 +- imagepy/ui/canvas.py | 332 --------------- imagepy/ui/canvas/__init__.py | 1 + imagepy/ui/canvas/boxutil.py | 49 +++ imagepy/ui/canvas/canvas.py | 252 ++++++++++++ imagepy/ui/canvas/imutil.py | 162 ++++++++ imagepy/ui/canvas/mark.py | 378 ++++++++++++++++++ imagepy/ui/canvasframe.py | 188 +++++++-- imagepy/ui/widgets/normal.py | 2 +- imagepy/ui/widgets/viewport.py | 9 +- imagepy/widgets/histogram/channels_wgt.py | 304 ++++++++++++++ imagepy/widgets/histogram/histogram_wgt.py | 4 +- imagepy/widgets/navigator/navigator_wgt.py | 39 +- 40 files changed, 1637 insertions(+), 476 deletions(-) create mode 100644 imagepy/core/myvi/test.py create mode 100644 imagepy/menus/Image/Channels/__init__.py delete mode 100644 imagepy/ui/canvas.py create mode 100644 imagepy/ui/canvas/__init__.py create mode 100644 imagepy/ui/canvas/boxutil.py create mode 100644 imagepy/ui/canvas/canvas.py create mode 100644 imagepy/ui/canvas/imutil.py create mode 100644 imagepy/ui/canvas/mark.py create mode 100644 imagepy/widgets/histogram/channels_wgt.py diff --git a/imagepy/IPy.py b/imagepy/IPy.py index e5f11fbc..939d155d 100644 --- a/imagepy/IPy.py +++ b/imagepy/IPy.py @@ -210,7 +210,7 @@ def plot(title, gtitle='Graph', labelx='X-Unit', labely='Y-Unit'): def set_info(i): if curapp is None: print('ImagePy Info >>> %s'%i) - else: wx.CallAfter(curapp.set_info, i) + else: wx.CallAfter(curapp.set_info, str(i)) # MT callafter(curapp.set_info, i) def run(cmd): diff --git a/imagepy/core/manager/windowmanager.py b/imagepy/core/manager/windowmanager.py index e2aed67b..ddb18fbd 100644 --- a/imagepy/core/manager/windowmanager.py +++ b/imagepy/core/manager/windowmanager.py @@ -17,7 +17,7 @@ def add(cls, win): def get(cls, title=None): if len(cls.wins)==0:return None if title==None:return cls.wins[0] - titles = [i.canvas.ips.title for i in cls.wins] + titles = [i.ips.title for i in cls.wins] if not title in titles:return None return cls.wins[titles.index(title)] @@ -26,7 +26,7 @@ def remove(cls, win): for i in cls.wins: if i == win: cls.wins.remove(i) - print('remove', i.canvas.ips.title) + print('remove', i.ips.title) class ImageManager: imgs = [] diff --git a/imagepy/core/myvi/test.py b/imagepy/core/myvi/test.py new file mode 100644 index 00000000..61a96b94 --- /dev/null +++ b/imagepy/core/myvi/test.py @@ -0,0 +1,176 @@ +import sys, wx +from scipy.misc import imread +import scipy.ndimage as ndimg +sys.path.append('..') +import numpy as np +from glob import glob +import myvi + +def dem(): + img = imread('data/dem.jpg') + vts, fs, ns, cs = myvi.util.build_surf2d(img, ds=1, k=0.3, sigma=2) + + manager = myvi.Manager() + manager.add_surf('dem', vts, fs, ns, cs) + manager.show('DEM Demo') + +def volume(): + fs = glob('data/vessel*.png') + imgs = np.array([imread(i, True) for i in fs]) + print() + imgs = ndimg.gaussian_filter(imgs, 1) + vts, fs, ns, vs = myvi.util.build_surf3d(imgs, 1, 80) + + manager = myvi.Manager() + manager.add_surf('vessel', vts, fs, ns, (1,0,0)) + manager.show('Vessel Demo') + +def ball(): + vts, fs, ns, cs = myvi.build_ball((100,100,100),50, (1,0,0)) + manager = myvi.Manager() + manager.add_surf('balls', vts, fs, ns, cs) + manager.show('Ball Demo') + +def random_balls(): + os = np.random.rand(30).reshape((-1,3)) + rs = np.random.rand(10)/5 + cs = (np.random.rand(10)*255).astype(np.uint8) + cs = myvi.linear_color('jet')[cs]/255 + + vts, fs, ns, cs = myvi.build_balls(os, rs, cs) + manager = myvi.Manager() + manager.add_surf('balls', vts, fs, ns, cs) + manager.show('Random Balls Demo') + +def line(): + vts = np.array([(0,0,0),(1,1,0),(2,1,0),(1,0,0)], dtype=np.float32) + fs = np.array([(0,1,2),(1,2,3)], dtype=np.uint32) + ns = np.ones((4,3), dtype=np.float32) + + n_mer, n_long = 6, 11 + pi = np.pi + dphi = pi / 1000.0 + phi = np.arange(0.0, 2 * pi + 0.5 * dphi, dphi) + mu = phi * n_mer + x = np.cos(mu) * (1 + np.cos(n_long * mu / n_mer) * 0.5) + y = np.sin(mu) * (1 + np.cos(n_long * mu / n_mer) * 0.5) + z = np.sin(n_long * mu / n_mer) * 0.5 + + vts, fs, ns, cs = myvi.build_line(x, y, z, (1, 0, 0)) + cs[:] = myvi.auto_lookup(vts[:,2], myvi.linear_color('jet'))/255 + + manager = myvi.Manager() + obj = manager.add_surf('line', vts, fs, ns, cs) + obj.set_style(mode='grid') + manager.show('Line Rings') + +def mesh(): + dphi, dtheta = np.pi/80.0, np.pi/80.0 + [phi,theta] = np.mgrid[0:np.pi+dphi*1.5:dphi,0:2*np.pi+dtheta*1.5:dtheta] + m0 = 4; m1 = 3; m2 = 2; m3 = 3; m4 = 6; m5 = 2; m6 = 6; m7 = 4; + r = np.sin(m0*phi)**m1 + np.cos(m2*phi)**m3 + np.sin(m4*theta)**m5 + np.cos(m6*theta)**m7 + x = r*np.sin(phi)*np.cos(theta) + y = r*np.cos(phi) + z = r*np.sin(phi)*np.sin(theta) + vts, fs, ns, cs = myvi.build_mesh(x, y, z) + cs[:] = myvi.util.auto_lookup(vts[:,2], myvi.util.linear_color('jet'))/255 + + manager = myvi.Manager() + obj = manager.add_surf('mesh', vts, fs, ns, cs) + obj.set_style(mode='grid') + manager.show('Mesh Demo') + +def ball_ring_box(): + os = np.random.rand(30).reshape((-1,3)) + rs = np.random.rand(10)/7 + cs = (np.random.rand(10)*255).astype(np.uint8) + cs = myvi.linear_color('jet')[cs]/255 + + vts_b, fs_b, ns_b, cs_b = myvi.build_balls(list(os), list(rs), list(cs)) + vts_l, fs_l, ns_l, cs_l = myvi.build_line(os[:,0], os[:,1], os[:,2], list(cs)) + vts_c, fs_c, ns_c, cs_c = myvi.build_cube((0,0,0), (1,1,1)) + manager = myvi.Manager() + manager.add_surf('balls', vts_b, fs_b, ns_b, cs_b) + line = manager.add_surf('line', vts_l, fs_l, ns_l, cs_l) + line.set_style(mode='grid') + box = manager.add_surf('box', vts_c, fs_c, ns_c, cs_c) + box.set_style(mode='grid') + manager.show('Balls Ring Demo') + +def balls_with_mark(): + os = np.random.rand(30).reshape((-1,3)) + rs = np.random.rand(10)/7 + cs = (np.random.rand(10)*255).astype(np.uint8) + cs = myvi.linear_color('jet')[cs]/255 + + vts_b, fs_b, ns_b, cs_b = myvi.build_balls(os, rs, cs) + cont = ['ID:%s'%i for i in range(10)] + vtss, fss, pps, h, color = myvi.build_marks(cont, os, rs, 0.05, (1,1,1)) + manager = myvi.Manager() + manager.add_surf('balls', vts_b, fs_b, ns_b, cs_b) + line = manager.add_mark('line', vtss, fss, pps, h, color) + line.set_style(mode='grid') + manager.show('Balls Mark Demo') + +def frame_demo(): + app = wx.App(False) + frm = myvi.Frame3D(None, 'Frame') + img = imread('data/dem.jpg') + vts, fs, ns, cs = myvi.util.build_surf2d(img, ds=1, k=0.3, sigma=2) + frm.viewer.add_surf_ansy('dem', vts, fs, ns, cs) + frm.Show() + app.MainLoop() + +def surface2d(): + x, y = np.ogrid[-2:2:20j, -2:2:20j] + z = x * np.exp( - x**2 - y**2) + vts, fs, ns, cs = myvi.util.build_surf2d(z, ds=1, k=20, sigma=2) + cs[:] = myvi.util.auto_lookup(vts[:,2], myvi.util.linear_color('jet'))/255 + manager = myvi.Manager() + manager.add_surf('dem', vts, fs, ns, cs) + manager.show('DEM Demo') + +def arrow(): + v1, v2 = np.array([[[0,0,0],[5,5,5]],[[0,15,5],[2,8,3]]], dtype=np.float32) + vts, fs, ns, c = myvi.util.build_arrows(v1, v2, 1, 1, 1, 1, (1,0,0)) + manager = myvi.Manager() + manager.add_surf('dem', vts, fs, ns, c) + manager.show('DEM Demo') + +def cube(): + vts, fs, ns, cs = myvi.build_cube((0,0,0), (1,1,1)) + manager = myvi.Manager() + obj = manager.add_surf('cube', vts, fs, ns, cs) + obj.set_style(mode='grid') + manager.show('Cube Demo') + +def cube_surf(): + from skimage.data import camera + lut = np.zeros((256,3), dtype=np.uint8) + lut[:,0] = np.arange(256) + imgs = np.array([camera()[:300,::]]*256) + vts, fs, ns, cs = myvi.build_img_cube(imgs) + manager = myvi.Manager() + obj = manager.add_surf('cube', vts, fs, ns, cs) + vts, fs, ns, cs = myvi.build_img_box(imgs) + obj = manager.add_surf('box', vts, fs, ns, cs) + obj.set_style(mode='grid') + manager.show('Cube Demo') + + +if __name__ == '__main__': + + cube_surf() + ''' + volume() + surface2d() + dem() + volume() + ball() + random_balls() + line() + mesh() + ball_ring_box() + balls_with_mark() + arrow() + ''' \ No newline at end of file diff --git a/imagepy/core/wraper/imageplus.py b/imagepy/core/wraper/imageplus.py index 91b63984..1a611ab6 100644 --- a/imagepy/core/wraper/imageplus.py +++ b/imagepy/core/wraper/imageplus.py @@ -1,4 +1,5 @@ import numpy as np +from time import time from ..manager import ImageManager, ColorManager def get_img_type(imgs): @@ -10,6 +11,32 @@ def get_img_type(imgs): if imgs[0].dtype == np.float64:return '64-float' if imgs[0].dtype == np.complex128:return 'complex' +def get_updown(imgs, slices='all', chans='all', step=1): + c = chans if isinstance(chans, int) else slice(None) + if isinstance(slices, int): imgs = [imgs[slices]] + if step<=1: step = int(1/step+0.5) + else: step = int(min(imgs[0].shape[:2])/step+0.5) + s = slice(None, None, max(step,1)) + s = (s,s,c)[:imgs[0].ndim] + mins = [i[s].min(axis=(0,1)) for i in imgs] + maxs = [i[s].max(axis=(0,1)) for i in imgs] + mins = np.array(mins).reshape((len(mins),-1)) + maxs = np.array(maxs).reshape((len(maxs),-1)) + mins, maxs = mins.min(axis=0), maxs.max(axis=0) + if chans!='all': return mins.min(), maxs.max() + return [(i,j) for i,j in zip(mins, maxs)] + +def histogram(imgs, rg=(0,256), slices='all', chans='all', step=1): + c = chans if isinstance(chans, int) else slice(None) + if isinstance(slices, int): imgs = [imgs[slices]] + if step<=1: step = int(1/step+0.5) + else: step = int(min(imgs[0].shape[:2])/step+0.5) + s = slice(None, None, max(step,1)) + s = (s,s,c)[:imgs[0].ndim] + rg = np.linspace(rg[0], rg[1], 257) + hist = [np.histogram(i[s], rg)[0] for i in imgs] + return np.sum(hist, axis=0) + class ImagePlus: """ImagePlus: a class to make operation more flexible """ def __init__(self, imgs, title=None, is3d=False): @@ -28,7 +55,8 @@ def __init__(self, imgs, title=None, is3d=False): self.tool = None self.data = {} self.unit = (1, 'pix') - self.range = (0, 255) + self.chan_range = [] + self.chan_mode = 'min' self.set_imgs(imgs) def update(self): self.dirty = True @@ -41,19 +69,30 @@ def set_imgs(self, imgs): self.scrchanged = True self.snap = None self.imgs = imgs - self.height, self.width = self.size = self.imgs[0].shape[:2] + + self.size = self.imgs[0].shape[:2] + self.height, self.width = self.size self.imgtype = get_img_type(self.imgs) - self.channels = 1 if self.imgs[0].ndim==2 else self.imgs[0].shape[2] + if self.imgs[0].ndim==2: self.channels = 1 + else: self.channels = self.imgs[0].shape[2] self.dtype = self.imgs[0].dtype - + if self.dtype == np.uint8: self.range = (0, 255) + else: self.range = self.get_updown('all', 'one') if self.dtype == np.uint8: - self.range = (0, 255) - else: self.range = self.get_updown() + self.chan_range = [(0, 255)] * self.channels + else: self.chan_range = self.get_updown('all', 'all', step=512) + self.chan = (0, [0,1,2])[self.channels==3] - def get_updown(self): - arr = np.array(([(i.min(),i.max()) for i in self.imgs])) - print('range', arr[:,0].min(), arr[:,1].max()) - return arr[:,0].min(), arr[:,1].max() + def get_updown(self, slices='all', chans='one', step=1): + if slices is None: slices = self.cur + if chans is None: chans = self.chan + return get_updown(self.imgs, slices, chans, step) + + def histogram(self, rg=None, slices=None, chans=None, step=1): + if slices is None: slices = self.cur + if chans is None: chans = self.chan + if rg is None: rg = self.range + return histogram(self.imgs, rg, slices, chans, step) def get_imgtype(self):return self.imgtype @@ -70,6 +109,15 @@ def get_nbytes(self): @property def img(self):return self.imgs[self.cur] + @property + def range(self): + rg = np.array(self.chan_range).reshape((-1,2)) + return (rg[:,0].min(), rg[:,1].max()) + + @range.setter + def range(self, value): + self.chan_range = [value] * len(self.chan_range) + def get_msk(self, mode='in'): if self.roi==None:return None if self.msk is None: @@ -109,26 +157,24 @@ def reset(self, msk=False): self.imgs[self.cur][msk] = self.snap[msk] else : self.imgs[self.cur][:] = self.snap - def histogram(self, arange=None, stack=False): - if arange == None: arange=self.range - if not stack: - return np.histogram(self.img, np.linspace(arange[0], arange[1], 257))[0] - # if stack - hists = [] - for i in self.imgs: - hists.append(np.histogram(i, np.linspace(arange[0], arange[1], 257))[0]) - return np.array(hists).sum(axis=0) - def lookup(self, img=None): if img is None: img = self.img - if img.ndim==2: - k = 255.0/(max(1e-10, self.range[1]-self.range[0])) - bf = np.clip(img, self.range[0], self.range[1]) - bf = ((bf - self.range[0]) * k).astype(np.uint8) - #print(bf.max(), self.range) - return self.lut[bf] - if img.ndim==3 and self.dtype==np.uint8: - return img + if isinstance(self.chan, int): + rg = self.chan_range[self.chan] + k = 255.0/(max(1e-10, rg[1]-rg[0])) + bf = np.clip(img, rg[0], rg[1]) + np.subtract(bf, rg[0], out=bf, casting='unsafe') + np.multiply(bf, k, out=bf, casting='unsafe') + return self.lut[bf.astype(np.uint8)] + rgb = np.zeros(img.shape[:2]+(3,), dtype=np.uint8) + for i in (0,1,2): + rg = self.chan_range[self.chan[i]] + k = 255.0/(max(1e-10, rg[1]-rg[0])) + bf = np.clip(img[:,:,self.chan[i]], rg[0], rg[1]) + np.subtract(bf, rg[0], out=bf, casting='unsafe') + np.multiply(bf, k, out=rgb[:,:,i], casting='unsafe') + return rgb + def swap(self): print(type(self.snap), type(self.imgs[self.cur]), self.cur) @@ -138,9 +184,9 @@ def swap(self): else: buf = self.img.copy() self.img[:], self.snap[:] = self.snap, buf - + def __del__(self): - print(self.title, '>>> deleted ips') + print(self.title, '>>> delete ips') if __name__=='__main__': from skimage.io import imread diff --git a/imagepy/ipyalg/hydrology/findmax.py b/imagepy/ipyalg/hydrology/findmax.py index cc7c660a..77b6902c 100644 --- a/imagepy/ipyalg/hydrology/findmax.py +++ b/imagepy/ipyalg/hydrology/findmax.py @@ -22,7 +22,6 @@ def idx2rc(idx, acc): return rst @jit # fill a node (may be two or more points) - def fill(img, msk, p, nbs, buf): buf[0] = p back = img[p] @@ -77,7 +76,7 @@ def filter(img, msk, idx, bur, tor, mode): msk = msk.ravel() arg = np.argsort(img[idx])[::-1 if mode else 1] - + for i in arg: if msk[idx[i]]!=3: idx[i] = 0 diff --git a/imagepy/menus/Edit/edit_plg.py b/imagepy/menus/Edit/edit_plg.py index 4cd5b3c9..3c8ad236 100644 --- a/imagepy/menus/Edit/edit_plg.py +++ b/imagepy/menus/Edit/edit_plg.py @@ -69,14 +69,14 @@ class Clear(Filter): note = ['req_roi', 'all', 'auto_snap', 'not_channel'] def run(self, ips, snap, img, para=None): - img[ips.get_msk()] = ColorManager.get_back(snap.ndim==2) + img[ips.get_msk()] = ColorManager.get_back(ips.channels!=3) class ClearOut(Filter): title = 'Clear Out' note = ['req_roi', 'all', 'auto_snap', 'not_channel'] def run(self, ips, snap, img, para=None): - img[ips.get_msk('out')] = ColorManager.get_back(snap.ndim==2) + img[ips.get_msk('out')] = ColorManager.get_back(ips.channels!=3) class Copy(Simple): title = 'Copy' @@ -99,14 +99,14 @@ class Sketch(Filter): view = [(int, 'width', (0,30), 0, 'width', 'pix')] def run(self, ips, snap, img, para = None): - img[ips.get_msk(para['width'])] = ColorManager.get_front(snap.ndim==2) + img[ips.get_msk(para['width'])] = ColorManager.get_front(ips.channels!=3) class Fill(Filter): title = 'Fill' note = ['req_roi', 'all', 'auto_snap', 'not_channel'] def run(self, ips, snap, img, para=None): - img[ips.get_msk()] = ColorManager.get_front(snap.ndim==2) + img[ips.get_msk()] = ColorManager.get_front(ips.channels!=3) class Undo(Simple): title = 'Undo' diff --git a/imagepy/menus/Image/Adjust/colorstairs_plg.py b/imagepy/menus/Image/Adjust/colorstairs_plg.py index 3fef60f4..b7485d2a 100644 --- a/imagepy/menus/Image/Adjust/colorstairs_plg.py +++ b/imagepy/menus/Image/Adjust/colorstairs_plg.py @@ -11,7 +11,6 @@ from imagepy.ui.widgets import HistCanvas class StairsDialog(ParaDialog): - def para_check(self, para, key): self.ctrl_dic['red'].set_lim(para['t1_red'], para['t2_red']) self.ctrl_dic['green'].set_lim(para['t1_green'], para['t2_green']) diff --git a/imagepy/menus/Image/Channels/__init__.py b/imagepy/menus/Image/Channels/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/menus/Image/__init__.py b/imagepy/menus/Image/__init__.py index 74d714b4..96c9ebb6 100644 --- a/imagepy/menus/Image/__init__.py +++ b/imagepy/menus/Image/__init__.py @@ -1,3 +1,3 @@ -catlog = ['Type', '-', 'Adjust', 'Color', 'Stack', 'Transform', '-', 'duplicate_plg', - 'crop_plg', 'canvassize_plg', 'resize_plg', '-', 'setscale_plg', 'background_plg', - '-', 'Mark', 'Lookup table'] \ No newline at end of file +catlog = ['Type', '-', 'Adjust', 'Color', 'Stack', 'Channels', 'Transform', '-', + 'duplicate_plg', 'crop_plg', 'canvassize_plg', 'resize_plg', '-', + 'setscale_plg', 'background_plg', '-', 'Mark', 'Lookup table'] \ No newline at end of file diff --git a/imagepy/tools/Draw/magic_tol.py b/imagepy/tools/Draw/magic_tol.py index 5cefe9f2..14af9daf 100644 --- a/imagepy/tools/Draw/magic_tol.py +++ b/imagepy/tools/Draw/magic_tol.py @@ -38,7 +38,7 @@ def __init__(self): self.oper = '' def mouse_down(self, ips, x, y, btn, **key): - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==1 or btn==3: if ips.roi!= None: self.curobj = ips.roi.pick(x, y, ips.cur, lim) @@ -84,7 +84,7 @@ def mouse_up(self, ips, x, y, btn, **key): def mouse_move(self, ips, x, y, btn, **key): if ips.roi==None:return - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==None: self.cursor = wx.CURSOR_CROSS if ips.roi.snap(x, y, ips.cur, lim)!=None: diff --git a/imagepy/tools/Measure/angle2_tol.py b/imagepy/tools/Measure/angle2_tol.py index a8ea02be..b1c7fec8 100644 --- a/imagepy/tools/Measure/angle2_tol.py +++ b/imagepy/tools/Measure/angle2_tol.py @@ -86,7 +86,7 @@ def mouse_down(self, ips, x, y, btn, **key): if isinstance(ips.mark, Angle): ips.mark.report(ips.title) return - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==1: # If not painting and exists roi, then try to select roi? if not self.doing: @@ -117,7 +117,7 @@ def mouse_up(self, ips, x, y, btn, **key): def mouse_move(self, ips, x, y, btn, **key): if not isinstance(ips.mark, Angle):return - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==None: self.cursor = wx.CURSOR_CROSS if ips.mark.snap(x, y, lim)!=None: diff --git a/imagepy/tools/Measure/angle_tol.py b/imagepy/tools/Measure/angle_tol.py index 929c5479..c7355def 100644 --- a/imagepy/tools/Measure/angle_tol.py +++ b/imagepy/tools/Measure/angle_tol.py @@ -90,7 +90,7 @@ def mouse_down(self, ips, x, y, btn, **key): if isinstance(ips.mark, Angle): ips.mark.report(ips.title) return - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==1: # 如果有没有在绘制中,且已经有roi,则试图选取 if not self.doing: @@ -121,7 +121,7 @@ def mouse_up(self, ips, x, y, btn, **key): def mouse_move(self, ips, x, y, btn, **key): if not isinstance(ips.mark, Angle):return - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==None: self.cursor = wx.CURSOR_CROSS if ips.mark.snap(x, y, lim)!=None: diff --git a/imagepy/tools/Measure/area_tol.py b/imagepy/tools/Measure/area_tol.py index 60b3f701..e2e56d83 100644 --- a/imagepy/tools/Measure/area_tol.py +++ b/imagepy/tools/Measure/area_tol.py @@ -101,7 +101,7 @@ def mouse_down(self, ips, x, y, btn, **key): ips.mark.report(ips.title) return - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==1: if not self.doing: if isinstance(ips.mark, Area): @@ -128,7 +128,7 @@ def mouse_down(self, ips, x, y, btn, **key): def mouse_move(self, ips, x, y, btn, **key): if not isinstance(ips.mark, Area):return - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==None: self.cursor = wx.CURSOR_CROSS if ips.mark.snap(x, y, lim)!=None: diff --git a/imagepy/tools/Measure/coordinate_tol.py b/imagepy/tools/Measure/coordinate_tol.py index 21215dc8..4b43fdb2 100644 --- a/imagepy/tools/Measure/coordinate_tol.py +++ b/imagepy/tools/Measure/coordinate_tol.py @@ -66,7 +66,7 @@ def mouse_down(self, ips, x, y, btn, **key): if isinstance(ips.mark, Coordinate): ips.mark.report(ips.title) return - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==1: if isinstance(ips.mark, Coordinate): self.curobj = ips.mark.pick(x, y, lim) @@ -85,7 +85,7 @@ def mouse_up(self, ips, x, y, btn, **key): def mouse_move(self, ips, x, y, btn, **key): if not isinstance(ips.mark, Coordinate):return - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==None: self.cursor = wx.CURSOR_CROSS if ips.mark.snap(x, y, lim)!=None: diff --git a/imagepy/tools/Measure/distance_tol.py b/imagepy/tools/Measure/distance_tol.py index d5c80d25..373fa7cd 100644 --- a/imagepy/tools/Measure/distance_tol.py +++ b/imagepy/tools/Measure/distance_tol.py @@ -86,7 +86,7 @@ def mouse_down(self, ips, x, y, btn, **key): ips.mark.report(ips.title) return - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==1: if not self.doing: if isinstance(ips.mark, Distance): @@ -116,7 +116,7 @@ def mouse_up(self, ips, x, y, btn, **key): def mouse_move(self, ips, x, y, btn, **key): if not isinstance(ips.mark, Distance):return - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==None: self.cursor = wx.CURSOR_CROSS if ips.mark.snap(x, y, lim)!=None: diff --git a/imagepy/tools/Measure/profile_tol.py b/imagepy/tools/Measure/profile_tol.py index 4da767d9..3bd0ed48 100644 --- a/imagepy/tools/Measure/profile_tol.py +++ b/imagepy/tools/Measure/profile_tol.py @@ -87,7 +87,7 @@ def mouse_down(self, ips, x, y, btn, **key): if isinstance(ips.mark, Profile): ips.mark.report(ips.title) return - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==1: if not self.doing: if isinstance(ips.mark, Profile): @@ -116,7 +116,7 @@ def mouse_up(self, ips, x, y, btn, **key): def mouse_move(self, ips, x, y, btn, **key): if not isinstance(ips.mark, Profile):return - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==None: self.cursor = wx.CURSOR_CROSS if ips.mark.snap(x, y, lim)!=None: diff --git a/imagepy/tools/Standard/freearea_tol.py b/imagepy/tools/Standard/freearea_tol.py index 9f3327ba..422c6be1 100644 --- a/imagepy/tools/Standard/freearea_tol.py +++ b/imagepy/tools/Standard/freearea_tol.py @@ -19,7 +19,7 @@ def __init__(self): self.helper = Polygonbuf() def mouse_down(self, ips, x, y, btn, **key): - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale ips.mark = self.helper if btn==1: if not self.doing: @@ -56,7 +56,7 @@ def mouse_up(self, ips, x, y, btn, **key): def mouse_move(self, ips, x, y, btn, **key): if ips.roi==None:return - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==None: self.cursor = wx.CURSOR_CROSS if ips.roi.snap(x, y, ips.cur, lim)!=None: diff --git a/imagepy/tools/Standard/freeline_tol.py b/imagepy/tools/Standard/freeline_tol.py index 81cc4b3f..4201da44 100644 --- a/imagepy/tools/Standard/freeline_tol.py +++ b/imagepy/tools/Standard/freeline_tol.py @@ -37,7 +37,7 @@ def __init__(self): self.odx,self.ody = 0, 0 def mouse_down(self, ips, x, y, btn, **key): - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale ips.mark = self.helper if btn==1: if not self.doing: @@ -65,7 +65,7 @@ def mouse_up(self, ips, x, y, btn, **key): def mouse_move(self, ips, x, y, btn, **key): if ips.roi==None:return - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==None: self.cursor = wx.CURSOR_CROSS if ips.roi.snap(x, y, ips.cur, lim)!=None: diff --git a/imagepy/tools/Standard/line_tol.py b/imagepy/tools/Standard/line_tol.py index 2f6cb5d2..2fd9b9ed 100644 --- a/imagepy/tools/Standard/line_tol.py +++ b/imagepy/tools/Standard/line_tol.py @@ -37,7 +37,7 @@ def __init__(self): self.odx,self.ody = 0, 0 def mouse_down(self, ips, x, y, btn, **key): - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale ips.mark = self.helper if btn==1: if not self.doing: @@ -73,7 +73,7 @@ def mouse_up(self, ips, x, y, btn, **key): def mouse_move(self, ips, x, y, btn, **key): if ips.roi==None:return - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==None: self.cursor = wx.CURSOR_CROSS if ips.roi.snap(x, y, ips.cur, lim)!=None: diff --git a/imagepy/tools/Standard/magic_tol.py b/imagepy/tools/Standard/magic_tol.py index 5cefe9f2..14af9daf 100644 --- a/imagepy/tools/Standard/magic_tol.py +++ b/imagepy/tools/Standard/magic_tol.py @@ -38,7 +38,7 @@ def __init__(self): self.oper = '' def mouse_down(self, ips, x, y, btn, **key): - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==1 or btn==3: if ips.roi!= None: self.curobj = ips.roi.pick(x, y, ips.cur, lim) @@ -84,7 +84,7 @@ def mouse_up(self, ips, x, y, btn, **key): def mouse_move(self, ips, x, y, btn, **key): if ips.roi==None:return - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==None: self.cursor = wx.CURSOR_CROSS if ips.roi.snap(x, y, ips.cur, lim)!=None: diff --git a/imagepy/tools/Standard/move_tol.py b/imagepy/tools/Standard/move_tol.py index 2cc8b963..bcb5c49b 100644 --- a/imagepy/tools/Standard/move_tol.py +++ b/imagepy/tools/Standard/move_tol.py @@ -24,7 +24,9 @@ def mouse_move(self, ips, x, y, btn, **key): x,y = key['canvas'].to_panel_coor(x,y) key['canvas'].move(x-self.ox, y-self.oy) self.ox, self.oy = x,y + ips.update() def mouse_wheel(self, ips, x, y, d, **key): - if d>0:key['canvas'].zoomout(x,y) - if d<0:key['canvas'].zoomin(x,y) \ No newline at end of file + if d>0:key['canvas'].zoomout(x, y, 'data') + if d<0:key['canvas'].zoomin(x, y, 'data') + ips.update() \ No newline at end of file diff --git a/imagepy/tools/Standard/oval_tol.py b/imagepy/tools/Standard/oval_tol.py index 3eb1e116..2faf4db4 100644 --- a/imagepy/tools/Standard/oval_tol.py +++ b/imagepy/tools/Standard/oval_tol.py @@ -19,7 +19,7 @@ def __init__(self): self.helper = Polygonbuf() def mouse_down(self, ips, x, y, btn, **key): - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale ips.mark = self.helper if btn==1: if not self.doing: @@ -68,7 +68,7 @@ def mouse_up(self, ips, x, y, btn, **key): def mouse_move(self, ips, x, y, btn, **key): if ips.roi==None:return - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==None: self.cursor = wx.CURSOR_CROSS if ips.roi.snap(x, y, ips.cur, lim)!=None: diff --git a/imagepy/tools/Standard/point_tol.py b/imagepy/tools/Standard/point_tol.py index d9f5fd29..1284dda1 100644 --- a/imagepy/tools/Standard/point_tol.py +++ b/imagepy/tools/Standard/point_tol.py @@ -15,7 +15,7 @@ def __init__(self): self.odx, self.ody = 0, 0 def mouse_down(self, ips, x, y, btn, **key): - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==1: if ips.roi!=None: self.curobj = ips.roi.pick(x, y, ips.cur, lim) @@ -34,7 +34,7 @@ def mouse_up(self, ips, x, y, btn, **key): def mouse_move(self, ips, x, y, btn, **key): if ips.roi==None:return - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==None: self.cursor = wx.CURSOR_CROSS self.onobj = ips.roi.snap(x, y, ips.cur, lim) @@ -46,7 +46,7 @@ def mouse_move(self, ips, x, y, btn, **key): self.odx, self.ody = x, y def mouse_wheel(self, ips, x, y, d, **key): - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if not isinstance(ips.roi, pointroi.PointRoi):return if not self.onobj is None: cur = ips.roi.body[self.onobj][2] diff --git a/imagepy/tools/Standard/polygon_tol.py b/imagepy/tools/Standard/polygon_tol.py index 8af59ef8..73867bcd 100644 --- a/imagepy/tools/Standard/polygon_tol.py +++ b/imagepy/tools/Standard/polygon_tol.py @@ -36,7 +36,7 @@ def __init__(self): self.helper = Polygonbuf() def mouse_down(self, ips, x, y, btn, **key): - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale ips.mark = self.helper if btn==1: if not self.doing: @@ -72,7 +72,7 @@ def mouse_down(self, ips, x, y, btn, **key): def mouse_move(self, ips, x, y, btn, **key): if ips.roi==None:return - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==None: self.cursor = wx.CURSOR_CROSS if ips.roi.snap(x, y, ips.cur, lim)!=None: diff --git a/imagepy/tools/Standard/rectangle_tol.py b/imagepy/tools/Standard/rectangle_tol.py index 7da12f65..28e998fc 100644 --- a/imagepy/tools/Standard/rectangle_tol.py +++ b/imagepy/tools/Standard/rectangle_tol.py @@ -18,7 +18,7 @@ def __init__(self): self.helper = Polygonbuf() def mouse_down(self, ips, x, y, btn, **key): - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale ips.mark = self.helper if btn==1: if not self.doing: @@ -67,7 +67,7 @@ def mouse_up(self, ips, x, y, btn, **key): def mouse_move(self, ips, x, y, btn, **key): if ips.roi==None:return - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if btn==None: self.cursor = wx.CURSOR_CROSS if ips.roi.snap(x, y, ips.cur, lim)!=None: diff --git a/imagepy/tools/Standard/scale_tol.py b/imagepy/tools/Standard/scale_tol.py index 8bcc6f79..8f64c1dc 100644 --- a/imagepy/tools/Standard/scale_tol.py +++ b/imagepy/tools/Standard/scale_tol.py @@ -16,8 +16,9 @@ def mouse_down(self, ips, x, y, btn, **key): self.ox, self.oy = key['canvas'].to_panel_coor(x,y) print(self.ox, self.oy) #print 'down', self.ox, self.oy - if btn==1: key['canvas'].zoomout(x,y) - if btn==3: key['canvas'].zoomin(x,y) + if btn==1: key['canvas'].zoomout(x, y, 'data') + if btn==3: key['canvas'].zoomin(x, y, 'data') + ips.update() def mouse_up(self, ips, x, y, btn, **key): pass @@ -28,8 +29,10 @@ def mouse_move(self, ips, x, y, btn, **key): #print 'x,y',x,y #print 'dx,dy:', x-self.ox, y-self.oy key['canvas'].move(x-self.ox, y-self.oy) + ips.update() self.ox, self.oy = x,y def mouse_wheel(self, ips, x, y, d, **key): - if d>0:key['canvas'].zoomout(x,y) - if d<0:key['canvas'].zoomin(x,y) \ No newline at end of file + if d>0:key['canvas'].zoomout(x, y, 'data') + if d<0:key['canvas'].zoomin(x, y, 'data') + ips.update() \ No newline at end of file diff --git a/imagepy/tools/Transform/rotate_tol.py b/imagepy/tools/Transform/rotate_tol.py index 3a1d616b..f1a3941b 100644 --- a/imagepy/tools/Transform/rotate_tol.py +++ b/imagepy/tools/Transform/rotate_tol.py @@ -11,7 +11,7 @@ def __init__(self, plg): self.moving = False def mouse_down(self, ips, x, y, btn, **key): - lim = 5.0/key['canvas'].get_scale() + lim = 5.0/key['canvas'].scale if abs(x-self.para['ox'])r1[0]:r2[0]=r1[0] - elif r2[0]+r2[2]r1[1]:r2[1]=r1[1] - elif r2[1]+r2[3]=0 and xx=0 and yy winbox[0]: + conbox[0] = winbox[0] + conbox[2] = conbox[0] + conw + elif conbox[2] < winbox[2]: + conbox[2] = winbox[2] + conbox[0] = conbox[2] - conw + +def layy(winbox, conbox): + winh = winbox[3]-winbox[1] + conh = conbox[3]-conbox[1] + if conh winbox[1]: + conbox[1] = winbox[1] + conbox[3] = conbox[1] + conh + elif conbox[3] < winbox[3]: + conbox[3] = winbox[3] + conbox[1] = conbox[3] - conh + +def lay(winbox, conbox): + layx(winbox, conbox) + layy(winbox, conbox) + +def mat(ori, vir, cros): + kx = (ori[2]-ori[0])/(vir[2]-vir[0]) + ky = (ori[3]-ori[1])/(vir[3]-vir[1]) + ox = (cros[1]-vir[1])*ky + oy = (cros[0]-vir[0])*kx + return (ox, oy), (kx, ky) diff --git a/imagepy/ui/canvas/canvas.py b/imagepy/ui/canvas/canvas.py new file mode 100644 index 00000000..1a5be63f --- /dev/null +++ b/imagepy/ui/canvas/canvas.py @@ -0,0 +1,252 @@ +import wx, numpy as np +from .boxutil import cross, multiply, lay, mat +from .imutil import mix_img +from .mark import drawmark +from time import time + +class Canvas (wx.Panel): + scales = [0.03125, 0.0625, 0.125, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4, 5, 8, 10, 15, 20, 30, 50] + + def __init__(self, parent, autofit=False): + wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.TAB_TRAVERSAL ) + self.img = None + self.back = None + self.mode = 'set' + + self.winbox = None + self.conbox = None + self.oribox = None + + self.outbak = None + self.outimg = None + self.outrgb = None + self.outbmp = None + self.outint = None + + self.buffer = None + + lut = np.arange(256*3) + lut.shape = (256,3) + lut = lut.astype(np.uint8) + + self.lut = lut + self.rg = (0, 255) + self.cn = 0 + + self._lut = lut + self._rg = (0, 255) + self._cn = 0 + + self.marks = {} + + self.scaidx = 6 + self.autofit = autofit + self.scrbox = wx.DisplaySize() + self.bindEvents() + + def bindEvents(self): + for event, handler in [ \ + (wx.EVT_SIZE, self.on_size), + (wx.EVT_MOUSE_EVENTS, self.on_mouseevent), + (wx.EVT_IDLE, self.on_idle), + (wx.EVT_PAINT, self.on_paint)]: + self.Bind(event, handler) + + def on_mouseevent(self, me): + if me.ButtonDown(): + if me.GetButton()==1: + self.oldxy = me.GetX(), me.GetY() + if me.GetButton()==3: + self.fit() + wheel = np.sign(me.GetWheelRotation()) + if wheel!=0: + if wheel == 1: + self.zoomout(me.GetX(), me.GetY()) + if wheel == -1: + self.zoomin(me.GetX(), me.GetY()) + if me.Dragging(): + x, y = self.oldxy + self.move(me.GetX()-x, me.GetY()-y) + self.oldxy = me.GetX(), me.GetY() + + def initBuffer(self): + box = self.GetClientSize() + self.buffer = wx.Bitmap(*box) + self.winbox = [0, 0, *box] + + def fit(self): + oriw = self.oribox[2]-self.oribox[0] + orih = self.oribox[3]-self.oribox[1] + if not self.autofit: a,b,c,d = self.winbox + else: + (a,b),(c,d) = (0,0), self.scrbox + c, d = c*0.9, d*0.9 + for i in self.scales[6::-1]: + if oriw*i=0 and xx=0 and yy1 else '', - ips.size[0], ips.size[1], ips.imgtype, round(ips.get_nbytes()/1024.0/1024.0, 2)) + ips.chan+1 if isinstance(ips.chan, int) else tuple(ips.chan), ips.get_nchannels(), + stk if ips.get_nslices()>1 else '', ips.size[0], ips.size[1], ips.imgtype, round(ips.get_nbytes()/1024.0/1024.0, 2)) if label != self.txt_info.GetLabel(): self.txt_info.SetLabel(label) - - + def set_fit(self, ips): + resize = False if ips.get_nslices() != self.opage: self.opage = ips.get_nslices() if ips.get_nslices()==1 and self.page.Shown: @@ -87,38 +182,62 @@ def set_info(self, ips, resize=False): resize = True if ips.get_nslices()>1 and not self.page.Shown: self.page.Show() + print('Show ......') resize = True self.page.SetScrollbar(0, 0, ips.get_nslices()-1, 0, refresh=True) - if ips.get_nchannels() != self.ochan: + if ips.get_nchannels() != self.ochan or type(ips.chan) != self.chantype: self.ochan = ips.get_nchannels() - isrgb = ips.get_imgtype()=='rgb' - if (ips.get_nchannels()==1 or isrgb) and self.chan.Shown: + self.chantype = type(ips.chan) + isrgb = not isinstance(ips.chan, int) + if not isinstance(ips.chan, int) and self.chan.Shown: self.chan.Hide() resize = True - if ips.get_nchannels()>1 and not isrgb and not self.chan.Shown: + if isinstance(ips.chan, int) and ips.get_nchannels()>1 and not self.chan.Shown: self.chan.Show() resize = True self.chan.SetMax(ips.get_nchannels()-1) - if resize: - if IPy.uimode()!='ipy': self.Fit() - else: - #self.SetSizer(self.GetSizer()) - self.Layout() + a,b,c,d = self.canvas.conbox + l = ((c-a)**2+(d-b)**2)**0.5 + if resize or abs(self.olddia-l)>1: + self.olddia = l + if IPy.uimode()!='ipy': + w = self.canvas.scrbox[0]*0.9 + h = self.canvas.scrbox[1]*0.9 + if c-aself.sli_high.GetValue(): + self.sli_high.SetValue(self.sli_low.GetValue()) + rg = (self.sli_low.GetValue(), self.sli_high.GetValue()) + ips.chan_range[self.active] = rg + minv, maxv = self.sli_low.min, self.sli_high.max + lim1 = 1.0 * (self.sli_low.GetValue() - minv)/(maxv-minv) + lim2 = 1.0 * (self.sli_high.GetValue() - minv)/(maxv-minv) + self.histpan.set_lim(lim1*255, lim2*255) + ips.update() + + def on_high( self, event ): + ips = IPy.get_ips() + if ips is None: return + if self.sli_low.GetValue()>self.sli_high.GetValue(): + self.sli_low.SetValue(self.sli_high.GetValue()) + rg = (self.sli_low.GetValue(), self.sli_high.GetValue()) + ips.chan_range[self.active] = rg + minv, maxv = self.sli_low.min, self.sli_high.max + lim1 = 1.0 * (self.sli_low.GetValue() - minv)/(maxv-minv) + lim2 = 1.0 * (self.sli_high.GetValue() - minv)/(maxv-minv) + self.histpan.set_lim(lim1*255, lim2*255) + ips.update() + + def on_rgb( self, event, color): + ips = IPy.get_ips() + if ips is None: return + if isinstance(ips.chan, int): + ips.chan = [0, 1, 2] + chs = ['C:%d'%i for i in range(ips.channels)] + for i in (self.com_r, self.com_g, self.com_b): + chn = i.GetValue() + i.SetItems(chs) + idx = chs.index(chn) if chn in chs else 0 + i.Select(idx) + chanred = ips.chan['rgb'.index(color)] + chanrg = ips.chan_range[chanred] + rg = ips.get_updown('all', chanred, 512) + if (rg[0]==rg[1]): rg = (rg[0]-1e-4, rg[1]+1e-4) + slis = 'all' if self.chk_stack.GetValue() else ips.cur + hist = ips.histogram(rg, slis, chanred, 512) + self.histpan.SetValue(hist) + self.sli_low.set_para(rg, 10) + self.sli_high.set_para(rg, 10) + self.sli_low.SetValue(chanrg[0]) + self.sli_high.SetValue(chanrg[1]) + self.active = ips.chan['rgb'.index(color)] + self.range = chanrg + lim1 = (chanrg[0]-rg[0])/(rg[1]-rg[0]) + lim2 = (chanrg[1]-rg[0])/(rg[1]-rg[0]) + self.histpan.set_lim(lim1*255, lim2*255) + ips.update() + + def on_gray(self, event): + ips = IPy.get_ips() + if ips is None: return + if not isinstance(ips.chan, int): ips.chan = 0 + chanrg = ips.chan_range[ips.chan] + rg = ips.get_updown('all', ips.chan, 512) + if (rg[0]==rg[1]): rg = (rg[0]-1e-4, rg[1]+1e-4) + slis = 'all' if self.chk_stack.GetValue() else ips.cur + hist = ips.histogram(rg, slis, ips.chan, 512) + self.histpan.SetValue(hist) + self.sli_low.set_para(rg, 10) + self.sli_high.set_para(rg, 10) + self.sli_low.SetValue(chanrg[0]) + self.sli_high.SetValue(chanrg[1]) + self.active = ips.chan + lim1 = (chanrg[0]-rg[0])/(rg[1]-rg[0]) + lim2 = (chanrg[1]-rg[0])/(rg[1]-rg[0]) + self.histpan.set_lim(lim1*255, lim2*255) + ips.update() + + def on_chan(self, event, color): + ips = IPy.get_ips() + if ips is None: return + C = (self.com_r, self.com_g, self.com_b) + host = C['rgb'.index(color)] + chnidx = host.GetSelection() + ips.chan['rgb'.index(color)] = chnidx + self.on_rgb(event, color) + + def on_8bit(self, event): + ips = IPy.get_ips() + if ips is None: return + rg = (0,255) + ips.chan_range[self.active] = rg + if (rg[0]==rg[1]): rg = (rg[0]-1e-4, rg[1]+1e-4) + slis = 'all' if self.chk_stack.GetValue() else ips.cur + hist = ips.histogram(rg, slis, self.active, 512) + self.histpan.SetValue(hist) + self.sli_low.set_para(rg, 10) + self.sli_high.set_para(rg, 10) + self.sli_low.SetValue(rg[0]) + self.sli_high.SetValue(rg[1]) + self.histpan.set_lim(0, 255) + ips.update() + + def on_p(self, event, k): + ips = IPy.get_ips() + if ips is None: return + rg = ips.get_updown('all', self.active, 512) + if (rg[0]==rg[1]): rg = (rg[0]-1e-4, rg[1]+1e-4) + slis = 'all' if self.chk_stack.GetValue() else ips.cur + hist = ips.histogram(rg, slis, self.active, 512) + msk = np.abs(np.cumsum(hist)/hist.sum()-0.5)self.sli_high.GetValue(): self.sli_low.SetValue(self.sli_high.GetValue()) ips.range = (self.sli_low.GetValue(), self.sli_high.GetValue()) + ips.chan_rg = ips.range lim1 = 1.0 * (self.sli_low.GetValue() - self.range[0])/(self.range[1]-self.range[0]) lim2 = 1.0 * (self.sli_high.GetValue() - self.range[0])/(self.range[1]-self.range[0]) self.histpan.set_lim(lim1*255, lim2*255) @@ -153,5 +155,5 @@ def on_slice( self, event ): def on_stack( self, event ): ips = IPy.get_ips() if ips is None: return - hists = ips.histogram(stack=True) + hists = ips.histogram(slices=True) self.histpan.SetValue(hists) \ No newline at end of file diff --git a/imagepy/widgets/navigator/navigator_wgt.py b/imagepy/widgets/navigator/navigator_wgt.py index e7ccc2e3..5188c888 100644 --- a/imagepy/widgets/navigator/navigator_wgt.py +++ b/imagepy/widgets/navigator/navigator_wgt.py @@ -5,7 +5,7 @@ class Plugin ( wx.Panel ): title = 'Navigator' - scales = [0.03125, 0.0625, 0.125, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4, 5, 8, 10] + scales = [0.03125, 0.0625, 0.125, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4, 5, 8, 10][::-1] def __init__( self, parent ): @@ -64,50 +64,47 @@ def __init__( self, parent ): def on_apply(self, event): win = IPy.get_window() if win is None: return - img = win.canvas.ips.img + img = win.ips.img step = max(max(img.shape[:2])//300,1) - self.viewport.set_img(win.canvas.ips.lookup(img[::step,::step]), img.shape) - self.viewport.set_box(win.canvas.imgbox, win.canvas.box) + self.viewport.set_img(win.ips.lookup(img[::step,::step]), img.shape) + self.viewport.set_box(win.canvas.conbox, win.canvas.winbox) def on_zoom(self, event): k = self.scales[self.slider.GetValue()] self.label.SetLabel('%.2f%%'%(k*100)) win = IPy.get_window() if win is None: return - a,b,c,d = win.canvas.box - x, y = win.canvas.to_data_coor(c/2, d/2) + a,b,c,d = win.canvas.winbox win.canvas.scaleidx = self.slider.GetValue() - win.canvas.zoom(k, x, y) - win.canvas.ips.update() - self.viewport.set_box(win.canvas.imgbox, win.canvas.box) + win.canvas.zoom(k, (a+c)/2, (b+d)/2) + win.ips.update() + self.viewport.set_box(win.canvas.conbox, win.canvas.winbox) def on_fit(self, event): win = IPy.get_window() if win is None: return - win.canvas.self_fit() - win.canvas.ips.update() + win.canvas.fit() + win.ips.update() self.slider.SetValue(win.canvas.scaleidx) k = self.scales[self.slider.GetValue()] self.label.SetLabel('%.2f%%'%(k*100)) - self.viewport.set_box(win.canvas.imgbox, win.canvas.box) + self.viewport.set_box(win.canvas.conbox, win.canvas.winbox) def on_one(self, event): win = IPy.get_window() if win is None: return - a,b,c,d = win.canvas.box - x, y = win.canvas.to_data_coor(c/2, d/2) + a,b,c,d = win.canvas.winbox win.canvas.scaleidx = self.scales.index(1) - win.canvas.zoom(1, x, y) - win.canvas.ips.update() + win.canvas.zoom(1, (a+c)/2, (b+d)/2) + win.ips.update() self.slider.SetValue(win.canvas.scaleidx) self.label.SetLabel('%.2f%%'%100) - self.viewport.set_box(win.canvas.imgbox, win.canvas.box) + self.viewport.set_box(win.canvas.conbox, win.canvas.winbox) def on_handle(self): win = IPy.get_window() if win is None: return x, y = self.viewport.GetValue() - print(x, y) - win.canvas.center(x, y) - win.canvas.ips.update() - self.viewport.set_box(win.canvas.imgbox, win.canvas.box) \ No newline at end of file + win.canvas.center(x, y, 'data') + win.ips.update() + self.viewport.set_box(win.canvas.conbox, win.canvas.winbox) \ No newline at end of file From 228d51f21115c3d847d11369434c8b933bbc87f4 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 15 Sep 2019 19:27:21 +0800 Subject: [PATCH 159/343] label tool --- imagepy/core/engine/mkdown.py | 3 +- imagepy/menus/Process/Classify/__init__.py | 0 imagepy/menus/Process/Classify/label_wgt.py | 107 ++++++++++++++++++++ imagepy/menus/Process/__init__.py | 2 +- imagepy/ui/mkdownwindow.py | 6 +- imagepy/widgets/histogram/histogram_wgt.py | 2 +- 6 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 imagepy/menus/Process/Classify/__init__.py create mode 100644 imagepy/menus/Process/Classify/label_wgt.py diff --git a/imagepy/core/engine/mkdown.py b/imagepy/core/engine/mkdown.py index b972c316..eb7a9f60 100644 --- a/imagepy/core/engine/mkdown.py +++ b/imagepy/core/engine/mkdown.py @@ -4,10 +4,11 @@ class MkDown: def __init__(self, title, cont, url=''): self.title = title self.cont = cont + self.url = url def __call__(self): return self - def run(self): IPy.show_md(self.title, self.cont) + def run(self): IPy.show_md(self.title, self.cont, self.url) def start(self, para=None, callafter=None): self.run() diff --git a/imagepy/menus/Process/Classify/__init__.py b/imagepy/menus/Process/Classify/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/menus/Process/Classify/label_wgt.py b/imagepy/menus/Process/Classify/label_wgt.py new file mode 100644 index 00000000..52900f8f --- /dev/null +++ b/imagepy/menus/Process/Classify/label_wgt.py @@ -0,0 +1,107 @@ +from imagepy.ui.widgets import CMapSelCtrl +from imagepy.core.manager import ColorManager, ImageManager, WindowsManager +import numpy as np +import wx + +class Plugin ( wx.Panel ): + title = 'Label Tool' + def __init__( self, parent ): + wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size(-1,-1), style = wx.TAB_TRAVERSAL ) + outsizer = wx.BoxSizer(wx.HORIZONTAL) + sizer = wx.BoxSizer( wx.VERTICAL ) + sizer_color = wx.BoxSizer( wx.HORIZONTAL ) + self.btns = [] + for i in range(16): + btn = wx.Button( self, wx.ID_ANY, str(i), wx.DefaultPosition, wx.DefaultSize, 0 ) + btn.SetMaxSize( wx.Size( 30,-1 ) ) + self.btns.append(btn) + sizer_color.Add( btn, 0, wx.ALL, 2 ) + self.spn_num = wx.SpinCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.SP_ARROW_KEYS, 2, 15, 0 ) + self.spn_num.SetMaxSize(wx.Size(45, -1)) + sizer_color.Add( self.spn_num, 0, wx.ALL|wx.EXPAND, 3 ) + + sizer.Add(sizer_color, 0, wx.ALL|wx.EXPAND, 0) + + sizer_other = wx.BoxSizer( wx.HORIZONTAL ) + + self.cmapsel = CMapSelCtrl(self) + self.cmapsel.SetItems(ColorManager.luts) + sizer_other.Add(self.cmapsel, 0, wx.ALL|wx.EXPAND, 3 ) + + com_backChoices = [ u"No Background" ] + self.com_back = wx.ComboBox( self, wx.ID_ANY, u"No Background", wx.DefaultPosition, wx.DefaultSize, com_backChoices, wx.CB_READONLY) + self.com_back.SetSelection( 0 ) + sizer_other.Add( self.com_back, 1, wx.ALL|wx.EXPAND, 3 ) + + com_modeChoices = [ u"None", u"Max", u"Min", u"Mask", u"2-8mix", u"4-6mix", u"5-5mix", u"6-4mix", u"8-2mix" ] + self.com_mode = wx.ComboBox( self, wx.ID_ANY, u"Min", wx.DefaultPosition, wx.DefaultSize, com_modeChoices, wx.CB_READONLY ) + self.com_mode.SetSelection( 0 ) + sizer_other.Add( self.com_mode, 0, wx.ALL|wx.EXPAND, 3 ) + + self.chk_hide = wx.CheckBox( self, wx.ID_ANY, u"Hide", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_other.Add( self.chk_hide, 0, wx.ALIGN_CENTER|wx.ALL, 0 ) + + sizer.Add(sizer_other, 0, wx.ALL|wx.EXPAND, 0) + + outsizer.AddStretchSpacer(prop=1) + outsizer.Add(sizer, 0, wx.ALL, 0) + outsizer.AddStretchSpacer(prop=1) + + self.SetSizer( outsizer ) + self.Layout() + + # Connect Events + self.spn_num.Bind( wx.EVT_SPINCTRL, self.on_cmapsel ) + self.com_back.Bind(wx.EVT_COMBOBOX_DROPDOWN, self.on_items ) + self.cmapsel.Bind(wx.EVT_COMBOBOX, self.on_cmapsel) + self.com_back.Bind( wx.EVT_COMBOBOX, self.on_setback) + self.com_mode.Bind( wx.EVT_COMBOBOX, self.on_mode) + self.chk_hide.Bind( wx.EVT_CHECKBOX, self.on_mode) + for i in self.btns: i.Bind(wx.EVT_BUTTON, self.on_color) + + def on_color(self, event): + ColorManager.set_front(self.btns.index(event.GetEventObject())) + + def on_items(self, event): + items = ['No Background Image']+ImageManager.get_titles() + self.com_back.SetItems(items) + if self.com_back.GetValue() in items: + self.com_back.Select(items.index(self.com_back.GetValue())) + else: self.com_back.Select(0) + + def on_cmapsel(self, event): + key = self.cmapsel.GetValue() + lut = ColorManager.get_lut(key) + n = self.spn_num.GetValue()+1 + idx = np.linspace(0, 255, n).astype(int) + cs = list(lut[idx]) + [(128,128,128)]*(16-n) + for btn, c in zip(self.btns, cs): + btn.SetBackgroundColour(c) + + ips = ImageManager.get() + if ips is None: return + newlut = lut*0 + newlut[:n] = lut[idx] + ips.lut = newlut + ips.update() + + def on_setback(self, event): + name = self.com_back.GetValue() + if name is None: return + curwin = WindowsManager.get() + curwin.set_back(ImageManager.get(name)) + curwin.ips.update() + + def on_mode(self, event): + ips = ImageManager.get() + if ips is None: return + if self.chk_hide.GetValue(): + ips.chan_mode = 0.0 + return ips.update() + modes = ['set', 'max', 'min', 'msk', 0.2, 0.4, 0.5, 0.6, 0.8] + ips.chan_mode = modes[self.com_mode.GetSelection()] + ips.update() + + def __del__( self ): + pass + \ No newline at end of file diff --git a/imagepy/menus/Process/__init__.py b/imagepy/menus/Process/__init__.py index 3abb3703..647836fe 100644 --- a/imagepy/menus/Process/__init__.py +++ b/imagepy/menus/Process/__init__.py @@ -1 +1 @@ -catlog = ['Math', 'Binary', 'Filters', 'FFT', '-', 'Threshold', 'Hydrology', 'Features', 'Segment', 'repair_plg', '-', 'calculator_plg'] \ No newline at end of file +catlog = ['Math', 'Binary', 'Filters', 'FFT', '-', 'Threshold', 'Hydrology', 'Features', 'Segment', 'Classify', 'repair_plg', '-', 'calculator_plg'] \ No newline at end of file diff --git a/imagepy/ui/mkdownwindow.py b/imagepy/ui/mkdownwindow.py index ca0a7096..7a8ded14 100644 --- a/imagepy/ui/mkdownwindow.py +++ b/imagepy/ui/mkdownwindow.py @@ -173,9 +173,11 @@ def md2html(mdstr): ''' ret = markdown(mdstr,extensions=exts) + ''' f = open('yn.html', 'w', encoding='utf-8') f.write(html % ret); f.close() + ''' return html % ret class HtmlPanel(wx.Panel): @@ -190,7 +192,9 @@ def __init__(self, parent, cont='', url=''): sizer.Add(self.wv, 1, wx.EXPAND) self.SetSizer(sizer) - self.wv.SetPage(cont, url) + + if url != '': self.wv.LoadURL(url) + else: self.wv.SetPage(cont, url) def SetValue(self, value): self.wv.SetPage(*value) diff --git a/imagepy/widgets/histogram/histogram_wgt.py b/imagepy/widgets/histogram/histogram_wgt.py index 3ef2bec8..3ecf9351 100644 --- a/imagepy/widgets/histogram/histogram_wgt.py +++ b/imagepy/widgets/histogram/histogram_wgt.py @@ -1,5 +1,5 @@ from ...ui.widgets import HistCanvas, CMapPanel, CMapSelCtrl, FloatSlider -from imagepy.core.manager import ColorManager +from imagepy.core.manager import ColorManager from imagepy import IPy import numpy as np import wx From 053b198afa032d564f1e66b72314148ff1dd319e Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 18 Sep 2019 14:45:22 +0800 Subject: [PATCH 160/343] randomforest --- .../Process/Classify/randomforest_plg.py | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 imagepy/menus/Process/Classify/randomforest_plg.py diff --git a/imagepy/menus/Process/Classify/randomforest_plg.py b/imagepy/menus/Process/Classify/randomforest_plg.py new file mode 100644 index 00000000..117e6c9a --- /dev/null +++ b/imagepy/menus/Process/Classify/randomforest_plg.py @@ -0,0 +1,72 @@ +from imagepy.core.engine import Filter +from imagepy.core.manager import ImageManager + +import numpy as np +from sklearn.ensemble import RandomForestClassifier +import scipy.ndimage as ndimg +from skimage.filters import sobel +from skimage.feature import structure_tensor, structure_tensor_eigvals +from skimage.io import imread, imsave +import pandas as pd + +def norm01(arr): + arr -= arr.min() + arr /= arr.max() + return arr + +# chans 是通道,可以是int,list,默认None,代表所有通道 +def get_feature(img, chans=None, ocvs=3, w=1, ori=True, blr=True, sob=True, eig=True): + feats, titles = [], [] + img = img.reshape(img.shape[:2]+(-1,)) + if chans is None: chans = range(img.shape[2]) + for c in [chans] if isinstance(chans, int) else chans: + if ori: + feats.append(norm01(img[:,:,c].ravel().astype(np.float32))) + titles.append('c%d_ori'%c) + for o in range(ocvs): + blurimg = ndimg.gaussian_filter(img[:,:,c], 2**o, output=np.float32) + feat_sobel = norm01(sobel(blurimg).ravel()) if sob else None + if eig: + Axx, Axy, Ayy = structure_tensor(blurimg, w) + l1, l2 = structure_tensor_eigvals(Axx, Axy, Ayy) + feat_l1, feat_l2 = norm01(l1.ravel()), norm01(l2.ravel()) + else: feat_l1 = feat_l2 = None + feat_gauss = norm01(blurimg.ravel()) if blr else None + featcr = [feat_gauss, feat_sobel, feat_l1, feat_l2] + title = ['c%d_s%d_%s'%(c,o,i) for i in ['gauss', 'sobel', 'l1', 'l2']] + titles.extend([title[i] for i in range(4) if not featcr[i] is None]) + feats.extend([featcr[i] for i in range(4) if not featcr[i] is None]) + return np.array(feats).T, titles + +class Plugin(Filter): + """Closing: derived from imagepy.core.engine.Filter """ + title = 'Random Forest Classify' + note = ['8-bit', 'auto_msk', 'auto_snap', 'preview'] + para = {'img':None, 'ocvs':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, 'eig':True} + view = [('img', 'img', 'img', 'back'), + (int, 'ocvs', (1,7), 0, 'ocvs', ''), + (int, 'w', (0,5), 0, 'sigma', 'tensor'), + (bool, 'ori', 'add image feature'), + (bool, 'blr', 'add blur feature'), + (bool, 'sob', 'add sobel feature'), + (bool, 'eig', 'add eig feature')] + + def run(self, ips, snap, img, para = None): + ori = ImageManager.get(para['img']).img + lab = snap.ravel() + msk = lab != 0 + + lab_value = lab[msk].reshape(-1, 1) + feats, titles = get_feature(ori, None, para['ocvs'], + para['w'], para['ori'], para['blr'], para['sob'], para['eig']) + lab_feats = feats[msk] + + print(titles) + + model = RandomForestClassifier(n_estimators=100, random_state=42, + max_features = 'sqrt', n_jobs=-1, verbose = 1) + + model.fit(lab_feats, lab_value) + rst = model.predict(feats) + img[:] = rst.reshape(img.shape[:2]) + #imsave('rst.png', rstimg) \ No newline at end of file From 5d91ae1562231aa90ea054507042e99859bf4ea4 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 29 Sep 2019 12:57:00 +0800 Subject: [PATCH 161/343] distance 3d modify --- environment.yml | 1 + imagepy/ipyalg/hydrology/edt.py | 28 +++++++--------------------- imagepy/menus/File/new_plg.py | 4 ++-- imagepy/ui/canvas/imutil.py | 4 ++-- setup.py | 1 + 5 files changed, 13 insertions(+), 25 deletions(-) diff --git a/environment.yml b/environment.yml index 4015d660..c184cf25 100644 --- a/environment.yml +++ b/environment.yml @@ -10,6 +10,7 @@ dependencies: - pypubsub - read-roi - scikit-image + - scikit-learn - shapely - wxpython - xlrd diff --git a/imagepy/ipyalg/hydrology/edt.py b/imagepy/ipyalg/hydrology/edt.py index d3469e32..f93ff1d7 100644 --- a/imagepy/ipyalg/hydrology/edt.py +++ b/imagepy/ipyalg/hydrology/edt.py @@ -88,8 +88,8 @@ def distance_transform_edt(img, output=np.float32, scale=1): nbs = neighbors(dis.shape) acc = np.cumprod((1,)+dis.shape[::-1][:-1])[::-1] line = dis.ravel() - pts = np.zeros(max(line.size//8, 1024**2), dtype=np.int64) - roots = np.zeros(max(line.size//8, 1024**2), dtype=np.int64) + pts = np.zeros(max(line.size//4, 1024**2), dtype=np.int64) + roots = np.zeros(max(line.size//4, 1024**2), dtype=np.int64) s = collect(line, nbs, pts, roots) for level in range(10000): s, c = clear(pts, roots, s, 0) @@ -98,22 +98,8 @@ def distance_transform_edt(img, output=np.float32, scale=1): return dis[(slice(1,-1),)*img.ndim] if __name__ == '__main__': - import matplotlib.pyplot as plt - from skimage.io import imread, imsave - from scipy.ndimage import distance_transform_edt as scipyedt - from time import time - - img = np.ones((2048,2048), dtype=np.uint8) - img[1024,1024] = 0 - - start = time() - dis1 = scipyedt(img) - print('scipy', time()-start) - #start = time() - dis = distance_transform_edt(img, np.float32, 2) - #print('my', time()-start) - start = time() - dis2 = distance_transform_edt(img, np.float32, 2) - print('my', time()-start) - plt.imshow(dis2, cmap='gray') - plt.show() + from skimage.io import imread + from glob import glob + fs = glob('C:/Users/54631/Desktop/二值化加黑框/*.png') + arr = np.array([imread(i) for i in fs]) + dis = distance_transform_edt(arr, np.float32) diff --git a/imagepy/menus/File/new_plg.py b/imagepy/menus/File/new_plg.py index daaffc5d..be882d01 100644 --- a/imagepy/menus/File/new_plg.py +++ b/imagepy/menus/File/new_plg.py @@ -16,8 +16,8 @@ class Plugin(Free): title = 'New' para = {'name':'Undefined','width':300, 'height':300, 'type':'8-bit','slice':1} view = [(str, 'name', 'name', ''), - (int, 'width', (1,2048), 0, 'width', 'pix'), - (int, 'height', (1,2048), 0, 'height', 'pix'), + (int, 'width', (1,10240), 0, 'width', 'pix'), + (int, 'height', (1,10240), 0, 'height', 'pix'), (list, 'type', ['8-bit','RGB'], str, 'Type', ''), (int, 'slice', (1,2048), 0, 'slice', '')] diff --git a/imagepy/ui/canvas/imutil.py b/imagepy/ui/canvas/imutil.py index fe7edd4e..8eca0e50 100644 --- a/imagepy/ui/canvas/imutil.py +++ b/imagepy/ui/canvas/imutil.py @@ -26,7 +26,7 @@ def blend(img, out, msk, mode): np.multiply(img, mode, out=img, casting='unsafe') out += img -def blend_jit(img, out, msk, mode): +if not jit is None: @jit def blend_set(img, out, msk, mode): for r in range(img.shape[0]): @@ -92,7 +92,7 @@ def stretch_jit(img, out, rg): def lookup(img, lut, out, mode='set'): blend(lut[img], out, img, mode) -if not lookup is None: +if not jit is None: @jit def lookup_set(img, lut, out, mode): for r in range(img.shape[0]): diff --git a/setup.py b/setup.py index 306e4343..2461558b 100644 --- a/setup.py +++ b/setup.py @@ -30,6 +30,7 @@ def get_data_files(): package_data=get_data_files(), install_requires=[ 'scikit-image', + 'scikit-learn', 'shapely', 'pypubsub', 'wxpython', From 909233dfc577bd005b0198e0677efec2b757d01f Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 10 Oct 2019 15:50:43 +0800 Subject: [PATCH 162/343] classify --- imagepy/core/util/fileio.py | 1 - imagepy/core/wraper/imageplus.py | 2 +- imagepy/menus/Analysis/statistic_plg.py | 2 +- imagepy/menus/Plugins/update_plg.py | 2 +- imagepy/menus/Process/Classify/__init__.py | 1 + .../menus/Process/Classify/classify_plgs.py | 212 ++++++++++++++++++ .../menus/Process/Classify/classify_wgt.py | 95 ++++++++ imagepy/menus/Process/Classify/features.py | 119 ++++++++++ imagepy/menus/Process/Classify/predict_plg.py | 47 ++++ .../Process/Classify/randomforest_plg.py | 72 ------ 10 files changed, 477 insertions(+), 76 deletions(-) create mode 100644 imagepy/menus/Process/Classify/classify_plgs.py create mode 100644 imagepy/menus/Process/Classify/classify_wgt.py create mode 100644 imagepy/menus/Process/Classify/features.py create mode 100644 imagepy/menus/Process/Classify/predict_plg.py delete mode 100644 imagepy/menus/Process/Classify/randomforest_plg.py diff --git a/imagepy/core/util/fileio.py b/imagepy/core/util/fileio.py index 1dd8c418..a57bfe6f 100644 --- a/imagepy/core/util/fileio.py +++ b/imagepy/core/util/fileio.py @@ -81,5 +81,4 @@ def run(self, ips, imgs, para = None): fn, fe = os.path.splitext(fn) write = WriterManager.get(fe[1:], 'img') write2 = write or WriterManager.get(fe[1:], 'imgs') - print(write2, write) write2(para['path'], imgs if write is None else ips.img) \ No newline at end of file diff --git a/imagepy/core/wraper/imageplus.py b/imagepy/core/wraper/imageplus.py index 1a611ab6..7c3dd965 100644 --- a/imagepy/core/wraper/imageplus.py +++ b/imagepy/core/wraper/imageplus.py @@ -77,7 +77,7 @@ def set_imgs(self, imgs): else: self.channels = self.imgs[0].shape[2] self.dtype = self.imgs[0].dtype if self.dtype == np.uint8: self.range = (0, 255) - else: self.range = self.get_updown('all', 'one') + else: self.range = self.get_updown('all', 'one', step=512) if self.dtype == np.uint8: self.chan_range = [(0, 255)] * self.channels else: self.chan_range = self.get_updown('all', 'all', step=512) diff --git a/imagepy/menus/Analysis/statistic_plg.py b/imagepy/menus/Analysis/statistic_plg.py index 36e4af41..f34c6af1 100644 --- a/imagepy/menus/Analysis/statistic_plg.py +++ b/imagepy/menus/Analysis/statistic_plg.py @@ -190,7 +190,7 @@ def run(self, ips, imgs, para = None): data, mark = [], [] pts = np.array(ips.roi.body).round().astype(np.int) for n in range(len(imgs)): - xs, ys = (pts.T*k).round(2) + xs, ys = (pts.T[:2]*k).round(2) vs = imgs[n][ys, xs] cont = ([n]*len(vs), xs, ys, vs.round(2)) if not para['slice']: cont = cont[1:] diff --git a/imagepy/menus/Plugins/update_plg.py b/imagepy/menus/Plugins/update_plg.py index 795f6f43..9f60d452 100644 --- a/imagepy/menus/Plugins/update_plg.py +++ b/imagepy/menus/Plugins/update_plg.py @@ -38,7 +38,7 @@ def deal_file(self): path = osp.dirname(root_dir) #remove for i in os.listdir(root_dir): - if i in ['plugins', 'preference.cfg', '.gitignore']: continue + if i in ['plugins', 'ilastik', 'preference.cfg', '.gitignore']: continue if osp.isdir(osp.join(root_dir,i)): shutil.rmtree(osp.join(root_dir, i)) else : os.remove(osp.join(root_dir,i)) diff --git a/imagepy/menus/Process/Classify/__init__.py b/imagepy/menus/Process/Classify/__init__.py index e69de29b..6ce5e66a 100644 --- a/imagepy/menus/Process/Classify/__init__.py +++ b/imagepy/menus/Process/Classify/__init__.py @@ -0,0 +1 @@ +catlog = ['label_wgt', 'classify_wgt', '-', 'classify_plgs', '-', 'predict_plg'] \ No newline at end of file diff --git a/imagepy/menus/Process/Classify/classify_plgs.py b/imagepy/menus/Process/Classify/classify_plgs.py new file mode 100644 index 00000000..7814d8c0 --- /dev/null +++ b/imagepy/menus/Process/Classify/classify_plgs.py @@ -0,0 +1,212 @@ +from imagepy.core.engine import Filter +from imagepy.core.manager import ImageManager + +import numpy as np +from sklearn.ensemble import RandomForestClassifier, \ + AdaBoostClassifier, BaggingClassifier, ExtraTreesClassifier,\ + GradientBoostingClassifier, VotingClassifier +from sklearn.dummy import DummyClassifier +from .features import get_feature, get_predict + +model_para = None + +class RandomForest(Filter): + """Closing: derived from imagepy.core.engine.Filter """ + title = 'Random Forest Classify' + note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] + para = {'img':None, 'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, + 'eig':True, 'n_estimators':100, 'max_features':'sqrt', 'max_depth':0} + view = [('img', 'img', 'img', 'back'), + ('lab', None, '===== Classifier Parameter ====='), + (int, 'n_estimators', (10,1024), 0, 'estimators', 'n'), + (int, 'max_depth', (0,64), 0, 'depth', 'max'), + (list, 'max_features', ['sqrt', 'log2', 'None'], str, 'features', 'max'), + ('lab', None, '===== Feature Parameter ====='), + (int, 'grade', (1,7), 0, 'grade', ''), + (int, 'w', (0,5), 0, 'sigma', 'tensor'), + (bool, 'ori', 'add image feature'), + (bool, 'blr', 'add blur feature'), + (bool, 'sob', 'add sobel feature'), + (bool, 'eig', 'add eig feature')] + + def run(self, ips, snap, img, para = None): + key = {'chans':None, 'grade':para['grade'], 'w':para['w']} + key['items'] = [i for i in ['ori', 'blr', 'sob', 'eig'] if para[i]] + ori = ImageManager.get(para['img']).img + feat, lab, key = get_feature(ori, snap, key) + feat_dic = {'sqrt':'sqrt', 'log2':'log2', 'None':None} + max_depth = None if para['max_depth']==0 else para['max_depth'] + model = RandomForestClassifier(n_estimators=para['n_estimators'], + max_features = feat_dic[para['max_features']], max_depth=max_depth) + model.fit(feat, lab) + get_predict(ori, model, key, out=img) + global model_para + model_para = model, key + +class AdaBoost(Filter): + """Closing: derived from imagepy.core.engine.Filter """ + title = 'AdaBoost Classify' + note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] + para = {'img':None, 'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, + 'eig':True, 'n_estimators':50, 'learning_rate':1, 'algorithm':'SAMME.R'} + view = [('img', 'img', 'img', 'back'), + ('lab', None, '===== Classifier Parameter ====='), + (int, 'n_estimators', (10,1024), 0, 'estimators', 'n'), + (float, 'learning_rate', (0.1, 10), 1, 'learn', 'rate'), + (list, 'algorithm', ['SAMME', 'SAMME.R'], str, 'algorithm', ''), + ('lab', None, '===== Feature Parameter ====='), + (int, 'grade', (1,7), 0, 'grade', ''), + (int, 'w', (0,5), 0, 'sigma', 'tensor'), + (bool, 'ori', 'add image feature'), + (bool, 'blr', 'add blur feature'), + (bool, 'sob', 'add sobel feature'), + (bool, 'eig', 'add eig feature')] + + def run(self, ips, snap, img, para = None): + key = {'chans':None, 'grade':para['grade'], 'w':para['w']} + key['items'] = [i for i in ['ori', 'blr', 'sob', 'eig'] if para[i]] + ori = ImageManager.get(para['img']).img + feat, lab, key = get_feature(ori, snap, key) + model = AdaBoostClassifier(n_estimators=para['n_estimators'], + learning_rate = para['learning_rate'], algorithm=para['algorithm']) + model.fit(feat, lab) + get_predict(ori, model, key, out=img) + global model_para + model_para = model, key + +class Bagging(Filter): + """Closing: derived from imagepy.core.engine.Filter """ + title = 'Bagging Classify' + note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] + para = {'img':None, 'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, + 'eig':True, 'n_estimators':50, 'max_features':1.0} + view = [('img', 'img', 'img', 'back'), + ('lab', None, '===== Classifier Parameter ====='), + (int, 'n_estimators', (10,1024), 0, 'estimators', 'n'), + (float, 'max_features', (0.2, 1), 1, 'features', 'k'), + ('lab', None, '===== Feature Parameter ====='), + (int, 'grade', (1,7), 0, 'grade', ''), + (int, 'w', (0,5), 0, 'sigma', 'tensor'), + (bool, 'ori', 'add image feature'), + (bool, 'blr', 'add blur feature'), + (bool, 'sob', 'add sobel feature'), + (bool, 'eig', 'add eig feature')] + + def run(self, ips, snap, img, para = None): + key = {'chans':None, 'grade':para['grade'], 'w':para['w']} + key['items'] = [i for i in ['ori', 'blr', 'sob', 'eig'] if para[i]] + ori = ImageManager.get(para['img']).img + feat, lab, key = get_feature(ori, snap, key) + model = BaggingClassifier(n_estimators=para['n_estimators'], + max_features = para['max_features']) + model.fit(feat, lab) + get_predict(ori, model, key, out=img) + global model_para + model_para = model, key + +class ExtraTrees(Filter): + """Closing: derived from imagepy.core.engine.Filter """ + title = 'ExtraTrees Classify' + note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] + para = {'img':None, 'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, + 'eig':True, 'n_estimators':10, 'max_features':'sqrt', 'max_depth':0} + view = [('img', 'img', 'img', 'back'), + ('lab', None, '===== Classifier Parameter ====='), + (int, 'n_estimators', (10,1024), 0, 'estimators', 'n'), + (int, 'max_depth', (0,64), 0, 'depth', 'max'), + (list, 'max_features', ['sqrt', 'log2', 'None'], str, 'features', 'max'), + ('lab', None, '===== Feature Parameter ====='), + (int, 'grade', (1,7), 0, 'grade', ''), + (int, 'w', (0,5), 0, 'sigma', 'tensor'), + (bool, 'ori', 'add image feature'), + (bool, 'blr', 'add blur feature'), + (bool, 'sob', 'add sobel feature'), + (bool, 'eig', 'add eig feature')] + + def run(self, ips, snap, img, para = None): + key = {'chans':None, 'grade':para['grade'], 'w':para['w']} + key['items'] = [i for i in ['ori', 'blr', 'sob', 'eig'] if para[i]] + ori = ImageManager.get(para['img']).img + feat, lab, key = get_feature(ori, snap, key) + feat_dic = {'sqrt':'sqrt', 'log2':'log2', 'None':None} + max_depth = None if para['max_depth']==0 else para['max_depth'] + model = ExtraTreesClassifier(n_estimators=para['n_estimators'], + max_features = feat_dic[para['max_features']], max_depth=max_depth) + model.fit(feat, lab) + get_predict(ori, model, key, out=img) + global model_para + model_para = model, key + +class GradientBoosting(Filter): + """Closing: derived from imagepy.core.engine.Filter """ + title = 'Gradient Boosting Classify' + note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] + para = {'img':None, 'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, + 'eig':True, 'n_estimators':100, 'max_features':'sqrt', + 'max_depth':3, 'learning_rate':0.1, 'loss':'deviance'} + view = [('img', 'img', 'img', 'back'), + ('lab', None, '===== Classifier Parameter ====='), + (list, 'loss', ['deviance', 'exponential'], str, 'loss', ''), + (int, 'n_estimators', (10,1024), 0, 'estimators', 'n'), + (int, 'max_depth', (1,10), 0, 'depth', 'max'), + (float, 'learning_rate', (0.1, 10), 1, 'learn', 'rate'), + (list, 'max_features', ['sqrt', 'log2', 'None'], str, 'features', 'max'), + ('lab', None, '===== Feature Parameter ====='), + (int, 'grade', (1,7), 0, 'grade', ''), + (int, 'w', (0,5), 0, 'sigma', 'tensor'), + (bool, 'ori', 'add image feature'), + (bool, 'blr', 'add blur feature'), + (bool, 'sob', 'add sobel feature'), + (bool, 'eig', 'add eig feature')] + + def run(self, ips, snap, img, para = None): + key = {'chans':None, 'grade':para['grade'], 'w':para['w']} + key['items'] = [i for i in ['ori', 'blr', 'sob', 'eig'] if para[i]] + ori = ImageManager.get(para['img']).img + feat, lab, key = get_feature(ori, snap, key) + feat_dic = {'sqrt':'sqrt', 'log2':'log2', 'None':None} + model = GradientBoostingClassifier(n_estimators=para['n_estimators'], + loss=para['loss'], max_features = feat_dic[para['max_features']], + max_depth=para['max_depth'], learning_rate=para['learning_rate']) + model.fit(feat, lab) + get_predict(ori, model, key, out=img) + global model_para + model_para = model, key + +class Voting(Filter): + """Closing: derived from imagepy.core.engine.Filter """ + title = 'Voting Classify' + note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] + para = {'img':None, 'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, + 'eig':True, 'n_estimators':100, 'max_features':'sqrt', + 'max_depth':3, 'learning_rate':0.1, 'loss':'deviance'} + view = [('img', 'img', 'img', 'back'), + ('lab', None, '===== Classifier Parameter ====='), + (list, 'loss', ['deviance', 'exponential'], str, 'loss', ''), + (int, 'n_estimators', (10,1024), 0, 'estimators', 'n'), + (int, 'max_depth', (1,10), 0, 'depth', 'max'), + (float, 'learning_rate', (0.1, 10), 1, 'learn', 'rate'), + (list, 'max_features', ['sqrt', 'log2', 'None'], str, 'features', 'max'), + ('lab', None, '===== Feature Parameter ====='), + (int, 'grade', (1,7), 0, 'grade', ''), + (int, 'w', (0,5), 0, 'sigma', 'tensor'), + (bool, 'ori', 'add image feature'), + (bool, 'blr', 'add blur feature'), + (bool, 'sob', 'add sobel feature'), + (bool, 'eig', 'add eig feature')] + + def run(self, ips, snap, img, para = None): + key = {'chans':None, 'grade':para['grade'], 'w':para['w']} + key['items'] = [i for i in ['ori', 'blr', 'sob', 'eig'] if para[i]] + ori = ImageManager.get(para['img']).img + feat, lab, key = get_feature(ori, snap, key) + feat_dic = {'sqrt':'sqrt', 'log2':'log2', 'None':None} + model = VotingClassifier(n_estimators=para['n_estimators'], + loss=para['loss'], max_features = feat_dic[para['max_features']], + max_depth=para['max_depth'], learning_rate=para['learning_rate']) + model.fit(feat, lab) + get_predict(ori, model, key, out=img) + global model_para + model_para = model, key + +plgs = [RandomForest, ExtraTrees, Bagging, AdaBoost, GradientBoosting] \ No newline at end of file diff --git a/imagepy/menus/Process/Classify/classify_wgt.py b/imagepy/menus/Process/Classify/classify_wgt.py new file mode 100644 index 00000000..3f76b4ec --- /dev/null +++ b/imagepy/menus/Process/Classify/classify_wgt.py @@ -0,0 +1,95 @@ +import wx, joblib, os, os.path as osp +from glob import glob +from imagepy.core.manager import RoiManager, ImageManager, ReaderManager, ViewerManager +from imagepy.core.engine import Macros +from . import classify_plgs as manager +from .predict_plg import Plugin as FCL +from imagepy import IPy + +ReaderManager.add('fcl', lambda x:x, 'fcl') +ViewerManager.add('fcl', lambda x,n:wx.CallAfter(FCL(path=x).start)) + +class Plugin( wx.Panel ): + title = 'Feature Classify Panel' + + def __init__( self, parent ): + wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size(256, 256), style = wx.TAB_TRAVERSAL ) + + sizer = wx.BoxSizer( wx.HORIZONTAL ) + + sizer_btns = wx.BoxSizer( wx.VERTICAL ) + + self.btn_save = wx.Button( self, wx.ID_ANY, u"Save", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_btns.Add( self.btn_save, 0, wx.ALL, 5 ) + + self.btn_run = wx.Button( self, wx.ID_ANY, u"Run", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_btns.Add( self.btn_run, 0, wx.ALL, 5 ) + + self.btn_rename = wx.Button( self, wx.ID_ANY, u"Rename", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_btns.Add( self.btn_rename, 0, wx.ALL, 5 ) + + self.btn_remove = wx.Button( self, wx.ID_ANY, u"Remove", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_btns.Add( self.btn_remove, 0, wx.ALL, 5 ) + + sizer.Add( sizer_btns, 0, wx.EXPAND, 5 ) + + sizer_list = wx.BoxSizer( wx.VERTICAL ) + + self.lst_model = wx.ListBox( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, ['a', 'b'], 0 ) + self.lst_model.SetFont( wx.Font( 12, 70, 90, 90, False, wx.EmptyString ) ) + sizer_list.Add( self.lst_model, 1, wx.ALL|wx.EXPAND, 5 ) + + sizer.Add( sizer_list, 1, wx.EXPAND, 5 ) + self.models = [] + self.AddEvent() + self.LoadModel() + + self.SetSizer( sizer ) + self.Layout() + + def LoadModel(self): + fs = glob(osp.join(IPy.root_dir, 'data/ilastik/*.fcl')) + self.models = [osp.split(i)[1] for i in fs] + self.lst_model.SetItems(self.models) + + def AddEvent(self): + self.btn_save.Bind(wx.EVT_BUTTON, self.on_save) + self.btn_rename.Bind(wx.EVT_BUTTON, self.on_rename) + self.btn_remove.Bind(wx.EVT_BUTTON, self.on_remove) + self.btn_run.Bind(wx.EVT_BUTTON, self.on_run) + + + def on_save(self, event): + if manager.model_para is None: + return IPy.alert('you must train your model first!') + para = {'name':'New Model'} + if not IPy.get_para('name', [(str, 'name', 'model', 'name')], para): return + if not osp.exists(osp.join(IPy.root_dir, 'data/ilastik')): + os.mkdir(osp.join(IPy.root_dir, 'data/ilastik')) + joblib.dump( manager.model_para, osp.join(IPy.root_dir, 'data/ilastik/%s.fcl'%para['name'])) + self.LoadModel() + + def on_rename(self, event): + idx = self.lst_model.GetSelection() + if idx==-1: return IPy.alert('no model selected!') + para = {'name':'New Model'} + if not IPy.get_para('name', [(str, 'name', 'model', 'name')], para): return + oldname = osp.join(IPy.root_dir, 'data/ilastik/%s'%self.lst_model.GetStringSelection()) + os.rename(oldname, osp.join(IPy.root_dir, 'data/ilastik/%s.fcl'%para['name'])) + self.LoadModel() + + def on_remove(self, event): + idx = self.lst_model.GetSelection() + if idx==-1: return IPy.alert('no model selected!') + os.remove(osp.join(IPy.root_dir, 'data/ilastik/%s'%self.lst_model.GetStringSelection())) + self.LoadModel() + + def on_run(self, event): + idx = self.lst_model.GetSelection() + if idx==-1: return IPy.alert('no model selected!') + name = self.lst_model.GetStringSelection() + Macros('', ["Feature Predictor>{'predictor':'%s', 'slice':True}"%name]).start() + + def __del__( self ): + pass + \ No newline at end of file diff --git a/imagepy/menus/Process/Classify/features.py b/imagepy/menus/Process/Classify/features.py new file mode 100644 index 00000000..fb6a9701 --- /dev/null +++ b/imagepy/menus/Process/Classify/features.py @@ -0,0 +1,119 @@ +import numpy as np +import scipy.ndimage as ndimg +from skimage.filters import sobel +from skimage.feature import structure_tensor, structure_tensor_eigvals + +# chans 是通道,可以是int,list,默认None,代表所有通道 +para = {'chans':None, 'grade':2, 'w':1, 'items':['ori', 'blr', 'sob', 'eig']} + +def get_feature_one(img, msk=None, para=para): + chans, grade, w, items = para['chans'], para['grade'], para['w'], para['items'] + feats, titles = [], [] + img = img.reshape(img.shape[:2]+(-1,)) + if msk is None: msk = np.ones(img.shape[:2], dtype=np.bool) + if chans is None: chans = range(img.shape[2]) + for c in [chans] if isinstance(chans, int) else chans: + if 'ori' in items: + feats.append(img[:,:,c][msk].astype(np.float32)) + titles.append('c%d_ori'%c) + for o in range(grade): + blurimg = ndimg.gaussian_filter(img[:,:,c], 2**o, output=np.float32) + feat_sobel = sobel(blurimg)[msk] if 'sob' in items else None + if 'eig' in items: + Axx, Axy, Ayy = structure_tensor(blurimg, w) + l1, l2 = structure_tensor_eigvals(Axx, Axy, Ayy) + feat_l1, feat_l2 = l1[msk], l2[msk] + else: feat_l1 = feat_l2 = None + feat_gauss = blurimg[msk] if 'blr' in items else None + featcr = [feat_gauss, feat_sobel, feat_l1, feat_l2] + title = ['c%d_s%d_%s'%(c,o,i) for i in ['gauss', 'sobel', 'l1', 'l2']] + titles.extend([title[i] for i in range(4) if not featcr[i] is None]) + feats.extend([featcr[i] for i in range(4) if not featcr[i] is None]) + return np.array(feats).T, titles + +def make_slice(l, size, mar): + xs = list(range(0, l, size))+[l] + ins = np.array((xs[:-1], xs[1:])).T + outs = np.clip(ins + [-mar, mar], 0, l) + return outs, ins + +def grid_slice(H, W, size, mar): + h_out, h_in = make_slice(H, size, mar) + w_out, w_in = make_slice(W, size, mar) + out_slice, in_slice = [], [] + for rs, re in h_out: + for cs, ce in w_out: + out_slice.append((slice(rs, re), slice(cs, ce))) + for rs, re in h_in: + for cs, ce in w_in: + in_slice.append((slice(rs, re), slice(cs, ce))) + return out_slice, in_slice + +def get_feature(img, lab, key=para, size=1024): + out_slice, in_slice = grid_slice(*img.shape[:2], size, 2**(key['grade']-1)*3) + feats, labs = [], [] + msk = np.zeros(img.shape[:2], dtype=np.bool) + for outs, ins in zip(out_slice, in_slice): + msk[outs] = 0; msk[ins] = 1; + msk[outs][lab[outs]==0] = 0 + if msk[outs].sum()==0: continue + feat, title = get_feature_one(img[outs], msk[outs], key) + feats.append(feat) + labs.append(lab[outs][msk[outs]]) + feats = np.vstack(feats) + labs = np.hstack(labs).reshape((-1,1)) + mins, ptps = feat.min(axis=0), feat.ptp(axis=0) + feats -= mins + feats /= ptps + para = key.copy() + para['min'] = mins.tolist() + para['ptp'] = ptps.tolist() + para['titles'] = title + return feats, np.hstack(labs), para + +def get_predict(img, model, key=para, out=None, size=1024): + out_slice, in_slice = grid_slice(*img.shape[:2], size, 2**(key['grade']-1)*3) + feats, labs = [], [] + if out is None: out = np.zeros(img.shape[:2], dtype=np.uint8) + msk = np.zeros(img.shape[:2], dtype=np.bool) + for outs, ins in zip(out_slice, in_slice): + msk[outs] = 0; msk[ins] = 1; + feat, title = get_feature_one(img[outs], msk[outs], key) + feat -= key['min'] + feat /= key['ptp'] + labs = model.predict(feat) + out[ins] = labs.reshape(out[ins].shape) + return out + +def dump_model(model, para, path): + joblib.sump(path, (model, para)) + +def load_model(model, para, path): + return joblib.load(path) + +if __name__ == '__main__': + from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier + #a, b = grid_slice(100, 80, 50, 5) + from skimage.data import camera + from skimage.io import imread + import matplotlib.pyplot as plt + import joblib + img = imread('img.png') + lab = imread('lab.png') + + #a, b = grid_slice(*img.shape, 500, 5) + #feats = get_feature(img, lab>0) + size = 512 + feat, lab, key = get_feature(img, lab, para, size=size) + + model = RandomForestClassifier(n_estimators=100, random_state=42, + max_features = 'sqrt', n_jobs=-1, verbose = 1) + # model = AdaBoostClassifier() + model.fit(feat, lab) + rst = get_predict(img, model, key, size=size) + ''' + para, model = joblib.load('a.clsf') + rst = predict(img, model, para) + ''' + plt.imshow(rst) + plt.show() diff --git a/imagepy/menus/Process/Classify/predict_plg.py b/imagepy/menus/Process/Classify/predict_plg.py new file mode 100644 index 00000000..46178881 --- /dev/null +++ b/imagepy/menus/Process/Classify/predict_plg.py @@ -0,0 +1,47 @@ +from imagepy.core.engine import Simple +from imagepy.core import ImagePlus +from imagepy import IPy +from glob import glob +import os.path as osp +import joblib +from .features import get_feature, get_predict +from imagepy.core.manager import ReaderManager, ViewerManager + +class Plugin(Simple): + title = 'Feature Predictor' + note = ['all'] + + para = {'predictor':None, 'slice':False} + view = [(list, 'predictor', [''], str, 'predictor', ''), + (bool, 'slice', 'slice')] + + def __init__(self, ips=None, path=''): + Simple.__init__(self, ips) + self.model = None + self.root = osp.join(IPy.root_dir, 'data/ilastik') + fs = glob(osp.join(self.root, '*.fcl')) + fs = [osp.split(i)[1] for i in fs] + if path != '': + self.root = '' + fs = [path] + + if len(fs) == 0: return IPy.alert('No feature classfier found!') + self.para['predictor'] = fs[0] + self.view[0] = (list, 'predictor', fs, str, 'predictor', '') + + + def run(self, ips, imgs, para=None): + if '/' in para['predictor']: path = para['predictor'] + else: path = self.root+'/'+para['predictor'] + model, key = joblib.load(path) + lut = {'ori':1, 'blr':key['grade'], 'sob':key['grade'], 'eig':key['grade']*2} + if sum([lut[i] for i in key['items']])*ips.channels != len(key['titles']): + return IPy.alert('image channels dismatch this predictor!') + if not para['slice']: imgs = [ips.img] + rst = [] + for i in range(len(imgs)): + self.progress(i+1, len(imgs)) + rst.append(get_predict(imgs[i], model, key)) + ips = ImagePlus(rst, ips.title+'-mark') + ips.range = ips.get_updown('all', 'one', step=512) + IPy.show_ips(ips) \ No newline at end of file diff --git a/imagepy/menus/Process/Classify/randomforest_plg.py b/imagepy/menus/Process/Classify/randomforest_plg.py deleted file mode 100644 index 117e6c9a..00000000 --- a/imagepy/menus/Process/Classify/randomforest_plg.py +++ /dev/null @@ -1,72 +0,0 @@ -from imagepy.core.engine import Filter -from imagepy.core.manager import ImageManager - -import numpy as np -from sklearn.ensemble import RandomForestClassifier -import scipy.ndimage as ndimg -from skimage.filters import sobel -from skimage.feature import structure_tensor, structure_tensor_eigvals -from skimage.io import imread, imsave -import pandas as pd - -def norm01(arr): - arr -= arr.min() - arr /= arr.max() - return arr - -# chans 是通道,可以是int,list,默认None,代表所有通道 -def get_feature(img, chans=None, ocvs=3, w=1, ori=True, blr=True, sob=True, eig=True): - feats, titles = [], [] - img = img.reshape(img.shape[:2]+(-1,)) - if chans is None: chans = range(img.shape[2]) - for c in [chans] if isinstance(chans, int) else chans: - if ori: - feats.append(norm01(img[:,:,c].ravel().astype(np.float32))) - titles.append('c%d_ori'%c) - for o in range(ocvs): - blurimg = ndimg.gaussian_filter(img[:,:,c], 2**o, output=np.float32) - feat_sobel = norm01(sobel(blurimg).ravel()) if sob else None - if eig: - Axx, Axy, Ayy = structure_tensor(blurimg, w) - l1, l2 = structure_tensor_eigvals(Axx, Axy, Ayy) - feat_l1, feat_l2 = norm01(l1.ravel()), norm01(l2.ravel()) - else: feat_l1 = feat_l2 = None - feat_gauss = norm01(blurimg.ravel()) if blr else None - featcr = [feat_gauss, feat_sobel, feat_l1, feat_l2] - title = ['c%d_s%d_%s'%(c,o,i) for i in ['gauss', 'sobel', 'l1', 'l2']] - titles.extend([title[i] for i in range(4) if not featcr[i] is None]) - feats.extend([featcr[i] for i in range(4) if not featcr[i] is None]) - return np.array(feats).T, titles - -class Plugin(Filter): - """Closing: derived from imagepy.core.engine.Filter """ - title = 'Random Forest Classify' - note = ['8-bit', 'auto_msk', 'auto_snap', 'preview'] - para = {'img':None, 'ocvs':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, 'eig':True} - view = [('img', 'img', 'img', 'back'), - (int, 'ocvs', (1,7), 0, 'ocvs', ''), - (int, 'w', (0,5), 0, 'sigma', 'tensor'), - (bool, 'ori', 'add image feature'), - (bool, 'blr', 'add blur feature'), - (bool, 'sob', 'add sobel feature'), - (bool, 'eig', 'add eig feature')] - - def run(self, ips, snap, img, para = None): - ori = ImageManager.get(para['img']).img - lab = snap.ravel() - msk = lab != 0 - - lab_value = lab[msk].reshape(-1, 1) - feats, titles = get_feature(ori, None, para['ocvs'], - para['w'], para['ori'], para['blr'], para['sob'], para['eig']) - lab_feats = feats[msk] - - print(titles) - - model = RandomForestClassifier(n_estimators=100, random_state=42, - max_features = 'sqrt', n_jobs=-1, verbose = 1) - - model.fit(lab_feats, lab_value) - rst = model.predict(feats) - img[:] = rst.reshape(img.shape[:2]) - #imsave('rst.png', rstimg) \ No newline at end of file From 7861cb53e25a4d377f3baf9be6b2911cad993778 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 10 Oct 2019 22:16:23 +0800 Subject: [PATCH 163/343] nothing --- imagepy/ui/canvas/imutil.py | 2 ++ imagepy/widgets/histogram/channels_wgt.py | 23 +++++++++++++---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/imagepy/ui/canvas/imutil.py b/imagepy/ui/canvas/imutil.py index 8eca0e50..2da877a0 100644 --- a/imagepy/ui/canvas/imutil.py +++ b/imagepy/ui/canvas/imutil.py @@ -125,12 +125,14 @@ def lookup_mix(img, lut, out, mode): for c in range(img.shape[1]): for i in (0,1,2): out[r,c,i] = lut[img[r,c],i]*mode + out[r,c,i]*(1-mode) + def lookup_jit(img, lut, out, mode): if mode == 'set': lookup_set(img, lut, out, mode) if mode == 'msk': lookup_msk(img, lut, out, mode) if mode == 'max': lookup_max(img, lut, out, mode) if mode == 'min': lookup_min(img, lut, out, mode) if isinstance(mode, float): lookup_mix(img, lut, out, mode) + lookup = lookup_jit # mode: set, min, max, mix, nor diff --git a/imagepy/widgets/histogram/channels_wgt.py b/imagepy/widgets/histogram/channels_wgt.py index 1d44024e..f1ab52a5 100644 --- a/imagepy/widgets/histogram/channels_wgt.py +++ b/imagepy/widgets/histogram/channels_wgt.py @@ -16,39 +16,41 @@ def __init__( self, parent ): self.btn_r = wx.Button( self, wx.ID_ANY, u" ", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) self.btn_r.SetBackgroundColour( wx.Colour( 255, 0, 0 ) ) self.btn_r.SetMaxSize( wx.Size( -1,40 ) ) - sizer_chans.Add( self.btn_r, 0, wx.ALL, 0) + sizer_chans.Add( self.btn_r, 0, wx.ALL|wx.CENTER, 0) com_rChoices = [ u"C:0" ] self.com_r = wx.ComboBox( self, wx.ID_ANY, u"Combo!", wx.DefaultPosition, wx.DefaultSize, com_rChoices, wx.CB_READONLY ) self.com_r.SetSelection( 0 ) + self.com_r.SetInitialSize((50,-1)) sizer_chans.Add( self.com_r, 1, wx.ALL|wx.EXPAND, 1) self.btn_g = wx.Button( self, wx.ID_ANY, u" ", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) self.btn_g.SetBackgroundColour( wx.Colour( 0, 255, 0 ) ) self.btn_g.SetMaxSize( wx.Size( -1,40 ) ) - sizer_chans.Add( self.btn_g, 0, wx.ALL, 0) + sizer_chans.Add( self.btn_g, 0, wx.ALL|wx.CENTER, 0) com_gChoices = [ u"C:1" ] self.com_g = wx.ComboBox( self, wx.ID_ANY, u"Combo!", wx.DefaultPosition, wx.DefaultSize, com_gChoices, wx.CB_READONLY ) self.com_g.SetSelection( 0 ) - + self.com_g.SetInitialSize((50,-1)) sizer_chans.Add( self.com_g, 1, wx.ALL|wx.EXPAND, 1) self.btn_b = wx.Button( self, wx.ID_ANY, u" ", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) self.btn_b.SetBackgroundColour( wx.Colour( 0, 0, 255 ) ) self.btn_b.SetMaxSize( wx.Size( -1,40 ) ) - sizer_chans.Add( self.btn_b, 0, wx.ALL, 0) + sizer_chans.Add( self.btn_b, 0, wx.ALL|wx.CENTER, 0) com_bChoices = [ u"C:2" ] - self.com_b = wx.ComboBox( self, wx.ID_ANY, u"Combo!", wx.DefaultPosition, wx.DefaultSize, com_bChoices, wx.CB_READONLY ) + self.com_b = wx.ComboBox( self, wx.ID_ANY, u"Combo!", wx.DefaultPosition, (-1,-1), com_bChoices, wx.CB_READONLY ) self.com_b.SetSelection( 0 ) + self.com_b.SetInitialSize((50,-1)) sizer_chans.Add( self.com_b, 1, wx.ALL|wx.EXPAND, 1 ) self.btn_gray = wx.Button( self, wx.ID_ANY, u" ", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) self.btn_gray.SetBackgroundColour( wx.Colour( 128, 128, 128 ) ) self.btn_gray.SetMaxSize( wx.Size( -1,40 ) ) - sizer_chans.Add( self.btn_gray, 0, wx.ALL, 0) + sizer_chans.Add( self.btn_gray, 0, wx.ALL|wx.CENTER, 0) bSizer1.Add(sizer_chans, 0, wx.ALL|wx.EXPAND, 2) @@ -111,9 +113,9 @@ def __init__( self, parent ): #self.btn_back = wx.Button( self, wx.ID_ANY, 'Bg', wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) #self.btn_back.SetMaxSize( wx.Size( -1,40 ) ) - self.btn_back = wx.StaticText( self, wx.ID_ANY, u" BG ", wx.DefaultPosition, wx.DefaultSize, 0|wx.SIMPLE_BORDER ) - self.btn_back.Wrap( -1 ) - sizer_mode.Add( self.btn_back, 0, wx.ALL|wx.EXPAND, 3) + #self.btn_back = wx.StaticText( self, wx.ID_ANY, u" BG ", wx.DefaultPosition, wx.DefaultSize, 0|wx.SIMPLE_BORDER ) + #self.btn_back.Wrap( -1 ) + #sizer_mode.Add( self.btn_back, 0, wx.ALL|wx.EXPAND, 3) com_backChoices = [ u"No Background" ] self.com_back = wx.ComboBox( self, wx.ID_ANY, u"No Background", wx.DefaultPosition, wx.DefaultSize, com_backChoices, wx.CB_READONLY) @@ -141,7 +143,7 @@ def __init__( self, parent ): self.btn_g.Bind( wx.EVT_BUTTON, lambda e: self.on_rgb(e, 'g') ) self.btn_b.Bind( wx.EVT_BUTTON, lambda e: self.on_rgb(e, 'b') ) self.btn_gray.Bind( wx.EVT_BUTTON, self.on_gray) - self.btn_back.Bind( wx.EVT_LEFT_DOWN, self.on_back) + # self.btn_back.Bind( wx.EVT_LEFT_DOWN, self.on_back) self.com_r.Bind( wx.EVT_COMBOBOX, lambda e: self.on_chan(e, 'r')) self.com_g.Bind( wx.EVT_COMBOBOX, lambda e: self.on_chan(e, 'g')) self.com_b.Bind( wx.EVT_COMBOBOX, lambda e: self.on_chan(e, 'b')) @@ -152,6 +154,7 @@ def __init__( self, parent ): self.btn_90.Bind( wx.EVT_BUTTON, lambda e: self.on_p(e, 0.9)) self.btn_95.Bind( wx.EVT_BUTTON, lambda e: self.on_p(e, 0.95)) self.btn_99.Bind( wx.EVT_BUTTON, lambda e: self.on_p(e, 0.99)) + self.com_back.Bind(wx.EVT_COMBOBOX_DROPDOWN, self.on_back) self.active = 0 def on_back(self, event): From 4bfb659afae3cd1c2445faa04a9eb0cbc8e290a0 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 11 Oct 2019 12:39:25 +0800 Subject: [PATCH 164/343] classify --- .../menus/Process/Classify/classify_wgt.py | 32 +++++++++++++++++-- imagepy/menus/Process/Classify/predict_plg.py | 3 +- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/imagepy/menus/Process/Classify/classify_wgt.py b/imagepy/menus/Process/Classify/classify_wgt.py index 3f76b4ec..b0a7bf21 100644 --- a/imagepy/menus/Process/Classify/classify_wgt.py +++ b/imagepy/menus/Process/Classify/classify_wgt.py @@ -1,4 +1,4 @@ -import wx, joblib, os, os.path as osp +import wx, joblib, os, shutil, os.path as osp from glob import glob from imagepy.core.manager import RoiManager, ImageManager, ReaderManager, ViewerManager from imagepy.core.engine import Macros @@ -21,6 +21,12 @@ def __init__( self, parent ): self.btn_save = wx.Button( self, wx.ID_ANY, u"Save", wx.DefaultPosition, wx.DefaultSize, 0 ) sizer_btns.Add( self.btn_save, 0, wx.ALL, 5 ) + + self.btn_saveas = wx.Button( self, wx.ID_ANY, u"Save As", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_btns.Add( self.btn_saveas, 0, wx.ALL, 5 ) + + self.btn_export = wx.Button( self, wx.ID_ANY, u"Export", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_btns.Add( self.btn_export, 0, wx.ALL, 5 ) self.btn_run = wx.Button( self, wx.ID_ANY, u"Run", wx.DefaultPosition, wx.DefaultSize, 0 ) sizer_btns.Add( self.btn_run, 0, wx.ALL, 5 ) @@ -54,6 +60,8 @@ def LoadModel(self): def AddEvent(self): self.btn_save.Bind(wx.EVT_BUTTON, self.on_save) + self.btn_saveas.Bind(wx.EVT_BUTTON, self.on_saveas) + self.btn_export.Bind(wx.EVT_BUTTON, self.on_export) self.btn_rename.Bind(wx.EVT_BUTTON, self.on_rename) self.btn_remove.Bind(wx.EVT_BUTTON, self.on_remove) self.btn_run.Bind(wx.EVT_BUTTON, self.on_run) @@ -69,6 +77,25 @@ def on_save(self, event): joblib.dump( manager.model_para, osp.join(IPy.root_dir, 'data/ilastik/%s.fcl'%para['name'])) self.LoadModel() + def on_saveas(self, event): + if manager.model_para is None: + return IPy.alert('you must train your model first!') + para = {'path':''} + filt = '|'.join(['%s files (*.%s)|*.%s'%('FCL', 'fcl', 'fcl')]) + if not IPy.getpath('Save..', filt, 'save', para): return + joblib.dump( manager.model_para, para['path']) + + def on_export(self, event): + idx = self.lst_model.GetSelection() + if idx==-1: return IPy.alert('no model selected!') + para = {'path':''} + filt = '|'.join(['%s files (*.%s)|*.%s'%('FCL', 'fcl', 'fcl')]) + if not IPy.getpath('Save..', filt, 'save', para): return + oldname = osp.join(IPy.root_dir, 'data/ilastik/%s'%self.lst_model.GetStringSelection()) + print(para['path']) + shutil.copyfile(oldname, para['path']) + self.LoadModel() + def on_rename(self, event): idx = self.lst_model.GetSelection() if idx==-1: return IPy.alert('no model selected!') @@ -87,8 +114,7 @@ def on_remove(self, event): def on_run(self, event): idx = self.lst_model.GetSelection() if idx==-1: return IPy.alert('no model selected!') - name = self.lst_model.GetStringSelection() - Macros('', ["Feature Predictor>{'predictor':'%s', 'slice':True}"%name]).start() + FCL(path=self.lst_model.GetStringSelection()).start() def __del__( self ): pass diff --git a/imagepy/menus/Process/Classify/predict_plg.py b/imagepy/menus/Process/Classify/predict_plg.py index 46178881..26cacfea 100644 --- a/imagepy/menus/Process/Classify/predict_plg.py +++ b/imagepy/menus/Process/Classify/predict_plg.py @@ -21,8 +21,7 @@ def __init__(self, ips=None, path=''): self.root = osp.join(IPy.root_dir, 'data/ilastik') fs = glob(osp.join(self.root, '*.fcl')) fs = [osp.split(i)[1] for i in fs] - if path != '': - self.root = '' + if '/' in path: fs = [path] if len(fs) == 0: return IPy.alert('No feature classfier found!') From 1af0ec454fa045165cfab677112f9c7a6acb6b57 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 22 Oct 2019 10:44:10 +0800 Subject: [PATCH 165/343] feature ipyalg --- imagepy/core/wraper/imageplus.py | 7 +- imagepy/ipyalg/__init__.py | 3 +- imagepy/ipyalg/classify/__init__.py | 0 .../classify/feature.py} | 69 +++++---- imagepy/menus/Image/crop_plg.py | 4 +- imagepy/menus/Image/duplicate_plg.py | 8 +- .../menus/Process/Classify/classify_plgs.py | 136 +++++++++--------- .../menus/Process/Classify/classify_wgt.py | 1 - imagepy/menus/Process/Classify/imgs/01.gif | Bin 0 -> 57 bytes imagepy/menus/Process/Classify/imgs/03.gif | Bin 0 -> 63 bytes imagepy/menus/Process/Classify/imgs/05.gif | Bin 0 -> 66 bytes imagepy/menus/Process/Classify/imgs/10.gif | Bin 0 -> 70 bytes .../menus/Process/Classify/imgs/__init__.py | 0 imagepy/menus/Process/Classify/imgs/fill.gif | Bin 0 -> 84 bytes imagepy/menus/Process/Classify/io_plgs.py | 30 ++++ imagepy/menus/Process/Classify/label_wgt.py | 63 ++++++-- imagepy/menus/Process/Classify/predict_plg.py | 18 +-- imagepy/ui/canvasframe.py | 13 +- imagepy/ui/mainframe.py | 2 +- imagepy/widgets/histogram/channels_wgt.py | 3 +- 20 files changed, 224 insertions(+), 133 deletions(-) create mode 100644 imagepy/ipyalg/classify/__init__.py rename imagepy/{menus/Process/Classify/features.py => ipyalg/classify/feature.py} (61%) create mode 100644 imagepy/menus/Process/Classify/imgs/01.gif create mode 100644 imagepy/menus/Process/Classify/imgs/03.gif create mode 100644 imagepy/menus/Process/Classify/imgs/05.gif create mode 100644 imagepy/menus/Process/Classify/imgs/10.gif create mode 100644 imagepy/menus/Process/Classify/imgs/__init__.py create mode 100644 imagepy/menus/Process/Classify/imgs/fill.gif create mode 100644 imagepy/menus/Process/Classify/io_plgs.py diff --git a/imagepy/core/wraper/imageplus.py b/imagepy/core/wraper/imageplus.py index 7c3dd965..ffd236f1 100644 --- a/imagepy/core/wraper/imageplus.py +++ b/imagepy/core/wraper/imageplus.py @@ -43,18 +43,19 @@ def __init__(self, imgs, title=None, is3d=False): self.set_title(title) self.snap = None self.cur = self.chan = 0 - self.dirty = False + self.dirty = True self.scrchanged = False self.roi = None self.mark = None self.msk = None self.mskmode = None self.lut = ColorManager.get_lut('grays') - self.backimg = None - self.backmode = (0.5, 'Mean') + self.tool = None self.data = {} self.unit = (1, 'pix') + + self.back = None self.chan_range = [] self.chan_mode = 'min' self.set_imgs(imgs) diff --git a/imagepy/ipyalg/__init__.py b/imagepy/ipyalg/__init__.py index b312dad9..120831fb 100644 --- a/imagepy/ipyalg/__init__.py +++ b/imagepy/ipyalg/__init__.py @@ -2,4 +2,5 @@ from .hydrology.ridge import ridge from .hydrology.isoline import stair, isoline from .hydrology.watershed import watershed -from .hydrology.edt import distance_transform_edt \ No newline at end of file +from .hydrology.edt import distance_transform_edt +from .classify import feature \ No newline at end of file diff --git a/imagepy/ipyalg/classify/__init__.py b/imagepy/ipyalg/classify/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/menus/Process/Classify/features.py b/imagepy/ipyalg/classify/feature.py similarity index 61% rename from imagepy/menus/Process/Classify/features.py rename to imagepy/ipyalg/classify/feature.py index fb6a9701..d896fc16 100644 --- a/imagepy/menus/Process/Classify/features.py +++ b/imagepy/ipyalg/classify/feature.py @@ -49,19 +49,24 @@ def grid_slice(H, W, size, mar): in_slice.append((slice(rs, re), slice(cs, ce))) return out_slice, in_slice -def get_feature(img, lab, key=para, size=1024): - out_slice, in_slice = grid_slice(*img.shape[:2], size, 2**(key['grade']-1)*3) - feats, labs = [], [] - msk = np.zeros(img.shape[:2], dtype=np.bool) - for outs, ins in zip(out_slice, in_slice): - msk[outs] = 0; msk[ins] = 1; - msk[outs][lab[outs]==0] = 0 - if msk[outs].sum()==0: continue - feat, title = get_feature_one(img[outs], msk[outs], key) - feats.append(feat) - labs.append(lab[outs][msk[outs]]) +def get_feature(imgs, labs, key=para, size=1024, callback=print): + if not isinstance(labs, list) and labs.ndim==2: + imgs, labs = [imgs], [labs] + out_slice, in_slice = grid_slice(*imgs[0].shape[:2], size, 2**(key['grade']-1)*3) + m, n = len(out_slice), len(labs) + feats, vs = [], [] + msk = np.zeros(imgs[0].shape[:2], dtype=np.bool) + for i, img, lab in zip(range(n), imgs, labs): + for j, outs, ins in zip(range(m), out_slice, in_slice): + callback(i*m+j, m*n) + msk[outs] = 0; msk[ins] = 1; + msk[outs][lab[outs]==0] = 0 + if msk[outs].sum()==0: continue + feat, title = get_feature_one(img[outs], msk[outs], key) + feats.append(feat) + vs.append(lab[outs][msk[outs]]) feats = np.vstack(feats) - labs = np.hstack(labs).reshape((-1,1)) + vs = np.hstack(vs).reshape((-1,1)) mins, ptps = feat.min(axis=0), feat.ptp(axis=0) feats -= mins feats /= ptps @@ -69,20 +74,34 @@ def get_feature(img, lab, key=para, size=1024): para['min'] = mins.tolist() para['ptp'] = ptps.tolist() para['titles'] = title - return feats, np.hstack(labs), para + return feats, vs, para -def get_predict(img, model, key=para, out=None, size=1024): - out_slice, in_slice = grid_slice(*img.shape[:2], size, 2**(key['grade']-1)*3) - feats, labs = [], [] - if out is None: out = np.zeros(img.shape[:2], dtype=np.uint8) - msk = np.zeros(img.shape[:2], dtype=np.bool) - for outs, ins in zip(out_slice, in_slice): - msk[outs] = 0; msk[ins] = 1; - feat, title = get_feature_one(img[outs], msk[outs], key) - feat -= key['min'] - feat /= key['ptp'] - labs = model.predict(feat) - out[ins] = labs.reshape(out[ins].shape) +def get_predict(imgs, model, key=para, out=None, size=1024, callback=print): + lut = {'ori':1, 'blr':key['grade'], 'sob':key['grade'], 'eig':key['grade']*2} + chans = len(key['titles'])/sum([lut[i] for i in key['items']]) + print(chans) + islist = isinstance(imgs, list) + if islist and imgs[0].ndim == 2 and chans == 1: pass + elif islist and imgs[0].shape[2] == chans: pass + elif not islist and imgs.shape[2] == chans and imgs.ndim == 3: imgs = [imgs] + elif not islist and imgs.ndim == 4 and imgs.shape[3] == chans: pass + else: return None + out_slice, in_slice = grid_slice(*imgs[0].shape[:2], size, 2**(key['grade']-1)*3) + m, n = len(out_slice), len(imgs) + if out is None: + temp = imgs[0] if imgs[0].ndim==2 else imgs[0][:,:,0] + out = [temp.astype(np.uint8)]; out[0] *= 0; + for i in range(1, len(imgs)): out.append(out[0].copy()) + msk = np.zeros(imgs[0].shape[:2], dtype=np.bool) + for i, img, ot in zip(range(len(imgs)), imgs, out): + for j, outs, ins in zip(range(len(out_slice)), out_slice, in_slice): + callback(i*m+j, m*n) + msk[outs] = 0; msk[ins] = 1; + feat, title = get_feature_one(img[outs], msk[outs], key) + feat -= key['min'] + feat /= key['ptp'] + labs = model.predict(feat) + ot[ins] = labs.reshape(ot[ins].shape) return out def dump_model(model, para, path): diff --git a/imagepy/menus/Image/crop_plg.py b/imagepy/menus/Image/crop_plg.py index 9d2e4cb4..ae14ca07 100644 --- a/imagepy/menus/Image/crop_plg.py +++ b/imagepy/menus/Image/crop_plg.py @@ -18,6 +18,6 @@ def run(self, ips, imgs, para = None): else: imgs = [i[sc,sr].copy() for i in imgs] ips.set_imgs(imgs) - if not ips.backimg is None: - ips.backimg = ips.backimg[sc, sr] + #if not ips.backimg is None: + # ips.backimg = ips.backimg[sc, sr] ips.roi = ips.roi.affine(np.eye(2), (-sr.start, -sc.start)) \ No newline at end of file diff --git a/imagepy/menus/Image/duplicate_plg.py b/imagepy/menus/Image/duplicate_plg.py index b3bd8034..bff7b50d 100644 --- a/imagepy/menus/Image/duplicate_plg.py +++ b/imagepy/menus/Image/duplicate_plg.py @@ -30,15 +30,17 @@ def run(self, ips, imgs, para = None): if ips.roi == None: img = ips.img.copy() ipsd = ImagePlus([img], name) - ipsd.backimg = ips.backimg + ipsd.back = ips.back else: img = ips.get_subimg().copy() ipsd = ImagePlus([img], name) box = ips.roi.get_box() ipsd.roi = ips.roi.affine(np.eye(2), (-box[0], -box[1])) + ''' if not ips.backimg is None: sr, sc = ips.get_rect() ipsd.backimg = ips.backimg[sr, sc] + ''' elif ips.get_nslices()>1 and self.para['stack']: if ips.roi == None: if ips.is3d:imgs=imgs.copy() @@ -49,12 +51,12 @@ def run(self, ips, imgs, para = None): if ips.is3d: imgs=imgs[:, sc, sr].copy() else: imgs = [i[sc,sr].copy() for i in imgs] if not ips.backimg is None: - backimg = ips.backimg[sr, sr] + backimg = None #ips.backimg[sr, sr] ipsd = ImagePlus(imgs, name) if ips.roi != None: ipsd.roi = ips.roi.affine(np.eye(2), (-sr.start, -sc.start)) if not ips.backimg is None: ipsd.backimg = backimg - ipsd.backmode = ips.backmode + ipsd.chan_mode = ips.chan_mode IPy.show_ips(ipsd) class Rename(Simple): diff --git a/imagepy/menus/Process/Classify/classify_plgs.py b/imagepy/menus/Process/Classify/classify_plgs.py index 7814d8c0..d3cd08a5 100644 --- a/imagepy/menus/Process/Classify/classify_plgs.py +++ b/imagepy/menus/Process/Classify/classify_plgs.py @@ -1,17 +1,65 @@ -from imagepy.core.engine import Filter +from imagepy.core.engine import Filter, Simple from imagepy.core.manager import ImageManager +from imagepy.core import ImagePlus import numpy as np from sklearn.ensemble import RandomForestClassifier, \ AdaBoostClassifier, BaggingClassifier, ExtraTreesClassifier,\ GradientBoostingClassifier, VotingClassifier from sklearn.dummy import DummyClassifier -from .features import get_feature, get_predict +from imagepy.ipyalg import feature +from imagepy import IPy model_para = None -class RandomForest(Filter): +class Base(Simple): """Closing: derived from imagepy.core.engine.Filter """ + def load(self, ips): + if len(ips.imgs)==1: ips.snapshot() + return True + + def preview(self, ips, para): + if len(ips.imgs)==1: + ips.img[:] = ips.snap + self.run(ips, ips.imgs, para, True) + return True + + def cancel(self, ips): + if len(ips.imgs)==1: ips.swap() + + def classify(self, para): pass + + def run(self, ips, imgs, para = None, preview=False): + if len(ips.imgs)==1: ips.img[:] = ips.snap + key = {'chans':None, 'grade':para['grade'], 'w':para['w']} + key['items'] = [i for i in ['ori', 'blr', 'sob', 'eig'] if para[i]] + slir, slic = ips.get_rect() + labs = [i[slir, slic] for i in imgs] + ori = ImageManager.get(para['img']).imgs + if len(imgs)==1: ori = [ImageManager.get(para['img']).img] + oris = [i[slir, slic] for i in ori] + + IPy.set_info('extract features...') + feat, lab, key = feature.get_feature(oris, labs, key, callback=self.progress) + + IPy.set_info('training data...') + self.progress(None, 1) + model = self.classify(para) + model.fit(feat, lab) + + IPy.set_info('predict data...') + if preview: + return feature.get_predict(oris, model, key, imgs, callback=self.progress) + if len(imgs) == 1: ips.swap() + outs = feature.get_predict(oris, model, key, callback=self.progress) + nips = ImagePlus(outs, ips.title+'rst') + nips.range, nips.lut = ips.range, ips.lut + nips.back, nips.chan_mode = ips.back, 0.4 + IPy.show_ips(nips) + global model_para + model_para = model, key + +class RandomForest(Base): title = 'Random Forest Classify' note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] para = {'img':None, 'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, @@ -29,21 +77,13 @@ class RandomForest(Filter): (bool, 'sob', 'add sobel feature'), (bool, 'eig', 'add eig feature')] - def run(self, ips, snap, img, para = None): - key = {'chans':None, 'grade':para['grade'], 'w':para['w']} - key['items'] = [i for i in ['ori', 'blr', 'sob', 'eig'] if para[i]] - ori = ImageManager.get(para['img']).img - feat, lab, key = get_feature(ori, snap, key) + def classify(self, para): feat_dic = {'sqrt':'sqrt', 'log2':'log2', 'None':None} max_depth = None if para['max_depth']==0 else para['max_depth'] - model = RandomForestClassifier(n_estimators=para['n_estimators'], + return RandomForestClassifier(n_estimators=para['n_estimators'], max_features = feat_dic[para['max_features']], max_depth=max_depth) - model.fit(feat, lab) - get_predict(ori, model, key, out=img) - global model_para - model_para = model, key -class AdaBoost(Filter): +class AdaBoost(Base): """Closing: derived from imagepy.core.engine.Filter """ title = 'AdaBoost Classify' note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] @@ -62,19 +102,11 @@ class AdaBoost(Filter): (bool, 'sob', 'add sobel feature'), (bool, 'eig', 'add eig feature')] - def run(self, ips, snap, img, para = None): - key = {'chans':None, 'grade':para['grade'], 'w':para['w']} - key['items'] = [i for i in ['ori', 'blr', 'sob', 'eig'] if para[i]] - ori = ImageManager.get(para['img']).img - feat, lab, key = get_feature(ori, snap, key) - model = AdaBoostClassifier(n_estimators=para['n_estimators'], + def classify(self, para): + return AdaBoostClassifier(n_estimators=para['n_estimators'], learning_rate = para['learning_rate'], algorithm=para['algorithm']) - model.fit(feat, lab) - get_predict(ori, model, key, out=img) - global model_para - model_para = model, key -class Bagging(Filter): +class Bagging(Base): """Closing: derived from imagepy.core.engine.Filter """ title = 'Bagging Classify' note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] @@ -92,19 +124,11 @@ class Bagging(Filter): (bool, 'sob', 'add sobel feature'), (bool, 'eig', 'add eig feature')] - def run(self, ips, snap, img, para = None): - key = {'chans':None, 'grade':para['grade'], 'w':para['w']} - key['items'] = [i for i in ['ori', 'blr', 'sob', 'eig'] if para[i]] - ori = ImageManager.get(para['img']).img - feat, lab, key = get_feature(ori, snap, key) - model = BaggingClassifier(n_estimators=para['n_estimators'], + def classify(self, para): + return BaggingClassifier(n_estimators=para['n_estimators'], max_features = para['max_features']) - model.fit(feat, lab) - get_predict(ori, model, key, out=img) - global model_para - model_para = model, key -class ExtraTrees(Filter): +class ExtraTrees(Base): """Closing: derived from imagepy.core.engine.Filter """ title = 'ExtraTrees Classify' note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] @@ -123,21 +147,13 @@ class ExtraTrees(Filter): (bool, 'sob', 'add sobel feature'), (bool, 'eig', 'add eig feature')] - def run(self, ips, snap, img, para = None): - key = {'chans':None, 'grade':para['grade'], 'w':para['w']} - key['items'] = [i for i in ['ori', 'blr', 'sob', 'eig'] if para[i]] - ori = ImageManager.get(para['img']).img - feat, lab, key = get_feature(ori, snap, key) + def classify(self, para): feat_dic = {'sqrt':'sqrt', 'log2':'log2', 'None':None} max_depth = None if para['max_depth']==0 else para['max_depth'] - model = ExtraTreesClassifier(n_estimators=para['n_estimators'], + return ExtraTreesClassifier(n_estimators=para['n_estimators'], max_features = feat_dic[para['max_features']], max_depth=max_depth) - model.fit(feat, lab) - get_predict(ori, model, key, out=img) - global model_para - model_para = model, key -class GradientBoosting(Filter): +class GradientBoosting(Base): """Closing: derived from imagepy.core.engine.Filter """ title = 'Gradient Boosting Classify' note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] @@ -159,21 +175,13 @@ class GradientBoosting(Filter): (bool, 'sob', 'add sobel feature'), (bool, 'eig', 'add eig feature')] - def run(self, ips, snap, img, para = None): - key = {'chans':None, 'grade':para['grade'], 'w':para['w']} - key['items'] = [i for i in ['ori', 'blr', 'sob', 'eig'] if para[i]] - ori = ImageManager.get(para['img']).img - feat, lab, key = get_feature(ori, snap, key) + def classify(self, para): feat_dic = {'sqrt':'sqrt', 'log2':'log2', 'None':None} - model = GradientBoostingClassifier(n_estimators=para['n_estimators'], + return GradientBoostingClassifier(n_estimators=para['n_estimators'], loss=para['loss'], max_features = feat_dic[para['max_features']], max_depth=para['max_depth'], learning_rate=para['learning_rate']) - model.fit(feat, lab) - get_predict(ori, model, key, out=img) - global model_para - model_para = model, key -class Voting(Filter): +class Voting(Base): """Closing: derived from imagepy.core.engine.Filter """ title = 'Voting Classify' note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] @@ -195,18 +203,10 @@ class Voting(Filter): (bool, 'sob', 'add sobel feature'), (bool, 'eig', 'add eig feature')] - def run(self, ips, snap, img, para = None): - key = {'chans':None, 'grade':para['grade'], 'w':para['w']} - key['items'] = [i for i in ['ori', 'blr', 'sob', 'eig'] if para[i]] - ori = ImageManager.get(para['img']).img - feat, lab, key = get_feature(ori, snap, key) + def classify(self, para): feat_dic = {'sqrt':'sqrt', 'log2':'log2', 'None':None} - model = VotingClassifier(n_estimators=para['n_estimators'], + return VotingClassifier(n_estimators=para['n_estimators'], loss=para['loss'], max_features = feat_dic[para['max_features']], max_depth=para['max_depth'], learning_rate=para['learning_rate']) - model.fit(feat, lab) - get_predict(ori, model, key, out=img) - global model_para - model_para = model, key plgs = [RandomForest, ExtraTrees, Bagging, AdaBoost, GradientBoosting] \ No newline at end of file diff --git a/imagepy/menus/Process/Classify/classify_wgt.py b/imagepy/menus/Process/Classify/classify_wgt.py index b0a7bf21..bb3c087b 100644 --- a/imagepy/menus/Process/Classify/classify_wgt.py +++ b/imagepy/menus/Process/Classify/classify_wgt.py @@ -1,7 +1,6 @@ import wx, joblib, os, shutil, os.path as osp from glob import glob from imagepy.core.manager import RoiManager, ImageManager, ReaderManager, ViewerManager -from imagepy.core.engine import Macros from . import classify_plgs as manager from .predict_plg import Plugin as FCL from imagepy import IPy diff --git a/imagepy/menus/Process/Classify/imgs/01.gif b/imagepy/menus/Process/Classify/imgs/01.gif new file mode 100644 index 0000000000000000000000000000000000000000..f4f91159e99a98812e0dec923de695d9c5b13470 GIT binary patch literal 57 zcmZ?wbhEHb6krfwXkY+=|Ns9h{$ycfU|?j>0r5dH3`_z&{VPwu0r5dH3`}A@{VPwu<(FnCJeR)qbb0m- M*GC@H{1_Rm0p%eP*8l(j literal 0 HcmV?d00001 diff --git a/imagepy/menus/Process/Classify/imgs/05.gif b/imagepy/menus/Process/Classify/imgs/05.gif new file mode 100644 index 0000000000000000000000000000000000000000..cdb2a70ae6d7ee1a70c3c18c7204b8c3508b07fb GIT binary patch literal 66 zcmZ?wbhEHb6krfwXkY+=|Ns9h{$ycfU|?j>0r5dH3`~+e{VPwu?QdYoKbNL^y1Z~_ PZA4h0r5dH3{0{;yep3{+%GZbK|q&Y$*$l0 UZHtcA&aBMxy&fSQ%)np`0D6}d8vp0r5dH3{2WR`3oB6&bgDQ<>kSgBmKU; iB$3sRKho`VdFIR|NgJM>>Qis~<@Z_Gs#%nQ!5RRt*BU|q literal 0 HcmV?d00001 diff --git a/imagepy/menus/Process/Classify/io_plgs.py b/imagepy/menus/Process/Classify/io_plgs.py new file mode 100644 index 00000000..5ea309db --- /dev/null +++ b/imagepy/menus/Process/Classify/io_plgs.py @@ -0,0 +1,30 @@ +from imagepy.core.engine import Filter, Simple +from imagepy.core.manager import ColorManager +from imagepy.core import ImagePlus +from imagepy import IPy +import numpy as np + +class BuildMark(Simple): + """Closing: derived from imagepy.core.engine.Filter """ + title = 'Build Mark Image' + note = ['all'] + para = {'mode':'Mask', 'cm':'16_Colors', 'n':2, 'slice':True} + view = [(int, 'n', (0, 15), 0, 'n', 'colors'), + ('cmap', 'cm', 'colormap'), + (list, 'mode', ['None', 'Max', 'Min', 'Mask', '2-8mix', \ + '4-6mix', '5-5mix', '6-4mix', '8-2mix'], str, 'mode', 'channel'), + (bool, 'slice', 'slice')] + + def run(self, ips, imgs, para = None): + shp = ips.img.shape[:2] + imgs = [np.zeros(shp, dtype=np.uint8) for i in range([1, len(imgs)][para['slice']])] + newips = ImagePlus(imgs, ips.title+'-mark') + newips.back = ips + idx = ['None', 'Max', 'Min', 'Mask', '2-8mix', '4-6mix', '5-5mix', '6-4mix', '8-2mix'] + modes = ['set', 'max', 'min', 'msk', 0.2, 0.4, 0.5, 0.6, 0.8] + newips.lut = ColorManager.get_lut(para['cm']) + newips.chan_mode = modes[idx.index(para['mode'])] + newips.range = (0, para['n']) + IPy.show_ips(newips) + +plgs = [BuildMark] \ No newline at end of file diff --git a/imagepy/menus/Process/Classify/label_wgt.py b/imagepy/menus/Process/Classify/label_wgt.py index 52900f8f..fb541d53 100644 --- a/imagepy/menus/Process/Classify/label_wgt.py +++ b/imagepy/menus/Process/Classify/label_wgt.py @@ -1,8 +1,15 @@ from imagepy.ui.widgets import CMapSelCtrl -from imagepy.core.manager import ColorManager, ImageManager, WindowsManager +from imagepy.core.manager import ColorManager, ImageManager, WindowsManager, ToolsManager +from imagepy.core.engine import Macros import numpy as np -import wx +import wx, os.path as osp +def make_bitmap(bmp): + img = bmp.ConvertToImage() + img.Resize((20, 20), (2, 2)) + return img.ConvertToBitmap() + +# apply, paint, fill, width, slic class Plugin ( wx.Panel ): title = 'Label Tool' def __init__( self, parent ): @@ -11,39 +18,57 @@ def __init__( self, parent ): sizer = wx.BoxSizer( wx.VERTICAL ) sizer_color = wx.BoxSizer( wx.HORIZONTAL ) self.btns = [] - for i in range(16): + self.btn_make = wx.Button( self, wx.ID_ANY, 'New Mark', wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_color.Add(self.btn_make, 0, wx.ALL, 2) + for i in range(11): btn = wx.Button( self, wx.ID_ANY, str(i), wx.DefaultPosition, wx.DefaultSize, 0 ) btn.SetMaxSize( wx.Size( 30,-1 ) ) self.btns.append(btn) sizer_color.Add( btn, 0, wx.ALL, 2 ) self.spn_num = wx.SpinCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.SP_ARROW_KEYS, 2, 15, 0 ) self.spn_num.SetMaxSize(wx.Size(45, -1)) - sizer_color.Add( self.spn_num, 0, wx.ALL|wx.EXPAND, 3 ) + sizer_color.Add( self.spn_num, 0, wx.ALL|wx.EXPAND, 2 ) sizer.Add(sizer_color, 0, wx.ALL|wx.EXPAND, 0) sizer_other = wx.BoxSizer( wx.HORIZONTAL ) + self.btn_update = wx.Button( self, wx.ID_ANY, 'Update', wx.DefaultPosition, wx.DefaultSize, 0 ) + sizer_other.Add( self.btn_update, 0, wx.ALL, 2) + self.cmapsel = CMapSelCtrl(self) self.cmapsel.SetItems(ColorManager.luts) - sizer_other.Add(self.cmapsel, 0, wx.ALL|wx.EXPAND, 3 ) + sizer_other.Add(self.cmapsel, 0, wx.ALL|wx.EXPAND, 2 ) com_backChoices = [ u"No Background" ] self.com_back = wx.ComboBox( self, wx.ID_ANY, u"No Background", wx.DefaultPosition, wx.DefaultSize, com_backChoices, wx.CB_READONLY) self.com_back.SetSelection( 0 ) - sizer_other.Add( self.com_back, 1, wx.ALL|wx.EXPAND, 3 ) + sizer_other.Add( self.com_back, 1, wx.ALL|wx.EXPAND, 2 ) com_modeChoices = [ u"None", u"Max", u"Min", u"Mask", u"2-8mix", u"4-6mix", u"5-5mix", u"6-4mix", u"8-2mix" ] self.com_mode = wx.ComboBox( self, wx.ID_ANY, u"Min", wx.DefaultPosition, wx.DefaultSize, com_modeChoices, wx.CB_READONLY ) self.com_mode.SetSelection( 0 ) - sizer_other.Add( self.com_mode, 0, wx.ALL|wx.EXPAND, 3 ) + sizer_other.Add( self.com_mode, 0, wx.ALL|wx.EXPAND, 2 ) self.chk_hide = wx.CheckBox( self, wx.ID_ANY, u"Hide", wx.DefaultPosition, wx.DefaultSize, 0 ) sizer_other.Add( self.chk_hide, 0, wx.ALIGN_CENTER|wx.ALL, 0 ) sizer.Add(sizer_other, 0, wx.ALL|wx.EXPAND, 0) + #sizer_tol = wx.GridSizer( 0, 3, 0, 0 ) + self.pens = [] + name = ['01.gif','03.gif','05.gif','10.gif','fill.gif'] + path = osp.abspath(osp.dirname(__file__)) + for i in (0,1,2,3,4): + pen = wx.BitmapButton(self, wx.ID_ANY, make_bitmap(wx.Bitmap(osp.join(path, name[i]))),#make_bitmap(wx.Bitmap(data[1])), + wx.DefaultPosition, (30, 30), wx.BU_AUTODRAW|wx.RAISED_BORDER ) + #wx.Button( self, wx.ID_ANY, str(i), wx.DefaultPosition, wx.DefaultSize, 0 ) + pen.SetMaxSize( wx.Size( 30,-1 ) ) + self.pens.append( pen ) + sizer_color.Add( pen, 0, wx.ALL, 2 ) + outsizer.AddStretchSpacer(prop=1) + #outsizer.Add(sizer_tol, 0, wx.ALL, 0) outsizer.Add(sizer, 0, wx.ALL, 0) outsizer.AddStretchSpacer(prop=1) @@ -57,7 +82,24 @@ def __init__( self, parent ): self.com_back.Bind( wx.EVT_COMBOBOX, self.on_setback) self.com_mode.Bind( wx.EVT_COMBOBOX, self.on_mode) self.chk_hide.Bind( wx.EVT_CHECKBOX, self.on_mode) + self.pens[-1].Bind( wx.EVT_BUTTON, self.on_fill) for i in self.btns: i.Bind(wx.EVT_BUTTON, self.on_color) + for i in range(4): self.pens[i].Bind(wx.EVT_BUTTON, \ + lambda e, x=(1,3,5,10)[i]: self.on_pen(x)) + self.btn_make.Bind( wx.EVT_BUTTON, self.on_make) + + def on_make(self, event): + Macros(None, ['Build Mark Image>None']).start() + + def on_fill(self, event): + tol = ToolsManager.get('Flood Fill')() + tol.para['tor'] = 0 + tol.start() + + def on_pen(self, width): + tol = ToolsManager.get('Pencil')() + tol.para['width'] = width + tol.start() def on_color(self, event): ColorManager.set_front(self.btns.index(event.GetEventObject())) @@ -88,9 +130,10 @@ def on_cmapsel(self, event): def on_setback(self, event): name = self.com_back.GetValue() if name is None: return - curwin = WindowsManager.get() - curwin.set_back(ImageManager.get(name)) - curwin.ips.update() + ImageManager.get().back = ImageManager.get(name) + #curwin = WindowsManager.get() + #curwin.set_back(ImageManager.get(name)) + ImageManager.get().update() def on_mode(self, event): ips = ImageManager.get() diff --git a/imagepy/menus/Process/Classify/predict_plg.py b/imagepy/menus/Process/Classify/predict_plg.py index 26cacfea..c0f25a85 100644 --- a/imagepy/menus/Process/Classify/predict_plg.py +++ b/imagepy/menus/Process/Classify/predict_plg.py @@ -4,7 +4,7 @@ from glob import glob import os.path as osp import joblib -from .features import get_feature, get_predict +from imagepy.ipyalg import feature from imagepy.core.manager import ReaderManager, ViewerManager class Plugin(Simple): @@ -21,9 +21,7 @@ def __init__(self, ips=None, path=''): self.root = osp.join(IPy.root_dir, 'data/ilastik') fs = glob(osp.join(self.root, '*.fcl')) fs = [osp.split(i)[1] for i in fs] - if '/' in path: - fs = [path] - + if '/' in path: fs = [path] if len(fs) == 0: return IPy.alert('No feature classfier found!') self.para['predictor'] = fs[0] self.view[0] = (list, 'predictor', fs, str, 'predictor', '') @@ -33,14 +31,12 @@ def run(self, ips, imgs, para=None): if '/' in para['predictor']: path = para['predictor'] else: path = self.root+'/'+para['predictor'] model, key = joblib.load(path) - lut = {'ori':1, 'blr':key['grade'], 'sob':key['grade'], 'eig':key['grade']*2} - if sum([lut[i] for i in key['items']])*ips.channels != len(key['titles']): - return IPy.alert('image channels dismatch this predictor!') if not para['slice']: imgs = [ips.img] - rst = [] - for i in range(len(imgs)): - self.progress(i+1, len(imgs)) - rst.append(get_predict(imgs[i], model, key)) + slir, slic = ips.get_rect() + imgs = [i[slir, slic] for i in imgs] + rst = feature.get_predict(imgs, model, key, callback=self.progress) + if rst is None: + return IPy.alert('image channels dismatch this predictor!') ips = ImagePlus(rst, ips.title+'-mark') ips.range = ips.get_updown('all', 'one', step=512) IPy.show_ips(ips) \ No newline at end of file diff --git a/imagepy/ui/canvasframe.py b/imagepy/ui/canvasframe.py index 0d841245..2151c557 100644 --- a/imagepy/ui/canvasframe.py +++ b/imagepy/ui/canvasframe.py @@ -128,11 +128,11 @@ def on_idle(self, event): self.canvas.set_cn(self.ips.chan) self.canvas.set_rg(self.ips.chan_range) - if self.back != None: - self.canvas.set_back(self.back.imgs[self.ips.cur]) - self.canvas.set_cn(self.back.chan, True) - self.canvas.set_rg(self.back.chan_range, True) - self.canvas.set_lut(self.back.lut, True) + if self.ips.back != None: + self.canvas.set_back(self.ips.back.imgs[self.ips.cur]) + self.canvas.set_cn(self.ips.back.chan, True) + self.canvas.set_rg(self.ips.back.chan_range, True) + self.canvas.set_lut(self.ips.back.lut, True) self.canvas.set_mode(self.ips.chan_mode) else: self.canvas.set_back(None) @@ -223,6 +223,7 @@ def set_ips(self, ips): self.canvas.set_rg(ips.chan_range) self.canvas.set_lut(ips.lut) + ''' def set_back(self, ips): self.back = ips if ips is None: @@ -231,7 +232,7 @@ def set_back(self, ips): self.canvas.set_cn(ips.chan, True) self.canvas.set_rg(ips.chan_range, True) self.canvas.set_lut(ips.lut, True) - + ''' def on_scroll(self, event): self.ips.cur = self.page.GetThumbPosition() if isinstance(self.ips.chan, int): diff --git a/imagepy/ui/mainframe.py b/imagepy/ui/mainframe.py index b7bda4dc..415df638 100644 --- a/imagepy/ui/mainframe.py +++ b/imagepy/ui/mainframe.py @@ -37,7 +37,7 @@ def __init__( self, parent ): #self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_3DLIGHT ) ) self.SetIcon(wx.Icon(logopath, wx.BITMAP_TYPE_ICO)) IPy.curapp = self - self.SetSizeHints( wx.Size(900,700) if IPy.uimode() == 'ipy' else wx.Size( 580,-1 )) + self.SetSizeHints( wx.Size(1024,768) if IPy.uimode() == 'ipy' else wx.Size( 700,-1 )) self.menubar = pluginloader.buildMenuBarByPath(self, 'menus', 'plugins', None, True) diff --git a/imagepy/widgets/histogram/channels_wgt.py b/imagepy/widgets/histogram/channels_wgt.py index f1ab52a5..9ea50f45 100644 --- a/imagepy/widgets/histogram/channels_wgt.py +++ b/imagepy/widgets/histogram/channels_wgt.py @@ -171,8 +171,7 @@ def on_back(self, event): def on_setback(self, event): name = self.com_back.GetValue() if name is None: return - curwin = WindowsManager.get() - curwin.set_back(ImageManager.get(name)) + ImageManager.get().back = ImageManager.get(name) curwin.ips.update() def on_mode(self, event): From 549ca995aca2d5805590c1094d7c9a07f45b6e87 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 25 Oct 2019 16:31:59 +0800 Subject: [PATCH 166/343] classify --- imagepy/menus/Image/resize_plg.py | 1 + imagepy/menus/Process/Classify/classify_plgs.py | 2 +- imagepy/menus/Process/Classify/io_plgs.py | 2 +- imagepy/menus/Process/Classify/label_wgt.py | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/imagepy/menus/Image/resize_plg.py b/imagepy/menus/Image/resize_plg.py index acac0c07..57f15b6c 100644 --- a/imagepy/menus/Image/resize_plg.py +++ b/imagepy/menus/Image/resize_plg.py @@ -50,6 +50,7 @@ def run(self, ips, imgs, para = None): new.append(arr) ips.set_imgs(new) + return backimg = ips.backimg if backimg is None:return if backimg.ndim == 3: diff --git a/imagepy/menus/Process/Classify/classify_plgs.py b/imagepy/menus/Process/Classify/classify_plgs.py index d3cd08a5..b76c6894 100644 --- a/imagepy/menus/Process/Classify/classify_plgs.py +++ b/imagepy/menus/Process/Classify/classify_plgs.py @@ -49,7 +49,7 @@ def run(self, ips, imgs, para = None, preview=False): IPy.set_info('predict data...') if preview: - return feature.get_predict(oris, model, key, imgs, callback=self.progress) + return feature.get_predict(oris, model, key, labs, callback=self.progress) if len(imgs) == 1: ips.swap() outs = feature.get_predict(oris, model, key, callback=self.progress) nips = ImagePlus(outs, ips.title+'rst') diff --git a/imagepy/menus/Process/Classify/io_plgs.py b/imagepy/menus/Process/Classify/io_plgs.py index 5ea309db..ecb20c85 100644 --- a/imagepy/menus/Process/Classify/io_plgs.py +++ b/imagepy/menus/Process/Classify/io_plgs.py @@ -24,7 +24,7 @@ def run(self, ips, imgs, para = None): modes = ['set', 'max', 'min', 'msk', 0.2, 0.4, 0.5, 0.6, 0.8] newips.lut = ColorManager.get_lut(para['cm']) newips.chan_mode = modes[idx.index(para['mode'])] - newips.range = (0, para['n']) + #newips.range = (0, para['n']) IPy.show_ips(newips) plgs = [BuildMark] \ No newline at end of file diff --git a/imagepy/menus/Process/Classify/label_wgt.py b/imagepy/menus/Process/Classify/label_wgt.py index fb541d53..cee2326e 100644 --- a/imagepy/menus/Process/Classify/label_wgt.py +++ b/imagepy/menus/Process/Classify/label_wgt.py @@ -60,7 +60,7 @@ def __init__( self, parent ): name = ['01.gif','03.gif','05.gif','10.gif','fill.gif'] path = osp.abspath(osp.dirname(__file__)) for i in (0,1,2,3,4): - pen = wx.BitmapButton(self, wx.ID_ANY, make_bitmap(wx.Bitmap(osp.join(path, name[i]))),#make_bitmap(wx.Bitmap(data[1])), + pen = wx.BitmapButton(self, wx.ID_ANY, make_bitmap(wx.Bitmap(osp.join(path, 'imgs', name[i]))),#make_bitmap(wx.Bitmap(data[1])), wx.DefaultPosition, (30, 30), wx.BU_AUTODRAW|wx.RAISED_BORDER ) #wx.Button( self, wx.ID_ANY, str(i), wx.DefaultPosition, wx.DefaultSize, 0 ) pen.SetMaxSize( wx.Size( 30,-1 ) ) From 9a1c334bbf088441485d4fd7bc972bb94d607aef Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 2 Nov 2019 01:58:43 +0800 Subject: [PATCH 167/343] navigator --- imagepy/ipyalg/classify/feature.py | 4 ++-- imagepy/widgets/navigator/navigator_wgt.py | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/imagepy/ipyalg/classify/feature.py b/imagepy/ipyalg/classify/feature.py index d896fc16..b00a3c00 100644 --- a/imagepy/ipyalg/classify/feature.py +++ b/imagepy/ipyalg/classify/feature.py @@ -105,7 +105,7 @@ def get_predict(imgs, model, key=para, out=None, size=1024, callback=print): return out def dump_model(model, para, path): - joblib.sump(path, (model, para)) + joblib.dump(path, (model, para)) def load_model(model, para, path): return joblib.load(path) @@ -135,4 +135,4 @@ def load_model(model, para, path): rst = predict(img, model, para) ''' plt.imshow(rst) - plt.show() + plt.show() \ No newline at end of file diff --git a/imagepy/widgets/navigator/navigator_wgt.py b/imagepy/widgets/navigator/navigator_wgt.py index 5188c888..e96ebcc2 100644 --- a/imagepy/widgets/navigator/navigator_wgt.py +++ b/imagepy/widgets/navigator/navigator_wgt.py @@ -5,7 +5,7 @@ class Plugin ( wx.Panel ): title = 'Navigator' - scales = [0.03125, 0.0625, 0.125, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4, 5, 8, 10][::-1] + scales = [0.03125, 0.0625, 0.125, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4, 5, 8, 10, 15, 20, 30, 50] def __init__( self, parent ): @@ -18,7 +18,7 @@ def __init__( self, parent ): self.viewport = ViewPort( self) bSizer3.Add( self.viewport, 1, wx.EXPAND |wx.ALL, 5 ) - self.slider = wx.Slider( self, wx.ID_ANY, 6, 0, 13, wx.DefaultPosition, wx.DefaultSize, wx.SL_LEFT|wx.SL_VERTICAL|wx.SL_SELRANGE ) + self.slider = wx.Slider( self, wx.ID_ANY, 6, 0, len(self.scales), wx.DefaultPosition, wx.DefaultSize, wx.SL_LEFT|wx.SL_VERTICAL|wx.SL_SELRANGE|wx.SL_INVERSE ) bSizer3.Add( self.slider, 0, wx.ALL|wx.EXPAND, 0 ) @@ -55,7 +55,7 @@ def __init__( self, parent ): self.SetSizer( bSizer1 ) self.Layout() - self.slider.Bind( wx.EVT_SCROLL_CHANGED, self.on_zoom ) + self.slider.Bind( wx.EVT_SCROLL, self.on_zoom ) self.btn_apply.Bind( wx.EVT_BUTTON, self.on_apply ) self.btn_fit.Bind( wx.EVT_BUTTON, self.on_fit ) self.btn_one.Bind( wx.EVT_BUTTON, self.on_one ) @@ -75,7 +75,7 @@ def on_zoom(self, event): win = IPy.get_window() if win is None: return a,b,c,d = win.canvas.winbox - win.canvas.scaleidx = self.slider.GetValue() + win.canvas.scaidx = self.slider.GetValue() win.canvas.zoom(k, (a+c)/2, (b+d)/2) win.ips.update() self.viewport.set_box(win.canvas.conbox, win.canvas.winbox) @@ -85,7 +85,8 @@ def on_fit(self, event): if win is None: return win.canvas.fit() win.ips.update() - self.slider.SetValue(win.canvas.scaleidx) + print(type(win.canvas)) + self.slider.SetValue(win.canvas.scaidx) k = self.scales[self.slider.GetValue()] self.label.SetLabel('%.2f%%'%(k*100)) self.viewport.set_box(win.canvas.conbox, win.canvas.winbox) @@ -94,10 +95,10 @@ def on_one(self, event): win = IPy.get_window() if win is None: return a,b,c,d = win.canvas.winbox - win.canvas.scaleidx = self.scales.index(1) + win.canvas.scaidx = self.scales.index(1) win.canvas.zoom(1, (a+c)/2, (b+d)/2) win.ips.update() - self.slider.SetValue(win.canvas.scaleidx) + self.slider.SetValue(win.canvas.scaidx) self.label.SetLabel('%.2f%%'%100) self.viewport.set_box(win.canvas.conbox, win.canvas.winbox) From 2d332aac1ecd4bd9d3d811e7439be6519171846b Mon Sep 17 00:00:00 2001 From: Tong Date: Mon, 4 Nov 2019 16:22:54 +0100 Subject: [PATCH 168/343] quick fix of roi import plg --- imagepy/menus/File/Import/roi_plg.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/imagepy/menus/File/Import/roi_plg.py b/imagepy/menus/File/Import/roi_plg.py index f7083ce6..17807984 100644 --- a/imagepy/menus/File/Import/roi_plg.py +++ b/imagepy/menus/File/Import/roi_plg.py @@ -7,7 +7,7 @@ import read_roi from imagepy.core.engine import Free from imagepy import IPy -from skimage.draw import polygon +from skimage.draw import polygon, ellipse class Plugin(Free): """load_ij_roi: use read_roi and th pass to shapely objects""" @@ -27,5 +27,18 @@ def run(self, para=None): ls = read_roi.read_roi_zip(para['path']) img = np.zeros((para['height'], para['width']), dtype=np.int32) for i in ls: - img[polygon(ls[i]['y'], ls[i]['x'], img.shape)] = int(i) + current_roi = ls[i] + roi_type = current_roi["type"] + if roi_type is "freehand": + rs, cs = polygon(ls[i]['y'], ls[i]['x'], img.shape) + elif roi_type is "oval": + rs, cs = ellipse(current_roi["top"]+current_roi["height"]/2, + current_roi["left"]+current_roi["width"]/2, + current_roi["height"]/2, + current_roi["width"]/2) + try: + ind = int(i) + except Exception: + ind = int(i.split("-")[-1]) + img[rs, cs] = ind IPy.show_img([img], para['name']) From faa60bc316e2bc04931cd2079faeb10590afd072 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 10 Nov 2019 09:28:35 +0800 Subject: [PATCH 169/343] chinese dictionary --- imagepy/data/language/Chinese.dic | 497 ++++++++++++++++++++++++ imagepy/menus/Image/Type/convert_plg.py | 1 + imagepy/ui/canvasframe.py | 4 +- 3 files changed, 501 insertions(+), 1 deletion(-) create mode 100644 imagepy/data/language/Chinese.dic diff --git a/imagepy/data/language/Chinese.dic b/imagepy/data/language/Chinese.dic new file mode 100644 index 00000000..3e3965cc --- /dev/null +++ b/imagepy/data/language/Chinese.dic @@ -0,0 +1,497 @@ +16-bit uint:转16位 +16_Colors:-- +2D Surface:表面积 +2DSurface Demo:二维表面重建演示 +32-bit float:转32位浮点 +32-bit int:转32位整数 +3D Image Cube:三维立方体 +3D Surface:三维表面重建 +5_ramps:-- +64-bit float:转64位浮点 +6_Shades:-- +8-bit:转8位无符号 +About:关于 ImagePy +Active Ridge:交互式山脊线 +AdaBoost Classify:AdaBoost分类 +Adaptive Threshold:自适应阈值 +Add:加 +Add Column:添加列 +Add Slice:添加切片 +Adjust:调整 +Analysis:分析 +Analysis 3D:三维分析 +Append Rows:添加行 +Area Chart:面积图表 +AuPbSn 40 56K:-- +Author:-- +Auto Threshold:自动阈值 +BMP:-- +BMP Open:打开BMP +BMP Save:关闭BMP +BRGBCMYW:-- +Baboon 56K:-- +Background Self:设置自身为背景 +Bagging Classify:Bagging分类器 +Bar Chart:条形图 +Basic Operator:基础操作 +Binary:二值 +Binary 3D:三维二值 +Binary Closeing:形态学闭运算 +Binary ConvexHull:形态学凸包 +Binary Dilation:形态学膨胀 +Binary Erosion:形态学腐蚀 +Binary Opening:形态学开运算 +Binary Outline:形态学轮廓 +Binary Voronoi:平面瓜分 +Binary Watershed:二值分水岭 +Binary Watershed 3D:三维二值分水岭 +Blob 356K:-- +Blue:-- +Boats 25K:-- +Bound Snake Fit:边界曲线逼近 +Box Chart:箱状图 +Bridge 174K:-- +Bright And Constract:亮度对比度 +Build Graph:创建拓扑结构 +Build Graph 3D:创建三维拓扑结构 +Build Mark Image:创建标记图像 +CIERGB To RGB:CIERGB转RGB +CSV Open:打开CSV +CSV Save:保存CSV +Calendar:日历 +Canny:Canny边缘算子 +Canvas Size:画布大小 +Cartwheel Galaxy 231K:-- +Cell Colony 34K:-- +Chart:图表 +Chinese:-- +Classify:特征分类 +Clean Dictionary:清除字典 +Clear:清除内部 +Clear Mark:清除覆盖物 +Clear Out:清除外部 +Closing 3D:三维形态学闭运算 +Clown 14K:-- +Color:颜色 +Color Balance:色彩平衡 +Color Cluster:色彩聚类 +Color Cluster 3D:三维色彩聚类 +Color Points Statistic:彩色点空间统计 +Color Stairs:彩色色阶 +Command Line:命令行工具 +Contribute:贡献 +Contribute Document:贡献文档 +Contribute Plugin:贡献插件 +Contributions:贡献者 +Copy:复制 +Crop:裁剪 +Cross Stick:十字绣 +Curve Adjust:曲线调整 +Cut Branch:修剪分支 +Cut By ROI:用ROI修剪分支 +Cyan:-- +Cyan_Hot:-- +DAT:-- +DAT Open:打开DAT +DAT Save:保存DAT +DCM Open:打开DICOM +DEM:-- +DICOM:-- +DOG:差分高斯滤波 +Decoration Demo:装饰品 +Delete Columns:删除列 +Delete Rows:删除行 +Delete Slice:删除切片 +Demo Repo:-- +Detect Black Point:-- +Develop Tool Sute:开发者工具 +Diatoms 60K:-- +Dilation 3D:三维二值膨胀 +Distance 3D:三维距离变换 +Distance Transform:距离变换 +Duplicate:副本 +Edit:编辑 +Elegant ImagePy Style:ImagePy风格 +English:-- +Erosion 3D:三维二值腐蚀 +Errors:-- +Evolving Level Set:边框收敛水平集 +Excel Open:打开Excel +Excel Save:保存Excel +Exit:退出 +Export:导出 +ExtraTrees Classify:ExtraTrees分类 +FFT:快速傅里叶变换 +FRC:-- +Feature Classify Panel:特征分类面板 +Feature Predictor:特征预测 +Features:特征 +Features 3D:三维特征 +Felzenszwalb:-- +Felzenszwalb Label:-- +File:文件 +Fill:填充 +Fill Holes:二值空隙填充 +Fill Holes 3D:三维空隙填充 +Filters:滤波器 +Filters 3D:三维滤波器 +Find IsoLine:查找等值线 +Find Maximum:查找局部最大值 +Find Minimum:查找局部最小值 +Find Riedge:查找山脊线 +Find Watershed:查找分水岭 +FluidSurface:-- +FluorescentCells 400K:-- +Fragment Repair:碎片修复 +Frangi:Frangi山脊线提取 +Frangi 3D:三维Frangi山脊线提取 +Frequence:频率统计 +Frequence 3D:三维频率统计 +GIF:-- +GIF Animate Open:打开GIF动画 +GIF Animate Save:保存GIF动画 +GIF Open:打开GIF +GIF Save:保存GIF +Game Of Life:生命游戏 +Games:游戏 +Gamma:Gamma矫正 +Gaussian:高斯模糊 +Gaussian 3D:三维高斯模糊 +Gaussian Laplace:高斯-拉普拉斯滤波 +Gaussian Random:高斯随机数 +Geometry Analysis:几何分析 +Geometry Analysis 3D:三维几何分析 +Geometry Filter:几何过滤器 +Geometry Filter 3D:三维几何过滤器 +Glow:-- +Gradient Boosting Classify:Gradient Boosting分类器 +Graph Cut Branch 3D:三维分支修剪 +Graph Shortest Path:最短路径 +Graph Statistic:拓扑统计 +Graph Statistic 3D:三维拓扑统计 +Graph Summarise:节点路径汇总 +Graph Summarise 3D:三维节点路径汇总 +Gray Cluster:灰度聚类 +Gray Cluster 3D:三维灰度聚类 +Gray Points Statistic:灰度点统计 +Gray Stairs:灰度色戒 +Grays:-- +Green:-- +Green_Fire_Blue:-- +Group Statistic:分组统计 +HSV To RGB:HSV转RGB +Harris:Harris角点检测 +Help:帮助 +Hessian:海森特征 +Hessian 3D:三维海森特征 +HiLo:-- +Hist Chart:频率直方图 +Histogram:直方图 +Histogram Match:直方图匹配 +Histogram Normalize:直方图标准化 +Home Page:主页 +Hydrology:水文算法 +Hysteresis Threshold:双阈值 +IBook:案例素材 +ICA:-- +ICA2:-- +ICA3:-- +Image:图像 +Image Calculator:图像运算 +Import:导入 +Import Rois from IJ:导入ImageJ的ROI文件 +Import Sequence:导入序列 +Install:安装 +Install Packages:安装python包 +Install Plugins:安装ImagePy插件 +Intensity Analysis:密度分析 +Intensity Filter:密度过滤 +Inverse FFT:快速傅里叶逆变换 +Invert:反向 +JPG:-- +JPG Open:打开JPG +JPG Save:保存JPG +Jet:-- +K-Means:K均值聚类 +Kill Image:关闭图像 +Kill TableLog:关闭表格 +Kill TextLog:关闭日志 +Kit3D:三维 +Kitchen Rosenfeld:-- +LUV To RGB:LUV转RGB +Lab To RGB:Lab转RGB +Label Image:图像标记 +Label Tool:标记工具 +Language:语言 +Laplace:拉普拉斯滤波 +Laplace Sharp:拉普拉斯瑞华 +Leaf 36K:-- +Lena:-- +Lena 68K:-- +Lines Demo:线条示例 +List Packages:列举已安装的包 +List Plugins:列举已安装的插件 +Log Power:对数功率谱 +Logo:-- +Lookup table:色彩映射表 +Lymp 17K:-- +M51 177K:-- +MAT:-- +MRI Head 47K:-- +Macros:宏 +Macros Recorder:宏录制 +Magenta:-- +Magenta_Hot:-- +Manager:管理 +Mark:覆盖物 +Mark Setting:覆盖物设定 +Mat 3D Open:打开三维Mat文件 +Mat 3D Save:保存三维Mat文件 +Mat Open:打开Mat +Mat Save:保存Mat +Math:数学 +Max:最大值 +Maximum:最大值滤波器 +Measure Surface And Volume:测量表面积体积 +Medial Axis:中轴线 +Median:中值滤波器 +Meijering:Meijering山脊线提取 +Meijering 3D:Meijering三维山脊线提取 +Merge RGB Channels:融合RGB通道 +Microm 32K:-- +Min:最小值 +Minimum:最小值滤波器 +Moravec:Moravec角点检测 +Morphological Snake Fit:形态学曲线逼近 +Multiply:乘 +Network 3D:三维拓扑网络 +New:新建 +New Filter:新建滤镜 +New Free:新建自由插件 +New Language:新建语言 +New Simple:新建基础插件 +New Tool:新建工具 +Next Slice:下一切片 +Niblack Threshold:Niblack阈值 +NileBend 1.9M:-- +Numpy:-- +Numpy 3D Open:打开三维Numpy +Numpy 3D Save:--保存三维Numpy +Numpy Open:打开Numpy +Numpy Save:保存Numpy +Open:打开 +Open Mark:打开覆盖物 +Open Raw:打开二进制文件 +Open Recent:最近打开 +Open Url:打开URL +OpenCV:OpenCV +Opening 3D:三维形态学膨胀 +Orange_Hot:-- +Orthogonal view:三视图 +Others:-- +PNG:-- +PNG Open:打开PNG +PNG Save:保存PNG +Paste:粘贴 +Pay Tribute To ImageJ:致敬ImageJ +Percent:百分比滤波 +Pie Chart:饼状图 +Pink:-- +Pixel Cluster:像素聚类 +Pixel Statistic:像素统计 +Pixel Statistic 3D:三维像素统计 +Plot Chart:折线图 +Plugin List View:插件列表视图 +Plugin Tree View:插件树形视图 +Plugins:插件 +Plugins Manager:插件管理器 +Points Value:点的像素值 +Previous Slice:前一切片 +Prewitt:Previtt梯度算子 +Process:处理 +Property Marker:属性标记 +Quick Shift:快速Shift超像素 +Quickshift Label:快速Shift超像素标记 +RAG Cut Normalized:RAG图标准化 +RAG Merge Hierarchical:RAG图层级融合 +RAG Threshold:RAG阈值 +RGB:转RGB彩色 +RGB Points Cloud:RGB点云 +RGB To CIERGB:RGB转CIERGB +RGB To Gray:RGB转灰度 +RGB To HSV:RGB转HSV +RGB To LUV:RGB转LUV +RGB To Lab:RGB转Lab +RGB To XYZ:RGB转XYZ +ROI Add:热区添加 +ROI Bound Box:热区外接矩形 +ROI Clip:热区裁剪 +ROI Convex Hull:热区凸包 +ROI Ctrl Panel:热区面板 +ROI Difference:热区差异 +ROI Inflate:热区膨胀 +ROI Intersect:热区交集 +ROI Invert:热区反向 +ROI Load:热区载入 +ROI Open:热区打开 +ROI Remove:热区移除 +ROI Save:热区保存 +ROI Setting:热区设置 +ROI Shrink:热区收缩 +ROI Snake Fit:热区蛇形收敛 +ROI Symmetric Diff:热区非相交并集 +ROI Union:热区并集 +Rainbow_RGB:-- +Random Balls Demo:随机球 +Random Forest Classify:Random Forest分类 +Random Walker:随机游走 +Red:-- +Red_Hot:-- +Region Analysis:区域分析 +Region Label 3D:三维区域标记 +Register By Mats:按照矩阵对齐 +Reload Plugins:插件重加载 +Remove 2Path Node:移除双路径节点 +Remove 2Path Node 3D:移除三维双路径节点 +Remove Isolate 3D:移除三维孤立节点 +Remove Isolate Node:移除孤立节点 +Rename:重命名 +Resize:重设大小 +Rotate:旋转 +Run Macros:运行宏 +SLIC Superpixel:SLIC超像素 +SLIC Superpixel Label:SLIC超像素标记 +Samples ImageJ:ImageJ素材图像 +Samples Local:本地素材 +Samples Online:在线素材 +Sato:Sato山脊线提取 +Sato 3D:Sato三维山脊线提取 +Sauvola Threshold:Sauvola阈值 +Save:保存 +Save Mark:保存覆盖物 +Save Sequence:保存序列 +Save With Mark:带覆盖物保存 +Scale:缩放 +Scale And Unit:比例尺和单位 +Scatter Chart:三点图表 +Screen Capture:屏幕截取 +Sea Ice:-- +Segment:分割 +Sel By C Name R Count:根据行号与列名选取 +Select All:全选 +Select None:取消热区 +Selection:热区 +Set Background:设置背景 +Set Slice:设置切片 +Shotcut Editor:快捷键编辑器 +Show Graph 3D:三维图网展示 +Show Graph R 3D:展示带有半径的三维图网 +Show Viewer 3D:三维画布 +Signal:信号 +Signal Uniform Filter:信号均值滤波 +Simple Threshold:阈值 +SimpleITK:-- +Skeleton:骨架 +Skeleton 3D:三维骨架 +Skeleton Network:骨架拓扑结构 +Sketch:描边 +SmartSEMSample 780K:-- +Sobel:Sobel梯度算子 +Sobel 3D:三维Sobel梯度算子 +Split RGB Channels:拆分RGB通道 +Squre Root:平方根 +Stack:图像栈 +Stack Register:图像栈对齐 +StackReg:对齐 +StackReg License:-图像栈对齐协议 +Statistic:统计 +Stroke Step:分布画笔 +Sub Stack:截取子栈 +Subtract:减 +TIF:-- +TIF 3D Open:打开三维TIF +TIF 3D Save:保存三维TIF +TIF Open:打开TIF +TIF Save:保存TIF +Table:表格 +Table Bins Frequency:表格值频率 +Table Corp:表格裁剪 +Table Duplicate:表格副本 +Table IO:表格输入输出 +Table Point Cloud:从表格制作点云 +Table Sort By Key:表格排序 +Table Statistic:表格统计 +Table Transpose:表格反转 +Tables Window:表格窗口 +Thermal:-- +Threshold:阈值 +Tomasi:-- +Tool Tree View:工具树型列表 +Toolbar:工具栏 +Topic:主题帮助 +Trans to List:转为离散序列 +Trans to Stack:转为连续栈 +Transform:变换 +Tree Rings 48K:-- +Two Boxes:-- +Type:类型 +Undo:撤销 +Uniform:均值滤波 +Uniform 3D:三维均值滤波 +Uniform Random:均匀随机数 +Unit Matrix:单位矩阵 +Universal Generator:通用数组生成器 +Unsharp Mask:USM锐化 +Unsharp Mask 3D:三维USM锐化 +Up And Down Watershed:高低阈值分水岭 +Up Down Watershed 3D:三维高低阈值分水岭 +Update Dictionary:更新字典 +Update Software:更新软件 +Values Frequency:数值频率统计 +Variance:方差滤波 +Video:视频 +Video Ctrl Panel:视频面板 +Viewer 3D:三维可视化 +Viridis:-- +Watershed With ROI:用ROI做种子进行分水岭 +Widgets:桌面控件 +Window:窗口 +Windows Style:窗口风格 +XYZ To RGB:XYZ转RGB +Yellow:-- +Yellow_Hot:-- +Zero Center:零位于中心 +Zero Edge:零位于边缘 +ametrine:-- +ascent:-- +astronaut:-- +blue_orange_icb:-- +camera:-- +coffee:-- +coins:-- +cool:-- +edges:轮廓 +face:-- +gem:-- +glasbey:-- +glasbey_inverted:-- +glasbey_on_dark:-- +horse:-- +hubble_deep_field:-- +immunohistochemistry:-- +isolum:-- +moon:-- +morgenstemning:-- +mpl-inferno:-- +mpl-magma:-- +mpl-plasma:-- +mpl-viridis:-- +page:-- +phase:-- +physics:-- +royal:-- +sepia:-- +smart:-- +thal:-- +thallium:-- +unionjack:-- diff --git a/imagepy/menus/Image/Type/convert_plg.py b/imagepy/menus/Image/Type/convert_plg.py index 15393dbd..303605df 100644 --- a/imagepy/menus/Image/Type/convert_plg.py +++ b/imagepy/menus/Image/Type/convert_plg.py @@ -30,6 +30,7 @@ def run(self, ips, imgs, para = None): minv, maxv = ips.get_updown() for i in range(n): self.progress(i, len(imgs)) + if ips.imgtype == 'rgb': img8.append(imgs[i].mean(axis=2).astype(np.uint8)) else: diff --git a/imagepy/ui/canvasframe.py b/imagepy/ui/canvasframe.py index 2151c557..eb8fe7b0 100644 --- a/imagepy/ui/canvasframe.py +++ b/imagepy/ui/canvasframe.py @@ -107,11 +107,13 @@ def on_idle(self, event): if self.ips.scrchanged: self.set_ips(self.ips) self.ips.scrchanged = False - self.canvas.fit() self.set_info(self.ips) + self.canvas.fit() self.set_fit(self.ips) print('scr changed =====') + if self.ips.dirty != False: + self.set_info(self.ips) if self.ips.roi is None: self.canvas.marks['roi'] = None else: draw = lambda dc, f, **key: self.ips.roi.draw(dc, f, cur=self.ips.cur, **key) From 10fff38d95626d57db9480eaa9b3731e7409d334 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 13 Dec 2019 15:04:59 +0800 Subject: [PATCH 170/343] numba 0.46 --- imagepy/ipyalg/graph/skel2d.py | 6 ++-- imagepy/ipyalg/graph/sknw.py | 28 +++++++-------- imagepy/ipyalg/hydrology/edt.py | 36 ++++++++++++------- imagepy/ipyalg/hydrology/findmax.py | 27 +++++++------- imagepy/ipyalg/hydrology/isoline.py | 21 +++++------ imagepy/ipyalg/hydrology/watershed.py | 10 +++--- .../Analysis/Skeleton Network/graph_plgs.py | 2 +- imagepy/menus/Image/duplicate_plg.py | 8 ++--- .../menus/Process/Hydrology/hydrology_plgs.py | 1 - imagepy/tools/Network/graphcut_tol.py | 2 +- imagepy/tools/Network/graphpen_tol.py | 2 +- requirements.txt | 1 + 12 files changed, 78 insertions(+), 66 deletions(-) diff --git a/imagepy/ipyalg/graph/skel2d.py b/imagepy/ipyalg/graph/skel2d.py index 0906ddf6..56292a65 100644 --- a/imagepy/ipyalg/graph/skel2d.py +++ b/imagepy/ipyalg/graph/skel2d.py @@ -3,7 +3,7 @@ from numba import jit from scipy.ndimage import label, generate_binary_structure -from ..hydrology.edt import distance_transform_edt +from scipy.ndimage import distance_transform_edt strc = np.ones((3,3), dtype=np.bool) # check whether this pixcel can be removed @@ -33,7 +33,7 @@ def check(n): fac = np.array([1,2,4,8,16,32,64,128]) -@jit +@jit(nopython=True) def medial_axis(data, idx, branch = True): h, w = data.shape data = data.ravel() @@ -59,7 +59,7 @@ def mid_axis(img): if __name__ == '__main__': from time import time from skimage.data import horse - from skimage.morphology import medial_axis + #from skimage.morphology import medial_axis import matplotlib.pyplot as plt img = ~horse()*255 diff --git a/imagepy/ipyalg/graph/sknw.py b/imagepy/ipyalg/graph/sknw.py index 090bbfb3..b390adde 100644 --- a/imagepy/ipyalg/graph/sknw.py +++ b/imagepy/ipyalg/graph/sknw.py @@ -2,7 +2,6 @@ from numba import jit import networkx as nx -# get neighbors d index def neighbors(shape): dim = len(shape) block = np.ones([3]*dim) @@ -13,9 +12,8 @@ def neighbors(shape): acc = np.cumprod((1,)+shape[::-1][:-1]) return np.dot(idx, acc[::-1]) -@jit # my mark -def mark(img): # mark the array use (0, 1, 2) - nbs = neighbors(img.shape) +@jit(nopython=True) # my mark +def mark(img, nbs): # mark the array use (0, 1, 2) img = img.ravel() for p in range(len(img)): if img[p]==0:continue @@ -25,7 +23,7 @@ def mark(img): # mark the array use (0, 1, 2) if s==2:img[p]=1 else:img[p]=2 -@jit # trans index to r, c... +@jit(nopython=True) # trans index to r, c... def idx2rc(idx, acc): rst = np.zeros((len(idx), len(acc)), dtype=np.int16) for i in range(len(idx)): @@ -35,7 +33,7 @@ def idx2rc(idx, acc): rst -= 1 return rst -@jit # fill a node (may be two or more points) +@jit(nopython=True) # fill a node (may be two or more points) def fill(img, p, num, nbs, acc, buf): back = img[p] img[p] = num @@ -54,7 +52,7 @@ def fill(img, p, num, nbs, acc, buf): if cur==s:break return idx2rc(buf[:s], acc) -@jit # trace the edge and use a buffer, then buf.copy, if use [] numba not works +@jit(nopython=True) # trace the edge and use a buffer, then buf.copy, if use [] numba not works def trace(img, p, nbs, acc, buf): c1 = 0; c2 = 0; newp = 0 @@ -75,12 +73,9 @@ def trace(img, p, nbs, acc, buf): if c2!=0:break return (c1-10, c2-10, idx2rc(buf[:cur], acc)) -@jit # parse the image then get the nodes and edges -def parse_struc(img): - nbs = neighbors(img.shape) - acc = np.cumprod((1,)+img.shape[::-1][:-1])[::-1] +@jit(nopython=True) # parse the image then get the nodes and edges +def parse_struc(img, pts, nbs, acc): img = img.ravel() - pts = np.array(np.where(img==2))[0] buf = np.zeros(131072, dtype=np.int64) num = 10 nodes = [] @@ -115,8 +110,11 @@ def buffer(ske): def build_sknw(ske, multi=False): buf = buffer(ske) - mark(buf) - nodes, edges = parse_struc(buf) + nbs = neighbors(buf.shape) + acc = np.cumprod((1,)+buf.shape[::-1][:-1])[::-1] + mark(buf, nbs) + pts = np.array(np.where(buf.ravel()==2))[0] + nodes, edges = parse_struc(buf, pts, nbs, acc) return build_graph(nodes, edges, multi) # draw the graph @@ -142,4 +140,4 @@ def draw_graph(img, graph, cn=255, ce=128): print('d') print(a) print('d') - \ No newline at end of file + diff --git a/imagepy/ipyalg/hydrology/edt.py b/imagepy/ipyalg/hydrology/edt.py index f93ff1d7..b51ba9cd 100644 --- a/imagepy/ipyalg/hydrology/edt.py +++ b/imagepy/ipyalg/hydrology/edt.py @@ -12,7 +12,7 @@ def neighbors(shape): acc = np.cumprod((1,)+shape[::-1][:-1]) return np.dot(idx, acc[::-1]) -@jit +@jit(nopython=True) def dist(idx1, idx2, acc): dis = 0 for i in range(len(acc)): @@ -23,7 +23,7 @@ def dist(idx1, idx2, acc): idx2 -= c2*acc[i] return dis -@jit +@jit(nopython=True) def step(dis, pts, roots, s, level, nbs, acc, scale): cur = 0 while curhigh:img[i]=high else: img[i] = (img[i]-low)//step*step+low -@jit # my mark +@jit(nopython=True) # my mark +def isoline_jit(img, mark, nbs): + for p in range(len(img)): + if mark[p]==0:continue + s = 0 + for dp in nbs: + if img[p] > img[p+dp]:s+=1 + if s==0:mark[p] = 0 + def isoline(img, low=0, high=255, step=20): stair(img, low, high, step) nbs = neighbors(img.shape) mark = np.zeros_like(img, dtype=np.uint8) mark[tuple([slice(1,-1)]*img.ndim)] = 255 - img = img.ravel() - mark1 = mark.ravel() - for p in range(len(img)): - if mark1[p]==0:continue - s = 0 - for dp in nbs: - if img[p] > img[p+dp]:s+=1 - if s==0:mark1[p] = 0 + isoline_jit(img.ravel(), mark.ravel(), nbs) return mark \ No newline at end of file diff --git a/imagepy/ipyalg/hydrology/watershed.py b/imagepy/ipyalg/hydrology/watershed.py index b7ebfb2d..70790c23 100644 --- a/imagepy/ipyalg/hydrology/watershed.py +++ b/imagepy/ipyalg/hydrology/watershed.py @@ -13,7 +13,7 @@ def neighbors(shape, conn=1): acc = np.cumprod((1,)+shape[::-1][:-1]) return np.dot(idx, acc[::-1]) -@jit +@jit(nopython=True) def step(img, msk, line, pts, s, level, up, nbs): cur = 0 while cur0xfffffff0:mark[i]=0 @@ -113,7 +113,7 @@ def watershed(img, mark, conn=1, line=False, up=True): markers[coins > 150] = 2 plt.imshow(markers) plt.show() - watershed(dem, markers) + markers = watershed(dem, markers) plt.imshow(markers) plt.show() ''' diff --git a/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py b/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py index 377fea9f..1bf80ad8 100644 --- a/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py +++ b/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py @@ -176,7 +176,7 @@ def run(self, ips, imgs, para = None): sknw.draw_graph(ips.img, g) ips.mark = Mark(ips.data) -@jit +@jit(nopython=True) def floodfill(img, x, y): buf = np.zeros((131072,2), dtype=np.uint16) color = img[int(y), int(x)] diff --git a/imagepy/menus/Image/duplicate_plg.py b/imagepy/menus/Image/duplicate_plg.py index bff7b50d..7877135f 100644 --- a/imagepy/menus/Image/duplicate_plg.py +++ b/imagepy/menus/Image/duplicate_plg.py @@ -45,17 +45,17 @@ def run(self, ips, imgs, para = None): if ips.roi == None: if ips.is3d:imgs=imgs.copy() else:imgs = [i.copy() for i in imgs] - backimg = ips.backimg + #backimg = ips.backimg else: sc, sr = ips.get_rect() if ips.is3d: imgs=imgs[:, sc, sr].copy() else: imgs = [i[sc,sr].copy() for i in imgs] - if not ips.backimg is None: - backimg = None #ips.backimg[sr, sr] + #if not ips.backimg is None: + # backimg = None #ips.backimg[sr, sr] ipsd = ImagePlus(imgs, name) if ips.roi != None: ipsd.roi = ips.roi.affine(np.eye(2), (-sr.start, -sc.start)) - if not ips.backimg is None: ipsd.backimg = backimg + #if not ips.backimg is None: ipsd.backimg = backimg ipsd.chan_mode = ips.chan_mode IPy.show_ips(ipsd) diff --git a/imagepy/menus/Process/Hydrology/hydrology_plgs.py b/imagepy/menus/Process/Hydrology/hydrology_plgs.py index 19a362a2..408f5180 100644 --- a/imagepy/menus/Process/Hydrology/hydrology_plgs.py +++ b/imagepy/menus/Process/Hydrology/hydrology_plgs.py @@ -1,6 +1,5 @@ import scipy.ndimage as ndimg import numpy as np -from numba import jit from imagepy.core.engine import Filter from imagepy.ipyalg import find_maximum, ridge, stair, isoline, watershed from imagepy.core.roi import PointRoi diff --git a/imagepy/tools/Network/graphcut_tol.py b/imagepy/tools/Network/graphcut_tol.py index dcf4e4ac..2acad92b 100644 --- a/imagepy/tools/Network/graphcut_tol.py +++ b/imagepy/tools/Network/graphcut_tol.py @@ -8,7 +8,7 @@ import wx from numba import jit -@jit +@jit(nopython=True) def floodfill(img, x, y): buf = np.zeros((131072,2), dtype=np.uint16) color = img[int(y), int(x)] diff --git a/imagepy/tools/Network/graphpen_tol.py b/imagepy/tools/Network/graphpen_tol.py index 180b94b3..0ca8584a 100644 --- a/imagepy/tools/Network/graphpen_tol.py +++ b/imagepy/tools/Network/graphpen_tol.py @@ -8,7 +8,7 @@ import wx from numba import jit -@jit +@jit(nopython=True) def floodfill(img, x, y): buf = np.zeros((131072,2), dtype=np.uint16) color = img[int(y), int(x)] diff --git a/requirements.txt b/requirements.txt index 265dd7b7..a48f6943 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,7 @@ pystackreg pypubsub read-roi scikit-image +scikit-learn shapely wxpython xlrd From bd4ad04e6cef1e92b2a3ce54c64d211ba35ac7c7 Mon Sep 17 00:00:00 2001 From: weisongzhao Date: Fri, 24 Jan 2020 18:29:34 +0800 Subject: [PATCH 171/343] temporal color and bleach --- imagepy/core/util/fileio.py | 3 +- imagepy/menus/Image/Adjust/__init__.py | 2 +- .../Image/Adjust/bleachCorrection_plg.py | 69 +++++++++++++++++++ imagepy/menus/Plugins/__init__.py | 2 +- imagepy/menus/Plugins/temporal_plg.py | 55 +++++++++++++++ 5 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 imagepy/menus/Image/Adjust/bleachCorrection_plg.py create mode 100644 imagepy/menus/Plugins/temporal_plg.py diff --git a/imagepy/core/util/fileio.py b/imagepy/core/util/fileio.py index a57bfe6f..af3e44e6 100644 --- a/imagepy/core/util/fileio.py +++ b/imagepy/core/util/fileio.py @@ -8,7 +8,8 @@ def show_img(img, title): if isinstance(img, list): return IPy.show_img(img, title) - if img.dtype!=np.uint8 and img.ndim>2 and img.shape[2]!=3: + # if img.dtype!=np.uint8 and img.ndim>2 and img.shape[2]!=3: + if img.ndim>2 and img.shape[2]!=3: return IPy.show_img(img, title) if img.dtype==np.uint8 and img.ndim==3 and img.shape[2]==4: img = img[:,:,:3].copy() diff --git a/imagepy/menus/Image/Adjust/__init__.py b/imagepy/menus/Image/Adjust/__init__.py index 843b06f2..ca20c292 100644 --- a/imagepy/menus/Image/Adjust/__init__.py +++ b/imagepy/menus/Image/Adjust/__init__.py @@ -1 +1 @@ -catlog = ['threshold_plg','graystairs_plg','brightcons_plg','curve_plg','-','colorbalance_plg','colorstairs_plg','-','histogram_plgs'] \ No newline at end of file +catlog = ['threshold_plg','graystairs_plg','brightcons_plg','curve_plg','-','bleachCorrection_plg','-','colorbalance_plg','colorstairs_plg','-','histogram_plgs'] \ No newline at end of file diff --git a/imagepy/menus/Image/Adjust/bleachCorrection_plg.py b/imagepy/menus/Image/Adjust/bleachCorrection_plg.py new file mode 100644 index 00000000..054a6b64 --- /dev/null +++ b/imagepy/menus/Image/Adjust/bleachCorrection_plg.py @@ -0,0 +1,69 @@ +""" +Created on Sun Jan 23 11:53:00 2020 +@author: weisong +""" +from imagepy.core.engine import Simple +import numpy as np +from imagepy import IPy +from scipy.optimize import curve_fit +# from .histogram_plg import like +from skimage.exposure import histogram_matching + +def simple_ratio(imgs): + x,y,z = imgs.shape + ref = imgs[:,:,1].sum()/x/y + for i in range(z-1): + imgs[:,:,i+1] *= (ref/(imgs[:,:,i+1].sum() + /x/y)).astype(imgs.dtype) + return imgs +def exponential_fit(imgs): + imgs_=imgs.astype('float32') + x,y,z = imgs.shape + t = np.linspace(0, z-1, z) + intensity = (imgs.sum(1).sum(0)/x/y) + popt, pcov = curve_fit(exponential_func, t, intensity) + for i in range(z-1): + ratio=exponential_func(0, popt[0], popt[1], popt[2])/ \ + exponential_func(i+1, popt[0], popt[1], popt[2]) + imgs_[:,:,i+1] *= ratio + return imgs_.astype(imgs.dtype) + +def histogram_match(imgs): + x,y,z = imgs.shape + for i in range(z-1): + imgs[:,:,i+1] = histogram_matching.match_histograms( + imgs[:,:,i+1],imgs[:,:,0]) + return imgs + +def exponential_func(t, ref, k, offset): + return ref * np.exp(- k * t) + offset + +class Plugin(Simple): + title = 'Bleach Correction' + note = ['8-bit','16-bit','stack'] + para = {'Method':'Simple ratio', 'Background':0} + view = [(list, 'Method', + ['Simple ratio','Exponential fit','Histogram match'], + str, 'Correction Method',''), + (int,'Background',(0,65535),0,'Background intensity','')] + + def run(self, ips, imgs, para = None): + Bconstant = para['Background'] + imgs_= imgs.transpose(1,2,0) + if Bconstant != 0: + for i in range (z): + imgs_[:,:,i] -= Bconstant + imgs_ = np.maximum(imgs_, 0) + print (para['Method']) + if para['Method'] == 'Simple ratio': + imgs_=simple_ratio(imgs_) + IPy.show_img(imgs_.transpose(2,0,1),'Corrected %s'%ips.title) + if para['Method'] == 'Exponential fit': + imgs_=exponential_fit(imgs_) + IPy.show_img(imgs_.transpose(2,0,1),'Corrected %s'%ips.title) + if para['Method'] == 'Histogram match': + imgs_=histogram_match(imgs_) + IPy.show_img(imgs_.transpose(2,0,1),'Corrected %s'%ips.title) + + + diff --git a/imagepy/menus/Plugins/__init__.py b/imagepy/menus/Plugins/__init__.py index 983573c9..5b511cef 100644 --- a/imagepy/menus/Plugins/__init__.py +++ b/imagepy/menus/Plugins/__init__.py @@ -1 +1 @@ -catlog = ['New', 'Macros', 'Manager', '-', 'Install', 'Contribute', 'update_plg', '-', 'StackReg', 'Games', 'screencap_plg'] \ No newline at end of file +catlog = ['New', 'Macros', 'Manager', '-', 'Install', 'Contribute', 'update_plg', '-', 'temporal_plg','StackReg', 'Games', 'screencap_plg'] \ No newline at end of file diff --git a/imagepy/menus/Plugins/temporal_plg.py b/imagepy/menus/Plugins/temporal_plg.py new file mode 100644 index 00000000..bd09cad7 --- /dev/null +++ b/imagepy/menus/Plugins/temporal_plg.py @@ -0,0 +1,55 @@ +""" +Created on Sun Jan 22 12:56:00 2020 +@author: weisong +""" +from imagepy.core.engine import Simple +import numpy as np +from imagepy.core.manager import ColorManager +from imagepy import IPy + +def color_code(imgs,cmap,start,end): + x,y,z = imgs.shape + z = end - start + imgLUT = np.zeros([x,y,3],'float32') + for i in range(start, end): + for rgb in range (2): + imgLUT[:,:,rgb] += imgs[:,:,i] * \ + cmap[np.floor((i+1)*256/z).astype(np.int)-1,rgb] + for rgb in range(2): + imgLUT[:,:,rgb] = 255 * imgLUT[:,:,rgb] / imgLUT[:,:,rgb].max() + return imgLUT + +def color_code(img, lut): + idx = np.linspace(0,255,len(img)).astype(int) + cs = lut[idx].astype(np.uint32) + buf = np.zeros(img[0].shape+(3,), dtype=np.uint32) + for im, c in zip(img, cs): + buf += im.reshape(im.shape+(-1,)) * c + k = 255/buf.max(axis=(0,1)).reshape((1,1,3)) + return (buf * k).astype(np.uint8) + +class Plugin(Simple): + title = 'Temporal color-code' + note = ['all', 'stack'] + para = {'LUT':'Jet', + 'Start image':1, + 'End image': 2, + 'Creat time color scale bar':True} + + def load(self, ips): + self.slength = len(ips.imgs) + self.para['End image'] = self.slength + self.view = [(list, 'LUT', list(ColorManager.luts.keys()), str, 'LUT',''), + (int, 'Start image', (1,self.slength),0,'Start image','1~%d'%self.slength), + (int, 'End image', (2,self.slength),0,'End image','start~%d'%self.slength), + (bool, 'Creat time color scale bar', 'Creat time color scale bar')] + return True + + def run(self, ips, imgs, para = None): + cmap = ColorManager.luts[ para['LUT'] ] + imglut = color_code(imgs[para['Start image']-1: para['End image']], cmap) + IPy.show_img([imglut],'Color-coded %s'%ips.title) + if para['Creat time color scale bar']: + cmapshow = np.ones([32,256,3])*cmap + IPy.show_img([cmapshow.astype('uint8')],'Color bar') + From 69cbe05c6ebea4b078a497f2145c85dd9ba1f95b Mon Sep 17 00:00:00 2001 From: weisongzhao Date: Fri, 24 Jan 2020 18:35:58 +0800 Subject: [PATCH 172/343] list2array --- imagepy/menus/Image/Adjust/bleachCorrection_plg.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imagepy/menus/Image/Adjust/bleachCorrection_plg.py b/imagepy/menus/Image/Adjust/bleachCorrection_plg.py index 054a6b64..a8116d53 100644 --- a/imagepy/menus/Image/Adjust/bleachCorrection_plg.py +++ b/imagepy/menus/Image/Adjust/bleachCorrection_plg.py @@ -16,6 +16,7 @@ def simple_ratio(imgs): imgs[:,:,i+1] *= (ref/(imgs[:,:,i+1].sum() /x/y)).astype(imgs.dtype) return imgs + def exponential_fit(imgs): imgs_=imgs.astype('float32') x,y,z = imgs.shape @@ -49,7 +50,7 @@ class Plugin(Simple): def run(self, ips, imgs, para = None): Bconstant = para['Background'] - imgs_= imgs.transpose(1,2,0) + imgs_= np.array(imgs).transpose(1,2,0) if Bconstant != 0: for i in range (z): imgs_[:,:,i] -= Bconstant From 4591fc6a618d6501c12567b6ac3aa666128d67f2 Mon Sep 17 00:00:00 2001 From: weisongzhao Date: Sat, 25 Jan 2020 10:47:42 +0800 Subject: [PATCH 173/343] nothing --- imagepy/menus/Image/Adjust/bleachCorrection_plg.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/imagepy/menus/Image/Adjust/bleachCorrection_plg.py b/imagepy/menus/Image/Adjust/bleachCorrection_plg.py index a8116d53..75b0dfc0 100644 --- a/imagepy/menus/Image/Adjust/bleachCorrection_plg.py +++ b/imagepy/menus/Image/Adjust/bleachCorrection_plg.py @@ -58,13 +58,13 @@ def run(self, ips, imgs, para = None): print (para['Method']) if para['Method'] == 'Simple ratio': imgs_=simple_ratio(imgs_) - IPy.show_img(imgs_.transpose(2,0,1),'Corrected %s'%ips.title) + # IPy.show_img(imgs_.transpose(2,0,1),'Corrected %s'%ips.title) if para['Method'] == 'Exponential fit': imgs_=exponential_fit(imgs_) - IPy.show_img(imgs_.transpose(2,0,1),'Corrected %s'%ips.title) + # IPy.show_img(imgs_.transpose(2,0,1),'Corrected %s'%ips.title) if para['Method'] == 'Histogram match': imgs_=histogram_match(imgs_) - IPy.show_img(imgs_.transpose(2,0,1),'Corrected %s'%ips.title) + IPy.show_img(imgs_.transpose(2,0,1),'Corrected %s'%ips.title) From 634b3cde2a8a65ef532e6ef95a78c7434f717141 Mon Sep 17 00:00:00 2001 From: weisongzhao Date: Sat, 25 Jan 2020 15:25:49 +0800 Subject: [PATCH 174/343] bleach table and plot --- .../Image/Adjust/bleachCorrection_plg.py | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/imagepy/menus/Image/Adjust/bleachCorrection_plg.py b/imagepy/menus/Image/Adjust/bleachCorrection_plg.py index 75b0dfc0..60ef5c8f 100644 --- a/imagepy/menus/Image/Adjust/bleachCorrection_plg.py +++ b/imagepy/menus/Image/Adjust/bleachCorrection_plg.py @@ -8,14 +8,20 @@ from scipy.optimize import curve_fit # from .histogram_plg import like from skimage.exposure import histogram_matching +import matplotlib.pyplot as plt +import pandas as pd def simple_ratio(imgs): x,y,z = imgs.shape ref = imgs[:,:,1].sum()/x/y + ratio=np.zeros([z,2]) + ratio[0,0]=ref + ratio[0,1]=1 for i in range(z-1): - imgs[:,:,i+1] *= (ref/(imgs[:,:,i+1].sum() - /x/y)).astype(imgs.dtype) - return imgs + ratio[i+1,0]=(imgs[:,:,i+1].sum()/x/y) + ratio[i+1,1]=ref/(imgs[:,:,i+1].sum()/x/y) + imgs[:,:,i+1] *= ratio[i+1,1].astype(imgs.dtype) + return imgs, ratio def exponential_fit(imgs): imgs_=imgs.astype('float32') @@ -27,7 +33,7 @@ def exponential_fit(imgs): ratio=exponential_func(0, popt[0], popt[1], popt[2])/ \ exponential_func(i+1, popt[0], popt[1], popt[2]) imgs_[:,:,i+1] *= ratio - return imgs_.astype(imgs.dtype) + return imgs_.astype(imgs.dtype), popt, intensity def histogram_match(imgs): x,y,z = imgs.shape @@ -41,6 +47,7 @@ def exponential_func(t, ref, k, offset): class Plugin(Simple): title = 'Bleach Correction' + asyn = False note = ['8-bit','16-bit','stack'] para = {'Method':'Simple ratio', 'Background':0} view = [(list, 'Method', @@ -57,10 +64,24 @@ def run(self, ips, imgs, para = None): imgs_ = np.maximum(imgs_, 0) print (para['Method']) if para['Method'] == 'Simple ratio': - imgs_=simple_ratio(imgs_) + imgs_,ratio = simple_ratio(imgs_) + t = np.linspace(0, imgs_.shape[2]-1, imgs_.shape[2]) + index = ['Frame%s'%i for i in range(1,imgs_.shape[2]+1)] + columns = ['Mean value', 'Ratio'] + IPy.show_table(pd.DataFrame(ratio, index, columns), 'Log of simple ratio') + # IPy.show_img(imgs_.transpose(2,0,1),'Corrected %s'%ips.title) if para['Method'] == 'Exponential fit': - imgs_=exponential_fit(imgs_) + [imgs_, popt, intensity] = exponential_fit(imgs_) + t = np.linspace(0, imgs_.shape[2]-1, imgs_.shape[2]) + fitresult = exponential_func(t, popt[0], popt[1], popt[2]) + plt.plot(t,intensity,'r.',label='Experiment') + plt.plot(t,fitresult,'k',label= + 'Exponential fitted curve\n y=a*exp(-bx)+c\n a=%f\n b=%f\n c=%f' + %(popt[0],popt[1],popt[2])) + plt.title('Exponential fitted result') + plt.legend() + plt.show() # IPy.show_img(imgs_.transpose(2,0,1),'Corrected %s'%ips.title) if para['Method'] == 'Histogram match': imgs_=histogram_match(imgs_) From ae21d4331bb4fee3e844f06eeabd2ddc073fe96c Mon Sep 17 00:00:00 2001 From: weisongzhao Date: Sat, 25 Jan 2020 15:26:37 +0800 Subject: [PATCH 175/343] nothing --- imagepy/menus/Image/Adjust/bleachCorrection_plg.py | 1 - 1 file changed, 1 deletion(-) diff --git a/imagepy/menus/Image/Adjust/bleachCorrection_plg.py b/imagepy/menus/Image/Adjust/bleachCorrection_plg.py index 60ef5c8f..52a6e0fa 100644 --- a/imagepy/menus/Image/Adjust/bleachCorrection_plg.py +++ b/imagepy/menus/Image/Adjust/bleachCorrection_plg.py @@ -65,7 +65,6 @@ def run(self, ips, imgs, para = None): print (para['Method']) if para['Method'] == 'Simple ratio': imgs_,ratio = simple_ratio(imgs_) - t = np.linspace(0, imgs_.shape[2]-1, imgs_.shape[2]) index = ['Frame%s'%i for i in range(1,imgs_.shape[2]+1)] columns = ['Mean value', 'Ratio'] IPy.show_table(pd.DataFrame(ratio, index, columns), 'Log of simple ratio') From 72c778cebce63aff734123d51698d272a8e4c197 Mon Sep 17 00:00:00 2001 From: weisongzhao Date: Sat, 25 Jan 2020 15:27:15 +0800 Subject: [PATCH 176/343] nothing --- imagepy/menus/Image/Adjust/bleachCorrection_plg.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/imagepy/menus/Image/Adjust/bleachCorrection_plg.py b/imagepy/menus/Image/Adjust/bleachCorrection_plg.py index 52a6e0fa..df77bebc 100644 --- a/imagepy/menus/Image/Adjust/bleachCorrection_plg.py +++ b/imagepy/menus/Image/Adjust/bleachCorrection_plg.py @@ -68,8 +68,6 @@ def run(self, ips, imgs, para = None): index = ['Frame%s'%i for i in range(1,imgs_.shape[2]+1)] columns = ['Mean value', 'Ratio'] IPy.show_table(pd.DataFrame(ratio, index, columns), 'Log of simple ratio') - - # IPy.show_img(imgs_.transpose(2,0,1),'Corrected %s'%ips.title) if para['Method'] == 'Exponential fit': [imgs_, popt, intensity] = exponential_fit(imgs_) t = np.linspace(0, imgs_.shape[2]-1, imgs_.shape[2]) @@ -81,7 +79,6 @@ def run(self, ips, imgs, para = None): plt.title('Exponential fitted result') plt.legend() plt.show() - # IPy.show_img(imgs_.transpose(2,0,1),'Corrected %s'%ips.title) if para['Method'] == 'Histogram match': imgs_=histogram_match(imgs_) IPy.show_img(imgs_.transpose(2,0,1),'Corrected %s'%ips.title) From 32d2592f032bc398680175b6d4b42729441cf9dc Mon Sep 17 00:00:00 2001 From: weisongzhao Date: Sat, 25 Jan 2020 16:16:18 +0800 Subject: [PATCH 177/343] normalize --- imagepy/menus/Image/Adjust/__init__.py | 2 +- imagepy/menus/Image/Adjust/normalize_plg.py | 35 +++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 imagepy/menus/Image/Adjust/normalize_plg.py diff --git a/imagepy/menus/Image/Adjust/__init__.py b/imagepy/menus/Image/Adjust/__init__.py index ca20c292..7e84df99 100644 --- a/imagepy/menus/Image/Adjust/__init__.py +++ b/imagepy/menus/Image/Adjust/__init__.py @@ -1 +1 @@ -catlog = ['threshold_plg','graystairs_plg','brightcons_plg','curve_plg','-','bleachCorrection_plg','-','colorbalance_plg','colorstairs_plg','-','histogram_plgs'] \ No newline at end of file +catlog = ['threshold_plg','graystairs_plg','brightcons_plg','curve_plg','-','bleachCorrection_plg','normalize_plg','-','colorbalance_plg','colorstairs_plg','-','histogram_plgs'] \ No newline at end of file diff --git a/imagepy/menus/Image/Adjust/normalize_plg.py b/imagepy/menus/Image/Adjust/normalize_plg.py new file mode 100644 index 00000000..d0d18c16 --- /dev/null +++ b/imagepy/menus/Image/Adjust/normalize_plg.py @@ -0,0 +1,35 @@ +""" +Created on Sun Jan 23 11:53:00 2020 +@author: weisong +""" +from imagepy.core.engine import Simple +import numpy as np +from imagepy import IPy + +class Plugin(Simple): + title = 'Normalize' + note = ['8-bit','16-bit','int','float','stack'] + para = {'if3d': False, 'Sb':False} + view = [(bool, 'if3d', '3D stack'), + (bool, 'Sb', 'Subtract background')] + + def run(self, ips, imgs, para = None): + # .astype('float64') + imgs_= np.array(imgs).astype('float64').transpose(1,2,0) + x, y, z = imgs_.shape + if para['if3d']: + if para['Sb']: + imgs_ -= imgs_.min() + imgs_ = imgs_ / imgs_.max() + else: + if para['Sb']: + for i in range(z): + imgs_[:,:,i] -= imgs_[:,:,i].min() + for i in range(z): + imgs_[:,:,i] = imgs_[:,:,i] / imgs_[:,:,i].max() + if imgs.dtype == np.uint8: + imgs[:] = (255*imgs_).astype(imgs.dtype).transpose(2,0,1) + elif imgs.dtype == np.uint16: + imgs[:] = (65535*imgs_).astype(imgs.dtype).transpose(2,0,1) + else: + imgs[:] = (imgs_).astype(imgs.dtype).transpose(2,0,1) From 2e5be88b44dafd7e71fd9f921cb3f1eea0fe4f16 Mon Sep 17 00:00:00 2001 From: weisongzhao Date: Sat, 25 Jan 2020 16:51:37 +0800 Subject: [PATCH 178/343] nothing --- imagepy/menus/Image/Adjust/normalize_plg.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/imagepy/menus/Image/Adjust/normalize_plg.py b/imagepy/menus/Image/Adjust/normalize_plg.py index d0d18c16..9863841d 100644 --- a/imagepy/menus/Image/Adjust/normalize_plg.py +++ b/imagepy/menus/Image/Adjust/normalize_plg.py @@ -1,5 +1,5 @@ """ -Created on Sun Jan 23 11:53:00 2020 +Created on Sun Jan 25 9:00:00 2020 @author: weisong """ from imagepy.core.engine import Simple @@ -8,13 +8,12 @@ class Plugin(Simple): title = 'Normalize' - note = ['8-bit','16-bit','int','float','stack'] + note = ['8-bit','16-bit','float','stack'] para = {'if3d': False, 'Sb':False} view = [(bool, 'if3d', '3D stack'), (bool, 'Sb', 'Subtract background')] def run(self, ips, imgs, para = None): - # .astype('float64') imgs_= np.array(imgs).astype('float64').transpose(1,2,0) x, y, z = imgs_.shape if para['if3d']: From 68b8ce7b04f745fdd7d2f8b8957cde7d4be68f9c Mon Sep 17 00:00:00 2001 From: weisongzhao Date: Sat, 25 Jan 2020 16:53:43 +0800 Subject: [PATCH 179/343] nothing --- imagepy/menus/Plugins/temporal_plg.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/imagepy/menus/Plugins/temporal_plg.py b/imagepy/menus/Plugins/temporal_plg.py index bd09cad7..08dd9a45 100644 --- a/imagepy/menus/Plugins/temporal_plg.py +++ b/imagepy/menus/Plugins/temporal_plg.py @@ -34,7 +34,7 @@ class Plugin(Simple): para = {'LUT':'Jet', 'Start image':1, 'End image': 2, - 'Creat time color scale bar':True} + 'Creatbar':True} def load(self, ips): self.slength = len(ips.imgs) @@ -42,14 +42,14 @@ def load(self, ips): self.view = [(list, 'LUT', list(ColorManager.luts.keys()), str, 'LUT',''), (int, 'Start image', (1,self.slength),0,'Start image','1~%d'%self.slength), (int, 'End image', (2,self.slength),0,'End image','start~%d'%self.slength), - (bool, 'Creat time color scale bar', 'Creat time color scale bar')] + (bool, 'Creatbar', 'Creat time color scale bar')] return True def run(self, ips, imgs, para = None): cmap = ColorManager.luts[ para['LUT'] ] imglut = color_code(imgs[para['Start image']-1: para['End image']], cmap) IPy.show_img([imglut],'Color-coded %s'%ips.title) - if para['Creat time color scale bar']: + if para['Creatbar']: cmapshow = np.ones([32,256,3])*cmap IPy.show_img([cmapshow.astype('uint8')],'Color bar') From d3b95103ccbdcc07ffda1b95655323fe2051933e Mon Sep 17 00:00:00 2001 From: weisongzhao Date: Sat, 25 Jan 2020 17:17:01 +0800 Subject: [PATCH 180/343] stretch contrast --- .../menus/Image/Adjust/bleachCorrection_plg.py | 1 + imagepy/menus/Image/Adjust/enhanceContrast.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 imagepy/menus/Image/Adjust/enhanceContrast.py diff --git a/imagepy/menus/Image/Adjust/bleachCorrection_plg.py b/imagepy/menus/Image/Adjust/bleachCorrection_plg.py index df77bebc..0ca33ceb 100644 --- a/imagepy/menus/Image/Adjust/bleachCorrection_plg.py +++ b/imagepy/menus/Image/Adjust/bleachCorrection_plg.py @@ -40,6 +40,7 @@ def histogram_match(imgs): for i in range(z-1): imgs[:,:,i+1] = histogram_matching.match_histograms( imgs[:,:,i+1],imgs[:,:,0]) + print(imgs.dtype) return imgs def exponential_func(t, ref, k, offset): diff --git a/imagepy/menus/Image/Adjust/enhanceContrast.py b/imagepy/menus/Image/Adjust/enhanceContrast.py new file mode 100644 index 00000000..fbe58fa4 --- /dev/null +++ b/imagepy/menus/Image/Adjust/enhanceContrast.py @@ -0,0 +1,16 @@ +""" +Created on Sun Jan 25 17:00:00 2020 +@author: weisong +""" +from imagepy.core.engine import Filter +from skimage import exposure + +class Gaussian(Filter): + title = 'Enhance contrast' + note = ['all', 'auto_msk', 'auto_snap','preview'] + para = {'percentage': 0.3} + view = [(float, 'percentage', (0,100), 1, 'Saturated pixels', '%')] + + def run(self, ips, snap, img, para = None): + p2, p98 = np.percentile(snap, (0, 100 - para['percentage'])) + exposure.rescale_intensity(snap, in_range=(p2, p98)) From cf37cbb679f828d978a56e32186103701af4cb50 Mon Sep 17 00:00:00 2001 From: weisongzhao Date: Sat, 25 Jan 2020 17:17:48 +0800 Subject: [PATCH 181/343] stretch contrast --- imagepy/menus/Image/Adjust/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/menus/Image/Adjust/__init__.py b/imagepy/menus/Image/Adjust/__init__.py index 7e84df99..af5b5b05 100644 --- a/imagepy/menus/Image/Adjust/__init__.py +++ b/imagepy/menus/Image/Adjust/__init__.py @@ -1 +1 @@ -catlog = ['threshold_plg','graystairs_plg','brightcons_plg','curve_plg','-','bleachCorrection_plg','normalize_plg','-','colorbalance_plg','colorstairs_plg','-','histogram_plgs'] \ No newline at end of file +catlog = ['threshold_plg','graystairs_plg','brightcons_plg','curve_plg','-','bleachCorrection_plg','enhanceContrast_plg','normalize_plg','-','colorbalance_plg','colorstairs_plg','-','histogram_plgs'] \ No newline at end of file From 73c1dff33df9d3017f3bd077722c1af103ad564f Mon Sep 17 00:00:00 2001 From: weisongzhao Date: Sat, 25 Jan 2020 17:19:05 +0800 Subject: [PATCH 182/343] stretch contrast --- .../Image/Adjust/{enhanceContrast.py => enhanceContrast_plg.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename imagepy/menus/Image/Adjust/{enhanceContrast.py => enhanceContrast_plg.py} (100%) diff --git a/imagepy/menus/Image/Adjust/enhanceContrast.py b/imagepy/menus/Image/Adjust/enhanceContrast_plg.py similarity index 100% rename from imagepy/menus/Image/Adjust/enhanceContrast.py rename to imagepy/menus/Image/Adjust/enhanceContrast_plg.py From b8b82ebebd9333efaa33795d567e2b2e171fb0e3 Mon Sep 17 00:00:00 2001 From: weisongzhao Date: Sat, 25 Jan 2020 17:20:27 +0800 Subject: [PATCH 183/343] stretch contrast --- imagepy/menus/Image/Adjust/enhanceContrast_plg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/menus/Image/Adjust/enhanceContrast_plg.py b/imagepy/menus/Image/Adjust/enhanceContrast_plg.py index fbe58fa4..cacb0aee 100644 --- a/imagepy/menus/Image/Adjust/enhanceContrast_plg.py +++ b/imagepy/menus/Image/Adjust/enhanceContrast_plg.py @@ -5,7 +5,7 @@ from imagepy.core.engine import Filter from skimage import exposure -class Gaussian(Filter): +class Plugin(Filter): title = 'Enhance contrast' note = ['all', 'auto_msk', 'auto_snap','preview'] para = {'percentage': 0.3} From 89c2b436c7d2807e0f0032b3184a7b7dcd19bb70 Mon Sep 17 00:00:00 2001 From: weisongzhao Date: Sat, 25 Jan 2020 17:21:45 +0800 Subject: [PATCH 184/343] np --- imagepy/menus/Image/Adjust/enhanceContrast_plg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/menus/Image/Adjust/enhanceContrast_plg.py b/imagepy/menus/Image/Adjust/enhanceContrast_plg.py index cacb0aee..0126b430 100644 --- a/imagepy/menus/Image/Adjust/enhanceContrast_plg.py +++ b/imagepy/menus/Image/Adjust/enhanceContrast_plg.py @@ -4,7 +4,7 @@ """ from imagepy.core.engine import Filter from skimage import exposure - +import numpy as np class Plugin(Filter): title = 'Enhance contrast' note = ['all', 'auto_msk', 'auto_snap','preview'] From c6098a02a766259ee3c3f226d32e0ac07327559c Mon Sep 17 00:00:00 2001 From: weisongzhao Date: Sat, 25 Jan 2020 17:43:50 +0800 Subject: [PATCH 185/343] duplicate --- imagepy/menus/Image/Adjust/enhanceContrast_plg.py | 5 +++-- imagepy/menus/Image/duplicate_plg.py | 9 ++++++++- imagepy/menus/Plugins/temporal_plg.py | 13 ++++++++----- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/imagepy/menus/Image/Adjust/enhanceContrast_plg.py b/imagepy/menus/Image/Adjust/enhanceContrast_plg.py index 0126b430..c16c8dda 100644 --- a/imagepy/menus/Image/Adjust/enhanceContrast_plg.py +++ b/imagepy/menus/Image/Adjust/enhanceContrast_plg.py @@ -5,6 +5,7 @@ from imagepy.core.engine import Filter from skimage import exposure import numpy as np + class Plugin(Filter): title = 'Enhance contrast' note = ['all', 'auto_msk', 'auto_snap','preview'] @@ -12,5 +13,5 @@ class Plugin(Filter): view = [(float, 'percentage', (0,100), 1, 'Saturated pixels', '%')] def run(self, ips, snap, img, para = None): - p2, p98 = np.percentile(snap, (0, 100 - para['percentage'])) - exposure.rescale_intensity(snap, in_range=(p2, p98)) + up, down = np.percentile(snap, (0, 100 - para['percentage'])) + exposure.rescale_intensity(snap, in_range=(up, down)) diff --git a/imagepy/menus/Image/duplicate_plg.py b/imagepy/menus/Image/duplicate_plg.py index 7877135f..47c6fd86 100644 --- a/imagepy/menus/Image/duplicate_plg.py +++ b/imagepy/menus/Image/duplicate_plg.py @@ -14,12 +14,18 @@ class Duplicate(Simple): title = 'Duplicate' note = ['all'] - para = {'name':'Undefined','stack':True} + # para = {'name':'Undefined','start':1,'end':2,'stack':True} def load(self, ips): + self.slength = len(ips.imgs) + self.para = {'name':'Undefined','start':1,'end':self.slength,'stack':True} self.para['name'] = ips.title+'-copy' self.view = [(str, 'name', 'Name', '')] if ips.get_nslices()>1: + self.view.append((int, 'start', + (1,self.slength),0,'Start slice','1~%d'%self.slength)) + self.view.append((int, 'end', + (1,self.slength),0,'End slice','1~%d'%self.slength)) self.view.append((bool, 'stack', 'duplicate stack')) return True #process @@ -42,6 +48,7 @@ def run(self, ips, imgs, para = None): ipsd.backimg = ips.backimg[sr, sc] ''' elif ips.get_nslices()>1 and self.para['stack']: + imgs=imgs[para['start']-1: para['end']] if ips.roi == None: if ips.is3d:imgs=imgs.copy() else:imgs = [i.copy() for i in imgs] diff --git a/imagepy/menus/Plugins/temporal_plg.py b/imagepy/menus/Plugins/temporal_plg.py index 08dd9a45..925d4690 100644 --- a/imagepy/menus/Plugins/temporal_plg.py +++ b/imagepy/menus/Plugins/temporal_plg.py @@ -31,13 +31,16 @@ def color_code(img, lut): class Plugin(Simple): title = 'Temporal color-code' note = ['all', 'stack'] - para = {'LUT':'Jet', - 'Start image':1, - 'End image': 2, - 'Creatbar':True} - + # para = {'LUT':'Jet', + # 'Start image':1, + # 'End image': 2, + # 'Creatbar':True} def load(self, ips): self.slength = len(ips.imgs) + self.para = {'LUT':'Jet', + 'Start image':1, + 'End image': self.slength, + 'Creatbar':True} self.para['End image'] = self.slength self.view = [(list, 'LUT', list(ColorManager.luts.keys()), str, 'LUT',''), (int, 'Start image', (1,self.slength),0,'Start image','1~%d'%self.slength), From 3a36e5ddd1fe268b6a547e94dab65c8844f60df7 Mon Sep 17 00:00:00 2001 From: weisongzhao Date: Sat, 25 Jan 2020 17:48:10 +0800 Subject: [PATCH 186/343] para --- imagepy/menus/Image/duplicate_plg.py | 4 ++-- imagepy/menus/Plugins/temporal_plg.py | 12 ++++-------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/imagepy/menus/Image/duplicate_plg.py b/imagepy/menus/Image/duplicate_plg.py index 47c6fd86..11757f92 100644 --- a/imagepy/menus/Image/duplicate_plg.py +++ b/imagepy/menus/Image/duplicate_plg.py @@ -14,12 +14,12 @@ class Duplicate(Simple): title = 'Duplicate' note = ['all'] - # para = {'name':'Undefined','start':1,'end':2,'stack':True} + para = {'name':'Undefined','start':1,'end':2,'stack':True} def load(self, ips): self.slength = len(ips.imgs) - self.para = {'name':'Undefined','start':1,'end':self.slength,'stack':True} self.para['name'] = ips.title+'-copy' + self.para['end'] = self.slength self.view = [(str, 'name', 'Name', '')] if ips.get_nslices()>1: self.view.append((int, 'start', diff --git a/imagepy/menus/Plugins/temporal_plg.py b/imagepy/menus/Plugins/temporal_plg.py index 925d4690..fb17bc2a 100644 --- a/imagepy/menus/Plugins/temporal_plg.py +++ b/imagepy/menus/Plugins/temporal_plg.py @@ -31,16 +31,12 @@ def color_code(img, lut): class Plugin(Simple): title = 'Temporal color-code' note = ['all', 'stack'] - # para = {'LUT':'Jet', - # 'Start image':1, - # 'End image': 2, - # 'Creatbar':True} - def load(self, ips): - self.slength = len(ips.imgs) - self.para = {'LUT':'Jet', + para = {'LUT':'Jet', 'Start image':1, - 'End image': self.slength, + 'End image': 2, 'Creatbar':True} + def load(self, ips): + self.slength = len(ips.imgs) self.para['End image'] = self.slength self.view = [(list, 'LUT', list(ColorManager.luts.keys()), str, 'LUT',''), (int, 'Start image', (1,self.slength),0,'Start image','1~%d'%self.slength), From a5f5783be09de717f7098e7dc5ddf2cc30a24f7d Mon Sep 17 00:00:00 2001 From: weisongzhao Date: Sat, 25 Jan 2020 17:55:03 +0800 Subject: [PATCH 187/343] nothing --- imagepy/menus/Image/Adjust/enhanceContrast_plg.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/imagepy/menus/Image/Adjust/enhanceContrast_plg.py b/imagepy/menus/Image/Adjust/enhanceContrast_plg.py index c16c8dda..db7c45aa 100644 --- a/imagepy/menus/Image/Adjust/enhanceContrast_plg.py +++ b/imagepy/menus/Image/Adjust/enhanceContrast_plg.py @@ -10,8 +10,8 @@ class Plugin(Filter): title = 'Enhance contrast' note = ['all', 'auto_msk', 'auto_snap','preview'] para = {'percentage': 0.3} - view = [(float, 'percentage', (0,100), 1, 'Saturated pixels', '%')] + view = [(float, 'percentage', (0,100), 2, 'Saturated pixels', '%')] def run(self, ips, snap, img, para = None): - up, down = np.percentile(snap, (0, 100 - para['percentage'])) - exposure.rescale_intensity(snap, in_range=(up, down)) + up, down = np.percentile(snap, (0, 100 - para['percentage']/100)) + return exposure.rescale_intensity(snap, in_range=(up, down)) From c990ab3d5a9c913a88dea2f28fb109d52c8e7c3c Mon Sep 17 00:00:00 2001 From: Laurent Thomas Date: Tue, 28 Jan 2020 09:38:21 +0100 Subject: [PATCH 188/343] No need to manually install shapely --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5f918f78..47946596 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,8 @@ Simply unzip the archive and run the ImagePy.bat file. This will open a command line window and open the GUI of ImagePy. ### - Using pip -In a command-prompt type `pip install imagepy`. -On Windows you currently need to first install shapely using conda. +In a command-prompt type `pip install imagepy`. +~~On Windows you currently need to first install shapely using conda.~~ This should also work for windows, now that shapely is available via pip. Once installed, ImagePy can be run by typing `python -m imagepy` in a command prompt. From 4c6568bfe7c4022dc180ca59c7cde4e486a34281 Mon Sep 17 00:00:00 2001 From: weisongzhao Date: Tue, 28 Jan 2020 21:02:29 +0800 Subject: [PATCH 189/343] current image bleach correction --- .../Image/Adjust/bleachCorrection_plg.py | 139 +++++++++--------- .../menus/Image/Adjust/enhanceContrast_plg.py | 4 +- imagepy/menus/Image/Adjust/normalize_plg.py | 1 - imagepy/menus/Plugins/temporal_plg.py | 12 -- 4 files changed, 73 insertions(+), 83 deletions(-) diff --git a/imagepy/menus/Image/Adjust/bleachCorrection_plg.py b/imagepy/menus/Image/Adjust/bleachCorrection_plg.py index 0ca33ceb..1771c497 100644 --- a/imagepy/menus/Image/Adjust/bleachCorrection_plg.py +++ b/imagepy/menus/Image/Adjust/bleachCorrection_plg.py @@ -6,83 +6,86 @@ import numpy as np from imagepy import IPy from scipy.optimize import curve_fit -# from .histogram_plg import like from skimage.exposure import histogram_matching import matplotlib.pyplot as plt import pandas as pd -def simple_ratio(imgs): - x,y,z = imgs.shape - ref = imgs[:,:,1].sum()/x/y - ratio=np.zeros([z,2]) - ratio[0,0]=ref - ratio[0,1]=1 - for i in range(z-1): - ratio[i+1,0]=(imgs[:,:,i+1].sum()/x/y) - ratio[i+1,1]=ref/(imgs[:,:,i+1].sum()/x/y) - imgs[:,:,i+1] *= ratio[i+1,1].astype(imgs.dtype) - return imgs, ratio - -def exponential_fit(imgs): - imgs_=imgs.astype('float32') - x,y,z = imgs.shape - t = np.linspace(0, z-1, z) - intensity = (imgs.sum(1).sum(0)/x/y) - popt, pcov = curve_fit(exponential_func, t, intensity) - for i in range(z-1): - ratio=exponential_func(0, popt[0], popt[1], popt[2])/ \ - exponential_func(i+1, popt[0], popt[1], popt[2]) - imgs_[:,:,i+1] *= ratio - return imgs_.astype(imgs.dtype), popt, intensity - -def histogram_match(imgs): - x,y,z = imgs.shape - for i in range(z-1): - imgs[:,:,i+1] = histogram_matching.match_histograms( - imgs[:,:,i+1],imgs[:,:,0]) - print(imgs.dtype) - return imgs +def copy(imgs): + if isinstance(imgs, list): + return [np.zeros_like(imgs[0])] + else: return np.zeros_like(imgs) def exponential_func(t, ref, k, offset): return ref * np.exp(- k * t) + offset -class Plugin(Simple): - title = 'Bleach Correction' - asyn = False - note = ['8-bit','16-bit','stack'] - para = {'Method':'Simple ratio', 'Background':0} - view = [(list, 'Method', - ['Simple ratio','Exponential fit','Histogram match'], - str, 'Correction Method',''), - (int,'Background',(0,65535),0,'Background intensity','')] +def simple_ratio(imgs, back=0, inplace=True, out=print): + if isinstance(back, int): back=imgs[back] + buf = imgs if inplace else copy(imgs) + z, (x, y) = len(imgs), imgs[0].shape + values, k0 = np.zeros(z), back.sum()/x/y + lim = 255 if imgs[0].dtype.type==np.uint8 else 65535 + for i in range(z): + values[i] = imgs[i].sum()/x/y + np.clip(imgs[i], 0, lim/(k0/values[i]), out=buf[i]) + np.multiply(buf[i], k0/values[i], out=buf[i], casting='unsafe') + out(i, z) + return buf, values, k0/values - def run(self, ips, imgs, para = None): - Bconstant = para['Background'] - imgs_= np.array(imgs).transpose(1,2,0) - if Bconstant != 0: - for i in range (z): - imgs_[:,:,i] -= Bconstant - imgs_ = np.maximum(imgs_, 0) - print (para['Method']) - if para['Method'] == 'Simple ratio': - imgs_,ratio = simple_ratio(imgs_) - index = ['Frame%s'%i for i in range(1,imgs_.shape[2]+1)] - columns = ['Mean value', 'Ratio'] - IPy.show_table(pd.DataFrame(ratio, index, columns), 'Log of simple ratio') - if para['Method'] == 'Exponential fit': - [imgs_, popt, intensity] = exponential_fit(imgs_) - t = np.linspace(0, imgs_.shape[2]-1, imgs_.shape[2]) - fitresult = exponential_func(t, popt[0], popt[1], popt[2]) - plt.plot(t,intensity,'r.',label='Experiment') - plt.plot(t,fitresult,'k',label= - 'Exponential fitted curve\n y=a*exp(-bx)+c\n a=%f\n b=%f\n c=%f' - %(popt[0],popt[1],popt[2])) - plt.title('Exponential fitted result') - plt.legend() - plt.show() - if para['Method'] == 'Histogram match': - imgs_=histogram_match(imgs_) - IPy.show_img(imgs_.transpose(2,0,1),'Corrected %s'%ips.title) +def exponential_fit(imgs, inplace=True, out=print): + buf = imgs if inplace else copy(imgs) + z, (x, y) = len(imgs), imgs[0].shape + intensity = [i.sum()/x/y for i in imgs] + popt, pcov = curve_fit(exponential_func, np.arange(z), intensity) + k0 = exponential_func(0, popt[0], popt[1], popt[2]) + rst = exponential_func(np.arange(z), popt[0], popt[1], popt[2]) + lim = 255 if imgs[0].dtype.type==np.uint8 else 65535 + for i in range(z): + np.clip(imgs[i], 0, lim/(rst[i]/k0), out=buf[i]) + np.multiply(buf[i], rst[i]/k0, out=buf[i], casting='unsafe') + out(i, z) + return buf, popt, intensity, rst +def histogram_match(imgs, back=0, inplace=True, out=print): + if isinstance(back, int): back=imgs[back] + buf = imgs if inplace else copy(imgs) + z, (x, y) = len(imgs), imgs[0].shape + for i in range(z): + buf[i] = histogram_matching.match_histograms(imgs[i], back) + out(i, z) + return buf +def plot(popt, intensity, fitresult): + t = np.arange(len(intensity)) + plt.plot(t, intensity,'r.',label='Experiment') + plt.plot(t, fitresult,'k',label= + 'Exponential fitted curve\n y=a*exp(-bx)+c\n a=%f\n b=%f\n c=%f'%tuple(popt)) + plt.title('Exponential fitted result') + plt.legend() + plt.show() +def plot_after(popt, intensity, fitresult): + import wx + wx.CallAfter(plot, popt, intensity, fitresult) + +class Plugin(Simple): + title = 'Bleach Correction' + note = ['8-bit','16-bit','stack'] + para = {'method':'Simple Ratio', 'new':True} + view = [(list, 'method', ['Simple Ratio','Exponential Fit','Histogram Match'], + str, 'Correction Method',''), + (bool, 'new', 'show new window'), + ('lab', 'lab', 'Correct intensity based on your current slice!')] + + def run(self, ips, imgs, para = None): + if para['method'] == 'Simple Ratio': + rst, value, ratio = simple_ratio(imgs, ips.img, not para['new'], self.progress) + body = pd.DataFrame({'Mean value': value, 'Ratio':ratio}) + IPy.show_table(body, '%s-simple ratio'%ips.title) + if para['method'] == 'Exponential Fit': + rst, popt, intensity, fitrst = exponential_fit(imgs, not para['new'], self.progress) + plot_after(popt, intensity, fitrst) + body = {'Intensity':intensity, 'Fit':fitrst} + IPy.show_table(pd.DataFrame(body), '%s-exp fit'%ips.title) + if para['method'] == 'Histogram Match': + rst = histogram_match(imgs, ips.img, not para['new'], self.progress) + if para['new']: IPy.show_img(rst, '%s-corrected'%ips.title) \ No newline at end of file diff --git a/imagepy/menus/Image/Adjust/enhanceContrast_plg.py b/imagepy/menus/Image/Adjust/enhanceContrast_plg.py index db7c45aa..a0373af3 100644 --- a/imagepy/menus/Image/Adjust/enhanceContrast_plg.py +++ b/imagepy/menus/Image/Adjust/enhanceContrast_plg.py @@ -10,8 +10,8 @@ class Plugin(Filter): title = 'Enhance contrast' note = ['all', 'auto_msk', 'auto_snap','preview'] para = {'percentage': 0.3} - view = [(float, 'percentage', (0,100), 2, 'Saturated pixels', '%')] + view = [(float, 'percentage', (0,100), 4, 'Saturated pixels', '%')] def run(self, ips, snap, img, para = None): - up, down = np.percentile(snap, (0, 100 - para['percentage']/100)) + up, down = np.percentile(snap, (0, 100 - para['percentage'])) return exposure.rescale_intensity(snap, in_range=(up, down)) diff --git a/imagepy/menus/Image/Adjust/normalize_plg.py b/imagepy/menus/Image/Adjust/normalize_plg.py index 9863841d..e9ef081e 100644 --- a/imagepy/menus/Image/Adjust/normalize_plg.py +++ b/imagepy/menus/Image/Adjust/normalize_plg.py @@ -15,7 +15,6 @@ class Plugin(Simple): def run(self, ips, imgs, para = None): imgs_= np.array(imgs).astype('float64').transpose(1,2,0) - x, y, z = imgs_.shape if para['if3d']: if para['Sb']: imgs_ -= imgs_.min() diff --git a/imagepy/menus/Plugins/temporal_plg.py b/imagepy/menus/Plugins/temporal_plg.py index fb17bc2a..017e78b3 100644 --- a/imagepy/menus/Plugins/temporal_plg.py +++ b/imagepy/menus/Plugins/temporal_plg.py @@ -7,18 +7,6 @@ from imagepy.core.manager import ColorManager from imagepy import IPy -def color_code(imgs,cmap,start,end): - x,y,z = imgs.shape - z = end - start - imgLUT = np.zeros([x,y,3],'float32') - for i in range(start, end): - for rgb in range (2): - imgLUT[:,:,rgb] += imgs[:,:,i] * \ - cmap[np.floor((i+1)*256/z).astype(np.int)-1,rgb] - for rgb in range(2): - imgLUT[:,:,rgb] = 255 * imgLUT[:,:,rgb] / imgLUT[:,:,rgb].max() - return imgLUT - def color_code(img, lut): idx = np.linspace(0,255,len(img)).astype(int) cs = lut[idx].astype(np.uint32) From 43cd5c4dcb9d6fefdcf11b8b9e9c0d56e11fab1e Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 29 Jan 2020 14:10:26 +0800 Subject: [PATCH 190/343] adjust --- imagepy/core/util/fileio.py | 1 - imagepy/core/wraper/imageplus.py | 1 - .../menus/Image/Adjust/enhanceContrast_plg.py | 8 ++-- imagepy/menus/Image/Adjust/normalize_plg.py | 43 ++++++++++--------- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/imagepy/core/util/fileio.py b/imagepy/core/util/fileio.py index af3e44e6..e87ad4dd 100644 --- a/imagepy/core/util/fileio.py +++ b/imagepy/core/util/fileio.py @@ -8,7 +8,6 @@ def show_img(img, title): if isinstance(img, list): return IPy.show_img(img, title) - # if img.dtype!=np.uint8 and img.ndim>2 and img.shape[2]!=3: if img.ndim>2 and img.shape[2]!=3: return IPy.show_img(img, title) if img.dtype==np.uint8 and img.ndim==3 and img.shape[2]==4: diff --git a/imagepy/core/wraper/imageplus.py b/imagepy/core/wraper/imageplus.py index ffd236f1..2b5d4a65 100644 --- a/imagepy/core/wraper/imageplus.py +++ b/imagepy/core/wraper/imageplus.py @@ -178,7 +178,6 @@ def lookup(self, img=None): def swap(self): - print(type(self.snap), type(self.imgs[self.cur]), self.cur) if self.snap is None:return if isinstance(self.imgs, list): self.snap, self.imgs[self.cur] = self.imgs[self.cur], self.snap diff --git a/imagepy/menus/Image/Adjust/enhanceContrast_plg.py b/imagepy/menus/Image/Adjust/enhanceContrast_plg.py index a0373af3..2389842e 100644 --- a/imagepy/menus/Image/Adjust/enhanceContrast_plg.py +++ b/imagepy/menus/Image/Adjust/enhanceContrast_plg.py @@ -7,11 +7,11 @@ import numpy as np class Plugin(Filter): - title = 'Enhance contrast' + title = 'Enhance Contrast' note = ['all', 'auto_msk', 'auto_snap','preview'] - para = {'percentage': 0.3} - view = [(float, 'percentage', (0,100), 4, 'Saturated pixels', '%')] + para = {'percentage': 5} + view = [(float, 'percentage', (0,100), 2, 'Saturated pixels', '%')] def run(self, ips, snap, img, para = None): - up, down = np.percentile(snap, (0, 100 - para['percentage'])) + up, down = np.percentile(snap, (para['percentage']/2, 100-para['percentage']/2)) return exposure.rescale_intensity(snap, in_range=(up, down)) diff --git a/imagepy/menus/Image/Adjust/normalize_plg.py b/imagepy/menus/Image/Adjust/normalize_plg.py index e9ef081e..99083233 100644 --- a/imagepy/menus/Image/Adjust/normalize_plg.py +++ b/imagepy/menus/Image/Adjust/normalize_plg.py @@ -8,26 +8,27 @@ class Plugin(Simple): title = 'Normalize' - note = ['8-bit','16-bit','float','stack'] - para = {'if3d': False, 'Sb':False} - view = [(bool, 'if3d', '3D stack'), - (bool, 'Sb', 'Subtract background')] + note = ['8-bit','16-bit','float'] + para = {'is3d': False, 'sb':True} + view = [(bool, 'is3d', '3D stack'), + (bool, 'sb', 'Subtract background')] def run(self, ips, imgs, para = None): - imgs_= np.array(imgs).astype('float64').transpose(1,2,0) - if para['if3d']: - if para['Sb']: - imgs_ -= imgs_.min() - imgs_ = imgs_ / imgs_.max() - else: - if para['Sb']: - for i in range(z): - imgs_[:,:,i] -= imgs_[:,:,i].min() - for i in range(z): - imgs_[:,:,i] = imgs_[:,:,i] / imgs_[:,:,i].max() - if imgs.dtype == np.uint8: - imgs[:] = (255*imgs_).astype(imgs.dtype).transpose(2,0,1) - elif imgs.dtype == np.uint16: - imgs[:] = (65535*imgs_).astype(imgs.dtype).transpose(2,0,1) - else: - imgs[:] = (imgs_).astype(imgs.dtype).transpose(2,0,1) + lim = np.zeros([len(imgs), 2], dtype=imgs[0].dtype) + dic = {np.uint8:255, np.uint16:65535, np.float32:1, np.float64:1} + + IPy.set_info('count range ...') + for i in range(len(imgs)): + lim[i] = imgs[i].min(), imgs[i].max() + self.progress(i, len(imgs)) + + maxvalue = dic[imgs[0].dtype.type] + if not para['sb']: lim[:,0] = 0 + rg = lim[:,0].min(), lim[:,1].max() + if para['is3d']: lim[:] = rg + IPy.set_info('adjust range ...') + for i in range(len(imgs)): + if para['sb']: imgs[i] -= lim[i,0] + np.multiply(imgs[i], maxvalue/(lim[i].ptp()), out=imgs[i], casting='unsafe') + self.progress(i, len(imgs)) + ips.range = 0, maxvalue \ No newline at end of file From 2a521f64167d7e32a743b1b79f0d9d12cf2e1717 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 29 Jan 2020 22:28:31 +0800 Subject: [PATCH 191/343] markdown math --- imagepy/data/markdown.css | 150 +++++++++++++++++++++++++++ imagepy/ui/mkdownwindow.py | 202 ++++++------------------------------- requirements.txt | 1 + setup.py | 1 + 4 files changed, 185 insertions(+), 169 deletions(-) create mode 100644 imagepy/data/markdown.css diff --git a/imagepy/data/markdown.css b/imagepy/data/markdown.css new file mode 100644 index 00000000..179ac447 --- /dev/null +++ b/imagepy/data/markdown.css @@ -0,0 +1,150 @@ +h1, +h2, +h3, +h4, +h5, +h6, +p, +blockquote { + margin: 0; + padding: 0; +} +body { + font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", Arial, sans-serif; + font-size: 13px; + line-height: 18px; + color: #737373; + background-color: white; + margin: 10px 13px 10px 13px; +} +table { + margin: 10px 0 15px 0; + border-collapse: collapse; +} +td,th { + border: 1px solid #ddd; + padding: 3px 10px; +} +th { + padding: 5px 10px; +} + +a { + color: #0069d6; +} +a:hover { + color: #0050a3; + text-decoration: none; +} +a img { + border: none; +} +p { + margin-bottom: 9px; +} +p img { + width:100%%; +} +h1, +h2, +h3, +h4, +h5, +h6 { + color: #404040; + line-height: 36px; +} +h1 { + margin-bottom: 18px; + font-size: 30px; +} +h2 { + font-size: 24px; +} +h3 { + font-size: 18px; +} +h4 { + font-size: 16px; +} +h5 { + font-size: 14px; +} +h6 { + font-size: 13px; +} +hr { + margin: 0 0 19px; + border: 0; + border-bottom: 1px solid #ccc; +} +blockquote { + padding: 13px 13px 21px 15px; + margin-bottom: 18px; + font-family:georgia,serif; + font-style: italic; +} +blockquote:before { + content:"\201C"; + font-size:40px; + margin-left:-10px; + font-family:georgia,serif; + color:#eee; +} +blockquote p { + font-size: 14px; + font-weight: 300; + line-height: 18px; + margin-bottom: 0; + font-style: italic; +} +code, pre { + font-family: Monaco, Andale Mono, Courier New, monospace; +} +code { + background-color: #fee9cc; + color: rgba(0, 0, 0, 0.75); + padding: 1px 3px; + font-size: 12px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +pre { + display: block; + padding: 14px; + margin: 0 0 18px; + line-height: 16px; + font-size: 11px; + border: 1px solid #d9d9d9; + white-space: pre-wrap; + word-wrap: break-word; +} +pre code { + background-color: #fff; + color:#737373; + font-size: 11px; + padding: 0; +} +sup { + font-size: 0.83em; + vertical-align: super; + line-height: 0; +} +* { + -webkit-print-color-adjust: exact; +} +@media screen and (min-width: 914px) { + body { + width: 854px; + margin:10px auto; + } +} +@media print { + body,code,pre code,h1,h2,h3,h4,h5,h6 { + color: black; + } + table, pre { + page-break-inside: avoid; + } +} \ No newline at end of file diff --git a/imagepy/ui/mkdownwindow.py b/imagepy/ui/mkdownwindow.py index 7a8ded14..b0d7a427 100644 --- a/imagepy/ui/mkdownwindow.py +++ b/imagepy/ui/mkdownwindow.py @@ -1,184 +1,42 @@ -import wx, wx.html2 as webview +import wx, os, time, wx.html2 as webview from markdown import markdown from imagepy import IPy, root_dir -import os def md2html(mdstr): - exts = ['markdown.extensions.extra', 'markdown.extensions.codehilite','markdown.extensions.tables','markdown.extensions.toc'] + exts = ['markdown.extensions.extra', 'markdown.extensions.codehilite', + 'markdown.extensions.tables','markdown.extensions.toc', 'mdx_math'] html = ''' - - - - + + + + - + - + -%s - - + + %s + + ''' ret = markdown(mdstr,extensions=exts) - ''' - f = open('yn.html', 'w', encoding='utf-8') - f.write(html % ret); - f.close() - ''' - return html % ret + + return html % (IPy.root_dir+'/data/markdown.css', ret) class HtmlPanel(wx.Panel): def __init__(self, parent, cont='', url=''): @@ -203,6 +61,8 @@ def OnWebViewTitleChanged(self, evt): if evt.GetString() == 'about:blank': return if evt.GetString() == 'http:///': return self.frame.SetTitle("%s -- %s" % (self.titleBase, evt.GetString())) + if os.path.exists(IPy.root_dir+'/data/index.htm'): + os.remove(IPy.root_dir+'/data/index.htm') class MkDownWindow(wx.Frame): def __init__(self, parent, title, cont, url): @@ -210,4 +70,8 @@ def __init__(self, parent, title, cont, url): logopath = os.path.join(root_dir, 'data/logo.ico') self.SetIcon(wx.Icon(logopath, wx.BITMAP_TYPE_ICO)) cont = '\n'.join([i.strip() for i in cont.split('\n')]) - HtmlPanel(self, md2html(cont), url) \ No newline at end of file + + with open(IPy.root_dir+'/data/index.htm', 'w', encoding='utf-8') as f: + f.write(md2html(cont)) + HtmlPanel(self, url = IPy.root_dir+'/data/index.htm') + \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index a48f6943..88143f67 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,4 +13,5 @@ wxpython xlrd xlwt markdown +python-markdown-math moderngl diff --git a/setup.py b/setup.py index 2461558b..d61b9ae1 100644 --- a/setup.py +++ b/setup.py @@ -43,6 +43,7 @@ def get_data_files(): 'xlwt', 'openpyxl', 'markdown', + 'python-markdown-math', 'numba' ], ) \ No newline at end of file From 3a9d8f591fee941f24b5fc025d449abcd9f511a0 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 1 Feb 2020 23:45:36 +0800 Subject: [PATCH 192/343] viewport and unpreview --- imagepy/core/engine/filter.py | 5 +++-- imagepy/data/markdown.css | 2 +- imagepy/ui/canvas/canvas.py | 4 ++-- imagepy/ui/panelconfig.py | 15 +++++++++++---- imagepy/ui/widgets/colormap.py | 2 +- imagepy/ui/widgets/curvepanel.py | 6 +++--- imagepy/ui/widgets/normal.py | 18 +++++++++--------- imagepy/ui/widgets/viewport.py | 2 +- imagepy/widgets/navigator/navigator_wgt.py | 6 +++++- 9 files changed, 36 insertions(+), 24 deletions(-) diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index d848f404..0a427e77 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -105,9 +105,10 @@ def show(self, temp=ParaDialog): self.dialog.on_help = lambda : IPy.show_md(self.title, DocumentManager.get(self.title)) self.dialog.set_handle(lambda x:self.preview(self.ips, x)) - if self.modal: return self.dialog.ShowModal() == wx.ID_OK + self.dialog.on_ok = lambda : self.ok(self.ips) self.dialog.on_cancel = lambda : self.cancel(self.ips) + if self.modal: return self.dialog.ShowModal() == wx.ID_OK self.dialog.Show() def run(self, ips, snap, img, para = None): @@ -184,7 +185,7 @@ def ok(self, ips, para=None, callafter=None): def cancel(self, ips): if 'auto_snap' in self.note: - ips.swap() + ips.img[:] = ips.snap ips.update() def start(self, para=None, callafter=None): diff --git a/imagepy/data/markdown.css b/imagepy/data/markdown.css index 179ac447..ccf5c08f 100644 --- a/imagepy/data/markdown.css +++ b/imagepy/data/markdown.css @@ -43,7 +43,7 @@ p { margin-bottom: 9px; } p img { - width:100%%; + width:100%; } h1, h2, diff --git a/imagepy/ui/canvas/canvas.py b/imagepy/ui/canvas/canvas.py index 1a5be63f..55775da6 100644 --- a/imagepy/ui/canvas/canvas.py +++ b/imagepy/ui/canvas/canvas.py @@ -243,8 +243,8 @@ def __del__(self): canvas.set_lut(lut) canvas.set_cn(0) canvas.set_back(astronaut()) - canvas.set_cn('rgb', 1) - canvas.set_mode('msk') + canvas.set_cn((0,1,2), True) + canvas.set_mode(0.5) x = np.arange(512) y = np.sin(x/30) * 100 + 256 canvas.marks['line'] = {'type':'line', 'lw':3, 'body':np.array([x,y]).T.tolist()} diff --git a/imagepy/ui/panelconfig.py b/imagepy/ui/panelconfig.py index f6d88633..36f1764d 100644 --- a/imagepy/ui/panelconfig.py +++ b/imagepy/ui/panelconfig.py @@ -63,6 +63,7 @@ def init_view(self, items, para, preview=False, modal = True): def OnDestroy( self, event ): self.set_handle(None) + self.on_cancel = self.on_ok = self.on_help = None del self.ctrl_dic def parse(self, para) : @@ -92,17 +93,23 @@ def pack(self): def para_check(self, para, key):pass - def para_changed(self, key): + def para_changed(self, obj): + key = '' para = self.para - for p in list(para.keys()): - if p in self.ctrl_dic: + for p in self.ctrl_dic: + if p in para: para[p] = self.ctrl_dic[p].GetValue() + if self.ctrl_dic[p] == obj: key = p + sta = sum([i is None for i in list(para.values())])==0 self.btn_ok.Enable(sta) if not sta: return self.para_check(para, key) if 'preview' not in self.ctrl_dic:return - if not self.ctrl_dic['preview'].GetValue():return + if not self.ctrl_dic['preview'].GetValue(): + if key=='preview' and self.on_cancel != None: + return self.on_cancel() + else: return self.handle(para) def reset(self, para=None): diff --git a/imagepy/ui/widgets/colormap.py b/imagepy/ui/widgets/colormap.py index ce55224b..b5b419af 100644 --- a/imagepy/ui/widgets/colormap.py +++ b/imagepy/ui/widgets/colormap.py @@ -95,4 +95,4 @@ def __init__( self, parent, title): def Bind(self, z, f): self.f = f def on_sel(self, event): - self.f(event) \ No newline at end of file + self.f(self) \ No newline at end of file diff --git a/imagepy/ui/widgets/curvepanel.py b/imagepy/ui/widgets/curvepanel.py index 3307892b..e1455bd0 100644 --- a/imagepy/ui/widgets/curvepanel.py +++ b/imagepy/ui/widgets/curvepanel.py @@ -61,7 +61,7 @@ def on_ld(self, event): self.pts.append((x, 255-y)) self.idx = len(self.pts)-1 self.update() - self.handle(event) + self.handle(self) def on_lu(self, event): self.idx = -1 @@ -75,7 +75,7 @@ def on_rd(self, event): del self.pts[self.idx] self.idx = -1 self.update() - self.handle(event) + self.handle(self) def on_mv(self, event): x = (event.GetX()-self.offset[0])/self.k @@ -91,7 +91,7 @@ def on_mv(self, event): y = np.clip(y, 0, 255) self.pts[self.idx] = (x, 255-y) self.update() - self.handle(event) + self.handle(self) def on_paint(self, event): diff --git a/imagepy/ui/widgets/normal.py b/imagepy/ui/widgets/normal.py index eca37352..a27194e4 100644 --- a/imagepy/ui/widgets/normal.py +++ b/imagepy/ui/widgets/normal.py @@ -30,7 +30,7 @@ def __init__(self, parent, rang, accury, title, unit): def Bind(self, z, f):self.f = f def ontext(self, event): - self.f(event) + self.f(self) if self.GetValue()==None: self.ctrl.SetBackgroundColour((255,255,0)) else: @@ -78,7 +78,7 @@ def __init__(self, parent, title, unit): def Bind(self, z, f):self.f = f def ontext(self, event): - self.f(event) + self.f(self) def SetValue(self, n): self.ctrl.SetValue(n) @@ -120,7 +120,7 @@ def oncolor(self, event): rst = dialog.GetColourData().GetColour() self.ctrl.SetBackgroundColour(rst) self.ctrl.SetValue(rst.GetAsString(wx.C2S_HTML_SYNTAX)) - self.f(event) + self.f(self) dialog.Destroy() def SetValue(self, color): @@ -199,7 +199,7 @@ def Bind(self, z, f): self.f = f def on_choice(self, event): - self.f(event) + self.f(self) def SetValue(self, x): n = self.choices.index(x) if x in self.choices else 0 @@ -260,7 +260,7 @@ def GetValue(self): # Virtual event handlers, overide them in your derived class def on_text( self, event ): - self.f(event) + self.f(self) if self.GetValue()==None: self.txt_value.SetBackgroundColour((255,255,0)) else: self.txt_value.SetBackgroundColour((255,255,255)) @@ -292,7 +292,7 @@ def Bind(self, z, f): self.f = f def on_check(self, event): - self.f(event) + self.f(self) def GetValue(self): return [self.choices[i] for i in self.ctrl.GetCheckedItems()] @@ -354,14 +354,14 @@ def on_scroll(self, event): n = value/255.0*(self.max-self.min)+self.min self.text.SetValue(str(round(n,self.accury) if self.accury>0 else int(n))) self.text.SetBackgroundColour((255,255,255)) - self.f(event) + self.f(self) def on_spin(self, event): self.slider.SetValue(self.spin.GetValue()) self.on_scroll(event) def on_text(self, event): - self.f(event) + self.f(self) if self.GetValue()==None: self.text.SetBackgroundColour((255,255,0)) else: @@ -416,7 +416,7 @@ def __init__(self, parent, title): self.GetValue = check.GetValue check.Bind(wx.EVT_CHECKBOX, self.on_check) - def on_check(self, event):self.f(event) + def on_check(self, event):self.f(self) def Bind(self, z, f): self.f = f if __name__ == '__main__': diff --git a/imagepy/ui/widgets/viewport.py b/imagepy/ui/widgets/viewport.py index 2b9e764e..5ea28fae 100644 --- a/imagepy/ui/widgets/viewport.py +++ b/imagepy/ui/widgets/viewport.py @@ -47,12 +47,12 @@ def on_paint(self, event): wx.BufferedPaintDC(self, self.buffer) def on_ld(self, event): + self.handle(True) x, y = event.GetX(), event.GetY() x = 1.0*(x-self.offx)/self.imgw y = 1.0*(y-self.offy)/self.imgh if x<0 or x>1 or y<0 or y>1:return self.loc = (x*self.ibox[0], y*self.ibox[1]) - self.handle() self.drag = True def on_lu(self, event): diff --git a/imagepy/widgets/navigator/navigator_wgt.py b/imagepy/widgets/navigator/navigator_wgt.py index e96ebcc2..f0c49a5c 100644 --- a/imagepy/widgets/navigator/navigator_wgt.py +++ b/imagepy/widgets/navigator/navigator_wgt.py @@ -70,6 +70,7 @@ def on_apply(self, event): self.viewport.set_box(win.canvas.conbox, win.canvas.winbox) def on_zoom(self, event): + self.on_apply(event) k = self.scales[self.slider.GetValue()] self.label.SetLabel('%.2f%%'%(k*100)) win = IPy.get_window() @@ -81,6 +82,7 @@ def on_zoom(self, event): self.viewport.set_box(win.canvas.conbox, win.canvas.winbox) def on_fit(self, event): + self.on_apply(event) win = IPy.get_window() if win is None: return win.canvas.fit() @@ -92,6 +94,7 @@ def on_fit(self, event): self.viewport.set_box(win.canvas.conbox, win.canvas.winbox) def on_one(self, event): + self.on_apply(event) win = IPy.get_window() if win is None: return a,b,c,d = win.canvas.winbox @@ -102,7 +105,8 @@ def on_one(self, event): self.label.SetLabel('%.2f%%'%100) self.viewport.set_box(win.canvas.conbox, win.canvas.winbox) - def on_handle(self): + def on_handle(self, update=False): + if update: self.on_apply(update) win = IPy.get_window() if win is None: return x, y = self.viewport.GetValue() From 0706b08efaf8d5d1e62fd9e931c51b76345b3318 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 2 Feb 2020 22:36:58 +0800 Subject: [PATCH 193/343] connectivity --- imagepy/ipyalg/graph/connect.py | 51 +++++++++++++++++++ .../Analysis/Region Analysis/__init__.py | 2 +- .../Analysis/Region Analysis/connect_plg.py | 51 +++++++++++++++++++ 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 imagepy/ipyalg/graph/connect.py create mode 100644 imagepy/menus/Analysis/Region Analysis/connect_plg.py diff --git a/imagepy/ipyalg/graph/connect.py b/imagepy/ipyalg/graph/connect.py new file mode 100644 index 00000000..3c413dad --- /dev/null +++ b/imagepy/ipyalg/graph/connect.py @@ -0,0 +1,51 @@ +import numpy as np +from numba import jit +from scipy.ndimage import generate_binary_structure + +def neighbors(shape, conn=1): + dim = len(shape) + block = generate_binary_structure(dim, conn) + block[tuple([1]*dim)] = 0 + idx = np.where(block>0) + idx = np.array(idx, dtype=np.uint8).T + idx = np.array(idx-[1]*dim) + acc = np.cumprod((1,)+shape[::-1][:-1]) + return np.dot(idx, acc[::-1]) + +@jit(nopython=True) +def search(img, nbs): + s, line = 0, img.ravel() + rst = np.zeros((len(line),2), img.dtype) + for i in range(len(line)): + if line[i]==0:continue + for d in nbs: + if line[i+d]==0: continue + if line[i]==line[i+d]: continue + rst[s,0] = line[i] + rst[s,1] = line[i+d] + s += 1 + return rst[:s] + +def connect(img, conn=1): + buf = np.pad(img, 1, 'constant') + nbs = neighbors(buf.shape, conn) + rst = search(buf, nbs) + if len(rst)==0: return rst + rst.sort() + return np.unique(rst, axis=0) + +def mapidx(idx): + dic = {} + for i in np.unique(idx): dic[i] = [] + for i,j in idx: + dic[i].append(j) + dic[j].append(i) + return dic + +if __name__ == '__main__': + img = np.array([[1,1,1,1,1], + [1,1,2,2,1], + [1,3,0,0,1], + [1,3,1,1,4]]) + rst = connect(img, 2) + print(rst) \ No newline at end of file diff --git a/imagepy/menus/Analysis/Region Analysis/__init__.py b/imagepy/menus/Analysis/Region Analysis/__init__.py index 3998600d..b3cb0887 100644 --- a/imagepy/menus/Analysis/Region Analysis/__init__.py +++ b/imagepy/menus/Analysis/Region Analysis/__init__.py @@ -1,2 +1,2 @@ # -catlog = ['regionprops_plgs', '-', 'statistic_plgs'] \ No newline at end of file +catlog = ['regionprops_plgs', 'connect_plg', '-', 'statistic_plgs'] \ No newline at end of file diff --git a/imagepy/menus/Analysis/Region Analysis/connect_plg.py b/imagepy/menus/Analysis/Region Analysis/connect_plg.py new file mode 100644 index 00000000..fb3a6bde --- /dev/null +++ b/imagepy/menus/Analysis/Region Analysis/connect_plg.py @@ -0,0 +1,51 @@ +from imagepy import IPy +import numpy as np +from imagepy.core.engine import Simple +from skimage.measure import regionprops +from imagepy.core.mark import GeometryMark +from scipy.ndimage import label, generate_binary_structure +from imagepy.ipyalg.graph.connect import connect, mapidx +import pandas as pd + +# center, area, l, extent, cov +class Plugin(Simple): + title = 'Connective Analysis' + note = ['8-bit', '16-bit', 'int'] + para = {'con':'8-connect', 'labled':False, 'slice':False} + view = [(list, 'con', ['4-connect', '8-connect'], str, 'conection', 'pix'), + (bool, 'labled', 'it is a label image'), + (bool, 'slice', 'slice')] + + #process + def run(self, ips, imgs, para = None): + if not para['slice']:imgs = [ips.img] + k = ips.unit[0] + + titles = ['Slice', 'ID'][0 if para['slice'] else 1:] + ['Center-X','Center-Y', 'N', 'Neighbors'] + buf = imgs[0].astype(np.uint32) + data, mark = [], {'type':'layers', 'body':{}} + for i in range(len(imgs)): + if para['labled']: buf = imgs[i] + else: label(imgs[i], generate_binary_structure(2, 1), output=buf) + conarr = connect(buf, 1 if para['con']=='4-connect' else 2) + conmap = mapidx(conarr) + + ls = regionprops(buf) + dt = [[i]*len(ls), list(range(1,1+len(ls)))] + + if not para['slice']:dt = dt[1:] + + layer = {'type':'layer', 'body':[]} + texts = [(i.centroid[::-1])+('id=%d'%i.label,) for i in ls] + lines = [(ls[i-1].centroid[::-1], ls[j-1].centroid[::-1]) for i,j in conarr] + layer['body'].append({'type':'texts', 'body':texts}) + layer['body'].append({'type':'lines', 'body':lines}) + mark['body'][i] = layer + + dt.append([round(i.centroid[1]*k,1) for i in ls]) + dt.append([round(i.centroid[0]*k,1) for i in ls]) + neibs = [conmap[i.label] if i.label in conmap else [] for i in ls] + dt.extend([[len(i) for i in neibs], neibs]) + data.extend(list(zip(*dt))) + ips.mark = GeometryMark(mark) + IPy.show_table(pd.DataFrame(data, columns=titles), ips.title+'-region') \ No newline at end of file From 0e3be4c056cc85de55e0294099656b85950397b8 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 6 Feb 2020 18:23:07 +0800 Subject: [PATCH 194/343] ai brush --- imagepy/core/manager/colormanager.py | 2 +- imagepy/core/mark/mark.py | 1 - imagepy/ipyalg/graph/connect.py | 40 ++- imagepy/ipyalg/graph/render.py | 27 ++ .../Analysis/Region Analysis/connect_plg.py | 7 +- imagepy/menus/Analysis/label_plg.py | 76 +++-- imagepy/menus/Process/Segment/shift_plgs.py | 4 +- imagepy/tools/Draw/__init__.py | 2 +- imagepy/tools/Draw/aibrush_tol.py | 282 ++++++++++++++++++ imagepy/tools/Draw/aibursh.gif | Bin 0 -> 88 bytes imagepy/tools/Draw/floodfill_tol.py | 6 +- imagepy/ui/canvasframe.py | 3 +- 12 files changed, 401 insertions(+), 49 deletions(-) create mode 100644 imagepy/ipyalg/graph/render.py create mode 100644 imagepy/tools/Draw/aibrush_tol.py create mode 100644 imagepy/tools/Draw/aibursh.gif diff --git a/imagepy/core/manager/colormanager.py b/imagepy/core/manager/colormanager.py index bc7818c6..71fa4358 100644 --- a/imagepy/core/manager/colormanager.py +++ b/imagepy/core/manager/colormanager.py @@ -50,7 +50,7 @@ def get_front(cls, one=False): return np.dot((cls.wr,cls.wg,cls.wb), cls.frontcolor) @classmethod - def get_back(cls, one): + def get_back(cls, one=False): if not one:return cls.backcolor return np.dot((cls.wr,cls.wg,cls.wb), cls.backcolor) diff --git a/imagepy/core/mark/mark.py b/imagepy/core/mark/mark.py index 6694ac58..b02bd877 100644 --- a/imagepy/core/mark/mark.py +++ b/imagepy/core/mark/mark.py @@ -251,7 +251,6 @@ def draw_text(pts, dc, f, **key): brush.SetColour(pen.GetColour()) brush.SetStyle(100) if 'fcolor' in pts: - print('hahaha') dc.SetTextBackground(pts['fcolor']) if 'size' in pts: font.SetPointSize(pts['size']) diff --git a/imagepy/ipyalg/graph/connect.py b/imagepy/ipyalg/graph/connect.py index 3c413dad..e025e0ac 100644 --- a/imagepy/ipyalg/graph/connect.py +++ b/imagepy/ipyalg/graph/connect.py @@ -1,5 +1,6 @@ import numpy as np from numba import jit +import random from scipy.ndimage import generate_binary_structure def neighbors(shape, conn=1): @@ -13,26 +14,45 @@ def neighbors(shape, conn=1): return np.dot(idx, acc[::-1]) @jit(nopython=True) -def search(img, nbs): +def unique(idx): + msk = idx[:,0]0: + k = nodes.pop(0) + counter[k] += 1 + hist = [1e4] + [0] * n + for p in conmap[k]: + hist[colors[p]] += 1 + if min(hist)==0: + colors[k] = hist.index(min(hist)) + counter[k] = 0 + continue + hist[colors[k]] = 1e4 + minc = hist.index(min(hist)) + if counter[k]==rand: + counter[k] = 0 + minc = random.randint(1,4) + colors[k] = minc + for p in conmap[k]: + if colors[p] == minc: + nodes.append(p) + return colors \ No newline at end of file diff --git a/imagepy/menus/Analysis/Region Analysis/connect_plg.py b/imagepy/menus/Analysis/Region Analysis/connect_plg.py index fb3a6bde..ee46270b 100644 --- a/imagepy/menus/Analysis/Region Analysis/connect_plg.py +++ b/imagepy/menus/Analysis/Region Analysis/connect_plg.py @@ -4,16 +4,17 @@ from skimage.measure import regionprops from imagepy.core.mark import GeometryMark from scipy.ndimage import label, generate_binary_structure -from imagepy.ipyalg.graph.connect import connect, mapidx +from imagepy.ipyalg.graph.connect import connect_graph, mapidx import pandas as pd # center, area, l, extent, cov class Plugin(Simple): title = 'Connective Analysis' note = ['8-bit', '16-bit', 'int'] - para = {'con':'8-connect', 'labled':False, 'slice':False} + para = {'con':'8-connect', 'labled':False, 'nozero':True, 'slice':False} view = [(list, 'con', ['4-connect', '8-connect'], str, 'conection', 'pix'), (bool, 'labled', 'it is a label image'), + (bool, 'nozero', 'nonzero'), (bool, 'slice', 'slice')] #process @@ -27,7 +28,7 @@ def run(self, ips, imgs, para = None): for i in range(len(imgs)): if para['labled']: buf = imgs[i] else: label(imgs[i], generate_binary_structure(2, 1), output=buf) - conarr = connect(buf, 1 if para['con']=='4-connect' else 2) + conarr = connect(buf, 1 if para['con']=='4-connect' else 2, not self.para['nozero']) conmap = mapidx(conarr) ls = regionprops(buf) diff --git a/imagepy/menus/Analysis/label_plg.py b/imagepy/menus/Analysis/label_plg.py index 6487dbcc..573d8122 100644 --- a/imagepy/menus/Analysis/label_plg.py +++ b/imagepy/menus/Analysis/label_plg.py @@ -5,38 +5,56 @@ """ import numpy as np from scipy.ndimage import label, generate_binary_structure - from imagepy import IPy -from imagepy.core.engine import Filter -from imagepy.ui.canvasframe import CanvasFrame +from imagepy.core import ImagePlus +from imagepy.core.engine import Filter, Simple +from imagepy.ipyalg.graph import connect, render -class Plugin(Filter): +class Label(Simple): title = 'Label Image' - note = ['8-bit', 'not_slice', 'preview'] - - para = {'thr':128, 'con':'4-Connect'} - view = [('slide', 'thr', (0,255), 0, 'Threshold'), - (list, 'con', ['4-Connect','8-Connect'], str, 'Structure', 'connect')] - - def load(self, ips): - self.lut = ips.lut - ips.lut = self.lut.copy() - return True - - def preview(self, ips, para): - ips.lut[:] = self.lut - ips.lut[para['thr']:] = [255,0,0] - ips.update() - - def run(self, ips, snap, img, para = None): - if para == None: para = self.para - ips.lut = self.lut - msk = img>para['thr'] - con = 1 if para['con']=='4-Connect' else 2 - strc = generate_binary_structure(2, con) - msk = label(msk, strc, output = np.int16) + note = ['8-bit', '16-bit'] + para = {'slice':False, 'con':'4-Connect'} + view = [(list, 'con', ['4-Connect','8-Connect'], str, 'Structure', 'connect'), + (bool, 'slice', 'slice')] - IPy.show_img([msk[0]], ips.title+'-label') + def run(self, ips, imgs, para = None): + if not para['slice']: imgs = [ips.img] + labels = [] + for i in range(len(imgs)): + self.progress(i, len(imgs)) + con = 1 if para['con']=='4-Connect' else 2 + strc = generate_binary_structure(2, con) + lab, n = label(imgs[i], strc, output = np.int32) + labels.append(lab) + IPy.show_img(labels, ips.title+'-label') +class Render(Simple): + title = 'Label Render' + note = ['8-bit', '16-bit', 'int'] + para = {'slice':False, 'con':'8-Connect', 'colors':6, 'back':False} + view = [(list, 'con', ['4-Connect','8-Connect'], str, 'Structure', 'connect'), + (int, 'colors', (4, 255), 0, 'colors', 'n'), + (bool, 'back', 'background'), + (bool, 'slice', 'slice')] - \ No newline at end of file + def run(self, ips, imgs, para = None): + if not para['slice']: imgs = [ips.img] + print(para) + labels = [] + for i in range(len(imgs)): + self.progress(i, len(imgs)) + con = 1 if para['con']=='4-Connect' else 2 + idx = connect.connect_graph(imgs[i], con, para['back']) + idx = connect.mapidx(idx) + cmap = render.node_render(idx, para['colors'], 10) + + lut = np.ones(imgs[i].max()+1, dtype=np.uint8) + lut[0] = 0 + for j in cmap: lut[j] = cmap[j] + labels.append(lut[imgs[i]]) + + ips = ImagePlus(labels, ips.title+'-render') + ips.range = (0, para['colors']) + IPy.show_ips(ips) + +plgs = [Label, Render] \ No newline at end of file diff --git a/imagepy/menus/Process/Segment/shift_plgs.py b/imagepy/menus/Process/Segment/shift_plgs.py index df3590c2..4b49d39d 100644 --- a/imagepy/menus/Process/Segment/shift_plgs.py +++ b/imagepy/menus/Process/Segment/shift_plgs.py @@ -37,7 +37,7 @@ class Felzenszwalb(Filter): note = ['all', 'not_slice', 'auto_snap', 'auto_msk', 'not_channel', 'preview'] para = {'scale':1.0, 'sigma':0.8, 'min_size':20} - view = [(float, 'scale', (0.01, 10), 2, 'scale', ''), + view = [(float, 'scale', (0.01, 1024), 2, 'scale', ''), (float, 'sigma', (0, 30), 2, 'sigma', ''), (int, 'min_size', (1, 1024), 0, 'min_size', '')] @@ -51,7 +51,7 @@ class SLICLab(Simple): para = {'n_segments':100, 'compactness':10.0, 'max_iter':10, 'sigma':0, 'stack':False} view = [(int, 'n_segments', (1, 1e8), 0, 'segments', 'n'), - (float, 'compactness', (0.01, 100), 2, 'campactness', 'color-space'), + (float, 'compactness', (0.01, 1024), 2, 'campactness', 'color-space'), (int, 'max_iter', (3, 50), 0, 'max_iter', 'n'), (float, 'sigma', (0, 30), 1, 'sigma', 'smooth'), (bool, 'stack', 'stack')] diff --git a/imagepy/tools/Draw/__init__.py b/imagepy/tools/Draw/__init__.py index 7f91fe32..a370e2d6 100644 --- a/imagepy/tools/Draw/__init__.py +++ b/imagepy/tools/Draw/__init__.py @@ -1 +1 @@ -catlog = ['magic_tol', 'floodfill_tol', 'flood3d_tol'] \ No newline at end of file +catlog = ['magic_tol', 'floodfill_tol', 'flood3d_tol', 'aibrush_tol'] \ No newline at end of file diff --git a/imagepy/tools/Draw/aibrush_tol.py b/imagepy/tools/Draw/aibrush_tol.py new file mode 100644 index 00000000..2bda9bb1 --- /dev/null +++ b/imagepy/tools/Draw/aibrush_tol.py @@ -0,0 +1,282 @@ +from imagepy.core.engine import Tool +import numpy as np +from time import time +from imagepy.core.manager import ColorManager +from skimage.morphology import flood_fill, flood +from skimage.draw import line, circle +from skimage.segmentation import felzenszwalb +from imagepy.core.mark import GeometryMark +from scipy.ndimage import binary_fill_holes, binary_dilation, binary_erosion + +def fill_normal(img, r, c, color, con, tor): + img = img.reshape((img.shape+(1,))[:3]) + msk = np.ones(img.shape[:2], dtype=np.bool) + for i in range(img.shape[2]): + msk &= flood(img[:,:,i], (r, c), connectivity=con, tolerance=tor) + img[msk] = color + +def local_brush(img, r, c, color, sigma, msize): + lab = felzenszwalb(img, 1, sigma, msize) + msk = flood(lab, (r, c), connectivity=2) + img[msk] = color + +def local_pen(img, r, c, R, color): + img = img.reshape((img.shape+(1,))[:3]) + rs, cs = circle(r, c, R/2+1e-6, shape=img.shape) + img[rs, cs] = color + +def local_in_fill(img, r, c, R, color, bcolor): + img = img.reshape((img.shape+(1,))[:3]) + msk = (img == color).min(axis=2) + filled = binary_fill_holes(msk) + filled ^= msk + rs, cs = circle(r, c, R/2+1e-6, shape=img.shape) + msk[:] = 0 + msk[rs, cs] = 1 + msk &= filled + img[msk] = bcolor + +def local_out_fill(img, r, c, R, color, bcolor): + img = img.reshape((img.shape+(1,))[:3]) + msk = (img != color).max(axis=2) + rs, cs = circle(r, c, R/2+1e-6, shape=img.shape) + buf = np.zeros_like(msk) + buf[rs, cs] = 1 + msk &= buf + img[msk] = bcolor + +def local_sketch(img, r, c, R, color, bcolor): + img = img.reshape((img.shape+(1,))[:3]) + msk = (img == color).min(axis=2) + dilation = binary_dilation(msk, np.ones((3,3))) + dilation ^= msk + rs, cs = circle(r, c, R/2+1e-6, shape=img.shape) + msk[:] = 0 + msk[rs, cs] = 1 + msk &= dilation + img[msk] = bcolor + +def global_both_line(img, r, c, color): + img = img.reshape((img.shape+(1,))[:3]) + msk = np.ones(img.shape[:2], dtype=np.bool) + for i in range(img.shape[2]): + msk &= flood(img[:,:,i], (r, c), connectivity=2) + dilation = binary_dilation(msk, np.ones((3,3))) + dilation ^= msk + img[dilation] = color + +def global_out_line(img, r, c, color): + img = img.reshape((img.shape+(1,))[:3]) + msk = np.ones(img.shape[:2], dtype=np.bool) + for i in range(img.shape[2]): + msk &= flood(img[:,:,i], (r, c), connectivity=2) + msk = binary_fill_holes(msk) + dilation = binary_dilation(msk, np.ones((3,3))) + dilation ^= msk + img[dilation] = color + +def global_in_line(img, r, c, color): + img = img.reshape((img.shape+(1,))[:3]) + msk = np.ones(img.shape[:2], dtype=np.bool) + for i in range(img.shape[2]): + msk &= flood(img[:,:,i], (r, c), connectivity=2) + inarea = binary_fill_holes(msk) + inarea ^= msk + inarea ^= binary_erosion(inarea, np.ones((3,3))) + img[inarea] = color + +def global_in_fill(img, r, c, color): + img = img.reshape((img.shape+(1,))[:3]) + msk = np.ones(img.shape[:2], dtype=np.bool) + for i in range(img.shape[2]): + msk &= flood(img[:,:,i], (r, c), connectivity=2) + filled = binary_fill_holes(msk) + filled ^= msk + img[filled] = color + +def global_out_fill(img, r, c, color): + img = img.reshape((img.shape+(1,))[:3]) + ori = np.ones(img.shape[:2], dtype=np.bool) + for i in range(img.shape[2]): + ori &= flood(img[:,:,i], (r, c), connectivity=2) + filled = binary_fill_holes(ori) + dilation = binary_dilation(ori) + dilation ^= filled + rs, cs = np.where(dilation) + if len(rs)==0: return + msk = ((img == img[r,c]).min(axis=2)).astype(np.uint8) + flood_fill(msk, (rs[0], cs[0]), 2, connectivity=2, inplace=True) + img[msk==2] = color + +''' +general: size + +>>> local =================================== +block: minsize | left +in fill: r | left shift +pen: r | left ctrl +sketch: r | left alt +out fill: r | left ctrl + alt + +>>> global ================================== +flood: | right +in fill: | right shift +out line: | right ctrl +in line: | right alt +out fill: | right ctrl + alt + +scale and move: | wheel +''' +class Plugin(Tool): + title = 'AI Painter' + + para = {'win':48, 'tor':10, 'con':'8-connect', 'ms':20, 'r':2, 'color':(255,0,128)} + view = [(int, 'win', (28, 64), 0, 'window', 'size'), + ('color', 'color', 'color', 'mark'), + ('lab', None, '======= Brush ======='), + (float, 'ms', (10, 50), 0, 'stickiness', 'pix'), + ('lab', None, '======= Pen ======='), + (int, 'r', (1, 30), 0, 'radius', 'pix'), + ('lab', None, '======= Flood ======='), + (int, 'tor', (0,1000), 0, 'tolerance', 'value'), + (list, 'con', ['4-connect', '8-connect'], str, 'connect', 'pix')] + + def __init__(self): + self.status = None + self.pickp = (0,0) + self.oldp = (0,0) + + def mouse_down(self, ips, x, y, btn, **key): + if btn==2: + self.oldp = key['canvas'].to_panel_coor(x,y) + self.status = 'move' + return + + self.oldp = self.pickp = (y, x) + color = ColorManager.get_front() + x = int(round(min(max(x,0), ips.img.shape[1]))) + y = int(round(min(max(y,0), ips.img.shape[0]))) + color = (np.mean(color), color)[ips.img.ndim==3] + self.pickcolor = ips.img[y, x] + ips.snapshot() + + if btn==1 and key['ctrl'] and key['alt']: + self.status = 'local_out' + elif btn==1 and key['ctrl']: + self.status = 'local_pen' + elif btn==1 and key['alt']: + self.status = 'local_sketch' + elif btn==1 and key['shift']: + self.status = 'local_in' + elif btn==1: + self.status = 'local_brush' + elif btn==3 and key['ctrl'] and key['alt']: + self.status = 'global_out_fill' + global_out_fill(ips.img, y, x, color) + ips.update() + elif btn==3 and key['ctrl']: + self.status = 'global_out_line' + global_out_line(ips.img, y, x, color) + ips.update() + elif btn==3 and key['alt']: + self.status = 'global_in_line' + global_in_line(ips.img, y, x, color) + ips.update() + elif btn==3 and key['shift']: + self.status = 'global_in_fill' + global_in_fill(ips.img, y, x, color) + ips.update() + elif btn==3: + if (ips.img[y, x] - color).sum()==0: return + conn = {'4-connect':1, '8-connect':2} + conn = conn[self.para['con']] + tor = self.para['tor'] + fill_normal(ips.img, y, x, color, conn, tor) + ips.update() + + def mouse_up(self, ips, x, y, btn, **key): + if btn==1 and (y,x)==self.pickp and key['ctrl']: + x = int(round(min(max(x,0), ips.img.shape[1]))) + y = int(round(min(max(y,0), ips.img.shape[0]))) + ColorManager.set_front(ips.img[y, x]) + self.status = None + ips.mark = None + ips.update() + + def make_mark(self, x, y): + wins = self.para['win'] + rect = {'type':'rectangle', 'body':(x, y, wins*2, wins*2), 'color':self.para['color']} + mark = {'type':'layer', 'body':[rect]} + r = 2 if self.status=='local_brush' else self.para['r']/2 + mark['body'].append({'type':'circle', 'body':(x, y, r), 'color':self.para['color']}) + + mark['body'].append({'type':'text', 'body':(x-wins, y-wins, + 'S:%s W:%s'%(self.para['ms'], self.para['win'])), 'pt':False, 'color':self.para['color']}) + return GeometryMark(mark) + + def mouse_move(self, ips, x, y, btn, **key): + if self.status == None and ips.mark != None: + ips.mark = None + ips.update() + if not self.status in ['local_pen','local_brush', + 'local_sketch','local_in','local_out','move']: return + img, color = ips.img, ColorManager.get_front() + x = int(round(min(max(x,0), img.shape[1]))) + y = int(round(min(max(y,0), img.shape[0]))) + + if self.status == 'move': + x,y = key['canvas'].to_panel_coor(x,y) + key['canvas'].move(x-self.oldp[0], y-self.oldp[1]) + self.oldp = x, y + ips.update() + print('move') + return + + rs, cs = line(*[int(round(i)) for i in self.oldp + (y, x)]) + np.clip(rs, 0, img.shape[0]-1, out=rs) + np.clip(cs, 0, img.shape[1]-1, out=cs) + + color = (np.mean(color), color)[img.ndim==3] + + for r,c in zip(rs, cs): + start = time() + w = self.para['win'] + sr = (max(0,r-w), min(img.shape[0], r+w)) + sc = (max(0,c-w), min(img.shape[1], c+w)) + r, c = min(r, w), min(c, w) + clip = img[slice(*sr), slice(*sc)] + + if self.status == 'local_pen': + local_pen(clip, r, c, self.para['r'], color) + if self.status == 'local_brush': + if (clip[r,c] - color).sum()==0: continue + local_brush(clip, r, c, color, 0, self.para['ms']) + if self.status == 'local_in': + local_in_fill(clip, r, c, self.para['r'], self.pickcolor, color) + if self.status == 'local_sketch': + local_sketch(clip, r, c, self.para['r'], self.pickcolor, color) + if self.status=='local_out': + local_out_fill(clip, r, c, self.para['r'], self.pickcolor, color) + + + ips.mark = self.make_mark(x, y) + self.oldp = (y, x) + ips.update() + + def mouse_wheel(self, ips, x, y, d, **key): + if key['shift']: + if d>0: self.para['ms'] = min(50, self.para['ms']+1) + if d<0: self.para['ms'] = max(10, self.para['ms']-1) + ips.mark = self.make_mark(x, y) + elif key['ctrl'] and key['alt']: + if d>0: self.para['win'] = min(64, self.para['win']+1) + if d<0: self.para['win'] = max(28, self.para['win']-1) + ips.mark = self.make_mark(x, y) + elif key['ctrl']: + if d>0: self.para['r'] = min(30, self.para['r']+1) + if d<0: self.para['r'] = max(2, self.para['r']-1) + ips.mark = self.make_mark(x, y) + elif self.status == None: + if d>0:key['canvas'].zoomout(x, y, 'data') + if d<0:key['canvas'].zoomin(x, y, 'data') + ips.update() \ No newline at end of file diff --git a/imagepy/tools/Draw/aibursh.gif b/imagepy/tools/Draw/aibursh.gif new file mode 100644 index 0000000000000000000000000000000000000000..761025c1d518a74352b07c2354553754e078cef0 GIT binary patch literal 88 zcmZ?wbhEHb6krfwXkY+>Y5)KKSNzGs$iTqJpaT*B$uKbK_w+C5V*KkfMbFl4^X|g5 ow#ut+eeTCSPcNFL^vWq$V|LQ!joQ^o6OO;W+qV1aLIwtF0EUwv@c;k- literal 0 HcmV?d00001 diff --git a/imagepy/tools/Draw/floodfill_tol.py b/imagepy/tools/Draw/floodfill_tol.py index 8245db91..f78c7f3f 100644 --- a/imagepy/tools/Draw/floodfill_tol.py +++ b/imagepy/tools/Draw/floodfill_tol.py @@ -18,8 +18,12 @@ class Plugin(Tool): (list, 'con', ['4-connect', '8-connect'], str, 'fill', 'pix')] def mouse_down(self, ips, x, y, btn, **key): - ips.snapshot() + img, color = ips.img, ColorManager.get_front() + if int(y)<0 or int(x)<0: return + if int(y)>=img.shape[0] or int(x)>=img.shape[1]: return + + ips.snapshot() connectivity=(self.para['con']=='8-connect')+1 img = ips.img.reshape((ips.img.shape+(1,))[:3]) msk = np.ones(img.shape[:2], dtype=np.bool) diff --git a/imagepy/ui/canvasframe.py b/imagepy/ui/canvasframe.py index eb8fe7b0..5d9ecf3e 100644 --- a/imagepy/ui/canvasframe.py +++ b/imagepy/ui/canvasframe.py @@ -120,7 +120,8 @@ def on_idle(self, event): self.canvas.marks['roi'] = draw if self.ips.mark is None: self.canvas.marks['mark'] = None else: - draw = lambda dc, f, **key: self.ips.mark.draw(dc, f, cur=self.ips.cur, **key) + if self.ips.mark is None: draw = None + else: draw = lambda dc, f, **key: self.ips.mark.draw(dc, f, cur=self.ips.cur, **key) self.canvas.marks['mark'] = draw if self.ips.unit == (1, 'pix'): self.canvas.marks['unit'] = None else: From f22ef8ac70a85038645150cd8e76dc5087fb1298 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 8 Feb 2020 11:29:52 +0800 Subject: [PATCH 195/343] blog detect --- imagepy/menus/Analysis/label_plg.py | 25 ++- .../menus/File/Samples Local/samples_plgs.py | 14 +- imagepy/menus/Process/Binary/binary_plgs.py | 12 +- imagepy/menus/Process/Features/__init__.py | 2 +- imagepy/menus/Process/Features/blob_plgs.py | 170 ++++++++++++++++++ .../tools/Draw/{aibursh.gif => aibrush.gif} | Bin imagepy/tools/Draw/aibrush_tol.py | 25 +-- imagepy/ui/canvasframe.py | 3 +- 8 files changed, 230 insertions(+), 21 deletions(-) create mode 100644 imagepy/menus/Process/Features/blob_plgs.py rename imagepy/tools/Draw/{aibursh.gif => aibrush.gif} (100%) diff --git a/imagepy/menus/Analysis/label_plg.py b/imagepy/menus/Analysis/label_plg.py index 573d8122..b8a98624 100644 --- a/imagepy/menus/Analysis/label_plg.py +++ b/imagepy/menus/Analysis/label_plg.py @@ -5,6 +5,7 @@ """ import numpy as np from scipy.ndimage import label, generate_binary_structure +from skimage.segmentation import find_boundaries from imagepy import IPy from imagepy.core import ImagePlus from imagepy.core.engine import Filter, Simple @@ -27,7 +28,27 @@ def run(self, ips, imgs, para = None): lab, n = label(imgs[i], strc, output = np.int32) labels.append(lab) IPy.show_img(labels, ips.title+'-label') + +class Boundaries(Simple): + title = 'Mark Boundaries' + note = ['8-bit', '16-bit','int'] + para = {'slice':False, 'mode':'outer', 'con':'4-Connect'} + view = [(list, 'con', ['4-Connect','8-Connect'], str, 'Structure', 'connect'), + (list, 'mode', ['thick', 'inner', 'outer', 'subpixel'], str, 'mode', ''), + (bool, 'slice', 'slice')] + def run(self, ips, imgs, para = None): + if not para['slice']: imgs = [ips.img] + labels = [] + for i in range(len(imgs)): + self.progress(i, len(imgs)) + con = 1 if para['con']=='4-Connect' else 2 + bound = find_boundaries(imgs[i], con, para['mode']) + bound.dtype = np.uint8 + bound *= 255 + labels.append(bound) + IPy.show_img(labels, ips.title+'-boundary') + class Render(Simple): title = 'Label Render' note = ['8-bit', '16-bit', 'int'] @@ -36,7 +57,7 @@ class Render(Simple): (int, 'colors', (4, 255), 0, 'colors', 'n'), (bool, 'back', 'background'), (bool, 'slice', 'slice')] - + def run(self, ips, imgs, para = None): if not para['slice']: imgs = [ips.img] print(para) @@ -57,4 +78,4 @@ def run(self, ips, imgs, para = None): ips.range = (0, para['colors']) IPy.show_ips(ips) -plgs = [Label, Render] \ No newline at end of file +plgs = [Label, Boundaries, Render] \ No newline at end of file diff --git a/imagepy/menus/File/Samples Local/samples_plgs.py b/imagepy/menus/File/Samples Local/samples_plgs.py index 2521ddc2..39e01158 100644 --- a/imagepy/menus/File/Samples Local/samples_plgs.py +++ b/imagepy/menus/File/Samples Local/samples_plgs.py @@ -13,14 +13,20 @@ def __init__(self, title): def run(self, para = None): img = self.data() - if img.dtype != np.uint8: - img = img.astype(np.uint8) + if isinstance(img, tuple): + return IPy.show_img(list(img), self.title) + if img.dtype == np.bool: + img.dtype = np.uint8 + img *= 255 IPy.show_img([img], self.title) def __call__(self): return self -datas = ['face', 'ascent', '-', 'page', 'astronaut', 'horse', 'camera', - 'coffee', 'hubble_deep_field', 'coins', 'immunohistochemistry', 'moon'] +datas = ['face', 'ascent', '-', 'binary_blobs', 'brick', 'astronaut', + 'camera', 'cell', 'checkerboard', 'chelsea', 'clock', 'coffee', 'coins', + 'colorwheel', 'grass', 'gravel', 'horse', 'hubble_deep_field', + 'immunohistochemistry', 'microaneurysms', 'moon', 'page', + 'text', 'retina', 'rocket', 'rough_wall', 'shepp_logan_phantom', 'stereo_motorcycle'] plgs = [i if i=='-' else Data(i) for i in datas] \ No newline at end of file diff --git a/imagepy/menus/Process/Binary/binary_plgs.py b/imagepy/menus/Process/Binary/binary_plgs.py index bff609b5..366da8e3 100644 --- a/imagepy/menus/Process/Binary/binary_plgs.py +++ b/imagepy/menus/Process/Binary/binary_plgs.py @@ -10,6 +10,7 @@ import numpy as np from imagepy.core.engine import Filter from skimage.morphology import convex_hull_object +from skimage.segmentation import clear_border class Closing(Filter): """Closing: derived from imagepy.core.engine.Filter """ @@ -82,13 +83,20 @@ def run(self, ips, snap, img, para = None): ndimg.binary_fill_holes(snap, output=img) img *= 255 +class ClearBorder(Filter): + title = 'Clear Border' + note = ['8-bit', 'auto_msk', 'auto_snap'] + + def run(self, ips, snap, img, para = None): + clear_border(img, in_place=True) + + class Convex(Filter): title = 'Binary ConvexHull' note = ['8-bit', 'auto_msk', 'auto_snap'] - #process def run(self, ips, snap, img, para = None): img[convex_hull_object(snap)] = 255 -plgs = [Dilation, Erosion, '-', Closing, Opening, '-', Outline, FillHoles, Convex] \ No newline at end of file +plgs = [Dilation, Erosion, '-', Closing, Opening, '-', Outline, FillHoles, ClearBorder, Convex] \ No newline at end of file diff --git a/imagepy/menus/Process/Features/__init__.py b/imagepy/menus/Process/Features/__init__.py index cf734479..6c24d1c3 100644 --- a/imagepy/menus/Process/Features/__init__.py +++ b/imagepy/menus/Process/Features/__init__.py @@ -1 +1 @@ -catlog = ['corner_plgs', '-', 'edge_plgs'] \ No newline at end of file +catlog = ['edge_plgs', '-', 'corner_plgs', '-', 'ridge_plgs', '-', 'blob_plgs'] \ No newline at end of file diff --git a/imagepy/menus/Process/Features/blob_plgs.py b/imagepy/menus/Process/Features/blob_plgs.py new file mode 100644 index 00000000..9072ebd5 --- /dev/null +++ b/imagepy/menus/Process/Features/blob_plgs.py @@ -0,0 +1,170 @@ +from imagepy import IPy +import numpy as np +from imagepy.core.engine import Simple +from skimage.feature import blob_dog, blob_doh, blob_log +from imagepy.core.mark import GeometryMark +import pandas as pd + +class Dog(Simple): + title = 'Blob Dog' + note = ['all', 'preview'] + para = {'min_sigma':1, 'max_sigma':50, 'sigma_ratio':1.6, 'threshold':0.1, + 'overlap':0.5, 'exclude_border':False, 'showid':True, 'slice':False} + + view = [(int, 'min_sigma', (1, 50), 0, 'min', 'sigma'), + (int, 'max_sigma', (1, 50), 0, 'max', 'sigma'), + (float, 'sigma_ratio', (1.3, 5), 1, 'ratio', '1.3~5'), + (float, 'threshold', (0.1, 10), 1, 'threshold', '0.1~10'), + (float, 'overlap', (0, 10), 1, 'overlap', ''), + (bool, 'exclude_border', 'exclude border'), + (bool, 'showid', 'show id on image'), + (bool, 'slice', 'slice')] + + def preview(self, ips, para): + grayimg = ips.img if ips.img.ndim==2 else ips.img.mean(axis=-1) + grayimg /= grayimg.max() + pts = blob_dog(grayimg, min_sigma=para['min_sigma'], max_sigma=para['max_sigma'], + sigma_ratio=para['sigma_ratio'], threshold=para['threshold'], + overlap=para['overlap'], exclude_border=para['exclude_border']) + pts[:,2] *= np.sqrt(2) + ips.mark = GeometryMark({'type':'circles', 'body':pts[:,[1,0,2]]}) + + def cancel(self, ips): ips.mark = None + + def run(self, ips, imgs, para = None): + if not para['slice']:imgs = [ips.img] + + data, sid, fid, mark = [], [], [], {'type':'layers', 'body':{}} + + for i in range(len(imgs)): + grayimg = imgs[i] if imgs[i].ndim==2 else imgs[i].mean(axis=-1) + grayimg /= grayimg.max() + pts = blob_dog(grayimg, min_sigma=para['min_sigma'], max_sigma=para['max_sigma'], + sigma_ratio=para['sigma_ratio'], threshold=para['threshold'], + overlap=para['overlap'], exclude_border=para['exclude_border']) + pts[:,2] *= np.sqrt(2) + sid.extend([i]*len(pts)) + fid.extend(range(1, len(pts)+1)) + data.append(pts) + + layer = {'type':'layer', 'body':[{'type':'circles', 'body':pts[:,[1,0,2]]}]} + if para['showid']: + layer['body'].append({'type':'texts', 'body':[ + (x,y,'id=%d'%i) for (x,y),i in zip(pts[:,1::-1], fid)]}) + mark['body'][i] = layer + + ips.mark = GeometryMark(mark) + df = pd.DataFrame(np.vstack(data)*ips.unit[0], columns = ['X', 'Y', 'R']) + df.insert(0, 'FID', fid) + if para['slice']: df.insert(o, 'SliceID', sid) + IPy.show_table(df, ips.title+'-dogblob') + +class Doh(Simple): + title = 'Blob Doh' + note = ['all', 'preview'] + para = {'min_sigma':1, 'max_sigma':30, 'num_sigma':10, 'threshold':0.01, + 'overlap':0.5, 'log_scale':False, 'showid':True, 'slice':False} + + view = [(int, 'min_sigma', (1, 50), 0, 'min', 'sigma'), + (int, 'max_sigma', (1, 50), 0, 'max', 'sigma'), + (int, 'num_sigma', (5, 30), 0, 'num', 'sigma'), + (float, 'threshold', (0.01, 1), 2, 'threshold', '0.1~10'), + (float, 'overlap', (0, 10), 1, 'overlap', ''), + (bool, 'log_scale', 'log scale'), + (bool, 'showid', 'show id on image'), + (bool, 'slice', 'slice')] + + def preview(self, ips, para): + grayimg = ips.img if ips.img.ndim==2 else ips.img.mean(axis=-1) + grayimg /= grayimg.max() + pts = blob_doh(grayimg, min_sigma=para['min_sigma'], max_sigma=para['max_sigma'], + num_sigma=para['num_sigma'], threshold=para['threshold'], + overlap=para['overlap'], log_scale=para['log_scale']) + ips.mark = GeometryMark({'type':'circles', 'body':pts[:,[1,0,2]]}) + + def cancel(self, ips): ips.mark = None + + def run(self, ips, imgs, para = None): + if not para['slice']:imgs = [ips.img] + + data, sid, fid, mark = [], [], [], {'type':'layers', 'body':{}} + + for i in range(len(imgs)): + grayimg = imgs[i] if imgs[i].ndim==2 else imgs[i].mean(axis=-1) + grayimg /= grayimg.max() + pts = blob_doh(grayimg, min_sigma=para['min_sigma'], max_sigma=para['max_sigma'], + num_sigma=para['num_sigma'], threshold=para['threshold'], + overlap=para['overlap'], log_scale=para['log_scale']) + + sid.extend([i]*len(pts)) + fid.extend(range(1, len(pts)+1)) + data.append(pts) + + layer = {'type':'layer', 'body':[{'type':'circles', 'body':pts[:,[1,0,2]]}]} + if para['showid']: + layer['body'].append({'type':'texts', 'body':[ + (x,y,'id=%d'%i) for (x,y),i in zip(pts[:,1::-1], fid)]}) + mark['body'][i] = layer + + ips.mark = GeometryMark(mark) + df = pd.DataFrame(np.vstack(data)*ips.unit[0], columns = ['X', 'Y', 'R']) + df.insert(0, 'FID', fid) + if para['slice']: df.insert(o, 'SliceID', sid) + IPy.show_table(df, ips.title+'-dohblob') + +class Log(Simple): + title = 'Blob Log' + note = ['all', 'preview'] + para = {'min_sigma':1, 'max_sigma':30, 'num_sigma':10, 'threshold':0.1, 'overlap':0.5, + 'log_scale':False, 'showid':True, 'exclude_border':False, 'slice':False} + + view = [(int, 'min_sigma', (1, 50), 0, 'min', 'sigma'), + (int, 'max_sigma', (1, 50), 0, 'max', 'sigma'), + (int, 'num_sigma', (5, 30), 0, 'num', 'sigma'), + (float, 'threshold', (0.01, 1), 2, 'threshold', '0.02~1'), + (float, 'overlap', (0, 10), 1, 'overlap', ''), + (bool, 'log_scale', 'log scale'), + (bool, 'exclude_border', 'exclude border'), + (bool, 'showid', 'show id on image'), + (bool, 'slice', 'slice')] + + def preview(self, ips, para): + grayimg = ips.img if ips.img.ndim==2 else ips.img.mean(axis=-1) + grayimg /= grayimg.max() + pts = blob_log(grayimg, min_sigma=para['min_sigma'], max_sigma=para['max_sigma'], + num_sigma=para['num_sigma'], threshold=para['threshold'], + overlap=para['overlap'], log_scale=para['log_scale'], exclude_border=para['exclude_border']) + pts[:,2] *= np.sqrt(2) + ips.mark = GeometryMark({'type':'circles', 'body':pts[:,[1,0,2]]}) + + def cancel(self, ips): ips.mark = None + + def run(self, ips, imgs, para = None): + if not para['slice']:imgs = [ips.img] + + data, sid, fid, mark = [], [], [], {'type':'layers', 'body':{}} + + for i in range(len(imgs)): + grayimg = imgs[i] if imgs[i].ndim==2 else imgs[i].mean(axis=-1) + grayimg /= grayimg.max() + pts = blob_log(grayimg, min_sigma=para['min_sigma'], max_sigma=para['max_sigma'], + num_sigma=para['num_sigma'], threshold=para['threshold'], + overlap=para['overlap'], log_scale=para['log_scale'], exclude_border=para['exclude_border']) + pts[:,2] *= np.sqrt(2) + sid.extend([i]*len(pts)) + fid.extend(range(1, len(pts)+1)) + data.append(pts) + + layer = {'type':'layer', 'body':[{'type':'circles', 'body':pts[:,[1,0,2]]}]} + if para['showid']: + layer['body'].append({'type':'texts', 'body':[ + (x,y,'id=%d'%i) for (x,y),i in zip(pts[:,1::-1], fid)]}) + mark['body'][i] = layer + + ips.mark = GeometryMark(mark) + df = pd.DataFrame(np.vstack(data)*ips.unit[0], columns = ['X', 'Y', 'R']) + df.insert(0, 'FID', fid) + if para['slice']: df.insert(o, 'SliceID', sid) + IPy.show_table(df, ips.title+'-dohblob') + +plgs = [Dog, Doh, Log] \ No newline at end of file diff --git a/imagepy/tools/Draw/aibursh.gif b/imagepy/tools/Draw/aibrush.gif similarity index 100% rename from imagepy/tools/Draw/aibursh.gif rename to imagepy/tools/Draw/aibrush.gif diff --git a/imagepy/tools/Draw/aibrush_tol.py b/imagepy/tools/Draw/aibrush_tol.py index 2bda9bb1..83c192a3 100644 --- a/imagepy/tools/Draw/aibrush_tol.py +++ b/imagepy/tools/Draw/aibrush_tol.py @@ -15,8 +15,8 @@ def fill_normal(img, r, c, color, con, tor): msk &= flood(img[:,:,i], (r, c), connectivity=con, tolerance=tor) img[msk] = color -def local_brush(img, r, c, color, sigma, msize): - lab = felzenszwalb(img, 1, sigma, msize) +def local_brush(img, back, r, c, color, sigma, msize): + lab = felzenszwalb(back, 1, sigma, msize) msk = flood(lab, (r, c), connectivity=2) img[msk] = color @@ -127,10 +127,11 @@ def global_out_fill(img, r, c, color): scale and move: | wheel ''' + class Plugin(Tool): - title = 'AI Painter' + title = 'AI Brush' - para = {'win':48, 'tor':10, 'con':'8-connect', 'ms':20, 'r':2, 'color':(255,0,128)} + para = {'win':48, 'tor':10, 'con':'8-connect', 'ms':30, 'r':2, 'color':(255,0,128)} view = [(int, 'win', (28, 64), 0, 'window', 'size'), ('color', 'color', 'color', 'mark'), ('lab', None, '======= Brush ======='), @@ -244,19 +245,21 @@ def mouse_move(self, ips, x, y, btn, **key): sr = (max(0,r-w), min(img.shape[0], r+w)) sc = (max(0,c-w), min(img.shape[1], c+w)) r, c = min(r, w), min(c, w) - clip = img[slice(*sr), slice(*sc)] + backclip = imgclip = img[slice(*sr), slice(*sc)] + if not ips.back is None: + backclip = ips.back.img[slice(*sr), slice(*sc)] if self.status == 'local_pen': - local_pen(clip, r, c, self.para['r'], color) + local_pen(imgclip, r, c, self.para['r'], color) if self.status == 'local_brush': - if (clip[r,c] - color).sum()==0: continue - local_brush(clip, r, c, color, 0, self.para['ms']) + if (imgclip[r,c] - color).sum()==0: continue + local_brush(imgclip, backclip, r, c, color, 0, self.para['ms']) if self.status == 'local_in': - local_in_fill(clip, r, c, self.para['r'], self.pickcolor, color) + local_in_fill(imgclip, r, c, self.para['r'], self.pickcolor, color) if self.status == 'local_sketch': - local_sketch(clip, r, c, self.para['r'], self.pickcolor, color) + local_sketch(imgclip, r, c, self.para['r'], self.pickcolor, color) if self.status=='local_out': - local_out_fill(clip, r, c, self.para['r'], self.pickcolor, color) + local_out_fill(imgclip, r, c, self.para['r'], self.pickcolor, color) ips.mark = self.make_mark(x, y) diff --git a/imagepy/ui/canvasframe.py b/imagepy/ui/canvasframe.py index 5d9ecf3e..821a7b53 100644 --- a/imagepy/ui/canvasframe.py +++ b/imagepy/ui/canvasframe.py @@ -132,7 +132,8 @@ def on_idle(self, event): self.canvas.set_rg(self.ips.chan_range) if self.ips.back != None: - self.canvas.set_back(self.ips.back.imgs[self.ips.cur]) + self.ips.back.cur = self.ips.cur + self.canvas.set_back(self.ips.back.img) self.canvas.set_cn(self.ips.back.chan, True) self.canvas.set_rg(self.ips.back.chan_range, True) self.canvas.set_lut(self.ips.back.lut, True) From 72808a30213e931c8f53c04f29e6ecd2ccacfb85 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 10 Feb 2020 11:07:31 +0800 Subject: [PATCH 196/343] compleximage view support --- imagepy/__init__.py | 1 + imagepy/core/manager/windowmanager.py | 1 - imagepy/core/wraper/imageplus.py | 6 +- imagepy/menus/Process/FFT/fft_plgs.py | 44 ++++++----- imagepy/ui/canvas/canvas.py | 58 ++++++++------ imagepy/ui/canvas/imutil.py | 107 ++++++++++++++++++++------ imagepy/ui/canvasframe.py | 2 + 7 files changed, 148 insertions(+), 71 deletions(-) diff --git a/imagepy/__init__.py b/imagepy/__init__.py index 0e2141b9..f5043bf3 100644 --- a/imagepy/__init__.py +++ b/imagepy/__init__.py @@ -6,6 +6,7 @@ import wx.lib.agw.advancedsplash as AS from .IPy import * +from .core import ImagePlus, TablePlus root_dir = osp.abspath(osp.dirname(__file__)) os.chdir(root_dir) diff --git a/imagepy/core/manager/windowmanager.py b/imagepy/core/manager/windowmanager.py index ddb18fbd..62043dd0 100644 --- a/imagepy/core/manager/windowmanager.py +++ b/imagepy/core/manager/windowmanager.py @@ -26,7 +26,6 @@ def remove(cls, win): for i in cls.wins: if i == win: cls.wins.remove(i) - print('remove', i.ips.title) class ImageManager: imgs = [] diff --git a/imagepy/core/wraper/imageplus.py b/imagepy/core/wraper/imageplus.py index 2b5d4a65..82724396 100644 --- a/imagepy/core/wraper/imageplus.py +++ b/imagepy/core/wraper/imageplus.py @@ -9,7 +9,8 @@ def get_img_type(imgs): if imgs[0].dtype == np.int32:return '32-int' if imgs[0].dtype == np.float32:return '32-float' if imgs[0].dtype == np.float64:return '64-float' - if imgs[0].dtype == np.complex128:return 'complex' + if imgs[0].dtype == np.complex128:return '128-complex' + if imgs[0].dtype == np.complex64:return '64-complex' def get_updown(imgs, slices='all', chans='all', step=1): c = chans if isinstance(chans, int) else slice(None) @@ -23,6 +24,8 @@ def get_updown(imgs, slices='all', chans='all', step=1): mins = np.array(mins).reshape((len(mins),-1)) maxs = np.array(maxs).reshape((len(maxs),-1)) mins, maxs = mins.min(axis=0), maxs.max(axis=0) + if np.iscomplexobj(mins): + mins, maxs = np.zeros(mins.shape), np.abs(maxs) if chans!='all': return mins.min(), maxs.max() return [(i,j) for i,j in zip(mins, maxs)] @@ -50,6 +53,7 @@ def __init__(self, imgs, title=None, is3d=False): self.msk = None self.mskmode = None self.lut = ColorManager.get_lut('grays') + self.log = False self.tool = None self.data = {} diff --git a/imagepy/menus/Process/FFT/fft_plgs.py b/imagepy/menus/Process/FFT/fft_plgs.py index a1a7c09b..1af81544 100644 --- a/imagepy/menus/Process/FFT/fft_plgs.py +++ b/imagepy/menus/Process/FFT/fft_plgs.py @@ -2,6 +2,7 @@ from imagepy.core.engine import Simple, Filter from numpy.fft import fft2, ifft2, fftshift, ifftshift from imagepy import IPy +from imagepy.core import ImagePlus class FFT(Simple): title = 'FFT' @@ -17,26 +18,9 @@ def run(self, ips, imgs, para = None): for i in range(len(imgs)): rst.append(shift(fft2(imgs[i]))) self.progress(i, len(imgs)) - IPy.show_img(rst, '%s-fft'%ips.title) - -class LogPower(Simple): - title = 'Log Power' - note = ['complex'] - para = {'slice':False, 'type':'float', 'log':2.718} - view = [(float, 'log', (2,30), 3, 'log', ''), - (list, 'type', ['uint8', 'int', 'float'], str, 'type', ''), - (bool, 'slice', 'slices')] - - def run(self, ips, imgs, para = None): - if not para['slice']: imgs = [ips.img] - tp = {'uint8':np.uint8, 'int':np.int32, 'float':np.float32} - rst, tp = [], tp[para['type']] - for i in range(len(imgs)): - zs = np.log(np.abs(imgs[i])) - zs /= np.log(para['log']) - rst.append(zs.astype(tp)) - self.progress(i, len(imgs)) - IPy.show_img(rst, '%s-fft'%ips.title) + ips = ImagePlus(rst, '%s-fft'%ips.title) + ips.log = True + IPy.show_ips(ips) class IFFT(Simple): title = 'Inverse FFT' @@ -70,4 +54,22 @@ class IShift(Filter): def run(self, ips, snap, img, para = None): return ifftshift(img) -plgs = [FFT, IFFT, '-', Shift, IShift, LogPower] \ No newline at end of file +class Split(Simple): + title = 'Split Real And Image' + note = ['complex'] + para = {'slice':False, 'copy':False} + view = [(bool, 'slice', 'slices'), + (bool, 'copy', 'memory copy')] + + def run(self, ips, imgs, para = None): + if not para['slice']: imgs = [ips.img] + copy = np.copy if para['copy'] else lambda x:x + imags, reals = [], [] + for i in range(len(imgs)): + reals.append(copy(imgs[i].real)) + imags.append(copy(imgs[i].imag)) + self.progress(i, len(imgs)) + IPy.show_img(reals, '%s-real'%ips.title) + IPy.show_img(imags, '%s-image'%ips.title) + +plgs = [FFT, IFFT, '-', Shift, IShift, '-', Split] \ No newline at end of file diff --git a/imagepy/ui/canvas/canvas.py b/imagepy/ui/canvas/canvas.py index 55775da6..00a6b696 100644 --- a/imagepy/ui/canvas/canvas.py +++ b/imagepy/ui/canvas/canvas.py @@ -25,17 +25,19 @@ def __init__(self, parent, autofit=False): self.buffer = None - lut = np.arange(256*3) + lut = np.arange(256*3)//3 lut.shape = (256,3) lut = lut.astype(np.uint8) self.lut = lut self.rg = (0, 255) self.cn = 0 + self.log = False self._lut = lut self._rg = (0, 255) self._cn = 0 + self._log = False self.marks = {} @@ -99,6 +101,10 @@ def set_img(self, img): def set_back(self, back): self.back = back + def set_log(self, log, b=False): + if b: self._log = log + else: self.log = log + def set_rg(self, rg, b=False): if b: self._rg = rg else: self.rg = rg @@ -158,17 +164,17 @@ def draw_image(self, dc, img, back, mode): #if not back is None: print('has back image') mix_img(back, m, o, shp, self.outbak, - self.outrgb, self.outint, - self._rg, self._lut, cns=self._cn, mode='set') + self.outrgb, self.outint, self._rg, + self._lut, self._log, cns=self._cn, mode='set') mix_img(self.img, m, o, shp, self.outimg, - self.outrgb, self.outint, - self.rg, self.lut, cns=self.cn, mode=self.mode) - + self.outrgb, self.outint, self.rg, + self.lut, self.log, cns=self.cn, mode=self.mode) self.outbmp.CopyFromBuffer(memoryview(self.outrgb)) dc.DrawBitmap(self.outbmp, *csbox[:2]) - def update(self): + def update(self, counter = [0,0]): + counter[0] += 1 start = time() lay(self.winbox, self.conbox) dc = wx.BufferedDC(wx.ClientDC(self), self.buffer) @@ -181,8 +187,11 @@ def update(self): else: drawmark(dc, self.to_panel_coor, self.marks[i], k=self.scale) dc.UnMask() - print('frame rate:',int(1/max(0.001, time()-start))) - + counter[1] += time()-start + if counter[0] == 10: + print('frame rate:',int(10/max(0.001,counter[1]))) + counter[0] = counter[1] = 0 + def center(self, x, y, coord='win'): if coord=='data': x,y = self.to_panel_coor(x, y) @@ -229,24 +238,27 @@ def __del__(self): print('========== canvas del') if __name__=='__main__': - msk = np.zeros((512,512), dtype=np.uint8) - msk[100:200,100:200] = 1 - msk[200:300,200:300] = 2 - msk[300:400,300:400] = 3 - lut = np.array([(0,0,0),(255,0,0),(0,255,0),(0,0,255)], dtype=np.uint8) - from skimage.data import astronaut, camera + from numpy.fft import fft2, ifft2, fftshift, ifftshift + import matplotlib.pyplot as plt + + img = camera() + img = fftshift(fft2(img)) + farr = img.view(dtype=np.float64) + #a = farr.reshape((512,2,512)).transpose(0,2,1) + # farr.shape = img.shape+(-1,) + #plt.imshow(np.log(np.abs(a[:,:,1]))) + #plt.show() + app = wx.App() frame = wx.Frame(None) canvas = Canvas(frame) - canvas.set_img(msk) - canvas.set_lut(lut) + + canvas.set_img(img) + # canvas.set_rg((-128, 127)) + canvas.set_rg((0,31015306)) canvas.set_cn(0) - canvas.set_back(astronaut()) - canvas.set_cn((0,1,2), True) - canvas.set_mode(0.5) - x = np.arange(512) - y = np.sin(x/30) * 100 + 256 - canvas.marks['line'] = {'type':'line', 'lw':3, 'body':np.array([x,y]).T.tolist()} + canvas.set_log(True) frame.Show(True) + frame.SetSize(512,512) app.MainLoop() diff --git a/imagepy/ui/canvas/imutil.py b/imagepy/ui/canvas/imutil.py index 2da877a0..3cde4e95 100644 --- a/imagepy/ui/canvas/imutil.py +++ b/imagepy/ui/canvas/imutil.py @@ -1,9 +1,11 @@ import numpy as np from scipy.ndimage import affine_transform try: from numba import njit as jit -except: jit = None +except: + print('install numba may be several times faster!') + jit = None # jit = None - + def affine_jit(img, m, offset, output_shape=0, output=0, order=0, prefilter=0): kr=m[0]; kc=m[1]; ofr=offset[0]; ofc=offset[1]; for r in range(output_shape[0]): @@ -61,34 +63,88 @@ def blend_jit(img, out, msk, mode): if isinstance(mode, float): blend_mix(img, out, msk, mode) blend = blend_jit -def stretch(img, out, rg, mode='set'): - if img.dtype==np.uint8 and (rg==[(0,255)] or rg==(0,255)): +def stretch(img, out, rg, log=False): + if img.dtype==np.uint8 and not log and (rg==[(0,255)] or rg==(0,255)): out[:] = img - else: + elif not log: + ptp = max(rg[1]-rg[0], 1e-6) np.clip(img, rg[0], rg[1], out=img) np.subtract(img, rg[0], out=img, casting='unsafe') - np.multiply(img, 255.0/np.ptp(rg), out=out, casting='unsafe') + np.multiply(img, 255/ptp, out=out, casting='unsafe') + elif img.itemsize<3: + length = 2**(img.itemsize*8) + lut = np.arange(length, dtype=np.float32) + if img.dtype in (np.int8, np.int16): + lut[length//2:] -= length + np.clip(lut, rg[0], rg[1], out=lut) + np.subtract(lut, rg[0]-1, out=lut) + ptp = np.log(max(rg[1]-rg[0]+1, 1+1e-6)) + np.log(lut, out=lut) + lut *= 255/np.log(max(rg[1]-rg[0]+1, 1+1e-6)) + out[:] = lut[img] + else: + fimg = img.ravel().view(np.float32) + fimg = fimg[:img.size].reshape(img.shape) + np.clip(img, rg[0], rg[1], out=fimg) + np.subtract(fimg, rg[0]-1, out=fimg) + ptp = np.log(max(rg[1]-rg[0]+1, 1+1e-6)) + np.log(fimg, out=fimg) + np.multiply(fimg, 255/ptp, out=out, casting='unsafe') if not jit is None: @jit - def stretch_int(img, out, rg): + def stretch_linear(img, out, rg): + ptp = max(rg[1]-rg[0], 1e-6) for r in range(img.shape[0]): for c in range(img.shape[1]): - out[r,c] = img[r,c] + v = (img[r,c]-rg[0])/ptp*255 + out[r,c] = min(max(v, 0), 255) @jit - def stretch_other(img, out, rg): - ptp = max(rg[1]-rg[0], 1e-6) + def stretch_log(img, out, rg): + ptp = 255/np.log(max(rg[1]-rg[0]+1, 1+1e-6)) for r in range(img.shape[0]): for c in range(img.shape[1]): - v = (img[r,c]-rg[0])/ptp*255 + v = np.log(img[r,c]-rg[0]+1)*ptp out[r,c] = min(max(v, 0), 255) - def stretch_jit(img, out, rg): - # print(img.dtype, rg) - if img.dtype==np.uint8 and (rg==[(0,255)] or rg==(0,255)): - stretch_int(img, out, rg) - else: stretch_other(img, out, rg) + @jit + def stretch_lut(img, out, lut): + for r in range(img.shape[0]): + for c in range(img.shape[1]): + out[r,c] = lut[img[r,c]] + + def stretch_jit(img, out, rg, log=False): + if img.dtype==np.uint8 and not log and (rg==[(0,255)] or rg==(0,255)): + out[:] = img + elif not log: + stretch_linear(img, out, rg) + elif img.itemsize<3: + length = 2**(img.itemsize*8) + lut = np.arange(length, dtype=np.float32) + if img.dtype in (np.int8, np.int16): + lut[length//2:] -= length + np.clip(lut, rg[0], rg[1], out=lut) + np.subtract(lut, rg[0]-1, out=lut) + ptp = np.log(max(rg[1]-rg[0]+1, 1+1e-6)) + np.log(lut, out=lut) + lut *= 255/np.log(max(rg[1]-rg[0]+1, 1+1e-6)) + stretch_lut(img, out, lut) + else: + stretch_log(img, out, rg) + stretch = stretch_jit +def complex_norm(ori, real, img, out): + np.abs(ori, out=out) + return out + +if not jit is None: + @jit + def complex_norm(ori, real, img, out): + for r in range(img.shape[0]): + for c in range(img.shape[1]): + out[r,c] = (img[r,c]**2+real[r,c]**2)**0.5 + return out + def lookup(img, lut, out, mode='set'): blend(lut[img], out, img, mode) @@ -136,29 +192,30 @@ def lookup_jit(img, lut, out, mode): lookup = lookup_jit # mode: set, min, max, mix, nor -def mix_img(img, m, o, shp, buf, rgb, byt, rg=(0,255), lut=None, cns=0, mode='set'): +def mix_img(img, m, o, shp, buf, rgb, byt, rg=(0,255), lut=None, log=True, cns=0, mode='set'): if img is None: return img = img.reshape((img.shape[0], img.shape[1], -1)) if isinstance(rg, tuple): rg = [rg]*img.shape[2] if isinstance(cns, int): - # print(img.dtype, buf.dtype, 'type') - affine_transform(img[:,:,cns], m, o, shp, buf, 0, prefilter=False) - stretch(buf, byt, rg[cns]) + if np.iscomplexobj(buf): + affine_transform(img[:,:,0].real, m, o, shp, buf.real, 0, prefilter=False) + affine_transform(img[:,:,0].imag, m, o, shp, buf.imag, 0, prefilter=False) + buf = complex_norm(buf, buf.real, buf.imag, buf.real) + else: affine_transform(img[:,:,cns], m, o, shp, buf, 0, prefilter=False) + stretch(buf, byt, rg[cns], log) return lookup(byt, lut, rgb, mode) - for i,v in enumerate(cns): if v==-1: rgb[:,:,i] = 0 - elif mode=='set' and buf.dtype==np.uint8 and rg[v]==(0,255): + elif mode=='set' and img.dtype==np.uint8 and rg[v]==(0,255) and not log: affine_transform(img[:,:,v], m, o, shp, rgb[:,:,i], 0, prefilter=False) else: affine_transform(img[:,:,v], m, o, shp, buf, 0, prefilter=False) - stretch(buf, byt, rg[v]) + stretch(buf, byt, rg[v], log) blend(byt, rgb[:,:,i], byt, mode) - ''' rgb = np.array([[[0,0,0]]], dtype=np.uint8) lut = np.array([[0,0,0]], dtype=np.uint8) mix_img(img, (1,1), (0,0), (1,1), img, rgb, img, (0,255), lut, 0, 'set') -''' \ No newline at end of file +''' diff --git a/imagepy/ui/canvasframe.py b/imagepy/ui/canvasframe.py index 821a7b53..430ceffa 100644 --- a/imagepy/ui/canvasframe.py +++ b/imagepy/ui/canvasframe.py @@ -130,11 +130,13 @@ def on_idle(self, event): self.canvas.set_img(self.ips.imgs[self.ips.cur]) self.canvas.set_cn(self.ips.chan) self.canvas.set_rg(self.ips.chan_range) + self.canvas.set_log(self.ips.log) if self.ips.back != None: self.ips.back.cur = self.ips.cur self.canvas.set_back(self.ips.back.img) self.canvas.set_cn(self.ips.back.chan, True) + self.canvas.set_log(self.ips.back.log, True) self.canvas.set_rg(self.ips.back.chan_range, True) self.canvas.set_lut(self.ips.back.lut, True) self.canvas.set_mode(self.ips.chan_mode) From ee94361bac45040f513a99125a3f3383f4c35b03 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 11 Feb 2020 13:18:54 +0800 Subject: [PATCH 197/343] add cellpose plugin to index --- .../Contribute/Contributions/CellPose.md | 70 +++++++++++++++++++ .../menus/Plugins/Contribute/pmanager_wgt.py | 4 +- 2 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 imagepy/menus/Plugins/Contribute/Contributions/CellPose.md diff --git a/imagepy/menus/Plugins/Contribute/Contributions/CellPose.md b/imagepy/menus/Plugins/Contribute/Contributions/CellPose.md new file mode 100644 index 00000000..b2d2b2e3 --- /dev/null +++ b/imagepy/menus/Plugins/Contribute/Contributions/CellPose.md @@ -0,0 +1,70 @@ +# cellpose-plgs + +**Path:** https://github.com/Image-Py/cellpose-plgs + +**Version:** 0.1 + +**Author:** YXDragon, Carsen-Stringer + +**Email:** yxdragon@imagepy.org + +**Keyword:** cellpose, segment, unet + +**Description:** cellpose is a generalist algorithm for cell and nucleus segmentation, this is a cellpose plugin for imagepy. + + + +## Document +### Install + +there are two method to install cellpose-plgs, Install Plugins or use Plugins Manager + +1. **Menus: Plugins > Install > Install Plugins** then input https://github.com/Image-Py/cellpose-plgs + + ![](http://skimgplgs.imagepy.org/cellpose/install.png) + + + +2. **Menus: Plugins > Manager > Plugins Manager**, befor you open the manager, you should update the software, (to getting the newest plugins catlog). for git version, please pull, for release version, please use *Menus: Plugins > Updata Software*. + + ![](http://skimgplgs.imagepy.org/cellpose/manager.png) + +when the cellpose-plgs is installed seccessfully, The cellpose would appears below the **Plugins** menus. + + + +### Usage + +**Menus: File > Open** to Open a Image, you can also use **Menus: File > Import > Import Sequence** to open a sequence. Then **Menus: Plugins > Cell Pose > Cell Pose Eval**, you would got a parameter dialog, Setting the parameter, then OK. + + + +**Parameters:** + +**model:** select model + +**cytoplasm:** select the cytomplasm channel. 0: gray, 1:red, 2:green, 3:blue + +**nucleus:** select the nucleus channel, if there is no, select 0. + +**show color flow:** the color flow result would be shown when checked. + +**show diams tabel:** the diams result would be shown when checked. + +**slice:** process the current image or all images when it is a sequence. + +![](http://skimgplgs.imagepy.org/cellpose/cellpose.png) + + + +### Analysis + +**Menus: Analysis > Region Analysis > Geometry Analysis** do a region analysis. And we can do many other things in ImagePy, such as set a colormap, or overlay the mask on the original image. + +![](http://skimgplgs.imagepy.org/cellpose/region.png) + + + +### Reference + +Thanks for CellPose and the supporting from Carsen Stringer and Marius Pachitariu. More detail about CellPose please read the [paper](https://t.co/4HFsxDezAP?amp=1) or watch the [talk](https://t.co/JChCsTD0SK?amp=1). \ No newline at end of file diff --git a/imagepy/menus/Plugins/Contribute/pmanager_wgt.py b/imagepy/menus/Plugins/Contribute/pmanager_wgt.py index 5b307578..857cba1b 100644 --- a/imagepy/menus/Plugins/Contribute/pmanager_wgt.py +++ b/imagepy/menus/Plugins/Contribute/pmanager_wgt.py @@ -4,7 +4,7 @@ @author: yxl """ -import wx, os, glob, shutil +import wx, os, glob, shutil, random from imagepy import IPy, root_dir from imagepy.core.manager import PluginsManager from imagepy.ui.mkdownwindow import HtmlPanel, md2html @@ -110,7 +110,7 @@ def load(self): keys = set([i['path'] for i in prjs]) for i in has: if not i['path'] in keys: prjs.append(i) - prjs = sorted([(i['name'], i) for i in prjs]) + prjs = sorted([(i['name']+str(random.random()), i) for i in prjs]) self.prjs = [i[1] for i in prjs] for i in self.prjs: From 17aa2927bc1216a8956b6a6f8173817cdd9481bb Mon Sep 17 00:00:00 2001 From: Prevalenter <434625142@qq.com> Date: Fri, 14 Feb 2020 16:25:08 +0800 Subject: [PATCH 198/343] add route_through_array Signed-off-by: Prevalenter <434625142@qq.com> --- imagepy/menus/Analysis/statistic_plg.py | 48 +++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/imagepy/menus/Analysis/statistic_plg.py b/imagepy/menus/Analysis/statistic_plg.py index f34c6af1..0d60270d 100644 --- a/imagepy/menus/Analysis/statistic_plg.py +++ b/imagepy/menus/Analysis/statistic_plg.py @@ -5,13 +5,15 @@ """ from imagepy import IPy, root_dir import wx, numpy as np, os -from imagepy.core.engine import Simple -from imagepy.core.roi import PointRoi +from imagepy.core.engine import Filter,Simple +from imagepy.core.roi import PointRoi,LineRoi from imagepy.core.manager import ImageManager, WindowsManager from imagepy.ui.widgets import HistCanvas from pubsub import pub import pandas as pd +from skimage.graph import route_through_array +from imagepy.core.mark import GeometryMark class HistogramFrame(wx.Frame): def __init__(self, parent, title, hist): wx.Frame.__init__(self, parent, title=title, style = wx.DEFAULT_DIALOG_STYLE) @@ -200,4 +202,44 @@ def run(self, ips, imgs, para = None): IPy.show_table(pd.DataFrame(data, columns=titles), ips.title+'-points') if para['buf']:ips.mark = Mark(mark) -plgs = [Frequence, Statistic, Histogram, PointsValue] \ No newline at end of file +class ShortRoute(Filter): + title = 'Short Route' + note = ['auto_snap','8-bit', '16-bit','int', 'float', 'req_roi','preview'] + + para = {'fully connected':True, 'geometric':True,'type':'white line'} + view = [(bool, 'fully connected', 'fully connected'), + (bool, 'geometric', 'geometric'), + (list, 'type', ['white line', 'gray line', 'white line on ori'], str, 'output', '')] + + def load(self, ips): + if not isinstance(ips.roi, LineRoi): + IPy.alert('LineRoi are needed!') + return False + return True + + def run(self, ips, snap, img, para = None): + img[:] = snap + print(np.array(ips.roi.body)) + pts = np.array(ips.roi.body).astype(np.int) + img_min,img_max=snap.min(),snap.max() + arr=img.copy() + arr = (arr-arr.min())/np.ptp(arr) + + print(pts) + + if para['type']=='gray line' or para['type']=='white line': + img[:] = img_min + for i in pts: + for j in range(len(i)-1): + p0=(i[j][1],i[j][0]) + p1=(i[j+1][1],i[j+1][0]) + indices, weight = route_through_array(arr,p0, p1) + indices=np.array(indices) + if para['type']=='white line on ori': + img[indices[:,0],indices[:,1]]=img_max + elif para['type']=='gray line': + img[indices[:,0],indices[:,1]] = snap[indices[:,0],indices[:,1]] + elif para['type']=='white line': + img[indices[:,0],indices[:,1]] = img_max + +plgs = [Frequence, Statistic, Histogram, PointsValue,ShortRoute] \ No newline at end of file From 1bd5aeca7df0b6019ea523f4d38eac6268f5fb1c Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 15 Feb 2020 21:29:49 +0800 Subject: [PATCH 199/343] shortest route --- imagepy/menus/Analysis/statistic_plg.py | 55 ++++++++++++------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/imagepy/menus/Analysis/statistic_plg.py b/imagepy/menus/Analysis/statistic_plg.py index 0d60270d..c2eb5144 100644 --- a/imagepy/menus/Analysis/statistic_plg.py +++ b/imagepy/menus/Analysis/statistic_plg.py @@ -203,43 +203,42 @@ def run(self, ips, imgs, para = None): if para['buf']:ips.mark = Mark(mark) class ShortRoute(Filter): - title = 'Short Route' - note = ['auto_snap','8-bit', '16-bit','int', 'float', 'req_roi','preview'] + title = 'Shortest Route' + note = ['auto_snap','8-bit', '16-bit','int', 'float', 'req_roi', '2int', 'preview'] - para = {'fully connected':True, 'geometric':True,'type':'white line'} - view = [(bool, 'fully connected', 'fully connected'), + para = {'fully connected':True, 'lcost':0, 'max':False, 'geometric':True, 'type':'white line'} + view = [(float, 'lcost', (0, 1e5), 3, 'step', 'cost'), + (bool, 'max', 'max cost'), + (bool, 'fully connected', 'fully connected'), (bool, 'geometric', 'geometric'), (list, 'type', ['white line', 'gray line', 'white line on ori'], str, 'output', '')] def load(self, ips): if not isinstance(ips.roi, LineRoi): - IPy.alert('LineRoi are needed!') - return False + return IPy.alert('LineRoi are needed!') return True def run(self, ips, snap, img, para = None): img[:] = snap - print(np.array(ips.roi.body)) - pts = np.array(ips.roi.body).astype(np.int) - img_min,img_max=snap.min(),snap.max() - arr=img.copy() - arr = (arr-arr.min())/np.ptp(arr) - - print(pts) - - if para['type']=='gray line' or para['type']=='white line': - img[:] = img_min - for i in pts: - for j in range(len(i)-1): - p0=(i[j][1],i[j][0]) - p1=(i[j+1][1],i[j+1][0]) - indices, weight = route_through_array(arr,p0, p1) - indices=np.array(indices) - if para['type']=='white line on ori': - img[indices[:,0],indices[:,1]]=img_max - elif para['type']=='gray line': - img[indices[:,0],indices[:,1]] = snap[indices[:,0],indices[:,1]] - elif para['type']=='white line': - img[indices[:,0],indices[:,1]] = img_max + if para['max']: img *= -1 + np.add(img, para['lcost']-img.min(), casting='unsafe', out=img) + + minv, maxv = ips.range + routes = [] + for line in ips.roi.body: + pts = np.array(list(zip(line[:-1], line[1:]))) + for p0, p1 in pts[:,:,::-1].astype(int): + indices, weight = route_through_array(img, p0, p1) + routes.append(indices) + rs, cs = np.vstack(routes).T + if para['type']=='white line on ori': + img[:] = snap + img[rs,cs] = maxv + elif para['type']=='gray line': + img[:] = minv + img[rs,cs] = snap[rs,cs] + elif para['type']=='white line': + img[:] = minv + img[rs,cs] = maxv plgs = [Frequence, Statistic, Histogram, PointsValue,ShortRoute] \ No newline at end of file From 07ab8ef7406aa6555c7350df75d853473846e58d Mon Sep 17 00:00:00 2001 From: prevalent <31162098+Prevalenter@users.noreply.github.com> Date: Mon, 17 Feb 2020 15:26:00 +0800 Subject: [PATCH 200/343] Add files via upload add route tool by prevalenter --- imagepy/tools/Draw/Route.gif | Bin 0 -> 98 bytes imagepy/tools/Draw/Route_tol.py | 173 ++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 imagepy/tools/Draw/Route.gif create mode 100644 imagepy/tools/Draw/Route_tol.py diff --git a/imagepy/tools/Draw/Route.gif b/imagepy/tools/Draw/Route.gif new file mode 100644 index 0000000000000000000000000000000000000000..d4cbd24b77d9361d7c0e10f6734910eec5831d7d GIT binary patch literal 98 zcmZ?wbhEHb6krfwXkcVuV3_v*|9{1Qg3d*$i6yBi3gww484B*6z5xu1KUo;L7#JCJ zfQo>M1sE8ZR9y0wcgvm>X$fSKdVhP@qIZ@HpESfJ^)1@9DKjJCgL1f`H!~}PH2|Uj BA7KCh literal 0 HcmV?d00001 diff --git a/imagepy/tools/Draw/Route_tol.py b/imagepy/tools/Draw/Route_tol.py new file mode 100644 index 00000000..ccdcac3d --- /dev/null +++ b/imagepy/tools/Draw/Route_tol.py @@ -0,0 +1,173 @@ +# -*- coding: utf-8 -*- +""" +Created on Wed Oct 19 17:35:09 2016 + +@author: yxl +""" +import wx +from imagepy.core.engine import Tool,Filter +import numpy as np +from imagepy.core.manager import ColorManager +from imagepy.core.roi import lineroi +from imagepy.core.mark import GeometryMark +from skimage.graph import route_through_array +import scipy.ndimage as ndimg +def route_through(ips, snap,poins,para): + para['max']=True + para['lcost']=2 + img = snap.astype('float32') + if para['max']: img *= -1 + np.add(img, para['lcost']-img.min(), casting='unsafe', out=img) + minv, maxv = ips.range + routes = [] + for line in poins: + pts = np.array(list(zip(line[:-1], line[1:]))) + for p0, p1 in pts[:,:,::-1].astype(int): + indices, weight = route_through_array(img, p0, p1) + routes.append(indices) + return routes + +class Plugin(Tool): + title = 'Route Toolk' + note = ['auto_snap','8-bit', '16-bit','int', 'float','2int', 'preview'] + para = {'fully connected':True, 'lcost':0, 'max':False, 'geometric':True, 'type':'ROI'} + view = [(float, 'lcost', (0, 1e5), 3, 'step', 'cost'), + (bool, 'max', 'max cost'), + (bool, 'fully connected', 'fully connected'), + (bool, 'geometric', 'geometric'), + (list, 'type', ['white line', 'ROI'], str, 'output', '')] + def __init__(self): + self.curobj = None + self.doing = 'Nothing' + #初始化线条缓存 + # self.helper = AnchorLine() + self.odx,self.ody = 0, 0 + self.cursor = wx.CURSOR_CROSS + self.buf=[] + + def mouse_down(self, ips, x, y, btn, **key): + # print(key['canvas'].scale) + lim = 5.0/key['canvas'].scale + if btn==2: + if key['ctrl']: + if len(self.buf)>1: + lines=route_through(ips, ips.img,[self.buf],self.para) + rs, cs = np.vstack(lines).T + minv, maxv = ips.range + if self.para['type']=='white line': + # ips.img[:,:] = minv + ips.img[rs,cs] = maxv + elif self.para['type']=='ROI': + ips.img[:,:] = minv + ips.img[rs,cs] = maxv + ndimg.binary_fill_holes(ips.img.copy(), output=ips.img) + ips.img[:,:] *= 255 + self.buf=[] + self.draw(ips) + return + else: + self.oldp = key['canvas'].to_panel_coor(x,y) + self.do_old=self.doing + self.doing = 'all_move' + return + #左键按下 + elif btn==1: + if self.doing=='Nothing': + if len(self.buf) == 0: + self.doing ='draw' + elif self.doing=='draw' and self.cursor==wx.CURSOR_HAND: + a=self.in_points(x,y,lim) + if a!=(-1,-1): + # print('doing to move') + self.doing='move' + #记录第几个点被改变 + self.p_index=self.buf.index(a) + elif self.doing=='down': + #判断是否在点内,如果在,说明在移动 + a=self.in_points(x,y,lim) + if a!=(-1,-1): + self.doing='move' + self.p_index=self.buf.index(a) + return + self.buf=[] + self.doing='Nothing' + self.draw(ips) + return + if self.doing=='draw' and self.cursor==wx.CURSOR_CROSS: + self.addpoint((x,y)) + self.draw(ips) + elif btn == 3: + + if (self.doing=='draw' or self.doing=='down')and key['ctrl']: + a=self.in_points(x,y,lim) + if a!=(-1,-1):self.delete(self.buf.index(a)) + self.draw(ips) + elif self.doing=='draw': + self.addpoint((x,y)) + self.addpoint(self.buf[0]) + self.draw(ips) + self.doing='down' + + def mouse_up(self, ips, x, y, btn, **key): + + if self.doing == 'move' and btn == 1: + self.doing = 'draw' + elif self.doing == 'all_move' and btn ==2: + self.doing = self.do_old + def mouse_move(self, ips, x, y, btn, **key): + if len(self.buf)==0:return + lim = 5.0/key['canvas'].scale + #如果鼠标移动的同时没有按下 + if btn==1: + #如果在鼠标是手的时候左键按下,而且在移动,说明在修改点的位置 + if self.doing=='move': + self.change(self.p_index,(x,y)) + self.draw(ips) + elif btn==None: + #鼠标变成一个十字架 + self.cursor = wx.CURSOR_CROSS + a=self.in_points(x,y,lim) + if a!=(-1,-1): + self.cursor = wx.CURSOR_HAND + if self.doing == 'all_move': + x,y = key['canvas'].to_panel_coor(x,y) + key['canvas'].move(x-self.oldp[0], y-self.oldp[1]) + self.oldp = x, y + ips.update() + # print('move') + self.odx, self.ody = x, y + def in_points(self,x,y,range): + for i in self.buf: + if ((i[0]>x-range) and (i[0]y-range) and (i[1]1: + lines=route_through(ips, ips.img,[self.buf],self.para) + lst=[] + for line in lines:lst.append([(j,i) for i,j in line]) + layer['body'].append({'type':'lines', 'body':lst}) + mark['body'][0] = layer + ips.mark = GeometryMark(mark) + ips.update() + + def mouse_wheel(self, ips, x, y, d, **key): + if d>0:key['canvas'].zoomout(x, y, 'data') + if d<0:key['canvas'].zoomin(x, y, 'data') + ips.update() \ No newline at end of file From 0a22c09512cd1343cea32e3667e85cdb3f225c07 Mon Sep 17 00:00:00 2001 From: prevalent <31162098+Prevalenter@users.noreply.github.com> Date: Mon, 17 Feb 2020 15:28:19 +0800 Subject: [PATCH 201/343] change draw init.py --- imagepy/tools/Draw/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/tools/Draw/__init__.py b/imagepy/tools/Draw/__init__.py index a370e2d6..4593e1fa 100644 --- a/imagepy/tools/Draw/__init__.py +++ b/imagepy/tools/Draw/__init__.py @@ -1 +1 @@ -catlog = ['magic_tol', 'floodfill_tol', 'flood3d_tol', 'aibrush_tol'] \ No newline at end of file +catlog = ['magic_tol', 'floodfill_tol', 'flood3d_tol', 'aibrush_tol','Route_tol'] From 52d9ae0ff4f818a79863356967f63dd25355f87e Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 17 Feb 2020 15:29:13 +0800 Subject: [PATCH 202/343] networkx upgrade --- imagepy/__init__.py | 2 +- imagepy/core/myvi/manager.py | 5 +++-- imagepy/ipyalg/graph/sknw.py | 2 +- .../Region Analysis/regionprops_plgs.py | 2 +- .../Analysis/Skeleton Network/graph_plgs.py | 8 ++++---- .../menus/Kit3D/Network 3D/toolkit3d_plgs.py | 18 +++++++++--------- imagepy/menus/Plugins/Games/drawstep_plg.py | 4 ++-- 7 files changed, 21 insertions(+), 20 deletions(-) diff --git a/imagepy/__init__.py b/imagepy/__init__.py index f5043bf3..2d072a3c 100644 --- a/imagepy/__init__.py +++ b/imagepy/__init__.py @@ -29,7 +29,7 @@ def show(ui = True): AS.AS_SHADOW_BITMAP, shadowcolour=shadow) - ImagePy(None).Show() + ImagePy(None).Show() app.MainLoop() \ No newline at end of file diff --git a/imagepy/core/myvi/manager.py b/imagepy/core/myvi/manager.py index f9bbfcb6..5779c55b 100644 --- a/imagepy/core/myvi/manager.py +++ b/imagepy/core/myvi/manager.py @@ -221,7 +221,8 @@ def draw(self): self.ctx.enable(moderngl.DEPTH_TEST) #self.ctx.enable(ModernGL.CULL_FACE) self.ctx.enable(moderngl.BLEND) - for i in self.objs.values(): i.draw(self.mvp, self.light, self.bright, self.scatter) + for i in self.objs.values(): + i.draw(self.mvp, self.light, self.bright, self.scatter) def count_box(self): minb = np.array([i.box[0] for i in self.objs.values() if not i.box is None]).min(axis=0) @@ -283,4 +284,4 @@ def show(self, title='Myvi'): if __name__ == '__main__': img = imread('gis.png') - build_surf2d(img) \ No newline at end of file + build_surf2d(img) diff --git a/imagepy/ipyalg/graph/sknw.py b/imagepy/ipyalg/graph/sknw.py index b390adde..631c192a 100644 --- a/imagepy/ipyalg/graph/sknw.py +++ b/imagepy/ipyalg/graph/sknw.py @@ -122,7 +122,7 @@ def draw_graph(img, graph, cn=255, ce=128): acc = np.cumprod((1,)+img.shape[::-1][:-1])[::-1] img = img.ravel() for idx in graph.nodes(): - pts = graph.node[idx]['pts'] + pts = graph.nodes[idx]['pts'] img[np.dot(pts, acc)] = cn for (s, e) in graph.edges(): eds = graph[s][e] diff --git a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py index 9a9f103d..dee0da3f 100644 --- a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py @@ -62,7 +62,7 @@ def run(self, ips, imgs, para = None): texts = [(i.centroid[::-1])+('id=%d'%n,) for i,n in zip(ls,range(len(ls)))] layer['body'].append({'type':'texts', 'body':texts}) if para['cov']: - ellips = [i.centroid[::-1] + (i.major_axis_length/2,i.minor_axis_length/2,i.orientation) for i in ls] + ellips = [i.centroid[::-1] + (i.major_axis_length/2,i.minor_axis_length/2, i.orientation+np.pi/2) for i in ls] layer['body'].append({'type':'ellipses', 'body':ellips}) mark['body'][i] = layer diff --git a/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py b/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py index 1bf80ad8..e43fbc07 100644 --- a/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py +++ b/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py @@ -20,7 +20,7 @@ def draw(self, dc, f, **key): dc.SetFont(font) ids = self.graph.nodes() - pts = [self.graph.node[i]['o'] for i in ids] + pts = [self.graph.nodes[i]['o'] for i in ids] pts = [f(i[1], i[0]) for i in pts] dc.DrawPointList(pts) dc.DrawTextList([str(i) for i in ids], pts) @@ -53,7 +53,7 @@ def run(self, ips, imgs, para = None): comid = 0 for g in nx.connected_component_subgraphs(ips.data, False): for idx in g.nodes(): - o = g.node[idx]['o'] + o = g.nodes[idx]['o'] print(idx, g.degree(idx)) nodes.append([comid, idx, g.degree(idx), round(o[1]*k,2), round(o[0]*k,2)]) for (s, e) in g.edges(): @@ -166,8 +166,8 @@ def run(self, ips, imgs, para = None): if len(e1)!=1 or len(e2)!=1: continue e1, e2 = e1[0], e2[0] l1, l2 = e1['pts'], e2['pts'] - d1 = norm(l1[0]-g.node[n]['o']) > norm(l1[-1]-g.node[n]['o']) - d2 = norm(l2[0]-g.node[n]['o']) < norm(l2[-1]-g.node[n]['o']) + d1 = norm(l1[0]-g.nodes[n]['o']) > norm(l1[-1]-g.nodes[n]['o']) + d2 = norm(l2[0]-g.nodes[n]['o']) < norm(l2[-1]-g.nodes[n]['o']) pts = np.vstack((l1[::[-1,1][d1]], l2[::[-1,1][d2]])) l = np.linalg.norm(pts[1:]-pts[:-1], axis=1).sum() g.remove_node(n) diff --git a/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py b/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py index a92e8e7d..c1ec1a12 100644 --- a/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py +++ b/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py @@ -49,12 +49,12 @@ def run(self, ips, imgs, para = None): balls, ids, rs, graph = [], [], [], ips.data for idx in graph.nodes(): ids.append(idx) - balls.append(graph.node[idx]['o']) + balls.append(graph.nodes[idx]['o']) xs, ys, zs = [], [], [] lxs, lys, lzs = [], [], [] for (s, e) in graph.edges(): eds = graph[s][e] - st, ed = graph.node[s]['o'], graph.node[e]['o'] + st, ed = graph.nodes[s]['o'], graph.nodes[e]['o'] lxs.append([st[0],ed[0]]) lys.append([st[1],ed[1]]) lzs.append([st[2],ed[2]]) @@ -102,14 +102,14 @@ def run(self, ips, imgs, para = None): balls, ids, rs, graph = [], [], [], ips.data for idx in graph.nodes(): ids.append(idx) - balls.append(graph.node[idx]['o']) + balls.append(graph.nodes[idx]['o']) xs, ys, zs = [], [], [] v1s, v2s = [], [] for (s, e) in graph.edges(): eds = graph[s][e] - st, ed = graph.node[s]['o'], graph.node[e]['o'] + st, ed = graph.nodes[s]['o'], graph.nodes[e]['o'] v1s.append(st) v2s.append(ed) for i in eds: @@ -157,13 +157,13 @@ def run(self, ips, imgs, para = None): comid = 0 for g in nx.connected_component_subgraphs(ips.data, False): for idx in g.nodes(): - o = g.node[idx]['o'] + o = g.nodes[idx]['o'] nodes.append([comid, idx, g.degree(idx), round(o[1]*k,2), round(o[0]*k,2), round(o[2])]) for (s, e) in g.edges(): eds = g[s][e] for i in eds: l = round(eds[i]['weight']*k, 2) - dis = round(np.linalg.norm(g.node[s]['o']-g.node[e]['o'])*k, 2) + dis = round(np.linalg.norm(g.nodes[s]['o']-g.nodes[e]['o'])*k, 2) edges.append([comid, s, e, l, dis]) comid += 1 @@ -215,7 +215,7 @@ def run(self, ips, imgs, para = None): graph = ips.data datas = [] for s in graph.nodes(): - o = graph.node[s]['o'] + o = graph.nodes[s]['o'] x = graph[s] if len(x)<=1: continue rst = [] @@ -302,8 +302,8 @@ def run(self, ips, imgs, para = None): if len(e1)!=1 or len(e2)!=1: continue e1, e2 = e1[0], e2[0] l1, l2 = e1['pts'], e2['pts'] - d1 = norm(l1[0]-g.node[n]['o']) > norm(l1[-1]-g.node[n]['o']) - d2 = norm(l2[0]-g.node[n]['o']) < norm(l2[-1]-g.node[n]['o']) + d1 = norm(l1[0]-g.nodes[n]['o']) > norm(l1[-1]-g.nodes[n]['o']) + d2 = norm(l2[0]-g.nodes[n]['o']) < norm(l2[-1]-g.nodes[n]['o']) pts = np.vstack((l1[::[-1,1][d1]], l2[::[-1,1][d2]])) l = np.linalg.norm(pts[1:]-pts[:-1], axis=1).sum() g.remove_node(n) diff --git a/imagepy/menus/Plugins/Games/drawstep_plg.py b/imagepy/menus/Plugins/Games/drawstep_plg.py index ab11b5b8..34d6402e 100644 --- a/imagepy/menus/Plugins/Games/drawstep_plg.py +++ b/imagepy/menus/Plugins/Games/drawstep_plg.py @@ -41,8 +41,8 @@ def build_graph(graph, dis, step=True): for i in eds: if step:ls.append(ls[-1].copy()) pts = eds[i]['pts'] - ptss = graph.node[s]['pts'] - ptse = graph.node[e]['pts'] + ptss = graph.nodes[s]['pts'] + ptse = graph.nodes[e]['pts'] pts = np.vstack((ptss,pts,ptse)) draw_edge(ls[-1], dis, pts) return ls if step else ls[0] From 94178949f7c5135f7fffdea625f20ab95489bd2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Cs=C3=A1ti?= Date: Mon, 17 Feb 2020 15:24:16 +0100 Subject: [PATCH 203/343] Handle all dependencies by conda --- environment.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/environment.yml b/environment.yml index c184cf25..09bc18b0 100644 --- a/environment.yml +++ b/environment.yml @@ -1,6 +1,7 @@ name: imagepy channels: - conda-forge + - nsls2forge dependencies: - numba - numpy-stl @@ -16,6 +17,6 @@ dependencies: - xlrd - xlwt - markdown - - pip: - - moderngl - - pystackreg + - python-markdown-math + - moderngl + - pystackreg From fc6ec54a838f7b7629946ce014092f3028890ee0 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 22 Feb 2020 07:53:13 +0800 Subject: [PATCH 204/343] screen snap --- imagepy/menus/Plugins/screencap_plg.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/imagepy/menus/Plugins/screencap_plg.py b/imagepy/menus/Plugins/screencap_plg.py index c031486f..785117b6 100644 --- a/imagepy/menus/Plugins/screencap_plg.py +++ b/imagepy/menus/Plugins/screencap_plg.py @@ -1,10 +1,21 @@ -from PIL import Image,ImageGrab from imagepy import IPy from imagepy.core.engine import Free -import numpy as np +import time, wx, numpy as np class Plugin(Free): - title = 'Screen Capture' - - def run(self, para = None): - IPy.show_img([np.array(ImageGrab.grab())], 'Screen Capture') \ No newline at end of file + title = 'Screen Capture' + para = {'delay':1} + view = [(int, 'delay', (1,10), 0, 'delay', 's')] + + def run(self, para = None): + for i in range(5): + self.progress(i, 5) + time.sleep(para['delay']/5) + screen = wx.ScreenDC() + size = screen.GetSize() + bmp = wx.Bitmap(size[0], size[1]) + mem = wx.MemoryDC(bmp) + mem.Blit(0, 0, size[0], size[1], screen, 0, 0) + arr = np.zeros((size[1], size[0], 3), dtype=np.uint8) + bmp.CopyToBuffer(arr) + IPy.show_img([arr], 'Screen Capture') \ No newline at end of file From e493aec96ac1700b38fed25a6ad68ba59666e557 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 1 Jun 2020 15:45:55 +0800 Subject: [PATCH 205/343] sciwx --- imagepy/IPy.py | 218 -------- imagepy/__init__.py | 32 -- imagepy/__main__.py | 62 ++- imagepy/core/__init__.py | 2 +- imagepy/core/app/__init__.py | 1 + imagepy/core/app/imagepy.py | 391 ++++++++++++++ imagepy/core/app/source.py | 31 ++ imagepy/core/engine/filter.py | 115 ++-- imagepy/core/engine/free.py | 30 +- imagepy/core/engine/macros.py | 60 +-- imagepy/core/engine/mkdown.py | 2 - imagepy/core/engine/report.py | 14 +- imagepy/core/engine/simple.py | 74 ++- imagepy/core/engine/table.py | 74 +-- imagepy/core/engine/tool.py | 4 - imagepy/core/engine/widget.py | 33 +- imagepy/core/engine/workflow.py | 8 +- imagepy/core/loader/loader.py | 95 ++-- imagepy/core/manager/__init__.py | 3 +- imagepy/core/manager/configmanager.py | 1 + imagepy/core/manager/iomanager.py | 87 ++- imagepy/core/manager/pluginmanager.py | 50 +- imagepy/core/manager/roimanager.py | 5 - imagepy/core/manager/shotcutmanager.py | 7 +- imagepy/core/manager/windowmanager.py | 218 -------- imagepy/core/myvi/__init__.py | 8 - imagepy/core/myvi/frame3d.py | 43 -- imagepy/core/myvi/manager.py | 287 ---------- imagepy/core/myvi/test.py | 176 ------- imagepy/core/myvi/txtmark.py | 13 - imagepy/core/roi/lineroi.py | 1 - imagepy/core/roi/ovalroi.py | 1 - imagepy/core/roi/pointroi.py | 1 - imagepy/core/roi/polygonroi.py | 1 - imagepy/core/roi/rectangleroi.py | 1 - imagepy/core/util/fileio.py | 48 +- imagepy/core/util/tableio.py | 4 +- imagepy/core/util/testdata.py | 4 +- imagepy/core/wraper/imageplus.py | 123 +---- imagepy/core/wraper/tableplus.py | 1 - imagepy/data/config.json | 1 + imagepy/data/language/Chinese.dic | 497 ------------------ imagepy/data/logo.ico | Bin 16958 -> 0 bytes imagepy/data/logolong.png | Bin 11801 -> 0 bytes imagepy/data/shotcut.cfg | 2 +- imagepy/data/watermark.png | Bin 4867 -> 0 bytes imagepy/doc/File/New.md | 19 - imagepy/doc/test.py | 10 - .../Analysis/Pixel Cluster/cluster_plgs.py | 20 +- .../Analysis/Pixel Cluster/kmeans_plgs.py | 1 - .../Analysis/Pixel Cluster/statistic_plgs.py | 9 +- .../Analysis/Region Analysis/connect_plg.py | 9 +- .../Region Analysis/regionprops_plgs.py | 11 +- .../Region Analysis/statistic_plgs.py | 51 +- .../Analysis/Skeleton Network/graph_plgs.py | 62 +-- imagepy/menus/Analysis/label_plg.py | 11 +- imagepy/menus/Analysis/statistic_plg.py | 43 +- imagepy/menus/Edit/edit_plg.py | 8 +- imagepy/menus/File/BMP/bmp_plgs.py | 4 +- imagepy/menus/File/DAT/dat_plgs.py | 4 +- imagepy/menus/File/DICOM/dicom_plgs.py | 2 +- imagepy/menus/File/Export/sequence_plg.py | 3 +- imagepy/menus/File/GIF/animate_plgs.py | 3 +- imagepy/menus/File/GIF/gif_plgs.py | 4 +- imagepy/menus/File/Import/raw_plg.py | 1 - imagepy/menus/File/Import/roi_plg.py | 1 - imagepy/menus/File/Import/sequence_plg.py | 27 +- imagepy/menus/File/JPG/jpg_plgs.py | 8 +- imagepy/menus/File/MAT/mat_plgs.py | 5 +- imagepy/menus/File/Numpy/ndarray_plgs.py | 5 +- imagepy/menus/File/Open Recent/recent_plgs.py | 8 - imagepy/menus/File/PNG/png_plgs.py | 4 +- .../File/Samples ImageJ/ijsample_plgs.py | 14 +- .../menus/File/Samples Local/samples_plgs.py | 8 +- imagepy/menus/File/TIF/tif3d_plgs.py | 3 +- imagepy/menus/File/TIF/tif_plgs.py | 6 +- imagepy/menus/File/exit_plg.py | 1 - imagepy/menus/File/new_plg.py | 9 +- imagepy/menus/File/open_plg.py | 16 +- imagepy/menus/File/save_plg.py | 5 +- imagepy/menus/Help/Help_plgs.py | 8 +- imagepy/menus/Help/Language/language_plgs.py | 3 +- .../Image/Adjust/bleachCorrection_plg.py | 1 - imagepy/menus/Image/Adjust/brightcons_plg.py | 58 +- .../menus/Image/Adjust/colorbalance_plg.py | 69 +-- imagepy/menus/Image/Adjust/colorstairs_plg.py | 44 +- imagepy/menus/Image/Adjust/curve_plg.py | 8 +- imagepy/menus/Image/Adjust/graystairs_plg.py | 53 +- imagepy/menus/Image/Adjust/histogram_plgs.py | 43 +- imagepy/menus/Image/Adjust/normalize_plg.py | 5 +- imagepy/menus/Image/Adjust/threshold_plg.py | 50 +- .../menus/Image/Color/splitandmerge_plgs.py | 46 +- .../Lookup table/Others/lookuptables_plg.py | 13 +- .../Image/Lookup table/lookuptables_plg.py | 55 +- imagepy/menus/Image/Mark/mark_plgs.py | 59 +-- imagepy/menus/Image/Stack/orthogonal_plg.py | 4 +- imagepy/menus/Image/Stack/stack_plgs.py | 20 +- .../menus/Image/Transform/Transform_plgs.py | 8 +- imagepy/menus/Image/Type/convert_plg.py | 129 ++--- imagepy/menus/Image/Type/tostack_plg.py | 8 +- imagepy/menus/Image/background_plg.py | 50 +- imagepy/menus/Image/canvassize_plg.py | 14 +- imagepy/menus/Image/crop_plg.py | 23 - imagepy/menus/Image/duplicate_plg.py | 101 ++-- imagepy/menus/Image/resize_plg.py | 36 +- imagepy/menus/Image/setscale_plg.py | 47 +- .../Kit3D/Analysis 3D/pixelstatistic_plgs.py | 5 +- .../Kit3D/Analysis 3D/regionprops3d_plgs.py | 1 - .../Kit3D/Analysis 3D/surfacemeasure_plg.py | 1 - imagepy/menus/Kit3D/Features 3D/ridge_plgs.py | 2 - .../menus/Kit3D/Filters 3D/filters3d_plgs.py | 2 +- .../menus/Kit3D/Network 3D/toolkit3d_plgs.py | 4 - imagepy/menus/Kit3D/Viewer 3D/colorpts_plg.py | 19 +- imagepy/menus/Kit3D/Viewer 3D/demo_plgs.py | 34 +- imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py | 54 +- .../menus/Kit3D/Viewer 3D/tablepoints_plg.py | 2 - .../menus/Plugins/Contribute/pmanager_wgt.py | 13 +- imagepy/menus/Plugins/Games/crossstick_plg.py | 1 - imagepy/menus/Plugins/Games/drawstep_plg.py | 2 +- imagepy/menus/Plugins/Games/lifegame_plg.py | 7 +- .../menus/Plugins/Install/installpkg_plgs.py | 3 +- .../menus/Plugins/Install/installplg_plgs.py | 5 +- imagepy/menus/Plugins/Macros/recorder_plg.py | 30 +- imagepy/menus/Plugins/Macros/recorder_wgt.py | 17 +- imagepy/menus/Plugins/Manager/console_wgt.py | 36 +- imagepy/menus/Plugins/Manager/plglist_wgt.py | 11 +- imagepy/menus/Plugins/Manager/plgtree_wgt.py | 11 +- imagepy/menus/Plugins/Manager/shotcut_wgt.py | 20 +- imagepy/menus/Plugins/Manager/toltree_wgt.py | 12 +- .../menus/Plugins/StackReg/stackreg_plgs.py | 2 - imagepy/menus/Plugins/detect_plg.py | 161 ++++++ imagepy/menus/Plugins/mser_plg.py | 147 ++++++ imagepy/menus/Plugins/screencap_plg.py | 3 +- imagepy/menus/Plugins/temporal_plg.py | 1 - imagepy/menus/Plugins/update_plg.py | 1 - imagepy/menus/Process/Binary/distance_plgs.py | 2 +- .../menus/Process/Classify/classify_plgs.py | 4 +- .../menus/Process/Classify/classify_wgt.py | 8 +- imagepy/menus/Process/Classify/io_plgs.py | 3 +- imagepy/menus/Process/Classify/label_wgt.py | 4 +- imagepy/menus/Process/Classify/predict_plg.py | 5 +- imagepy/menus/Process/FFT/fft_plgs.py | 14 +- imagepy/menus/Process/Features/blob_plgs.py | 27 +- imagepy/menus/Process/Features/corner_plgs.py | 10 +- imagepy/menus/Process/Features/edge_plgs.py | 2 +- imagepy/menus/Process/Features/ridge_plgs.py | 1 - .../menus/Process/Hydrology/hydrology_plgs.py | 14 +- imagepy/menus/Process/Math/math_plgs.py | 2 +- imagepy/menus/Process/Segment/active_plgs.py | 11 +- imagepy/menus/Process/Segment/graph_plgs.py | 37 +- imagepy/menus/Process/Segment/shift_plgs.py | 7 +- .../menus/Process/Threshold/threshold_plgs.py | 1 - imagepy/menus/Process/calculator_plg.py | 22 +- imagepy/menus/Process/repair_plg.py | 2 +- imagepy/menus/Selection/roiwindow_wgt.py | 68 +-- imagepy/menus/Selection/select_plg.py | 100 ++-- .../menus/Table/Basic Operator/basic_plgs.py | 15 +- imagepy/menus/Table/Chart/plot_plgs.py | 46 +- imagepy/menus/Table/Signal/signal_plgs.py | 2 +- .../menus/Table/Statistic/frequency_plgs.py | 5 +- .../menus/Table/Statistic/statistic_plgs.py | 7 +- imagepy/menus/Table/Table IO/tableio_plgs.py | 15 +- .../Universal Generator/gnerator_plgs.py | 9 +- .../menus/Window/Windows Style/style_plgs.py | 1 - imagepy/menus/Window/develop_wgts.py | 5 +- imagepy/menus/Window/widgets_plgs.py | 21 +- imagepy/menus/Window/windowskiller_plg.py | 60 +-- imagepy/tools/Measure/angle2_tol.py | 2 +- imagepy/tools/Measure/angle_tol.py | 2 +- imagepy/tools/Measure/area_tol.py | 2 +- imagepy/tools/Measure/coordinate_tol.py | 2 +- imagepy/tools/Measure/distance_tol.py | 2 +- imagepy/tools/Measure/profile_tol.py | 2 +- imagepy/tools/Standard/freearea_tol.py | 73 +-- imagepy/tools/Standard/freeline_tol.py | 82 +-- imagepy/tools/Standard/line_tol.py | 88 +--- imagepy/tools/Standard/oval_tol.py | 89 +--- imagepy/tools/Standard/point_tol.py | 55 +- imagepy/tools/Standard/polygon_tol.py | 87 +-- imagepy/tools/Standard/rectangle_tol.py | 85 +-- imagepy/tools/Toolkit3D/cursor3d_tol.py | 4 +- imagepy/ui/__init__.py | 14 - imagepy/ui/canvas/__init__.py | 1 - imagepy/ui/canvasframe.py | 369 ------------- imagepy/ui/logwindow.py | 129 ----- imagepy/ui/mainframe.py | 219 -------- imagepy/ui/mkdownwindow.py | 77 --- imagepy/ui/plotwindow.py | 213 -------- imagepy/ui/pluginloader.py | 86 --- imagepy/ui/propertygrid.py | 89 ---- imagepy/ui/tableframe.py | 95 ---- imagepy/ui/toolsloader.py | 125 ----- imagepy/ui/widgets/__init__.py | 7 - imagepy/ui/widgets/advanced.py | 29 - imagepy/ui/widgetsloader.py | 34 -- imagepy/ui/workflowwindow.py | 120 ----- imagepy/widgets/__init__.py | 0 imagepy/widgets/histogram/__init__.py | 1 - imagepy/widgets/navigator/__init__.py | 0 sciapp/__init__.py | 1 + sciapp/action/__init__.py | 5 + sciapp/action/action.py | 6 + sciapp/action/free.py | 39 ++ sciapp/action/imgact.py | 35 ++ sciapp/action/roiact.py | 61 +++ sciapp/action/shpact.py | 242 +++++++++ sciapp/action/shpbase.py | 299 +++++++++++ sciapp/action/simple.py | 109 ++++ sciapp/action/tolact.py | 73 +++ sciapp/app.py | 149 ++++++ sciapp/manager.py | 48 ++ sciapp/object/__init__.py | 5 + sciapp/object/image.py | 174 ++++++ sciapp/object/roi.py | 107 ++++ sciapp/object/shape.py | 374 +++++++++++++ sciapp/object/surface.py | 68 +++ sciapp/object/table.py | 79 +++ sciapp/object/test.txt | 1 + sciapp/util/__init__.py | 2 + sciapp/util/shputil.py | 56 ++ .../myvi/util.py => sciapp/util/surfutil.py | 15 +- sciwx/__init__.py | 13 + sciwx/app/__init__.py | 2 + sciwx/app/sciapp.py | 222 ++++++++ sciwx/canvas/__init__.py | 4 + {imagepy/ui => sciwx}/canvas/boxutil.py | 26 +- sciwx/canvas/canvas.py | 307 +++++++++++ {imagepy/ui => sciwx}/canvas/imutil.py | 4 +- {imagepy/ui => sciwx}/canvas/mark.py | 342 ++++++------ sciwx/canvas/mcanvas.py | 261 +++++++++ .../canvas/canvas.py => sciwx/canvas/test.py | 26 +- sciwx/canvas/vwidget.py | 131 +++++ sciwx/canvas/widget.py | 143 +++++ sciwx/demo/aipen_demo.py | 60 +++ sciwx/demo/app_demo.py | 38 ++ sciwx/demo/canvas1_demo.py | 59 +++ sciwx/demo/canvas2_m_demo.py | 33 ++ sciwx/demo/canvas3_image_obj.py | 31 ++ sciwx/demo/canvas4_tool.py | 33 ++ sciwx/demo/canvas5_frame_demo.py | 26 + sciwx/demo/canvas6_frame_toolbar.py | 45 ++ sciwx/demo/canvas7_frame_menu.py | 35 ++ sciwx/demo/canvas7_frame_menu_tool.py | 63 +++ sciwx/demo/canvas9_image_roi.py | 17 + sciwx/demo/dialog_demo.py | 24 + sciwx/demo/grid_demo.py | 70 +++ sciwx/demo/markdown_demo.py | 46 ++ sciwx/demo/mesh1_demo.py | 50 ++ sciwx/demo/mesh2_mesh_demo.py | 33 ++ sciwx/demo/mesh3_geoutil.py | 160 ++++++ sciwx/demo/plot_demo.py | 52 ++ sciwx/demo/plt_demo.py | 30 ++ sciwx/demo/roi_edit_demo.py | 31 ++ sciwx/demo/shape_demo.py | 57 ++ sciwx/demo/shape_edite_demo.py | 30 ++ sciwx/demo/shape_frame_demo.py | 55 ++ sciwx/demo/text_demo.py | 40 ++ sciwx/demo/widget_demo.py | 30 ++ sciwx/grid/__init__.py | 2 + .../ui/tablewindow.py => sciwx/grid/grid.py | 229 ++++---- sciwx/grid/widget.py | 140 +++++ sciwx/mesh/__init__.py | 4 + sciwx/mesh/canvas.py | 173 ++++++ .../core/myvi => sciwx/mesh}/imgs/__init__.py | 0 .../myvi => sciwx/mesh}/imgs/configure.png | Bin .../myvi => sciwx/mesh}/imgs/isometric.png | Bin .../core/myvi => sciwx/mesh}/imgs/logo.ico | Bin .../core/myvi => sciwx/mesh}/imgs/open.png | Bin .../myvi => sciwx/mesh}/imgs/parallel.png | Bin .../core/myvi => sciwx/mesh}/imgs/save.png | Bin .../core/myvi => sciwx/mesh}/imgs/stl.png | Bin .../core/myvi => sciwx/mesh}/imgs/x-axis.png | Bin .../core/myvi => sciwx/mesh}/imgs/y-axis.png | Bin .../core/myvi => sciwx/mesh}/imgs/z-axis.png | Bin sciwx/mesh/matutil.py | 49 ++ .../myvi/canvas3d.py => sciwx/mesh/mcanvas.py | 240 ++------- sciwx/mesh/scene.py | 219 ++++++++ sciwx/mesh/widget.py | 113 ++++ sciwx/plot/__init__.py | 1 + sciwx/plot/plot.py | 112 ++++ sciwx/plt.py | 77 +++ {imagepy/data => sciwx/plugins}/__init__.py | 0 .../plugins/channels.py | 70 ++- .../curve_wgt.py => sciwx/plugins/curve.py | 26 +- sciwx/plugins/filters.py | 15 + .../plugins/histogram.py | 47 +- sciwx/plugins/io.py | 22 + sciwx/plugins/pencil.py | 29 + .../plugins/viewport.py | 47 +- sciwx/text/__init__.py | 3 + .../doc/File/File.md => sciwx/text/index1.htm | 0 imagepy/doc/root.md => sciwx/text/index2.htm | 0 sciwx/text/index3.htm | 133 +++++ {imagepy/data => sciwx/text}/markdown.css | 0 sciwx/text/mdpad.py | 122 +++++ sciwx/text/mdutil.py | 38 ++ sciwx/text/textpad.py | 217 ++++++++ sciwx/widgets/__init__.py | 11 + sciwx/widgets/advanced.py | 28 + sciwx/widgets/choicebook.py | 27 + {imagepy/ui => sciwx}/widgets/cmappanel.py | 11 +- {imagepy/ui => sciwx}/widgets/colormap.py | 29 +- {imagepy/ui => sciwx}/widgets/curvepanel.py | 20 +- {imagepy/ui => sciwx}/widgets/histpanel.py | 38 +- sciwx/widgets/menubar.py | 59 +++ {imagepy/ui => sciwx}/widgets/normal.py | 60 ++- .../widgets/paradialog.py | 94 ++-- sciwx/widgets/threpanel.py | 52 ++ sciwx/widgets/toolbar.py | 108 ++++ sciwx/widgets/util.py | 27 + {imagepy/ui => sciwx}/widgets/viewport.py | 44 +- 311 files changed, 8417 insertions(+), 6395 deletions(-) delete mode 100644 imagepy/IPy.py create mode 100644 imagepy/core/app/__init__.py create mode 100644 imagepy/core/app/imagepy.py create mode 100644 imagepy/core/app/source.py delete mode 100644 imagepy/core/manager/windowmanager.py delete mode 100644 imagepy/core/myvi/__init__.py delete mode 100644 imagepy/core/myvi/frame3d.py delete mode 100644 imagepy/core/myvi/manager.py delete mode 100644 imagepy/core/myvi/test.py delete mode 100644 imagepy/core/myvi/txtmark.py create mode 100644 imagepy/data/config.json delete mode 100644 imagepy/data/language/Chinese.dic delete mode 100644 imagepy/data/logo.ico delete mode 100644 imagepy/data/logolong.png delete mode 100644 imagepy/data/watermark.png delete mode 100644 imagepy/doc/File/New.md delete mode 100644 imagepy/doc/test.py delete mode 100644 imagepy/menus/Image/crop_plg.py create mode 100644 imagepy/menus/Plugins/detect_plg.py create mode 100644 imagepy/menus/Plugins/mser_plg.py delete mode 100644 imagepy/ui/__init__.py delete mode 100644 imagepy/ui/canvas/__init__.py delete mode 100644 imagepy/ui/canvasframe.py delete mode 100644 imagepy/ui/logwindow.py delete mode 100644 imagepy/ui/mainframe.py delete mode 100644 imagepy/ui/mkdownwindow.py delete mode 100644 imagepy/ui/plotwindow.py delete mode 100644 imagepy/ui/pluginloader.py delete mode 100644 imagepy/ui/propertygrid.py delete mode 100644 imagepy/ui/tableframe.py delete mode 100644 imagepy/ui/toolsloader.py delete mode 100644 imagepy/ui/widgets/__init__.py delete mode 100644 imagepy/ui/widgets/advanced.py delete mode 100644 imagepy/ui/widgetsloader.py delete mode 100644 imagepy/ui/workflowwindow.py delete mode 100644 imagepy/widgets/__init__.py delete mode 100644 imagepy/widgets/histogram/__init__.py delete mode 100644 imagepy/widgets/navigator/__init__.py create mode 100644 sciapp/__init__.py create mode 100644 sciapp/action/__init__.py create mode 100644 sciapp/action/action.py create mode 100644 sciapp/action/free.py create mode 100644 sciapp/action/imgact.py create mode 100644 sciapp/action/roiact.py create mode 100644 sciapp/action/shpact.py create mode 100644 sciapp/action/shpbase.py create mode 100644 sciapp/action/simple.py create mode 100644 sciapp/action/tolact.py create mode 100644 sciapp/app.py create mode 100644 sciapp/manager.py create mode 100644 sciapp/object/__init__.py create mode 100644 sciapp/object/image.py create mode 100644 sciapp/object/roi.py create mode 100644 sciapp/object/shape.py create mode 100644 sciapp/object/surface.py create mode 100644 sciapp/object/table.py create mode 100644 sciapp/object/test.txt create mode 100644 sciapp/util/__init__.py create mode 100644 sciapp/util/shputil.py rename imagepy/core/myvi/util.py => sciapp/util/surfutil.py (91%) create mode 100644 sciwx/__init__.py create mode 100644 sciwx/app/__init__.py create mode 100644 sciwx/app/sciapp.py create mode 100644 sciwx/canvas/__init__.py rename {imagepy/ui => sciwx}/canvas/boxutil.py (64%) create mode 100644 sciwx/canvas/canvas.py rename {imagepy/ui => sciwx}/canvas/imutil.py (98%) rename {imagepy/ui => sciwx}/canvas/mark.py (50%) create mode 100644 sciwx/canvas/mcanvas.py rename imagepy/ui/canvas/canvas.py => sciwx/canvas/test.py (93%) create mode 100644 sciwx/canvas/vwidget.py create mode 100644 sciwx/canvas/widget.py create mode 100644 sciwx/demo/aipen_demo.py create mode 100644 sciwx/demo/app_demo.py create mode 100644 sciwx/demo/canvas1_demo.py create mode 100644 sciwx/demo/canvas2_m_demo.py create mode 100644 sciwx/demo/canvas3_image_obj.py create mode 100644 sciwx/demo/canvas4_tool.py create mode 100644 sciwx/demo/canvas5_frame_demo.py create mode 100644 sciwx/demo/canvas6_frame_toolbar.py create mode 100644 sciwx/demo/canvas7_frame_menu.py create mode 100644 sciwx/demo/canvas7_frame_menu_tool.py create mode 100644 sciwx/demo/canvas9_image_roi.py create mode 100644 sciwx/demo/dialog_demo.py create mode 100644 sciwx/demo/grid_demo.py create mode 100644 sciwx/demo/markdown_demo.py create mode 100644 sciwx/demo/mesh1_demo.py create mode 100644 sciwx/demo/mesh2_mesh_demo.py create mode 100644 sciwx/demo/mesh3_geoutil.py create mode 100644 sciwx/demo/plot_demo.py create mode 100644 sciwx/demo/plt_demo.py create mode 100644 sciwx/demo/roi_edit_demo.py create mode 100644 sciwx/demo/shape_demo.py create mode 100644 sciwx/demo/shape_edite_demo.py create mode 100644 sciwx/demo/shape_frame_demo.py create mode 100644 sciwx/demo/text_demo.py create mode 100644 sciwx/demo/widget_demo.py create mode 100644 sciwx/grid/__init__.py rename imagepy/ui/tablewindow.py => sciwx/grid/grid.py (54%) create mode 100644 sciwx/grid/widget.py create mode 100644 sciwx/mesh/__init__.py create mode 100644 sciwx/mesh/canvas.py rename {imagepy/core/myvi => sciwx/mesh}/imgs/__init__.py (100%) rename {imagepy/core/myvi => sciwx/mesh}/imgs/configure.png (100%) rename {imagepy/core/myvi => sciwx/mesh}/imgs/isometric.png (100%) rename {imagepy/core/myvi => sciwx/mesh}/imgs/logo.ico (100%) rename {imagepy/core/myvi => sciwx/mesh}/imgs/open.png (100%) rename {imagepy/core/myvi => sciwx/mesh}/imgs/parallel.png (100%) rename {imagepy/core/myvi => sciwx/mesh}/imgs/save.png (100%) rename {imagepy/core/myvi => sciwx/mesh}/imgs/stl.png (100%) rename {imagepy/core/myvi => sciwx/mesh}/imgs/x-axis.png (100%) rename {imagepy/core/myvi => sciwx/mesh}/imgs/y-axis.png (100%) rename {imagepy/core/myvi => sciwx/mesh}/imgs/z-axis.png (100%) create mode 100644 sciwx/mesh/matutil.py rename imagepy/core/myvi/canvas3d.py => sciwx/mesh/mcanvas.py (54%) create mode 100644 sciwx/mesh/scene.py create mode 100644 sciwx/mesh/widget.py create mode 100644 sciwx/plot/__init__.py create mode 100644 sciwx/plot/plot.py create mode 100644 sciwx/plt.py rename {imagepy/data => sciwx/plugins}/__init__.py (100%) rename imagepy/widgets/histogram/channels_wgt.py => sciwx/plugins/channels.py (89%) rename imagepy/widgets/histogram/curve_wgt.py => sciwx/plugins/curve.py (88%) create mode 100644 sciwx/plugins/filters.py rename imagepy/widgets/histogram/histogram_wgt.py => sciwx/plugins/histogram.py (83%) create mode 100644 sciwx/plugins/io.py create mode 100644 sciwx/plugins/pencil.py rename imagepy/widgets/navigator/navigator_wgt.py => sciwx/plugins/viewport.py (81%) create mode 100644 sciwx/text/__init__.py rename imagepy/doc/File/File.md => sciwx/text/index1.htm (100%) rename imagepy/doc/root.md => sciwx/text/index2.htm (100%) create mode 100644 sciwx/text/index3.htm rename {imagepy/data => sciwx/text}/markdown.css (100%) create mode 100644 sciwx/text/mdpad.py create mode 100644 sciwx/text/mdutil.py create mode 100644 sciwx/text/textpad.py create mode 100644 sciwx/widgets/__init__.py create mode 100644 sciwx/widgets/advanced.py create mode 100644 sciwx/widgets/choicebook.py rename {imagepy/ui => sciwx}/widgets/cmappanel.py (97%) rename {imagepy/ui => sciwx}/widgets/colormap.py (85%) rename {imagepy/ui => sciwx}/widgets/curvepanel.py (94%) rename {imagepy/ui => sciwx}/widgets/histpanel.py (60%) create mode 100644 sciwx/widgets/menubar.py rename {imagepy/ui => sciwx}/widgets/normal.py (89%) rename imagepy/ui/panelconfig.py => sciwx/widgets/paradialog.py (64%) create mode 100644 sciwx/widgets/threpanel.py create mode 100644 sciwx/widgets/toolbar.py create mode 100644 sciwx/widgets/util.py rename {imagepy/ui => sciwx}/widgets/viewport.py (82%) diff --git a/imagepy/IPy.py b/imagepy/IPy.py deleted file mode 100644 index 939d155d..00000000 --- a/imagepy/IPy.py +++ /dev/null @@ -1,218 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Sat Oct 15 10:03:00 2016 - -@author: yxl -""" - -import wx, os.path as osp -from pubsub import pub - -root_dir = osp.abspath(osp.dirname(__file__)) - -curapp = None - -def load_plugins(): - from imagepy.core.loader import loader - loader.build_plugins('menus', True) - from glob import glob - extends = glob('plugins/*/menus') - for i in extends: loader.build_plugins(i, True) - -def uimode(): - if curapp is None: return 'no' - from .core import manager - return manager.ConfigManager.get('uistyle') or 'ipy' - -def get_window(): - from .core import manager - return manager.WindowsManager.get() - -def get_twindow(): - from .core import manager - return manager.WTableManager.get() - -def get_ips(): - from .core import manager - return manager.ImageManager.get() - -def get_tps(): - from .core import manager - return manager.TableManager.get() - # return None if win==None else win.canvas.ips - -def showips(ips): - if uimode()=='ipy': - from .ui.canvasframe import CanvasPanel - canvasp = CanvasPanel(curapp.canvasnb) - canvasp.set_ips(ips) - curapp.canvasnb.add_page( canvasp, ips) - #canvasp.canvas.initBuffer() - - curapp.auimgr.Update() - elif uimode()=='ij': - from .ui.canvasframe import CanvasFrame - frame = CanvasFrame(curapp) - frame.set_ips(ips) - frame.Show() - -pub.subscribe(showips, 'showips') -def show_ips(ips): - if uimode()=='no': - from .core import manager - from .ui.canvasframe import VirturlCanvas - frame = VirturlCanvas(ips) - print('ImagePy New ImagePlus >>> %s'%ips.title) - else: wx.CallAfter(pub.sendMessage, 'showips', ips=ips) - -def showimg(imgs, title): - print('show img') - from .core import ImagePlus - ips = ImagePlus(imgs, title) - showips(ips) - -pub.subscribe(showimg, 'showimg') -def show_img(imgs, title): - if uimode()=='no': - from .core import manager, ImagePlus - from .ui.canvasframe import VirturlCanvas - frame = VirturlCanvas(ImagePlus(imgs, title)) - else:wx.CallAfter(pub.sendMessage, 'showimg', imgs=imgs, title=title) - -def reloadplgs(report=False, menus=True, tools=False, widgets=False): - print('reload........') - curapp.reload_plugins(report, menus, tools, widgets) - -pub.subscribe(reloadplgs, 'reload') -def reload_plgs(report=False, menus=True, tools=False, widgets=False): - print('reload========') - wx.CallAfter(pub.sendMessage, 'reload', report=report, menus=menus, tools=tools, widgets=widgets) - -def showmd(title, cont, url=''): - from .ui.mkdownwindow import MkDownWindow - MkDownWindow(curapp, title, cont, url).Show() - -pub.subscribe(showmd, 'showmd') -def show_md(title, cont, url=''): - wx.CallAfter(pub.sendMessage, 'showmd', title=title, cont=cont, url=url) - -def _alert(info, title="ImagePy Alert!"): - if uimode()=='no': - print('ImagePy Alert >>> %s'%title) - print(info) - else: - dialog=wx.MessageDialog(curapp, info, title, wx.OK) - dialog.ShowModal() == wx.ID_OK - dialog.Destroy() - -pub.subscribe(_alert, 'alert') - -def alert(info, title="ImagePy Alert!"): - wx.CallAfter(pub.sendMessage, 'alert', info=info, title=title) - -# MT alert = lambda info, title='image-py':callafter(alert_, *(info, title)) - -def yes_no(info, title="ImagePy Yes-No ?!"): - dialog = wx.MessageDialog(curapp, info, title, wx.YES_NO | wx.CANCEL) - rst = dialog.ShowModal() - dialog.Destroy() - dic = {wx.ID_YES:'yes', wx.ID_NO:'no', wx.ID_CANCEL:'cancel'} - return dic[rst] - -def getpath(title, filt, k, para=None, name=''): - """Get the defaultpath of the ImagePy""" - from .core import manager - dpath = manager.ConfigManager.get('defaultpath') - if dpath ==None: - dpath = root_dir # './' - dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} - dialog = wx.FileDialog(curapp, title, dpath, name, filt, dic[k]) - rst = dialog.ShowModal() - path = None - if rst == wx.ID_OK: - path = dialog.GetPath() - dpath = osp.split(path)[0] - manager.ConfigManager.set('defaultpath', dpath) - if para!=None:para['path'] = path - dialog.Destroy() - - return rst == wx.ID_OK if para!=None else path - -def getdir(title, filt, para=None): - from .core import manager - dpath = manager.ConfigManager.get('defaultpath') - if dpath ==None: - dpath = root_dir - dialog = wx.DirDialog(curapp, title, dpath ) - rst = dialog.ShowModal() - path = None - if rst == wx.ID_OK: - path = dialog.GetPath() - if para!=None:para['path'] = path - dialog.Destroy() - return rst == wx.ID_OK if para!=None else path - -def get_para(title, view, para): - from .ui.panelconfig import ParaDialog - pd = ParaDialog(curapp, title) - pd.init_view(view, para) - rst = pd.ShowModal() - pd.Destroy() - return rst == wx.ID_OK - -def showtable(data, title): - from .core import TablePlus - if uimode()=='ipy': - from .ui.tableframe import TablePanel - tps = TablePlus(data, title) - tablep = TablePanel(curapp.tablenb) - tablep.set_tps(tps) - curapp.tablenb.add_page( tablep, tps) - info = curapp.auimgr.GetPane(curapp.tablenbwrap) - info.Show(True) - curapp.auimgr.Update() - elif uimode()=='ij': - from .ui.tableframe import TableFrame - tps = TablePlus(data, title) - frame = TableFrame(curapp) - frame.set_tps(tps) - frame.Show() - - # MT callafter(TableLog.table, *(title, data, cols, rows)) - -pub.subscribe(showtable, 'showtable') -def show_table(data, title): - wx.CallAfter(pub.sendMessage, "showtable", data=data, title=title) - -def showlog(title, cont): - from .ui.logwindow import TextLog - TextLog.write(cont, title) - -pub.subscribe(showlog, 'showlog') -def show_log(title, cont): - wx.CallAfter(pub.sendMessage, 'showlog', cont=cont, title=title) - -def write(cont, title='ImagePy'): - if curapp is None: - print('ImagePy Write >>> %s'%title) - print(cont) - else: - from .ui.logwindow import TextLog - wx.CallAfter(pub.sendMessage, 'showlog', title=title, cont=cont) - -def plot(title, gtitle='Graph', labelx='X-Unit', labely='Y-Unit'): - from .ui.plotwindow import PlotFrame - return PlotFrame.get_frame(title, gtitle, labelx, labely) - -#def set_progress(i): -# curapp.set_progress(i) - # MT callafter(curapp.set_progress, i) - -def set_info(i): - if curapp is None: print('ImagePy Info >>> %s'%i) - else: wx.CallAfter(curapp.set_info, str(i)) - # MT callafter(curapp.set_info, i) - -def run(cmd): - from imagepy.core.engine import Macros - Macros('', cmd.replace('\r\n', '\n').split('\n')).start() \ No newline at end of file diff --git a/imagepy/__init__.py b/imagepy/__init__.py index 2d072a3c..62699fa7 100644 --- a/imagepy/__init__.py +++ b/imagepy/__init__.py @@ -1,35 +1,3 @@ -#from __future__ import absolute_import import os.path as osp -import sys, os, wx -from wx.adv import SplashScreen as SplashScreen -import wx.lib.agw.advancedsplash as AS - -from .IPy import * -from .core import ImagePlus, TablePlus root_dir = osp.abspath(osp.dirname(__file__)) -os.chdir(root_dir) - -import matplotlib -matplotlib.use('WxAgg') -# sys.path.append(root_dir) - -from .ui.mainframe import ImagePy - -def show(ui = True): - app = wx.App(False) - - bitmap = wx.Bitmap('data/logolong.png', wx.BITMAP_TYPE_PNG) - shadow = wx.Colour(255,255,255) - # SplashScreen(bitmap, wx.adv.SPLASH_CENTRE_ON_SCREEN | wx.adv.SPLASH_TIMEOUT, 3000, None, -1) - - asp = AS.AdvancedSplash(None, bitmap=bitmap, timeout=1000, - agwStyle=AS.AS_TIMEOUT | - AS.AS_CENTER_ON_PARENT | - AS.AS_SHADOW_BITMAP, - shadowcolour=shadow) - - ImagePy(None).Show() - app.MainLoop() - - \ No newline at end of file diff --git a/imagepy/__main__.py b/imagepy/__main__.py index 762bc4c9..3064e253 100644 --- a/imagepy/__main__.py +++ b/imagepy/__main__.py @@ -1,4 +1,58 @@ -import sys, os -sys.path.append('..') -import imagepy -imagepy.show() \ No newline at end of file +import wx, sys +sys.path.append('../') +from imagepy.core.app import ImagePy +from imagepy.core.loader import loader + +from sciapp.action import ImgAction, Tool, DefaultTool +from sciwx.plugins.curve import Curve +from sciwx.plugins.channels import Channels +from sciwx.plugins.histogram import Histogram +from sciwx.plugins.viewport import ViewPort +from sciwx.plugins.filters import Gaussian, Undo +from sciwx.plugins.pencil import Pencil +from sciwx.plugins.io import Open, Save + +def extend_plgs(plg): + if isinstance(plg, tuple): + return (plg[0].title, extend_plgs(plg[1])) + elif isinstance(plg, list): + return [extend_plgs(i) for i in plg] + elif isinstance(plg, str): return plg + else: return (plg.title, plg) + +def extend_tols(tol): + if isinstance(tol, tuple) and isinstance(tol[1], list): + return (tol[0].title, extend_tols(tol[1])) + elif isinstance(tol, tuple) and isinstance(tol[1], str): + return (tol[1], tol[0]) + elif isinstance(tol, list): return [extend_tols(i) for i in tol] + +if __name__ == '__main__': + from skimage.data import camera, astronaut + + app = wx.App(False) + frame = ImagePy(None) + + #frame.load_menu(('menu',[('File',[('Open', Open), + # ('Save', Save)]), + # ('Filters', [('Gaussian', Gaussian), + # ('Undo', Undo)])])) + + frame.load_menu(extend_plgs(loader.build_plugins('menus'))) + + #frame.load_tool(('tools',[('standard', [('P', Pencil), + # ('D', DefaultTool)]), + # ('draw', [('X', Pencil), + # ('X', Pencil)])]), 'draw') + + frame.load_tool(extend_tols(loader.build_tools('tools')), 'Transform') + + frame.load_widget(('widgets', [('Histogram', [('Histogram', Histogram), + ('Curve', Curve), + ('Channels', Channels)]), + ('Navigator', [('Viewport', ViewPort)])])) + + frame.show_img([camera()], 'camera') + frame.show_img([astronaut()], 'astronaut') + frame.Show() + app.MainLoop() \ No newline at end of file diff --git a/imagepy/core/__init__.py b/imagepy/core/__init__.py index 8498a1b0..9d6d2792 100644 --- a/imagepy/core/__init__.py +++ b/imagepy/core/__init__.py @@ -1,2 +1,2 @@ -from . wraper.imageplus import ImagePlus +#from . wraper.imageplus import ImagePlus from . wraper.tableplus import TablePlus \ No newline at end of file diff --git a/imagepy/core/app/__init__.py b/imagepy/core/app/__init__.py new file mode 100644 index 00000000..c887d111 --- /dev/null +++ b/imagepy/core/app/__init__.py @@ -0,0 +1 @@ +from .imagepy import ImagePy diff --git a/imagepy/core/app/imagepy.py b/imagepy/core/app/imagepy.py new file mode 100644 index 00000000..f5770cf4 --- /dev/null +++ b/imagepy/core/app/imagepy.py @@ -0,0 +1,391 @@ +import wx, os, sys +import time, threading +sys.path.append('../../../') +import wx.lib.agw.aui as aui +from sciwx.widgets import MenuBar, ToolBar, ChoiceBook, ParaDialog +from sciwx.canvas import CanvasNoteBook +from sciwx.grid import GridNoteBook +from sciwx.mesh import Canvas3DNoteBook +from sciwx.text import MDNoteFrame, TextNoteFrame +from sciwx.plot import PlotFrame +from skimage.data import camera +from sciapp import App, Source +from sciapp.object import Image +from .source import * + +class ImagePy(wx.Frame, App): + def __init__( self, parent ): + wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = 'SciApp', + size = wx.Size(-1,-1), pos = wx.DefaultPosition, + style = wx.RESIZE_BORDER|wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + App.__init__(self) + self.auimgr = aui.AuiManager() + self.auimgr.SetManagedWindow( self ) + self.SetSizeHints( wx.Size(1024,768) ) + + self.init_menu() + self.init_tool() + self.init_canvas() + self.init_table() + self.init_mesh() + self.init_widgets() + self.init_text() + self.init_status() + + self.Layout() + self.auimgr.Update() + self.Fit() + self.Centre( wx.BOTH ) + + self.Bind(wx.EVT_CLOSE, self.on_close) + self.Bind(aui.EVT_AUI_PANE_CLOSE, self.on_pan_close) + + def init_status(self): + self.stapanel = stapanel = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + sizersta = wx.BoxSizer( wx.HORIZONTAL ) + self.txt_info = wx.StaticText( stapanel, wx.ID_ANY, "ImagePy v0.2", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.txt_info.Wrap( -1 ) + sizersta.Add( self.txt_info, 1, wx.ALIGN_BOTTOM|wx.BOTTOM|wx.LEFT|wx.RIGHT, 2 ) + self.pro_bar = wx.Gauge( stapanel, wx.ID_ANY, 100, wx.DefaultPosition, wx.Size( 100,15 ), wx.GA_HORIZONTAL ) + sizersta.Add( self.pro_bar, 0, wx.ALIGN_BOTTOM|wx.BOTTOM|wx.LEFT|wx.RIGHT, 2 ) + stapanel.SetSizer(sizersta) + self.auimgr.AddPane( stapanel, aui.AuiPaneInfo() .Bottom() .CaptionVisible( False ).PinButton( True ) + .PaneBorder( False ).Dock().Resizable().FloatingSize( wx.DefaultSize ).DockFixed( True ) + . MinSize(wx.Size(-1, 20)). MaxSize(wx.Size(-1, 20)).Layer( 10 ) ) + + def load_menu(self, data): + self.menubar.load(data) + + def load_tool(self, data, default=None): + for i, (name, tols) in enumerate(data[1]): + self.toolbar.add_tools(name, tols, i==0) + if not default is None: self.toolbar.add_pop('P', default) + self.toolbar.Layout() + + def load_widget(self, data): + self.widgets.load(data) + + def init_menu(self): + self.menubar = MenuBar(self) + + def init_tool(self): + sizer = wx.BoxSizer(wx.VERTICAL) + self.toolbar = ToolBar(self, True) + self.toolbar.Fit() + + self.auimgr.AddPane(self.toolbar, aui.AuiPaneInfo() .Left() .PinButton( True ) + .CaptionVisible( True ).Dock().Resizable().FloatingSize( wx.DefaultSize ).MaxSize(wx.Size( 32,-1 )) + . BottomDockable( True ).TopDockable( False ).Layer( 10 ) ) + + def init_canvas(self): + self.canvasnbwrap = wx.Panel(self) + sizer = wx.BoxSizer( wx.VERTICAL ) + self.canvasnb = CanvasNoteBook( self.canvasnbwrap) + sizer.Add( self.canvasnb, 1, wx.EXPAND |wx.ALL, 0 ) + self.canvasnbwrap.SetSizer( sizer ) + self.canvasnbwrap.Layout() + self.auimgr.AddPane( self.canvasnbwrap, aui.AuiPaneInfo() .Center() .CaptionVisible( False ).PinButton( True ).Dock() + .PaneBorder( False ).Resizable().FloatingSize( wx.DefaultSize ). BottomDockable( True ).TopDockable( False ) + .LeftDockable( True ).RightDockable( True ) ) + self.canvasnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_new_img) + self.canvasnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close_img) + + def init_table(self): + self.tablenbwrap = wx.Panel(self) + sizer = wx.BoxSizer( wx.VERTICAL ) + self.tablenb = GridNoteBook( self.tablenbwrap) + sizer.Add( self.tablenb, 1, wx.EXPAND |wx.ALL, 0 ) + self.tablenbwrap.SetSizer( sizer ) + self.tablenbwrap.Layout() + + self.auimgr.AddPane( self.tablenbwrap, aui.AuiPaneInfo() .Bottom() .CaptionVisible( True ).PinButton( True ).Dock().Hide() + .MaximizeButton( True ).Resizable().FloatingSize((800, 600)).BestSize(( 120,120 )). Caption('Tables') . + BottomDockable( True ).TopDockable( False ).LeftDockable( True ).RightDockable( True ) ) + self.tablenb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_new_tab) + self.tablenb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close_tab) + + def init_mesh(self): + self.meshnbwrap = wx.Panel(self) + sizer = wx.BoxSizer( wx.VERTICAL ) + self.meshnb = Canvas3DNoteBook( self.meshnbwrap) + sizer.Add( self.meshnb, 1, wx.EXPAND |wx.ALL, 0 ) + self.meshnbwrap.SetSizer( sizer ) + self.meshnbwrap.Layout() + + self.auimgr.AddPane( self.meshnbwrap, aui.AuiPaneInfo() .Bottom() .CaptionVisible( True ).PinButton( True ).Float().Hide() + .MaximizeButton( True ).Resizable().FloatingSize((800, 600)).BestSize(( 120,120 )). Caption('Meshes') . + BottomDockable( True ).TopDockable( False ).LeftDockable( True ).RightDockable( True ) ) + self.meshnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_new_mesh) + self.meshnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close_mesh) + + def init_widgets(self): + self.widgets = ChoiceBook(self) + self.auimgr.AddPane( self.widgets, aui.AuiPaneInfo() .Right().Caption('Widgets') .PinButton( True ) + .Dock().Resizable().FloatingSize( wx.DefaultSize ).MinSize( wx.Size( 266,-1 ) ).Layer( 10 ) ) + + def init_text(self): + self.mdframe = MDNoteFrame(self, 'Sci Document') + self.txtframe = TextNoteFrame(self, 'Sci Text') + + def on_pan_close(self, event): + if event.GetPane().window in [self.toolbar, self.widgets]: + event.Veto() + if hasattr(event.GetPane().window, 'close'): + event.GetPane().window.close() + + def on_new_img(self, event): + self.add_img(self.canvasnb.canvas().image) + self.add_img_win(self.canvasnb.canvas()) + + def on_close_img(self, event): + canvas = event.GetEventObject().GetPage(event.GetSelection()) + self.remove_img_win(canvas) + self.remove_img(canvas.image) + + def on_new_tab(self, event): + self.add_tab(self.tablenb.grid().table) + self.add_tab_win(self.tablenb.grid()) + + def on_close_tab(self, event): + grid = event.GetEventObject().GetPage(event.GetSelection()) + self.remove_tab_win(grid) + self.remove_tab(grid.table) + + def on_new_mesh(self, event): + self.add_mesh(self.meshnb.canvas().mesh) + self.add_mesh_win(self.meshnb.canvas()) + + def on_close_mesh(self, event): + canvas3d = event.GetEventObject().GetPage(event.GetSelection()) + self.remove_mesh(canvas3d.mesh) + self.remove_mesh_win(canvas3d) + + def set_info(self, value): + self.txt_info.SetLabel(value) + + def set_progress(self, value): + v = max(min(value, 100), 0) + self.pro_bar.SetValue(v) + if value==-1: + self.pro_bar.Hide() + elif not self.pro_bar.IsShown(): + self.pro_bar.Show() + self.stapanel.GetSizer().Layout() + self.pro_bar.Update() + + def on_close(self, event): + print('close') + #ConfigManager.write() + self.auimgr.UnInit() + del self.auimgr + self.Destroy() + Source.manager('config').write() + sys.exit() + + def _show_img(self, img, title=None): + canvas = self.canvasnb.add_canvas() + self.remove_img(canvas.image) + self.remove_img_win(canvas) + if not title is None: + canvas.set_imgs(img) + canvas.image.name = title + else: canvas.set_img(img) + self.add_img(canvas.image) + self.add_img_win(canvas) + + def show_img(self, img, title=None): + wx.CallAfter(self._show_img, img, title) + + def _show_table(self, tab, title): + grid = self.tablenb.add_grid() + self.remove_tab(grid.table) + self.remove_tab_win(grid) + grid.set_data(tab) + grid.table.name = title + info = self.auimgr.GetPane(self.tablenbwrap) + info.Show(True) + self.auimgr.Update() + self.add_tab(grid.table) + self.add_tab_win(grid) + + def show_table(self, tab, title=None): + wx.CallAfter(self._show_table, tab, title) + + def show_plot(self, title): + fig = PlotFrame(self) + fig.figure.title = title + return fig + + def show_md(self, cont, title='Document'): + page = self.mdframe.add_page() + page.set_cont(cont) + self.mdframe.Show() + + def _show_txt(self, cont, title='ImagePy'): + page = self.txtframe.add_notepad() + page.append(cont) + self.txtframe.Show() + + def show_txt(self, cont, title='ImagePy'): + wx.CallAfter(self._show_txt, cont, title) + + def _show_mesh(self, mesh=None, title=None): + if mesh is None: + canvas = self.meshnb.add_canvas() + canvas.mesh.name = 'Surface' + elif hasattr(mesh, 'ns'): + canvas = self.get_mesh_win() + if canvas is None: + canvas = self.meshnb.add_canvas() + canvas.mesh.name = 'Surface' + canvas.add_surf(title, mesh) + else: + canvas = self.meshnb.add_canvas() + canvas.set_mesh(mesh) + self.add_mesh(canvas.mesh) + self.add_mesh_win(canvas) + + info = self.auimgr.GetPane(self.meshnbwrap) + info.Show(True) + self.auimgr.Update() + + def show_mesh(self, mesh=None, title=None): + wx.CallAfter(self._show_mesh, mesh, title) + + def show_widget(self, panel, title='Widgets'): + obj = self.manager('widget').get(panel.title) + if obj is None: + pan = panel(self) + self.manager('widget').add(obj=pan, name=panel.title) + self.auimgr.AddPane(pan, aui.AuiPaneInfo().Caption(panel.title).Left().Layer( 15 ).PinButton( True ) + .Float().Resizable().FloatingSize( wx.DefaultSize ).Dockable(True)) #.DestroyOnClose()) + else: + info = self.auimgr.GetPane(obj) + info.Show(True) + self.Layout() + self.auimgr.Update() + + def switch_widget(self, visible=None): + info = self.auimgr.GetPane(self.widgets) + info.Show(not info.IsShown() if visible is None else visible) + self.auimgr.Update() + + def switch_toolbar(self, visible=None): + info = self.auimgr.GetPane(self.toolbar) + info.Show(not info.IsShown() if visible is None else visible) + self.auimgr.Update() + + def switch_table(self, visible=None): + info = self.auimgr.GetPane(self.tablenbwrap) + info.Show(not info.IsShown() if visible is None else visible) + self.auimgr.Update() + + def close_img(self, name=None): + names = self.get_img_name() if name is None else [name] + for name in names: + idx = self.canvasnb.GetPageIndex(self.get_img_win(name)) + self.remove_img(self.get_img_win(name).image) + self.remove_img_win(self.get_img_win(name)) + self.canvasnb.DeletePage(idx) + + def close_table(self, name=None): + names = self.get_tab_name() if name is None else [name] + for name in names: + idx = self.tablenb.GetPageIndex(self.get_tab_win(name)) + self.remove_tab(self.get_tab_win(name).table) + self.remove_tab_win(self.get_tab_win(name)) + self.tablenb.DeletePage(idx) + + def record_macros(self, cmd): + obj = self.manager('widget').get(name='Macros Recorder') + if obj is None or not obj.IsShown(): return + wx.CallAfter(obj.write, cmd) + + def run_macros(self, cmd, callafter=None): + cmds = [i for i in cmd] + def one(cmds, after): + cmd = cmds.pop(0) + title, para = cmd.split('>') + plg = Source.manager('plugin').get(name=title)() + after = lambda cmds=cmds: one(cmds, one) + if len(cmds)==0: after = callafter + wx.CallAfter(plg.start, self, eval(para), after) + one(cmds, None) + + def show(self, tag, cont, title): + tag = tag or 'img' + if tag=='img': + self.show_img([cont], title) + if tag=='imgs': + self.show_img(cont, title) + if tag=='macros': + self.run_macros(cont) + + def info(self, cont): + wx.CallAfter(self.txt_info.SetLabel, cont) + + def alert(self, info, title='ImagePy'): + dialog=wx.MessageDialog(self, info, title, wx.OK) + dialog.ShowModal() == wx.ID_OK + dialog.Destroy() + + def yes_no(self, info, title='ImagePy'): + dialog = wx.MessageDialog(self, info, title, wx.YES_NO | wx.CANCEL) + rst = dialog.ShowModal() + dialog.Destroy() + dic = {wx.ID_YES:'yes', wx.ID_NO:'no', wx.ID_CANCEL:'cancel'} + return dic[rst] + + def getpath(self, title, filt, io, name=''): + filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in filt]) + dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} + dialog = wx.FileDialog(self, title, '', name, filt, dic[io]) + rst = dialog.ShowModal() + path = dialog.GetPath() if rst == wx.ID_OK else None + dialog.Destroy() + return path + + def show_para(self, title, view, para, on_handle=None, on_ok=None, on_cancel=None, preview=False, modal=True): + dialog = ParaDialog(self, title) + dialog.init_view(view, para, preview, modal=modal, app=self) + dialog.Bind('cancel', on_cancel) + dialog.Bind('parameter', on_handle) + dialog.Bind('commit', on_ok) + return dialog.show() + +class P: + def __init__(self, name): + self.name = name + + def start(self): + print(self.name) + + def __call__(self): + return self + +data = ('menu', [ + ('File', [('Open', P('O')), + '-', + ('Close', P('C'))]), + ('Edit', [('Copy', P('C')), + ('A', [('B', P('B')), + ('C', P('C'))]), + ('Paste', P('P'))])]) + +if __name__ == '__main__': + import numpy as np + import pandas as pd + + app = wx.App(False) + frame = ImagePy(None) + frame.Show() + frame.show_img([np.zeros((512, 512), dtype=np.uint8)], 'zeros') + #frame.show_img(None) + frame.show_table(pd.DataFrame(np.arange(100).reshape((10,10))), 'title') + ''' + frame.show_md('abcdefg', 'md') + frame.show_md('ddddddd', 'md') + frame.show_txt('abcdefg', 'txt') + frame.show_txt('ddddddd', 'txt') + ''' + app.MainLoop() \ No newline at end of file diff --git a/imagepy/core/app/source.py b/imagepy/core/app/source.py new file mode 100644 index 00000000..dc0d5002 --- /dev/null +++ b/imagepy/core/app/source.py @@ -0,0 +1,31 @@ +from sciapp import Source +from imagepy import root_dir +import os.path as osp +from glob import glob +import numpy as np + +Source.manager('plugin') +Source.manager('tool') +Source.manager('widget') +Source.manager('macros') +Source.manager('config') + +Source.manager('config').read(osp.join(root_dir, 'data/config.json')) +from sciapp.object.shape import default_style +mark_style = Source.manager('config').get('mark_style') +if not mark_style is None: + for i in mark_style: default_style[i] = mark_style[i] + +Source.manager('colormap').remove() + +filenames = glob(osp.join(root_dir,'data/luts/*/*.lut')) +keys = [osp.split(filename)[-1][:-4] for filename in filenames] +values = [np.fromfile(i, dtype=np.uint8).reshape((3,256)).T.copy() for i in filenames] +for k,v in zip(keys[::-1], values[::-1]): Source.manager('colormap').add(k, v, 'adv') + +filenames = glob(osp.join(root_dir, 'data/luts/*.lut')) +keys = [osp.split(filename)[-1][:-4] for filename in filenames] +values = [np.fromfile(i, dtype=np.uint8).reshape((3,256)).T.copy() for i in filenames] +for k,v in zip(keys[::-1], values[::-1]): Source.manager('colormap').add(k, v, 'base') +print(Source.manager('colormap').names) +Source.manager('colormap').add('Grays', Source.manager('colormap').get('Grays'), 'base') \ No newline at end of file diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index 0a427e77..a75acdd8 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -8,10 +8,7 @@ import threading import numpy as np -from ... import IPy -from ...ui.panelconfig import ParaDialog -from ...core.manager import TextLogManager, ImageManager, \ -WindowsManager, TaskManager, WidgetsManager, DocumentManager +from ...core.manager import TaskManager, DocumentManager from time import time def process_channels(plg, ips, src, des, para): @@ -29,7 +26,6 @@ def process_channels(plg, ips, src, des, para): def process_one(plg, ips, src, img, para, callafter=None): TaskManager.add(plg) start = time() - transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16) transfloat = '2float' in plg.note and not ips.dtype in (np.complex128, np.float32, np.float64) if transint: @@ -42,10 +38,10 @@ def process_one(plg, ips, src, img, para, callafter=None): if not img is rst and not rst is None: imgrange = {np.uint8:(0,255), np.uint16:(0, 65535)}[img.dtype.type] np.clip(rst, imgrange[0], imgrange[1], out=img) - if 'auto_msk' in plg.note and not ips.get_msk() is None: - msk = True ^ ips.get_msk() + if 'auto_msk' in plg.note and not ips.mask('out') is None: + msk = ips.mask('out') img[msk] = src[msk] - IPy.set_info('%s: cost %.3fs'%(ips.title, time()-start)) + plg.app.info('%s: cost %.3fs'%(ips.title, time()-start)) ips.update() TaskManager.remove(plg) if not callafter is None:callafter() @@ -73,10 +69,10 @@ def process_stack(plg, ips, src, imgs, para, callafter=None): if not i is rst and not rst is None: imgrange = {np.uint8:(0,255), np.uint16:(0,65535)}[i.dtype.type] np.clip(rst, imgrange[0], imgrange[1], out=i) - if 'auto_msk' in plg.note and not ips.get_msk() is None: - msk = True ^ ips.get_msk() + if 'auto_msk' in plg.note and not ips.mask() is None: + msk = ips.mask('out') i[msk] = src[msk] - IPy.set_info('%s: cost %.3fs'%(ips.title, time()-start)) + plg.app.info('%s: cost %.3fs'%(ips.title, time()-start)) ips.update() TaskManager.remove(plg) if not callafter is None:callafter() @@ -91,25 +87,16 @@ class Filter: view = None prgs = (None, 1) - def __init__(self, ips=None): - if ips==None:ips = IPy.get_ips() - self.dialog = None - self.ips = ips + def __init__(self, ips=None): pass def progress(self, i, n): self.prgs = (i, n) - def show(self, temp=ParaDialog): - self.dialog = temp(WindowsManager.get(), self.title) - self.dialog.init_view(self.view, self.para, 'preview' in self.note, modal=self.modal) - - self.dialog.on_help = lambda : IPy.show_md(self.title, DocumentManager.get(self.title)) - self.dialog.set_handle(lambda x:self.preview(self.ips, x)) - - self.dialog.on_ok = lambda : self.ok(self.ips) - self.dialog.on_cancel = lambda : self.cancel(self.ips) - if self.modal: return self.dialog.ShowModal() == wx.ID_OK - self.dialog.Show() + def show(self): + preview = lambda para, ips=self.ips: self.preview(ips, para) or ips.update() + return self.app.show_para(self.title, self.view, self.para, preview, + on_ok=lambda : self.ok(self.ips), on_cancel=lambda : self.cancel(self.ips) or self.ips.update(), + preview='preview' in self.note, modal=self.modal) def run(self, ips, snap, img, para = None): return 255-img @@ -117,27 +104,21 @@ def run(self, ips, snap, img, para = None): def check(self, ips): note = self.note if ips == None: - IPy.alert('No image opened!') + return self.app.alert('No image opened!') return False elif 'req_roi' in note and ips.roi == None: - IPy.alert('No Roi found!') - return False + return self.app.alert('No Roi found!') elif not 'all' in note: - if ips.get_imgtype()=='rgb' and not 'rgb' in note: - IPy.alert('Do not surport rgb image') - return False - elif ips.get_imgtype()=='8-bit' and not '8-bit' in note: - IPy.alert('Do not surport 8-bit image') - return False - elif ips.get_imgtype()=='16-bit' and not '16-bit' in note: - IPy.alert('Do not surport 16-bit uint image') - return False - elif ips.get_imgtype()=='32-int' and not 'int' in note: - IPy.alert('Do not surport 32-bit int uint image') - return False - elif 'float' in ips.get_imgtype() and not 'float' in note: - IPy.alert('Do not surport float image') - return False + if ips.dtype==np.uint8 and ips.channels==3 and not 'rgb' in note: + return self.app.alert('Do not surport rgb image') + elif ips.dtype==np.uint8 and ips.channels==1 and not '8-bit' in note: + return self.app.alert('Do not surport 8-bit image') + elif ips.dtype==np.uint16 and not '16-bit' in note: + return self.app.alert('Do not surport 16-bit uint image') + elif ips.dtype in [np.int32, np.int64] and not 'int' in note: + return self.app.alert('Do not surport int image') + elif ips.dtype in [np.float32, np.float64] and not 'float' in note: + return self.app.alert('Do not surport float image') return True def preview(self, ips, para): @@ -148,38 +129,33 @@ def load(self, ips):return True def ok(self, ips, para=None, callafter=None): if para == None: para = self.para - if not 'not_slice' in self.note and ips.get_nslices()>1: + if not 'not_slice' in self.note and ips.slices>1: if para == None:para = {} if para!=None and 'stack' in para:del para['stack'] - win = WidgetsManager.getref('Macros Recorder') - if ips.get_nslices()==1 or 'not_slice' in self.note: + # = WidgetsManager.getref('Macros Recorder') + if ips.slices==1 or 'not_slice' in self.note: # process_one(self, ips, ips.snap, ips.img, para) - if IPy.uimode() == 'no': - process_one(self, ips, ips.snap, ips.img, para, callafter) - else: threading.Thread(target = process_one, args = + threading.Thread(target = process_one, args = (self, ips, ips.snap, ips.img, para, callafter)).start() - if win!=None: win.write('{}>{}'.format(self.title, para)) - elif ips.get_nslices()>1: + # if win!=None: win.write('{}>{}'.format(self.title, para)) + self.app.record_macros('{}>{}'.format(self.title, para)) + elif ips.slices>1: has, rst = 'stack' in para, None if not has: - rst = IPy.yes_no('Run every slice in current stacks?') + rst = self.app.yes_no('Run every slice in current stacks?') if 'auto_snap' in self.note and self.modal:ips.reset() if has and para['stack'] or rst == 'yes': para['stack'] = True #process_stack(self, ips, ips.snap, ips.imgs, para) - if IPy.uimode() == 'no': - process_stack(self, ips, ips.snap, ips.imgs, para, callafter) - else: threading.Thread(target = process_stack, args = + threading.Thread(target = process_stack, args = (self, ips, ips.snap, ips.imgs, para, callafter)).start() - if win!=None: win.write('{}>{}'.format(self.title, para)) + self.app.record_macros('{}>{}'.format(self.title, para)) elif has and not para['stack'] or rst == 'no': para['stack'] = False #process_one(self, ips, ips.snap, ips.img, para) - if IPy.uimode() == 'no': - process_one(self, ips, ips.snap, ips.img, para, callafter) - else: threading.Thread(target = process_one, args = + threading.Thread(target = process_one, args = (self, ips, ips.snap, ips.img, para, callafter)).start() - if win!=None: win.write('{}>{}'.format(self.title, para)) + self.app.record_macros('{}>{}'.format(self.title, para)) elif rst == 'cancel': pass #ips.update() @@ -188,11 +164,11 @@ def cancel(self, ips): ips.img[:] = ips.snap ips.update() - def start(self, para=None, callafter=None): - ips = self.ips - if not self.check(ips):return - if not self.load(ips):return - if 'auto_snap' in self.note:ips.snapshot() + def start(self, app, para=None, callafter=None): + self.app, self.ips = app, app.get_img() + if not self.check(self.ips):return + if not self.load(self.ips):return + if 'auto_snap' in self.note:self.ips.snapshot() if para!=None: self.ok(self.ips, para, callafter) @@ -203,10 +179,9 @@ def start(self, para=None, callafter=None): else: self.ok(self.ips, para, callafter) elif self.modal: if self.show(): - self.ok(ips, None, callafter) - else:self.cancel(ips) - self.dialog.Destroy() + self.ok(self.ips, None, callafter) + else:self.cancel(self.ips) else: self.show() def __del__(self): - print('filter del') \ No newline at end of file + print('filter del') diff --git a/imagepy/core/engine/free.py b/imagepy/core/engine/free.py index 1756866f..bba815d2 100644 --- a/imagepy/core/engine/free.py +++ b/imagepy/core/engine/free.py @@ -3,11 +3,8 @@ Created on Sat Dec 3 03:57:53 2016 @author: yxl """ -import threading, wx - -from ... import IPy -from ...ui.panelconfig import ParaDialog -from ...core.manager import WindowsManager, TextLogManager, TaskManager, WidgetsManager, DocumentManager +import threading +from ...core.manager import TaskManager, DocumentManager from time import time class Free: @@ -27,7 +24,7 @@ def runasyn(self, para, callback=None): TaskManager.add(self) start = time() self.run(para) - IPy.set_info('%s: cost %.3fs'%(self.title, time()-start)) + self.app.info('%s: cost %.3fs'%(self.title, time()-start)) TaskManager.remove(self) if callback!=None:callback() @@ -35,20 +32,15 @@ def load(self):return True def show(self): if self.view==None:return True - with ParaDialog(WindowsManager.get(), self.title) as dialog: - dialog.init_view(self.view, self.para, False, True) - doc = self.__doc__ or '### Sorry\nNo document yet!' - dialog.on_help = lambda : IPy.show_md(self.title, DocumentManager.get(self.title)) - return dialog.ShowModal() == wx.ID_OK + return self.app.show_para(self.title, self.view, self.para, None) - def start(self, para=None, callback=None): + def start(self, app, para=None, callback=None): + self.app = app if not self.load():return if para!=None or self.show(): if para==None:para = self.para - win = WidgetsManager.getref('Macros Recorder') - if win!=None: - win.write('{}>{}'.format(self.title, para)) - if self.asyn and IPy.uimode()!='no': - threading.Thread(target = self.runasyn, args = (para, callback)).start() - else: - self.runasyn(para, callback) \ No newline at end of file + self.app.record_macros('{}>{}'.format(self.title, para)) + if self.asyn: + threading.Thread(target = self.runasyn, + args = (para, callback)).start() + else: self.runasyn(para, callback) diff --git a/imagepy/core/engine/macros.py b/imagepy/core/engine/macros.py index e04387b4..dff0ec54 100644 --- a/imagepy/core/engine/macros.py +++ b/imagepy/core/engine/macros.py @@ -1,63 +1,9 @@ -# -*- coding: utf-8 -*- -""" -Created on Thu Dec 29 01:48:23 2016 -@author: yxl -""" -import wx -from ... import IPy -from ...core.manager import PluginsManager -from pubsub import pub -from imagepy.core.manager import ReaderManager, ViewerManager -from imagepy import IPy - -def stepmacros(plg, callafter=None): - plg._next(callafter) -pub.subscribe(stepmacros, 'stepmacros') - class Macros: def __init__(self, title, cmds): self.title = title self.cmds = cmds - def _next(self, callafter=None): - if self.cur==len(self.cmds): - if self.callafter!=None: - self.callafter() - return - if len(self.cmds[self.cur])<3 or self.cmds[self.cur][0] == '#': - self.cur += 1 - return self._next(callafter) - title, para = self.cmds[self.cur].split('>') - self.cur += 1 - plg = PluginsManager.get(title)() - plg.start(eval(para), self.next) - - def next(self): - if IPy.uimode() == 'no': - self._next(self) - else: wx.CallAfter(pub.sendMessage, 'stepmacros', plg=self) - - def run(self):self.next() - #IPy.run_macros(self.cmds) + def __call__(self): return self - def __call__(self): - return self - - def start(self, para=None, callafter=None): - self.callafter = callafter - self.cur = 0 - self.run() - -def show_mc(data, title): - wx.CallAfter(Macros(title, data).start) - -ViewerManager.add('mc', show_mc) - -def read_mc(path): - f = open(path, encoding='utf-8') - cont = f.readlines() - f.close() - print(cont) - return cont - -ReaderManager.add('mc', read_mc, tag='mc') \ No newline at end of file + def start(self, app, para=None, callafter=None): + app.run_macros(self.cmds, callafter) \ No newline at end of file diff --git a/imagepy/core/engine/mkdown.py b/imagepy/core/engine/mkdown.py index eb7a9f60..5890c140 100644 --- a/imagepy/core/engine/mkdown.py +++ b/imagepy/core/engine/mkdown.py @@ -1,5 +1,3 @@ -from imagepy import IPy - class MkDown: def __init__(self, title, cont, url=''): self.title = title diff --git a/imagepy/core/engine/report.py b/imagepy/core/engine/report.py index 86fdd6bf..d801ab75 100644 --- a/imagepy/core/engine/report.py +++ b/imagepy/core/engine/report.py @@ -4,10 +4,10 @@ @author: yxl """ import wx -from imagepy import IPy -from imagepy.core.manager import WidgetsManager, TaskManager, ImageManager -from imagepy.core.manager import ReaderManager, ViewerManager, TableManager -from imagepy.ui.propertygrid import GridDialog +from imagepy.core.manager import TaskManager +from sciapp import Source +from imagepy.core.manager import ReaderManager +#from imagepy.ui.propertygrid import GridDialog from imagepy.core.util import xlreport from time import time import openpyxl as pyxl @@ -50,13 +50,13 @@ def start(self, para=None, callafter=None): if rst != 5100: return filt = '|'.join(['%s files (*.%s)|*.%s'%('XLSX', 'xlsx', 'xlsx')]) if not IPy.getpath('Save..', filt, 'save', para): return - win = WidgetsManager.getref('Macros Recorder') + win = Source.manager('widget').get('obj', name='Macros Recorder') if win!=None: win.write('{}>{}'.format(self.title, para)) self.runasyn(wb, info, key, para, callafter) def show_rpt(data, title): wx.CallAfter(Report(title, data).start) -ViewerManager.add('rpt', show_rpt) +# ViewerManager.add('rpt', show_rpt) def read_rpt(path): return path -ReaderManager.add('rpt', read_rpt, tag='rpt') \ No newline at end of file +ReaderManager.add(name='rpt', obj=read_rpt, tag='rpt') \ No newline at end of file diff --git a/imagepy/core/engine/simple.py b/imagepy/core/engine/simple.py index 62e89f80..b4076b44 100644 --- a/imagepy/core/engine/simple.py +++ b/imagepy/core/engine/simple.py @@ -6,10 +6,9 @@ import wx import threading -from ... import IPy -from ...ui.panelconfig import ParaDialog -from ..manager import TextLogManager, TaskManager, WidgetsManager, DocumentManager +from ..manager import TaskManager, DocumentManager from time import time +import numpy as np class Simple: title = 'SimpleFilter' @@ -21,10 +20,7 @@ class Simple: prgs = (None, 1) modal = True - def __init__(self, ips=None): - print('Simple start') - self.ips = IPy.get_ips() if ips==None else ips - self.dialog = None + def __init__(self): pass def progress(self, i, n): self.prgs = (i, n) @@ -33,35 +29,29 @@ def load(self, ips):return True def preview(self, ips, para):pass - def show(self, temp=ParaDialog): - if self.view==None:return True - self.dialog = temp(IPy.get_window(), self.title) - self.dialog.init_view(self.view, self.para, 'preview' in self.note, modal=self.modal) - self.dialog.on_help = lambda : IPy.show_md(self.title, DocumentManager.get(self.title)) - self.dialog.set_handle(lambda x:self.preview(self.ips, self.para) is self.ips.update()) - if self.modal: return self.dialog.ShowModal() == wx.ID_OK - self.dialog.on_ok = lambda : self.ok(self.ips) - self.dialog.on_cancel = lambda : self.cancel(self.ips) is self.ips.update() - self.dialog.Show() + def show(self): + preview = lambda para, ips=self.ips: self.preview(ips, para) or ips.update() + return self.app.show_para(self.title, self.view, self.para, preview, + on_ok=lambda : self.ok(self.ips), on_cancel=lambda : self.cancel(self.ips) or self.ips.update(), + preview='preview' in self.note, modal=self.modal) def run(self, ips, imgs, para = None):pass - + def cancel(self, ips):pass def ok(self, ips, para=None, callafter=None): if para == None: para = self.para - if self.asyn and IPy.uimode()!='no': + if self.asyn : threading.Thread(target = self.runasyn, args = (ips, ips.imgs, para, callafter)).start() else: self.runasyn(ips, ips.imgs, para, callafter) - win = WidgetsManager.getref('Macros Recorder') - if win!=None: win.write('{}>{}'.format(self.title, para)) + self.app.record_macros('{}>{}'.format(self.title, para)) def runasyn(self, ips, imgs, para = None, callback = None): TaskManager.add(self) start = time() self.run(ips, imgs, para) - IPy.set_info('%s: cost %.3fs'%(ips.title, time()-start)) + self.app.info('%s: cost %.3fs'%(ips.title, time()-start)) ips.update() TaskManager.remove(self) if callback!=None:callback() @@ -69,41 +59,42 @@ def runasyn(self, ips, imgs, para = None, callback = None): def check(self, ips): note = self.note if ips == None: - IPy.alert('No image opened!') + self.app.alert('No image opened!') return False if 'req_roi' in note and ips.roi == None: - IPy.alert('No Roi found!') + self.app.alert('No Roi found!') return False if not 'all' in note: - if ips.get_imgtype()=='rgb' and not 'rgb' in note: - IPy.alert('Do not surport rgb image') + if ips.dtype==np.uint8 and ips.channels==3 and not 'rgb' in note: + self.app.alert('Do not surport rgb image') return False - elif ips.get_imgtype()=='8-bit' and not '8-bit' in note: - IPy.alert('Do not surport 8-bit image') + elif ips.dtype==np.uint8 and ips.channels==1 and not '8-bit' in note: + self.app.alert('Do not surport 8-bit image') return False - elif ips.get_imgtype()=='16-bit' and not '16-bit' in note: - IPy.alert('Do not surport 16-bit uint image') + elif ips.dtype==np.uint16 and not '16-bit' in note: + self.app.alert('Do not surport 16-bit uint image') return False - elif ips.get_imgtype()=='32-int' and not 'int' in note: - IPy.alert('Do not surport 32-bit int uint image') + elif ips.dtype==np.int32 and not 'int' in note: + self.app.alert('Do not surport 32-bit int uint image') return False - elif 'float' in ips.get_imgtype() and not 'float' in note: - IPy.alert('Do not surport float image') + elif ips.dtype in {np.float32, np.float64} and not 'float' in note: + self.app.alert('Do not surport float image') return False if sum([i in note for i in ('stack','stack2d','stack3d')])>0: - if ips.get_nslices()==1: - IPy.alert('Stack required!') + if ips.slices==1: + self.app.alert('Stack required!') return False - elif 'stack2d' in note and ips.is3d: - IPy.alert('Stack2d required!') + elif 'stack2d' in note and ips.isarray: + self.app.alert('Stack2d required!') return False - elif 'stack3d' in note and not ips.is3d: - IPy.alert('Stack3d required!') + elif 'stack3d' in note and not ips.isarray: + self.app.alert('Stack3d required!') return False return True - def start(self, para=None, callback=None): + def start(self, app, para=None, callback=None): #print self.title, para + self.app, self.ips = app, app.get_img() if not self.check(self.ips):return if not self.load(self.ips):return @@ -121,5 +112,4 @@ def start(self, para=None, callback=None): else: self.cancel(self.ips) self.ips.update() - if not self.dialog is None: self.dialog.Destroy() else: self.show() \ No newline at end of file diff --git a/imagepy/core/engine/table.py b/imagepy/core/engine/table.py index a3cb0d3a..27069ff0 100644 --- a/imagepy/core/engine/table.py +++ b/imagepy/core/engine/table.py @@ -6,115 +6,97 @@ import wx import threading -from ... import IPy -from ...ui.panelconfig import ParaDialog -from ..manager import TextLogManager, TaskManager, WidgetsManager, DocumentManager +from ..manager import TaskManager, DocumentManager +from sciapp import Source from time import time class Table: title = 'TableFilter' note = [] para = None - 'req_sel, req_row, req_col, snap, row_not, row_msk, col_msk, col_not, num_only, preview' + 'req_sel, req_row, req_col, auto_snap, auto_msk, msk_not, num_only, preview' view = None prgs = (None, 1) modal = True asyn = True def __init__(self, tps=None): - print('simple start') - self.tps = IPy.get_tps() if tps==None else tps + self.dialog = None def progress(self, i, n): self.prgs = (i, n) - def load(self, ips):return True + def load(self, tps):return True def preview(self, tps, para): - self.run(tps, tps.data, tps.snap, para) + self.run(tps, tps.snap, tps.data, para) tps.update() - + def show(self): - if self.view==None:return True - self.dialog = ParaDialog(IPy.get_twindow(), self.title) - self.dialog.init_view(self.view, self.para, 'preview' in self.note, modal=self.modal) - self.dialog.on_help = lambda : IPy.show_md(self.title, DocumentManager.get(self.title)) - self.dialog.set_handle(lambda x:self.preview(self.tps, self.para)) - if self.modal: return self.dialog.ShowModal() == wx.ID_OK - self.dialog.on_ok = lambda : self.ok(self.tps) - self.dialog.on_cancel = lambda : self.cancel(self.tps) - self.dialog.Show() + preview = lambda para, tps=self.tps: self.preview(tps, para) or tps.update() + return self.app.show_para(self.title, self.view, self.para, preview, + on_ok=lambda : self.ok(self.tps), on_cancel=lambda : self.cancel(self.tps) or self.tps.update(), + preview='preview' in self.note, modal=self.modal) def run(self, tps, snap, data, para = None):pass def cancel(self, tps): - if 'snap' in self.note: + if 'auto_snap' in self.note: tps.data[tps.snap.columns] = tps.snap tps.update() def ok(self, tps, para=None, callafter=None): if para == None: para = self.para - if self.asyn and IPy.uimode() != 'no': + if self.asyn: threading.Thread(target = self.runasyn, - args = (tps, tps.data, tps.snap, para, callafter)).start() + args = (tps, tps.data, tps.snap, para, callafter)).start() else: self.runasyn(tps, tps.data, tps.snap, para, callafter) - win = WidgetsManager.getref('Macros Recorder') + win = Source.get('widget').get('obj', name='Macros Recorder') if win!=None: win.write('{}>{}'.format(self.title, para)) def runasyn(self, tps, snap, data, para = None, callback = None): TaskManager.add(self) start = time() self.run(tps, data, snap, para) - IPy.set_info('%s: cost %.3fs'%(tps.title, time()-start)) - tps.update('shp') + self.app.set_info('%s: cost %.3fs'%(tps.title, time()-start)) + tps.update() TaskManager.remove(self) if callback!=None:callback() def check(self, tps): print(self.note) if tps == None: - IPy.alert('no table opened!') + self.app.alert('no table opened!') return False if 'req_sel' in self.note: print(tps.rowmsk, tps.colmsk) if isinstance(tps.rowmsk, slice) and\ isinstance(tps.colmsk, slice): - IPy.alert('no selection!') + self.app.alert('no selection!') return False if 'req_row' in self.note: print(tps.rowmsk, tps.colmsk) if isinstance(tps.rowmsk, slice): - IPy.alert('need row selection!') + self.app.alert('need row selection!') return False if 'req_col' in self.note: print(tps.rowmsk, tps.colmsk) if isinstance(tps.colmsk, slice): - IPy.alert('need col selection!') + self.app.alert('need col selection!') return False return True - - def snapshot(self, tps): - note = self.note - if not 'snap' in note: return None - if 'row_msk' in note: - rmsk = True - elif 'row_not' in note: - rmsk = False - else: rmsk = None - if 'col_msk' in note: - cmsk = True - elif 'col_not' in note: - cmsk = False - else: cmsk = None - only = 'num_only' in note - tps.snapshot(rmsk, cmsk, only) - def start(self, para=None, callback=None): + def start(self, app, para=None, callback=None): + self.app, self.tps = app, app.get_tab() #print self.title, para if not self.check(self.tps):return if not self.load(self.tps):return - if 'snap' in self.note:self.snapshot(self.tps) + if 'auto_snap' in self.note: + if 'auto_msk' in self.note: mode = True + elif 'msk_not' in self.note: mode = False + else: mode = None + self.tps.snapshot(mode, 'num_only' in self.note) if para!=None: self.ok(self.tps, para, callback) elif self.view==None: diff --git a/imagepy/core/engine/tool.py b/imagepy/core/engine/tool.py index 1ebdc23b..81738fbf 100644 --- a/imagepy/core/engine/tool.py +++ b/imagepy/core/engine/tool.py @@ -3,8 +3,6 @@ Created on Sat Dec 3 03:55:51 2016 @author: yxl """ -from ... import IPy -from ...core.manager import ToolsManager class Tool: title = 'Tool' @@ -23,8 +21,6 @@ def start(self): ips = IPy.get_ips() if not ips is None and not ips.tool is None: ips.tool = None - ips.update() - ToolsManager.set(self) def mouse_down(self, ips, x, y, btn, **key): pass def mouse_up(self, ips, x, y, btn, **key): pass diff --git a/imagepy/core/engine/widget.py b/imagepy/core/engine/widget.py index 933658be..8684c9a4 100644 --- a/imagepy/core/engine/widget.py +++ b/imagepy/core/engine/widget.py @@ -1,34 +1,9 @@ -# -*- coding: utf-8 -*- -""" -Created on Sat Dec 3 03:57:53 2016 -@author: yxl -""" -import threading, wx, os, wx.lib.agw.aui as aui -from imagepy import IPy, root_dir -from ..manager import WidgetsManager - class Widget(): def __init__(self, panel): - self.pan = panel + self.panel = panel self.title = panel.title - def __call__(self):return self - - def start(self): - #if not WidgetsManager.getref(self.title) is None: return - pan = self.pan(IPy.curapp) - WidgetsManager.addref(pan) - IPy.curapp.auimgr.AddPane(pan, aui.AuiPaneInfo().Caption(self.title).Left().Layer( 15 ).PinButton( True ) - .Float().Resizable().FloatingSize( wx.DefaultSize ).Dockable(IPy.uimode()=='ipy').DestroyOnClose()) + def __call__(self): return self - IPy.curapp.Layout() - IPy.curapp.auimgr.Update() - ''' - frame = wx.Frame(IPy.curapp) - frame.SetTitle(self.title) - logopath = os.path.join(root_dir, 'data/logo.ico') - frame.SetIcon(wx.Icon(logopath, wx.BITMAP_TYPE_ICO)) - self.pan(frame) - frame.Fit() - frame.Show() - ''' + def start(self, app): + app.show_widget(self.panel) \ No newline at end of file diff --git a/imagepy/core/engine/workflow.py b/imagepy/core/engine/workflow.py index f9091522..fb740d27 100644 --- a/imagepy/core/engine/workflow.py +++ b/imagepy/core/engine/workflow.py @@ -1,7 +1,5 @@ -from imagepy.ui.workflowwindow import WorkFlowPanel import threading, wx, os, wx.lib.agw.aui as aui -from imagepy.core.manager import ReaderManager, ViewerManager -from imagepy import IPy +from imagepy.core.manager import ReaderManager def parse(cont): ls = cont.split('\n') @@ -42,7 +40,7 @@ def start(self, para=None, callafter=None): def show_wf(data, title): wx.CallAfter(WorkFlow(title, data).start) -ViewerManager.add('wf', show_wf) +# ViewerManager.add('wf', show_wf) def read_wf(path): f = open(path, encoding='utf-8') @@ -51,4 +49,4 @@ def read_wf(path): print(cont) return cont -ReaderManager.add('wf', read_wf, tag='wf') \ No newline at end of file +ReaderManager.add(name='wf', obj=read_wf, tag='wf') \ No newline at end of file diff --git a/imagepy/core/loader/loader.py b/imagepy/core/loader/loader.py index 3eb83dfa..32098160 100644 --- a/imagepy/core/loader/loader.py +++ b/imagepy/core/loader/loader.py @@ -6,8 +6,9 @@ """ import os, sys from ..engine import Macros, MkDown, Widget, WorkFlow, Report -from ..manager import ToolsManager, PluginsManager, WidgetsManager, DocumentManager -from ... import IPy, root_dir +from ..manager import DocumentManager +from sciapp import Source +from ... import root_dir from codecs import open def getpath(root, path): @@ -27,56 +28,56 @@ def extend_plugins(path, lst, err): pt = os.path.join(root_dir,path) print(pt+'/'+i) rst.append(Report(i[:-4], pt+'/'+i)) - PluginsManager.add(rst[-1]) + Source.manager('plugin').add(obj=rst[-1], name=rst[-1].title) elif i[-3:] == '.mc': pt = os.path.join(root_dir, path) f = open(pt+'/'+i, 'r', 'utf-8') cmds = f.readlines() f.close() rst.append(Macros(i[:-3], [getpath(pt, i) for i in cmds])) - PluginsManager.add(rst[-1]) + Source.manager('plugin').add(obj=rst[-1], name=rst[-1].title) elif i[-3:] == '.wf': pt = os.path.join(root_dir,path) f = open(pt+'/'+i, 'r', 'utf-8') cmds = f.read() f.close() rst.append(WorkFlow(i[:-3], cmds)) - PluginsManager.add(rst[-1]) + Source.manager('plugin').add(obj=rst[-1], name=rst[-1].title) elif i[-3:] == '.md': f = open(os.path.join(root_dir,path)+'/'+i, 'r', 'utf-8') cont = f.read() f.close() rst.append(MkDown(i[:-3], cont)) - PluginsManager.add(rst[-1]) + Source.manager('plugin').add(obj=rst[-1], name=rst[-1].title) elif i[-6:] in ['wgt.py', 'gts.py']: - try: - rpath = path.replace('/', '.').replace('\\','.') - #rpath = rpath[rpath.index('imagepy.'):] - plg = __import__('imagepy.'+ rpath+'.'+i[:-3],'','',['']) - if hasattr(plg, 'wgts'): - rst.extend([j if j=='-' else Widget(j) for j in plg.wgts]) - for p in plg.wgts: - if not isinstance(p, str):WidgetsManager.add(p) - else: - rst.append(Widget(plg.Plugin)) - WidgetsManager.add(plg.Plugin) - except Exception as e: - err.append((path, i, sys.exc_info()[1])) + #try: + rpath = path.replace('/', '.').replace('\\','.') + #rpath = rpath[rpath.index('imagepy.'):] + plg = __import__('imagepy.'+ rpath+'.'+i[:-3],'','',['']) + if hasattr(plg, 'wgts'): + rst.extend([j if j=='-' else Widget(j) for j in plg.wgts]) + for p in plg.wgts: + print(p) + if not isinstance(p, str):Source.manager('widget').add(obj=p, name=p.title) + else: + rst.append(Widget(plg.Plugin)) + Source.manager('widget').add(obj=plg.Plugin, name=plg.Plugin.title) + #except Exception as e: + # err.append((path, i, sys.exc_info()[1])) else: - try: - rpath = path.replace('/', '.').replace('\\','.') - #rpath = rpath[rpath.index('imagepy.'):] - plg = __import__('imagepy.'+ rpath+'.'+i[:-3],'','',['']) - if hasattr(plg, 'plgs'): - rst.extend([j for j in plg.plgs]) - for p in plg.plgs: - if not isinstance(p, str):PluginsManager.add(p) - else: - rst.append(plg.Plugin) - PluginsManager.add(plg.Plugin) - except Exception as e: - err.append((path, i, sys.exc_info()[1])) - + #try: + rpath = path.replace('/', '.').replace('\\','.') + #rpath = rpath[rpath.index('imagepy.'):] + plg = __import__('imagepy.'+ rpath+'.'+i[:-3],'','',['']) + if hasattr(plg, 'plgs'): + rst.extend([j for j in plg.plgs]) + for p in plg.plgs: + if not isinstance(p, str): Source.manager('plugin').add(obj=p, name=p.title) + else: + rst.append(plg.Plugin) + Source.manager('plugin').add(obj=plg.Plugin, name=plg.Plugin.title) + #except Exception as e: + # err.append((path, i, sys.exc_info()[1])) return rst def sort_plugins(catlog, lst): @@ -111,8 +112,6 @@ def build_plugins(path, err=False): pg = __import__('imagepy.'+rpath,'','',['']) pg.title = os.path.basename(path) if hasattr(pg, 'catlog'): - if 'Personal Information' in pg.catlog: - print(subtree) subtree = sort_plugins(pg.catlog, subtree) subtree = extend_plugins(path, subtree, err) @@ -133,18 +132,18 @@ def extend_tools(path, lst, err): rst.append((Macros(i[:-3], [getpath(pt, i) for i in cmds]), os.path.join(root_dir, path)+'/'+i[:-3]+'.gif')) else: - try: - rpath = path.replace('/', '.').replace('\\','.') - #rpath = rpath[rpath.index('imagepy.'):] - - plg = __import__('imagepy.'+rpath+'.'+i,'','',['']) - if hasattr(plg, 'plgs'): - for i,j in plg.plgs: rst.append((i, path+'/'+j)) - else: rst.append((plg.Plugin, - os.path.join(root_dir, path)+'/'+i.split('_')[0]+'.gif')) - except Exception as e: - err.append((path, i, sys.exc_info()[1])) - for i in rst:ToolsManager.add(i[0]) + #try: + rpath = path.replace('/', '.').replace('\\','.') + #rpath = rpath[rpath.index('imagepy.'):] + + plg = __import__('imagepy.'+rpath+'.'+i,'','',['']) + if hasattr(plg, 'plgs'): + for i,j in plg.plgs: rst.append((i, path+'/'+j)) + else: rst.append((plg.Plugin, + os.path.join(root_dir, path)+'/'+i.split('_')[0]+'.gif')) + #except Exception as e: + # err.append((path, i, sys.exc_info()[1])) + for i in rst:Source.manager('tool').add(obj=i[0], name=i[0].title) return rst def sort_tools(catlog, lst): @@ -197,7 +196,7 @@ def extend_widgets(path, lst, err): rst.append(plg.Plugin) except Exception as e: err.append((path, i, sys.exc_info()[1])) - for i in rst:WidgetsManager.add(i) + for i in rst:Source.manager('widget').add(obj=i, name=i.name) return rst def sort_widgets(catlog, lst): diff --git a/imagepy/core/manager/__init__.py b/imagepy/core/manager/__init__.py index 323c2709..d6c18fe0 100644 --- a/imagepy/core/manager/__init__.py +++ b/imagepy/core/manager/__init__.py @@ -1,6 +1,5 @@ from .colormanager import * from .pluginmanager import * -from .windowmanager import * from .roimanager import * from .clipbdmanager import * from .configmanager import * @@ -8,4 +7,4 @@ from .taskmanager import * from .iomanager import * from .languagemanager import * -from .documentmanager import * \ No newline at end of file +from .documentmanager import * diff --git a/imagepy/core/manager/configmanager.py b/imagepy/core/manager/configmanager.py index 775280ee..0f9dbb93 100644 --- a/imagepy/core/manager/configmanager.py +++ b/imagepy/core/manager/configmanager.py @@ -5,6 +5,7 @@ """ import pickle, os from ... import root_dir + class ConfigManager: cfg = {} filename = os.path.join(root_dir, "preference.cfg") diff --git a/imagepy/core/manager/iomanager.py b/imagepy/core/manager/iomanager.py index 2e8bf33c..0f63292f 100644 --- a/imagepy/core/manager/iomanager.py +++ b/imagepy/core/manager/iomanager.py @@ -1,63 +1,48 @@ +''' class ReaderManager: - reader = {} - + reader = [] + @classmethod - def add(cls, ext, read, tag='img'): - if not tag in cls.reader: cls.reader[tag] = {} - if isinstance(ext, str): - cls.reader[tag][ext.lower()] = read - return + def add(cls, ext, read, tag='img', note=''): + if isinstance(ext, str): ext = [ext] for i in ext: - cls.reader[tag][i.lower()] = read + obj = (i, read, tag, note) + if not obj in cls.reader: cls.reader.append(obj) @classmethod - def get(cls, ext=None, tag='img'): - if ext is None and tag is None: - ls = [cls.reader[i].keys() for i in cls.reader.keys()] - return sorted([x for j in ls for x in j]) - elif ext is None and not tag is None: - return sorted(cls.reader[tag].keys()) - elif not ext is None and tag is None: - for i in cls.reader.values(): - if ext.lower() in i: return i[ext.lower()] - elif not tag is None and not ext is None: - if ext.lower() in cls.reader[tag]: - return cls.reader[tag][ext.lower()] + def get(cls, ext=None, tag=None, note=None): + msk = [True for i in cls.reader] + if not ext is None: + for i in range(len(msk)): msk[i] &= cls.reader[i][0]==ext + if not tag is None: + for i in range(len(msk)): msk[i] &= cls.reader[i][2]==tag + if not note is None: + for i in range(len(msk)): msk[i] &= cls.reader[i][3]==note + return [cls.reader[i] for i in range(len(msk)) if msk[i]] class WriterManager: - writer = {} + writer = [] @classmethod - def add(cls, ext, write, tag='img'): - if not tag in cls.writer: cls.writer[tag] = {} - if isinstance(ext, str): - cls.writer[tag][ext.lower()] = write - return + def add(cls, ext, read, tag='img', note=''): + if isinstance(ext, str): ext = [ext] for i in ext: - cls.writer[tag][i.lower()] = write - - @classmethod - def get(cls, ext=None, tag='img'): - if ext is None and tag is None: - ls = [cls.writer[i].keys() for i in cls.writer.keys()] - return sorted([x for j in ls for x in j]) - elif ext is None and not tag is None: - return sorted(cls.writer[tag].keys()) - elif not ext is None and tag is None: - for i in cls.writer.values(): - if ext.lower() in i: return i[ext.lower()] - elif not tag is None and not ext is None: - if ext.lower() in cls.writer[tag]: - return cls.writer[tag][ext.lower()] - -class ViewerManager: - viewer = {} + obj = (i, read, tag, note) + if not obj in cls.writer: cls.writer.append(obj) @classmethod - def add(cls, ext, view):cls.viewer[ext.lower()] = view - - @classmethod - def get(cls, ext='img'): - for i in ReaderManager.reader: - if ext.lower() in ReaderManager.reader[i]: - return cls.viewer[i] \ No newline at end of file + def get(cls, ext=None, tag=None, note=None): + msk = [True for i in cls.writer] + if not ext is None: + for i in range(len(msk)): msk[i] &= cls.writer[i][0]==ext + if not tag is None: + for i in range(len(msk)): msk[i] &= cls.writer[i][2]==tag + if not note is None: + for i in range(len(msk)): msk[i] &= cls.writer[i][3]==note + return [cls.writer[i] for i in range(len(msk)) if msk[i]] +''' + +from sciapp import Manager + +ReaderManager = Manager() +WriterManager = Manager() \ No newline at end of file diff --git a/imagepy/core/manager/pluginmanager.py b/imagepy/core/manager/pluginmanager.py index b526023f..1efa4370 100644 --- a/imagepy/core/manager/pluginmanager.py +++ b/imagepy/core/manager/pluginmanager.py @@ -3,50 +3,8 @@ Created on Sat Jan 14 23:23:30 2017 @author: yxl """ -import weakref +from sciapp import Manager -class ToolsManager: - tools = {} - curtool = None - - @classmethod - def set(cls, tool): - if tool.__class__ == cls.curtool.__class__:return - if cls.curtool!=None: cls.curtool.switch() - cls.curtool = tool - - @classmethod - def add(cls, tol):cls.tools[tol.title] = tol - - @classmethod - def get(cls, name=None): - if name==None:return cls.curtool - return cls.tools[name] - -class PluginsManager: - plgs = {} - - @classmethod - def add(cls, plg):cls.plgs[plg.title] = plg - - @classmethod - def get(cls, name):return cls.plgs[name] - -class WidgetsManager: - wgts, insts = {}, {} - - @classmethod - def add(cls, wgt): cls.wgts[wgt.title] = wgt - - @classmethod - def addref(cls, obj): - cls.insts[obj.title] = weakref.ref(obj) - - @classmethod - def get(cls, name):return cls.wgts[name] - - @classmethod - def getref(cls, name): - if not name in cls.insts: return None - if cls.insts[name] is None: return None - return cls.insts[name]() \ No newline at end of file +# PluginsManager = Manager() +# WidgetsManager = Manager() +# ToolsManager = Manager() \ No newline at end of file diff --git a/imagepy/core/manager/roimanager.py b/imagepy/core/manager/roimanager.py index 9304c42c..e93705b2 100644 --- a/imagepy/core/manager/roimanager.py +++ b/imagepy/core/manager/roimanager.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -""" -Created on Sat Jan 14 23:26:14 2017 -@author: yxl -""" from .configmanager import ConfigManager class RoiManager: diff --git a/imagepy/core/manager/shotcutmanager.py b/imagepy/core/manager/shotcutmanager.py index 441e6dbf..478178be 100644 --- a/imagepy/core/manager/shotcutmanager.py +++ b/imagepy/core/manager/shotcutmanager.py @@ -1,6 +1,9 @@ -import os +from sciapp import Manager from ... import root_dir +import os +ShotcutManager = Manager(path=os.path.join(root_dir,'data/shotcut.cfg')) +''' class ShotcutManager: shotcuts = {} filename = os.path.join(root_dir,'data/shotcut.cfg') @@ -39,4 +42,4 @@ def rm(cls, key): ShotcutManager.rm('c') print(ShotcutManager.shotcuts) ShotcutManager.write() - \ No newline at end of file +''' \ No newline at end of file diff --git a/imagepy/core/manager/windowmanager.py b/imagepy/core/manager/windowmanager.py deleted file mode 100644 index 62043dd0..00000000 --- a/imagepy/core/manager/windowmanager.py +++ /dev/null @@ -1,218 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Sat Jan 14 23:22:53 2017 -@author: yxl -""" -import weakref - -class WindowsManager: - wins = [] - - @classmethod - def add(cls, win): - if win in cls.wins: cls.remove(win) - cls.wins.insert(0, win) - - @classmethod - def get(cls, title=None): - if len(cls.wins)==0:return None - if title==None:return cls.wins[0] - titles = [i.ips.title for i in cls.wins] - if not title in titles:return None - return cls.wins[titles.index(title)] - - @classmethod - def remove(cls, win): - for i in cls.wins: - if i == win: - cls.wins.remove(i) - -class ImageManager: - imgs = [] - - @classmethod - def add(cls, ips): - print(ips) - cls.remove(ips) - callback = lambda a: cls.remove(a()) - def callback(a): - print('image removed') - cls.remove(a()) - print('image add!') - cls.imgs.insert(0, weakref.ref(ips, callback)) - - @classmethod - def remove(cls, ips): - for i in cls.imgs: - if i() == ips: cls.imgs.remove(i) - - @classmethod - def get(cls, title=None): - if len(cls.imgs)==0:return None - if title==None:return cls.imgs[0]() - titles = [i().title for i in cls.imgs] - if not title in titles:return None - return cls.imgs[titles.index(title)]() - - @classmethod - def get_titles(cls): - return [i().title for i in cls.imgs] - - @classmethod - def name(cls, name): - if name==None:name='Undefined' - titles = [i().title for i in cls.imgs] - if not name in titles : - return name - for i in range(1, 100) : - title = "{}-{}".format(name,i) - if not title in titles: - return title - -class TextLogManager: - windows = {} - - @classmethod - def name(cls, name): - if name==None:name='Log' - if name not in cls.windows: - return name - for i in range(1, 100) : - title = "{}-{}".format(name,i) - if title not in cls.windows: - return title - - @classmethod - def add(cls, title, win): - cls.windows[title] = win - print(list(cls.windows.keys())) - - @classmethod - def remove(cls, name): - if name in cls.windows: - cls.windows.pop(name) - - @classmethod - def get(cls, title): - if title in cls.windows: - return cls.windows[title] - return None - - @classmethod - def get_titles(cls): - return list(cls.windows.keys()) - - @classmethod - def close(cls, name): - win = cls.get(name) - if win==None:return - cls.remove(name) - win.Close() - -class WTableManager: - wins = [] - - @classmethod - def add(cls, win): - if not win in cls.wins:cls.wins.append(win) - - @classmethod - def get(cls, title=None): - if len(cls.wins)==0:return None - if title==None:return cls.wins[0] - titles = [i.grid.tps.title for i in cls.wins] - if not title in titles:return None - return cls.wins[titles.index(title)] - - @classmethod - def remove(cls, win): - for i in cls.wins: - if i == win: - cls.wins.remove(i) - print('remove', i.grid.tps.title) - -class TableManager: - tabs = [] - - @classmethod - def add(cls, tps): - print(tps) - cls.remove(tps) - callback = lambda a: cls.remove(a()) - def callback(a): - print('table removed') - cls.remove(a()) - print('table add!') - cls.tabs.insert(0, weakref.ref(tps, callback)) - - @classmethod - def remove(cls, tps): - for i in cls.tabs: - if i() == tps: cls.tabs.remove(i) - - @classmethod - def get(cls, title=None): - if len(cls.tabs)==0:return None - if title==None:return cls.tabs[0]() - titles = [i().title for i in cls.tabs] - if not title in titles:return None - return cls.tabs[titles.index(title)]() - - @classmethod - def get_titles(cls): - return [i().title for i in cls.tabs] - - @classmethod - def name(cls, name): - if name is None: name='Table' - titles = [i().title for i in cls.tabs] - if not name in titles : - return name - for i in range(1, 100) : - title = "{}-{}".format(name,i) - if not title in titles: - return title - -class PlotManager: - windows = [] - - @classmethod - def add(cls, win): - cls.remove(win) - callback = lambda a: cls.remove(a()) - cls.windows.insert(0, weakref.ref(win, callback)) - - @classmethod - def remove(cls, win): - for i in cls.windows: - if i() == win: cls.windows.remove(i) - - @classmethod - def get(cls, title=None): - if len(cls.windows)==0:return None - if title==None:return cls.windows[0]() - titles = [i().title for i in cls.windows] - if not title in titles:return None - return cls.windows[titles.index(title)]() - - @classmethod - def get_titles(cls): - return [i().title for i in cls.windows] - - @classmethod - def name(cls, name): - if name==None:name='Table' - titles = [i().title for i in cls.windows] - if not name in titles : - return name - for i in range(1, 100) : - title = "{}-{}".format(name,i) - if not title in titles: - return title - - @classmethod - def close(cls, name): - win = cls.get(name) - if win==None:return - cls.remove(win) - win.close() \ No newline at end of file diff --git a/imagepy/core/myvi/__init__.py b/imagepy/core/myvi/__init__.py deleted file mode 100644 index afb5a156..00000000 --- a/imagepy/core/myvi/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -''' -As Mayavi is a little outdated, and not support wxphoenix -So I wrote a simple one, and remove two a, M(a)y(a)vi.self.color = cs if isinstance(cs, tuple) else (0,0,0)self.color = cs if isinstance(cs, tuple) else (0,0,0)self.color = cs if isinstance(cs, tuple) else (0,0,0)self.color = cs if isinstance(cs, tuple) else (0,0,0)self.color = cs if isinstance(cs, tuple) else (0,0,0)self.color = cs if isinstance(cs, tuple) else (0,0,0)self.color = cs if isinstance(cs, tuple) else (0,0,0)self.color = cs if isinstance(cs, tuple) else (0,0,0)self.color = cs if isinstance(cs, tuple) else (0,0,0) -''' -from .canvas3d import * -from .frame3d import * -from .manager import * -from .util import * \ No newline at end of file diff --git a/imagepy/core/myvi/frame3d.py b/imagepy/core/myvi/frame3d.py deleted file mode 100644 index 1455b7f8..00000000 --- a/imagepy/core/myvi/frame3d.py +++ /dev/null @@ -1,43 +0,0 @@ -import wx, os -from .canvas3d import Canvas3D -from . import util -from . import canvas3d -import numpy as np - -class Frame3D(wx.Frame): - frms = {} - - def __init__(self, parent, title='Frame3D', manager=None): - wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = title, pos = wx.DefaultPosition, size = wx.Size( 800,600 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) - self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) - sizer = wx.BoxSizer( wx.VERTICAL ) - root = os.path.abspath(os.path.dirname(__file__)) - - self.SetIcon(wx.Icon(os.path.join(root, 'imgs/logo.ico'), wx.BITMAP_TYPE_ICO)) - self.viewer = canvas3d.Viewer3D( self , manager) - sizer.Add( self.viewer, 1, wx.EXPAND |wx.ALL, 0 ) - self.Bind(wx.EVT_CLOSE, self.on_closing) - - self.SetSizer( sizer ) - self.Layout() - - self.Centre( wx.BOTH ) - - @classmethod - def figure(cls, parent, title): - if not title in cls.frms: - cls.frms[title] = Frame3D(parent, title) - cls.frms[title].Show() - # wx.Yield() - return cls.frms[title] - - def on_closing(self, event): - if self.GetTitle() in Frame3D.frms: - Frame3D.frms.pop(self.GetTitle()) - event.Skip() - -if __name__ == '__main__': - app = wx.App(False) - frm = Frame3D(None, title='GLCanvas Sample') - frm.Show() - app.MainLoop() diff --git a/imagepy/core/myvi/manager.py b/imagepy/core/myvi/manager.py deleted file mode 100644 index 5779c55b..00000000 --- a/imagepy/core/myvi/manager.py +++ /dev/null @@ -1,287 +0,0 @@ -import struct -import numpy as np -import moderngl -from time import time - -from skimage.io import imread -import numpy as np -from math import sin, cos, tan, pi -import scipy.ndimage as nimg - -def look_at(eye, target, up, dtype=None): - forward = (target - eye)/np.linalg.norm(target - eye) - side = (np.cross(forward, up))/np.linalg.norm(np.cross(forward, up)) - up = (np.cross(side, forward)/np.linalg.norm(np.cross(side, forward))) - - return np.array(( - (side[0], up[0], -forward[0], 0.), - (side[1], up[1], -forward[1], 0.), - (side[2], up[2], -forward[2], 0.), - (-np.dot(side, eye), -np.dot(up, eye), np.dot(forward, eye), 1.0) - ), dtype=np.float32) - -def perspective(xmax, ymax, near, far): - left, right = -xmax, xmax - bottom, top = -ymax, ymax - - A = (right + left) / (right - left) - B = (top + bottom) / (top - bottom) - C = -(far + near) / (far - near) - D = -2. * far * near / (far - near) - E = 2. * near / (right - left) - F = 2. * near / (top - bottom) - return np.array(( - ( E, 0., 0., 0.), - ( 0., F, 0., 0.), - ( A, B, C,-1.), - ( 0., 0., D, 0.), - ), dtype=np.float32) - -def orthogonal(xmax, ymax, near, far): - rml = xmax * 2 - tmb = ymax * 2 - fmn = far - near - - A = 2. / rml - B = 2. / tmb - C = -2. / fmn - Tx = 0 - Ty = 0 - Tz = -(far + near) / fmn - - return np.array(( - ( A, 0., 0., 0.), - (0., B, 0., 0.), - (0., 0., C, 0.), - (Tx, Ty, Tz, 1.), - ), dtype=np.float32) - -class Surface: - def __init__(self, vts, ids, ns, cs=(0,0,1)): - self.vts, self.ids, self.ns, self.cs = vts, ids, ns, cs - self.box = np.vstack((vts.min(axis=0), vts.max(axis=0))) - self.mode, self.blend, self.visible = 'mesh', 1.0, True - self.color = cs if isinstance(cs, tuple) else (0,0,0) - self.width = 1 - - def on_ctx(self, ctx, prog): - self.ctx = ctx - vts, ids, ns, cs = self.vts, self.ids, self.ns, self.cs; - buf = self.buf = np.zeros((len(vts), 9), dtype=np.float32) - buf[:,0:3], buf[:,3:6], buf[:,6:9] = vts, ns, cs - self.vbo = ctx.buffer(buf.tobytes()) - ibo = ctx.buffer(ids.tobytes()) - - content = [(self.vbo, '3f 3f 3f', 'v_vert', 'v_norm', 'v_color')] - self.vao = ctx.vertex_array(prog, content, ibo) - self.prog = prog - - def set_style(self, mode=None, blend=None, color=None, visible=None): - if not mode is None: self.mode = mode - if not blend is None: self.blend=blend - if not visible is None: self.visible=visible - if not color is None: - self.buf[:,6:9] = color - self.vbo.write(self.buf.tobytes()) - self.color = color if isinstance(color, tuple) else (0,0,0) - - def draw(self, mvp, light, bright, scatter): - if not self.visible: return - self.ctx.line_width = self.width - mvp = np.dot(*mvp) - self.prog['Mvp'].write(mvp.astype(np.float32).tobytes()) - self.prog['blend'].value = self.blend - self.prog['scatter'].value = scatter - self.prog['light'].value = tuple(light) - self.prog['bright'].value = bright - self.vao.render({'mesh':moderngl.TRIANGLES, 'grid':moderngl.LINES}[self.mode]) - -class MarkText: - def __init__(self, vts, ids, os, h, color): - self.vts, self.ids, self.color, self.os, self.h = vts, ids, color, os, h - self.blend, self.box, self.visible, self.mode = 1, None, True, 'grid' - - def on_ctx(self, ctx, prog): - self.ctx = ctx - vts, ids, os = self.vts, self.ids, self.os - buf = self.buf = np.zeros((len(vts), 6), dtype=np.float32) - buf[:,0:3], buf[:,3:6] = vts, os - self.vbo = ctx.buffer(buf.tobytes()) - ibo = ctx.buffer(ids.tobytes()) - content = [(self.vbo, '3f 3f', 'v_vert', 'v_pos')] - self.vao = ctx.vertex_array(prog, content, ibo) - self.prog = prog - - def set_style(self, mode=None, blend=None, color=None, visible=None): - if not visible is None: self.visible = visible - if not color is None: self.color = color - - def draw(self, mvp, light, bright, scatter): - if not self.visible: return - self.ctx.line_width = 2 - self.prog['mv'].write(mvp[0].astype(np.float32).tobytes()) - self.prog['proj'].write(mvp[1].astype(np.float32).tobytes()) - self.prog['f_color'].write(np.array(self.color).astype(np.float32).tobytes()) - self.prog['h'].value = self.h - self.vao.render(moderngl.LINES) - -class Manager: - def __init__(self): - self.h, self.v, self.r = 1.5, 0, 300 - self.ratio, self.dial = 1.0, 1.0 - self.pers, self.center = True, (0,0,0) - self.background = 0.4, 0.4, 0.4 - self.light = (1,0,0) - self.bright, self.scatter = 0.66, 0.66 - self.objs = {} - self.ctx = None - - def on_ctx(self): - self.ctx = moderngl.create_context() - self.prog_suf = self.ctx.program( - vertex_shader=''' - #version 330 - uniform mat4 Mvp; - in vec3 v_vert; - in vec3 v_norm; - in vec3 v_color; - out vec3 f_norm; - out vec3 f_color; - void main() { - gl_Position = Mvp * vec4(v_vert, 1); - f_norm = v_norm; - f_color = v_color; - } - ''', - fragment_shader=''' - #version 330 - uniform vec3 light; - uniform float blend; - uniform float scatter; - uniform float bright; - in vec3 f_norm; - in vec3 f_color; - out vec4 color; - void main() { - float d = clamp(dot(light, f_norm)*bright+scatter, 0, 1); - color = vec4(f_color*d, blend); - } - ''' - ) - - self.prog_txt = self.ctx.program( - vertex_shader=''' - #version 330 - uniform mat4 mv; - uniform mat4 proj; - uniform float h; - in vec3 v_vert; - in vec3 v_pos; - void main() { - vec4 o = mv * vec4(v_pos, 1); - gl_Position = proj *(o + vec4(v_vert.x*h, v_vert.y*h, v_vert.z, 0)); - } - ''', - fragment_shader=''' - #version 330 - uniform vec3 f_color; - out vec4 color; - void main() { - color = vec4(f_color, 1); - } - ''') - - for i in self.objs.values(): - if isinstance(i, Surface): i.on_ctx(self.ctx, self.prog_suf) - if isinstance(i, MarkText): i.on_ctx(self.ctx, self.prog_txt) - - def add_surf(self, name, vts, ids, ns=None, cs=(0,0,1), real=True): - surf = Surface(vts, ids, ns, cs) - if not real: surf.box = None - if not self.ctx is None: - surf.on_ctx(self.ctx, self.prog_suf) - self.objs[name] = surf - self.count_box() - return surf - - def add_mark(self, name, vts, ids, o, h, cs=(0,0,1)): - mark = MarkText(vts, ids, o, h, cs) - if not self.ctx is None: - mark.on_ctx(self.ctx, self.prog_txt) - self.objs[name] = mark - return mark - - - def get_obj(self, key): - if not key in self.objs: return None - return self.objs[key] - - def draw(self): - self.ctx.clear(*self.background) - self.ctx.enable(moderngl.DEPTH_TEST) - #self.ctx.enable(ModernGL.CULL_FACE) - self.ctx.enable(moderngl.BLEND) - for i in self.objs.values(): - i.draw(self.mvp, self.light, self.bright, self.scatter) - - def count_box(self): - minb = np.array([i.box[0] for i in self.objs.values() if not i.box is None]).min(axis=0) - maxb = np.array([i.box[1] for i in self.objs.values() if not i.box is None]).max(axis=0) - self.box = np.vstack((minb, maxb)) - #print(self.box) - self.center = self.box.mean(axis=0) - self.dial = np.linalg.norm(self.box[1]-self.box[0]) - - def count_mvp(self): - #print('mvp') - ymax = (1.0 if self.pers else self.l) * np.tan(self.fovy * np.pi / 360.0) - xmax = ymax * self.ratio - proj = (perspective if self.pers else orthogonal)(xmax, ymax, 1.0, 100000) - lookat = look_at(self.eye, self.center, (0.0,0.0,1.0)) - self.mvp = (lookat, proj) - - def set_viewport(self, x, y, width, height): - self.ctx.viewport = (x, y, width, height) - self.ratio = width*1.0/height - - def set_background(self, rgb): self.background = rgb - - def set_light(self, light): self.light = light - - def set_bright_scatter(self, bright=None, scatter=None): - if not bright is None: self.bright = bright - if not scatter is None: self.scatter = scatter - - def reset(self, fovy=45, angx=0, angy=0): - self.fovy, self.angx, self.angy = fovy, angx, angy - self.l = self.dial/2/(tan(fovy*pi/360)) - v = np.array([cos(angy)*cos(angx), cos(angy)*sin(angx), sin(angy)]) - self.eye = self.center + v*self.l*1 - self.count_mvp() - #print('reset', self.eye, self.center) - - def set_pers(self, fovy=None, angx=None, angy=None, l=None, pers=None): - if not pers is None: self.pers = pers - if not fovy is None: self.fovy = fovy - if not angx is None: self.angx = angx - if not angy is None: self.angy = angy - self.angx %= 2*pi - self.angy = max(min(pi/2-1e-4, self.angy), -pi/2+1e-4) - if not l is None: self.l = l - v = np.array([cos(self.angy)*cos(self.angx), - cos(self.angy)*sin(self.angx), sin(self.angy)]) - - self.eye = self.center + v*self.l*1 - self.count_mvp() - - def show(self, title='Myvi'): - import wx - from .frame3d import Frame3D - app = wx.App(False) - self.locale = wx.Locale(wx.LANGUAGE_ENGLISH) - Frame3D(None, title, self).Show() - app.MainLoop() - -if __name__ == '__main__': - img = imread('gis.png') - build_surf2d(img) diff --git a/imagepy/core/myvi/test.py b/imagepy/core/myvi/test.py deleted file mode 100644 index 61a96b94..00000000 --- a/imagepy/core/myvi/test.py +++ /dev/null @@ -1,176 +0,0 @@ -import sys, wx -from scipy.misc import imread -import scipy.ndimage as ndimg -sys.path.append('..') -import numpy as np -from glob import glob -import myvi - -def dem(): - img = imread('data/dem.jpg') - vts, fs, ns, cs = myvi.util.build_surf2d(img, ds=1, k=0.3, sigma=2) - - manager = myvi.Manager() - manager.add_surf('dem', vts, fs, ns, cs) - manager.show('DEM Demo') - -def volume(): - fs = glob('data/vessel*.png') - imgs = np.array([imread(i, True) for i in fs]) - print() - imgs = ndimg.gaussian_filter(imgs, 1) - vts, fs, ns, vs = myvi.util.build_surf3d(imgs, 1, 80) - - manager = myvi.Manager() - manager.add_surf('vessel', vts, fs, ns, (1,0,0)) - manager.show('Vessel Demo') - -def ball(): - vts, fs, ns, cs = myvi.build_ball((100,100,100),50, (1,0,0)) - manager = myvi.Manager() - manager.add_surf('balls', vts, fs, ns, cs) - manager.show('Ball Demo') - -def random_balls(): - os = np.random.rand(30).reshape((-1,3)) - rs = np.random.rand(10)/5 - cs = (np.random.rand(10)*255).astype(np.uint8) - cs = myvi.linear_color('jet')[cs]/255 - - vts, fs, ns, cs = myvi.build_balls(os, rs, cs) - manager = myvi.Manager() - manager.add_surf('balls', vts, fs, ns, cs) - manager.show('Random Balls Demo') - -def line(): - vts = np.array([(0,0,0),(1,1,0),(2,1,0),(1,0,0)], dtype=np.float32) - fs = np.array([(0,1,2),(1,2,3)], dtype=np.uint32) - ns = np.ones((4,3), dtype=np.float32) - - n_mer, n_long = 6, 11 - pi = np.pi - dphi = pi / 1000.0 - phi = np.arange(0.0, 2 * pi + 0.5 * dphi, dphi) - mu = phi * n_mer - x = np.cos(mu) * (1 + np.cos(n_long * mu / n_mer) * 0.5) - y = np.sin(mu) * (1 + np.cos(n_long * mu / n_mer) * 0.5) - z = np.sin(n_long * mu / n_mer) * 0.5 - - vts, fs, ns, cs = myvi.build_line(x, y, z, (1, 0, 0)) - cs[:] = myvi.auto_lookup(vts[:,2], myvi.linear_color('jet'))/255 - - manager = myvi.Manager() - obj = manager.add_surf('line', vts, fs, ns, cs) - obj.set_style(mode='grid') - manager.show('Line Rings') - -def mesh(): - dphi, dtheta = np.pi/80.0, np.pi/80.0 - [phi,theta] = np.mgrid[0:np.pi+dphi*1.5:dphi,0:2*np.pi+dtheta*1.5:dtheta] - m0 = 4; m1 = 3; m2 = 2; m3 = 3; m4 = 6; m5 = 2; m6 = 6; m7 = 4; - r = np.sin(m0*phi)**m1 + np.cos(m2*phi)**m3 + np.sin(m4*theta)**m5 + np.cos(m6*theta)**m7 - x = r*np.sin(phi)*np.cos(theta) - y = r*np.cos(phi) - z = r*np.sin(phi)*np.sin(theta) - vts, fs, ns, cs = myvi.build_mesh(x, y, z) - cs[:] = myvi.util.auto_lookup(vts[:,2], myvi.util.linear_color('jet'))/255 - - manager = myvi.Manager() - obj = manager.add_surf('mesh', vts, fs, ns, cs) - obj.set_style(mode='grid') - manager.show('Mesh Demo') - -def ball_ring_box(): - os = np.random.rand(30).reshape((-1,3)) - rs = np.random.rand(10)/7 - cs = (np.random.rand(10)*255).astype(np.uint8) - cs = myvi.linear_color('jet')[cs]/255 - - vts_b, fs_b, ns_b, cs_b = myvi.build_balls(list(os), list(rs), list(cs)) - vts_l, fs_l, ns_l, cs_l = myvi.build_line(os[:,0], os[:,1], os[:,2], list(cs)) - vts_c, fs_c, ns_c, cs_c = myvi.build_cube((0,0,0), (1,1,1)) - manager = myvi.Manager() - manager.add_surf('balls', vts_b, fs_b, ns_b, cs_b) - line = manager.add_surf('line', vts_l, fs_l, ns_l, cs_l) - line.set_style(mode='grid') - box = manager.add_surf('box', vts_c, fs_c, ns_c, cs_c) - box.set_style(mode='grid') - manager.show('Balls Ring Demo') - -def balls_with_mark(): - os = np.random.rand(30).reshape((-1,3)) - rs = np.random.rand(10)/7 - cs = (np.random.rand(10)*255).astype(np.uint8) - cs = myvi.linear_color('jet')[cs]/255 - - vts_b, fs_b, ns_b, cs_b = myvi.build_balls(os, rs, cs) - cont = ['ID:%s'%i for i in range(10)] - vtss, fss, pps, h, color = myvi.build_marks(cont, os, rs, 0.05, (1,1,1)) - manager = myvi.Manager() - manager.add_surf('balls', vts_b, fs_b, ns_b, cs_b) - line = manager.add_mark('line', vtss, fss, pps, h, color) - line.set_style(mode='grid') - manager.show('Balls Mark Demo') - -def frame_demo(): - app = wx.App(False) - frm = myvi.Frame3D(None, 'Frame') - img = imread('data/dem.jpg') - vts, fs, ns, cs = myvi.util.build_surf2d(img, ds=1, k=0.3, sigma=2) - frm.viewer.add_surf_ansy('dem', vts, fs, ns, cs) - frm.Show() - app.MainLoop() - -def surface2d(): - x, y = np.ogrid[-2:2:20j, -2:2:20j] - z = x * np.exp( - x**2 - y**2) - vts, fs, ns, cs = myvi.util.build_surf2d(z, ds=1, k=20, sigma=2) - cs[:] = myvi.util.auto_lookup(vts[:,2], myvi.util.linear_color('jet'))/255 - manager = myvi.Manager() - manager.add_surf('dem', vts, fs, ns, cs) - manager.show('DEM Demo') - -def arrow(): - v1, v2 = np.array([[[0,0,0],[5,5,5]],[[0,15,5],[2,8,3]]], dtype=np.float32) - vts, fs, ns, c = myvi.util.build_arrows(v1, v2, 1, 1, 1, 1, (1,0,0)) - manager = myvi.Manager() - manager.add_surf('dem', vts, fs, ns, c) - manager.show('DEM Demo') - -def cube(): - vts, fs, ns, cs = myvi.build_cube((0,0,0), (1,1,1)) - manager = myvi.Manager() - obj = manager.add_surf('cube', vts, fs, ns, cs) - obj.set_style(mode='grid') - manager.show('Cube Demo') - -def cube_surf(): - from skimage.data import camera - lut = np.zeros((256,3), dtype=np.uint8) - lut[:,0] = np.arange(256) - imgs = np.array([camera()[:300,::]]*256) - vts, fs, ns, cs = myvi.build_img_cube(imgs) - manager = myvi.Manager() - obj = manager.add_surf('cube', vts, fs, ns, cs) - vts, fs, ns, cs = myvi.build_img_box(imgs) - obj = manager.add_surf('box', vts, fs, ns, cs) - obj.set_style(mode='grid') - manager.show('Cube Demo') - - -if __name__ == '__main__': - - cube_surf() - ''' - volume() - surface2d() - dem() - volume() - ball() - random_balls() - line() - mesh() - ball_ring_box() - balls_with_mark() - arrow() - ''' \ No newline at end of file diff --git a/imagepy/core/myvi/txtmark.py b/imagepy/core/myvi/txtmark.py deleted file mode 100644 index f4ace5a7..00000000 --- a/imagepy/core/myvi/txtmark.py +++ /dev/null @@ -1,13 +0,0 @@ -lib = {'0':([(0,0.5,0.5,0,0)],[(1,1,0,0,1)],0.5), - '1':([(0.25,0.25)], [(0,1)], 0.5), - '2':([(0,0.5,0.5,0,0,0.5)], [(1,1,0.5,0.5,0,0)], 0.5), - '3':([(0,0.5,0.5,0),(0,0.5)],[(1,1,0,0),(0.5,0.5)], 0.5), - '4':([(0,0,0.5),(0.5,0.5)],[(1,0.5,0.5),(1,0)],0.5), - '5':([(0.5,0,0,0.5,0.5,0)], [(1,1,0.5,0.5,0,0)], 0.5), - '6':([(0.5,0,0,0.5,0.5,0,0)], [(1,1,0.5,0.5,0,0,0.5)], 0.5), - '7':([(0,0.5,0.5)], [(1,1,0)], 0.5), - '8':([(0.5,0.5,0,0,0.5,0.5,0,0)], [(0.5,1,1,0.5,0.5,0,0,0.5)], 0.5), - '9':([(0.5,0.5,0,0,0.5,0.5,0)], [(0.5,1,1,0.5,0.5,0,0)], 0.5), - 'I':([(0,0.5),(0.25,0.25),(0,0.5)],[(1,1),(1,0),(0,0)],0.5), - 'D':([(0,0.25,0.4,0.5,0.5,0.4,0.25,0),(0.1,0.1)],[(1,1,0.9,0.75,0.25,0.1,0,0),(0,1)],0.5), - ':':([(0.2,0.3),(0.2,0.3)],[(0.75,0.75),(0.25,0.25)],0.5)} \ No newline at end of file diff --git a/imagepy/core/roi/lineroi.py b/imagepy/core/roi/lineroi.py index 184e9c8a..2adfbbf6 100644 --- a/imagepy/core/roi/lineroi.py +++ b/imagepy/core/roi/lineroi.py @@ -9,7 +9,6 @@ from ..draw import paint from .roi import ROI from ..manager import RoiManager -from imagepy import IPy class LineRoi(ROI): dtype = 'line' diff --git a/imagepy/core/roi/ovalroi.py b/imagepy/core/roi/ovalroi.py index 3ddd1c69..754071f3 100644 --- a/imagepy/core/roi/ovalroi.py +++ b/imagepy/core/roi/ovalroi.py @@ -9,7 +9,6 @@ from .roi import ROI from .polygonroi import PolygonRoi from ..manager import RoiManager -from imagepy import IPy class OvalRoi(ROI): dtype = 'rect' diff --git a/imagepy/core/roi/pointroi.py b/imagepy/core/roi/pointroi.py index 57f953e8..91e31bd6 100644 --- a/imagepy/core/roi/pointroi.py +++ b/imagepy/core/roi/pointroi.py @@ -7,7 +7,6 @@ from ..draw import paint from .roi import ROI from ..manager import RoiManager -from imagepy import IPy import numpy as np class PointRoi(ROI): diff --git a/imagepy/core/roi/polygonroi.py b/imagepy/core/roi/polygonroi.py index 5097ae25..7aeaa472 100644 --- a/imagepy/core/roi/polygonroi.py +++ b/imagepy/core/roi/polygonroi.py @@ -10,7 +10,6 @@ from ..draw import paint from .roi import ROI from ..manager import RoiManager -from imagepy import IPy def parse_poly(geom): out = list(geom.exterior.coords) diff --git a/imagepy/core/roi/rectangleroi.py b/imagepy/core/roi/rectangleroi.py index 3356b802..53164742 100644 --- a/imagepy/core/roi/rectangleroi.py +++ b/imagepy/core/roi/rectangleroi.py @@ -8,7 +8,6 @@ from .polygonroi import PolygonRoi from .roi import ROI from ..manager import RoiManager -from imagepy import IPy class RectangleRoi(ROI): dtype = 'rect' diff --git a/imagepy/core/util/fileio.py b/imagepy/core/util/fileio.py index e87ad4dd..45f23b58 100644 --- a/imagepy/core/util/fileio.py +++ b/imagepy/core/util/fileio.py @@ -1,7 +1,7 @@ import os -from ..manager import ViewerManager, ConfigManager +from ..manager import ConfigManager from ..manager import ReaderManager, WriterManager -from ... import IPy, root_dir +from ... import root_dir from ..engine import Free, Simple, Macros import numpy as np @@ -14,8 +14,7 @@ def show_img(img, title): img = img[:,:,:3].copy() IPy.show_img([img], title) -ViewerManager.add('img', show_img) -ViewerManager.add('imgs', IPy.show_img) +# ViewerManager.add('imgs', IPy.show_img) recent = ConfigManager.get('recent') if recent==None : recent = [] @@ -38,14 +37,15 @@ def add_recent(path): rlist.pop(-1) ConfigManager.set('recent', recent) - IPy.curapp.reload_plugins() + #IPy.curapp.reload_plugins() class Reader(Free): para = {'path':''} + tag, note = None, None def show(self): - filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in self.filt]) - return IPy.getpath('Open..', filt, 'open', self.para) + self.para['path'] = self.app.getpath('Open..', self.filt, 'open', '') + return not self.para['path'] is None #process def run(self, para = None): @@ -53,32 +53,32 @@ def run(self, para = None): fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) - read = ReaderManager.get(fe[1:], None) - if read is None: + reader = ReaderManager.gets(name=fe[1:], tag=self.tag, note=self.note) + print(fe, self.tag, self.note, reader) + ''' + if len(reader) == 0: a, b = os.path.splitext(fn) fn, fe = a, b+fe - read = ReaderManager.get(fe[1:], None) - if read is None: - return IPy.alert('No reader found for %s'%fe[1:]) - view = ViewerManager.get(fe[1:]) - - #group, read = (True, read[0]) if isinstance(read, tuple) else (False, read) - obj = read(para['path']) - # if not group: img = [img] - view(obj, fn) + reader = ReaderManager.gets(name=fe[1:], tag=self.tag, note=self.note) + if len(reader) is None: + return self.app.alert('No reader found for %s'%fe[1:]) + # ext, read, tag, note = reader + ''' + self.app.show(self.tag, reader[0](para['path'])) class Writer(Simple): note = ['all'] - para={'path':root_dir} + para={'path':''} def show(self): - filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in self.filt]) - return IPy.getpath('Save..', filt, 'save', self.para) + self.para['path'] = self.app.getpath('Save..', self.filt, 'save', '') + return not self.para['path'] is None #process def run(self, ips, imgs, para = None): fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) - write = WriterManager.get(fe[1:], 'img') - write2 = write or WriterManager.get(fe[1:], 'imgs') - write2(para['path'], imgs if write is None else ips.img) \ No newline at end of file + writer = WriterManager.get(ext=fe[1:], tag='img') + if len(writer)==1: return writer[0][1](para['path'], ips.img) + writer = WriterManager.get(fe[1:], 'imgs') + if len(writer)==1: return writer[0][1](para['path'], imgs) \ No newline at end of file diff --git a/imagepy/core/util/tableio.py b/imagepy/core/util/tableio.py index 92012115..11d7f013 100644 --- a/imagepy/core/util/tableio.py +++ b/imagepy/core/util/tableio.py @@ -1,6 +1,6 @@ import os -from ..manager import ViewerManager, ReaderManager, WriterManager -from ... import IPy, root_dir +from ..manager import ReaderManager, WriterManager +from ... import root_dir from ..engine import Free, Table, Macros import numpy as np diff --git a/imagepy/core/util/testdata.py b/imagepy/core/util/testdata.py index b2d70365..975fc92a 100644 --- a/imagepy/core/util/testdata.py +++ b/imagepy/core/util/testdata.py @@ -1,6 +1,6 @@ -from imagepy import IPy +#from imagepy import IPy from imagepy.core.engine import Free -from imagepy.core.manager import ReaderManager, WriterManager, ViewerManager +from imagepy.core.manager import ReaderManager, WriterManager from skimage.io import imread import os.path as osp, os import numpy as np diff --git a/imagepy/core/wraper/imageplus.py b/imagepy/core/wraper/imageplus.py index 82724396..c55cea30 100644 --- a/imagepy/core/wraper/imageplus.py +++ b/imagepy/core/wraper/imageplus.py @@ -1,6 +1,6 @@ import numpy as np from time import time -from ..manager import ImageManager, ColorManager +from sciapp.object import Image def get_img_type(imgs): if imgs[0].ndim==3 and imgs[0].dtype==np.uint8:return 'rgb' @@ -12,35 +12,7 @@ def get_img_type(imgs): if imgs[0].dtype == np.complex128:return '128-complex' if imgs[0].dtype == np.complex64:return '64-complex' -def get_updown(imgs, slices='all', chans='all', step=1): - c = chans if isinstance(chans, int) else slice(None) - if isinstance(slices, int): imgs = [imgs[slices]] - if step<=1: step = int(1/step+0.5) - else: step = int(min(imgs[0].shape[:2])/step+0.5) - s = slice(None, None, max(step,1)) - s = (s,s,c)[:imgs[0].ndim] - mins = [i[s].min(axis=(0,1)) for i in imgs] - maxs = [i[s].max(axis=(0,1)) for i in imgs] - mins = np.array(mins).reshape((len(mins),-1)) - maxs = np.array(maxs).reshape((len(maxs),-1)) - mins, maxs = mins.min(axis=0), maxs.max(axis=0) - if np.iscomplexobj(mins): - mins, maxs = np.zeros(mins.shape), np.abs(maxs) - if chans!='all': return mins.min(), maxs.max() - return [(i,j) for i,j in zip(mins, maxs)] - -def histogram(imgs, rg=(0,256), slices='all', chans='all', step=1): - c = chans if isinstance(chans, int) else slice(None) - if isinstance(slices, int): imgs = [imgs[slices]] - if step<=1: step = int(1/step+0.5) - else: step = int(min(imgs[0].shape[:2])/step+0.5) - s = slice(None, None, max(step,1)) - s = (s,s,c)[:imgs[0].ndim] - rg = np.linspace(rg[0], rg[1], 257) - hist = [np.histogram(i[s], rg)[0] for i in imgs] - return np.sum(hist, axis=0) - -class ImagePlus: +class ImagePlus(Image): """ImagePlus: a class to make operation more flexible """ def __init__(self, imgs, title=None, is3d=False): self.set_title(title) @@ -64,64 +36,9 @@ def __init__(self, imgs, title=None, is3d=False): self.chan_mode = 'min' self.set_imgs(imgs) - def update(self): self.dirty = True - - def set_title(self, title): - self.title = ImageManager.name(title) - - def set_imgs(self, imgs): - self.is3d = not isinstance(imgs, list) - self.scrchanged = True - self.snap = None - self.imgs = imgs - - self.size = self.imgs[0].shape[:2] - self.height, self.width = self.size - self.imgtype = get_img_type(self.imgs) - if self.imgs[0].ndim==2: self.channels = 1 - else: self.channels = self.imgs[0].shape[2] - self.dtype = self.imgs[0].dtype - if self.dtype == np.uint8: self.range = (0, 255) - else: self.range = self.get_updown('all', 'one', step=512) - if self.dtype == np.uint8: - self.chan_range = [(0, 255)] * self.channels - else: self.chan_range = self.get_updown('all', 'all', step=512) - self.chan = (0, [0,1,2])[self.channels==3] - - def get_updown(self, slices='all', chans='one', step=1): - if slices is None: slices = self.cur - if chans is None: chans = self.chan - return get_updown(self.imgs, slices, chans, step) - - def histogram(self, rg=None, slices=None, chans=None, step=1): - if slices is None: slices = self.cur - if chans is None: chans = self.chan - if rg is None: rg = self.range - return histogram(self.imgs, rg, slices, chans, step) - - def get_imgtype(self):return self.imgtype - - def get_nslices(self):return len(self.imgs) - - def get_nchannels(self):return self.channels - - def set_cur(self, n): - if n>=0 and n>> delete ips') diff --git a/imagepy/core/wraper/tableplus.py b/imagepy/core/wraper/tableplus.py index 3e997e8a..97a91d7d 100644 --- a/imagepy/core/wraper/tableplus.py +++ b/imagepy/core/wraper/tableplus.py @@ -1,6 +1,5 @@ import numpy as np import pandas as pd -from ..manager import TableManager class TablePlus(): def __init__(self, data, title=None): diff --git a/imagepy/data/config.json b/imagepy/data/config.json new file mode 100644 index 00000000..40f51520 --- /dev/null +++ b/imagepy/data/config.json @@ -0,0 +1 @@ +[["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null]] \ No newline at end of file diff --git a/imagepy/data/language/Chinese.dic b/imagepy/data/language/Chinese.dic deleted file mode 100644 index 3e3965cc..00000000 --- a/imagepy/data/language/Chinese.dic +++ /dev/null @@ -1,497 +0,0 @@ -16-bit uint:转16位 -16_Colors:-- -2D Surface:表面积 -2DSurface Demo:二维表面重建演示 -32-bit float:转32位浮点 -32-bit int:转32位整数 -3D Image Cube:三维立方体 -3D Surface:三维表面重建 -5_ramps:-- -64-bit float:转64位浮点 -6_Shades:-- -8-bit:转8位无符号 -About:关于 ImagePy -Active Ridge:交互式山脊线 -AdaBoost Classify:AdaBoost分类 -Adaptive Threshold:自适应阈值 -Add:加 -Add Column:添加列 -Add Slice:添加切片 -Adjust:调整 -Analysis:分析 -Analysis 3D:三维分析 -Append Rows:添加行 -Area Chart:面积图表 -AuPbSn 40 56K:-- -Author:-- -Auto Threshold:自动阈值 -BMP:-- -BMP Open:打开BMP -BMP Save:关闭BMP -BRGBCMYW:-- -Baboon 56K:-- -Background Self:设置自身为背景 -Bagging Classify:Bagging分类器 -Bar Chart:条形图 -Basic Operator:基础操作 -Binary:二值 -Binary 3D:三维二值 -Binary Closeing:形态学闭运算 -Binary ConvexHull:形态学凸包 -Binary Dilation:形态学膨胀 -Binary Erosion:形态学腐蚀 -Binary Opening:形态学开运算 -Binary Outline:形态学轮廓 -Binary Voronoi:平面瓜分 -Binary Watershed:二值分水岭 -Binary Watershed 3D:三维二值分水岭 -Blob 356K:-- -Blue:-- -Boats 25K:-- -Bound Snake Fit:边界曲线逼近 -Box Chart:箱状图 -Bridge 174K:-- -Bright And Constract:亮度对比度 -Build Graph:创建拓扑结构 -Build Graph 3D:创建三维拓扑结构 -Build Mark Image:创建标记图像 -CIERGB To RGB:CIERGB转RGB -CSV Open:打开CSV -CSV Save:保存CSV -Calendar:日历 -Canny:Canny边缘算子 -Canvas Size:画布大小 -Cartwheel Galaxy 231K:-- -Cell Colony 34K:-- -Chart:图表 -Chinese:-- -Classify:特征分类 -Clean Dictionary:清除字典 -Clear:清除内部 -Clear Mark:清除覆盖物 -Clear Out:清除外部 -Closing 3D:三维形态学闭运算 -Clown 14K:-- -Color:颜色 -Color Balance:色彩平衡 -Color Cluster:色彩聚类 -Color Cluster 3D:三维色彩聚类 -Color Points Statistic:彩色点空间统计 -Color Stairs:彩色色阶 -Command Line:命令行工具 -Contribute:贡献 -Contribute Document:贡献文档 -Contribute Plugin:贡献插件 -Contributions:贡献者 -Copy:复制 -Crop:裁剪 -Cross Stick:十字绣 -Curve Adjust:曲线调整 -Cut Branch:修剪分支 -Cut By ROI:用ROI修剪分支 -Cyan:-- -Cyan_Hot:-- -DAT:-- -DAT Open:打开DAT -DAT Save:保存DAT -DCM Open:打开DICOM -DEM:-- -DICOM:-- -DOG:差分高斯滤波 -Decoration Demo:装饰品 -Delete Columns:删除列 -Delete Rows:删除行 -Delete Slice:删除切片 -Demo Repo:-- -Detect Black Point:-- -Develop Tool Sute:开发者工具 -Diatoms 60K:-- -Dilation 3D:三维二值膨胀 -Distance 3D:三维距离变换 -Distance Transform:距离变换 -Duplicate:副本 -Edit:编辑 -Elegant ImagePy Style:ImagePy风格 -English:-- -Erosion 3D:三维二值腐蚀 -Errors:-- -Evolving Level Set:边框收敛水平集 -Excel Open:打开Excel -Excel Save:保存Excel -Exit:退出 -Export:导出 -ExtraTrees Classify:ExtraTrees分类 -FFT:快速傅里叶变换 -FRC:-- -Feature Classify Panel:特征分类面板 -Feature Predictor:特征预测 -Features:特征 -Features 3D:三维特征 -Felzenszwalb:-- -Felzenszwalb Label:-- -File:文件 -Fill:填充 -Fill Holes:二值空隙填充 -Fill Holes 3D:三维空隙填充 -Filters:滤波器 -Filters 3D:三维滤波器 -Find IsoLine:查找等值线 -Find Maximum:查找局部最大值 -Find Minimum:查找局部最小值 -Find Riedge:查找山脊线 -Find Watershed:查找分水岭 -FluidSurface:-- -FluorescentCells 400K:-- -Fragment Repair:碎片修复 -Frangi:Frangi山脊线提取 -Frangi 3D:三维Frangi山脊线提取 -Frequence:频率统计 -Frequence 3D:三维频率统计 -GIF:-- -GIF Animate Open:打开GIF动画 -GIF Animate Save:保存GIF动画 -GIF Open:打开GIF -GIF Save:保存GIF -Game Of Life:生命游戏 -Games:游戏 -Gamma:Gamma矫正 -Gaussian:高斯模糊 -Gaussian 3D:三维高斯模糊 -Gaussian Laplace:高斯-拉普拉斯滤波 -Gaussian Random:高斯随机数 -Geometry Analysis:几何分析 -Geometry Analysis 3D:三维几何分析 -Geometry Filter:几何过滤器 -Geometry Filter 3D:三维几何过滤器 -Glow:-- -Gradient Boosting Classify:Gradient Boosting分类器 -Graph Cut Branch 3D:三维分支修剪 -Graph Shortest Path:最短路径 -Graph Statistic:拓扑统计 -Graph Statistic 3D:三维拓扑统计 -Graph Summarise:节点路径汇总 -Graph Summarise 3D:三维节点路径汇总 -Gray Cluster:灰度聚类 -Gray Cluster 3D:三维灰度聚类 -Gray Points Statistic:灰度点统计 -Gray Stairs:灰度色戒 -Grays:-- -Green:-- -Green_Fire_Blue:-- -Group Statistic:分组统计 -HSV To RGB:HSV转RGB -Harris:Harris角点检测 -Help:帮助 -Hessian:海森特征 -Hessian 3D:三维海森特征 -HiLo:-- -Hist Chart:频率直方图 -Histogram:直方图 -Histogram Match:直方图匹配 -Histogram Normalize:直方图标准化 -Home Page:主页 -Hydrology:水文算法 -Hysteresis Threshold:双阈值 -IBook:案例素材 -ICA:-- -ICA2:-- -ICA3:-- -Image:图像 -Image Calculator:图像运算 -Import:导入 -Import Rois from IJ:导入ImageJ的ROI文件 -Import Sequence:导入序列 -Install:安装 -Install Packages:安装python包 -Install Plugins:安装ImagePy插件 -Intensity Analysis:密度分析 -Intensity Filter:密度过滤 -Inverse FFT:快速傅里叶逆变换 -Invert:反向 -JPG:-- -JPG Open:打开JPG -JPG Save:保存JPG -Jet:-- -K-Means:K均值聚类 -Kill Image:关闭图像 -Kill TableLog:关闭表格 -Kill TextLog:关闭日志 -Kit3D:三维 -Kitchen Rosenfeld:-- -LUV To RGB:LUV转RGB -Lab To RGB:Lab转RGB -Label Image:图像标记 -Label Tool:标记工具 -Language:语言 -Laplace:拉普拉斯滤波 -Laplace Sharp:拉普拉斯瑞华 -Leaf 36K:-- -Lena:-- -Lena 68K:-- -Lines Demo:线条示例 -List Packages:列举已安装的包 -List Plugins:列举已安装的插件 -Log Power:对数功率谱 -Logo:-- -Lookup table:色彩映射表 -Lymp 17K:-- -M51 177K:-- -MAT:-- -MRI Head 47K:-- -Macros:宏 -Macros Recorder:宏录制 -Magenta:-- -Magenta_Hot:-- -Manager:管理 -Mark:覆盖物 -Mark Setting:覆盖物设定 -Mat 3D Open:打开三维Mat文件 -Mat 3D Save:保存三维Mat文件 -Mat Open:打开Mat -Mat Save:保存Mat -Math:数学 -Max:最大值 -Maximum:最大值滤波器 -Measure Surface And Volume:测量表面积体积 -Medial Axis:中轴线 -Median:中值滤波器 -Meijering:Meijering山脊线提取 -Meijering 3D:Meijering三维山脊线提取 -Merge RGB Channels:融合RGB通道 -Microm 32K:-- -Min:最小值 -Minimum:最小值滤波器 -Moravec:Moravec角点检测 -Morphological Snake Fit:形态学曲线逼近 -Multiply:乘 -Network 3D:三维拓扑网络 -New:新建 -New Filter:新建滤镜 -New Free:新建自由插件 -New Language:新建语言 -New Simple:新建基础插件 -New Tool:新建工具 -Next Slice:下一切片 -Niblack Threshold:Niblack阈值 -NileBend 1.9M:-- -Numpy:-- -Numpy 3D Open:打开三维Numpy -Numpy 3D Save:--保存三维Numpy -Numpy Open:打开Numpy -Numpy Save:保存Numpy -Open:打开 -Open Mark:打开覆盖物 -Open Raw:打开二进制文件 -Open Recent:最近打开 -Open Url:打开URL -OpenCV:OpenCV -Opening 3D:三维形态学膨胀 -Orange_Hot:-- -Orthogonal view:三视图 -Others:-- -PNG:-- -PNG Open:打开PNG -PNG Save:保存PNG -Paste:粘贴 -Pay Tribute To ImageJ:致敬ImageJ -Percent:百分比滤波 -Pie Chart:饼状图 -Pink:-- -Pixel Cluster:像素聚类 -Pixel Statistic:像素统计 -Pixel Statistic 3D:三维像素统计 -Plot Chart:折线图 -Plugin List View:插件列表视图 -Plugin Tree View:插件树形视图 -Plugins:插件 -Plugins Manager:插件管理器 -Points Value:点的像素值 -Previous Slice:前一切片 -Prewitt:Previtt梯度算子 -Process:处理 -Property Marker:属性标记 -Quick Shift:快速Shift超像素 -Quickshift Label:快速Shift超像素标记 -RAG Cut Normalized:RAG图标准化 -RAG Merge Hierarchical:RAG图层级融合 -RAG Threshold:RAG阈值 -RGB:转RGB彩色 -RGB Points Cloud:RGB点云 -RGB To CIERGB:RGB转CIERGB -RGB To Gray:RGB转灰度 -RGB To HSV:RGB转HSV -RGB To LUV:RGB转LUV -RGB To Lab:RGB转Lab -RGB To XYZ:RGB转XYZ -ROI Add:热区添加 -ROI Bound Box:热区外接矩形 -ROI Clip:热区裁剪 -ROI Convex Hull:热区凸包 -ROI Ctrl Panel:热区面板 -ROI Difference:热区差异 -ROI Inflate:热区膨胀 -ROI Intersect:热区交集 -ROI Invert:热区反向 -ROI Load:热区载入 -ROI Open:热区打开 -ROI Remove:热区移除 -ROI Save:热区保存 -ROI Setting:热区设置 -ROI Shrink:热区收缩 -ROI Snake Fit:热区蛇形收敛 -ROI Symmetric Diff:热区非相交并集 -ROI Union:热区并集 -Rainbow_RGB:-- -Random Balls Demo:随机球 -Random Forest Classify:Random Forest分类 -Random Walker:随机游走 -Red:-- -Red_Hot:-- -Region Analysis:区域分析 -Region Label 3D:三维区域标记 -Register By Mats:按照矩阵对齐 -Reload Plugins:插件重加载 -Remove 2Path Node:移除双路径节点 -Remove 2Path Node 3D:移除三维双路径节点 -Remove Isolate 3D:移除三维孤立节点 -Remove Isolate Node:移除孤立节点 -Rename:重命名 -Resize:重设大小 -Rotate:旋转 -Run Macros:运行宏 -SLIC Superpixel:SLIC超像素 -SLIC Superpixel Label:SLIC超像素标记 -Samples ImageJ:ImageJ素材图像 -Samples Local:本地素材 -Samples Online:在线素材 -Sato:Sato山脊线提取 -Sato 3D:Sato三维山脊线提取 -Sauvola Threshold:Sauvola阈值 -Save:保存 -Save Mark:保存覆盖物 -Save Sequence:保存序列 -Save With Mark:带覆盖物保存 -Scale:缩放 -Scale And Unit:比例尺和单位 -Scatter Chart:三点图表 -Screen Capture:屏幕截取 -Sea Ice:-- -Segment:分割 -Sel By C Name R Count:根据行号与列名选取 -Select All:全选 -Select None:取消热区 -Selection:热区 -Set Background:设置背景 -Set Slice:设置切片 -Shotcut Editor:快捷键编辑器 -Show Graph 3D:三维图网展示 -Show Graph R 3D:展示带有半径的三维图网 -Show Viewer 3D:三维画布 -Signal:信号 -Signal Uniform Filter:信号均值滤波 -Simple Threshold:阈值 -SimpleITK:-- -Skeleton:骨架 -Skeleton 3D:三维骨架 -Skeleton Network:骨架拓扑结构 -Sketch:描边 -SmartSEMSample 780K:-- -Sobel:Sobel梯度算子 -Sobel 3D:三维Sobel梯度算子 -Split RGB Channels:拆分RGB通道 -Squre Root:平方根 -Stack:图像栈 -Stack Register:图像栈对齐 -StackReg:对齐 -StackReg License:-图像栈对齐协议 -Statistic:统计 -Stroke Step:分布画笔 -Sub Stack:截取子栈 -Subtract:减 -TIF:-- -TIF 3D Open:打开三维TIF -TIF 3D Save:保存三维TIF -TIF Open:打开TIF -TIF Save:保存TIF -Table:表格 -Table Bins Frequency:表格值频率 -Table Corp:表格裁剪 -Table Duplicate:表格副本 -Table IO:表格输入输出 -Table Point Cloud:从表格制作点云 -Table Sort By Key:表格排序 -Table Statistic:表格统计 -Table Transpose:表格反转 -Tables Window:表格窗口 -Thermal:-- -Threshold:阈值 -Tomasi:-- -Tool Tree View:工具树型列表 -Toolbar:工具栏 -Topic:主题帮助 -Trans to List:转为离散序列 -Trans to Stack:转为连续栈 -Transform:变换 -Tree Rings 48K:-- -Two Boxes:-- -Type:类型 -Undo:撤销 -Uniform:均值滤波 -Uniform 3D:三维均值滤波 -Uniform Random:均匀随机数 -Unit Matrix:单位矩阵 -Universal Generator:通用数组生成器 -Unsharp Mask:USM锐化 -Unsharp Mask 3D:三维USM锐化 -Up And Down Watershed:高低阈值分水岭 -Up Down Watershed 3D:三维高低阈值分水岭 -Update Dictionary:更新字典 -Update Software:更新软件 -Values Frequency:数值频率统计 -Variance:方差滤波 -Video:视频 -Video Ctrl Panel:视频面板 -Viewer 3D:三维可视化 -Viridis:-- -Watershed With ROI:用ROI做种子进行分水岭 -Widgets:桌面控件 -Window:窗口 -Windows Style:窗口风格 -XYZ To RGB:XYZ转RGB -Yellow:-- -Yellow_Hot:-- -Zero Center:零位于中心 -Zero Edge:零位于边缘 -ametrine:-- -ascent:-- -astronaut:-- -blue_orange_icb:-- -camera:-- -coffee:-- -coins:-- -cool:-- -edges:轮廓 -face:-- -gem:-- -glasbey:-- -glasbey_inverted:-- -glasbey_on_dark:-- -horse:-- -hubble_deep_field:-- -immunohistochemistry:-- -isolum:-- -moon:-- -morgenstemning:-- -mpl-inferno:-- -mpl-magma:-- -mpl-plasma:-- -mpl-viridis:-- -page:-- -phase:-- -physics:-- -royal:-- -sepia:-- -smart:-- -thal:-- -thallium:-- -unionjack:-- diff --git a/imagepy/data/logo.ico b/imagepy/data/logo.ico deleted file mode 100644 index 616ad78e2db4a40456a62b417dbfad1004655591..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16958 zcmeHP33yf2wGPtOzP3->_xh@RwNM`lNzS?VCU?k81_&WQ2w^Z`2=hFrj9~}~LqUPwV~vzW2mKk{bg8eZKF-@37C=`wZ(} zYwb1def9C_hX10Yeemy3e0=-)`24`f$Hx~3x_q*5u;1ICi1_^far1c4v#0fYefn7c ztB)QS;Hw8jpbhG++x>g#dXL_{d*AwhNf!S80loeG?bCFN^H6|2Ffvy z3~saq1Yg2Ef5CH4XqtV#rUeAwefQnp{3`Iq&(H4EEY7XKaE>^GgCpzjOCH-id(M1W zwQ7xQ-MU@&?%gK`4jho;;$kT%ER?*wJlVZ_kE~yxBTJSnmnl585SjukiHTU)nEF?#>=3@5i()I6j``%k>uoTl7fN)#gmB> zr;0PUuRtF}w+D*`UT6WX!@j-&cmFf_0Nl4wzagKq0f8ZnQ>M&Nm`h4ZWbN8)Ng16g zPFI9mh9Ti%9hE5hyz!!E&K5mqf#};8i@s~A=$q0+UpH6uB~!$bI#O)0{ejUbR(psH z22ZFH`}gmctgLl1Xy|a_7fW!s;)iB+ZU5m91HOH;`9Rx+xq6E8b@uG}lAnJ7dc0p| z%$y_6pon_P%3^hiHg34+Piz#+Yk609Jiklyed(glA1{`W2(bhN$;jlfvSrIw*^DD0 zX_Q+Z93i3yxX%0e1xDWt9(2?7Z&)m@Ex^|@di40_efx5OJzpkGp4I|AXvA?ww>q0N z=&4?madnuz@#y7!qUWy^Yf6&RCF;bME!!j$`Vbb`U#!khw@tM;GNF^*ZWJFl-(c6u zoUZUj>LTa%;>F9v?g*>5*<9}_TW@n!YtVK5@oSK|H~gl4{gL%z88<@BkJJg1W#7Ji z!j|(zhmL4A*JC%rkJ7GNZNV1?4Ib98FE>|mbMs{A&=C#L@j8Fqp3ZM-7FVU_gg$!F z_kh>)BiW=)m^I6}n&n}Lwxwd-jUFt_4}bM%<;E%aF%Hf$89Gos#V4LU&|Pkn<;dYyKu ztM(|}dGtBSkqdEG+=7_#wflLR+vlVU~r|xD#p7tAvxh$2CoV+5P45jN~6ZE5#*4V*s>w1+e@er0SP91_WR zrm*e(y?R;i&}`27MO)IO+Jk)~Z1?rVb1mz!AWPh@w{7cteW9}Q7AQ#Jvm7U9izF!NyH{uyReVPQi!jwIXMs8tY zE~VM0wv}PqYg<6jQ*i@_HgL|bSh*T8th3SA*XrB$K3<0fM?CS)i@Ox|JlOLaf#aIr z`AV)>hr~89TdHVBL z%L>J2iqWo#X2iIb7j}!~_YWvPVtF<9OY@VqKtpW6ST$wr1h-GL*mJz$L2Uc%u%uB< zls$7A+W(I9p3hgEF0fX?#(#RUKn8Xar**~)J#VFGqlXD&M&=7zP?%`JI9!P51CTp~ zA$MFcUG$%?y=GqM^HL=W`cHqrI0;8XM>>GHaW8ZCO`9nD0>t#84d|QgjaQKS5z2no z`ngw4o3=U4ov+y4^$mWv(<3TMG}syaVCGeSOMiNs$_p|OAE%5N@74j$zN9TKi^Z7} z9X(LZd)6Od2R`S9}f(I%oC;j1htN`~u*n z?ngz()neXnAdi641>g3k>WZQ`gJx$b#;xrQ2L@QOk1jI3FMcWC**k-`K|Cje81u!=Rg=@W6}&i7i!>9 zH9Pb%muG#B<9>d&K5qQQZxSnHK9Sc|CkyVtbpwv-_yI9XRP2WuPN(!hUW}3WUf2|w zGG&&WIrBj~ydCkt=&P)J5B0OziVu8`7LM5NsjZ!V*Tf@h+yI#|V~$LpFQ}%P_E)dA)U1F^+&;KE@<2av{m8J5Cn$yJ#RbNGM0dqg(81>qmah=kk z^^H{6smnWe?Y%1827aS`FKM4bxYWR zCgcl=3V(UgT;j+4-)-P;+d>zAPkq!M%x*`A{_GB=r>Ko8-8JaH9_@(}C&2^coXEYj zX`|bH+l*bEvxWw{!M>)@Yx{_XPBHj_tABcy_t7Mj~;XT57rttE^PPxw)9vB4V1a_7RngJLKfIk z%-;mq!4P8RIt1&9i2v&84~P%gvx&c|ykwDqx1*NNGlsFewD)rwO2NyZP{Eo2-d%Ft zu^W7N_>seIn;01-`r!?qf8S*L3_j>nlVvoplLt1`h`43}+cy|~Ma6uJ7A=MC3wh7L zSW{8FmiTG&jJ}Rq$an+&y8giDen2l=t!#csXtdCe8T8(WcJbnL(k)&BqsSS#2{k}FeMbWf%>&_dB zo&03{5E6nIku^BPdxpH*w)&b`5z~*YwQidu1b3Wj`(%js9Q}FXU2}4J+_AT@~ zZDr4Eef@k1?-wVlGmu9h7V-6USW(X#$~7#?f8P8w%D>FO2){R8$-km_kI~mztLe_i zD0BUxY~^3aPnddBup2gou`+RKQ<6KzpD{~f;QN*>UqL@Fy?XV!o%L<%0_*x*%YhyF zz`$5tQ9RJC|0RcwzRp^#(+8r)P@@eWg1p-$>FF6a6+ijF9EvjNQ2tn7ck%umJ9o*tb@!8hP4ItT;}~N+;>U5KGu-AeO}9TuJD{Nkxd{161V1_F6YG z_mJ1IVeqQ2sB!{HkPU=cwF_`v+F8%9LL}{Tpd$ zX#B!wz3J4%iKrPNCcI(}aIFk;o;uEWRJXcXV>%t{*#qORIld-N82s&p^PL|CXGh|!l zOj(3{DF}72Aw!bn`0j8f{lAXU!jM_Y@^R;KhKehcNu*~i+5g_ zGdHar{oA(flKwIM|y8?GxKJ3{rSMlI#^`N%4R>e0O_S6`2z#0`TjBn!xqK;^F z?lWjKTI7+35{8aYc>wOIgMDcA`IpNI{6JeXj*FL%6<@LU>OxuZkI~P1P*vM8&${^) zx!=bhe*&Ekkt4;cJH?LosqvL*6J)@E!B?F_poS;f0_1s&drC4@?U(EJZrR$M25zH8 zF3?xUzAE|v<_A~4)oO;Jhqj64J z5&NFzUbCX2BF3(D;K@p3yyir~UBFm{qxtgbq6nj(ttU>X$5`EGOfd{4DCRxt#HRfCBGw$mrnY(emqBD@W8+3Id*2FmP`3>WE>R<+P{KzihfB*e6>U~zkJB)Wn zjT+O8e5VZZQgOYRu8z*Jm#r`&qF+60V%Y!D1iH`MeRtq@jeBcO6$gW^S~qRQSFDl2 zt}XG<{7H3Lk6d&tZKE`4{D6906GSn?EhN6+7Q|>^ED~ z_~px&zmNy4w}EEvTWDskhxa#|{kfT@j?Re{^WekelyNF1>KhqbXSKSX2LD?35<%C8 zfv!dz^ow)M?}07IC(SrMt|>1GF^_To4D~AjvOe+Kt&+cafl%%Sc60mYGn-p^aO@d3 z{;H~~PVrY)SBopCugZg0tXRdpiH(@U>&-ND<=o%jW{0dA7QzlPH{|?5%)Hn9Y*j_c zSYRX%T5x0=_P~=j6`_yx*J2%EhWR-^J#cIQbTbMy%l*JlUYK$7oIKgFc2+C?2lpzpLli*Eo@~TD2-e?fK*U#U95E=I1?YB-O+U zoA}=s-zrh_guFoAYz_LUAH?w-_+>Gkg)QnIh`LL7Xas6b?eWtN7=GeI#EAoAV`ahs>t^5q_Wn6@%{&D* zu92Sc;C!#Tyl{x|t*TQ+u^3Z10>;UUC#y~uB~wT4wOD?Pc;xTIN|~!QEsq_r0tfy6 z(}yxttelcEPQ1@cTn=R-!{4I;|&daCszt43s4-Y8sBh{x0137;7_||W`bwSk^1x>D zfV{9HCrnC+S3aMyGUd*g-t6xh8yn@NmtH|kH>Z*IlLzCwA&@&{bfHflho3>)^=KjU z+4x2?*HCxt*ooXewjSR(%edM6{;G~D3TJ@iw=QQ{Jf%Abj0dud8`4abmo8x8HM|iaTKJ6-H4BaWBooqYq?Hae< zg4$FP=HAJzPF0|1pCJhYT*8J)>N4@Ax@~q zT-nCow{`X{ysuLp+;dOB4-hxZ1Ma^<=1ts_J89B1<>QGRx{}h_w{FxJ zYsaV?JWLu`KVuD*zt!p2FTRf0AftDIGl%OeTpuyhLChG-Q#{Zd#pbzyq3&u=3ic^s ze*cbk#}yP&mzp|R)o!uw-GDw-UjwhNh`r3;xLN14wV?AL>_AgkSadz@F!#yNoH!sp5g|I14PlWe!fgVqaKzbPa!Vz~6)oAD+xO zoPO;eSZ8^a@GJwd>W+ID>tnt?1bcawH1nX{3=NB}jgE<{iybhej&p_i1#Jg(MBT&ta9)F0;5aZp zjy07B(f2pcJI-^>@}G(ATE5NSqHw(t^`POf`%~!OaID6$41IIZj)(5V`dR|RF)!BQ zp4Wl*vIq00;6W+I7x?-4{p+=S|Eu~8?c`UL_OA%tAXEKaWT8)_#mA>7+lxLvj9erO z|LXuW==Sl65@}-FQw{t0AcU>tHEt|r3sGf7^8^uX7Te60?(VkfR$lr<)!nYzo^?G` zd;3MBja1jSR<7~1t5w^*RDI8)wR#MD0h2HYn!Dyz7f^7 zE%o#pZKda0wcT!s0)7CxrKh{i?ir%4vwK9i+uQD9+kNd01)%#Ma&A-CrLS#gdu!=S zIjXIk{8ARLD<`S;_KN}UmE!vLx;xV<(e|wSPDmQssHWS#$qz)e-1@cepv{zS*=WGW ztkShv7^9`Fedg?G4=w1D0**yIG;Rv3pgunS- zrf`y2tNY5ztHcVG&C&bSY@Y-we(+4QFa_SBI5GH+ELZs&DvD(rO3XSSmu#X>P>5h3 zv_HGJDcxE2`s7n_-Ups@x+hKZ z%Wd10)QJ0*1%Ve44&~t1Szn`DA@+|NDs&3NjVOTU>X%2oBH94=AZ(i&xP}LiItM#f z39ezn2Z2`Cq+xG1SwWysoVRqan^KC2K>(2cJK8k=Ka^nef6Nfz&8k&1<@KpXa3?(< z5e^9CU2~~bu1d_b<#Ol?%y!xD%x=vMQ%?8-^d#PF!i)sI57beP+!E!TuVKrW?C};v zPy&E#r&prth3C@n<G$AOo=;O)C!!|=wL2PcJa67LdfDvzVCNtWqOnQY&N4fzZ zv3!!gfk~6a^cgYMIaOpbSZMtAdn|tBuVb{dpI!@eYk7LDBf$f@t8+|j@_8JP->P1B+xL1 zh9GkSjzTtrO-dNRlLIn=(mkOI-;YM(6mu|zyk&@KkO#(%w#~$+s?Fc8G2Ent%LA;W zKSWiRhVvfVl#qxNGQmi3K&wXXbUmf`RP9frL#}^@zy+_5Ev{_>bkMEu8S1Abqg)%F zJYu<52Pl$aO%qaA$Y%Ba)89}2sA|8#*M|%R=EK6L&Qbt#h2J<)<&?do-MR$Crr_45 z>CTXwhW&W=!sof@!pzTrW%j+|DK$$i^>K;i2{HqVqA>Wihi29)HqEEZ6k+@`2>^fl zo)MH^q4d0AHi1RhQQkZgMhb;)C+MviU42Qdnn?a~B$O<#Pty(+FpWFr)*39iga8gH zTdNO-)7mny{??T%!oTj7xpE0O@C!LI+$LC?gXN|43iGEB-fxr;!~=!iamc2mD6@Qn z-%#mSY8hUiU@5sf1%nN(h0@iIVeiS`sw4^6FoHjnXbWP>=8{!)xdN1!ifNibh$`$zY5p9apMU+ZE%zie6d6a#^RL|o89`c|;g)yWIjmj@WVhVx@jaA+T)+>{82c~FOE zxa4Ge>ior1j90sD8l~vh<}2nBfkGW>Y!!`4S-swojDMfjk#RQL3UgJl|NMC> zah%$D!hoOrh!+Gpx1@8A8`d4)c~4i^aIvl1Eo@!?F{botCRsPBkO^Zlhy>rdyFY}_ zrOZJOv1UQ>L_Yx@!sCQxYm_6HA^c!M1I04_D#R4=^RwpwJ0Jl}2dgX%6j!{08bQPQ z5}p*1KsnwQO~W`dU#w38d+OX4;~F>a8eM5hO4YDhqBSBKpL02$q&)f8DaY_Rpeqdp z5-VdZ!N-cNb#9@{ids~ifgJlG*Ht&~%!?Hva_*1lW(oQEZEs`&dq03*Mpu8M@v$yj zokx0#Z63PNmn@W{Io9NR33))Py~NZ#7n?cRVH%T#Wz^ ziF~$X14IL<#8BE?FAMJ_8xgaA{x&B5RSERJxFhx6s$`-_a6z&|vlTk_{Wc=kP>+~M zn7LPL0wS9^)qun0V$h+pa$AhWf*Kd}3>Aplr|Q&wJL4iRHJvDEQXbwO9}6YR6n)l$ z4}{`#`~h_J4rxyqm&I1C_r{r+J+$5eVqeD|{p+{mMLa1+`P!8R2!*RBqm^KfoBk+O zvTmVgCVk_b5A_OLgwQ~gUmnXf&1bPDeKf-I;3DFLY&Yg`;z4p?DPh4d-zX5fyJB8e zQ>yv?nr(NzCZPRqtzF8VFhQ`OoSA(d9eRfj3xsQt2$<2Y&ug7(A$)u?SPM$Vd!sIC zlmD5~?`W_>r*aUxjw6)4T#(O;vc?Xf_D+d8C&GBWzSNVAAKTCyh+2 zWYClAAdLZ3L+T$-`HY|>+~&y!s<1L(BStCR1LrnNFu=}dUrpW%z2$UUDp--b@ZfT3 z(rrWzNY0uptf(quih*Cpdm)=lFbaTdH_ZQ^(!xbdPv3h4errA`RQV{aLPuVb1p=-5 zM$?~13EIc~fb#79{jJOSdzYfs8`Z-z0xi5-7QBc}?_zG)zi4WjzvHIug954Gt+h7z z?6NhYH-0GNBR830cVNgWcUUDJC~JK74;XvBq@$mB=Ns#*$MehtAgL682!i0nl1#hz zvmJ3okDDc85Xhh0)DUHJe)7Fo(W;yz>#{&Srwb3%6>N{z7SI^z==ggib94W}#q>Rh zsavBX#HYob-w2l(7Zl1cfcBg?5@}BOGS%xq1}jRwG9`SCaELe7}p!F6Rss`jd(kb!0*PTCZY+9TBrp z{T10H&eS0Y2zUqBcyU$4aU5pN+$rcYq8E~n?xpeZDXlhvO8ePo?pMWg7(aF(F!^46&Trn!s#== zOp%8xPYHm?#%73`iECX`T+>Z@9p^MO+8%7=^VWo-p)O(KX5{94VjHNMrO+#R*S3yS zQu_7Dk9>V{gX8yau2ya^7O&{l!0{#&n07eia_8ncWJ?iYj&mCsJ0Fr+&|F8}9Csd- zX~9ZXom6yEEF}R~XS0GeZM}!dW_)C_Epc%If~(Zq^CG1h?u?E7P0JEAr6Q;+KZ*us zPNT-Z;k;ak4mo=ql4|klK+?phbaih;j__$VDAbUujg9OZ>y;6|)C1Q9Aebmz&N^BL zN7QYcnE3xdR+su30d8R~-$wb`HQ|HObY6mg4IN7>kMmi~e_Ny6Ql zNwaT2p^>!;;Ifz=tKpTBkZb@JsIf!ms86wWYW|y_`ZHJm{@#9EpYSM$rgQFcGW|$D zf&ZBA-;@OgGFwOod}h>oy40Bv5s9nl(N-+(8 zZs{(bNm{W0&Vvdx3$2u`9>O_SnF7d}Jbd36H4u+gh<~#1tO%UsK!f8KdHD4cB;526 z-QrxanYy=PX7wN;?DlolP?UlT<&u!NG=v_Sl^ETg1)E&%7ZeVNAdPL)E7 zx}TC(D!P==u>P}uEzK>!&MbGYE|)$8QZKhK;1^fYf4RIJ+5{Ayj&U*f*GQ)rUP@;! zaOSyciM^Ci=Lv^fB88H1lU8w`Qh-Cqw_{K;h6z*zw#qZ4;Aq^6 z<`3)R0zj%L4P1C%cErBJw|gP;USDYP{fs4n-IfROCu5ijcc=Hznz&E%w9L94m5PL3 zVPSY*gRKj#f$y`reWqc{z4V8aJ@=i)wC%mL-%|JVg(2smnZd;D&$J1mGDqZW!g+q) zH+eJML{3|kNsSNlC&f~6FVMY{M+>bb%J)#RSo=FOpp&+y?AM#ARL+wNXnHb7x_>bp z1+t{R5alXn4fFKKFAV27_8qb_=-Y9Gs7q`nRMQHhaD;;s@G(*IsVqe$tD*Ru+6;!G z)a5VDsefZCg?^G%I6I4Nu}G3Vn8QN}>HMA4?o)Up@U`8)#UDkfq{{jtoP+{yxz3Zk z9(7K-&c;#^uK!1}`9(qD0`_iaO7HxdloFVFMyd&+pa^fj+#`eU>g3I<)i0+c$noeW z%qQ4Py%yloy2b~!6pydCC9hA`RJOQ>X3D!SNHM)&=$;RgQU|AFVgeN>wFdv{B4fC} z$~cg-N09Y>(cjPYT9Yew3e3l+(jxXNn-$(wV&Tjbw*Q+IW;m7=-^66NDw_pG#b$Dd zwrm-8Y{)weSzK@4>Adm@-g8hi6Z(NCk1Nht>l5-&(n1|Ekm`Xq z`6Oh%DcS9)AzS=IN)6-g5VtVFS7liQQL%}Sd9{O++mlJu2x(s6>9$bml2Xrnu;dEcFjp4KM8-(Fa7+1IRE=l$cAkP* zUyTWJdy(OWOlv@Z)4@IF9!c&n7b9z?@gw!$yaoisbUk6jD<%}2Tq-wYBWrt9Dr8!1 zL(K(Lu?g+idi!O`85UE;J&aM^RPr%9RezmCf}{ zpz7Vc8?`CrWY4EN2v4&5yYE(G6H9D7!VGdvr_GC9_rK&wR(*K&(w=j(^z6f_o^q+6 zbyJlmC%X*+FSNsB0uN5RMZ#+8>mF`%`Pd94ExAVLJW99u6ZLCqkGBl6 zogC?+Guh{ddL^Yokbd7Gm{0V2TXBZZ+U(D?;lh#>@7LX)@4{NS*$xgr>a^Fu-V|oy zs{@CXv}m>y5z8z&SC^0r#rScd-`eY%C5j7#N=4EvI{{lTdP}LBz{T%CF0!exH3RZv0zsJ za5KAALJ=I2it;!t5jAp{n9`Wk>XJDUbjVjw3NqXD`H|`s%dh!fBUwG*yKl?XHdgjk z;)L1YT`U@lFG^x+@(vcV8x!hPm7Z9i;q0Ag3BY^aNjlRx#zP>9=*Kjj+Xm!-` z16TMD(L|1|pu&Pq)xxOVh|M=v)D(fLjddq~GIy1#0MWQF6rw&$QD(DhIkRQmZs@ag z?OL*)t%#&)ILH!*XZr`av**5OZF6C*4f@xRph`qF-bDYyN6bI!_-AmoM7x~#!jEk5 zXxjs+2;KH#ZMBvj=z9*_?@fU&)BA%YgCk~d?dQiat2J3m=+US(E5-tG{f`@%vAg9| z=7SqKrCIDAWvW{RK8D+i3cno71?>+{UzW<-5RQJle=M{D6D)HWw*8k3b$r;)ydH_v z?V}5;fXyEcjxO6EwO?+;cN#q+BCPZ5|LV6*W|*a3p0&m+^kHmjZMUkkky^x-A%*L& zCtt~Bzgd0fC&e|QOGNPWKef3HI-EK#Rs*NWcYzz zMmhS3Sbk86kTW+rQPej1qqOa6nNysO{d6>$3=xT*N5QoB!<*d~lh--i6PHw7VpM5H z0;s77q^sHT^G7vYF-=L4?2*`Lf03yP{wrQ0iR}T!dC7cEUqiV>>RMjWOf%1az08?f zWyP84@b=iayvEOdsh=HkYuo2U@pglqQcAT9G-nNArH5bz@N8*E+MJAbsm^9YrK$@-MVD&N%0Y zXN^4t?>|>&tPht_69i9izv<0yV_=wd%L2|>MId0^7+oP(V&jdw&d_h2PTr@U86Veu zOYR05NA_BVZ8aQ==P+Oj;q zvUM88r)NhH7+@SJ%3s!-GHIN-+#99#`0pRHC(&-a4txt}2x&5h@$O^oWX%tflxd*q1G0>D(8OqB3c%K#ajp4*vy9 z3AblDlaz$XShGHcEtpFmehMFx?DSPE{#c+*@Wkfc0{L%=wqaNM!^qGvbj~|bY;j#m z$`_y>^R&+srg6PW?_o0LF_(KhpqEL;has9+xJ3-#!gPbyD(az z?e2o0AjFy@kKbsZLuUx})1RY1R}Y+{o77dlSJtZgQ`t*2m9}oSYI}M2IIZ zZ5mo2G9|wR(@bfK=q?z)lg^;!n4!dP@K}xNkRxHb!p3`d5NL#T8Gr8o@CUqCJy{gE z-ja@)(yE;{#1k*O%*v2jBU}9FrTOHaiL^#($Gbblqm(uUGn1(^qU-`@5hjUK*wTbz zU+HYZGOzbaSi*TmP6q8Hnbrt2tT<26qwK(iK)D(;m$(CH}Bh4kr3bz?jzFvJ#p57j*IMS_{66Pp}v;HvqFWWwGz9Qm? zzeaLL+|N%{(gALfIyW!c9$g-qwOE)BD=m_lpdKDvUi?%XV6QdDf61`eayS<+w6dYc zRpYGK#O>OvDfQ)_)|&3dGyxt5QtTSFtCkujWtiIYC9{q5xx$F$LC!3#MSK_iL-O=- zU(8ey%fqOtafVL)3X^+aJg1>F92!o}>?f%jD|b(+cA6?@tqsM)7)Foy+i0MrF2+ds zjA+GCOfe=(wLs?a#QY+ug7J?2Kme7$$dGiShc6=Cz9BJLPsB(0M*m8;2*`llBH?Il z0wh7Z-a5vFx>Zo^i);P)-O7Mt{=jW?imccMdHQOf00-XNIL5DG&@>BvrPH0^7Hws- zQ=@@KANXqDv0qEJ09^dNMsQh@jIY;Lv{3Y-%NRRE)Gg<(>0seC$7u-USBA%_-7x?C zTDxcOBBzx@fMZ18aoR(F_^ECod4g%&Y6wmwe?pY2t1dJV?U| z@nwvXJfuthwmGBd?>%0HzGHruF?9#4xm*#RJXo&(KEdqRUPcR6)CSbK*WtAgQ*KHb z>i~;jU7!7XZ{V*ppSRbj&%u?+2Vb|L&2oc~@I$iLepL@qG&Ob67Ek2UYJ zp+7I!v8IIUAAXY0&|U~ZjCv5W_ZGO_7co64=9bYnZjyPxzL}D+JIB75<(48ROcryJ zwEJ~CPC6ITf)pF(cP4rI3g%aGKk#6_oJnNCh80V!?va_5(suc@$5f~<|6WNrM@+3= zVIo!AaYG%~l2}5x4ZTfg265xWufmfh`hV3V(92rkU1#K^xM0cJ{Dn>YVAJG}L#1Dj znJ>lLx9mpC%)T@F_HbPH?bNSnLyqrAL|;iatkP7KvJw~#%xB%Xbn-AsnQJ_b*XDGy zDW@xL4cO`N%!)NOotk-TRl!`ZD!5Shmz;Qu-Ir4kIl}+tQ&c8bV9q1WjWY}OUXz9s z`~DX&|CleSG4gL1-{{Jnggc^xYGt+WVdeLqe|&pU*;r!9XOoyZ;MY99KP3w`X;lBP zLzsd-f~r)iCPjFU9lv(J-)=aWZ>PDZ=lp*0OMU{QXAjVogxYY&N9{N~Usdiey{lFN>exZF9w zy-5-GYW+Gr0#$41?=R)6GQ1xw!uKrzZwou%W^|ZIE9dfFb(B;ajN6&z zPAeN92`;`19+qg92F!9~mnD1n{HwC9wa@(FRV1zSN{4^`njuZY`C4S!!*K2l*03k6 z^>@QIax|Grp84V*;uhpZqD)l+2Aqq?G_ecsei~nppUatW zw-%eECHOWd)kTWPhAk)#!gd*QY8~d&()+HZb4WQ&tR-gKueVP+zPeN0$ z1q$P|*o}Kp=^X~5#!ED{^W>B+vB}QnWBCIjvoS4YBQhC4zf)$DC71rT z&GMFGu{kVZ(OhA4%WZoO{X9-zF29{o$tok$?kCwqJpn4L8u&4wM-M)W)2+*MSs1L& zN_B4=;s2vK$1n&7c136U*}NXwlSc<8BF@$9XWjtqh9k6=2j?v_74}|{3pG<|l=Hex z=*so$&|0*h$rha!r<#v&lR`?(qoWZ8lxh*~cicuoD|@W|yseaXf+TKevqaw$BqAzM zQIfIEBJ=zQoKcmxj7TcD&PCE>UsU!!MB1}FfqYNCn$f}k`^$~DM;AgfUdLTn90M1< zOu)g4$1*UeY|kjt`hFZ;X`)){%A>De&1(p&e4AqRZz;*b?ayB$U4;S%_KiY?;0N90 zu)&COmJ7BA`pwpNp{78l&st;AU(i4n<5x*tbXfB<(3|bL9z6UWj3pNrHQ38mX3^aD z@`(O@ypjHLFo72+SDpD76*sE1D4p-YKPVawUS0H!4J)Qm#X`3OI%>b#)W47^IS4!C zKltZXn~&NZU$Attveb>M7%ET|9q{~P)}c>fEbNnN+N8WVuG&ae6_2%llQ%b*U>Ubb zEKGOzhTehgnN+d)@UeFJ2py-tqEUU?z)ML)e;2>*rGO8-#4;vW9#xNFm+UavU0 zL*MPAUqhcDWdlN*Tq(c53Psw*7vh8|#Mwn?QMFgj+Z(~&H8$@E%-W@7R&FFK1a~H_ z(n8isp1#)Rd2u*>_TYlur1IDE6WsiJ?w7jH%L)YS5zfr*7iP?g3o4dM7a@oG3dWnl zU7p{kBzNzf+cwvE%?tgWen7FAUHDA(xl#pVEko~VYyZN-GZCQ{_+ww4lb1uUkBg~p zm~(}xrjoqri%F$^C(9+R7q#f|8`{;P?)#Zr^rz!L6;{`MYrRG1GdJ~O=;gd(t#j}% zDv|Ur`&TSR(uG%hSo@X!ERmu-;5x`FoHvC$@Y@vpjXuwr)U%@B?#I0!BJE7-Q>PVK z)K9GO3emMZc{71*1=bC#!CGm->Q3s&L~q1P&h9e<LC&5_y{aP;OjzF0WGI$id$9(&CWLwOLdxQpC^Zb)Q4S9xFn)h7&VF?{L~lbNHGu& zDs1aAo>c3^UQw00Xnp^NtnY?5eVQp;h&WCPk+72}HD*cE#9P#ee(*3;X!N6a`zuaE z9{%h)J@I+57jMP{T4KLOKi8no()nmVPpHNEBXgqt{HJ9-pU``+u89w{xl=4B20ahR zRWup+ohzi+$3aJSzkIo*=$^vG_ifyd{f*4k;}AqhSBw_ydu^oeZddYV(q(r5B58N` zNzF&)1+0ZAruNGP&n$GEN`y6WCHqq;Ekx&9L8-s4P+q3z)XeK*efgVnzr64_S1gKs zqwCK*s6H25E!_~yAVY-WO8V(w9`PW8ytYE!X?$mDPj>ToxHSPVGSy;~GEOV#;v7O(!Z(6Y8f9$BQdBHham0i?8l zi8Ka})|wCzx|!bDt051~4A1-C!q(3&o_)naS4*mXdD6tkoV&psC-NvE&%@NiaroJP zGwN)c4j6PRw73*eQe`ce=W+h}{$pc}KDlN1=XqypWg&9(pMp}Z*wf(bL+YiA-m5Ly zDq2|HuTMLJnNSo;eMgm_y&e%@xFa4ch@)Xj3Aaa%BvmNzKnmwKofPn-|`M`+rl-iZypIreRvk| ziAI_{-f;V!Lhy~1Hp_>Hoj07Hxv+cmUkb3!78ddsiorLYzEWZ)J{T@dA^{@mWRqg| zlJRu;YU$1ZM#Q2YXRphLt*`_Sw?iMG*PGbV>}^Ez0@iOz^q;hAwIH zd7*q>pLu)56Lw0c*kA9OcCRqaZhlbmj`m|vHT6#{L6_x)UOop_CbwW}wEyqE5bsz* zOJ1L#4oQ4o8}Da48T{{yNu`<}q ztCM=c6P8pefo`%kBGpRMETk+ovK)`l$~mFV#fNSEWVyZg*=K^ZP0OfacNyx~o#**Y zB%?b64L38IZHvf%NS))eqH4bJpooi@IK^YEv4{FmN%B1jW#)~?Jb$e|4yw?r`|m^c z`I-=NxJTl=TgPP-1JqpmisGwhDwHnXLVtEa@*+d^kz8(XG;t^lEZzW6WfH_JY zaUGV#ZM^LqatN&cW38Am*Ps4jYU|A<3(zza^#!e+RCeB*t zCwk3ZpxLrtN)=37@`fmB(QF{gT#6lji?y%58`+h8e!j*pWdwephRJ!V!0ktNg$J~% zqV~mWx%n}L-#*>DpaFV1=f5y$eLlv8v}G6Qsy}3fD7tKD;a`_ib-=;6wu)Dj1hc$oleVl4GX0N8ju3d|8-s|2Pw~!qJSp8 zEc{@NXH2&cZPZdqzwvLI7vu47sH|uxfu=5hviW;vIJa=iK#!4`EO2SO$}qUe8vKHd z@)jpFp70joPJRnhU)3E7yj$XrL&v79ZYl4Bc7G}!5ZT$^k_DYR{`Zbupi%aqmAd`E zrKDvEi2mQ?|FN1uaoxA2dkgLP2n&5iMq&4AB6_&V?~-{!wXw52S_ ztu4G+{*w{}G@#+#vQ+e4M{YOfe>tM=?${FlkGe`uxXyn_5-lur9MytP@NQ)rhq*ME<)ViuVI4kaH@XYxPwx3MJ`!3oqW`FXXxj*G z2{VQ1b|WolpiSWaMnnitu3P;zJ!ocXyX==Mw&eS7m_AZP$f4cs`JZt2*~3_95&!a7 z=`LA-CBDh?gk`N{Do;`n@!q2103CKY;k;eZ0EfQ$h|dYK%}ff-=J>Z9H>@MN4+G^= zb`&jLfGhITH49PzcIQ9XS>0gfG|%5}sJN%$VYdc{noz085=`Kj<^#RM_{FPw|ADRZ z=zl))VMXlI-7-Ttl!i~G-QC(X%MAjdgv-N8idQxNn}|3l%ptEJ*hEr7n;2ZV3}96k@iTUNn@&~v_OHhfU1@domq-oe^kVAXr-ucRf4kOBgzf` z^q+ffa?`SJCU1OP<-Da(jRAUac|8el6$H-zc7r0xx^pWdTjDSgZ4mzX3D5xqUOk)` z&Y5Q+HMr$9w8BffJNZ^NSu+0m2ojdD2iN;IICiB?y~nklp@7eWR24N9Dq)u4{{uMk BvWx%# diff --git a/imagepy/data/shotcut.cfg b/imagepy/data/shotcut.cfg index f82fadba..72e0e02e 100644 --- a/imagepy/data/shotcut.cfg +++ b/imagepy/data/shotcut.cfg @@ -1 +1 @@ -{'Topic': 'F1', 'Open': 'Ctrl-O', 'Select All': 'Ctrl-A', 'Save': 'Ctrl-S', 'New': 'Ctrl-N', 'Copy': 'Ctrl-C', 'Paste': 'Ctrl-V', 'Undo': 'Ctrl-Z', '6_Shades': '', '16_Colors': '', 'Show ConvexHull': '', 'Exit': 'Alt-X', '2DSurface Demo': '', '16-bit uint': '', '8-bit': 'Ctrl-8'} \ No newline at end of file +[{"name": "3D Surface", "shotcut": "Ctrl-F"}, {"name": "2DSurface Demo", "shotcut": "Ctrl-D"}] \ No newline at end of file diff --git a/imagepy/data/watermark.png b/imagepy/data/watermark.png deleted file mode 100644 index ad8fb25c3a109114bc24bd0632c583cdd343d8b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4867 zcmZu!X*3jm*!|5|$5OI}Y}v9t?0uVXk z?&g*|wJ<~O79$YQXlqNdAo)D+Io@IdCKs22dx`rGBA07x2$zKkN&jgD9fju*nwa_y zLKlHR2yz(y(N27hQzm3Pp{>9EHLy5bikR#0xMUr()9tr?)1uS2vZ043BzH;C?QG~Kiy%U7S@7O#|L;g) zLVo*HCz@FCa2ism?9Mr>)q%f$|2pgStQS{a@V{KIBvpKK_w?%ZYV4kC`|`j!F;*f8 z7)eUXcq{QJ+3~BY?q3s>&Mj`}Xh=GUr{r$hF*CwSxx+YO#C}PYO2ycVIcL~~y|;g* zhCi@LF+l$Lykz^?FkFY#A*DL| zs7k^YzhuA6wAeJD`9^0lzvEJO*MR-NK{ZkPXRDXYw<9^YEXRkKQaOL?0D~3#m8KD$ zh?>>Nmc&wf%m$eZ9Q)rk-P4O8Hq5UybU5{RSn?v7qm3)3r+m zOoq}1{GB!JhV6gatHr*VkG%?0p+v#0vYqP846VIvhDuko)%UkvYz;EUFg<$Y#Umsi zqu*E(`!#n~a9EXC3rb|4&1wc0hS)KOGyfTba1y0)#XoK;k! zZ}cCYHf-IXjD#?=gD7#ZWdg|0L6;09%?>JDPK$7Xfyv>c^^>eN_us@+rLRpc<-_wr zbLAs(S$1d`dVY<3Qfx!eJtb3%QwUXptmXc~c6YPk1O z19%979*&1?NI;taszquQ8LunGA2vX>JalZ$65Z&r!^{a$+JOO#YV-)=jRiY8R9SO^n>J$&ElAn1d-Ix7OZ$9XVA#es5vvIZZGk1w z#~lz7*CmE&}{i_mdmj+4cS4n8gjdq}F6rQHa|$%)>_vwyagv z%MTpo89?$eU)y>@3k2FA17~M^tQcQJoE$ocWOEY%zb~KmaY(4+D&@1dg=U5l8}F^2 zZ{1di$w{YoXt5>ZO$EDjtyA^CQkQt4qu0ru!vfFsTEwpeUr?UDyEKIr&3ObtBphcn zcj!%BZy3;a=k8>OBPMn<5sBqcf?C=dHgQ7WH21s%{ir`VE0S8r8HwpTI%{comDzoN zkccux{hZ5djtD^}uQ zu*eAtQ0b;mT~QTZ+7GhGc4hm~V%BI#NZwfJ#xqBu(`F&Y?`#ACT}gl#x1Gz758zd5 z3K^HP-adm!lyVWSKt6LdIi}`5VJ2kSt@T|zK6mcwj_!Ne6&U$+N2W$NUZ`1<>K{CZ z6Pco8&!MM|dJM;=TspN$4TRIMiPFU&+@6W!yaS=J%eQE&M}-(bnle=_Pc_@vWq#)9 z=W@%;q8N5;F>f}?ee}s?_$b#m!C!=bTd9~{;*SU%Yk&Icw8F)j_L(^OO`X#VE}Dch zLyoei`x0Eo2K%oLAzri8C=VxvooqFF^(Q16OPl^2|PL1;ShgLkeQ&?l`1WrFhMpxljB0S00&yM zVvK0+c{|$;NB*j2sPpI5BQN@l?|%ja7_U;lHySqU{8)w_C}rRwpsbmpVbf`gv3%h0 z=**J7Th>JmJRPFsU{+lQT{N)!-blY=gpEW|y@vUDYY5Cc`Y3<=k%$0X{i3F_H_RVD zTbFE67Gdi;JEgwxm4|cS4pV^AR$QrSu7qe;2doHb?VsLh@1bW$?;os+5p)~J6~Q20 z!1xtpg@}@*6IZ$-s`YG}+_&@_%a3-TS9O66 z`wcI)bt08Lv+K96R;H2IDkoDW8@th(+cMqqIrAuDwKz3&oBB!Q@#MBySJj5GOs34e zpzxk8MPX~bWG>2|^e&&FHbr)~lv~N&f3%$Zg!^E z5_Lv}2@ieiOK4{4pNboIChcsD>VX{jMpB;_%;&C(*sldI)D~$dE^Kt} z#A2f6P^5cZ)PeXTB|^pzN&lAv|*OpP%DjU-%YW!z@+}KMk|916Vq5 z^}-GwHIs{Wwl)HAt2=M7>9hoWLiHlvLCa-X?OS!6FB~vKT>BS9b%satwz)?Z%hpc7 zW)Zl5Tyb&s=$+pd6Ln4c0*&7xr@7#6ztq~PSK8+OPOwSCcA#o5AlqBis#JtMx$he$ z>q&F}c(^%}bp&(z+7mJ#DZe|@FG<@?{vvn|BG9p7`8^j)j^s&Lni`18E;nc;-KVPX zKqW5BoNjqLya^W6t$nZXJOEp{rf|~nMYm{|CCI*KQou^|Jud{PP^VU_m(P+U52J{# z?{Ez!1E*ZQ0G$Me=Qr6VD``1hqL{!GLijACYiq*{V-C(&C*u-Vf1j1VD|U|s^|IsK zE3&sU(H5HV2iS2ijBJ_|a=O1d?pyG%=R$L|hLELrBQVM31{hV)57AvkR&L$TIgVNL zHUe*PZbd>yN9L|=<>lXdsB55qBstiK|%0L<3a8SaIu^GT#8eJUOc zH!B_QTNPu10(p)NWLJoj;gx*X3E?ly`G!x(f;rUA+>4q5E1k<|n`!k24SxzV!i`svnA-ZD+{91R zfU;Gt+Ow%&()T4S z3pzSGIjKz|6`WwFE45_N6iCTnpL*{EHTx%Y76XD2M!@pg681~qHIMR{$xkdKJK=`n z0~8D^(G$ssr?q%Xj%$*h8(#&V?h0d$wmUTf7nVJi9^PrRxX!L;Sm>-wr9@6e+@m_W z!X^tC62_k#Hz%KOEbOo+s$Jnt__^&$t$)95&p=x*x1YMSv-i{OV)tW@3%#TDP{;pF z%17-hrhi0EwADXS)JDoC?_PYVOluv0bvG@@izXXrIttE+Ucg(brxn;zQBcd^MVm*oQui)kd`YjbS*{Lk zMs%zGvwm5yr}(C2picQ?>tu*mZ;gq?9m}oRodsbLI(A}Q-^z-N37(zTPyU6NR1@G8n~({Z2}`Ju4a zrlDUCzD0ky7X2)f1ZYN5)%XS6-7nCd$65|wT|xtT0gd%E{xRf zaJjz8(|*pr+kx|=`H-k{#bexR;_6&M7{G1{x_F*e#JY~HST)&5ZqRH5q&Dv7bGEFD zyE5xRQgK_hm*)gLu+Zw;^Y-PLEs@&FA+e(&Cy)cc94wc_r&gMHw`@QuTZ0RzdEPHF zomc;i=Uj_lBM@u7v$HcsLZRS@#<|bY$wn6j&B(VU(p4Gb(SnWi_=ll`zsF?w@)_r1 zD`e=L>Xe=dK(La#A2X~kX1T`glUk&gVh2Ex>DTK{hxr zh1y`Lfb(_oWWndg2&OP`$YF9=^$Szqd>dBsV3Bhhz0^yK$iyGt#pQ1;obT)t2SE~| z0Z%TidHi^X957um0r`mj2@>C=K6y^=qnf^4S$9&^X4U!;B&psRrv_| z;obS%P-s?(<^$TdwFXtpsy^I_vJ|`y9cgs-AQzL?z!o^a#lr|H=WpL&tr*jc;rN1~ z>cXYPu}r9Hf4Yg=uftR0GxBbh&i#1GipZnI2oUxzbawC?|JzHGu$)u4(aZ#%$bGcck2ebX6A6j{K)uebx>XPXrC=RZ5E|!AEE650s zJH|&Yj?&nar#`*s&_3L(JSv2g++co`P*1-%Y$H(Z*vVe}v7p>I$Pc$(!Xc=8B)zFx z4NO_h<9{waq@Q`F932(AQ>ySIg~kYzA`R&VHP1)`$cK_Emj?`&QhFS|Y0S7zbN1iy zBO!%Mu*O>{mpSfBzTm+oV4v{QqikQBJIh&`t|d9-Aw~sH)EKqK`v&w&3%ERf-~2?{ zTec;Mp{j&~J}7C}Q9~l2B@w4DeSNG0TBk!yUq_Vu{!<*s&wCA~-u!{ynD6=-xwv=E z_}eC=#6o`cj_Fe6r3p+}DP)OyLoqqiQ!V}Y=4JTz3m}}0#AUm4EYISVkCojf8Gu%|Ub64zisq$oGxO9k1D^z-Q(|9! zg9N`iLk~0%3oxY~mV0}Mm@JS6zP=4d#j!VewZ!B#Lftr-E|@t6a9Tj!qQl80KC+N0 zdtPzyFAg`U_k$KI%z5z_gCro~*!;o*pl+Ko42EL=PZh>C{9i}DaOV diff --git a/imagepy/doc/File/New.md b/imagepy/doc/File/New.md deleted file mode 100644 index 2464a4a2..00000000 --- a/imagepy/doc/File/New.md +++ /dev/null @@ -1,19 +0,0 @@ -# New - -**Description:** create a new Image - - -## Parameter - -**name:** the new image's title - -**width:** image's width - -**height:** image's height - -**type:** image's type - -1. 8-bit: create a 8-bit one channel image -2. rgb: create a 24-bit three channel image - -**slice:** the z thickness \ No newline at end of file diff --git a/imagepy/doc/test.py b/imagepy/doc/test.py deleted file mode 100644 index f32330d7..00000000 --- a/imagepy/doc/test.py +++ /dev/null @@ -1,10 +0,0 @@ -import os - -def get_docs(path): - docs = [] - for dirpath,dirnames,filenames in os.walk(path): - for filename in filenames: - docs.append(os.path.join(dirpath,filename)) - return [i for i in docs if i[-3:] == '.md'] - -print(get_docs('./')) diff --git a/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py b/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py index f000f4ad..02b3b77b 100644 --- a/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py +++ b/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py @@ -2,7 +2,6 @@ import numpy as np from imagepy.core.engine import Filter, Simple import pandas as pd -from imagepy import IPy def colorselect(img, pts, k, usecov=True): pts = img[pts].T @@ -45,23 +44,22 @@ class ColorCluster(Filter): def preview(self, ips, para): snap, img = ips.snap, ips.img - pts = np.where(ips.get_msk()) + pts = np.where(ips.mask()) img[:] = snap msk = colorselect(img, pts, para['sigma'], para['cov']) if para['within']:msk = within(msk, pts) img[msk] = (255,0,0) - ips.update() def run(self, ips, snap, img, para = None): img[:] = snap - pts = np.where(ips.get_msk()) + pts = np.where(ips.mask()) msk = colorselect(snap, pts, para['sigma'], para['cov']) if para['within']:msk = within(msk, pts) if para['msk'] == 'red':img[msk]=(255,0,0) if para['msk'] == 'dark out':img[~msk]//=3 if para['new']: msk = np.multiply(msk,255,dtype=np.uint8) - IPy.show_img([msk], ips.title+'-colormsk') + self.app.show_img([msk], ips.title+'-colormsk') class ColorCluster3D(Simple): title = 'Color Cluster 3D' @@ -89,7 +87,7 @@ def load(self, ips): def preview(self, ips, para): snap, img = ips.snap, ips.img - pts = np.where(ips.get_msk()) + pts = np.where(ips.mask()) pts = img[pts].T mean = pts.mean() std = np.std(pts) @@ -109,13 +107,13 @@ def cancel(self, ips): def run(self, ips, snap, img, para = None): ips.lut = self.buflut - pts = np.where(ips.get_msk()) + pts = np.where(ips.mask()) msk = grayselect(snap, pts, para['sigma'], para['cov']) if para['within']:msk = within(msk, pts) if para['msk'] == 'clear out':img[~msk]=ips.range[0] if para['new']: msk = np.multiply(msk,255,dtype=np.uint8) - IPy.show_img([msk], ips.title+'-graymsk') + self.app.show_img([msk], ips.title+'-graymsk') class GrayCluster3D(Simple): title = 'Gray Cluster 3D' @@ -129,8 +127,8 @@ class GrayCluster3D(Simple): (list, 'msk', ['clear out', 'nothing'], str, 'mask', '')] def load(self, ips): - if ips.roi is None or ips.roi.dtype != 'point': - IPy.alert('need a point roi') + if ips.roi is None or ips.roi.roitype != 'point': + self.app.alert('need a point roi') return False self.buflut = ips.lut ips.lut = ips.lut.copy() @@ -166,6 +164,4 @@ def run(self, ips, imgs, para = None): msk = np.multiply(msk,255,dtype=np.uint8) IPy.show_img(msk, ips.title+'-graymsk') - - plgs = [GrayCluster, ColorCluster, '-', GrayCluster3D, ColorCluster3D] \ No newline at end of file diff --git a/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py b/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py index a6963db9..e9e52485 100644 --- a/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py +++ b/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py @@ -1,5 +1,4 @@ from imagepy.core.engine import Filter -from imagepy import IPy import numpy as np from scipy.cluster.vq import kmeans, vq diff --git a/imagepy/menus/Analysis/Pixel Cluster/statistic_plgs.py b/imagepy/menus/Analysis/Pixel Cluster/statistic_plgs.py index d1170578..95d74866 100644 --- a/imagepy/menus/Analysis/Pixel Cluster/statistic_plgs.py +++ b/imagepy/menus/Analysis/Pixel Cluster/statistic_plgs.py @@ -1,6 +1,5 @@ from imagepy.core.engine import Simple import pandas as pd -from imagepy import IPy class GrayStatistic(Simple): title = 'Gray Points Statistic' @@ -18,8 +17,8 @@ class GrayStatistic(Simple): (bool, 'kurt', 'kurt')] def load(self, ips): - if ips.roi is None or ips.roi.dtype != 'point': - IPy.alert('need a point roi') + if ips.roi is None or ips.roi.roitype != 'point': + self.app.alert('need a point roi') return False return True @@ -43,8 +42,8 @@ def run(self, ips, imgs, para = None): if para['kurt']:rst['kurt'] = data[['V']].kurt() if para['lst']: - IPy.show_table(data, ips.title+'-pts color') - IPy.show_table(pd.DataFrame(rst), ips.title+'-pts color statistic') + self.app.show_table(data, ips.title+'-pts color') + self.app.show_table(pd.DataFrame(rst), ips.title+'-pts color statistic') class ColorStatistic(Simple): title = 'Color Points Statistic' diff --git a/imagepy/menus/Analysis/Region Analysis/connect_plg.py b/imagepy/menus/Analysis/Region Analysis/connect_plg.py index ee46270b..abd93816 100644 --- a/imagepy/menus/Analysis/Region Analysis/connect_plg.py +++ b/imagepy/menus/Analysis/Region Analysis/connect_plg.py @@ -1,10 +1,9 @@ -from imagepy import IPy import numpy as np from imagepy.core.engine import Simple from skimage.measure import regionprops -from imagepy.core.mark import GeometryMark from scipy.ndimage import label, generate_binary_structure from imagepy.ipyalg.graph.connect import connect_graph, mapidx +from sciapp.object import mark2shp import pandas as pd # center, area, l, extent, cov @@ -28,7 +27,7 @@ def run(self, ips, imgs, para = None): for i in range(len(imgs)): if para['labled']: buf = imgs[i] else: label(imgs[i], generate_binary_structure(2, 1), output=buf) - conarr = connect(buf, 1 if para['con']=='4-connect' else 2, not self.para['nozero']) + conarr = connect_graph(buf, 1 if para['con']=='4-connect' else 2, not self.para['nozero']) conmap = mapidx(conarr) ls = regionprops(buf) @@ -48,5 +47,5 @@ def run(self, ips, imgs, para = None): neibs = [conmap[i.label] if i.label in conmap else [] for i in ls] dt.extend([[len(i) for i in neibs], neibs]) data.extend(list(zip(*dt))) - ips.mark = GeometryMark(mark) - IPy.show_table(pd.DataFrame(data, columns=titles), ips.title+'-region') \ No newline at end of file + ips.mark = mark2shp(mark) + self.app.show_table(pd.DataFrame(data, columns=titles), ips.title+'-region') \ No newline at end of file diff --git a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py index dee0da3f..01c1ab89 100644 --- a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py @@ -3,13 +3,11 @@ Created on Tue Dec 27 01:06:59 2016 @author: yxl """ -from imagepy import IPy import numpy as np from imagepy.core.engine import Simple, Filter -from imagepy.core.manager import ImageManager, ColorManager from scipy.ndimage import label, generate_binary_structure from skimage.measure import regionprops -from imagepy.core.mark import GeometryMark +from sciapp.object import mark2shp import pandas as pd # center, area, l, extent, cov @@ -64,7 +62,8 @@ def run(self, ips, imgs, para = None): if para['cov']: ellips = [i.centroid[::-1] + (i.major_axis_length/2,i.minor_axis_length/2, i.orientation+np.pi/2) for i in ls] layer['body'].append({'type':'ellipses', 'body':ellips}) - mark['body'][i] = layer + print(i,i,i,i,i) + if len(ls)>0: mark['body'][i] = layer if para['center']: dt.append([round(i.centroid[1]*k,1) for i in ls]) @@ -92,8 +91,8 @@ def run(self, ips, imgs, para = None): dt.append([round(i.orientation*k, 1) for i in ls]) data.extend(list(zip(*dt))) - ips.mark = GeometryMark(mark) - IPy.show_table(pd.DataFrame(data, columns=titles), ips.title+'-region') + ips.mark = mark2shp(mark if para['slice'] else mark['body'][0]) + self.app.show_table(pd.DataFrame(data, columns=titles), ips.title+'-region') # center, area, l, extent, cov class RegionFilter(Filter): diff --git a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py index dcbbd9b0..0770401a 100644 --- a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py @@ -7,38 +7,9 @@ from scipy import ndimage import wx -from imagepy import IPy from imagepy.core.engine import Simple, Filter -from imagepy.core.manager import ImageManager -from imagepy.core.roi.pointroi import PointRoi +from sciapp.object import mark2shp import pandas as pd -from imagepy.core.mark import GeometryMark - -class Mark: - def __init__(self, data): - self.data = data - - def draw(self, dc, f, **key): - dc.SetPen(wx.Pen((255,255,0), width=1, style=wx.SOLID)) - dc.SetTextForeground((255,255,0)) - font = wx.Font(8, wx.FONTFAMILY_DEFAULT, - wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) - - dc.SetFont(font) - data = self.data[0 if len(self.data)==0 else key['cur']] - - pos = [f(*(i[0][1], i[0][0])) for i in data] - for i in pos:dc.DrawCircle(i[0], i[1], 2) - - txts = ['id={}'.format(i) for i in range(len(data))] - dc.DrawTextList(txts, pos) - - if data[0][1]==None:return - lt = [f(*(i[1][1], i[1][0])) for i in data] - rb = [f(*(i[1][3], i[1][2])) for i in data] - rects = [(x1,y1,x2-x1,y2-y1) for (x1,y1),(x2,y2) in zip(*(lt,rb))] - dc.DrawRectangleList(rects, brushes = wx.Brush((0,0,0), wx.BRUSHSTYLE_TRANSPARENT)) - class RegionStatistic(Simple): title = 'Intensity Analysis' @@ -62,7 +33,7 @@ class RegionStatistic(Simple): #process def run(self, ips, imgs, para = None): - inten = ImageManager.get(para['inten']) + inten = self.app.get_img(para['inten']) if not para['slice']: imgs = [inten.img] msks = [ips.img] @@ -100,8 +71,7 @@ def run(self, ips, imgs, para = None): boxs = [None] * n if para['extent']: boxs = ndimage.find_objects(buf) - boxs = [( i[1].start+(i[1].stop-i[1].start)/2, i[0].start+(i[0].stop-i[0].start)/2, - i[1].stop-i[1].start,i[0].stop-i[0].start) for i in boxs] + boxs = [( i[1].start, i[0].start, i[1].stop-i[1].start,i[0].stop-i[0].start) for i in boxs] for j in (0,1,2,3): dt.append([i[j]*k for i in boxs]) if para['max']:dt.append(ndimage.maximum(imgs[i], buf, index).round(2)) @@ -120,8 +90,8 @@ def run(self, ips, imgs, para = None): mark['body'][i] = layer data.extend(list(zip(*dt))) - IPy.show_table(pd.DataFrame(data, columns=titles), inten.title+'-pixels') - inten.mark = GeometryMark(mark) + self.app.show_table(pd.DataFrame(data, columns=titles), inten.title+'-pixels') + inten.mark = mark2shp(mark) inten.update() class RGMark: @@ -164,7 +134,7 @@ class IntensityFilter(Filter): #process def run(self, ips, snap, img, para = None): - intenimg = ImageManager.get(para['inten']).img + intenimg = self.app.get_img(para['inten']).img strc = ndimage.generate_binary_structure(2, 1 if para['con']=='4-connect' else 2) buf, n = ndimage.label(snap, strc, output=np.uint32) index = range(1, n+1) @@ -182,7 +152,6 @@ def run(self, ips, snap, img, para = None): if para['std']>0: msk *= ndimage.standard_deviation(intenimg, buf, index)>=para['std'] if para['std']<0: msk *= ndimage.standard_deviation(intenimg, buf, index)<-para['std'] - xy = ndimage.center_of_mass(intenimg, buf, index) xy = np.array(xy).round(2).T @@ -190,7 +159,11 @@ def run(self, ips, snap, img, para = None): idx[0] = 0 img[:] = idx[buf] - ImageManager.get(para['inten']).mark = RGMark((xy.T, msk)) - ImageManager.get(para['inten']).update() + red_pts = {'type':'points', 'body':xy[::-1].T[~msk], 'color':(255,0,0)} + green_pts = {'type':'points', 'body':xy[::-1].T[msk], 'color':(0,255,0)} + + self.app.get_img(para['inten']).mark = mark2shp( + {'type':'layer', 'body':[red_pts, green_pts]}) + self.app.get_img(para['inten']).update() plgs = [RegionStatistic, IntensityFilter] \ No newline at end of file diff --git a/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py b/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py index e43fbc07..4339631c 100644 --- a/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py +++ b/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py @@ -3,27 +3,17 @@ import numpy as np from numpy.linalg import norm import networkx as nx, wx -from imagepy import IPy from numba import jit import pandas as pd +from sciapp.object import mark2shp -# build statistic sumerise cut edit -class Mark: - def __init__(self, graph): - self.graph = graph - - def draw(self, dc, f, **key): - dc.SetPen(wx.Pen((255,255,0), width=3, style=wx.SOLID)) - dc.SetTextForeground((255,255,0)) - font = wx.Font(8, wx.FONTFAMILY_DEFAULT, - wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) - dc.SetFont(font) - - ids = self.graph.nodes() - pts = [self.graph.nodes[i]['o'] for i in ids] - pts = [f(i[1], i[0]) for i in pts] - dc.DrawPointList(pts) - dc.DrawTextList([str(i) for i in ids], pts) +def graph_mark(graph): + ids = graph.nodes() + pts = [graph.nodes[i]['o'] for i in ids] + pts = {'type':'points', 'body':[(i[1], i[0]) for i in pts]} + txt = [(a,b,str(c)) for (a,b),c in zip(pts['body'], ids)] + txt = {'type':'texts', 'body':txt} + return mark2shp({'type':'layer', 'body':[pts, txt]}) class BuildGraph(Filter): title = 'Build Graph' @@ -33,7 +23,7 @@ class BuildGraph(Filter): def run(self, ips, snap, img, para = None): ips.data = sknw.build_sknw(img, True) sknw.draw_graph(img, ips.data) - ips.mark = Mark(ips.data) + ips.mark = graph_mark(ips.data) class Statistic(Simple): title = 'Graph Statistic' @@ -51,7 +41,7 @@ def run(self, ips, imgs, para = None): etitles = ['PartID', 'StartID', 'EndID', 'Length'] k, unit = ips.unit comid = 0 - for g in nx.connected_component_subgraphs(ips.data, False): + for g in nx.connected_components(ips.data): for idx in g.nodes(): o = g.nodes[idx]['o'] print(idx, g.degree(idx)) @@ -62,8 +52,8 @@ def run(self, ips, imgs, para = None): edges.append([comid, s, e, round(eds[i]['weight']*k, 2)]) comid += 1 - IPy.show_table(pd.DataFrame(nodes, columns=ntitles), ips.title+'-nodes') - IPy.show_table(pd.DataFrame(edges, columns=etitles), ips.title+'-edges') + self.app.show_table(pd.DataFrame(nodes, columns=ntitles), ips.title+'-nodes') + self.app.show_table(pd.DataFrame(edges, columns=etitles), ips.title+'-edges') class Sumerise(Simple): title = 'Graph Summarise' @@ -74,7 +64,7 @@ class Sumerise(Simple): def load(self, ips): if not isinstance(ips.data, nx.MultiGraph): - IPy.alert("Please build graph!"); + self.app.alert("Please build graph!"); return False; return True; @@ -82,7 +72,7 @@ def run(self, ips, imgs, para = None): titles = ['PartID', 'Noeds', 'Edges', 'TotalLength', 'Density', 'AveConnect'] k, unit = ips.unit - gs = nx.connected_component_subgraphs(ips.data, False) if para['parts'] else [ips.data] + gs = nx.connected_components(ips.data, False) if para['parts'] else [ips.data] comid, datas = 0, [] for g in gs: sl = 0 @@ -91,7 +81,7 @@ def run(self, ips, imgs, para = None): datas.append([comid, g.number_of_nodes(), g.number_of_edges(), round(sl*k, 2), round(nx.density(g), 2), round(nx.average_node_connectivity(g),2)][1-para['parts']:]) comid += 1 - IPy.show_table(pd.DataFrame(datas, columns=titles[1-para['parts']:]), ips.title+'-graph') + self.app.show_table(pd.DataFrame(datas, columns=titles[1-para['parts']:]), ips.title+'-graph') class CutBranch(Filter): title = 'Cut Branch' @@ -103,7 +93,7 @@ class CutBranch(Filter): def load(self, ips): if not isinstance(ips.data, nx.MultiGraph): - IPy.alert("Please build graph!"); + self.app.alert("Please build graph!"); return False; self.buf = ips.data return True; @@ -135,7 +125,7 @@ class RemoveIsolate(Filter): def load(self, ips): if not isinstance(ips.data, nx.MultiGraph): - IPy.alert("Please build graph!"); + self.app.alert("Please build graph!"); return False; return True; @@ -145,7 +135,7 @@ def run(self, ips, snap, img, para = None): if len(g[n])==0: g.remove_node(n) img *= 0 sknw.draw_graph(img, g) - ips.mark = Mark(ips.data) + ips.mark = graph_mark(ips.data) class Remove2Node(Simple): title = 'Remove 2Path Node' @@ -153,7 +143,7 @@ class Remove2Node(Simple): def load(self, ips): if not isinstance(ips.data, nx.MultiGraph): - IPy.alert("Please build graph!"); + self.app.alert("Please build graph!"); return False; return True; @@ -174,7 +164,7 @@ def run(self, ips, imgs, para = None): g.add_edge(k1, k2, pts=pts, weight=l) ips.img[:] = 0 sknw.draw_graph(ips.img, g) - ips.mark = Mark(ips.data) + ips.mark = graph_mark(ips.data) @jit(nopython=True) def floodfill(img, x, y): @@ -203,10 +193,10 @@ def floodfill(img, x, y): class CutROI(Filter): title = 'Cut By ROI' - note = ['8-bit', 'not_slice', 'not_channel', 'auto_snap', 'preview'] + note = ['8-bit', 'req_roi', 'not_slice', 'not_channel', 'auto_snap', 'preview'] def run(self, ips, snap, img, para = None): - msk = ips.get_msk(3) * (img>0) + msk = ips.mask(3) * (img>0) r,c = np.where(msk) for x,y in zip(c,r): if img[y,x]>0: @@ -222,7 +212,7 @@ class ShortestPath(Simple): def load(self, ips): if not isinstance(ips.data, nx.MultiGraph): - IPy.alert("Please build graph!"); + self.app.alert("Please build graph!"); return False; return True; @@ -235,10 +225,12 @@ def run(self, ips, imgs, para = None): pts = sorted([(i['weight'], i['pts']) for i in ps]) paths.append(((s,e), pts[0])) sknw.draw_graph(ips.img, ips.data) + for i in paths: ips.img[i[1][1][:,0], i[1][1][:,1]] = 255 - IPy.write('%s-%s:%.4f'%(i[0][0], i[0][1], i[1][0]), 'ShortestPath') - IPy.write('Nodes:%s, Length:%.4f'%(len(nodes), sum([i[1][0] for i in paths])), 'ShortestPath') + + data = [(a[0], a[1], b[0]) for a,b in paths] + self.app.show_table(pd.DataFrame(data, columns=['from','to','l']), 'shortest-path') diff --git a/imagepy/menus/Analysis/label_plg.py b/imagepy/menus/Analysis/label_plg.py index b8a98624..d0ff2ccf 100644 --- a/imagepy/menus/Analysis/label_plg.py +++ b/imagepy/menus/Analysis/label_plg.py @@ -6,10 +6,9 @@ import numpy as np from scipy.ndimage import label, generate_binary_structure from skimage.segmentation import find_boundaries -from imagepy import IPy -from imagepy.core import ImagePlus from imagepy.core.engine import Filter, Simple from imagepy.ipyalg.graph import connect, render +from sciapp.object import Image class Label(Simple): title = 'Label Image' @@ -27,7 +26,7 @@ def run(self, ips, imgs, para = None): strc = generate_binary_structure(2, con) lab, n = label(imgs[i], strc, output = np.int32) labels.append(lab) - IPy.show_img(labels, ips.title+'-label') + self.app.show_img(labels, ips.title+'-label') class Boundaries(Simple): title = 'Mark Boundaries' @@ -47,7 +46,7 @@ def run(self, ips, imgs, para = None): bound.dtype = np.uint8 bound *= 255 labels.append(bound) - IPy.show_img(labels, ips.title+'-boundary') + self.app.show_img(labels, ips.title+'-boundary') class Render(Simple): title = 'Label Render' @@ -74,8 +73,8 @@ def run(self, ips, imgs, para = None): for j in cmap: lut[j] = cmap[j] labels.append(lut[imgs[i]]) - ips = ImagePlus(labels, ips.title+'-render') + ips = Image(labels, ips.title+'-render') ips.range = (0, para['colors']) - IPy.show_ips(ips) + self.app.show_img(ips) plgs = [Label, Boundaries, Render] \ No newline at end of file diff --git a/imagepy/menus/Analysis/statistic_plg.py b/imagepy/menus/Analysis/statistic_plg.py index c2eb5144..9c242836 100644 --- a/imagepy/menus/Analysis/statistic_plg.py +++ b/imagepy/menus/Analysis/statistic_plg.py @@ -3,17 +3,15 @@ Created on Mon Dec 26 20:34:59 2016 @author: yxl """ -from imagepy import IPy, root_dir +from imagepy import root_dir import wx, numpy as np, os from imagepy.core.engine import Filter,Simple -from imagepy.core.roi import PointRoi,LineRoi -from imagepy.core.manager import ImageManager, WindowsManager -from imagepy.ui.widgets import HistCanvas from pubsub import pub import pandas as pd from skimage.graph import route_through_array -from imagepy.core.mark import GeometryMark +from sciapp.object import mark2shp, Circles + class HistogramFrame(wx.Frame): def __init__(self, parent, title, hist): wx.Frame.__init__(self, parent, title=title, style = wx.DEFAULT_DIALOG_STYLE) @@ -72,7 +70,7 @@ class Histogram(Simple): note = ['8-bit', '16-bit', 'rgb'] def run(self, ips, imgs, para = None): - msk = ips.get_msk('in') + msk = ips.mask('in') if ips.imgtype == 'rgb': img = ips.img if msk is None else ips.img[msk] hist = [np.histogram(img.ravel()[i::3], np.arange(257))[0] for i in (0,1,2)] @@ -93,7 +91,7 @@ class Frequence(Simple): def run(self, ips, imgs, para = None): if not para['slice']: imgs = [ips.img] data = [] - msk = ips.get_msk('in') + msk = ips.mask('in') for i in range(len(imgs)): img = imgs[i] if msk is None else imgs[i][msk] maxv = img.max() @@ -110,7 +108,7 @@ def run(self, ips, imgs, para = None): dt = list(zip(*dt)) data.extend(dt) - IPy.show_table(pd.DataFrame(data, columns=titles), ips.title+'-histogram') + self.app.show_table(pd.DataFrame(data, columns=titles), ips.title+'-histogram') class Statistic(Simple): title = 'Pixel Statistic' @@ -140,12 +138,12 @@ def run(self, ips, imgs, para = None): if not self.para['slice']:imgs = [ips.img] data = [] - msk = ips.get_msk('in') + msk = ips.mask('in') for n in range(len(imgs)): img = imgs[n] if msk is None else imgs[n][msk] data.append(self.count(img, para)) self.progress(n, len(imgs)) - IPy.show_table(pd.DataFrame(data, columns=titles), ips.title+'-statistic') + self.app.show_table(pd.DataFrame(data, columns=titles), ips.title+'-statistic') class Mark: def __init__(self, data): @@ -177,9 +175,8 @@ class PointsValue(Simple): (bool, 'slice', 'slice')] def load(self, ips): - if not isinstance(ips.roi, PointRoi): - IPy.alert('a PointRoi needed!') - return False + if ips.roi.roitype != 'point': + return self.app.alert('a PointRoi needed!') return True @@ -189,18 +186,20 @@ def run(self, ips, imgs, para = None): if not para['slice']: imgs = [ips.img] titles = titles[1:] - data, mark = [], [] - pts = np.array(ips.roi.body).round().astype(np.int) + data = [] + pts = np.vstack([i.body.reshape(-1,2) for i in ips.roi.body]) + layers = {'type':'layers', 'body':{}} for n in range(len(imgs)): - xs, ys = (pts.T[:2]*k).round(2) + xs, ys = (pts.T[:2]*k).round(2).astype(np.int16) vs = imgs[n][ys, xs] cont = ([n]*len(vs), xs, ys, vs.round(2)) if not para['slice']: cont = cont[1:] data.extend(zip(*cont)) - if para['buf']:mark.append(list(zip(xs, ys, vs.round(2)))) + if para['buf']: + layers['body'][n] = {'type':'circles', 'body':list(zip(xs, ys, vs.round(2)))} self.progress(n, len(imgs)) - IPy.show_table(pd.DataFrame(data, columns=titles), ips.title+'-points') - if para['buf']:ips.mark = Mark(mark) + self.app.show_table(pd.DataFrame(data, columns=titles), ips.title+'-points') + if para['buf']:ips.mark = mark2shp(layers) class ShortRoute(Filter): title = 'Shortest Route' @@ -214,8 +213,8 @@ class ShortRoute(Filter): (list, 'type', ['white line', 'gray line', 'white line on ori'], str, 'output', '')] def load(self, ips): - if not isinstance(ips.roi, LineRoi): - return IPy.alert('LineRoi are needed!') + if ips.roi.roitype != 'line': + return self.app.alert('LineRoi are needed!') return True def run(self, ips, snap, img, para = None): @@ -226,7 +225,7 @@ def run(self, ips, snap, img, para = None): minv, maxv = ips.range routes = [] for line in ips.roi.body: - pts = np.array(list(zip(line[:-1], line[1:]))) + pts = np.array(list(zip(line.body[:-1], line.body[1:]))) for p0, p1 in pts[:,:,::-1].astype(int): indices, weight = route_through_array(img, p0, p1) routes.append(indices) diff --git a/imagepy/menus/Edit/edit_plg.py b/imagepy/menus/Edit/edit_plg.py index 3c8ad236..bb00993b 100644 --- a/imagepy/menus/Edit/edit_plg.py +++ b/imagepy/menus/Edit/edit_plg.py @@ -69,14 +69,14 @@ class Clear(Filter): note = ['req_roi', 'all', 'auto_snap', 'not_channel'] def run(self, ips, snap, img, para=None): - img[ips.get_msk()] = ColorManager.get_back(ips.channels!=3) + img[ips.mask()] = ColorManager.get_back(ips.channels!=3) class ClearOut(Filter): title = 'Clear Out' note = ['req_roi', 'all', 'auto_snap', 'not_channel'] def run(self, ips, snap, img, para=None): - img[ips.get_msk('out')] = ColorManager.get_back(ips.channels!=3) + img[ips.mask('out')] = ColorManager.get_back(ips.channels!=3) class Copy(Simple): title = 'Copy' @@ -99,14 +99,14 @@ class Sketch(Filter): view = [(int, 'width', (0,30), 0, 'width', 'pix')] def run(self, ips, snap, img, para = None): - img[ips.get_msk(para['width'])] = ColorManager.get_front(ips.channels!=3) + img[ips.mask(para['width'])] = ColorManager.get_front(ips.channels!=3) class Fill(Filter): title = 'Fill' note = ['req_roi', 'all', 'auto_snap', 'not_channel'] def run(self, ips, snap, img, para=None): - img[ips.get_msk()] = ColorManager.get_front(ips.channels!=3) + img[ips.mask()] = ColorManager.get_front(ips.channels!=3) class Undo(Simple): title = 'Undo' diff --git a/imagepy/menus/File/BMP/bmp_plgs.py b/imagepy/menus/File/BMP/bmp_plgs.py index 61a5c11a..31885945 100644 --- a/imagepy/menus/File/BMP/bmp_plgs.py +++ b/imagepy/menus/File/BMP/bmp_plgs.py @@ -2,8 +2,8 @@ from skimage.io import imread, imsave from imagepy.core.manager import ReaderManager, WriterManager -ReaderManager.add('bmp', imread) -WriterManager.add('bmp', imsave) +ReaderManager.add(name='bmp', obj=imread) +WriterManager.add(name='bmp', obj=imsave) class OpenFile(fileio.Reader): title = 'BMP Open' diff --git a/imagepy/menus/File/DAT/dat_plgs.py b/imagepy/menus/File/DAT/dat_plgs.py index 3b57523c..d89e1057 100644 --- a/imagepy/menus/File/DAT/dat_plgs.py +++ b/imagepy/menus/File/DAT/dat_plgs.py @@ -8,8 +8,8 @@ def imread(path): def imsave(path,img): np.savetxt(path,img) -ReaderManager.add('dat', imread) -WriterManager.add('dat', imsave) +ReaderManager.add(name='dat', obj=imread) +WriterManager.add(name='dat', obj=imsave) class OpenFile(fileio.Reader): title = 'DAT Open' diff --git a/imagepy/menus/File/DICOM/dicom_plgs.py b/imagepy/menus/File/DICOM/dicom_plgs.py index 6eefbf14..6e7a3f63 100644 --- a/imagepy/menus/File/DICOM/dicom_plgs.py +++ b/imagepy/menus/File/DICOM/dicom_plgs.py @@ -7,7 +7,7 @@ def imread(path): return pydicom.read_file(path, force=True).pixel_array -ReaderManager.add('dcm', imread) +ReaderManager.add(name='dcm', obj=imread) class OpenFile(fileio.Reader): title = 'DCM Open' diff --git a/imagepy/menus/File/Export/sequence_plg.py b/imagepy/menus/File/Export/sequence_plg.py index 0dfd21f2..6729c4a8 100644 --- a/imagepy/menus/File/Export/sequence_plg.py +++ b/imagepy/menus/File/Export/sequence_plg.py @@ -7,8 +7,7 @@ import wx from skimage.io import imsave from imagepy.core.engine import Simple -from imagepy.core.manager import WriterManager, ViewerManager -from imagepy import IPy, root_dir +from imagepy.core.manager import WriterManager class Plugin(Simple): title = 'Save Sequence' diff --git a/imagepy/menus/File/GIF/animate_plgs.py b/imagepy/menus/File/GIF/animate_plgs.py index 4d2fb457..17bbe06a 100644 --- a/imagepy/menus/File/GIF/animate_plgs.py +++ b/imagepy/menus/File/GIF/animate_plgs.py @@ -1,13 +1,12 @@ from imagepy.core.util import fileio from imagepy.core.engine import Simple -from imagepy import IPy, root_dir import os, imageio import numpy as np class SaveAnimate(Simple): title = 'GIF Animate Save' note = ['all'] - para={'path':root_dir, 'dur':0.2} + para={'path':'', 'dur':0.2} view = [(int, 'dur', (0.01, 10), 2, 'duration', 's')] def load(self, ips): diff --git a/imagepy/menus/File/GIF/gif_plgs.py b/imagepy/menus/File/GIF/gif_plgs.py index d88a7840..caad9f3e 100644 --- a/imagepy/menus/File/GIF/gif_plgs.py +++ b/imagepy/menus/File/GIF/gif_plgs.py @@ -2,8 +2,8 @@ from skimage.io import imread, imsave from imagepy.core.manager import ReaderManager, WriterManager -ReaderManager.add('gif', imread) -WriterManager.add('gif', imsave) +ReaderManager.add(name='gif', obj=imread) +WriterManager.add(name='gif', obj=imsave) class OpenFile(fileio.Reader): title = 'GIF Open' diff --git a/imagepy/menus/File/Import/raw_plg.py b/imagepy/menus/File/Import/raw_plg.py index 54f81079..7847a523 100644 --- a/imagepy/menus/File/Import/raw_plg.py +++ b/imagepy/menus/File/Import/raw_plg.py @@ -2,7 +2,6 @@ import numpy as np import io# urllib2 urllib.request, urllib.error, urllib.parse from skimage.io import imread -from imagepy import IPy from imagepy.core.engine import Free class Plugin(Free): diff --git a/imagepy/menus/File/Import/roi_plg.py b/imagepy/menus/File/Import/roi_plg.py index 17807984..61cd0565 100644 --- a/imagepy/menus/File/Import/roi_plg.py +++ b/imagepy/menus/File/Import/roi_plg.py @@ -6,7 +6,6 @@ import numpy as np import read_roi from imagepy.core.engine import Free -from imagepy import IPy from skimage.draw import polygon, ellipse class Plugin(Free): diff --git a/imagepy/menus/File/Import/sequence_plg.py b/imagepy/menus/File/Import/sequence_plg.py index 48a8d9a5..f9cf5ec6 100644 --- a/imagepy/menus/File/Import/sequence_plg.py +++ b/imagepy/menus/File/Import/sequence_plg.py @@ -6,9 +6,8 @@ from imagepy.core.util import fileio from skimage.io import imread -from imagepy.core.manager import ReaderManager, ViewerManager +from imagepy.core.manager import ReaderManager from imagepy.core.engine import Free -from imagepy import IPy from glob import glob import wx, os @@ -17,14 +16,14 @@ class Plugin(Free): para = {'path':'', 'start':0, 'end':0, 'step':1, 'title':'sequence'} def load(self): - self.filt = sorted(ReaderManager.get()) + self.filt = ReaderManager.gets('name') return True def show(self): filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in self.filt]) - rst = IPy.getpath('Import sequence', filt, 'open', self.para) - if not rst: return rst - + rst = self.app.getpath('Import sequence', self.filt, 'open') + if rst is None: return rst + self.para['path'] = rst files = self.getfiles(self.para['path']) nfs = len(files) self.para['end'] = nfs-1 @@ -32,7 +31,7 @@ def show(self): (int, 'start', (0, nfs-1), 0, 'Start', '0~{}'.format(nfs-1)), (int, 'end', (0, nfs-1), 0, 'End', '0~{}'.format(nfs-1)), (int, 'step', (0, nfs-1), 0, 'Step', '')] - return IPy.get_para('Import sequence', self.view, self.para) + return self.app.show_para('Import sequence', self.view, self.para) def getfiles(self, name): p,f = os.path.split(name) @@ -54,20 +53,14 @@ def readimgs(self, names, read, shape, dtype): def run(self, para = None): fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) - read = ReaderManager.get(fe[1:]) - view = ViewerManager.get(fe[1:]) - - try: - img = read(para['path']) - except: - IPy.alert('unknown img format!') - return - + read = ReaderManager.get(name=fe[1:]) + try: img = read(para['path']) + except: return self.app.alert('unknown img format!') files = self.getfiles(para['path']) files.sort() imgs = self.readimgs(files[para['start']:para['end']+1:para['step']], read, img.shape, img.dtype) - view(imgs, para['title']) + self.app.show('imgs', imgs, para['title']) if __name__ == '__main__': print(Plugin.title) diff --git a/imagepy/menus/File/JPG/jpg_plgs.py b/imagepy/menus/File/JPG/jpg_plgs.py index 6a23ed88..177a75c6 100644 --- a/imagepy/menus/File/JPG/jpg_plgs.py +++ b/imagepy/menus/File/JPG/jpg_plgs.py @@ -2,10 +2,10 @@ from imageio import imread, imsave from imagepy.core.manager import ReaderManager, WriterManager -ReaderManager.add('jpg', imread) -WriterManager.add('jpg', imsave) -ReaderManager.add('jpeg', imread) -WriterManager.add('jpeg', imsave) +ReaderManager.add(name='jpg', obj=imread) +WriterManager.add(name='jpg', obj=imsave) +ReaderManager.add(name='jpeg', obj=imread) +WriterManager.add(name='jpeg', obj=imsave) class OpenFile(fileio.Reader): title = 'JPG Open' diff --git a/imagepy/menus/File/MAT/mat_plgs.py b/imagepy/menus/File/MAT/mat_plgs.py index 378a5298..4d4f93dd 100644 --- a/imagepy/menus/File/MAT/mat_plgs.py +++ b/imagepy/menus/File/MAT/mat_plgs.py @@ -1,11 +1,10 @@ from imagepy.core.util import fileio from scipy.io import savemat, loadmat from imagepy.core.manager import ReaderManager, WriterManager -from imagepy import IPy import os -ReaderManager.add('mat', lambda path: loadmat(path)['img']) -WriterManager.add('mat', lambda path, img: savemat(path, {'img':img})) +ReaderManager.add(name='mat', obj=lambda path: loadmat(path)['img']) +WriterManager.add(name='mat', obj=lambda path, img: savemat(path, {'img':img})) class OpenFile(fileio.Reader): title = 'Mat Open' diff --git a/imagepy/menus/File/Numpy/ndarray_plgs.py b/imagepy/menus/File/Numpy/ndarray_plgs.py index d2a0957b..527c3762 100644 --- a/imagepy/menus/File/Numpy/ndarray_plgs.py +++ b/imagepy/menus/File/Numpy/ndarray_plgs.py @@ -1,11 +1,10 @@ from imagepy.core.util import fileio import numpy as np from imagepy.core.manager import ReaderManager, WriterManager -from imagepy import IPy import os -ReaderManager.add('npy', np.load) -WriterManager.add('npy', np.save) +ReaderManager.add(name='npy', obj=np.load) +WriterManager.add(name='npy', obj=np.save) class OpenFile(fileio.Reader): title = 'Numpy Open' diff --git a/imagepy/menus/File/Open Recent/recent_plgs.py b/imagepy/menus/File/Open Recent/recent_plgs.py index 03805c39..41973ade 100644 --- a/imagepy/menus/File/Open Recent/recent_plgs.py +++ b/imagepy/menus/File/Open Recent/recent_plgs.py @@ -1,11 +1,3 @@ -# -*- coding: utf-8 -*- -""" -Created on Mon Dec 5 02:38:04 2016 -@author: yxl -""" -from imagepy.core import manager -from imagepy.core.engine import Macros -from imagepy import IPy from imagepy.core.util import fileio plgs = fileio.rlist \ No newline at end of file diff --git a/imagepy/menus/File/PNG/png_plgs.py b/imagepy/menus/File/PNG/png_plgs.py index b913676e..a73b8fa5 100644 --- a/imagepy/menus/File/PNG/png_plgs.py +++ b/imagepy/menus/File/PNG/png_plgs.py @@ -2,8 +2,8 @@ from skimage.io import imread, imsave from imagepy.core.manager import ReaderManager, WriterManager -ReaderManager.add('png', imread) -WriterManager.add('png', imsave) +ReaderManager.add(name='png', obj=imread) +WriterManager.add(name='png', obj=imsave) class OpenFile(fileio.Reader): title = 'PNG Open' diff --git a/imagepy/menus/File/Samples ImageJ/ijsample_plgs.py b/imagepy/menus/File/Samples ImageJ/ijsample_plgs.py index 9a2c3cf5..b70f793e 100644 --- a/imagepy/menus/File/Samples ImageJ/ijsample_plgs.py +++ b/imagepy/menus/File/Samples ImageJ/ijsample_plgs.py @@ -1,13 +1,7 @@ from skimage.io import imread -import sys -if sys.version_info[0]==2: - from urllib2 import urlopen - from cStringIO import StringIO -else: - from urllib.request import urlopen - from io import BytesIO as StringIO +from urllib.request import urlopen +from io import BytesIO as StringIO -from imagepy import IPy from imagepy.core.engine import Free class IJImg(Free): @@ -21,9 +15,9 @@ def run(self, para = None): response = urlopen('http://imagej.net/images/'+self.name) stream = StringIO(response.read()) img = imread(stream) - IPy.show_img([img], self.title) + self.app.show_img([img], self.title) except Exception as e: - IPy.write('Open url failed!\tErrof:%s'%sys.exc_info()[1]) + self.app.write('Open url failed!\tErrof:%s'%sys.exc_info()[1]) plgs = [IJImg(*i) for i in [('Leaf 36K', 'leaf.jpg'), ('Lena 68K', 'lena.jpg'), ('MRI Head 47K', 'mri.gif'), ('AuPbSn 40 56K', 'AuPbSn40.jpg'), ('Blob 356K', 'blobs.gif'), ('Baboon 56K', 'baboon.jpg'), diff --git a/imagepy/menus/File/Samples Local/samples_plgs.py b/imagepy/menus/File/Samples Local/samples_plgs.py index 39e01158..abe0ef0a 100644 --- a/imagepy/menus/File/Samples Local/samples_plgs.py +++ b/imagepy/menus/File/Samples Local/samples_plgs.py @@ -1,4 +1,3 @@ -from imagepy import IPy from imagepy.core.engine import Free from skimage import data from scipy import misc @@ -14,14 +13,13 @@ def __init__(self, title): def run(self, para = None): img = self.data() if isinstance(img, tuple): - return IPy.show_img(list(img), self.title) + return self.app.show_img(list(img), self.title) if img.dtype == np.bool: img.dtype = np.uint8 img *= 255 - IPy.show_img([img], self.title) + self.app.show_img([img], self.title) - def __call__(self): - return self + def __call__(self): return self datas = ['face', 'ascent', '-', 'binary_blobs', 'brick', 'astronaut', 'camera', 'cell', 'checkerboard', 'chelsea', 'clock', 'coffee', 'coins', diff --git a/imagepy/menus/File/TIF/tif3d_plgs.py b/imagepy/menus/File/TIF/tif3d_plgs.py index b39e4c51..49db21af 100644 --- a/imagepy/menus/File/TIF/tif3d_plgs.py +++ b/imagepy/menus/File/TIF/tif3d_plgs.py @@ -1,6 +1,5 @@ from skimage.io import imread, imsave from imagepy.core.util import fileio -from imagepy import IPy import os class Save(fileio.Writer): @@ -21,6 +20,6 @@ def run(self, para = None): imgs = imread(para['path']).transpose(2,0,1) fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) - IPy.show_img(imgs, fn) + self.app.show_img(imgs, fn) plgs = [Open, Save] \ No newline at end of file diff --git a/imagepy/menus/File/TIF/tif_plgs.py b/imagepy/menus/File/TIF/tif_plgs.py index 0084c3bf..6efb9ea5 100644 --- a/imagepy/menus/File/TIF/tif_plgs.py +++ b/imagepy/menus/File/TIF/tif_plgs.py @@ -2,9 +2,9 @@ from skimage.io import imread, imsave from imagepy.core.manager import ReaderManager, WriterManager -ReaderManager.add('tif', imread) -ReaderManager.add('tiff', imread) -WriterManager.add('tif', imsave) +ReaderManager.add(name='tif', obj=imread) +ReaderManager.add(name='tiff', obj=imread) +WriterManager.add(name='tif', obj=imsave) class OpenFile(fileio.Reader): title = 'TIF Open' diff --git a/imagepy/menus/File/exit_plg.py b/imagepy/menus/File/exit_plg.py index e8f78c11..9f3ebcff 100644 --- a/imagepy/menus/File/exit_plg.py +++ b/imagepy/menus/File/exit_plg.py @@ -3,7 +3,6 @@ Created on Mon Dec 5 05:43:50 2016 @author: yxl """ -from imagepy import IPy from imagepy.core.engine import Free class Plugin(Free): diff --git a/imagepy/menus/File/new_plg.py b/imagepy/menus/File/new_plg.py index be882d01..e08c1f68 100644 --- a/imagepy/menus/File/new_plg.py +++ b/imagepy/menus/File/new_plg.py @@ -4,13 +4,8 @@ @author: yxl """ - -import wx,os -from imagepy.ui.canvasframe import CanvasFrame -import numpy as np -from imagepy import IPy - from imagepy.core.engine import Free +import numpy as np class Plugin(Free): title = 'New' @@ -28,7 +23,7 @@ def run(self, para = None): slices = para['slice'] shape = (h,w,channels) if channels!=1 else (h,w) imgs = [np.zeros(shape, dtype=np.uint8) for i in range(slices)] - IPy.show_img(imgs, para['name']) + self.app.show_img(imgs, para['name']) if __name__ == '__main__': print(Plugin.title) diff --git a/imagepy/menus/File/open_plg.py b/imagepy/menus/File/open_plg.py index 7de84d04..a9041b9a 100644 --- a/imagepy/menus/File/open_plg.py +++ b/imagepy/menus/File/open_plg.py @@ -1,15 +1,10 @@ import wx,os,sys from skimage.io import imread -if sys.version_info[0]==2: - from urllib2 import urlopen - from cStringIO import StringIO -else: - from urllib.request import urlopen - from io import BytesIO as StringIO +from urllib.request import urlopen +from io import BytesIO as StringIO from imagepy.core import manager -from imagepy import IPy from imagepy.core.engine import Free from imagepy.core.util import fileio from imagepy.core.manager import ReaderManager @@ -18,7 +13,7 @@ class OpenFile(fileio.Reader): title = 'Open' def load(self): - self.filt = sorted(ReaderManager.get(tag=None)) + self.filt = [i for i in sorted(ReaderManager.gets('name'))] return True class OpenUrl(Free): @@ -35,9 +30,10 @@ def run(self, para = None): ## TODO: Fixme! stream = StringIO(response.read()) img = imread(stream) - IPy.show_img([img], fn) + self.app.show_img([img], fn) except Exception as e: - IPy.write('Open url failed!\tErrof:%s'%sys.exc_info()[1]) + print(self.app) + self.app.show_txt('Open url failed!\tErrof:%s'%sys.exc_info()[1]) plgs = [OpenFile, OpenUrl] diff --git a/imagepy/menus/File/save_plg.py b/imagepy/menus/File/save_plg.py index a6a5f1cf..52a60268 100644 --- a/imagepy/menus/File/save_plg.py +++ b/imagepy/menus/File/save_plg.py @@ -4,15 +4,14 @@ @author: yxl """ from imagepy.core.util import fileio -from imagepy import IPy, root_dir -from imagepy.core.manager import WriterManager, ImageManager, WindowsManager +from imagepy.core.manager import WriterManager from imagepy.core.engine import Simple class SaveImage(fileio.Writer): title = 'Save' def load(self, ips): - self.filt = sorted(WriterManager.get(tag='img')) + self.filt = [i[0] for i in sorted(WriterManager.get(tag='img'))] return True class WindowCapture(fileio.Writer): diff --git a/imagepy/menus/Help/Help_plgs.py b/imagepy/menus/Help/Help_plgs.py index 52eba731..77621776 100644 --- a/imagepy/menus/Help/Help_plgs.py +++ b/imagepy/menus/Help/Help_plgs.py @@ -3,7 +3,6 @@ Created on Sat Nov 19 14:05:12 2016 @author: yxl """ -from imagepy import IPy import webbrowser from imagepy.core.engine import Free @@ -11,18 +10,19 @@ class About(Free): title = 'About' asyn = False + def run(self, para=None): - IPy.alert('ImagePy v0.2') + self.app.alert('ImagePy v0.2') class Topic(Free): title = 'Topic' - asyn = False + def run(self, para=None): webbrowser.open('http://www.imagepy.org/document') class Home(Free): title = 'Home Page' - asyn = False + def run(self, para=None): webbrowser.open('http://imagepy.org') diff --git a/imagepy/menus/Help/Language/language_plgs.py b/imagepy/menus/Help/Language/language_plgs.py index cee4cb79..69e30ead 100644 --- a/imagepy/menus/Help/Language/language_plgs.py +++ b/imagepy/menus/Help/Language/language_plgs.py @@ -1,4 +1,3 @@ -from imagepy import IPy from imagepy.core.manager import ColorManager, LanguageManager from imagepy.core.engine import Free @@ -10,7 +9,7 @@ def __init__(self, key): #process def run(self, para = None): LanguageManager.set(self.title) - IPy.curapp.reload_plugins() + self.app.reload_plugins() def __call__(self): return self diff --git a/imagepy/menus/Image/Adjust/bleachCorrection_plg.py b/imagepy/menus/Image/Adjust/bleachCorrection_plg.py index 1771c497..4dad9999 100644 --- a/imagepy/menus/Image/Adjust/bleachCorrection_plg.py +++ b/imagepy/menus/Image/Adjust/bleachCorrection_plg.py @@ -4,7 +4,6 @@ """ from imagepy.core.engine import Simple import numpy as np -from imagepy import IPy from scipy.optimize import curve_fit from skimage.exposure import histogram_matching import matplotlib.pyplot as plt diff --git a/imagepy/menus/Image/Adjust/brightcons_plg.py b/imagepy/menus/Image/Adjust/brightcons_plg.py index 870c9340..8763dc6f 100644 --- a/imagepy/menus/Image/Adjust/brightcons_plg.py +++ b/imagepy/menus/Image/Adjust/brightcons_plg.py @@ -2,66 +2,38 @@ Created on Sun Nov 27 00:56:00 2016 @author: yxl """ - -from imagepy import IPy import numpy as np from imagepy.core.engine import Filter -from imagepy.ui.panelconfig import ParaDialog -from imagepy.ui.widgets import HistCanvas - -class ThresholdDialog(ParaDialog): - def __init__(self, parent, title, lim): - ParaDialog.__init__(self, parent, title) - self.lim = lim - - def para_check(self, para, key): - mid = 128-para['bright']/(self.lim[1]-self.lim[0])*255 - length = 255/np.tan(para['contrast']/180.0*np.pi) - self.ctrl_dic['hist'].set_lim(mid-length/2, mid+length/2) - return True +#from imagepy.ui.widgets import HistCanvas class Plugin(Filter): title = 'Bright And Constract' note = ['all', 'auto_msk', 'auto_snap', 'not_channel', 'preview'] def load(self, ips): - hist = np.histogram(self.ips.lookup(),list(range(257)))[0] - if ips.imgtype in ('8-bit', 'rgb'): - self.arange = (0, 255) - self.para = {'bright':0, 'contrast':45} - self.view = [('hist', 'hist', hist), - ('slide', 'bright', (-100,100), 0, 'Brightness'), - ('slide', 'contrast', (1,89), 0, 'Contrast')] - if 'not_slice' in self.note: - self.note.remove('not_slice') + hist = ips.histogram(chans='all', step=512) + if ips.dtype == np.uint8: + self.para = {'b_c':(0, 45)} + self.view = [('hist', 'b_c', 'bc', hist, (-255, 255), 0)] else : - self.arange = minv, maxv = ips.img.min(), ips.img.max() - self.para = {'bright':np.mean(ips.range) - np.mean(self.arange), + self.rrange = minv, maxv = ips.img.min(), ips.img.max() + self.para = {'bright':np.mean(ips.range) - np.mean(self.range), 'contrast':round(np.arctan((maxv-minv)/(ips.range[1]-ips.range[0]))/np.pi*180)} - self.view = [('hist', 'hist', hist), - ('slide', 'bright', (-(maxv-minv)/2, (maxv-minv)/2), 10, 'Brightness'), - ('slide', 'contrast', (1,89), 0, 'Contrast')] - if not 'not_slice' in self.note: - self.note.append('not_slice') + self.view = [('hist', 'b_c', 'bc', hist, (-(maxv-minv)/2, (maxv-minv)/2), 10)] + self.lut = ips.lut return True - def show(self, temp=ThresholdDialog): - dialog = lambda win, title, lim = self.ips.range:temp(win, title, lim) - return Filter.show(self, dialog) - - #process def run(self, ips, snap, img, para = None): - if not ips.imgtype in ('8-bit', 'rgb'): - mid = (self.arange[0] + self.arange[1])/2 - para['bright'] - length = (self.arange[1] - self.arange[0])/np.tan(para['contrast']/180.0*np.pi) + bright, contrast = para['b_c'] + if not ips.dtype == np.uint8: + mid = (self.arange[0] + self.arange[1])/2 - bright + length = (self.arange[1] - self.arange[0])/np.tan(contrast/180.0*np.pi) ips.range = (mid-length/2, mid+length/2) return if para == None: para = self.para - mid = 128-para['bright'] - length = 255/np.tan(para['contrast']/180.0*np.pi) - print(255/np.tan(para['contrast']/180.0*np.pi)/2) - print(mid-length/2, mid+length/2) + mid = 128-bright + length = 255/np.tan(contrast/180.0*np.pi) img[:] = snap if mid-length/2>0: np.subtract(img, mid-length/2, out=img, casting='unsafe') diff --git a/imagepy/menus/Image/Adjust/colorbalance_plg.py b/imagepy/menus/Image/Adjust/colorbalance_plg.py index ef98aa48..94db40c0 100644 --- a/imagepy/menus/Image/Adjust/colorbalance_plg.py +++ b/imagepy/menus/Image/Adjust/colorbalance_plg.py @@ -1,58 +1,25 @@ # -*- coding: utf-8 -*- -""" -Created on Fri Nov 18 22:56:50 2016 - -@author: yxl -""" -from imagepy import IPy import numpy as np from imagepy.core.engine import Filter -from imagepy.ui.panelconfig import ParaDialog -from imagepy.ui.widgets import HistCanvas -class BalanceDialog(ParaDialog): - def para_check(self, para, key): - mid = 128-para['b_red'] - length = 255/np.tan(para['c_red']/180.0*np.pi) - self.ctrl_dic['red'].set_lim(mid-length/2, mid+length/2) - mid = 128-para['b_green'] - length = 255/np.tan(para['c_green']/180.0*np.pi) - self.ctrl_dic['green'].set_lim(mid-length/2, mid+length/2) - mid = 128-para['b_blue'] - length = 255/np.tan(para['c_blue']/180.0*np.pi) - self.ctrl_dic['blue'].set_lim(mid-length/2, mid+length/2) - #self.reset() - return True - class Plugin(Filter): - title = 'Color Balance' - note = ['rgb', 'auto_msk', 'auto_snap', 'not_channel', 'preview'] - - #parameter - para = {'b_red':0, 'c_red':45,'b_green':0, 'c_green':45,'b_blue':0, 'c_blue':45} + title = 'Color Balance' + note = ['rgb', 'auto_msk', 'auto_snap', 'not_channel', 'preview'] + + para = {'bc_r':(0, 45), 'bc_g':(0, 45), 'bc_b':(0, 45)} - def load(self, ips): - hists = [np.histogram(ips.img[:,:,i],list(range(257)))[0] for i in (0,1,2)] - self. view = [('hist', 'red', hists[0]), - ('slide', 'b_red', (-100,100), 0, 'Brightness'), - ('slide', 'c_red', (1,89), 0, 'Contrast'), - ('hist', 'green', hists[0]), - ('slide', 'b_green', (-100,100), 0, 'Brightness'), - ('slide', 'c_green', (1,89), 0, 'Contrast'), - ('hist', 'blue', hists[2]), - ('slide', 'b_blue', (-100,100), 0, 'Brightness'), - ('slide', 'c_blue', (1,89), 0, 'Contrast')] - return True - - def show(self, temp=BalanceDialog): - return Filter.show(self, temp) + def load(self, ips): + hists = [ips.histogram(chans=i, step=512) for i in (0,1,2)] + self.view = [('hist', 'bc_r', 'bc', hists[0], (-255,255), 0), + ('hist', 'bc_g', 'bc', hists[1], (-255,255), 0), + ('hist', 'bc_b', 'bc', hists[2], (-255,255), 0)] + return True - #process - def run(self, ips, snap, img, para = None): - for i, c in zip([0,1,2],['red','green','blue']): - mid = 128-para['b_'+c] - length = 255/np.tan(para['c_'+c]/180.0*np.pi) - xs = np.linspace(0,255,256) - ys = 128 + (xs-mid)*(255/length) - index = np.clip(ys, 0, 255).astype(np.uint8) - img[:,:,i] = index[snap[:,:,i]] \ No newline at end of file + def run(self, ips, snap, img, para = None): + for i, c in zip([0,1,2], 'rgb'): + mid = 128-para['bc_'+c][0] + length = 255/np.tan(para['bc_'+c][1]/180.0*np.pi) + xs = np.linspace(0,255,256) + ys = 128 + (xs-mid)*(255/length) + index = np.clip(ys, 0, 255).astype(np.uint8) + img[:,:,i] = index[snap[:,:,i]] \ No newline at end of file diff --git a/imagepy/menus/Image/Adjust/colorstairs_plg.py b/imagepy/menus/Image/Adjust/colorstairs_plg.py index b7485d2a..adcd2b55 100644 --- a/imagepy/menus/Image/Adjust/colorstairs_plg.py +++ b/imagepy/menus/Image/Adjust/colorstairs_plg.py @@ -4,53 +4,27 @@ @author: yxl """ -from imagepy import IPy import numpy as np from imagepy.core.engine import Filter -from imagepy.ui.panelconfig import ParaDialog -from imagepy.ui.widgets import HistCanvas - -class StairsDialog(ParaDialog): - def para_check(self, para, key): - self.ctrl_dic['red'].set_lim(para['t1_red'], para['t2_red']) - self.ctrl_dic['green'].set_lim(para['t1_green'], para['t2_green']) - self.ctrl_dic['blue'].set_lim(para['t1_blue'], para['t2_blue']) - if key == 't1_red':para['t2_red']=max(para['t2_red'], para['t1_red']) - if key == 't2_red':para['t1_red']=min(para['t2_red'], para['t1_red']) - if key == 't1_green':para['t2_green']=max(para['t2_green'], para['t1_green']) - if key == 't2_green':para['t1_green']=min(para['t2_green'], para['t1_green']) - if key == 't1_blue':para['t2_blue']=max(para['t2_blue'], para['t1_blue']) - if key == 't2_blue':para['t1_blue']=min(para['t2_blue'], para['t1_blue']) - self.reset() - return True +#from imagepy.ui.widgets import HistCanvas class Plugin(Filter): title = 'Color Stairs' note = ['rgb', 'auto_msk', 'auto_snap', 'not_channel', 'preview'] - - #parameter - para = {'t1_red':0, 't2_red':255,'t1_green':0, 't2_green':255,'t1_blue':0, 't2_blue':255} + para = {'thre_r':(0, 255), 'thre_g':(0, 255), 'thre_b':(0, 255)} + def load(self, ips): - hists = [np.histogram(ips.img[:,:,i],list(range(257)))[0] for i in (0,1,2)] - self. view = [('hist', 'red', hists[0]), - ('slide', 't1_red', (0,255), 0, 'Low'), - ('slide', 't2_red', (0,255), 0, 'High'), - ('hist', 'green', hists[0]), - ('slide', 't1_green', (0,255), 0, 'High'), - ('slide', 't2_green', (0,255), 0, 'Low'), - ('hist', 'blue', hists[2]), - ('slide', 't1_blue', (0,255), 0, 'Low'), - ('slide', 't2_blue', (0,255), 0, 'High')] + hists = [ips.histogram(chans=i, step=512) for i in (0,1,2)] + self. view = [('hist', 'thre_r', 'lh', hists[0], (0,255), 0), + ('hist', 'thre_g', 'lh', hists[1], (0,255), 0), + ('hist', 'thre_b', 'lh', hists[2], (0,255), 0)] return True - def show(self, temp=StairsDialog): - return Filter.show(self, temp) - #process def run(self, ips, snap, img, para = None): if para == None: para = self.para - for i, c in zip([0,1,2],['red','green','blue']): - t1, t2 = para['t1_'+c], para['t2_'+c] + for i, c in zip([0,1,2],'rgb'): + t1, t2 = para['thre_'+c] xs = np.linspace(0,255,256) ys = (xs-t1)*(255/max(0.5, t2-t1)) index = np.clip(ys, 0, 255).astype(np.uint8) diff --git a/imagepy/menus/Image/Adjust/curve_plg.py b/imagepy/menus/Image/Adjust/curve_plg.py index 97720a3a..1d4b4e65 100644 --- a/imagepy/menus/Image/Adjust/curve_plg.py +++ b/imagepy/menus/Image/Adjust/curve_plg.py @@ -1,11 +1,9 @@ -from imagepy import IPy import numpy as np from imagepy.core.engine import Filter -from imagepy.ui.panelconfig import ParaDialog, widgets -from imagepy.ui.widgets import CurvePanel +#from imagepy.ui.widgets import CurvePanel from scipy import interpolate -widgets['curve'] = CurvePanel +#widgets['curve'] = CurvePanel class Plugin(Filter): title = 'Curve Adjust' @@ -13,7 +11,7 @@ class Plugin(Filter): para = {'curve': [(0,0), (255, 255)]} def load(self, ips): - hist = np.histogram(self.ips.lookup(),list(range(257)))[0] + hist = ips.histogram(chans='all', step=512) self.view = [('curve', 'curve', hist)] return True diff --git a/imagepy/menus/Image/Adjust/graystairs_plg.py b/imagepy/menus/Image/Adjust/graystairs_plg.py index 2bfc0bfc..33a6bcd1 100644 --- a/imagepy/menus/Image/Adjust/graystairs_plg.py +++ b/imagepy/menus/Image/Adjust/graystairs_plg.py @@ -3,26 +3,9 @@ Created on Sun Nov 27 00:56:00 2016 @author: yxl """ - -from imagepy import IPy import numpy as np from imagepy.core.engine import Filter -from imagepy.ui.panelconfig import ParaDialog -from imagepy.ui.widgets import HistCanvas - -class ThresholdDialog(ParaDialog): - def __init__(self, parent, title, lim): - ParaDialog.__init__(self, parent, title) - self.lim = lim - def para_check(self, para, key): - if key=='thr1':para['thr2'] = max(para['thr1'], para['thr2']) - if key=='thr2':para['thr1'] = min(para['thr1'], para['thr2']) - lim1 = 1.0 * (para['thr1'] - self.lim[0])/(self.lim[1]-self.lim[0]) - lim2 = 1.0 * (para['thr2'] - self.lim[0])/(self.lim[1]-self.lim[0]) - self.ctrl_dic['hist'].set_lim(lim1*255, lim2*255) - self.reset() - return True class Plugin(Filter): title = 'Gray Stairs' @@ -30,36 +13,26 @@ class Plugin(Filter): arange = (0,255) def load(self, ips): - hist = np.histogram(self.ips.lookup(),list(range(257)))[0] - if ips.imgtype in ('8-bit', 'rgb'): - self.para = {'thr1':0, 'thr2':255} - self.view = [('hist', 'hist', hist), - ('slide', 'thr1', (0,255), 0, 'Low'), - ('slide', 'thr2', (0,255), 0, 'High')] - if 'not_slice' in self.note: - self.note.remove('not_slice') + hist = ips.histogram(chans='all', step=512) + if ips.dtype == np.uint8: + self.para = {'thre_lh':(0, 255)} + self.view = [('hist', 'thre_lh', 'lh', hist, (0,255), 0)] else : - self.arange = minv, maxv = ips.img.min(), ips.img.max() - self.para = {'thr1':ips.range[0], 'thr2':ips.range[1]} - self.view = [('hist', 'hist', hist), - ('slide', 'thr1', (minv, maxv), 10, 'Low'), - ('slide', 'thr2', (minv, maxv), 10, 'High')] - if not 'not_slice' in self.note: - self.note.append('not_slice') + self.para = {'thre_lh':(ips.range[0], ips.range[1])} + self.view = [('hist', 'thre_lh', 'lh', hist, ips.range, 10)] + self.arange = ips.range + #if not 'not_slice' in self.note: + # self.note.append('not_slice') return True - - def show(self, temp=ThresholdDialog): - dialog = lambda win, title, lim = self.ips.range:temp(win, title, lim) - return Filter.show(self, dialog) #process def run(self, ips, snap, img, para = None): - if not ips.imgtype in ('8-bit', 'rgb'): - ips.range = (para['thr1'], para['thr2']) + if not ips.dtype != np.uint8: + ips.range = para['thre_lh'] return img[:] = snap np.subtract(img, para['thr1'], out=img, casting='unsafe') k = 255.0/max(para['thr2']-para['thr1'], 1e-10) np.multiply(img, k, out=img, casting='unsafe') - img[snappara['thr2']] = 255 \ No newline at end of file + img[snappara['thre_lh'][1]] = 255 \ No newline at end of file diff --git a/imagepy/menus/Image/Adjust/histogram_plgs.py b/imagepy/menus/Image/Adjust/histogram_plgs.py index 2803e3e1..b02cfa88 100644 --- a/imagepy/menus/Image/Adjust/histogram_plgs.py +++ b/imagepy/menus/Image/Adjust/histogram_plgs.py @@ -3,11 +3,8 @@ Created on Sun Nov 27 00:56:00 2016 @author: yxl """ - -from imagepy import IPy import numpy as np from imagepy.core.engine import Filter, Simple -from imagepy.core.manager import ImageManager def like(hist1, hist2): hist1 = np.cumsum(hist1)/hist1.sum() @@ -20,7 +17,7 @@ def like(hist1, hist2): i2+=1 return hist -def match(img1, img2): +def match(img2, img1): if img1.ndim == 2: temp = np.histogram(img1, np.arange(257))[0] if img2.ndim == 2: @@ -55,37 +52,17 @@ def run(self, ips, snap, img, para = None): ahist = like(temp, hist) img[:] = ahist[img] -class Match(Simple): +class Match(Filter): """Calculator Plugin derived from imagepy.core.engine.Simple """ title = 'Histogram Match' - note = ['all'] - para = {'img1':'', 'img2':''} - - def load(self, ips): - titles = ImageManager.get_titles() - self.para['img1'] = titles[0] - self.para['img2'] = titles[0] - Match.view = [(list, 'img1', titles, str, 'template', ''), - (list, 'img2', titles, str, 'object', '')] - return True + note = ['all', 'not_channel', 'auto_snap', 'auto_msk'] + para = {'img':None} + view = [('img', 'img', 'temp', '')] - def run(self, ips, imgs, para = None): - ips1 = ImageManager.get(para['img1']) - ips2 = ImageManager.get(para['img2']) - ips2.snapshot() - - img = ips1.img - imgs = ips2.imgs - - sl1, sl2 = ips1.get_nslices(), ips2.get_nslices() - cn1, cn2 = ips1.get_nchannels(), ips2.get_nchannels() - if not(ips1.img.dtype == np.uint8 and ips2.img.dtype == np.uint8): - IPy.alert('Two image must be type of 8-bit or rgb!') - return - - for i in range(sl2): - self.progress(i, sl2) - match(img, imgs[i]) - ips2.update() + def run(self, ips, snap, img, para = None): + temp = self.app.get_img(para['img']).img + if not(ips.dtype == np.uint8 and temp.dtype == np.uint8): + return self.app.alert('Two image must be type of 8-bit or rgb!') + match(img, temp) plgs = [Normalize, Match] \ No newline at end of file diff --git a/imagepy/menus/Image/Adjust/normalize_plg.py b/imagepy/menus/Image/Adjust/normalize_plg.py index 99083233..f6f09854 100644 --- a/imagepy/menus/Image/Adjust/normalize_plg.py +++ b/imagepy/menus/Image/Adjust/normalize_plg.py @@ -4,7 +4,6 @@ """ from imagepy.core.engine import Simple import numpy as np -from imagepy import IPy class Plugin(Simple): title = 'Normalize' @@ -17,7 +16,7 @@ def run(self, ips, imgs, para = None): lim = np.zeros([len(imgs), 2], dtype=imgs[0].dtype) dic = {np.uint8:255, np.uint16:65535, np.float32:1, np.float64:1} - IPy.set_info('count range ...') + self.app.set_info('count range ...') for i in range(len(imgs)): lim[i] = imgs[i].min(), imgs[i].max() self.progress(i, len(imgs)) @@ -26,7 +25,7 @@ def run(self, ips, imgs, para = None): if not para['sb']: lim[:,0] = 0 rg = lim[:,0].min(), lim[:,1].max() if para['is3d']: lim[:] = rg - IPy.set_info('adjust range ...') + self.app.set_info('adjust range ...') for i in range(len(imgs)): if para['sb']: imgs[i] -= lim[i,0] np.multiply(imgs[i], maxvalue/(lim[i].ptp()), out=imgs[i], casting='unsafe') diff --git a/imagepy/menus/Image/Adjust/threshold_plg.py b/imagepy/menus/Image/Adjust/threshold_plg.py index 8edb63a6..0eba462a 100644 --- a/imagepy/menus/Image/Adjust/threshold_plg.py +++ b/imagepy/menus/Image/Adjust/threshold_plg.py @@ -3,27 +3,10 @@ Created on Fri Nov 18 22:56:50 2016 @author: yxl """ -from imagepy import IPy + import numpy as np from imagepy.core.engine import Filter -from imagepy.ui.panelconfig import ParaDialog -from imagepy.ui.widgets import HistCanvas -from ....core.manager import WindowsManager - -class ThresholdDialog(ParaDialog): - def __init__(self, parent, title, lim): - ParaDialog.__init__(self, parent, title) - self.lim = lim - def para_check(self, para, key): - if key=='thr1':para['thr2'] = max(para['thr1'], para['thr2']) - if key=='thr2':para['thr1'] = min(para['thr1'], para['thr2']) - lim1 = 1.0 * (para['thr1'] - self.lim[0])/(self.lim[1]-self.lim[0]) - lim2 = 1.0 * (para['thr2'] - self.lim[0])/(self.lim[1]-self.lim[0]) - self.ctrl_dic['hist'].set_lim(lim1*255, lim2*255) - self.reset() - return True - class Plugin(Filter): modal = False title = 'Threshold' @@ -31,46 +14,35 @@ class Plugin(Filter): arange = (0,255) def load(self, ips): - hist = np.histogram(self.ips.lookup(),list(range(257)))[0] - if ips.imgtype == '8-bit': - self.para = {'thr1':0, 'thr2':255} - self.view = [('hist', 'hist', hist), - ('slide', 'thr1', (0,255), 0, 'Low'), - ('slide', 'thr2', (0,255), 0, 'High')] + hist = ips.histogram(chans='all', step=512) + if ips.dtype == np.uint8: + self.para = {'thre_lh':(0, 255)} + self.view = [('hist', 'thre_lh', 'lh', hist, (0,255), 0)] else : - self.para = {'thr1':ips.range[0], 'thr2':ips.range[1]} - self.view = [('hist', 'hist', hist,), - ('slide', 'thr1', ips.range, 10, 'Low'), - ('slide', 'thr2', ips.range, 10, 'High')] + self.para = {'thre_lh':(ips.range[0], ips.range[1])} + self.view = [('hist', 'thre_lh', 'lh', hist, ips.range, 10)] self.arange = ips.range self.lut = ips.lut ips.lut = self.lut.copy() return True - def show(self, temp=ThresholdDialog): - dialog = lambda win, title, lim = self.ips.range:temp(win, title, lim) - return Filter.show(self, dialog) - def cancel(self, ips): ips.lut = self.lut - ips.update() def preview(self, ips, para): ips.lut[:] = self.lut - thr1 = int((para['thr1']-self.arange[0])*( + thr1 = int((para['thre_lh'][0]-self.arange[0])*( 255.0/max(1e-10, self.arange[1]-self.arange[0]))) - thr2 = int((para['thr2']-self.arange[0])*( + thr2 = int((para['thre_lh'][1]-self.arange[0])*( 255.0/max(1e-10, self.arange[1]-self.arange[0]))) # print(thr1, thr2) ips.lut[:thr1] = [0,255,0] ips.lut[thr2:] = [255,0,0] - ips.update() - #process def run(self, ips, snap, img, para = None): if para == None: para = self.para ips.lut = self.lut img[:] = 0 - img[snap>=para['thr2']] = 255 - img[snap=para['thre_lh'][1]] = 255 + img[snap=0 and para['num']=0 and para['num']0: - ips.cur-=1 + if ips.cur>0: ips.cur-=1 class Delete(Simple): title = 'Delete Slice' @@ -44,8 +41,7 @@ class Delete(Simple): #process def run(self, ips, imgs, para = None): ips.imgs.pop(ips.cur) - if ips.cur==ips.get_nslices(): - ips.cur -= 1 + if ips.cur==ips.slices: ips.cur -= 1 class Add(Simple): title = 'Add Slice' @@ -65,9 +61,7 @@ class Sub(Simple): (int, 'end', (0,1e8), 0, 'end', 'slice')] def run(self, ips, imgs, para = None): - (sc, sr), sz = ips.get_rect(), slice(para['start'], para['end']) - if ips.is3d: imgs = imgs[sz, sc, sr].copy() - else: imgs = [i[sc,sr].copy() for i in imgs[sz]] - IPy.show_img(imgs, ips.title+'-substack') + s, e = para['start'], para['end'] + self.app.show_img(ips.subimg()[s:e], ips.title+'-substack') plgs = [SetSlice, Next, Pre, Add, Delete, '-', Sub] \ No newline at end of file diff --git a/imagepy/menus/Image/Transform/Transform_plgs.py b/imagepy/menus/Image/Transform/Transform_plgs.py index f3b93343..1d2d054c 100644 --- a/imagepy/menus/Image/Transform/Transform_plgs.py +++ b/imagepy/menus/Image/Transform/Transform_plgs.py @@ -16,9 +16,9 @@ class Rotate(Filter): def run(self, ips, snap, img, para = None): if para == None: para = self.para a = para['ang']/180.0*np.pi - o = np.array(ips.size)*0.5 + o = np.array(ips.shape)*0.5 if ips.roi!=None: - box = ips.roi.get_box() + box = ips.roi.box o = np.array([box[1]+box[3],box[0]+box[2]])*0.5 trans = np.array([[np.cos(a),-np.sin(a)],[np.sin(a),np.cos(a)]]) offset = o-trans.dot(o) @@ -33,9 +33,9 @@ class Scale(Filter): def run(self, ips, snap, img, para = None): if para == None: para = self.para k = 1/para['zoom'] - o = np.array(ips.size)*0.5 + o = np.array(ips.shape)*0.5 if ips.roi!=None: - box = ips.roi.get_box() + box = ips.roi.box o = np.array([box[1]+box[3],box[0]+box[2]])*0.5 trans = np.array([[k,0],[0,k]]) offset = o-trans.dot(o) diff --git a/imagepy/menus/Image/Type/convert_plg.py b/imagepy/menus/Image/Type/convert_plg.py index 303605df..bb7f0cac 100644 --- a/imagepy/menus/Image/Type/convert_plg.py +++ b/imagepy/menus/Image/Type/convert_plg.py @@ -5,39 +5,33 @@ """ import numpy as np from imagepy.core.engine import Simple -from imagepy import IPy + +def trans(imgs, shp, cn, sl, rg1, rg2, tp, prog=print): + buf = np.zeros(shp, dtype=np.float32) + (x1, x2), (y1, y2) = rg1, rg2 + if x1 == x2: x1, x2 = x1-1e-8, x2+1e-8 + if y1 == y2: y1, y2 = y1-1e-8, y2+1e-8 + k, b = np.dot(np.linalg.inv([[x1,1],[x2,1]]), [[y1],[y2]]).ravel() + + rst = [None]*sl if isinstance(imgs, list) else np.zeros((sl,)+shp, dtype=tp) + for i in range(sl): + if cn == 1: buf[:] = imgs[i] + else: imgs[i].mean(axis=-1, out=buf) + if rg1 != rg2: + buf *= k + buf += b + if isinstance(imgs, list): + rst[i] = np.clip(buf, y1, y2).astype(tp) + else: np.clip(buf, y1, y2, out=tp) + return rst class To8bit(Simple): title = '8-bit' note = ['all'] def run(self, ips, imgs, para = None): - if ips.imgtype == '8-bit': return - n = ips.get_nslices() - if ips.is3d: - if ips.imgtype == 'rgb': - img8 = np.zeros((n,) + ips.size, dtype=np.uint8) - for i in range(n): - self.progress(i, len(imgs)) - img8[i] = imgs[i].mean(axis=2) - else: - minv, maxv = ips.get_updown() - k = 255.0/(max(1e-8, maxv-minv)) - bf = np.clip(imgs, minv, maxv) - img8 = ((bf - minv) * k).astype(np.uint8) - else: - img8 = [] - minv, maxv = ips.get_updown() - for i in range(n): - self.progress(i, len(imgs)) - - if ips.imgtype == 'rgb': - img8.append(imgs[i].mean(axis=2).astype(np.uint8)) - else: - k = 255.0/(max(1e-8, maxv-minv)) - bf = np.clip(imgs[i], minv, maxv) - img8.append(((bf - minv) * k).astype(np.uint8)) - ips.set_imgs(img8) + if ips.dtype == np.uint8 and ips.channels == 1: return + ips.set_imgs(trans(imgs, ips.shape, ips.channels, ips.slices, ips.range, (0,255), np.uint8)) class ToRGB(Simple): title = 'RGB' @@ -74,95 +68,32 @@ class ToUint16(Simple): note = ['all'] def run(self, ips, imgs, para = None): - if ips.imgtype == '16-bit': return - n = ips.get_nslices() - if ips.is3d: - if ips.imgtype == 'rgb': - img16 = imgs.mean(axis=3, dtype=np.uint16) - else: - img16 = np.clip(imgs, 0, 65535).astype(np.uint16) - else: - img16 = [] - minv, maxv = ips.get_updown() - for i in range(n): - self.progress(i, len(imgs)) - if ips.imgtype == 'rgb': - img16.append(imgs[i].mean(axis=2).astype(np.uint16)) - else: - k = 255.0/(max(1e-10, maxv-minv)) - img16.append(np.clip(imgs[i], 0, 65535).astype(np.uint16)) - ips.set_imgs(img16) + if ips.dtype == np.uint16 and ips.channels == 1: return + ips.set_imgs(trans(imgs, ips.shape, ips.channels, ips.slices, ips.range, (0,65535), np.uint16)) + class ToInt32(Simple): title = '32-bit int' note = ['all'] def run(self, ips, imgs, para = None): - if ips.imgtype == '32-int': return - n = ips.get_nslices() - if ips.is3d: - if ips.imgtype == 'rgb': - img32 = imgs.mean(axis=3, dtype=np.int32) - else: - img32 = imgs.astype(np.int32) - else: - img32 = [] - minv, maxv = ips.get_updown() - for i in range(n): - self.progress(i, len(imgs)) - if ips.imgtype == 'rgb': - img32.append(imgs[i].mean(axis=2).astype(np.int32)) - else: - k = 255.0/(max(1e-10, maxv-minv)) - img32.append(imgs[i].astype(np.int32)) - ips.set_imgs(img32) + if ips.dtype == np.int32 and ips.channels == 1: return + ips.set_imgs(trans(imgs, ips.shape, ips.channels, ips.slices, ips.range, ips.range, np.int32)) class ToFloat32(Simple): title = '32-bit float' note = ['all'] def run(self, ips, imgs, para = None): - if ips.imgtype == '32-float': return - n = ips.get_nslices() - if ips.is3d: - if ips.imgtype == 'rgb': - img32 = imgs.mean(axis=3, dtype=np.float32) - else: - img32 = imgs.astype(np.float32) - else: - img32 = [] - minv, maxv = ips.get_updown() - for i in range(n): - self.progress(i, len(imgs)) - if ips.imgtype == 'rgb': - img32.append(imgs[i].mean(axis=2).astype(np.float32)) - else: - k = 255.0/(max(1e-10, maxv-minv)) - img32.append(imgs[i].astype(np.float32)) - ips.set_imgs(img32) + if ips.dtype == np.float32 and ips.channels == 1: return + ips.set_imgs(trans(imgs, ips.shape, ips.channels, ips.slices, ips.range, ips.range, np.float32)) class ToFloat64(Simple): title = '64-bit float' note = ['all'] def run(self, ips, imgs, para = None): - if ips.imgtype == '64-bit': return - n = ips.get_nslices() - if ips.is3d: - if ips.imgtype == 'rgb': - img64 = imgs.mean(axis=3, dtype=np.float64) - else: - img64 = imgs.astype(np.float64) - else: - img64 = [] - minv, maxv = ips.get_updown() - for i in range(n): - self.progress(i, len(imgs)) - if ips.imgtype == 'rgb': - img64.append(imgs[i].mean(axis=2).astype(np.float64)) - else: - k = 255.0/(max(1e-10, maxv-minv)) - img64.append(imgs[i].astype(np.float64)) - ips.set_imgs(img64) + if ips.dtype == np.float64 and ips.channels == 1: return + ips.set_imgs(trans(imgs, ips.shape, ips.channels, ips.slices, ips.range, ips.range, np.float64)) plgs = [To8bit, ToRGB, '-', ToUint16, ToInt32, ToFloat32, ToFloat64] \ No newline at end of file diff --git a/imagepy/menus/Image/Type/tostack_plg.py b/imagepy/menus/Image/Type/tostack_plg.py index 76133ed7..2a03c360 100644 --- a/imagepy/menus/Image/Type/tostack_plg.py +++ b/imagepy/menus/Image/Type/tostack_plg.py @@ -5,16 +5,21 @@ """ import numpy as np from imagepy.core.engine import Simple + class ToStack(Simple): title = 'Trans to Stack' note = ['all','no_change','req_stack'] def run(self, ips, imgs, para = None): - imgstack = np.zeros((ips.get_nslices(),) + imgs[0].shape, dtype=ips.dtype) + ips.imgs = np.asarray(imgs) + ''' + ips.imgs = np.array() + imgstack = np.zeros((ips.slices,) + ips.shape, dtype=ips.dtype) for i in range(ips.get_nslices()): imgstack[i] = ips.imgs[i] ips.imgs = imgstack ips.is3d = True + ''' class ToList(Simple): title = 'Trans to List' @@ -22,6 +27,5 @@ class ToList(Simple): def run(self, ips, imgs, para = None): ips.imgs = list(imgs) - ips.is3d = False plgs = [ToStack, ToList] \ No newline at end of file diff --git a/imagepy/menus/Image/background_plg.py b/imagepy/menus/Image/background_plg.py index 4fe90636..ef8af0d5 100644 --- a/imagepy/menus/Image/background_plg.py +++ b/imagepy/menus/Image/background_plg.py @@ -1,51 +1,35 @@ -# -*- coding: utf-8 -*- -""" -Created on Thu Dec 1 01:22:19 2016 -@author: yxl -""" -from imagepy.core.manager import ImageManager -from imagepy import IPy import numpy as np +from sciapp.object import Image from imagepy.core.engine import Simple class SetBackground(Simple): - """Calculator Plugin derived from imagepy.core.engine.Simple """ title = 'Set Background' note = ['all'] - para = {'img':None,'op':'Mean', 'k':0.5, 'kill':False} + para = {'img':None,'mode':'msk', 'k':0.5, 'kill':False} view = [('img','img', 'background', '8-bit'), - (list, 'op', ['Mean', 'Clip'], str, 'mode', ''), - (float, 'k', (0,1), 1, 'blender', ''), + (list, 'mode', ['set', 'min', 'max', 'msk', 'ratial'], str, 'mode', ''), + (float, 'k', (0, 1), 1, 'ratial', ''), (bool, 'kill', 'kill')] def run(self, ips, imgs, para = None): - if para['kill']: - ips.backimg = None + if para['kill']: ips.mode, ips.back = 'set', None else: - print(ImageManager.get()) - img = ImageManager.get(para['img']).img - if img.dtype != np.uint8 or img.shape[:2] != ips.img.shape[:2]: - IPy.alert('a background image must be 8-bit and with the same size') - return - ips.backimg = img - ips.backmode = (para['k'], para['op']) - ips.update() + ips.back = self.app.get_img(para['img']) + ips.mode = para['k'] if para['mode']=='ratial' else para['mode'] class BackgroundSelf(Simple): - """Calculator Plugin derived from imagepy.core.engine.Simple """ title = 'Background Self' - note = ['8-bit', 'rgb'] - para = {'op':'Mean', 'k':0.5, 'kill':False} - view = [(list, 'op', ['Mean', 'Clip'], str, 'mode', ''), - (float, 'k', (0,1), 1, 'blender', ''), - (bool, 'kill', 'kill')] + note = ['all'] + para = {'mode':'msk', 'k':0.5} + view = [(list, 'mode', ['set', 'min', 'max', 'msk', 'ratial'], str, 'mode', ''), + (float, 'k', (0, 1), 1, 'ratial', '')] def run(self, ips, imgs, para = None): - if para['kill']: - ips.backimg = None - else: - ips.backimg = ips.img.copy() - ips.backmode = (para['k'], para['op']) - ips.update() + if ips.isarray: imgs = imgs.copy() + else: imgs = [i.copy() for i in imgs] + back = Image(imgs) + back.cn, back.rg = ips.cn, ips.rg + ips.back = back + ips.mode = para['k'] if para['mode']=='ratial' else para['mode'] plgs = [SetBackground, BackgroundSelf] \ No newline at end of file diff --git a/imagepy/menus/Image/canvassize_plg.py b/imagepy/menus/Image/canvassize_plg.py index 29e035c0..8e3d1499 100644 --- a/imagepy/menus/Image/canvassize_plg.py +++ b/imagepy/menus/Image/canvassize_plg.py @@ -6,7 +6,6 @@ from imagepy.core.engine import Simple import numpy as np from imagepy.core.pixel import bliter -from imagepy import IPy class Plugin(Simple): title = 'Canvas Size' @@ -19,15 +18,15 @@ class Plugin(Simple): (list, 'ver', ['top', 'center', 'bottom'], str, 'Vertical', '')] def load(self, ips): - sp = ips.size + sp = ips.shape self.para['w'] = sp[1] self.para['h'] = sp[0] return True def run(self, ips, imgs, para = None): - old = ips.size + old = ips.shape shp = (para['w'], para['h']) - chns = ips.get_nchannels() + chns = ips.channels if chns>1:shp = (shp[1], shp[0], chns) if para['hor'] == 'left':c=0 @@ -37,7 +36,7 @@ def run(self, ips, imgs, para = None): if para['hor'] == 'right':c=shp[1]-old[1] if para['ver'] == 'bottom':r=shp[0]-old[0] - if ips.is3d: + if ips.slices>1: s = list(imgs.shape) s[1], s[2] = shp[0], shp[1] rst = np.zeros(s, dtype=ips.dtype) @@ -52,8 +51,9 @@ def run(self, ips, imgs, para = None): bliter.blit(rst[-1], imgs[i], c, r) ips.roi = None ips.set_imgs(rst) - if ips.backimg is None: return + ''' + if ips.back is None: return nbc = np.zeros(shp, dtype=np.uint8) bliter.blit(nbc, ips.backimg, c, r) ips.backimg = nbc - + ''' diff --git a/imagepy/menus/Image/crop_plg.py b/imagepy/menus/Image/crop_plg.py deleted file mode 100644 index ae14ca07..00000000 --- a/imagepy/menus/Image/crop_plg.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Dec 7 02:32:31 2016 -@author: yxl -""" - -from imagepy.core.engine import Simple -import numpy as np - -class Plugin(Simple): - title = 'Crop' - note = ['all', 'req_roi'] - - def run(self, ips, imgs, para = None): - sc, sr = ips.get_rect() - if ips.is3d: - imgs = imgs[:, sc, sr].copy() - else: - imgs = [i[sc,sr].copy() for i in imgs] - ips.set_imgs(imgs) - #if not ips.backimg is None: - # ips.backimg = ips.backimg[sc, sr] - ips.roi = ips.roi.affine(np.eye(2), (-sr.start, -sc.start)) \ No newline at end of file diff --git a/imagepy/menus/Image/duplicate_plg.py b/imagepy/menus/Image/duplicate_plg.py index 11757f92..023b4175 100644 --- a/imagepy/menus/Image/duplicate_plg.py +++ b/imagepy/menus/Image/duplicate_plg.py @@ -5,66 +5,56 @@ """ from imagepy.core.engine import Simple -from imagepy.core import ImagePlus -from imagepy.core.manager import ImageManager +from sciapp.object import Image, ROI +from sciapp.util import offset, mark2shp import numpy as np -from imagepy import IPy class Duplicate(Simple): title = 'Duplicate' note = ['all'] - para = {'name':'Undefined','start':1,'end':2,'stack':True} - def load(self, ips): - self.slength = len(ips.imgs) - self.para['name'] = ips.title+'-copy' - self.para['end'] = self.slength - self.view = [(str, 'name', 'Name', '')] - if ips.get_nslices()>1: - self.view.append((int, 'start', - (1,self.slength),0,'Start slice','1~%d'%self.slength)) - self.view.append((int, 'end', - (1,self.slength),0,'End slice','1~%d'%self.slength)) - self.view.append((bool, 'stack', 'duplicate stack')) + if ips.slices > 1: + self.para = {'stack':True} + self.view = [(bool, 'stack', 'duplicate stack')] + else: + self.para = {'stack':True} + self.view = None return True - #process + + def run(self, ips, imgs, para = None): + if not para['stack']: imgs = [ips.img] + sli = ips.rect + imgs = [i[sli].copy() for i in imgs] + if ips.isarray: imgs = np.array(imgs) + new = Image(imgs, ips.name + '-duplicate') + if not ips.roi is None: + new.roi = ROI(mark2shp(ips.roi.to_mark())) + offset(new.roi, new.roi.box[0]*-1, new.roi.box[1]*-1) + new.roi.dirty = True + if not ips.back is None: + back = [i[sli].copy() for i in ips.back.imgs] + if ips.isarray: back = np.array(back) + back = Image(back, ips.back.name+'-duplicate') + back.cn, back.rg, back.mode = ips.back.cn, ips.back.rg, ips.back.mode + new.back, new.mode = back, ips.mode + self.app.show_img(back) + self.app.show_img(new) + +class Crop(Simple): + title = 'Crop' + note = ['all', 'req_roi'] + def run(self, ips, imgs, para = None): - name = para['name'] - print('name------------------', name) - if ips.get_nslices()==1 or self.para['stack']==False: - if ips.roi == None: - img = ips.img.copy() - ipsd = ImagePlus([img], name) - ipsd.back = ips.back - else: - img = ips.get_subimg().copy() - ipsd = ImagePlus([img], name) - box = ips.roi.get_box() - ipsd.roi = ips.roi.affine(np.eye(2), (-box[0], -box[1])) - ''' - if not ips.backimg is None: - sr, sc = ips.get_rect() - ipsd.backimg = ips.backimg[sr, sc] - ''' - elif ips.get_nslices()>1 and self.para['stack']: - imgs=imgs[para['start']-1: para['end']] - if ips.roi == None: - if ips.is3d:imgs=imgs.copy() - else:imgs = [i.copy() for i in imgs] - #backimg = ips.backimg - else: - sc, sr = ips.get_rect() - if ips.is3d: imgs=imgs[:, sc, sr].copy() - else: imgs = [i[sc,sr].copy() for i in imgs] - #if not ips.backimg is None: - # backimg = None #ips.backimg[sr, sr] - ipsd = ImagePlus(imgs, name) - if ips.roi != None: - ipsd.roi = ips.roi.affine(np.eye(2), (-sr.start, -sc.start)) - #if not ips.backimg is None: ipsd.backimg = backimg - ipsd.chan_mode = ips.chan_mode - IPy.show_ips(ipsd) + sc, sr = ips.rect + if ips.isarray: imgs = imgs[:, sc, sr].copy() + else: imgs = [i[sc,sr].copy() for i in imgs] + ips.set_imgs(imgs) + if not ips.back is None: + if ips.back.isarray: imgs = ips.back.imgs[:, sc, sr].copy() + else: imgs = [i[sc,sr].copy() for i in ips.back.imgs] + ips.back.set_imgs(imgs) + offset(ips.roi, ips.roi.box[0]*-1, ips.roi.box[1]*-1) class Rename(Simple): title = 'Rename' @@ -74,6 +64,11 @@ class Rename(Simple): view = [(str, 'name', 'name', '')] #process def run(self, ips, imgs, para = None): - ips.title = ImageManager.name(para['name']) + win = self.app.wimg_manager.get(ips.name) + self.app.img_manager.remove(ips.name) + self.app.wimg_manager.remove(ips.name) + ips.name = self.app.img_manager.name(para['name']) + self.app.img_manager.add(ips.name, ips) + self.app.wimg_manager.add(ips.name, win) -plgs = [Rename, Duplicate] \ No newline at end of file +plgs = [Rename, Duplicate, Crop] \ No newline at end of file diff --git a/imagepy/menus/Image/resize_plg.py b/imagepy/menus/Image/resize_plg.py index 57f15b6c..5bd0535e 100644 --- a/imagepy/menus/Image/resize_plg.py +++ b/imagepy/menus/Image/resize_plg.py @@ -7,7 +7,6 @@ from imagepy.core.engine import Simple import scipy.ndimage as ndimg import numpy as np -from imagepy import IPy class Plugin(Simple): title = 'Resize' @@ -22,24 +21,42 @@ class Plugin(Simple): def run(self, ips, imgs, para = None): kx, ky, kz = [para[i] for i in ('ky','kx','kz')] - size = np.round([ips.width*kx, ips.height*ky]) - w, h = size.astype(np.uint16) - if ips.is3d: - if ips.get_nchannels()>1: + size = np.round([ips.slices*kz, ips.shape[1]*kx, ips.shape[0]*ky]) + n, w, h = size.astype(np.uint16) + + buf = np.zeros((n, w, h, ips.channels), dtype=ips.dtype) + if kz==1: + for i in range(ips.slices): + img = imgs[i].reshape(ips.shape+(-1,)) + for c in range(ips.channels): + ndimg.zoom(img[:,:,c], (ky, kx), output=buf[i,:,:,c], order=para['order']) + else: + for c in range(ips.channels): + imgsc = [i.reshape(i.shape[:2]+(-1,))[:,:,c] for i in imgs] + ndimg.zoom(imgsc, (kz, kx, ky), order=para['order'], output=buf[:,:,:,c]) + + if ips.channels == 1: buf.shape = (buf.shape[:3]) + if n == 1: buf = [buf.reshape(buf.shape[1:])] + ips.set_imgs(buf) + ''' + else: + + if ips.slice>1: + if ips.channels>1: new = np.zeros(np.multiply(imgs.shape, (kz, kx, ky, 1)).round().astype(np.uint32), dtype=imgs.dtype) - for i in range(ips.get_nchannels()): + for i in range(ips.channels): ndimg.zoom(imgs[:,:,:,i], (kz, kx, ky), output=new[:,:,:,i], order=para['order']) else : new = ndimg.zoom(imgs, (kz, kx, ky), order=para['order']) else: - if ips.get_nchannels()>1: + if ips.channels>1: new = [] for i in range(len(imgs)): self.progress(i, len(imgs)) arr = np.zeros(np.multiply(imgs[i].shape, (kx, ky, 1)).round().astype(np.uint32), dtype=imgs[i].dtype) - for n in range(ips.get_nchannels()): + for n in range(ips.channels: ndimg.zoom(imgs[i][:,:,n], (kx, ky), output=arr[:,:,n], order=para['order']) new.append(arr) else : @@ -62,4 +79,5 @@ def run(self, ips, imgs, para = None): else : nbc = ndimg.zoom(backimg, (kz, kx)) print(nbc.dtype) - ips.backimg = nbc \ No newline at end of file + ips.backimg = nbc + ''' \ No newline at end of file diff --git a/imagepy/menus/Image/setscale_plg.py b/imagepy/menus/Image/setscale_plg.py index b121adf7..e71d71c0 100644 --- a/imagepy/menus/Image/setscale_plg.py +++ b/imagepy/menus/Image/setscale_plg.py @@ -1,59 +1,16 @@ -# -*- coding: utf-8 -*- - from imagepy.core.engine import Simple -from imagepy.ui.canvasframe import CanvasFrame -from imagepy.core.manager import ConfigManager -from imagepy.ui.panelconfig import ParaDialog import numpy as np -from imagepy import IPy - -def add(recent, v): - if v in recent: - idx = recent.index(v) - recent.insert(0, recent.pop(idx)) - else: - recent.insert(0, v) - if len(recent)>5: - del recent[5:] - -class ScaleDialog(ParaDialog): - def para_check(self, para, key): - if key=='recent' and para[key] != 'Recent': - k, u = para[key].split(' - ') - para['k'], para['unit'] = float(k), u - self.reset() class Plugin(Simple): title = 'Scale And Unit' note = ['all'] - recent = [] para = {'k':1.0, 'unit':'pix', 'kill':False, 'recent':'Recent'} view = [(float, 'k', (0,1000000), 2, 'per', 'pix'), (str, 'unit', 'unit', ''), - (list, 'recent', [], str, 'commen', ''), (bool, 'kill', 'kill scale')] - def show(self, temp=ScaleDialog): - return Simple.show(self, temp) - - def load(self, ips): - self.recent = ConfigManager.get('recent-units') - if self.recent == None : self.recent = ['Recent'] - else: self.recent.insert(0, 'Recent') - self.view[2] = (list, 'recent', self.recent, str, 'commen', '') - if ips.unit==None: - self.para['K'],self.para['unit'] = (1, 'pix') - else: self.para['k'], self.para['unit'] = ips.unit - return True def run(self, ips, imgs, para = None): - if para['kill'] : - ips.unit=(1,'pix') - print('huhuhu') - else : - print('hahaha') - ips.unit = (para['k'], para['unit']) - self.recent.pop(0) - add(self.recent, '%s - %s'%(para['k'], para['unit'])) - ConfigManager.set('recent-units', self.recent) \ No newline at end of file + if para['kill'] : ips.unit = 1, 'pix' + else : ips.unit = para['k'], para['unit'] \ No newline at end of file diff --git a/imagepy/menus/Kit3D/Analysis 3D/pixelstatistic_plgs.py b/imagepy/menus/Kit3D/Analysis 3D/pixelstatistic_plgs.py index cf0a0c98..1217c944 100644 --- a/imagepy/menus/Kit3D/Analysis 3D/pixelstatistic_plgs.py +++ b/imagepy/menus/Kit3D/Analysis 3D/pixelstatistic_plgs.py @@ -1,4 +1,3 @@ -from imagepy import IPy import numpy as np from imagepy.core.engine import Simple, Filter import pandas as pd @@ -32,7 +31,7 @@ def run(self, ips, imgs, para = None): titles = [i for i in titles if para[key[i]]] if para['nozero']: imgs = imgs[imgs!=0] data = self.count(imgs, para) - IPy.show_table(pd.DataFrame(data, columns=titles), ips.title+'-statistic') + self.app.show_table(pd.DataFrame(data, columns=titles), ips.title+'-statistic') class Frequence(Simple): title = 'Frequence 3D' @@ -52,6 +51,6 @@ def run(self, ips, imgs, para = None): dt = [bins[:-1].round(2), ct, (ct/ct.sum()).round(4)] dt = list(zip(*dt)) - IPy.show_table(pd.DataFrame(dt, columns=titles), ips.title+'-histogram') + self.app.show_table(pd.DataFrame(dt, columns=titles), ips.title+'-histogram') plgs = [Statistic, Frequence] \ No newline at end of file diff --git a/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py b/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py index 5f3ea574..9d5c59f9 100644 --- a/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py +++ b/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py @@ -1,4 +1,3 @@ -from imagepy import IPy import numpy as np from imagepy.core.engine import Simple, Filter from scipy.ndimage import label, generate_binary_structure diff --git a/imagepy/menus/Kit3D/Analysis 3D/surfacemeasure_plg.py b/imagepy/menus/Kit3D/Analysis 3D/surfacemeasure_plg.py index 9fd7fe4f..e1f8c89a 100644 --- a/imagepy/menus/Kit3D/Analysis 3D/surfacemeasure_plg.py +++ b/imagepy/menus/Kit3D/Analysis 3D/surfacemeasure_plg.py @@ -1,6 +1,5 @@ from imagepy.core.engine import Filter from skimage.measure import marching_cubes_lewiner, mesh_surface_area -from imagepy import IPy import numpy as np import pandas as pd diff --git a/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py b/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py index fa2369a5..95252294 100644 --- a/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py +++ b/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py @@ -1,7 +1,5 @@ from skimage.filters import frangi, sato, hessian ,meijering from imagepy.core.engine import Filter, Simple -from imagepy import IPy - class Frangi(Simple): title = 'Frangi 3D' diff --git a/imagepy/menus/Kit3D/Filters 3D/filters3d_plgs.py b/imagepy/menus/Kit3D/Filters 3D/filters3d_plgs.py index 3d1ca018..0cf8c3a4 100644 --- a/imagepy/menus/Kit3D/Filters 3D/filters3d_plgs.py +++ b/imagepy/menus/Kit3D/Filters 3D/filters3d_plgs.py @@ -84,8 +84,8 @@ def preview(self, ips, para): ips.lut[para['thr2']:] = [255,0,0] ips.update() - #process def run(self, ips, snap, img, para = None): + print('hahahaha') imgs = ips.imgs gradient = np.zeros(imgs.shape, dtype=np.float32) gradient += ndimg.sobel(imgs, axis=0, output=np.float32)**2 diff --git a/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py b/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py index c1ec1a12..57f867f2 100644 --- a/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py +++ b/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py @@ -1,11 +1,7 @@ from imagepy.core.engine import Filter, Simple -from imagepy.core.manager import ImageManager from imagepy.ipyalg.graph import sknw from skimage.morphology import skeletonize_3d from itertools import combinations - -from imagepy.core import myvi -from imagepy import IPy import networkx as nx import numpy as np import pandas as pd diff --git a/imagepy/menus/Kit3D/Viewer 3D/colorpts_plg.py b/imagepy/menus/Kit3D/Viewer 3D/colorpts_plg.py index 18525eb9..dd3be1af 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/colorpts_plg.py +++ b/imagepy/menus/Kit3D/Viewer 3D/colorpts_plg.py @@ -1,6 +1,6 @@ -from imagepy import IPy from imagepy.core.engine import Simple -from imagepy.core import myvi +from sciapp.object import Surface, MarkText +from sciapp.util import surfutil import numpy as np class Plugin(Simple): @@ -10,28 +10,21 @@ class Plugin(Simple): view = [(str, 'name', 'Name', ''), (int, 'num', (10,1024), 0, 'number', 'points'), (float, 'r', (0.1,30), 1, 'radius', '')] - - def load(self, para): - self.frame = myvi.Frame3D.figure(IPy.curapp, title='3D Canvas') - return True def run(self, ips, imgs, para = None): num,r = para['num'], para['r'] if ips.roi != None: pts = ips.img[ips.get_msk()] else: pts = ips.img.reshape((-1,3)) pts = pts[::len(pts)//num] - vts, fs, ns, cs = myvi.build_balls(pts, np.ones(len(pts))*r, pts/255) - self.frame.viewer.add_surf_asyn(para['name'], vts, fs, ns, cs) + vts, fs, ns, cs = surfutil.build_balls(pts, np.ones(len(pts))*r, pts/255) + self.app.show_mesh(Surface(vts, fs, ns, cs), para['name']) (r1,g1,b1),(r2,g2,b2) = (0,0,0),(1,1,1) rs = (r1,r2,r2,r1,r1,r1,r1,r1,r1,r2,r2,r1,r2,r2,r2,r2) gs = (g1,g1,g1,g1,g1,g2,g2,g1,g2,g2,g2,g2,g2,g1,g1,g2) bs = (b1,b1,b2,b2,b1,b1,b2,b2,b2,b2,b1,b1,b1,b1,b2,b2) - vts, fs, ns, cs = myvi.build_cube((0,0,0),(255,255,255)) + vts, fs, ns, cs = surfutil.build_cube((0,0,0),(255,255,255)) cs = list(zip(rs,gs,bs)) - self.frame.viewer.add_surf_asyn('cube', vts, fs, ns, cs, mode='grid') - self.frame.Raise() - self.frame = None - #self.frame.add_surf2d('dem', ips.img, ips.lut, scale, sigma) + self.app.show_mesh(Surface(vts, fs, ns, cs, mode='grid'), 'cube') if __name__ == '__main__': pass \ No newline at end of file diff --git a/imagepy/menus/Kit3D/Viewer 3D/demo_plgs.py b/imagepy/menus/Kit3D/Viewer 3D/demo_plgs.py index c88cff64..ae79aece 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/demo_plgs.py +++ b/imagepy/menus/Kit3D/Viewer 3D/demo_plgs.py @@ -1,11 +1,10 @@ from imagepy.core.engine import Free -from imagepy.core import myvi -from imagepy import IPy +from sciapp.object import Surface, MarkText +from sciapp.util import surfutil import numpy as np class Decoration(Free): title = 'Decoration Demo' - asyn = False def run(self, para=None): dphi, dtheta = np.pi/20.0, np.pi/20.0 @@ -15,16 +14,12 @@ def run(self, para=None): x = r*np.sin(phi)*np.cos(theta) y = r*np.cos(phi) z = r*np.sin(phi)*np.sin(theta) - vts, fs, ns, cs = myvi.build_mesh(x, y, z) - cs[:] = myvi.util.auto_lookup(vts[:,2], myvi.util.linear_color('jet'))/255 - - manager = myvi.Manager() - manager.add_surf('mesh', vts, fs, ns, cs) - myvi.Frame3D(IPy.curapp, 'Decoration Demo', manager).Show() + vts, fs, ns, cs = surfutil.build_mesh(x, y, z) + cs[:] = surfutil.auto_lookup(vts[:,2], surfutil.linear_color('jet'))/255 + self.app.show_mesh(Surface(vts, fs, ns, cs), 'decoration') class Lines(Free): title = 'Lines Demo' - asyn = False def run(self, para=None): vts = np.array([(0,0,0),(1,1,0),(2,1,0),(1,0,0)], dtype=np.float32) @@ -40,27 +35,20 @@ def run(self, para=None): y = np.sin(mu) * (1 + np.cos(n_long * mu / n_mer) * 0.5) z = np.sin(n_long * mu / n_mer) * 0.5 - vts, fs, ns, cs = myvi.build_line(x, y, z, (1, 0, 0)) - cs[:] = myvi.auto_lookup(vts[:,2], myvi.linear_color('jet'))/255 - - manager = myvi.Manager() - obj = manager.add_surf('line', vts, fs, ns, cs) - obj.set_style(mode='grid') - myvi.Frame3D(IPy.curapp, 'Colorful Lines Demo', manager).Show() + vts, fs, ns, cs = surfutil.build_line(x, y, z, (1, 0, 0)) + cs[:] = surfutil.auto_lookup(vts[:,2], surfutil.linear_color('jet'))/255 + self.app.show_mesh(Surface(vts, fs, ns, cs, mode='grid'), 'line') class Balls(Free): title = 'Random Balls Demo' - asyn = False def run(self, para=None): os = np.random.rand(30).reshape((-1,3)) rs = np.random.rand(10)/5 cs = (np.random.rand(10)*255).astype(np.uint8) - cs = myvi.linear_color('jet')[cs]/255 + cs = surfutil.linear_color('jet')[cs]/255 - vts, fs, ns, cs = myvi.build_balls(os, rs, cs) - manager = myvi.Manager() - manager.add_surf('balls', vts, fs, ns, cs) - myvi.Frame3D(IPy.curapp, 'Random Balls Demo', manager).Show() + vts, fs, ns, cs = surfutil.build_balls(os, rs, cs) + self.app.show_mesh(Surface(vts, fs, ns, cs), 'balls') plgs = [Lines, Balls, Decoration] \ No newline at end of file diff --git a/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py b/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py index b343f00d..b2fdbed2 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py +++ b/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py @@ -4,16 +4,15 @@ @author: yxl """ -from imagepy import IPy from imagepy.core.engine import Simple, Filter, Free from scipy.ndimage.filters import gaussian_filter -from imagepy.core import myvi +from sciapp.object import Surface, MarkText +from sciapp.util import surfutil class Show(Free): title = 'Show Viewer 3D' - asyn = False def run(self, para): - myvi.Frame3D.figure(IPy.curapp, title='3D Canvas').Raise() + self.app.show_mesh() class Surface2D(Simple): title = '2D Surface' @@ -23,23 +22,16 @@ class Surface2D(Simple): (int, 'scale', (1,5), 0, 'down scale', 'pix'), (int, 'sigma', (0,30), 0, 'sigma', ''), (float, 'h', (0.1,10), 1, 'scale z', '')] - - def load(self, para): - self.frame = myvi.Frame3D.figure(IPy.curapp, title='3D Canvas') - return True def run(self, ips, imgs, para = None): ds, sigma = para['scale'], para['sigma'] - vts, fs, ns, cs = myvi.build_surf2d(ips.img, ds=ds, sigma=para['sigma'], k=para['h']) - self.frame.viewer.add_surf_asyn(para['name'], vts, fs, ns, cs) - self.frame.Raise() - self.frame = None - #self.frame.add_surf2d('dem', ips.img, ips.lut, scale, sigma) + vts, fs, ns, cs = surfutil.build_surf2d(ips.img, ds=ds, sigma=para['sigma'], k=para['h']) + self.app.show_mesh(Surface(vts, fs, ns, cs), para['name']) -class Surface3D(Filter): +class Surface3D(Simple): modal = False title = '3D Surface' - note = ['8-bit', 'not_slice', 'not_channel', 'preview'] + note = ['8-bit', 'stack3d', 'preview'] para = {'name':'undifine', 'ds':2, 'thr':128, 'step':1, 'color':(0,255,0)} view = [(str, 'name', 'Name', ''), ('slide', 'thr', (0,255), 0, 'threshold'), @@ -48,10 +40,6 @@ class Surface3D(Filter): ('color', 'color', 'color', 'rgb')] def load(self, ips): - if not ips.is3d: - IPy.alert('stack3d required!') - return False - self.frame = myvi.Frame3D.figure(IPy.curapp, title='3D Canvas') self.buflut = ips.lut ips.lut = ips.lut.copy() return True @@ -59,23 +47,15 @@ def load(self, ips): def preview(self, ips, para): ips.lut[:] = self.buflut ips.lut[:para['thr']] = [255,0,0] - ips.update() - - def run(self, ips, snap, img, para = None): - imgs = ips.imgs def cancel(self, ips): ips.lut = self.buflut - ips.update() - def run(self, ips, snap, img, para = None): + def run(self, ips, imgs, para = None): ips.lut = self.buflut - print('------------', para['color']) cs = tuple([int(i/255.0) for i in para['color']]) - vts, fs, ns, cs = myvi.build_surf3d(ips.imgs, para['ds'], para['thr'], para['step'], cs) - self.frame.viewer.add_surf_asyn(para['name'], vts, fs, ns, cs) - self.frame.Raise() - self.frame = None + vts, fs, ns, cs = surfutil.build_surf3d(ips.imgs, para['ds'], para['thr'], para['step'], cs) + self.app.show_mesh(Surface(vts, fs, ns, cs), para['name']) class ImageCube(Simple): modal = False @@ -88,18 +68,12 @@ class ImageCube(Simple): (bool, 'box', 'show box'), ('color', 'color', 'box color', 'rgb')] - def load(self, para): - self.frame = myvi.Frame3D.figure(IPy.curapp, title='3D Canvas') - return True - def run(self, ips, imgs, para = None): if para['surface']: - vts, fs, ns, cs = myvi.build_img_cube(imgs, para['ds']) - obj = self.frame.viewer.add_surf_asyn(para['name']+'-surface', vts, fs, ns, cs) + vts, fs, ns, cs = surfutil.build_img_cube(imgs, para['ds']) + self.app.show_mesh(Surface(vts, fs, ns, cs), para['name']+'-surface') if para['box']: - vts, fs, ns, cs = myvi.build_img_box(imgs, para['color']) - obj = self.frame.viewer.add_surf_asyn(para['name']+'-box', vts, fs, ns, cs, mode='grid') - self.frame.Raise() - self.frame = None + vts, fs, ns, cs = surfutil.build_img_box(imgs, para['color']) + self.app.show_mesh(Surface(vts, fs, ns, cs, mode='grid'), para['name']+'-box') plgs = [Show, Surface2D, Surface3D, ImageCube] diff --git a/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py b/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py index 86fe5778..b186ffca 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py +++ b/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py @@ -1,8 +1,6 @@ from imagepy.core.engine import Table from imagepy.core.manager import ColorManager -from imagepy.core import myvi import numpy as np -from imagepy import IPy class Plugin(Table): title = 'Table Point Cloud' diff --git a/imagepy/menus/Plugins/Contribute/pmanager_wgt.py b/imagepy/menus/Plugins/Contribute/pmanager_wgt.py index 857cba1b..c0a2ee6b 100644 --- a/imagepy/menus/Plugins/Contribute/pmanager_wgt.py +++ b/imagepy/menus/Plugins/Contribute/pmanager_wgt.py @@ -5,9 +5,10 @@ @author: yxl """ import wx, os, glob, shutil, random -from imagepy import IPy, root_dir -from imagepy.core.manager import PluginsManager -from imagepy.ui.mkdownwindow import HtmlPanel, md2html +from imagepy import root_dir +from sciapp import Source +from sciwx.text import MDPad +#from imagepy.ui.mkdownwindow import HtmlPanel, md2html class VirtualListCtrl(wx.ListCtrl): def __init__(self, parent, title, data=[]): @@ -83,7 +84,7 @@ def __init__( self, parent,): self.lst_plgs.SetColumnWidth(1,100) self.lst_plgs.SetColumnWidth(2,60) self.lst_plgs.SetColumnWidth(3,60) - self.htmlpanel = HtmlPanel(self) + self.htmlpanel = MDPad(self) bSizer1.Add( self.lst_plgs, 1, wx.LEFT|wx.RIGHT|wx.EXPAND, 5 ) bSizer1.Add( bSizer3, 0, wx.EXPAND, 5 ) sizer.Add(bSizer1, 0, wx.ALL|wx.EXPAND, 0) @@ -135,13 +136,13 @@ def on_run(self, event): cont = f.read() f.close() cont = '\n'.join([i.strip() for i in cont.split('\n')]) - self.htmlpanel.SetValue((md2html(cont), '')) + self.htmlpanel.set_cont(cont) def on_install(self, event): i = self.lst_plgs.GetFirstSelected() if i==-1: return path = self.buf[i][-1]['path'] - PluginsManager.get('Install Plugins')().start( + Source.manager('plugin').get('Install Plugins')().start( {'repo':self.buf[i][-1]['path']}, self.load) def on_remove(self, event): diff --git a/imagepy/menus/Plugins/Games/crossstick_plg.py b/imagepy/menus/Plugins/Games/crossstick_plg.py index 5b514243..19f7d880 100644 --- a/imagepy/menus/Plugins/Games/crossstick_plg.py +++ b/imagepy/menus/Plugins/Games/crossstick_plg.py @@ -1,5 +1,4 @@ from imagepy.core.engine import Filter -from imagepy import IPy import numpy as np from scipy.cluster.vq import kmeans, vq diff --git a/imagepy/menus/Plugins/Games/drawstep_plg.py b/imagepy/menus/Plugins/Games/drawstep_plg.py index 34d6402e..f8c0402a 100644 --- a/imagepy/menus/Plugins/Games/drawstep_plg.py +++ b/imagepy/menus/Plugins/Games/drawstep_plg.py @@ -5,7 +5,7 @@ import numpy as np from imagepy.ipyalg.graph import sknw from imagepy.core.engine import Simple -from imagepy import IPy + def draw_pixs(img, xs, ys, color=None): mskx = (xs>=0) * (xs 100 : per = 100 diff --git a/imagepy/menus/Plugins/Macros/recorder_plg.py b/imagepy/menus/Plugins/Macros/recorder_plg.py index cca7a29c..39be2e9a 100644 --- a/imagepy/menus/Plugins/Macros/recorder_plg.py +++ b/imagepy/menus/Plugins/Macros/recorder_plg.py @@ -1,23 +1,13 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Dec 28 23:24:43 2016 +from imagepy.core.util import fileio +from imagepy.core.manager import ReaderManager -@author: yxl -""" -import wx -from imagepy.core.engine import Free, Macros -from imagepy import IPy +def readmc(path): + with open(path) as f: + return f.readlines() -class Plugin(Free): +class Plugin(fileio.Reader): title = 'Run Macros' - para = {'path':''} - - def show(self): - filt = 'Macros files (*.mc)|*.mc' - return IPy.getpath('open..', filt, 'open', self.para) - - def run(self, para = None): - f = open(para['path']) - lines = f.readlines() - f.close() - Macros('noname', lines).start() \ No newline at end of file + tag = 'macros' + filt = ['MC'] + +ReaderManager.add(name='mc', tag='macros', obj=readmc) \ No newline at end of file diff --git a/imagepy/menus/Plugins/Macros/recorder_wgt.py b/imagepy/menus/Plugins/Macros/recorder_wgt.py index c6877000..49a90460 100644 --- a/imagepy/menus/Plugins/Macros/recorder_wgt.py +++ b/imagepy/menus/Plugins/Macros/recorder_wgt.py @@ -61,15 +61,12 @@ def __init__( self, parent ): self.Bind( wx.EVT_TOOL, self.on_runlines, id = self.tol_runlines.GetId() ) - self.recording = True - - def __del__(self): - print('Recoder closed!') - + self.recording = True # Virtual event handlers, overide them in your derived class def on_open( self, event ): - dialog=wx.FileDialog(None,'wxpython Notebook(o)',style=wx.FD_OPEN) + filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in ['mc']]) + dialog=wx.FileDialog(None, 'Open Macros', '', '', filt, style=wx.FD_OPEN) if dialog.ShowModal()==wx.ID_OK: self.file=dialog.GetPath() file=open(self.file) @@ -78,9 +75,9 @@ def on_open( self, event ): dialog.Destroy() def on_save( self, event ): - print('save') if self.file=='': - dialog=wx.FileDialog(None,'wxpython Notebook(s)',style=wx.FD_SAVE) + filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in ['mc']]) + dialog=wx.FileDialog(None,'Save Macros', '', '', filt, style=wx.FD_SAVE) if dialog.ShowModal()==wx.ID_OK: self.file=dialog.GetPath() self.txt_cont.SaveFile(self.file) @@ -102,7 +99,7 @@ def on_delete( self, event ): def on_run( self, event ): cmds = self.txt_cont.GetValue().split('\n') - Macros(None, cmds).start() + Macros(None, cmds).start(self.GetParent().GetParent()) def on_record( self, event ): self.recording = True @@ -112,7 +109,7 @@ def on_pause( self, event ): def on_runlines( self, event ): cmds = self.txt_cont.GetStringSelection().split('\n') - Macros(None, cmds).start() + Macros(None, cmds).start(self.GetParent().GetParent()) def write(self, cont): if not self.recording: return diff --git a/imagepy/menus/Plugins/Manager/console_wgt.py b/imagepy/menus/Plugins/Manager/console_wgt.py index abeb112b..68985a57 100644 --- a/imagepy/menus/Plugins/Manager/console_wgt.py +++ b/imagepy/menus/Plugins/Manager/console_wgt.py @@ -3,41 +3,23 @@ from wx.py.shell import Shell import scipy.ndimage as ndimg import numpy as np -from imagepy import IPy +# from imagepy import IPy from imagepy.core.engine import Free -from imagepy.core.manager import PluginsManager +from sciapp import Source -## There is something wrong! -## To be fixed! - -def get_ips(): - ips = IPy.get_ips() - if ips is None: - print('No image opened!') - return ips - -def update(): - ips = IPy.get_ips() - if not ips is None : - ips.update='pix' +cmds = {'app':'app', 'np':np, 'ndimg':ndimg, 'update':None, 'get_img':None} class Macros(dict): def __init__(self): - for i in list(PluginsManager.plgs.keys()): + for i in Source.manager('plugin').names(): if not isinstance(i, str) or i == 'Command Line': #print(PluginsManager.plgs[i]) continue name = ''.join(list(filter(str.isalnum, i))) - ### TODO:Fixme! - #exec('self.run_%s = lambda para=None, - # plg=PluginsManager.plgs[i]:plg().start(para)'%name) - #self['run_%s'%i] = lambda para=None, plg=PluginsManager.plgs[i]:plg().start(para) - exec('self.run_%s = lambda para=None, plg=PluginsManager.plgs[i]:plg().start(para)'%name) - #exec('self._%s = PluginsManager.plgs[i]().start'%name) - print(self) + exec("self.run_%s = lambda para=None, plg=Source.manager('plugin').get(i):plg().start(cmds['app'], para)"%name) -cmds = {'IPy':IPy, 'ndimg':ndimg, 'update':update, 'curips':get_ips} + print(self) class Plugin(wx.Panel): title = 'Command Line' @@ -46,11 +28,13 @@ def __init__(self, parent): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + cmds['app'] = parent + cmds['get_img'] = lambda name=None, app=self: self.app.get_img() + cmds['update'] = lambda app=self: self.app.get_img().update() shell = Shell(self, locals=cmds) bSizer = wx.BoxSizer( wx.VERTICAL ) bSizer.Add( shell, 1, wx.EXPAND|wx.ALL, 5 ) self.SetSizer(bSizer) cmds['plgs'] = Macros() - shell.run('# numpy(np) and scipy.ndimage(ndimg) has been imported!\n') shell.run('# plgs.run_name() to call a ImagePy plugin.\n') - shell.run('# IPy is avalible here, and curips() to get the current ImagePlus, update() to redraw.\n') \ No newline at end of file + shell.run('# app is avalible here, and get_img() to get the current ImagePlus, update() to redraw.\n') \ No newline at end of file diff --git a/imagepy/menus/Plugins/Manager/plglist_wgt.py b/imagepy/menus/Plugins/Manager/plglist_wgt.py index d384a34a..a5e88c2e 100644 --- a/imagepy/menus/Plugins/Manager/plglist_wgt.py +++ b/imagepy/menus/Plugins/Manager/plglist_wgt.py @@ -5,8 +5,8 @@ @author: yxl """ import wx, os -from imagepy import IPy, root_dir -from imagepy.core.manager import PluginsManager +#from imagepy import IPy, root_dir +from sciapp import Source class VirtualListCtrl(wx.ListCtrl): def __init__(self, parent, title, data=[]): @@ -35,10 +35,12 @@ def refresh(self): class Plugin( wx.Panel ): title = 'Plugin List View' single = None + def __init__( self, parent,): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + self.app = parent bSizer1 = wx.BoxSizer( wx.VERTICAL ) bSizer2 = wx.BoxSizer( wx.HORIZONTAL ) self.m_staticText1 = wx.StaticText( self, wx.ID_ANY, "Search:", @@ -66,7 +68,7 @@ def __del__( self ): #def list_plg(self, lst, items def load(self): - lst = list(PluginsManager.plgs.values()) + lst = [i[1] for i in list(Source.manager('plugin').gets())] self.plgs = [(i.title, i.__module__) for i in lst] self.plgs.sort() self.buf = self.plgs @@ -80,4 +82,5 @@ def on_search( self, event ): self.lst_plgs.Refresh() def on_run(self, event): - PluginsManager.plgs[self.buf[event.GetIndex()][0]]().start() \ No newline at end of file + name=self.buf[event.GetIndex()][0] + Source.manager('plugin').get(name)().start(self.app) \ No newline at end of file diff --git a/imagepy/menus/Plugins/Manager/plgtree_wgt.py b/imagepy/menus/Plugins/Manager/plgtree_wgt.py index 406441b3..1a47fe40 100644 --- a/imagepy/menus/Plugins/Manager/plgtree_wgt.py +++ b/imagepy/menus/Plugins/Manager/plgtree_wgt.py @@ -7,10 +7,10 @@ from imagepy.core.engine import Free import wx,os -from imagepy import IPy, root_dir +from imagepy import root_dir from imagepy.core.loader import loader from wx.py.editor import EditorFrame -from imagepy.ui.mkdownwindow import HtmlPanel, md2html +from sciwx.text import MDPad from imagepy.core.manager import DocumentManager from glob import glob @@ -21,6 +21,7 @@ def __init__( self, parent ): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + self.app = parent bSizer1 = wx.BoxSizer( wx.HORIZONTAL ) self.tre_plugins = wx.TreeCtrl( self, wx.ID_ANY, wx.DefaultPosition, @@ -45,7 +46,7 @@ def __init__( self, parent ): bSizer4.Add( self.m_staticText3, 0, wx.ALL, 5 ) bSizer3.Add( bSizer4, 0, wx.EXPAND, 5 ) - self.txt_info = HtmlPanel( self ) + self.txt_info = MDPad( self ) bSizer3.Add( self.txt_info, 1, wx.ALL|wx.EXPAND, 5 ) @@ -94,14 +95,14 @@ def load(self): # Virtual event handlers, overide them in your derived class def on_run( self, event ): plg = self.tre_plugins.GetItemPyData(event.GetItem()) - if hasattr(plg, 'start'):plg().start() + if hasattr(plg, 'start'):plg().start(self.app) def on_select( self, event ): plg = self.tre_plugins.GetItemData(event.GetItem()) if plg!=None: self.plg = plg name = self.tre_plugins.GetItemText(event.GetItem()) - self.txt_info.SetValue((md2html(DocumentManager.get(name)), '')) + self.txt_info.set_cont(DocumentManager.get(name=name)) def on_source(self, event): ## TODO: should it be absolute path ? diff --git a/imagepy/menus/Plugins/Manager/shotcut_wgt.py b/imagepy/menus/Plugins/Manager/shotcut_wgt.py index f15d56b4..c143662c 100644 --- a/imagepy/menus/Plugins/Manager/shotcut_wgt.py +++ b/imagepy/menus/Plugins/Manager/shotcut_wgt.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- import wx, os from imagepy.core.engine import Free -from imagepy.core.manager import ShotcutManager, PluginsManager -from imagepy import IPy, root_dir +from imagepy.core.manager import ShotcutManager +from sciapp import Source +from imagepy import root_dir class VirtualListCtrl(wx.ListCtrl): def __init__(self, parent, title, data=[]): @@ -35,7 +36,7 @@ def __init__( self, parent): wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) - + self.app = parent bSizer1 = wx.BoxSizer( wx.VERTICAL ) bSizer2 = wx.BoxSizer( wx.HORIZONTAL ) self.m_staticText1 = wx.StaticText( self, wx.ID_ANY, "Search:", @@ -64,8 +65,8 @@ def __init__( self, parent): #def list_plg(self, lst, items def load(self): - lst = list(PluginsManager.plgs.values()) - self.plgs = [[i.title, ShotcutManager.get(i.title)] for i in lst] + lst = Source.manager('plugin').gets(item='name') + self.plgs = [[i, ShotcutManager.get(item='shotcut', name=i)] for i in lst] for i in self.plgs: if i[1]==None:i[1]='' self.plgs.sort() @@ -96,7 +97,7 @@ def on_select(self, event): def on_run(self, event): if self.active != event.GetIndex(): - return IPy.alert('please double click to activate an item') + return self.app.alert('please double click to activate an item') code = event.GetKeyCode() title = self.buf[event.GetIndex()][0] txt = self.buf[event.GetIndex()][1] @@ -116,10 +117,9 @@ def on_run(self, event): if len(txt)>0 and txt[-1]=='-':txt=txt[:-1] self.buf[event.GetIndex()][1] = txt self.lst_plgs.RefreshItem(event.GetIndex()) - if txt=='':ShotcutManager.rm(title) - ShotcutManager.set(title, txt) + ShotcutManager.remove(name=title) + if txt!='': ShotcutManager.add(name=title, shotcut=txt) #PluginsManager.plgs[self.buf[event.GetIndex()][0]]().start() def close(self): - print('close') - ShotcutManager.write() + ShotcutManager.write(os.path.join(root_dir,'data/shotcut.cfg')) diff --git a/imagepy/menus/Plugins/Manager/toltree_wgt.py b/imagepy/menus/Plugins/Manager/toltree_wgt.py index ca20bf9a..1c0029c8 100644 --- a/imagepy/menus/Plugins/Manager/toltree_wgt.py +++ b/imagepy/menus/Plugins/Manager/toltree_wgt.py @@ -7,10 +7,11 @@ from imagepy.core.engine import Free import wx,os -from imagepy import IPy, root_dir +from imagepy import root_dir from imagepy.core.loader import loader from wx.py.editor import EditorFrame -from imagepy.ui.mkdownwindow import HtmlPanel, md2html +#from imagepy.ui.mkdownwindow import HtmlPanel, md2html +from sciwx.text import MDPad from imagepy.core.manager import DocumentManager from glob import glob @@ -21,6 +22,7 @@ def __init__( self, parent ): wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + self.app = parent bSizer1 = wx.BoxSizer( wx.HORIZONTAL ) self.tre_plugins = wx.TreeCtrl( self, wx.ID_ANY, wx.DefaultPosition, @@ -45,7 +47,7 @@ def __init__( self, parent ): bSizer4.Add( self.m_staticText3, 0, wx.ALL, 5 ) bSizer3.Add( bSizer4, 0, wx.EXPAND, 5 ) - self.txt_info = HtmlPanel( self ) + self.txt_info = MDPad( self ) bSizer3.Add( self.txt_info, 1, wx.ALL|wx.EXPAND, 5 ) @@ -93,14 +95,14 @@ def load(self): # Virtual event handlers, overide them in your derived class def on_run( self, event ): plg = self.tre_plugins.GetItemData(event.GetItem()) - if hasattr(plg, 'start'):plg().start() + if hasattr(plg, 'start'):plg().start(self.app) def on_select( self, event ): plg = self.tre_plugins.GetItemData(event.GetItem()) if plg!=None: self.plg = plg name = self.tre_plugins.GetItemText(event.GetItem()) - self.txt_info.SetValue((md2html(DocumentManager.get(name)), '')) + self.txt_info.set_cont(DocumentManager.get(name=name)) def on_source(self, event): ## TODO: should it be absolute path ? diff --git a/imagepy/menus/Plugins/StackReg/stackreg_plgs.py b/imagepy/menus/Plugins/StackReg/stackreg_plgs.py index f0c6a443..ab78271b 100644 --- a/imagepy/menus/Plugins/StackReg/stackreg_plgs.py +++ b/imagepy/menus/Plugins/StackReg/stackreg_plgs.py @@ -1,11 +1,9 @@ from imagepy.core.engine import Filter, Simple -from imagepy import IPy from pystackreg import StackReg import numpy as np import pandas as pd from skimage import transform as tf import scipy.ndimage as ndimg -from imagepy.core.manager import TableManager class Register(Simple): title = 'Stack Register' diff --git a/imagepy/menus/Plugins/detect_plg.py b/imagepy/menus/Plugins/detect_plg.py new file mode 100644 index 00000000..b1bb0d8b --- /dev/null +++ b/imagepy/menus/Plugins/detect_plg.py @@ -0,0 +1,161 @@ +import numpy as np +from numba import jit + +def neighbors(shape): # 邻居坐标转索引内存 + dim = len(shape) + block = np.ones([3]*dim) + block[tuple([1]*dim)] = 0 + idx = np.where(block>0) + idx = np.array(idx, dtype=np.uint8).T + idx = np.array(idx-[1]*dim) + acc = np.cumprod((1,)+shape[::-1][:-1]) + return np.dot(idx, acc[::-1]) + +@jit # trans index to r, c... +def idx2rc(idx, acc): # 索引转坐标 + rst = np.zeros((len(idx), len(acc)), dtype=np.int16) + for i in range(len(idx)): + for j in range(len(acc)): + rst[i,j] = idx[i]//acc[j] + idx[i] -= rst[i,j]*acc[j] + return rst + +@jit # fill a node (may be two or more points) +def fill(img, msk, p, nbs, buf): + buf[0] = p + back = img[p] + cur = 0; s = 1; + + while curthr:continue # 大于阈值,不继续 + sta = 0 + for dp in nbs: # 获取邻居像素索引 + if img[p+dp]==img[p]:sta+=1 # 如果周围的有一样大的,标记+1 + if mode and img[p+dp]>img[p]: # 如果周围的有更大的,标记为100 + sta = 100 + break + elif not mode and img[p+dp]0: + fill(img, msk, p, nbs, buf) # 如果有若干一样大的,将一样大的区域填充(对于大块平坦区域,可以有效提升性能) + idx[s] = p # 装入极值点 + s += 1 + if s==len(idx):break # 装满退出(异常情况) + return idx[:s].copy() # 截断拷贝 + +@jit # 3 max 2 zmd b4 ptd +def filter(img, msk, idx, bur, tor, area, mode): + nbs = neighbors(img.shape) + img = img.ravel() + msk = msk.ravel() + + arg = np.argsort(img[idx])[::-1 if mode else 1] # 将极值点排序 + + for i in arg: # 从最小的点开始遍历 + if msk[idx[i]]!=3: # 如果标记位置已经不是3,则跳过 + idx[i] = 0 + continue + cur = 0; s = 1; + bur[0] = idx[i] + while cur area: # 面积超出,不符合,擦除退出 + idx[i]=0 + break + for dp in nbs: # 邻居压栈 + cp = p+dp + if msk[cp]==0 or cp==idx[i] or msk[cp] == 4: continue # 边缘不加,自己不加,已经标记不加 + if mode and img[cp] < img[idx[i]]-tor: continue # 超过水位高度不加 + if not mode and img[cp] > img[idx[i]]+tor: continue # 极小值情况 + bur[s] = cp + s += 1 + if s==msk.size//3: # 装满清空 + cut = cur//2 + msk[bur[:cut]] = 2 # 被填充的点设定为2 (占领) + bur[:s-cut] = bur[cut:] + cur -= cut + s -= cut + + if msk[cp]!=2:msk[cp] = 4 # 本次标记 + cur += 1 + msk[bur[:s]] = 2 # 将领地标记为2 + return idx # 返回有效极值坐标 + +def find_maximum(img, tor, thr, area, mode = True, mar=1): + msk = np.zeros_like(img, dtype=np.uint8) # 复制掩膜数组 + msk[tuple([slice(mar,-mar)]*img.ndim)] = 1 # 中间部分设定为1,边缘是0 + buf = np.zeros(img.size//2, dtype=np.int64) # 缓冲数组 + idx = mark(img, msk, buf, thr, mode) # 图像预标记 + acc = np.cumprod((1,)+img.shape[::-1][:-1])[::-1] + idx = filter(img, msk, idx, buf, tor, area, mode) + #idx2 = filter(img, msk.copy(), idx.copy(), buf, tor2, area, mode) + pts = idx2rc(idx[idx>0], acc) + #pts2 = idx2rc(idx2[idx2>0], acc) + return pts + +from imagepy.core.engine import Simple +from imagepy.core.mark import GeometryMark + +class Plugin(Simple): + title = 'Desys' + note = ['8-bit', 'auto_snap', 'preview'] + para = {'tol':60, 'thr':180, 'area':256, 'mode':False} + view = [(int, 'tol', (0,100), 0, 'tolerance', 'no wrong'), + # (int, 'tol2', (0,100), 0, 'tolerance2', 'no left'), + (int, 'thr', (1,255), 0, 'threshold', ''), + (int, 'area', (0,10240), 0, 'area', 'max'), + (bool, 'mode', 'white')] + + def preview(self, ips, para): + pts = find_maximum(ips.img, para['tol'], para['thr'], para['area'], para['mode']) + sure = {'type':'circles', 'color':(255,0,0), 'body':[list(i[::-1])+[20] for i in pts], 'lw':2} + ips.mark = GeometryMark(sure) + + def run(self, ips, imgs, para = None): + marks = {'type':'layers', 'body':{}} + for i in range(len(imgs)): + pts = find_maximum(imgs[i], para['tol'], para['thr'], para['area'], para['mode']) + sure = {'type':'circles', 'color':(255,0,0), 'body':[list(i[::-1])+[20] for i in pts], 'lw':2} + marks['body'][i] = sure + ips.mark = GeometryMark(marks) + +if __name__ == '__main__': + from skimage.io import imread + from scipy.ndimage import gaussian_filter + from time import time + import matplotlib.pyplot as plt + img = gaussian_filter(imread('test.png'), 0) + pts = find_maximum(img, 20, True) + start = time() + pts = find_maximum(img, 10, True) + print(time()-start) + plt.imshow(img, cmap='gray') + plt.plot(pts[:,1], pts[:,0], 'y.') + plt.show() diff --git a/imagepy/menus/Plugins/mser_plg.py b/imagepy/menus/Plugins/mser_plg.py new file mode 100644 index 00000000..b7533f8b --- /dev/null +++ b/imagepy/menus/Plugins/mser_plg.py @@ -0,0 +1,147 @@ +import scipy.ndimage as ndimg +import numpy as np +from numba import jit + +def neighbors(shape): + dim = len(shape) + block = np.ones([3]*dim) + block[tuple([1]*dim)] = 0 + idx = np.where(block>0) + idx = np.array(idx, dtype=np.uint8).T + idx = np.array(idx-[1]*dim) + acc = np.cumprod((1,)+shape[::-1][:-1]) + return np.dot(idx, acc[::-1]) + +@jit(nopython=True) # trans index to r, c... +def idx2rc(idx, acc): + rst = np.zeros((len(idx), len(acc)), dtype=np.int16) + for i in range(len(idx)): + for j in range(len(acc)): + rst[i,j] = idx[i]//acc[j] + idx[i] -= rst[i,j]*acc[j] + return rst + +@jit(nopython=True) # fill a node (may be two or more points) +def fill(img, msk, p, nbs, buf): + buf[0] = p + back = img[p] + cur = 0; s = 1; + + while curimg[p]: + sta = 100 + break + elif not mode and img[p+dp]0: + fill(img, msk, p, nbs, buf) + + idx[s] = p + s += 1 + if s==len(idx):break + return idx[:s].copy() + +@jit(nopython=True) # 3 max 2 zmd b4 ptd +def filter(img, msk, nbs, acc, idx, bur, tor, mode): + img = img.ravel() + msk = msk.ravel() + + arg = np.argsort(img[idx])[::-1 if mode else 1] + + for i in arg: + if msk[idx[i]]!=3: + idx[i] = 0 + continue + cur = 0; s = 1; + bur[0] = idx[i] + while cur img[idx[i]]+tor: continue + bur[s] = cp + s += 1 + if s==msk.size//3: + cut = cur//2 + msk[bur[:cut]] = 2 + bur[:s-cut] = bur[cut:] + cur -= cut + s -= cut + + if msk[cp]!=2:msk[cp] = 4 + cur += 1 + msk[bur[:s]] = 2 + return idx[idx>0] + + +def mser(img, tor, mode = True): + msk = np.zeros_like(img, dtype=np.uint8) + msk[tuple([slice(1,-1)]*img.ndim)] = 1 + buf = np.zeros(img.size//3, dtype=np.int64) + nbs = neighbors(img.shape) + acc = np.cumprod((1,)+img.shape[::-1][:-1])[::-1] + mskfilter = msk.copy() + idx = mark(img, nbs, mskfilter, buf, mode) + idx = filter(img, mskfilter, nbs, acc, idx, buf, tor, mode) + mark(img, nbs, msk, buf, mode) + idx = filter(img, msk, nbs, acc, idx, buf, tor, mode) + idx = idx2rc(idx, acc) + return (msk==2) * 255 + +from imagepy.core.engine import Filter +from imagepy.core.mark import GeometryMark + +class Plugin(Filter): + title = 'Mser' + note = ['8-bit', 'auto_snap', 'preview'] + para = {'tol':60} + view = [(int, 'tol', (0,100), 0, 'tolerance', '')] + + def run(self, ips, snap, img, para = None): + img[:] = mser(snap, para['tol'], False) + +if __name__ == '__main__': + from skimage.io import imread + from scipy.ndimage import gaussian_filter + from time import time + import matplotlib.pyplot as plt + img = gaussian_filter(imread('test.png'), 0) + pts = find_maximum(img, 20, True) + start = time() + pts = find_maximum(img, 10, True) + print(time()-start) + plt.imshow(img, cmap='gray') + plt.plot(pts[:,1], pts[:,0], 'y.') + plt.show() diff --git a/imagepy/menus/Plugins/screencap_plg.py b/imagepy/menus/Plugins/screencap_plg.py index 785117b6..06d2409b 100644 --- a/imagepy/menus/Plugins/screencap_plg.py +++ b/imagepy/menus/Plugins/screencap_plg.py @@ -1,4 +1,3 @@ -from imagepy import IPy from imagepy.core.engine import Free import time, wx, numpy as np @@ -18,4 +17,4 @@ def run(self, para = None): mem.Blit(0, 0, size[0], size[1], screen, 0, 0) arr = np.zeros((size[1], size[0], 3), dtype=np.uint8) bmp.CopyToBuffer(arr) - IPy.show_img([arr], 'Screen Capture') \ No newline at end of file + self.app.show_img([arr], 'Screen Capture') \ No newline at end of file diff --git a/imagepy/menus/Plugins/temporal_plg.py b/imagepy/menus/Plugins/temporal_plg.py index 017e78b3..b6f0c0a2 100644 --- a/imagepy/menus/Plugins/temporal_plg.py +++ b/imagepy/menus/Plugins/temporal_plg.py @@ -5,7 +5,6 @@ from imagepy.core.engine import Simple import numpy as np from imagepy.core.manager import ColorManager -from imagepy import IPy def color_code(img, lut): idx = np.linspace(0,255,len(img)).astype(int) diff --git a/imagepy/menus/Plugins/update_plg.py b/imagepy/menus/Plugins/update_plg.py index 9f60d452..a9395d20 100644 --- a/imagepy/menus/Plugins/update_plg.py +++ b/imagepy/menus/Plugins/update_plg.py @@ -1,4 +1,3 @@ -from imagepy import IPy, root_dir from imagepy.core.engine import Free import os, sys, os.path as osp import zipfile, urllib diff --git a/imagepy/menus/Process/Binary/distance_plgs.py b/imagepy/menus/Process/Binary/distance_plgs.py index 00847c40..cdf9015b 100644 --- a/imagepy/menus/Process/Binary/distance_plgs.py +++ b/imagepy/menus/Process/Binary/distance_plgs.py @@ -57,7 +57,7 @@ def run(self, ips, snap, img, para = None): img[:] = snap>0 dist = distance_transform_edt(snap, output=np.uint16) pts = find_maximum(dist, para['tor'], True) - buf = np.zeros(ips.size, dtype=np.uint32) + buf = np.zeros(ips.shape, dtype=np.uint32) buf[pts[:,0], pts[:,1]] = img[pts[:,0], pts[:,1]] = 2 markers, n = ndimg.label(buf, np.ones((3,3))) line = watershed(dist, markers, line=True, conn=para['con']+1, up=False) diff --git a/imagepy/menus/Process/Classify/classify_plgs.py b/imagepy/menus/Process/Classify/classify_plgs.py index b76c6894..3a1bf0e5 100644 --- a/imagepy/menus/Process/Classify/classify_plgs.py +++ b/imagepy/menus/Process/Classify/classify_plgs.py @@ -1,6 +1,5 @@ from imagepy.core.engine import Filter, Simple -from imagepy.core.manager import ImageManager -from imagepy.core import ImagePlus +# from imagepy.core import ImagePlus import numpy as np from sklearn.ensemble import RandomForestClassifier, \ @@ -8,7 +7,6 @@ GradientBoostingClassifier, VotingClassifier from sklearn.dummy import DummyClassifier from imagepy.ipyalg import feature -from imagepy import IPy model_para = None diff --git a/imagepy/menus/Process/Classify/classify_wgt.py b/imagepy/menus/Process/Classify/classify_wgt.py index bb3c087b..fd787da9 100644 --- a/imagepy/menus/Process/Classify/classify_wgt.py +++ b/imagepy/menus/Process/Classify/classify_wgt.py @@ -1,12 +1,12 @@ import wx, joblib, os, shutil, os.path as osp from glob import glob -from imagepy.core.manager import RoiManager, ImageManager, ReaderManager, ViewerManager +#from imagepy.core.manager import RoiManager, ImageManager, ReaderManager, ViewerManager from . import classify_plgs as manager from .predict_plg import Plugin as FCL -from imagepy import IPy +#from imagepy import IPy -ReaderManager.add('fcl', lambda x:x, 'fcl') -ViewerManager.add('fcl', lambda x,n:wx.CallAfter(FCL(path=x).start)) +#ReaderManager.add('fcl', lambda x:x, 'fcl') +#ViewerManager.add('fcl', lambda x,n:wx.CallAfter(FCL(path=x).start)) class Plugin( wx.Panel ): title = 'Feature Classify Panel' diff --git a/imagepy/menus/Process/Classify/io_plgs.py b/imagepy/menus/Process/Classify/io_plgs.py index ecb20c85..e5336f9a 100644 --- a/imagepy/menus/Process/Classify/io_plgs.py +++ b/imagepy/menus/Process/Classify/io_plgs.py @@ -1,7 +1,6 @@ from imagepy.core.engine import Filter, Simple from imagepy.core.manager import ColorManager -from imagepy.core import ImagePlus -from imagepy import IPy +# from imagepy.core import ImagePlus import numpy as np class BuildMark(Simple): diff --git a/imagepy/menus/Process/Classify/label_wgt.py b/imagepy/menus/Process/Classify/label_wgt.py index cee2326e..ba8aa909 100644 --- a/imagepy/menus/Process/Classify/label_wgt.py +++ b/imagepy/menus/Process/Classify/label_wgt.py @@ -1,5 +1,5 @@ -from imagepy.ui.widgets import CMapSelCtrl -from imagepy.core.manager import ColorManager, ImageManager, WindowsManager, ToolsManager +# from imagepy.ui.widgets import CMapSelCtrl +# from imagepy.core.manager import ColorManager, ImageManager, WindowsManager, ToolsManager from imagepy.core.engine import Macros import numpy as np import wx, os.path as osp diff --git a/imagepy/menus/Process/Classify/predict_plg.py b/imagepy/menus/Process/Classify/predict_plg.py index c0f25a85..5dc4bde4 100644 --- a/imagepy/menus/Process/Classify/predict_plg.py +++ b/imagepy/menus/Process/Classify/predict_plg.py @@ -1,11 +1,10 @@ from imagepy.core.engine import Simple -from imagepy.core import ImagePlus -from imagepy import IPy +# from imagepy.core import ImagePlus from glob import glob import os.path as osp import joblib from imagepy.ipyalg import feature -from imagepy.core.manager import ReaderManager, ViewerManager +from imagepy.core.manager import ReaderManager class Plugin(Simple): title = 'Feature Predictor' diff --git a/imagepy/menus/Process/FFT/fft_plgs.py b/imagepy/menus/Process/FFT/fft_plgs.py index 1af81544..7740d8b9 100644 --- a/imagepy/menus/Process/FFT/fft_plgs.py +++ b/imagepy/menus/Process/FFT/fft_plgs.py @@ -1,8 +1,8 @@ import numpy as np from imagepy.core.engine import Simple, Filter from numpy.fft import fft2, ifft2, fftshift, ifftshift -from imagepy import IPy -from imagepy.core import ImagePlus +from sciapp.object import Image +#from imagepy.core import ImagePlus class FFT(Simple): title = 'FFT' @@ -18,9 +18,9 @@ def run(self, ips, imgs, para = None): for i in range(len(imgs)): rst.append(shift(fft2(imgs[i]))) self.progress(i, len(imgs)) - ips = ImagePlus(rst, '%s-fft'%ips.title) + ips = Image(rst, '%s-fft'%ips.title) ips.log = True - IPy.show_ips(ips) + self.app.show_img(ips) class IFFT(Simple): title = 'Inverse FFT' @@ -38,7 +38,7 @@ def run(self, ips, imgs, para = None): for i in range(len(imgs)): rst.append(ifft2(shift(ips.img)).astype(tp)) self.progress(i, len(imgs)) - IPy.show_img(rst, '%s-ifft'%ips.title) + self.app.show_img(rst, '%s-ifft'%ips.title) class Shift(Filter): title = 'Zero Center' @@ -69,7 +69,7 @@ def run(self, ips, imgs, para = None): reals.append(copy(imgs[i].real)) imags.append(copy(imgs[i].imag)) self.progress(i, len(imgs)) - IPy.show_img(reals, '%s-real'%ips.title) - IPy.show_img(imags, '%s-image'%ips.title) + self.app.show_img(reals, '%s-real'%ips.title) + self.app.show_img(imags, '%s-image'%ips.title) plgs = [FFT, IFFT, '-', Shift, IShift, '-', Split] \ No newline at end of file diff --git a/imagepy/menus/Process/Features/blob_plgs.py b/imagepy/menus/Process/Features/blob_plgs.py index 9072ebd5..b2927b13 100644 --- a/imagepy/menus/Process/Features/blob_plgs.py +++ b/imagepy/menus/Process/Features/blob_plgs.py @@ -1,8 +1,7 @@ -from imagepy import IPy import numpy as np from imagepy.core.engine import Simple from skimage.feature import blob_dog, blob_doh, blob_log -from imagepy.core.mark import GeometryMark +from sciapp.object import mark2shp import pandas as pd class Dog(Simple): @@ -14,7 +13,7 @@ class Dog(Simple): view = [(int, 'min_sigma', (1, 50), 0, 'min', 'sigma'), (int, 'max_sigma', (1, 50), 0, 'max', 'sigma'), (float, 'sigma_ratio', (1.3, 5), 1, 'ratio', '1.3~5'), - (float, 'threshold', (0.1, 10), 1, 'threshold', '0.1~10'), + (float, 'threshold', (0.01, 10), 2, 'threshold', '0.01~10'), (float, 'overlap', (0, 10), 1, 'overlap', ''), (bool, 'exclude_border', 'exclude border'), (bool, 'showid', 'show id on image'), @@ -27,7 +26,7 @@ def preview(self, ips, para): sigma_ratio=para['sigma_ratio'], threshold=para['threshold'], overlap=para['overlap'], exclude_border=para['exclude_border']) pts[:,2] *= np.sqrt(2) - ips.mark = GeometryMark({'type':'circles', 'body':pts[:,[1,0,2]]}) + ips.mark = mark2shp({'type':'circles', 'body':pts[:,[1,0,2]]}) def cancel(self, ips): ips.mark = None @@ -53,11 +52,11 @@ def run(self, ips, imgs, para = None): (x,y,'id=%d'%i) for (x,y),i in zip(pts[:,1::-1], fid)]}) mark['body'][i] = layer - ips.mark = GeometryMark(mark) + ips.mark = mark2shp(mark) df = pd.DataFrame(np.vstack(data)*ips.unit[0], columns = ['X', 'Y', 'R']) df.insert(0, 'FID', fid) if para['slice']: df.insert(o, 'SliceID', sid) - IPy.show_table(df, ips.title+'-dogblob') + self.app.show_table(df, ips.title+'-dogblob') class Doh(Simple): title = 'Blob Doh' @@ -68,7 +67,7 @@ class Doh(Simple): view = [(int, 'min_sigma', (1, 50), 0, 'min', 'sigma'), (int, 'max_sigma', (1, 50), 0, 'max', 'sigma'), (int, 'num_sigma', (5, 30), 0, 'num', 'sigma'), - (float, 'threshold', (0.01, 1), 2, 'threshold', '0.1~10'), + (float, 'threshold', (0.01, 1), 2, 'threshold', '0.01~10'), (float, 'overlap', (0, 10), 1, 'overlap', ''), (bool, 'log_scale', 'log scale'), (bool, 'showid', 'show id on image'), @@ -80,7 +79,7 @@ def preview(self, ips, para): pts = blob_doh(grayimg, min_sigma=para['min_sigma'], max_sigma=para['max_sigma'], num_sigma=para['num_sigma'], threshold=para['threshold'], overlap=para['overlap'], log_scale=para['log_scale']) - ips.mark = GeometryMark({'type':'circles', 'body':pts[:,[1,0,2]]}) + ips.mark = mark2shp({'type':'circles', 'body':pts[:,[1,0,2]]}) def cancel(self, ips): ips.mark = None @@ -106,11 +105,11 @@ def run(self, ips, imgs, para = None): (x,y,'id=%d'%i) for (x,y),i in zip(pts[:,1::-1], fid)]}) mark['body'][i] = layer - ips.mark = GeometryMark(mark) + ips.mark = mark2shp(mark) df = pd.DataFrame(np.vstack(data)*ips.unit[0], columns = ['X', 'Y', 'R']) df.insert(0, 'FID', fid) if para['slice']: df.insert(o, 'SliceID', sid) - IPy.show_table(df, ips.title+'-dohblob') + self.app.show_table(df, ips.title+'-dohblob') class Log(Simple): title = 'Blob Log' @@ -121,7 +120,7 @@ class Log(Simple): view = [(int, 'min_sigma', (1, 50), 0, 'min', 'sigma'), (int, 'max_sigma', (1, 50), 0, 'max', 'sigma'), (int, 'num_sigma', (5, 30), 0, 'num', 'sigma'), - (float, 'threshold', (0.01, 1), 2, 'threshold', '0.02~1'), + (float, 'threshold', (0.02, 1), 2, 'threshold', '0.02~1'), (float, 'overlap', (0, 10), 1, 'overlap', ''), (bool, 'log_scale', 'log scale'), (bool, 'exclude_border', 'exclude border'), @@ -135,7 +134,7 @@ def preview(self, ips, para): num_sigma=para['num_sigma'], threshold=para['threshold'], overlap=para['overlap'], log_scale=para['log_scale'], exclude_border=para['exclude_border']) pts[:,2] *= np.sqrt(2) - ips.mark = GeometryMark({'type':'circles', 'body':pts[:,[1,0,2]]}) + ips.mark = mark2shp({'type':'circles', 'body':pts[:,[1,0,2]]}) def cancel(self, ips): ips.mark = None @@ -161,10 +160,10 @@ def run(self, ips, imgs, para = None): (x,y,'id=%d'%i) for (x,y),i in zip(pts[:,1::-1], fid)]}) mark['body'][i] = layer - ips.mark = GeometryMark(mark) + ips.mark = mark2shp(mark) df = pd.DataFrame(np.vstack(data)*ips.unit[0], columns = ['X', 'Y', 'R']) df.insert(0, 'FID', fid) if para['slice']: df.insert(o, 'SliceID', sid) - IPy.show_table(df, ips.title+'-dohblob') + self.app.show_table(df, ips.title+'-dohblob') plgs = [Dog, Doh, Log] \ No newline at end of file diff --git a/imagepy/menus/Process/Features/corner_plgs.py b/imagepy/menus/Process/Features/corner_plgs.py index 042480c1..30058e54 100644 --- a/imagepy/menus/Process/Features/corner_plgs.py +++ b/imagepy/menus/Process/Features/corner_plgs.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -* from skimage import feature from imagepy.core.engine import Filter -from imagepy.core.roi import PointRoi +from sciapp.object import Points, ROI class Harris(Filter): title = 'Harris' @@ -13,7 +13,7 @@ class Harris(Filter): def run(self, ips, snap, img, para = None): cimg = feature.corner_harris(img, sigma=para['sigma'], k=para['k']) pts = feature.corner_peaks(cimg, min_distance=1) - self.ips.roi = PointRoi([tuple(i[::-1]) for i in pts]) + ips.roi = ROI([Points(pts[:,::-1])]) class Kitchen(Filter): title = 'Kitchen Rosenfeld' @@ -24,7 +24,7 @@ class Kitchen(Filter): def run(self, ips, snap, img, para = None): cimg = feature.corner_kitchen_rosenfeld(img, mode='constant', cval=para['cval']) pts = feature.corner_peaks(cimg, min_distance=1) - self.ips.roi = PointRoi([tuple(i[::-1]) for i in pts]) + ips.roi = ROI([Points(pts[:,::-1])]) class Moravec(Filter): title = 'Moravec' @@ -35,7 +35,7 @@ class Moravec(Filter): def run(self, ips, snap, img, para = None): cimg = feature.corner_moravec(img, window_size=para['size']) pts = feature.corner_peaks(cimg, min_distance=1) - self.ips.roi = PointRoi([tuple(i[::-1]) for i in pts]) + ips.roi = ROI([Points(pts[:,::-1])]) class Tomasi(Filter): title = 'Tomasi' @@ -46,6 +46,6 @@ class Tomasi(Filter): def run(self, ips, snap, img, para = None): cimg = feature.corner_shi_tomasi(img, sigma=para['sigma']) pts = feature.corner_peaks(cimg, min_distance=1) - self.ips.roi = PointRoi([tuple(i[::-1]) for i in pts]) + ips.roi = ROI([Points(pts[:,::-1])]) plgs = [Harris, Kitchen, Moravec, Tomasi] \ No newline at end of file diff --git a/imagepy/menus/Process/Features/edge_plgs.py b/imagepy/menus/Process/Features/edge_plgs.py index c352f078..bfb55635 100644 --- a/imagepy/menus/Process/Features/edge_plgs.py +++ b/imagepy/menus/Process/Features/edge_plgs.py @@ -12,6 +12,6 @@ class Canny(Filter): def run(self, ips, snap, img, para = None): return feature.canny(snap, sigma=para['sigma'], low_threshold=para[ - 'low_threshold'], high_threshold=para['high_threshold'], mask=ips.get_msk())*255 + 'low_threshold'], high_threshold=para['high_threshold'], mask=ips.mask())*255 plgs = [Canny] \ No newline at end of file diff --git a/imagepy/menus/Process/Features/ridge_plgs.py b/imagepy/menus/Process/Features/ridge_plgs.py index 9c3f582d..925f364f 100644 --- a/imagepy/menus/Process/Features/ridge_plgs.py +++ b/imagepy/menus/Process/Features/ridge_plgs.py @@ -1,6 +1,5 @@ from skimage.filters import frangi, sato, hessian ,meijering from imagepy.core.engine import Filter, Simple -from imagepy import IPy def scale(img, low, high): img *= (high-low)/(max(img.ptp(), 1e-5)) diff --git a/imagepy/menus/Process/Hydrology/hydrology_plgs.py b/imagepy/menus/Process/Hydrology/hydrology_plgs.py index 408f5180..d938b86f 100644 --- a/imagepy/menus/Process/Hydrology/hydrology_plgs.py +++ b/imagepy/menus/Process/Hydrology/hydrology_plgs.py @@ -2,11 +2,11 @@ import numpy as np from imagepy.core.engine import Filter from imagepy.ipyalg import find_maximum, ridge, stair, isoline, watershed -from imagepy.core.roi import PointRoi +# from imagepy.core.roi import PointRoi +from sciapp.object import Points, ROI #from skimage.morphology import watershed, disk from skimage.filters import rank from skimage.filters import sobel -from imagepy import IPy class IsoLine(Filter): title = 'Find IsoLine' @@ -42,8 +42,8 @@ class FindMax(Filter): def run(self, ips, snap, img, para = None): pts = find_maximum(self.ips.img, para['tol']) - self.ips.roi = PointRoi([tuple(i) for i in pts[:,::-1]]) - self.ips.update() + ips.roi = ROI([Points(pts[:,::-1])]) + ips.update() class FindMin(Filter): title = 'Find Minimum' @@ -54,8 +54,8 @@ class FindMin(Filter): def run(self, ips, snap, img, para = None): pts = find_maximum(self.ips.img, para['tol'], False) - self.ips.roi = PointRoi([tuple(i) for i in pts[:,::-1]]) - self.ips.update() + ips.roi = ROI([Points(pts[:,::-1])]) + ips.update() class UPRidge(Filter): title = 'Find Riedge' @@ -213,7 +213,7 @@ def run(self, ips, snap, img, para = None): #gradient = rank.gradient(denoised, disk(para['gdt'])) ndimg.gaussian_filter(snap, para['sigma'], output=img) - markers, n = ndimg.label(ips.get_msk(), np.ones((3,3)), output=np.uint32) + markers, n = ndimg.label(ips.mask(), np.ones((3,3)), output=np.uint32) if not para['ud']:img[:] = 255-img mark = watershed(img, markers, line=True, conn=para['con']+1) mark = np.multiply((mark==0), 255, dtype=np.uint8) diff --git a/imagepy/menus/Process/Math/math_plgs.py b/imagepy/menus/Process/Math/math_plgs.py index a1b75c7f..ab7d5a07 100644 --- a/imagepy/menus/Process/Math/math_plgs.py +++ b/imagepy/menus/Process/Math/math_plgs.py @@ -61,7 +61,7 @@ class Sqrt(Filter): note = ['all', 'auto_msk', 'auto_snap', 'preview'] def run(self, ips, snap, img, para = None): - np.sqrt(snap, out=img) + np.sqrt(snap, out=img, casting='unsafe') class Gamma(Filter): """Garmma_plg: derived from imagepy.core.engine.Filter """ diff --git a/imagepy/menus/Process/Segment/active_plgs.py b/imagepy/menus/Process/Segment/active_plgs.py index 48ad32c8..ec46bbfc 100644 --- a/imagepy/menus/Process/Segment/active_plgs.py +++ b/imagepy/menus/Process/Segment/active_plgs.py @@ -1,7 +1,6 @@ import numpy as np from scipy.ndimage import binary_dilation from imagepy.core.engine import Filter -from imagepy import IPy from skimage import img_as_float from skimage.segmentation import chan_vese, morphological_chan_vese, \ morphological_geodesic_active_contour, inverse_gaussian_gradient, \ @@ -63,7 +62,7 @@ def run(self, ips, snap, img, para = None): (c1, c2), img[:] = ips.range, snap if para['out'] == 'mask': img[~msk], img[msk] = c1, c2 else: img[binary_dilation(msk) ^ msk] = c2 - if para['sub']: IPy.show_img(stackimg, ips.title+'-sub') + if para['sub']: self.app.show_img(stackimg, ips.title+'-sub') class MorphGeoChanVese(Filter): title = 'Bound Snake Fit' @@ -103,7 +102,7 @@ def run(self, ips, snap, img, para = None): (c1, c2), img[:] = ips.range, snap if para['out'] == 'mask': img[~msk], img[msk] = c1, c2 else: img[binary_dilation(msk) ^ msk] = c2 - if para['sub']: IPy.show_img(stackimg, ips.title+'-sub') + if para['sub']: self.app.show_img(stackimg, ips.title+'-sub') class MorphGeoRoi(Filter): title = 'ROI Snake Fit' @@ -122,7 +121,7 @@ class MorphGeoRoi(Filter): def preview(self, ips, para): snap, img = ips.snap, ips.img gimage = inverse_gaussian_gradient(img_as_float(snap)) - init = ips.get_msk('out') + init = ips.mask('out') msk = morphological_geodesic_active_contour(gimage, para['iter'], init_level_set=init, smoothing=para['smooth'], threshold='auto' if para['auto'] else para['thr'], balloon=para['balloon']) > 0 @@ -135,7 +134,7 @@ def run(self, ips, snap, img, para = None): stackimg = [] callback = lambda x: stackimg.append((x*255).astype(np.uint8)) if para['sub'] else 0 gimage = inverse_gaussian_gradient(img_as_float(snap)) - init = ips.get_msk('out') + init = ips.mask('out') msk = morphological_geodesic_active_contour(gimage, para['iter'], init_level_set=init, smoothing=para['smooth'], threshold='auto' if para['auto'] else para['thr'], @@ -143,7 +142,7 @@ def run(self, ips, snap, img, para = None): (c1, c2), img[:] = ips.range, snap if para['out'] == 'mask': img[~msk], img[msk] = c1, c2 else: img[binary_dilation(msk) ^ msk] = c2 - if para['sub']: IPy.show_img(stackimg, ips.title+'-sub') + if para['sub']: self.app.show_img(stackimg, ips.title+'-sub') class RandomWalker(Filter): title = 'Random Walker' diff --git a/imagepy/menus/Process/Segment/graph_plgs.py b/imagepy/menus/Process/Segment/graph_plgs.py index 9819baf4..ae2f17fa 100644 --- a/imagepy/menus/Process/Segment/graph_plgs.py +++ b/imagepy/menus/Process/Segment/graph_plgs.py @@ -1,7 +1,5 @@ from imagepy.core.engine import Simple -from imagepy import IPy from skimage import data, io, segmentation, color -from imagepy.core.manager import ImageManager from skimage.future import graph import numpy as np @@ -21,8 +19,7 @@ def load(self, ips): return ips.snapshot()==None def cancel(self, ips): ips.swap() def preview(self, ips, para): - print(para) - lab = ImageManager.get(para['lab']).img + lab = self.app.get_img(para['lab']).img connect = ['4-connected', '8-connected'].index(para['connect']) + 1 g = graph.rag_mean_color(ips.snap, lab, connect, para['mode'], para['sigma']) lab = graph.cut_threshold(lab, g, para['thresh']) @@ -30,11 +27,11 @@ def preview(self, ips, para): def run(self, ips, imgs, para = None): if not para['stack']: - imgs, labs = [ips.img], [ImageManager.get(para['lab']).img] + imgs, labs = [ips.img], [self.app.get_img(para['lab']).img] else: - labs = ImageManager.get(para['lab']).imgs + labs = self.app.get_img(para['lab']).imgs if len(imgs) != len(labs): - labs = [ImageManager.get(para['lab']).img] * len(imgs) + labs = [self.app.get_img(para['lab']).img] * len(imgs) for i in range(len(imgs)): img, lab = imgs[i], labs[i] connect = ['4-connected', '8-connected'].index(para['connect']) + 1 @@ -60,7 +57,7 @@ def load(self, ips): return ips.snapshot()==None def cancel(self, ips): ips.swap() def preview(self, ips, para): - lab = ImageManager.get(para['lab']).img + lab = self.app.get_img(para['lab']).img connect = ['4-connected', '8-connected'].index(para['connect']) + 1 g = graph.rag_mean_color(ips.snap, lab, connect, para['mode'], para['sigma']) lab = graph.cut_normalized(lab, g, para['thresh'], para['num']) @@ -68,11 +65,11 @@ def preview(self, ips, para): def run(self, ips, imgs, para = None): if not para['stack']: - imgs, labs = [ips.img], [ImageManager.get(para['lab']).img] + imgs, labs = [ips.img], [self.app.get_img(para['lab']).img] else: - labs = ImageManager.get(para['lab']).imgs + labs = self.app.get_img(para['lab']).imgs if len(imgs) != len(labs): - labs = [ImageManager.get(para['lab']).img] * len(imgs) + labs = [self.app.get_img(para['lab']).img] * len(imgs) for i in range(len(imgs)): img, lab = imgs[i], labs[i] connect = ['4-connected', '8-connected'].index(para['connect']) + 1 @@ -82,15 +79,15 @@ def run(self, ips, imgs, para = None): self.progress(i, len(imgs)) def _weight_mean_color(graph, src, dst, n): - diff = graph.node[dst]['mean color'] - graph.node[n]['mean color'] + diff = graph.nodes[dst]['mean color'] - graph.nodes[n]['mean color'] diff = np.linalg.norm(diff) return {'weight': diff} def merge_mean_color(graph, src, dst): - graph.node[dst]['total color'] += graph.node[src]['total color'] - graph.node[dst]['pixel count'] += graph.node[src]['pixel count'] - graph.node[dst]['mean color'] = (graph.node[dst]['total color'] / - graph.node[dst]['pixel count']) + graph.nodes[dst]['total color'] += graph.nodes[src]['total color'] + graph.nodes[dst]['pixel count'] += graph.nodes[src]['pixel count'] + graph.nodes[dst]['mean color'] = (graph.nodes[dst]['total color'] / + graph.nodes[dst]['pixel count']) class MergeHierarchical(Simple): title = 'RAG Merge Hierarchical' @@ -108,7 +105,7 @@ def load(self, ips): return ips.snapshot()==None def cancel(self, ips): ips.swap() def preview(self, ips, para): - lab = ImageManager.get(para['lab']).img + lab = self.app.get_img(para['lab']).img connect = ['4-connected', '8-connected'].index(para['connect']) + 1 g = graph.rag_mean_color(ips.snap, lab, connect, para['mode'], para['sigma']) lab = graph.merge_hierarchical(lab, g, thresh=para['thresh'], rag_copy=False, @@ -117,11 +114,11 @@ def preview(self, ips, para): def run(self, ips, imgs, para = None): if not para['stack']: - imgs, labs = [ips.img], [ImageManager.get(para['lab']).img] + imgs, labs = [ips.img], [self.app.get_img(para['lab']).img] else: - labs = ImageManager.get(para['lab']).imgs + labs = self.app.get_img(para['lab']).imgs if len(imgs) != len(labs): - labs = [ImageManager.get(para['lab']).img] * len(imgs) + labs = [self.app.get_img(para['lab']).img] * len(imgs) for i in range(len(imgs)): img, lab = imgs[i], labs[i] connect = ['4-connected', '8-connected'].index(para['connect']) + 1 diff --git a/imagepy/menus/Process/Segment/shift_plgs.py b/imagepy/menus/Process/Segment/shift_plgs.py index 4b49d39d..c8dca29d 100644 --- a/imagepy/menus/Process/Segment/shift_plgs.py +++ b/imagepy/menus/Process/Segment/shift_plgs.py @@ -1,5 +1,4 @@ from imagepy.core.engine import Filter, Simple -from imagepy import IPy from skimage import data, io, segmentation, color class SLIC(Filter): @@ -73,7 +72,7 @@ def run(self, ips, imgs, para = None): para['compactness'], para['max_iter'], para['sigma']).astype('int32')) rst[-1] += 1 self.progress(i, len(imgs)) - IPy.show_img(rst, ips.title+'-sliclab') + self.app.show_img(rst, ips.title+'-sliclab') class QuickshiftLab(Simple): title = 'Quickshift Label' @@ -103,7 +102,7 @@ def run(self, ips, imgs, para = None): para['max_dist'], para['sigma']).astype('int32')) rst[-1] += 1 self.progress(i, len(imgs)) - IPy.show_img(rst, ips.title+'-quickshiftlab') + self.app.show_img(rst, ips.title+'-quickshiftlab') class FelzenszwalbLab(Simple): title = 'Felzenszwalb Label' @@ -131,6 +130,6 @@ def run(self, ips, imgs, para = None): para['sigma'], para['min_size']).astype('int32')) rst[-1] += 1 self.progress(i, len(imgs)) - IPy.show_img(rst, ips.title+'-felzenszwalblab') + self.app.show_img(rst, ips.title+'-felzenszwalblab') plgs = [SLIC, Quickshift, Felzenszwalb, '-', SLICLab, QuickshiftLab, FelzenszwalbLab] \ No newline at end of file diff --git a/imagepy/menus/Process/Threshold/threshold_plgs.py b/imagepy/menus/Process/Threshold/threshold_plgs.py index 097c801e..55bf773a 100644 --- a/imagepy/menus/Process/Threshold/threshold_plgs.py +++ b/imagepy/menus/Process/Threshold/threshold_plgs.py @@ -3,7 +3,6 @@ Created on Fri Nov 18 22:56:50 2016 @author: yxl """ -from imagepy import IPy import numpy as np from imagepy.core.engine import Filter import scipy.ndimage as ndimg diff --git a/imagepy/menus/Process/calculator_plg.py b/imagepy/menus/Process/calculator_plg.py index 41edce60..f72b01f0 100644 --- a/imagepy/menus/Process/calculator_plg.py +++ b/imagepy/menus/Process/calculator_plg.py @@ -3,9 +3,6 @@ Created on Thu Dec 1 01:22:19 2016 @author: yxl """ -from imagepy.core.manager import ImageManager -from imagepy import IPy - from imagepy.core.engine import Simple from imagepy.core.pixel import bliter @@ -20,23 +17,20 @@ class Plugin(Simple): ('img', 'img2', 'image2', '')] def run(self, ips, imgs, para = None): - ips1 = ImageManager.get(para['img1']) - ips2 = ImageManager.get(para['img2']) + ips1 = self.app.get_img(para['img1']) + ips2 = self.app.get_img(para['img2']) ips1.snapshot() - sl1, sl2 = ips1.get_nslices(), ips2.get_nslices() - cn1, cn2 = ips1.get_nchannels(), ips2.get_nchannels() + sl1, sl2 = ips1.slices, ips2.slices + cn1, cn2 = ips1.channels, ips2.channels if ips1.dtype != ips2.dtype: - IPy.alert('Two stack must be equal dtype!') - return + return self.app.alert('Two stack must be equal dtype!') elif sl1>1 and sl2>1 and sl1!=sl2: - IPy.alert('Two stack must have equal slices!') - return + return self.app.alert('Two stack must have equal slices!') elif cn1>1 and cn2>1 and cn1!=cn2: - IPy.alert('Two stack must have equal channels!') - return + return self.app.alert('Two stack must have equal channels!') - w, h = ips1.size, ips2.size + w, h = ips1.shape, ips2.shape w, h = min(w[0], h[0]), min(w[1], h[1]) if sl1 == 1: bliter.blit(ips1.get_subimg(), ips2.get_subimg(), mode=para['op']) diff --git a/imagepy/menus/Process/repair_plg.py b/imagepy/menus/Process/repair_plg.py index 28e5e20c..cb941138 100644 --- a/imagepy/menus/Process/repair_plg.py +++ b/imagepy/menus/Process/repair_plg.py @@ -10,7 +10,7 @@ class Plugin(Filter): view = [(list, 'mode', ['nearest', 'mean'], str, 'replace by', 'pix')] def run(self, ips, snap, img, para = None): - msk = ips.get_msk() + msk = ips.mask() if self.para['mode']=='nearest': rr, cc = ndimg.distance_transform_edt(msk, return_distances=False, return_indices=True) img[:] = snap[rr, cc] diff --git a/imagepy/menus/Selection/roiwindow_wgt.py b/imagepy/menus/Selection/roiwindow_wgt.py index a5a1561c..b90dd274 100644 --- a/imagepy/menus/Selection/roiwindow_wgt.py +++ b/imagepy/menus/Selection/roiwindow_wgt.py @@ -1,7 +1,8 @@ import wx -from imagepy.core.manager import RoiManager, ImageManager +#from imagepy.core.manager import RoiManager, ImageManager from imagepy.core.engine import Macros -from imagepy import IPy +from sciapp.object import ROI, mark2shp +#from imagepy import IPy class VirtualListCtrl(wx.ListCtrl): def __init__(self, parent, title, data=[]): @@ -33,7 +34,7 @@ class Plugin(wx.Panel): def __init__( self, parent ): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size(-1,-1), style = wx.TAB_TRAVERSAL ) - + self.app = parent sizer = wx.BoxSizer( wx.HORIZONTAL ) self.note_book = wx.Notebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.NB_LEFT|wx.NB_TOP ) @@ -228,92 +229,93 @@ def on_end_edit(self, event): self.UpdateData() def on_add(self, event): - Macros('', ['ROI Add>None']).start(callafter=self.UpdateData) + Macros('', ['ROI Add>None']).start(self.app, callafter=self.UpdateData) def on_add_nameless(self, event): ips = ImageManager.get() - if ips is None: return IPy.alert('No image opened!') - if ips.roi is None: return IPy.alert('No Roi found!') - Macros('', ['ROI Add>{"name":"%s-roi"}'%ips.title]).start(callafter=self.UpdateData) + if ips is None: return self.app.alert('No image opened!') + if ips.roi is None: return self.app.alert('No Roi found!') + Macros('', ['ROI Add>{"name":"%s-roi"}'%ips.title]).start(self.app, callafter=self.UpdateData) def on_load(self, event): idx = self.lst_rois.GetFirstSelected() - if idx==-1: return IPy.alert('No ROI Selected!') + if idx==-1: return self.app.alert('No ROI Selected!') name = self.lst_rois.OnGetItemText(idx, 0) - Macros('', ['ROI Load>{"name":"%s"}'%name]).start() + Macros('', ['ROI Load>{"name":"%s"}'%name]).start(self.app) def on_remove(self, event): idx = self.lst_rois.GetFirstSelected() - if idx==-1: return IPy.alert('No ROI Selected!') + if idx==-1: return self.app.alert('No ROI Selected!') name = self.lst_rois.OnGetItemText(idx, 0) - Macros('', ['ROI Remove>{"name":"%s"}'%name]).start(callafter=self.UpdateData) + Macros('', ['ROI Remove>{"name":"%s"}'%name]).start(self.app, callafter=self.UpdateData) def on_open(self, event): - Macros('', ['ROI Open>None']).start() + Macros('', ['ROI Open>None']).start(self.app) def on_save(self, event): - Macros('', ['ROI Save>None']).start() + Macros('', ['ROI Save>None']).start(self.app) def on_inflate(self, event): - Macros('', ['ROI Inflate>None']).start() + Macros('', ['ROI Inflate>None']).start(self.app) def on_shrink(self, event): - Macros('', ['ROI Shrink>None']).start() + Macros('', ['ROI Shrink>None']).start(self.app) def on_convex(self, event): - Macros('', ['ROI Convex Hull>None']).start() + Macros('', ['ROI Convex Hull>None']).start(self.app) def on_box(self, event): - Macros('', ['ROI Bound Box>None']).start() + Macros('', ['ROI Bound Box>None']).start(self.app) def on_clip(self, event): - Macros('', ['ROI Clip>None']).start() + Macros('', ['ROI Clip>None']).start(self.app) def on_invert(self, event): - Macros('', ['ROI Invert>None']).start() + Macros('', ['ROI Invert>None']).start(self.app) def on_intersect(self, event): idx = self.lst_rois.GetFirstSelected() - if idx==-1: return IPy.alert('No ROI Selected!') + if idx==-1: return self.app.alert('No ROI Selected!') name = self.lst_rois.OnGetItemText(idx, 0) - Macros('', ['ROI Intersect>{"name":"%s"}'%name]).start() + Macros('', ['ROI Intersect>{"name":"%s"}'%name]).start(self.app) def on_union(self, event): idx = self.lst_rois.GetFirstSelected() - if idx==-1: return IPy.alert('No ROI Selected!') + if idx==-1: return self.app.alert('No ROI Selected!') name = self.lst_rois.OnGetItemText(idx, 0) - Macros('', ['ROI Union>{"name":"%s"}'%name]).start() + Macros('', ['ROI Union>{"name":"%s"}'%name]).start(self.app) def on_difference(self, event): idx = self.lst_rois.GetFirstSelected() - if idx==-1: return IPy.alert('No ROI Selected!') + if idx==-1: return self.app.alert('No ROI Selected!') name = self.lst_rois.OnGetItemText(idx, 0) - Macros('', ['ROI Difference>{"name":"%s"}'%name]).start() + Macros('', ['ROI Difference>{"name":"%s"}'%name]).start(self.app) def on_symdiff(self, event): idx = self.lst_rois.GetFirstSelected() - if idx==-1: return IPy.alert('No ROI Selected!') + if idx==-1: return self.app.alert('No ROI Selected!') name = self.lst_rois.OnGetItemText(idx, 0) - Macros('', ['ROI Symmetric Diff>{"name":"%s"}'%name]).start() + Macros('', ['ROI Symmetric Diff>{"name":"%s"}'%name]).start(self.app) def on_clear(self, event): - Macros('', ['Clear>None']).start() + Macros('', ['Clear>None']).start(self.app) def on_clearout(self, event): - Macros('', ['Clear Out>None']).start() + Macros('', ['Clear Out>None']).start(self.app) def on_sketch(self, event): - Macros('', ['Sketch>None']).start() + Macros('', ['Sketch>None']).start(self.app) def on_update(self, event): self.UpdateData() def on_setting(self, event): - Macros('', ['ROI Setting>None']).start() + Macros('', ['ROI Setting>None']).start(self.app) def UpdateData(self): - names = RoiManager.get_titles() - types = [RoiManager.get(i).dtype for i in names] + names = self.app.manager('roi').gets('name') + objs = self.app.manager('roi').gets('obj') + types = [ROI(mark2shp(i)).roitype for i in objs] self.lst_rois.SetValue(list(zip(names, types))) def __del__( self ): diff --git a/imagepy/menus/Selection/select_plg.py b/imagepy/menus/Selection/select_plg.py index ed08b3d3..5b7f28af 100644 --- a/imagepy/menus/Selection/select_plg.py +++ b/imagepy/menus/Selection/select_plg.py @@ -4,11 +4,9 @@ @author: yxl """ -from imagepy.core.manager import RoiManager from imagepy.core.engine import Simple, Free -from imagepy.core.roi import RectangleRoi -from imagepy.core.roi import roiio -from imagepy import IPy +from sciapp.object import ROI, geom2shp, geom_flatten, Rectangle, geom_union, mark2shp +import json, time class SelectAll(Simple): """SelectAll: derived from imagepy.core.engine.Simple """ @@ -16,7 +14,7 @@ class SelectAll(Simple): note = ['all'] def run(self, ips, imgs, para = None): - ips.roi = RectangleRoi(0,0,ips.size[1],ips.size[0]) + ips.roi = ROI(Rectangle([0, 0, ips.shape[1], ips.shape[0]])) class SelectNone(Simple): """SelectNone: derived from imagepy.core.engine.Simple """ @@ -25,6 +23,7 @@ class SelectNone(Simple): def run(self, ips, imgs, para = None): ips.roi = None + time.sleep(0.1) class Add2Manager(Simple): """Add2Manager: derived from imagepy.core.engine.Simple """ @@ -34,43 +33,41 @@ class Add2Manager(Simple): view = [(str, 'name', 'Name', '')] def run(self, ips, imgs, para = None): - RoiManager.add(para['name'], ips.roi) + self.app.manager('roi').add(obj=ips.roi.to_mark(), name=para['name']) class RemoveFManager(Simple): - """Add2Manager: derived from imagepy.core.engine.Simple """ title = 'ROI Remove' note = ['all'] para = {'name':''} def load(self, ips): - titles = list(RoiManager.rois.keys()) + titles = self.app.manager('roi').gets('name') if len(titles)==0: - IPy.alert('No roi in manager!') + self.app.alert('No roi in manager!') return False self.para['name'] = titles[0] RemoveFManager.view = [(list, 'name', titles, str, 'Name', '')] return True def run(self, ips, imgs, para = None): - RoiManager.remove(para['name']) + self.app.manager('roi').remove(name=para['name']) class LoadRoi(Simple): - """LoadRoi: derived from imagepy.core.engine.Simple """ title = 'ROI Load' note = ['all'] para = {'name':''} def load(self, ips): - titles = list(RoiManager.rois.keys()) + titles = self.app.manager('roi').gets('name') if len(titles)==0: - IPy.alert('No roi in manager!') + self.app.alert('No roi in manager!') return False self.para['name'] = titles[0] LoadRoi.view = [(list, 'name', titles, str, 'Name', '')] return True def run(self, ips, imgs, para = None): - ips.roi = RoiManager.get(para['name']) + ips.roi = mark2shp(self.app.manager('roi').get(name=para['name'])) class Inflate(Simple): """Inflate: derived from imagepy.core.engine.Simple """ @@ -80,7 +77,8 @@ class Inflate(Simple): view = [(int, 'r', (1,100),0, 'radius', 'pix')] def run(self, ips, imgs, para = None): - ips.roi = ips.roi.buffer(para['r']) + geom = ips.roi.to_geom().buffer(para['r']) + ips.roi = ROI(geom2shp(geom_flatten(geom))) class Shrink(Simple): """Shrink: derived from imagepy.core.engine.Simple """ @@ -90,7 +88,8 @@ class Shrink(Simple): view = [(int, 'r', (1,100),0, 'radius', 'pix')] def run(self, ips, imgs, para = None): - ips.roi = ips.roi.buffer(-para['r']) + geom = ips.roi.to_geom().buffer(-para['r']) + ips.roi = ROI(geom2shp(geom_flatten(geom))) class Convex(Simple): """Convex: derived from imagepy.core.engine.Simple """ @@ -98,7 +97,8 @@ class Convex(Simple): note = ['all', 'req_roi'] def run(self, ips, imgs, para = None): - ips.roi = ips.roi.convex() + geom = ips.roi.to_geom().convex_hull + ips.roi = ROI(geom2shp(geom_flatten(geom))) class Box(Simple): """Box: derived from imagepy.core.engine.Simple """ @@ -106,7 +106,8 @@ class Box(Simple): note = ['all', 'req_roi'] def run(self, ips, imgs, para = None): - ips.roi = ips.roi.bounds() + a,b,c,d = ips.roi.to_geom().bounds + ips.roi = ROI(Rectangle([a,b,c-a,d-b])) class Clip(Simple): """Clip: derived from imagepy.core.engine.Simple """ @@ -114,8 +115,9 @@ class Clip(Simple): note = ['all', 'req_roi'] def run(self, ips, imgs, para = None): - rect = RectangleRoi(0,0,ips.size[1],ips.size[0]) - ips.roi = ips.roi.clip(rect) + rect = Rectangle([0, 0, ips.shape[1], ips.shape[0]]) + geom = rect.to_geom().intersection(geom_flatten(ips.roi.to_geom())) + ips.roi = ROI(geom2shp(geom_flatten(geom))) class Invert(Simple): """Invert: derived from imagepy.core.engine.Simple """ @@ -123,8 +125,9 @@ class Invert(Simple): note = ['all', 'req_roi'] def run(self, ips, imgs, para = None): - rect = RectangleRoi(0,0,ips.size[1],ips.size[0]) - ips.roi = ips.roi.invert(rect) + rect = Rectangle([0, 0, ips.shape[1], ips.shape[0]]) + geom = rect.to_geom().difference(geom_flatten(ips.roi.to_geom())) + ips.roi = ROI(geom2shp(geom_flatten(geom))) class Save(Simple): """Save: save roi as a wkt file """ @@ -133,13 +136,12 @@ class Save(Simple): para={'path':''} def show(self): - filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in ['roi', 'wkt']]) - return IPy.getpath('Save..', filt, 'save', self.para) + self.para['path'] = self.app.getpath('ROI Save', ['roi'], 'save') + return not self.para['path'] is None def run(self, ips, imgs, para = None): - file = para['path'] - if file[-3:] == 'wkt':roiio.savewkt(ips.roi, file) - if file[-3:] == 'roi':roiio.saveroi(ips.roi, file) + with open(para['path'], 'w') as f: + f.write(json.dumps(ips.roi.to_mark())) class Open(Simple): """Save: save roi as a wkt file """ @@ -148,14 +150,12 @@ class Open(Simple): para={'path':''} def show(self): - filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in ['roi', 'wkt']]) - return IPy.getpath('Save..', filt, 'open', self.para) + self.para['path'] = self.app.getpath('ROI Save', ['roi'], 'open') + return not self.para['path'] is None def run(self, ips, imgs, para = None): - file = para['path'] - - if file[-3:] == 'wkt':ips.roi = roiio.readwkt(file) - if file[-3:] == 'roi':ips.roi = roiio.readroi(file) + with open(para['path']) as f: + ips.roi = ROI(mark2shp(json.loads(f.read()))) class Intersect(Simple): """Union: derived from imagepy.core.engine.Simple """ @@ -164,16 +164,18 @@ class Intersect(Simple): para = {'name':''} def load(self, ips): - titles = list(RoiManager.rois.keys()) + titles = self.app.manager('roi').gets('name') if len(titles)==0: - IPy.alert('No roi in manager!') + self.app.alert('No roi in manager!') return False self.para['name'] = titles[0] self.view = [(list, 'name', titles, str, 'Name', '')] return True def run(self, ips, imgs, para = None): - ips.roi = ips.roi.intersect(RoiManager.get(para['name'])) + obj = mark2shp(self.app.manager('roi').get(name=para['name'])).to_geom() + roi = geom_flatten(ips.roi.to_geom()) + ips.roi = ROI(geom2shp(geom_flatten(roi.intersection(obj)))) class Union(Simple): """Union: derived from imagepy.core.engine.Simple """ @@ -182,16 +184,18 @@ class Union(Simple): para = {'name':''} def load(self, ips): - titles = list(RoiManager.rois.keys()) + titles = self.app.manager('roi').gets('name') if len(titles)==0: - IPy.alert('No roi in manager!') + self.app.alert('No roi in manager!') return False self.para['name'] = titles[0] self.view = [(list, 'name', titles, str, 'Name', '')] return True def run(self, ips, imgs, para = None): - ips.roi = ips.roi.union(RoiManager.get(para['name'])) + obj = mark2shp(self.app.manager('roi').get(name=para['name'])).to_geom() + roi = geom_flatten(ips.roi.to_geom()) + ips.roi = ROI(geom2shp(geom_flatten(roi.union(obj)))) class Diff(Simple): """Diff: derived from imagepy.core.engine.Simple """ @@ -200,34 +204,38 @@ class Diff(Simple): para = {'name':''} def load(self, ips): - titles = list(RoiManager.rois.keys()) + titles = self.app.manager('roi').gets('name') if len(titles)==0: - IPy.alert('No roi in manager!') + self.app.alert('No roi in manager!') return False self.para['name'] = titles[0] self.view = [(list, 'name', titles, str, 'Name', '')] return True def run(self, ips, imgs, para = None): - ips.roi = ips.roi.diff(RoiManager.get(para['name'])) + print(self.app.manager('roi').get(name=para['name'])) + obj = mark2shp(self.app.manager('roi').get(name=para['name'])).to_geom() + roi = geom_flatten(ips.roi.to_geom()) + ips.roi = ROI(geom2shp(geom_flatten(roi.difference(obj)))) class SymDiff(Simple): - """Diff: derived from imagepy.core.engine.Simple """ title = 'ROI Symmetric Diff' note = ['all', 'req_roi'] para = {'name':''} def load(self, ips): - titles = list(RoiManager.rois.keys()) + titles = self.app.manager('roi').gets('name') if len(titles)==0: - IPy.alert('No roi in manager!') + self.app.alert('No roi in manager!') return False self.para['name'] = titles[0] self.view = [(list, 'name', titles, str, 'Name', '')] return True def run(self, ips, imgs, para = None): - ips.roi = ips.roi.symmetric_diff(RoiManager.get(para['name'])) + obj = mark2shp(self.app.manager('roi').get(name=para['name'])).to_geom() + roi = geom_flatten(ips.roi.to_geom()) + ips.roi = ROI(geom2shp(geom_flatten(roi.symmetric_difference(obj)))) class Setting(Free): title = 'ROI Setting' diff --git a/imagepy/menus/Table/Basic Operator/basic_plgs.py b/imagepy/menus/Table/Basic Operator/basic_plgs.py index b6e7a4c4..6945c1ff 100644 --- a/imagepy/menus/Table/Basic Operator/basic_plgs.py +++ b/imagepy/menus/Table/Basic Operator/basic_plgs.py @@ -1,17 +1,16 @@ from imagepy.core.engine import Table -from imagepy import IPy class Transpose(Table): title = 'Table Transpose' def run(self, tps, snap, data, para = None): - tps.set_data(data.T) + tps.data = data.T class Corp(Table): title = 'Table Corp' note = ['req_sel'] def run(self, tps, snap, data, para): - tps.set_data(data.loc[tps.rowmsk, tps.colmsk]) + tps.data = tps.subtab() class Duplicate(Table): title = 'Table Duplicate' @@ -23,8 +22,7 @@ def load(self, tps): return True def run(self, tps, snap, data, para = None): - newdata = data.loc[tps.rowmsk, tps.colmsk] - IPy.show_table(para['name'], newdata) + self.app.show_table(tps.subtab(), para['name']) class DeleteRow(Table): title = 'Delete Rows' @@ -47,9 +45,8 @@ class AppendRow(Table): (bool, 'fill', 'fill by last row')] def run(self, tps, snap, data, para = None): - newdata = data.reindex(index=range(data.shape[0]+para['count']), \ + tps.data = data.reindex(index=range(data.shape[0]+para['count']), \ method=[None,'pad'][para['fill']]) - tps.set_data(newdata) class AddCol(Table): title = 'Add Column' @@ -58,8 +55,6 @@ class AddCol(Table): ('any', 'value', 'value')] def run(self, tps, snap, data, para = None): - ctype = data.columns.dtype.type - data[ctype(para['name'])] = para['value'] - print(data.info()) + data[para['name']] = para['value'] plgs = [Transpose, Duplicate, Corp, '-', DeleteRow, DeleteCol, AppendRow, AddCol] \ No newline at end of file diff --git a/imagepy/menus/Table/Chart/plot_plgs.py b/imagepy/menus/Table/Chart/plot_plgs.py index f43879a5..f2e37928 100644 --- a/imagepy/menus/Table/Chart/plot_plgs.py +++ b/imagepy/menus/Table/Chart/plot_plgs.py @@ -1,7 +1,6 @@ from imagepy.core.engine import Table -from imagepy import IPy import matplotlib.pyplot as plt -from imagepy.core.manager import ColorManager +#from imagepy.core.manager import ColorManager from matplotlib import colors class Plot(Table): @@ -14,8 +13,10 @@ class Plot(Table): (bool, 'grid', 'grid')] def run(self, tps, snap, data, para = None): - data[para['cn']].plot(lw=para['lw'], grid=para['grid'], title=para['title']) - plt.show() + plt = self.app.show_plot(para['title']) + data[para['cn']].plot(lw=para['lw'], grid=para['grid'], + title=para['title'], ax=plt.add_subplot()) + plt.Show() class Bar(Table): title = 'Bar Chart' @@ -28,10 +29,13 @@ class Bar(Table): (bool, 'grid', 'grid')] def run(self, tps, snap, data, para = None): + plt = self.app.show_plot(para['title']) if para['dir']: - data[para['cn']].plot.barh(stacked=para['stack'], grid=para['grid'], title=para['title']) - else: data[para['cn']].plot.bar(stacked=para['stack'], grid=para['grid'], title=para['title']) - plt.show() + data[para['cn']].plot.barh(stacked=para['stack'], grid=para['grid'], + title=para['title'], ax=plt.add_subplot()) + else: data[para['cn']].plot.bar(stacked=para['stack'], grid=para['grid'], + title=para['title'], ax=plt.add_subplot()) + plt.Show() class Hist(Table): title = 'Hist Chart' @@ -48,13 +52,14 @@ class Hist(Table): (bool, 'grid', 'grid')] def run(self, tps, snap, data, para = None): + plt = self.app.show_plot(para['title']) if para['overlay']: data[para['cn']].plot.hist(stacked=para['stack'], bins=para['bins'], alpha=para['alpha'], - orientation=para['dir'], grid=para['grid'], title=para['title']) + orientation=para['dir'], grid=para['grid'], title=para['title'], ax=plt.add_subplot()) else: data[para['cn']].hist(stacked=para['stack'], bins=para['bins'], alpha=para['alpha'], - orientation=para['dir'], grid=para['grid']) - plt.show() + orientation=para['dir'], grid=para['grid'], ax=plt.add_subplot()) + plt.Show() class Box(Table): title = 'Box Chart' @@ -67,8 +72,10 @@ class Box(Table): (bool, 'grid', 'grid')] def run(self, tps, snap, data, para = None): - data[para['cn']].plot.box(by=None, vert=~para['hor'], grid=para['grid'], title=para['title']) - plt.show() + plt = self.app.show_plot(para['title']) + data[para['cn']].plot.box(by=None, vert=~para['hor'], grid=para['grid'], + title=para['title'], ax=plt.add_subplot()) + plt.Show() class Area(Table): title = 'Area Chart' @@ -81,9 +88,10 @@ class Area(Table): (bool, 'grid', 'grid')] def run(self, tps, snap, data, para = None): + plt = self.app.show_plot(para['title']) data[para['cn']].plot.area(stacked=para['stack'], alpha=para['alpha'], - grid=para['grid'], title=para['title']) - plt.show() + grid=para['grid'], title=para['title'], ax=plt.add_subplot()) + plt.Show() class Scatter(Table): title = 'Scatter Chart' @@ -106,9 +114,10 @@ def run(self, tps, snap, data, para = None): cs = data[para['cs']] if para['cs'] != 'None' else '#%.2x%.2x%.2x'%para['c'] cm = ColorManager.get_lut(para['cm'])/255.0 cm = None if para['cs'] == 'None' else colors.ListedColormap(cm, N=256) + plt = self.app.show_plot(para['title']) data.plot.scatter(x=para['x'], y=para['y'], s=rs, c=cs, alpha=para['alpha'], - cmap=cm, grid=para['grid'], title=para['title']) - plt.show() + cmap=cm, grid=para['grid'], title=para['title'], ax=plt.add_subplot()) + plt.Show() class Pie(Table): title = 'Pie Chart' @@ -118,7 +127,8 @@ class Pie(Table): ('fields', 'cn', 'select fields')] def run(self, tps, snap, data, para = None): - data[para['cn']].plot.pie(subplots=True, title=para['title']) - plt.show() + plt = self.app.show_plot(para['title']) + data[para['cn']].plot.pie(subplots=True, title=para['title'], ax=plt.add_subplot()) + plt.Show() plgs = [Plot, Area, Bar, Box, Hist, Pie, Scatter] \ No newline at end of file diff --git a/imagepy/menus/Table/Signal/signal_plgs.py b/imagepy/menus/Table/Signal/signal_plgs.py index 62752dd9..e8c83611 100644 --- a/imagepy/menus/Table/Signal/signal_plgs.py +++ b/imagepy/menus/Table/Signal/signal_plgs.py @@ -6,7 +6,7 @@ class Statistic(Table): title = 'Signal Uniform Filter' - note = ['snap', 'only_num', 'col_msk', 'preview'] + note = ['auto_snap', 'only_num', 'auto_msk', 'preview'] para = {'size':2} diff --git a/imagepy/menus/Table/Statistic/frequency_plgs.py b/imagepy/menus/Table/Statistic/frequency_plgs.py index 50453f20..c3713d58 100644 --- a/imagepy/menus/Table/Statistic/frequency_plgs.py +++ b/imagepy/menus/Table/Statistic/frequency_plgs.py @@ -1,7 +1,6 @@ from imagepy.core.engine import Table import pandas as pd import numpy as np -from imagepy import IPy class Count(Table): title = 'Values Frequency' @@ -12,7 +11,7 @@ def run(self, tps, snap, data, para=None): for i in para['cn']: df = pd.DataFrame(data[i].value_counts()) df.columns = ['%s-vf'%i] - IPy.show_table(df, '%s-values-frequency'%i) + self.app.show_table(df, '%s-values-frequency'%i) class Frequency(Table): title = 'Table Bins Frequency' @@ -37,6 +36,6 @@ def run(self, tps, snap, data, para=None): if para['fre']:vs['frequency'] = hist/hist.sum() if para['weight']:vs['weight'] = np.histogram(data[i], bins, rg, weights=data[i])[0] df = pd.DataFrame(vs, columns = [i for i in ['bins', 'count', 'frequency', 'weight'] if i in vs]) - IPy.show_table(df, '%s-frequency'%i) + self.app.show_table(df, '%s-frequency'%i) plgs = [Count, Frequency] \ No newline at end of file diff --git a/imagepy/menus/Table/Statistic/statistic_plgs.py b/imagepy/menus/Table/Statistic/statistic_plgs.py index 7ba08140..8bce5ead 100644 --- a/imagepy/menus/Table/Statistic/statistic_plgs.py +++ b/imagepy/menus/Table/Statistic/statistic_plgs.py @@ -1,10 +1,9 @@ from imagepy.core.engine import Table import pandas as pd -from imagepy import IPy class Statistic(Table): title = 'Table Statistic' - note = ['snap', 'only_num', 'row_msk', 'col_msk'] + note = ['snap', 'only_num', 'auto_msk'] para = {'axis':'Column', 'sum':True, 'mean':True,'max':False, 'min':False,'var':False,'std':False,'skew':False,'kurt':False} @@ -31,7 +30,7 @@ def run(self, tps, snap, data, para=None): if para['kurt']:rst['kurt'] = snap.kurt(axis=axis) cols = ['sum', 'mean', 'min', 'max', 'var', 'std', 'skew', 'kurt'] cols = [i for i in cols if i in rst] - IPy.show_table(pd.DataFrame(rst, columns=cols).T, tps.title+'-statistic') + self.app.show_table(pd.DataFrame(rst, columns=cols).T, tps.title+'-statistic') class GroupStatistic(Table): title = 'Group Statistic' @@ -68,6 +67,6 @@ def post(a, fix): if para['std']:rst.append(post(gp.std(), 'std')) if para['skew']:rst.append(post(gp.skew(), 'skew')) - IPy.show_table(pd.concat(rst, axis=1), tps.title+'-statistic') + self.app.show_table(pd.concat(rst, axis=1), tps.title+'-statistic') plgs = [Statistic, GroupStatistic] \ No newline at end of file diff --git a/imagepy/menus/Table/Table IO/tableio_plgs.py b/imagepy/menus/Table/Table IO/tableio_plgs.py index fcb8cd4a..289d4e9d 100644 --- a/imagepy/menus/Table/Table IO/tableio_plgs.py +++ b/imagepy/menus/Table/Table IO/tableio_plgs.py @@ -1,16 +1,15 @@ from imagepy.core.util import tableio from pandas import read_csv, read_excel, read_hdf -from imagepy import IPy -from imagepy.core.manager import ReaderManager, WriterManager, ViewerManager +from imagepy.core.manager import ReaderManager, WriterManager def show(data, title): IPy.show_table(data, title) -ViewerManager.add('tab', show) +# ViewerManager.add('tab', show) save_csv = lambda path, data:data.to_csv(path) -ReaderManager.add('csv', read_csv, tag='tab') -WriterManager.add('csv', save_csv, tag='tab') +ReaderManager.add(name='csv', obj=read_csv, tag='tab') +WriterManager.add(name='csv', obj=save_csv, tag='tab') class OpenCSV(tableio.Reader): title = 'CSV Open' @@ -21,8 +20,10 @@ class SaveCSV(tableio.Writer): filt = ['csv'] save_excel = lambda path, data:data.to_excel(path) -ReaderManager.add(['xls','xlsx'], read_excel, tag='tab') -WriterManager.add(['xls','xlsx'], save_excel, tag='tab') +ReaderManager.add(name='xls', obj=read_excel, tag='tab') +ReaderManager.add(name='xlsx', obj=read_excel, tag='tab') +WriterManager.add(name='xls', obj=save_excel, tag='tab') +WriterManager.add(name='xlsx', obj=save_excel, tag='tab') class OpenExcel(tableio.Reader): title = 'Excel Open' diff --git a/imagepy/menus/Table/Universal Generator/gnerator_plgs.py b/imagepy/menus/Table/Universal Generator/gnerator_plgs.py index 12eb99f6..945f294a 100644 --- a/imagepy/menus/Table/Universal Generator/gnerator_plgs.py +++ b/imagepy/menus/Table/Universal Generator/gnerator_plgs.py @@ -1,7 +1,6 @@ from imagepy.core.engine import Free import numpy as np import pandas as pd -from imagepy import IPy class One(Free): title = 'Unit Matrix' @@ -11,7 +10,7 @@ class One(Free): def run(self, para=None): data = np.eye(para['size']) dataframe = pd.DataFrame(data) - IPy.show_table(dataframe, 'Eye[%s,%s]'%data.shape) + self.app.show_table(dataframe, 'Eye[%s,%s]'%data.shape) class Random01(Free): title = 'Uniform Random' @@ -26,7 +25,7 @@ def run(self, para=None): data *= para['high']-para['low'] data -= para['low'] dataframe = pd.DataFrame(data) - IPy.show_table(dataframe, 'Random01[%s,%s]'%data.shape) + self.app.show_table(dataframe, 'Random01[%s,%s]'%data.shape) class RandomN(Free): title = 'Gaussian Random' @@ -41,7 +40,7 @@ def run(self, para=None): data *= para['std'] data += para['mean'] dataframe = pd.DataFrame(data) - IPy.show_table(dataframe, 'RandomN[%s,%s]'%data.shape) + self.app.show_table(dataframe, 'RandomN[%s,%s]'%data.shape) class Calendar(Free): title = 'Calendar' @@ -59,6 +58,6 @@ def run(self, para=None): a = i.replace(' ', ' None ').strip() table.append(a.replace(' ', ' ').split(' ')) dataframe = pd.DataFrame(table, columns=titles) - IPy.show_table(dataframe, ls[0].strip()) + self.app.show_table(dataframe, ls[0].strip()) plgs = [One, Random01, RandomN, Calendar] \ No newline at end of file diff --git a/imagepy/menus/Window/Windows Style/style_plgs.py b/imagepy/menus/Window/Windows Style/style_plgs.py index 335b58b3..8e71f279 100644 --- a/imagepy/menus/Window/Windows Style/style_plgs.py +++ b/imagepy/menus/Window/Windows Style/style_plgs.py @@ -1,6 +1,5 @@ from imagepy.core.engine import Free from imagepy.core.manager import ConfigManager -from imagepy import IPy class ImageJStyle(Free): title = 'Pay Tribute To ImageJ' diff --git a/imagepy/menus/Window/develop_wgts.py b/imagepy/menus/Window/develop_wgts.py index eeb6154f..4e0938f6 100644 --- a/imagepy/menus/Window/develop_wgts.py +++ b/imagepy/menus/Window/develop_wgts.py @@ -1,5 +1,5 @@ import wx, wx.lib.agw.aui as aui -from imagepy.core.manager import WidgetsManager +from sciapp import Source from imagepy.menus.Plugins.Macros.recorder_wgt import Plugin as recorder from imagepy.menus.Plugins.Manager.console_wgt import Plugin as console from imagepy.menus.Plugins.Manager.plglist_wgt import Plugin as plglist @@ -21,7 +21,8 @@ def __init__( self, parent ): self.notebook.AddPage( plglist(self), plglist.title, False, wx.NullBitmap ) self.notebook.AddPage( plgtree(self), plgtree.title, False, wx.NullBitmap ) self.notebook.AddPage( toltree(self), toltree.title, False, wx.NullBitmap ) - WidgetsManager.addref(mrecorder) + for i in range(5): self.notebook.GetPage(i).app = parent + Source.manager('widget').add('Macros Recorder', mrecorder) sizer.Add( self.notebook, 1, wx.EXPAND |wx.ALL, 0 ) self.SetSizer( sizer ) self.Fit() diff --git a/imagepy/menus/Window/widgets_plgs.py b/imagepy/menus/Window/widgets_plgs.py index ee00c448..fc8434d2 100644 --- a/imagepy/menus/Window/widgets_plgs.py +++ b/imagepy/menus/Window/widgets_plgs.py @@ -1,30 +1,19 @@ from imagepy.core.engine import Free -from imagepy import IPy -import wx class Widgets(Free): """ImageKiller: derived from imagepy.core.engine.Free""" title = 'Widgets' asyn = False - #process def run(self, para = None): - app = IPy.curapp - info = app.auimgr.GetPane(app.widgets) - info.Show(not info.IsShown()) - app.auimgr.Update() + self.app.switch_widget() class ToolBar(Free): - """ImageKiller: derived from imagepy.core.engine.Free""" title = 'Toolbar' asyn = False - #process def run(self, para = None): - app = IPy.curapp - info = app.auimgr.GetPane(app.toolbar) - info.Show(not info.IsShown()) - app.auimgr.Update() + self.app.switch_toolbar() class TableWindow(Free): """ImageKiller: derived from imagepy.core.engine.Free""" @@ -33,10 +22,6 @@ class TableWindow(Free): #process def run(self, para = None): - if IPy.uimode() != 'ipy': return - app = IPy.curapp - info = app.auimgr.GetPane(app.tablenbwrap) - info.Show(not info.IsShown()) - app.auimgr.Update() + self.app.switch_table() plgs = [Widgets, ToolBar, TableWindow] \ No newline at end of file diff --git a/imagepy/menus/Window/windowskiller_plg.py b/imagepy/menus/Window/windowskiller_plg.py index 79f92091..dee5b780 100644 --- a/imagepy/menus/Window/windowskiller_plg.py +++ b/imagepy/menus/Window/windowskiller_plg.py @@ -5,64 +5,28 @@ @author: yxl """ from imagepy.core.engine import Free -from imagepy.core.manager import ImageManager, TextLogManager, \ - TableManager, WindowsManager, WTableManager +#from imagepy.core.manager import ImageManager, TextLogManager, \ +# TableManager, WindowsManager, WTableManager class ImageKiller(Free): - """ImageKiller: derived from imagepy.core.engine.Free""" title = 'Kill Image' asyn = False - - def load(self): - ImageKiller.para = {'name':'All'} - titles =['All'] + ImageManager.get_titles() - ImageKiller.view = [(list, 'name', titles, str, 'Name', 'selected')] - return True + para = {'img':None, 'all':False} + view = [('img', 'img', 'name', ''), + (bool, 'all', 'close all images')] - #process def run(self, para = None): - if para['name'] == 'All': - for i in ImageManager.get_titles(): - WindowsManager.get(i).close() - else: - print(WindowsManager.get(para['name'])) - WindowsManager.get(para['name']).close() - -class TextKiller(Free): - """TextKiller: derived from imagepy.core.engine.Free""" - title = 'Kill TextLog' - asyn = False - - def load(self): - TextKiller.para = {'name':'All'} - titles =['All'] + TextLogManager.get_titles() - TextKiller.view = [(list, 'name', titles, str, 'Name', 'selected')] - return True - - #process - def run(self, para = None): - if para['name'] == 'All': - for i in TextLogManager.get_titles(): - TextLogManager.close(i) - else: TextLogManager.close(para['name']) + self.app.close_image(None if para['all'] else para['img']) class TableKiller(Free): - """TableKiller: derived from imagepy.core.engine.Free""" - title = 'Kill TableLog' + title = 'Kill Table' asyn = False - - def load(self): - self.para = {'name':'All'} - titles = ['All'] + TableManager.get_titles() - self.view = [(list, 'name', titles, str, 'Name', 'selected')] - return True + para = {'tab':None, 'all':False} + view = [('tab', 'tab', 'name', ''), + (bool, 'all', 'close all tables')] - #process def run(self, para = None): - if para['name'] == 'All': - for i in TableManager.get_titles(): - WTableManager.get(i).close() - else: WTableManager.get(para['name']).close() + self.app.close_table(None if para['all'] else para['tab']) #!TODO: plugins ?! -plgs = [ImageKiller, TextKiller, TableKiller] \ No newline at end of file +plgs = [ImageKiller, TableKiller] \ No newline at end of file diff --git a/imagepy/tools/Measure/angle2_tol.py b/imagepy/tools/Measure/angle2_tol.py index b1c7fec8..3048dee4 100644 --- a/imagepy/tools/Measure/angle2_tol.py +++ b/imagepy/tools/Measure/angle2_tol.py @@ -11,7 +11,7 @@ import pandas as pd from numpy.linalg import norm from .setting import Setting -from imagepy import IPy +#from imagepy import IPy class Angle: """Define the class with line drawing fucntions """ diff --git a/imagepy/tools/Measure/angle_tol.py b/imagepy/tools/Measure/angle_tol.py index c7355def..bc55f609 100644 --- a/imagepy/tools/Measure/angle_tol.py +++ b/imagepy/tools/Measure/angle_tol.py @@ -10,7 +10,7 @@ import pandas as pd from numpy.linalg import norm from .setting import Setting -from imagepy import IPy +#from imagepy import IPy class Angle: """Define the class with line drawing fucntions """ diff --git a/imagepy/tools/Measure/area_tol.py b/imagepy/tools/Measure/area_tol.py index e2e56d83..0d827783 100644 --- a/imagepy/tools/Measure/area_tol.py +++ b/imagepy/tools/Measure/area_tol.py @@ -9,7 +9,7 @@ from shapely.geometry import Polygon, Point from imagepy.core.engine import Tool from .setting import Setting -from imagepy import IPy +#from imagepy import IPy import pandas as pd class Area: diff --git a/imagepy/tools/Measure/coordinate_tol.py b/imagepy/tools/Measure/coordinate_tol.py index 4b43fdb2..3eb58151 100644 --- a/imagepy/tools/Measure/coordinate_tol.py +++ b/imagepy/tools/Measure/coordinate_tol.py @@ -8,7 +8,7 @@ import wx from imagepy.core.engine import Tool from .setting import Setting -from imagepy import IPy +#from imagepy import IPy import pandas as pd class Coordinate: diff --git a/imagepy/tools/Measure/distance_tol.py b/imagepy/tools/Measure/distance_tol.py index 373fa7cd..3901add6 100644 --- a/imagepy/tools/Measure/distance_tol.py +++ b/imagepy/tools/Measure/distance_tol.py @@ -11,7 +11,7 @@ import pandas as pd from numpy.linalg import norm from .setting import Setting -from imagepy import IPy +#from imagepy import IPy class Distance: """Define the distance class""" diff --git a/imagepy/tools/Measure/profile_tol.py b/imagepy/tools/Measure/profile_tol.py index 3bd0ed48..0c50af8b 100644 --- a/imagepy/tools/Measure/profile_tol.py +++ b/imagepy/tools/Measure/profile_tol.py @@ -6,7 +6,7 @@ """ import wx -from imagepy import IPy +#from imagepy import IPy from imagepy.core.engine import Tool import numpy as np import pandas as pd diff --git a/imagepy/tools/Standard/freearea_tol.py b/imagepy/tools/Standard/freearea_tol.py index 422c6be1..3f95cb4e 100644 --- a/imagepy/tools/Standard/freearea_tol.py +++ b/imagepy/tools/Standard/freearea_tol.py @@ -1,72 +1 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Oct 19 17:35:09 2016 - -@author: yxl -""" -from imagepy.core.roi import polygonroi -import wx -from .polygon_tol import Polygonbuf -from imagepy.core.engine import Tool - -class Plugin(Tool): - """FreeArea class plugin with events callbacks""" - title = 'Free Area' - def __init__(self): - self.curobj = None - self.doing = False - self.oper = '' - self.helper = Polygonbuf() - - def mouse_down(self, ips, x, y, btn, **key): - lim = 5.0/key['canvas'].scale - ips.mark = self.helper - if btn==1: - if not self.doing: - if ips.roi!= None: - self.curobj = ips.roi.pick(x, y, ips.cur, lim) - ips.roi.info(ips, self.curobj) - if not self.curobj in (None,True):return - #self.oper = '+' - if ips.roi == None: - ips.roi = polygonroi.PolygonRoi() - self.doing = True - elif hasattr(ips.roi, 'topolygon'): - if key['shift']: - ips.roi = ips.roi.topolygon() - self.oper,self.doing = '+',True - elif key['ctrl']: - ips.roi = ips.roi.topolygon() - self.oper,self.doing = '-',True - elif self.curobj: return - else: ips.roi=None - else: ips.roi = None - if self.doing: - self.helper.addpoint((x,y)) - self.odx, self.ody = x, y - ips.update() - - def mouse_up(self, ips, x, y, btn, **key): - if self.doing: - self.helper.addpoint((x,y)) - self.doing = False - self.curobj = None - ips.roi.commit(self.helper.pop(), self.oper) - ips.update() - - def mouse_move(self, ips, x, y, btn, **key): - if ips.roi==None:return - lim = 5.0/key['canvas'].scale - if btn==None: - self.cursor = wx.CURSOR_CROSS - if ips.roi.snap(x, y, ips.cur, lim)!=None: - self.cursor = wx.CURSOR_HAND - elif btn==1: - if self.doing: - self.helper.addpoint((x,y)) - elif self.curobj: ips.roi.draged(self.odx, self.ody, x, y, ips.cur, self.curobj) - ips.update() - self.odx, self.ody = x, y - - def mouse_wheel(self, ips, x, y, d, **key): - pass \ No newline at end of file +from sciapp.action import FreePolygonROI as Plugin \ No newline at end of file diff --git a/imagepy/tools/Standard/freeline_tol.py b/imagepy/tools/Standard/freeline_tol.py index 4201da44..f8888bc5 100644 --- a/imagepy/tools/Standard/freeline_tol.py +++ b/imagepy/tools/Standard/freeline_tol.py @@ -1,81 +1 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Oct 19 17:35:09 2016 - -@author: yxl -""" -from imagepy.core.roi import lineroi -import wx -from imagepy.core.engine import Tool - -class Linebuf: - """FreeLinebuf class""" - title = 'Free Line' - def __init__(self): - self.buf = [] - - def addpoint(self, p): - self.buf.append(p) - - def draw(self, dc, f, **key): - dc.SetPen(wx.Pen((0,255,255), width=1, style=wx.SOLID)) - if len(self.buf)>1: - dc.DrawLines([f(*i) for i in self.buf]) - for i in self.buf:dc.DrawCircle(f(*i),2) - - def pop(self): - a = self.buf - self.buf = [] - return a - -class Plugin(Tool): - """FreeLinebuf class plugin with events callbacks""" - def __init__(self): - self.curobj = None - self.doing = False - self.helper = Linebuf() - self.odx,self.ody = 0, 0 - - def mouse_down(self, ips, x, y, btn, **key): - lim = 5.0/key['canvas'].scale - ips.mark = self.helper - if btn==1: - if not self.doing: - if ips.roi!= None: - self.curobj = ips.roi.pick(x, y, ips.cur, lim) - ips.roi.info(ips, self.curobj) - if self.curobj!=None:return - - if ips.roi == None: - ips.roi = lineroi.LineRoi() - self.doing = True - elif ips.roi.dtype=='line' and key['shift']: - self.doing = True - else: ips.roi = None - if self.doing: - self.helper.addpoint((x,y)) - self.odx, self.ody = x,y - - def mouse_up(self, ips, x, y, btn, **key): - if self.doing: - self.doing = False - self.curobj = None - ips.roi.addline(self.helper.pop()) - ips.update() - - def mouse_move(self, ips, x, y, btn, **key): - if ips.roi==None:return - lim = 5.0/key['canvas'].scale - if btn==None: - self.cursor = wx.CURSOR_CROSS - if ips.roi.snap(x, y, ips.cur, lim)!=None: - self.cursor = wx.CURSOR_HAND - elif btn==1: - if self.doing: - self.helper.addpoint((x,y)) - elif self.curobj: ips.roi.draged(self.odx, self.ody, x, y, ips.cur, self.curobj) - ips.update() - self.odx, self.ody = x, y - - def mouse_wheel(self, ips, x, y, d, **key): - pass \ No newline at end of file +from sciapp.action import FreeLineROI as Plugin \ No newline at end of file diff --git a/imagepy/tools/Standard/line_tol.py b/imagepy/tools/Standard/line_tol.py index 2fd9b9ed..935a40b6 100644 --- a/imagepy/tools/Standard/line_tol.py +++ b/imagepy/tools/Standard/line_tol.py @@ -1,87 +1 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Oct 19 17:35:09 2016 - -@author: yxl -""" -from imagepy.core.roi import lineroi -import wx -from imagepy.core.engine import Tool - -class Linebuf: - """Linebuf class""" - def __init__(self): - self.buf = [] - - def addpoint(self, p): - self.buf.append(p) - - def draw(self, dc, f, **key): - dc.SetPen(wx.Pen((0,255,255), width=1, style=wx.SOLID)) - if len(self.buf)>1: - dc.DrawLines([f(*i) for i in self.buf]) - for i in self.buf:dc.DrawCircle(f(*i),2) - - def pop(self): - a = self.buf - self.buf = [] - return a - -class Plugin(Tool): - """FreeLinebuf class plugin with events callbacks""" - title = 'Line' - def __init__(self): - self.curobj = None - self.doing = False - self.helper = Linebuf() - self.odx,self.ody = 0, 0 - - def mouse_down(self, ips, x, y, btn, **key): - lim = 5.0/key['canvas'].scale - ips.mark = self.helper - if btn==1: - if not self.doing: - print(ips.roi) - print(self.curobj) - if ips.roi!= None: - self.curobj = ips.roi.pick(x, y, ips.cur, lim) - ips.roi.info(ips, self.curobj) - if self.curobj!=None:return - - if ips.roi == None: - print(1) - ips.roi = lineroi.LineRoi() - self.doing = True - elif ips.roi.dtype=='line' and key['shift']: - print(2) - self.doing = True - else: ips.roi = None - if self.doing: - self.helper.addpoint((x,y)) - self.curobj = (self.helper.buf, -1) - self.odx, self.ody = x,y - - elif btn==3: - if self.doing: - self.helper.addpoint((x,y)) - self.doing = False - ips.roi.addline(self.helper.pop()) - ips.update() - - def mouse_up(self, ips, x, y, btn, **key): - self.curobj = None - - def mouse_move(self, ips, x, y, btn, **key): - if ips.roi==None:return - lim = 5.0/key['canvas'].scale - if btn==None: - self.cursor = wx.CURSOR_CROSS - if ips.roi.snap(x, y, ips.cur, lim)!=None: - self.cursor = wx.CURSOR_HAND - elif btn==1: - ips.roi.draged(self.odx, self.ody, x, y, ips.cur, self.curobj) - ips.update() - self.odx, self.ody = x, y - - def mouse_wheel(self, ips, x, y, d, **key): - pass \ No newline at end of file +from sciapp.action import LineROI as Plugin \ No newline at end of file diff --git a/imagepy/tools/Standard/oval_tol.py b/imagepy/tools/Standard/oval_tol.py index 2faf4db4..07a9a360 100644 --- a/imagepy/tools/Standard/oval_tol.py +++ b/imagepy/tools/Standard/oval_tol.py @@ -1,88 +1 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Oct 19 17:35:09 2016 - -@author: yxl -""" -from imagepy.core.roi import ovalroi -import numpy as np -import wx -from .polygon_tol import Polygonbuf -from imagepy.core.engine import Tool - -class Plugin(Tool): - title = 'Ellipse' - def __init__(self): - self.curobj = None - self.doing = False - self.oper = '' - self.helper = Polygonbuf() - - def mouse_down(self, ips, x, y, btn, **key): - lim = 5.0/key['canvas'].scale - ips.mark = self.helper - if btn==1: - if not self.doing: - if ips.roi!= None: - self.curobj = ips.roi.pick(x, y, ips.cur, lim) - ips.roi.info(ips, self.curobj) - if not self.curobj in (None,True):return - self.oper = '+' - if ips.roi==None or not hasattr(ips.roi, 'topolygon'): - ips.roi = ovalroi.OvalRoi() - self.doing = True - ips.roi.lt, ips.roi.tp = x, y - ips.roi.rt, ips.roi.bm = x, y - self.curobj = 'rb' - self.odx, self.ody = x,y - elif hasattr(ips.roi, 'topolygon'): - self.odx, self.ody = x, y - self.ox, self.oy = x, y - if key['shift']: - ips.roi = ips.roi.topolygon() - self.oper,self.doing,self.curobj = '+',True,None - elif key['ctrl']: - ips.roi = ips.roi.topolygon() - self.oper,self.doing,self.curobj = '-',True,None - elif self.curobj: return - else: - ips.roi = ovalroi.OvalRoi() - self.doing = True - ips.roi.lt, ips.roi.tp = x, y - ips.roi.rt, ips.roi.bm = x, y - self.curobj = 'rb' - self.odx, self.ody = x,y - else: ips.roi = None - - ips.update() - - def mouse_up(self, ips, x, y, btn, **key): - if self.doing: - self.doing = False - self.curobj = None - if ips.roi.dtype == 'rect': - if not ips.roi.commit():ips.roi = None - elif ips.roi.dtype == 'polygon': - ips.roi.commit(self.helper.pop(), self.oper) - ips.update() - - def mouse_move(self, ips, x, y, btn, **key): - if ips.roi==None:return - lim = 5.0/key['canvas'].scale - if btn==None: - self.cursor = wx.CURSOR_CROSS - if ips.roi.snap(x, y, ips.cur, lim)!=None: - self.cursor = wx.CURSOR_HAND - elif btn==1: - if ips.roi.dtype == 'polygon' and self.doing: - l,b,r,t = self.ox, self.oy, x, y - ar = np.linspace(0, np.pi*2,29) - xs = np.cos(ar)*abs(r-l)/2+(r+l)/2 - ys = np.sin(ar)*abs(t-b)/2+(t+b)/2 - self.helper.buf = [[(x,y) for x,y in zip(xs,ys)],[]] - if self.curobj: ips.roi.draged(self.odx, self.ody, x, y, ips.cur, self.curobj) - ips.update() - self.odx, self.ody = x, y - - def mouse_wheel(self, ips, x, y, d, **key): - pass \ No newline at end of file +from sciapp.action import EllipseROI as Plugin \ No newline at end of file diff --git a/imagepy/tools/Standard/point_tol.py b/imagepy/tools/Standard/point_tol.py index 1284dda1..3d2a288d 100644 --- a/imagepy/tools/Standard/point_tol.py +++ b/imagepy/tools/Standard/point_tol.py @@ -1,54 +1 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Oct 19 17:35:09 2016 - -@author: yxl -""" -from imagepy.core.roi import pointroi -import wx -from imagepy.core.engine import Tool - -class Plugin(Tool): - title = 'Point' - def __init__(self): - self.curobj, self.onobj = None, None - self.odx, self.ody = 0, 0 - - def mouse_down(self, ips, x, y, btn, **key): - lim = 5.0/key['canvas'].scale - if btn==1: - if ips.roi!=None: - self.curobj = ips.roi.pick(x, y, ips.cur, lim) - ips.roi.info(ips, self.curobj) - if self.curobj!=None:return - if not isinstance(ips.roi, pointroi.PointRoi): - ips.roi = pointroi.PointRoi() - if not key['shift']:del ips.roi.body[:] - ips.roi.add((x,y,ips.cur)) - self.curobj = ips.roi.pick(x, y, ips.cur, lim) - ips.update() - self.odx, self.ody = x, y - - def mouse_up(self, ips, x, y, btn, **key): - self.curobj = None - - def mouse_move(self, ips, x, y, btn, **key): - if ips.roi==None:return - lim = 5.0/key['canvas'].scale - if btn==None: - self.cursor = wx.CURSOR_CROSS - self.onobj = ips.roi.snap(x, y, ips.cur, lim) - if self.onobj!=None: - self.cursor = wx.CURSOR_HAND - elif btn==1: - ips.roi.draged(self.odx, self.ody, x, y, ips.cur, self.curobj) - ips.update() - self.odx, self.ody = x, y - - def mouse_wheel(self, ips, x, y, d, **key): - lim = 5.0/key['canvas'].scale - if not isinstance(ips.roi, pointroi.PointRoi):return - if not self.onobj is None: - cur = ips.roi.body[self.onobj][2] - ips.roi.draged(self.odx, self.ody, x, y, cur+d, self.onobj) - ips.update() \ No newline at end of file +from sciapp.action import PointROI as Plugin \ No newline at end of file diff --git a/imagepy/tools/Standard/polygon_tol.py b/imagepy/tools/Standard/polygon_tol.py index 73867bcd..86fabfb6 100644 --- a/imagepy/tools/Standard/polygon_tol.py +++ b/imagepy/tools/Standard/polygon_tol.py @@ -1,86 +1 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Oct 19 17:35:09 2016 - -@author: yxl -""" -from imagepy.core.roi import polygonroi -import wx - -class Polygonbuf: - def __init__(self): - self.buf = [[],[]] - - def addpoint(self, p): - self.buf[0].append(p) - - def draw(self, dc, f, **key): - dc.SetPen(wx.Pen((0,255,0), width=1, style=wx.SOLID)) - if len(self.buf[0])>1: - dc.DrawLines([f(*i) for i in self.buf[0]]) - for i in self.buf[0]: dc.DrawCircle(f(*i),2) - - def pop(self): - a = self.buf - self.buf = [[],[]] - return a - -from imagepy.core.engine import Tool - -class Plugin(Tool): - title = 'Polygon' - def __init__(self): - self.curobj = None - self.doing = False - self.oper = '' - self.helper = Polygonbuf() - - def mouse_down(self, ips, x, y, btn, **key): - lim = 5.0/key['canvas'].scale - ips.mark = self.helper - if btn==1: - if not self.doing: - if ips.roi!= None: - self.curobj = ips.roi.pick(x, y, ips.cur, lim) - ips.roi.info(ips, self.curobj) - if not self.curobj in (None,True):return - self.oper = '+' - if ips.roi == None: - ips.roi = polygonroi.PolygonRoi() - self.doing = True - elif hasattr(ips.roi, 'topolygon'): - if key['shift']: - ips.roi = ips.roi.topolygon() - self.oper,self.doing = '+',True - elif key['ctrl']: - ips.roi = ips.roi.topolygon() - self.oper,self.doing = '-',True - elif self.curobj: return - else: ips.roi=None - else: ips.roi = None - if self.doing: - self.helper.addpoint((x,y)) - self.curobj = (self.helper.buf[0], -1) - self.odx, self.ody = x, y - - elif btn==3: - if self.doing: - self.helper.addpoint((x,y)) - self.doing = False - ips.roi.commit(self.helper.pop(), self.oper) - ips.update() - - def mouse_move(self, ips, x, y, btn, **key): - if ips.roi==None:return - lim = 5.0/key['canvas'].scale - if btn==None: - self.cursor = wx.CURSOR_CROSS - if ips.roi.snap(x, y, ips.cur, lim)!=None: - self.cursor = wx.CURSOR_HAND - elif btn==1: - ips.roi.draged(self.odx, self.ody, x, y, ips.cur, self.curobj) - ips.update() - self.odx, self.ody = x, y - - def on_switch(self): - print('hahaha') \ No newline at end of file +from sciapp.action import PolygonROI as Plugin \ No newline at end of file diff --git a/imagepy/tools/Standard/rectangle_tol.py b/imagepy/tools/Standard/rectangle_tol.py index 28e998fc..c73ba4d5 100644 --- a/imagepy/tools/Standard/rectangle_tol.py +++ b/imagepy/tools/Standard/rectangle_tol.py @@ -1,84 +1 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Oct 19 17:35:09 2016 - -@author: yxl -""" -from imagepy.core.roi import rectangleroi, polygonroi -import wx -from .polygon_tol import Polygonbuf -from imagepy.core.engine import Tool - -class Plugin(Tool): - title = 'Rectangle' - def __init__(self): - self.curobj = None - self.doing = False - self.oper = '' - self.helper = Polygonbuf() - - def mouse_down(self, ips, x, y, btn, **key): - lim = 5.0/key['canvas'].scale - ips.mark = self.helper - if btn==1: - if not self.doing: - if ips.roi!= None: - self.curobj = ips.roi.pick(x, y, ips.cur, lim) - ips.roi.info(ips, self.curobj) - if not self.curobj in (None,True):return - self.oper = '+' - if ips.roi==None or not hasattr(ips.roi, 'topolygon'): - ips.roi = rectangleroi.RectangleRoi() - self.doing = True - ips.roi.lt, ips.roi.tp = x, y - ips.roi.rt, ips.roi.bm = x, y - self.curobj = 'rb' - self.odx, self.ody = x,y - elif hasattr(ips.roi, 'topolygon'): - self.odx, self.ody = x, y - self.ox, self.oy = x, y - if key['shift']: - ips.roi = ips.roi.topolygon() - self.oper,self.doing,self.curobj = '+',True,None - elif key['ctrl']: - ips.roi = ips.roi.topolygon() - self.oper,self.doing,self.curobj = '-',True,None - elif self.curobj: return - else: - ips.roi = rectangleroi.RectangleRoi() - self.doing = True - ips.roi.lt, ips.roi.tp = x, y - ips.roi.rt, ips.roi.bm = x, y - self.curobj = 'rb' - self.odx, self.ody = x,y - else: ips.roi = None - - ips.update() - - def mouse_up(self, ips, x, y, btn, **key): - if self.doing: - self.doing = False - self.curobj = None - if ips.roi.dtype == 'rect': - if not ips.roi.commit():ips.roi = None - elif ips.roi.dtype == 'polygon': - ips.roi.commit(self.helper.pop(), self.oper) - ips.update() - - def mouse_move(self, ips, x, y, btn, **key): - if ips.roi==None:return - lim = 5.0/key['canvas'].scale - if btn==None: - self.cursor = wx.CURSOR_CROSS - if ips.roi.snap(x, y, ips.cur, lim)!=None: - self.cursor = wx.CURSOR_HAND - elif btn==1: - if ips.roi.dtype == 'polygon' and self.doing: - l,b,r,t = self.ox, self.oy, x, y - self.helper.buf = [[(l,b),(r,b),(r,t),(l,t),(l,b)],[]] - if self.curobj: ips.roi.draged(self.odx, self.ody, x, y, ips.cur, self.curobj) - ips.update() - self.odx, self.ody = x, y - - def mouse_wheel(self, ips, x, y, d, **key): - pass \ No newline at end of file +from sciapp.action import RectangleROI as Plugin \ No newline at end of file diff --git a/imagepy/tools/Toolkit3D/cursor3d_tol.py b/imagepy/tools/Toolkit3D/cursor3d_tol.py index 12087ebe..4d94e9e2 100644 --- a/imagepy/tools/Toolkit3D/cursor3d_tol.py +++ b/imagepy/tools/Toolkit3D/cursor3d_tol.py @@ -1,7 +1,7 @@ import wx from imagepy.core.engine import Tool -from imagepy.core import myvi -from imagepy import IPy +#from imagepy.core import myvi +#from imagepy import IPy import numpy as np class Plugin(Tool): diff --git a/imagepy/ui/__init__.py b/imagepy/ui/__init__.py deleted file mode 100644 index 08a7b888..00000000 --- a/imagepy/ui/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -''' -from __future__ import absolute_import -from . import canvasframe -from . import panelconfig -from . import widgets -from . import canvas -from . import logwindow -from . import plotwindow -from . import tablewindow -from . import imagepy -from . import macroseditor -from . import pluginloader -from . import toolsloader -''' \ No newline at end of file diff --git a/imagepy/ui/canvas/__init__.py b/imagepy/ui/canvas/__init__.py deleted file mode 100644 index 746eb82d..00000000 --- a/imagepy/ui/canvas/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .canvas import Canvas \ No newline at end of file diff --git a/imagepy/ui/canvasframe.py b/imagepy/ui/canvasframe.py deleted file mode 100644 index 430ceffa..00000000 --- a/imagepy/ui/canvasframe.py +++ /dev/null @@ -1,369 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Oct 14 01:24:41 2016 - -@author: yxl -""" -import wx, os -import wx.lib.agw.aui as aui -from .canvas import Canvas -from ..core.manager import ImageManager, WindowsManager, ToolsManager -from ..core.manager import ShotcutManager -from .. import IPy, root_dir -import numpy as np -import weakref - -class CanvasPanel(wx.Panel): - """CanvasFrame: derived from the wx.core.Frame""" - ## TODO: Main frame ??? - def __init__(self, parent=None): - wx.Frame.__init__ ( self, parent) - - #self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_3DLIGHT ) ) - - self.SetSizeHints( wx.Size( 560,-1 ), wx.DefaultSize ) - WindowsManager.add(self) - - self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) - self.SetBackgroundColour( wx.Colour( 255, 255, 255 ) ) - - sizer = wx.BoxSizer( wx.VERTICAL ) - self.txt_info = wx.StaticText( self, wx.ID_ANY, - '500*500 pixels 173k', - wx.DefaultPosition, wx.DefaultSize, 0 ) - self.txt_info.Wrap( -1 ) - sizer.Add( self.txt_info, 0, wx.ALL, 0 ) - - self.canvas = Canvas(self, autofit = IPy.uimode()=='ij') - self.canvas.Bind(wx.EVT_MOUSE_EVENTS, self.on_mouse) - self.Bind(wx.EVT_IDLE, self.on_idle) - #self.canvas.set_handler(self.set_info) - self.handle = None - sizer.Add( self.canvas, 1, wx.EXPAND |wx.ALL, 0 ) - - self.chan = wx.Slider( self, wx.ID_ANY, 0, 0, 100, wx.DefaultPosition, wx.DefaultSize, - wx.SL_HORIZONTAL| wx.SL_SELRANGE| wx.SL_TOP) - sizer.Add( self.chan, 0, wx.ALL|wx.EXPAND, 0 ) - self.chan.SetMaxSize( wx.Size( -1,18 ) ) - self.chan.Hide() - - self.page = wx.ScrollBar( self, wx.ID_ANY, - wx.DefaultPosition, wx.DefaultSize, wx.SB_HORIZONTAL) - self.page.SetScrollbar(0,0,0,0, refresh=True) - sizer.Add( self.page, 0, wx.ALL|wx.EXPAND, 0 ) - self.page.Hide() - - self.SetSizer(sizer) - self.Layout() - self.page.Bind(wx.EVT_SCROLL, self.on_scroll) - self.chan.Bind(wx.EVT_SCROLL, self.on_scroll) - # panel.Bind(wx.EVT_CHAR, self.OnKeyDown) - self.opage = self.ochan = 0 - self.chantype = None - self.ips = self.back = None - self.olddia = 0 - #self.Fit() - - #self.SetAcceleratorTable(IPy.curapp.shortcut) - - ''' - def SetTitle(self, title): - parent = self.GetParent() - if not IPy.aui: parent.SetTitle(title) - else: parent.SetPageText(parent.GetPageIndex(self), title) - #print(dir(parent)) #parent.DeletePage(parent.GetPageIndex(self)) - ''' - - def on_mouse(self, me): - tool = self.ips.tool - if tool == None : tool = ToolsManager.curtool - x,y = self.canvas.to_data_coor(me.GetX(), me.GetY()) - if me.Moving() and not me.LeftIsDown() and not me.RightIsDown() and not me.MiddleIsDown(): - xx,yy = int(round(x)), int(round(y)) - k, unit = self.ips.unit - if xx>=0 and xx=0 and yy1 else '', ips.size[0], ips.size[1], ips.imgtype, round(ips.get_nbytes()/1024.0/1024.0, 2)) - if label != self.txt_info.GetLabel(): self.txt_info.SetLabel(label) - - def set_fit(self, ips): - resize = False - if ips.get_nslices() != self.opage: - self.opage = ips.get_nslices() - if ips.get_nslices()==1 and self.page.Shown: - self.page.Hide() - resize = True - if ips.get_nslices()>1 and not self.page.Shown: - self.page.Show() - print('Show ......') - resize = True - self.page.SetScrollbar(0, 0, ips.get_nslices()-1, 0, refresh=True) - if ips.get_nchannels() != self.ochan or type(ips.chan) != self.chantype: - self.ochan = ips.get_nchannels() - self.chantype = type(ips.chan) - isrgb = not isinstance(ips.chan, int) - if not isinstance(ips.chan, int) and self.chan.Shown: - self.chan.Hide() - resize = True - if isinstance(ips.chan, int) and ips.get_nchannels()>1 and not self.chan.Shown: - self.chan.Show() - resize = True - self.chan.SetMax(ips.get_nchannels()-1) - a,b,c,d = self.canvas.conbox - l = ((c-a)**2+(d-b)**2)**0.5 - if resize or abs(self.olddia-l)>1: - self.olddia = l - if IPy.uimode()!='ipy': - w = self.canvas.scrbox[0]*0.9 - h = self.canvas.scrbox[1]*0.9 - if c-a{'path':'%s'}"%i for i in path]) - Macros('noname', ["Open>{'path':'%s'}"%i.replace('\\', '/') for i in path]).start() - return 0 - -class ImagePy(wx.Frame): - def __init__( self, parent ): - wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = 'ImagePy', - size = wx.Size(-1,-1), pos = wx.DefaultPosition, - style = wx.RESIZE_BORDER|wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) - self.auimgr = aui.AuiManager() - self.auimgr.SetManagedWindow( self ) - #self.auimgr.SetFlags(aui.AUI_MGR_DEFAULT) - - logopath = os.path.join(root_dir, 'data/logo.ico') - #self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_3DLIGHT ) ) - self.SetIcon(wx.Icon(logopath, wx.BITMAP_TYPE_ICO)) - IPy.curapp = self - self.SetSizeHints( wx.Size(1024,768) if IPy.uimode() == 'ipy' else wx.Size( 700,-1 )) - - - self.menubar = pluginloader.buildMenuBarByPath(self, 'menus', 'plugins', None, True) - self.SetMenuBar( self.menubar ) - self.shortcut = pluginloader.buildShortcut(self) - self.SetAcceleratorTable(self.shortcut) - #sizer = wx.BoxSizer(wx.VERTICAL) - self.toolbar = toolsloader.build_tools(self, 'tools', 'plugins', None, True) - - - print(IPy.uimode()) - if IPy.uimode()=='ipy': self.load_aui() - else: self.load_ijui() - self.load_document() - self.Fit() - - - self.stapanel = stapanel = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) - sizersta = wx.BoxSizer( wx.HORIZONTAL ) - self.txt_info = wx.StaticText( stapanel, wx.ID_ANY, "ImagePy v0.2", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.txt_info.Wrap( -1 ) - #self.txt_info.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_INFOBK ) ) - sizersta.Add( self.txt_info, 1, wx.ALIGN_BOTTOM|wx.BOTTOM|wx.LEFT|wx.RIGHT, 2 ) - self.pro_bar = wx.Gauge( stapanel, wx.ID_ANY, 100, wx.DefaultPosition, wx.Size( 100,15 ), wx.GA_HORIZONTAL ) - sizersta.Add( self.pro_bar, 0, wx.ALIGN_BOTTOM|wx.BOTTOM|wx.LEFT|wx.RIGHT, 2 ) - stapanel.SetSizer(sizersta) - stapanel.SetDropTarget(FileDrop()) - self.auimgr.AddPane( stapanel, aui.AuiPaneInfo() .Bottom() .CaptionVisible( False ).PinButton( True ) - .PaneBorder( False ).Dock().Resizable().FloatingSize( wx.DefaultSize ).DockFixed( True ) - . MinSize(wx.Size(-1, 20)). MaxSize(wx.Size(-1, 20)).Layer( 10 ) ) - - - self.Centre( wx.BOTH ) - self.Layout() - self.auimgr.Update() - self.Fit() - self.Centre( wx.BOTH ) - if(IPy.uimode()=='ij'): - self.SetMaxSize((-1, self.GetSize()[1])) - self.SetMinSize(self.GetSize()) - self.update = False - - self.Bind(wx.EVT_CLOSE, self.on_close) - self.Bind(aui.EVT_AUI_PANE_CLOSE, self.on_pan_close) - thread = threading.Thread(None, self.hold, ()) - thread.setDaemon(True) - thread.start() - - def load_aui(self): - self.toolbar.GetSizer().SetOrientation(wx.VERTICAL) - self.toolbar.GetSizer().Layout() - self.toolbar.Fit() - self.auimgr.AddPane(self.toolbar, aui.AuiPaneInfo() .Left() .PinButton( True ) - .CaptionVisible( True ).Dock().Resizable().FloatingSize( wx.DefaultSize ).MaxSize(wx.Size( 32,-1 )) - . BottomDockable( True ).TopDockable( False ).Layer( 10 ) ) - self.widgets = widgetsloader.build_widgets(self, 'widgets', 'plugins') - self.auimgr.AddPane( self.widgets, aui.AuiPaneInfo() .Right().Caption('Widgets') .PinButton( True ) - .Dock().Resizable().FloatingSize( wx.DefaultSize ).MinSize( wx.Size( 266,-1 ) ) .Layer( 10 ) ) - - self.canvasnbwrap = wx.Panel(self) - sizer = wx.BoxSizer( wx.VERTICAL ) - self.canvasnb = CanvasNoteBook( self.canvasnbwrap) - sizer.Add( self.canvasnb, 1, wx.EXPAND |wx.ALL, 0 ) - self.canvasnbwrap.SetSizer( sizer ) - self.canvasnbwrap.Layout() - self.auimgr.AddPane( self.canvasnbwrap, aui.AuiPaneInfo() .Center() .CaptionVisible( False ).PinButton( True ).Dock() - .PaneBorder( False ).Resizable().FloatingSize( wx.DefaultSize ). BottomDockable( True ).TopDockable( False ) - .LeftDockable( True ).RightDockable( True ) ) - - self.tablenbwrap = wx.Panel(self) - sizer = wx.BoxSizer( wx.VERTICAL ) - self.tablenb = TableNoteBook( self.tablenbwrap) - sizer.Add( self.tablenb, 1, wx.EXPAND |wx.ALL, 0 ) - self.tablenbwrap.SetSizer( sizer ) - self.tablenbwrap.Layout() - - self.auimgr.AddPane( self.tablenbwrap, aui.AuiPaneInfo() .Bottom() .CaptionVisible( True ).PinButton( True ).Dock().Hide() - .MaximizeButton( True ).Resizable().FloatingSize((800, 600)).BestSize(( 120,120 )). Caption('Tables') . - BottomDockable( True ).TopDockable( False ).LeftDockable( True ).RightDockable( True ) ) - img = ConfigManager.get('watermark') or '' - if not os.path.exists(img): img = 'data/watermark.png' - self.set_background(img) - #self.auimgr.SetArtProvider(MyArtProvider()) - #self.canvasnb.Bind( aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_pagevalid) - - def load_ijui(self): - self.auimgr.AddPane(self.toolbar, aui.AuiPaneInfo() .Top() .CaptionVisible( False ).PinButton( True ) - .PaneBorder( False ).Dock().Resizable().FloatingSize( wx.DefaultSize ).DockFixed( True ) - .BottomDockable( False ).TopDockable( False ).LeftDockable( False ).RightDockable( False ) - .MinSize(wx.Size(-1, 32)). Layer( 10 ) ) - self.widgets = widgetsloader.build_widgets(self, 'widgets', 'plugins') - self.auimgr.AddPane( self.widgets, aui.AuiPaneInfo() .Right().Caption('Widgets') .PinButton( True ) - .Float().Resizable().FloatingSize( wx.DefaultSize ).MinSize( wx.Size( 266,-1 ) ).Hide() .Layer( 10 ) ) - - def load_dev(self): - return - self.devpan = aui.AuiNotebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, aui.AUI_NB_DEFAULT_STYLE ) - self.auimgr.AddPane( self.devpan, waui.AuiPaneInfo() .Bottom() .CaptionVisible( False ).PinButton( True ).Dock() - .PaneBorder( False ).Resizable().FloatingSize( wx.DefaultSize ) ) - - def on_pan_close(self, event): - if event.GetPane().window in [self.toolbar, self.widgets]: - event.Veto() - if hasattr(event.GetPane().window, 'close'): - event.GetPane().window.close() - - def load_document(self): - from glob import glob - extends = glob('plugins/*/doc') - for i in extends: loader.build_document(i) - loader.build_document('doc') - - def reload_plugins(self, report=False, menus=True, tools=False, widgets=False): - if menus: pluginloader.buildMenuBarByPath(self, 'menus', 'plugins', self.menubar, report) - if tools: toolsloader.build_tools(self, 'tools', 'plugins', self.toolbar, report) - if widgets: widgetsloader.build_widgets(self, 'widgets', 'plugins', self.widgets) - self.load_document() - if IPy.uimode()!='ipy': self.Fit() - - def hold(self): - dire = 1 - while True: - try: - if time == None: break - time.sleep(0.05) - tasks = TaskManager.get() - if(len(tasks)==0): - if self.pro_bar.IsShown(): - wx.CallAfter(self.set_progress, -1) - continue - arr = [i.prgs for i in tasks] - if (None, 1) in arr: - if self.pro_bar.GetValue()<=0: - dire = 1 - if self.pro_bar.GetValue()>=100: - dire = -1 - v = self.pro_bar.GetValue()+dire*5 - wx.CallAfter(self.set_progress, v) - else: - v = max([(i[0]+1)*100.0/i[1] for i in arr]) - wx.CallAfter(self.set_progress, v) - except: - pass - def set_info(self, value): - self.txt_info.SetLabel(value) - - def set_progress(self, value): - v = max(min(value, 100), 0) - self.pro_bar.SetValue(v) - if value==-1: - self.pro_bar.Hide() - elif not self.pro_bar.IsShown(): - self.pro_bar.Show() - self.stapanel.GetSizer().Layout() - self.pro_bar.Update() - - def set_color(self, value): - self.line_color.SetBackgroundColour(value) - - def set_background(self, img): - if IPy.uimode()!='ipy': return - self.canvasnb.set_background(img) - - def on_close(self, event): - print('close') - ConfigManager.write() - self.auimgr.UnInit() - del self.auimgr - self.Destroy() - sys.exit() - - def __del__( self ): - pass - -if __name__ == '__main__': - app = wx.App(False) - mainFrame = ImagePy(None) - mainFrame.Show() - app.MainLoop() diff --git a/imagepy/ui/mkdownwindow.py b/imagepy/ui/mkdownwindow.py deleted file mode 100644 index b0d7a427..00000000 --- a/imagepy/ui/mkdownwindow.py +++ /dev/null @@ -1,77 +0,0 @@ -import wx, os, time, wx.html2 as webview -from markdown import markdown -from imagepy import IPy, root_dir - -def md2html(mdstr): - exts = ['markdown.extensions.extra', 'markdown.extensions.codehilite', - 'markdown.extensions.tables','markdown.extensions.toc', 'mdx_math'] - - html = ''' - - - - - - - - - - - - - %s - - - ''' - - ret = markdown(mdstr,extensions=exts) - - return html % (IPy.root_dir+'/data/markdown.css', ret) - -class HtmlPanel(wx.Panel): - def __init__(self, parent, cont='', url=''): - wx.Panel.__init__(self, parent) - self.frame = self.GetTopLevelParent() - self.titleBase = self.frame.GetTitle() - - sizer = wx.BoxSizer(wx.VERTICAL) - self.wv = webview.WebView.New(self) - self.Bind(webview.EVT_WEBVIEW_TITLE_CHANGED, self.OnWebViewTitleChanged, self.wv) - - sizer.Add(self.wv, 1, wx.EXPAND) - self.SetSizer(sizer) - - if url != '': self.wv.LoadURL(url) - else: self.wv.SetPage(cont, url) - - def SetValue(self, value): - self.wv.SetPage(*value) - - def OnWebViewTitleChanged(self, evt): - if evt.GetString() == 'about:blank': return - if evt.GetString() == 'http:///': return - self.frame.SetTitle("%s -- %s" % (self.titleBase, evt.GetString())) - if os.path.exists(IPy.root_dir+'/data/index.htm'): - os.remove(IPy.root_dir+'/data/index.htm') - -class MkDownWindow(wx.Frame): - def __init__(self, parent, title, cont, url): - wx.Frame.__init__ (self, parent, id = wx.ID_ANY, title = title, size = wx.Size(500,500)) - logopath = os.path.join(root_dir, 'data/logo.ico') - self.SetIcon(wx.Icon(logopath, wx.BITMAP_TYPE_ICO)) - cont = '\n'.join([i.strip() for i in cont.split('\n')]) - - with open(IPy.root_dir+'/data/index.htm', 'w', encoding='utf-8') as f: - f.write(md2html(cont)) - HtmlPanel(self, url = IPy.root_dir+'/data/index.htm') - \ No newline at end of file diff --git a/imagepy/ui/plotwindow.py b/imagepy/ui/plotwindow.py deleted file mode 100644 index 1b384869..00000000 --- a/imagepy/ui/plotwindow.py +++ /dev/null @@ -1,213 +0,0 @@ -# -*- coding: utf-8 -*- -import os,wx -from .. import IPy, root_dir -import numpy as np -from math import ceil -from ..core.manager import PlotManager - -class LineCanvas(wx.Panel): - """LineCanvas: derived from wx.core.Panel""" - def __init__(self, parent): - wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, - pos = wx.DefaultPosition, size = wx.Size(256,80), - style = wx.SIMPLE_BORDER|wx.TAB_TRAVERSAL ) - self.init_buf() - self.data, self.extent = [], [0,0,1,1] - self.set_title_label('Graph', 'X-unit', 'Y-unit') - self.dirty = False - - self.SetBackgroundColour( wx.Colour( 255, 255, 255 ) ) - self.Bind(wx.EVT_SIZE, self.on_size) - self.Bind(wx.EVT_PAINT, self.on_paint) - self.Bind(wx.EVT_IDLE, self.on_idle) - self.Bind(wx.EVT_MOTION, self.on_move ) - - def update(self):self.dirty = True - - def init_buf(self): - box = self.GetClientSize() - self.width, self.height = box.width, box.height - self.buffer = wx.Bitmap(self.width, self.height) - - def on_size(self, event): - self.init_buf() - self.draw() - - def on_paint(self, event): - wx.BufferedPaintDC(self, self.buffer) - - def trans(self, x, y): - l, t, r, b = 35,35,15,35 - w = self.width - l - r - h = self.height - t - b - left, low, right, high = self.extent - x = (x-l)*1.0/w*(right-left)+left - y = (t+h-y)*1.0/(h)*(high-low)+low - return x, y - - def clear(self): - del self.data[:] - - def on_move(self, event): - self.handle_move(*self.trans(event.x, event.y)) - - def handle_move(self, x, y):pass - - def on_idle(self, event): - if self.dirty == True: - self.draw() - self.dirty = False - - def set_title_label(self, title, labelx, labely): - self.title, self.labelx, self.labely = title, labelx, labely - - def paint(self): - if len(self.data)==0 : - return - ext = np.array([[x.min(), y.min(), x.max(), y.max()] for x,y,c,w in self.data]) - d = ext[:,3].max() - ext[:,1].min() - top, bot = ext[:,3].max() + 0.1 * d, ext[:,1].min() - 0.1 * d - if top == bot: top, bot = top+1, bot-1 - self.extent = [ext[:,0].min(), bot, ext[:,2].max(), top] - self.update() - - def add_data(self, xs, ys=None, color=(0,0,255), lw=2): - if ys is None: - ys, xs = xs, np.arange(len(xs)) - self.data.append((xs, ys, color, lw)) - - def draw_coord(self, dc, w, h, l, t, r, b): - xs = [5, 10, 20, 40, 50, 100, 200, 400, 500, 1000, 2000, 4000, 10000] - n, dx, dy = len(self.data), 0, 0 - left, low, right, high = self.extent - for i in xs[::-1]: - if (right-left)*1.0/i<=10:dx=i - for i in xs[::-1]: - if (high-low)*1.0/i<=10:dy=i - dc.SetPen(wx.Pen((0, 0, 0), width=1, style=wx.SOLID)) - dc.DrawRectangle(l, t, w+1, h+1) - dc.SetPen(wx.Pen((100, 100, 100), width=1, style=wx.SOLID)) - for i in range(int(ceil(left*1.0/dx)*dx), int(right)+1, dx): - x = l+(i-left)*1.0/(right-left)*w - dc.DrawLine(x, t, x, t+h) - dc.DrawText(str(i), x-5, t+h) - for i in range(int(ceil(low*1.0/dy)*dy), int(high)+1, dy): - y = h+t-(i-low)*1.0/(high-low)*h - dc.DrawLine(l, y, l+w, y) - dc.DrawText(str(i), 5, y-5) - - titlefont = wx.Font(18, wx.FONTFAMILY_DEFAULT, - wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) - dc.SetFont(titlefont) - dw,dh = dc.GetTextExtent(self.title) - dc.DrawText(self.title, l+w/2-dw/2, 3) - - lablelfont = wx.Font(14, wx.FONTFAMILY_DEFAULT, - wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) - dc.SetFont(lablelfont) - dw,dh = dc.GetTextExtent(self.labelx) - dc.DrawText(self.labelx, l+w-dw, t+h+15) - dc.DrawText(self.labely, 5, 10) - - def draw(self): - l, t, r, b = 35,35,15,35 - w = self.width - l - r - h = self.height - t - b - if self.data is None:return - dc = wx.BufferedDC(wx.ClientDC(self), self.buffer) - dc.Clear() - - left, low, right, high = self.extent - self.draw_coord(dc, w, h, l, t, r, b) - for xs, ys, c, lw in self.data: - ys = h+t - (ys - low)*(h/(high-low)) - xs = l+(xs-left)*(1.0/(right-left)*w) - pts = list(zip(xs, ys)) - dc.SetPen(wx.Pen(c, width=lw, style=wx.SOLID)) - dc.DrawLines(pts) - - def save(self, path): - self.buffer.SaveFile(path, wx.BITMAP_TYPE_PNG) - - -class PlotFrame ( wx.Frame ): - """PlotFrame:derived from wx.core.Frame""" - frms = {} - - @classmethod - def get_frame(cls, title, gtitle='Graph', labelx='X-Unit', labely='Y-Unit'): - if PlotManager.get(title) == None: - PlotManager.add(cls(IPy.curapp, title)) - PlotManager.get(title).set_title_label(gtitle, labelx, labely) - return PlotManager.get(title) - - def __init__( self, parent, title): - wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, - title = title, pos = wx.DefaultPosition, - size = wx.Size( 500,300 ) ) - self.title = title - logopath = os.path.join(root_dir, 'data/logo.ico') - self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_3DLIGHT ) ) - self.SetIcon(wx.Icon(logopath, wx.BITMAP_TYPE_ICO)) - self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) - sizer = wx.BoxSizer( wx.VERTICAL ) - self.canvas = LineCanvas( self) - #self.canvas.set_data(np.random.rand(256)*100) - #self.canvas.set_lim(0, 0) - sizer.Add( self.canvas, 1, wx.EXPAND |wx.ALL, 5 ) - sizer2 = wx.BoxSizer( wx.HORIZONTAL ) - self.lab_info = wx.StaticText( self, wx.ID_ANY, "Information", - wx.DefaultPosition, wx.DefaultSize, 0 ) - self.lab_info.Wrap( -1 ) - sizer2.Add( self.lab_info, 0, wx.ALL, 5 ) - sizer2.AddStretchSpacer(1) - self.btn_save = wx.Button( self, wx.ID_ANY, "Save", - wx.DefaultPosition, wx.DefaultSize, 0 ) - sizer2.Add( self.btn_save, 0, wx.ALL, 5 ) - self.btn_cancel = wx.Button( self, wx.ID_ANY, "Cancel", - wx.DefaultPosition, wx.DefaultSize, 0 ) - sizer2.Add( self.btn_cancel, 0, wx.ALL, 5 ) - sizer.Add( sizer2, 0, wx.ALL|wx.EXPAND, 5 ) - self.SetSizer( sizer ) - self.Layout() - self.Centre( wx.BOTH ) - - self.canvas.handle_move = self.handle_move - self.Bind(wx.EVT_CLOSE, self.on_closing) - self.btn_cancel.Bind(wx.EVT_BUTTON, self.on_cancel) - self.btn_save.Bind(wx.EVT_BUTTON, self.on_save) - - self.set_title_label = self.canvas.set_title_label - self.add_data = self.canvas.add_data - self.clear = self.canvas.clear - - def draw(self): - self.canvas.paint() - self.Show() - - def handle_move(self, x, y): - self.lab_info.SetLabel('X = %.1f, Y = %.1f' %(x, y)) - - def on_cancel(self, event): - self.Close() - - def on_save(self, event): - para = {'path':'./'} - filt = 'PNG files (*.png)|*.png' - IPy.getpath('Save..', filt, 'save', para) - self.canvas.save(para['path']) - - def on_closing(self, event): - PlotManager.remove(self.GetTitle()) - event.Skip() - -if __name__ == '__main__': - app = wx.App(False) - xs = np.linspace(10,20,50) - ys = np.sin(xs)+100 - - plotframe = PlotFrame.get_frame('first', 'Histogram', 'Line length', 'value of pix') - plotframe.add_data(xs, ys, (0,0,255), 1) - plotframe.draw() - plotframe.draw() - app.MainLoop() \ No newline at end of file diff --git a/imagepy/ui/pluginloader.py b/imagepy/ui/pluginloader.py deleted file mode 100644 index af691efe..00000000 --- a/imagepy/ui/pluginloader.py +++ /dev/null @@ -1,86 +0,0 @@ -# -*- coding: utf-8 -* -import wx -from ..core.loader import loader -from ..core.manager import ShotcutManager, PluginsManager, LanguageManager -from glob import glob - -def buildItem(parent, root, item): - if item=='-': - root.AppendSeparator() - return - sc = ShotcutManager.get(item.title) - LanguageManager.add(item.title) - - title = LanguageManager.get(item.title) if sc==None else LanguageManager.get(item.title)+'\t'+sc - - mi = wx.MenuItem(root, -1, title) - parent.Bind(wx.EVT_MENU, lambda x, p=item:p().start(), mi) - root.Append(mi) - -def buildMenu(parent, data, curpath): - menu = wx.Menu() - for item in data[1]: - if isinstance(item, tuple): - ## TODO: fixed by auss - nextpath = curpath + '.' + item[0].title - #print(nextpath) - LanguageManager.add(item[0].title) - menu.Append(-1, LanguageManager.get(item[0].title), buildMenu(parent, item,nextpath)) - else: - buildItem(parent, menu, item) - return menu - -def buildMenuBar(parent, datas, menuBar=None): - if menuBar==None: - menuBar = wx.MenuBar() - for data in datas[1]: - if len(data[1]) == 0: - continue - LanguageManager.add(data[0].title) - menuBar.Append(buildMenu(parent, data, data[0].title), LanguageManager.get(data[0].title)) - return menuBar - -#!ToDO: tongguo lujing goujian menu -def buildMenuBarByPath(parent, path, extends, menuBar=None, report=False): - datas = loader.build_plugins(path, report) - keydata = {} - for i in datas[1]: - if isinstance(i, tuple): keydata[i[0].__name__.split('.')[-1]] = i[1] - #print(keydata) - extends = glob(extends+'/*/menus') - for i in extends: - plgs = loader.build_plugins(i, report) - for j in plgs[1]: - if not isinstance(j, tuple): continue - name = j[0].__name__.split('.')[-1] - if name in keydata: - keydata[name].extend(j[1]) - else: datas[1].append(j) - #if len(wgts)!=0: datas[1].extend(wgts[1]) - # print(datas) - if not menuBar is None:menuBar.SetMenus([]) - return buildMenuBar(parent, datas, menuBar) - -def codeSplit(txt): - sep = txt.split('-') - acc, code = wx.ACCEL_NORMAL, -1 - if 'Ctrl' in sep: acc|= wx.ACCEL_CTRL - if 'Alt' in sep: acc|= wx.ACCEL_ALT - if 'Shift' in sep: acc|= wx.ACCEL_SHIFT - fs = ['F{}'.format(i) for i in range(1,13)] - if sep[-1] in fs: - code = 340+fs.index(sep[-1]) - elif len(sep[-1])==1: code = ord(sep[-1]) - return acc, code - -def buildShortcut(parent): - shortcuts = [] - for item in list(PluginsManager.plgs.values()): - cut = ShotcutManager.get(item.title) - if cut!=None: - acc, code = codeSplit(cut) - if code==-1: continue; - nid = wx.NewId() - parent.Bind(wx.EVT_MENU, lambda x, p=item:p().start(), id=nid) - shortcuts.append((acc, code, nid)) - return wx.AcceleratorTable(shortcuts) diff --git a/imagepy/ui/propertygrid.py b/imagepy/ui/propertygrid.py deleted file mode 100644 index e5372bdf..00000000 --- a/imagepy/ui/propertygrid.py +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env python - -import sys, time, math, os.path - -import wx, wx.adv -import wx.propgrid as wxpg - -from six import exec_ -_ = wx.GetTranslation - -from imagepy.core.manager import ImageManager, TableManager - -data = [('Sheet1', [((4, 5), ['str', 'Sample_ID', '5', "image's name"]), ((4, 17), ['str', 'Operator_Name', None, 'your name']), ((4, 30), ['date', 'Date', None, 'today']), ((10, 1), ['img', 'Original_Image', '[8.16,5.76,0.9,0]', 'the original image']), ((10, 20), ['img', 'Mask_Image', '[8.16,5.76,0.9,0]', '']), ((28, 1), ['tab', 'Record', '[1,3,0,0]', 'records'])]), ('Sheet2', [((0,0), ['list', 'a', '[1,2,345]', 'nothing'])]), ('Sheet3', [])] -key = {'Sample_ID': ['str', 'Sample_ID', '5', "image's name"], 'Operator_Name': ['str', 'Operator_Name', None, 'your name'], 'Date': ['date', 'Date', None, 'today'], 'Original_Image': ['img', 'Original_Image', '[8.16,5.76,0.9,0]', 'the original image'], 'Mask_Image': ['img', 'Mask_Image', '[8.16,5.76,0.9,0]', ''], 'Record': ['tab', 'Record', '[1,3,0,0]', 'records']} - -class GridDialog( wx.Dialog ): - - def __init__( self, parent, title, tree, key): - wx.Dialog.__init__ (self, parent, -1, title, style = wx.DEFAULT_DIALOG_STYLE, size = wx.Size((300, 480))) - # wx.Panel.__init__(self, parent, wx.ID_ANY) - - self.panel = panel = wx.Panel(self, wx.ID_ANY) - topsizer = wx.BoxSizer(wx.VERTICAL) - - # Difference between using PropertyGridManager vs PropertyGrid is that - # the manager supports multiple pages and a description box. - self.pg = pg = wxpg.PropertyGridManager(panel, - style=wxpg.PG_SPLITTER_AUTO_CENTER) - - # Show help as tooltips - pg.SetExtraStyle(wxpg.PG_EX_HELP_AS_TOOLTIPS) - - pg.Bind( wxpg.EVT_PG_CHANGED, self.OnPropGridChange ) - pg.Bind( wxpg.EVT_PG_SELECTED, self.OnPropGridSelect ) - - pg.AddPage('Page 1') - self.key = key - for page in tree: - pg.Append(wxpg.PropertyCategory(page[0])) - for item in page[1]: - v = item[1] - if v[0] == 'int': pg.Append( wxpg.IntProperty(v[1], value=int(v[2]) or 0)) - if v[0] == 'float': pg.Append( wxpg.FloatProperty(v[1], value=float(v[2]) or 0)) - if v[0] == 'str': pg.Append( wxpg.StringProperty(v[1], value=v[2] or '')) - if v[0] == 'txt': pg.Append( wxpg.LongStringProperty(v[1], value=v[2] or '')) - if v[0] == 'bool': pg.Append( wxpg.BoolProperty(v[1])) - if v[0] == 'date': pg.Append( wxpg.DateProperty(v[1], value=wx.DateTime.Now())) - if v[0] == 'list': pg.Append( wxpg.EnumProperty(v[1], v[1], [i.strip() for i in v[2][1:-1].split(',')])) - if v[0] == 'img': pg.Append( wxpg.EnumProperty(v[1], v[1], ImageManager.get_titles())) - if v[0] == 'tab': pg.Append( wxpg.EnumProperty(v[1], v[1], TableManager.get_titles())) - - topsizer.Add(pg, 1, wx.EXPAND) - self.txt_info = wx.TextCtrl( self, wx.ID_ANY, 'information', wx.DefaultPosition, wx.Size(80, 80), wx.TE_MULTILINE|wx.TRANSPARENT_WINDOW ) - topsizer.Add(self.txt_info, 0, wx.EXPAND|wx.ALL, 0) - rowsizer = wx.BoxSizer(wx.HORIZONTAL) - but = wx.Button(panel, wx.ID_OK, "OK") - rowsizer.Add(but,1) - #but.Bind( wx.EVT_BUTTON, self.OnGetPropertyValues ) - but = wx.Button(panel, wx.ID_CANCEL,"Cancel") - rowsizer.Add(but,1) - topsizer.Add(rowsizer,0,wx.EXPAND) - - panel.SetSizer(topsizer) - topsizer.SetSizeHints(panel) - - sizer = wx.BoxSizer(wx.VERTICAL) - sizer.Add(panel, 1, wx.EXPAND) - self.SetSizer(sizer) - self.SetAutoLayout(True) - - def GetValue(self): - return self.pg.GetPropertyValues(as_strings=True) - - def OnGetPropertyValues(self,event): - para = self.pg.GetPropertyValues(inc_attributes=True) - - def OnPropGridChange(self, event): - p = event.GetProperty() - if p: print('%s changed to "%s"\n' % (p.GetName(),p.GetValueAsString())) - - def OnPropGridSelect(self, event): - p = event.GetProperty() - if p: self.txt_info.SetValue('%s: %s'%(p.GetName(), self.key[p.GetName()][3])) - -if __name__ == '__main__': - app = wx.App(False) - frame = GridDialog(None, 'Property Grid', data, key) - rst = frame.ShowModal() == wx.ID_OK - app.MainLoop() \ No newline at end of file diff --git a/imagepy/ui/tableframe.py b/imagepy/ui/tableframe.py deleted file mode 100644 index 824ed19a..00000000 --- a/imagepy/ui/tableframe.py +++ /dev/null @@ -1,95 +0,0 @@ -from .tablewindow import * -#import aui as aui -import wx.lib.agw.aui as aui - -class TablePanel ( wx.Panel ): - def __init__( self, parent): - wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 819,488 ), style = wx.TAB_TRAVERSAL ) - self.SetBackgroundColour( wx.Colour( 255, 255, 255 ) ) - WTableManager.add(self) - - bSizer = wx.BoxSizer( wx.VERTICAL ) - - self.lab_info = wx.StaticText( self, wx.ID_ANY, 'MyLabel asdfasfa ', wx.DefaultPosition, wx.DefaultSize, 0 ) - self.lab_info.Wrap( -1 ) - bSizer.Add( self.lab_info, 0, wx.ALL|wx.EXPAND, 0 ) - self.grid = GridBase( self) - self.grid.set_handler(self.set_info) - bSizer.Add( self.grid, 1, wx.ALL|wx.EXPAND, 0 ) - - - self.SetSizer( bSizer ) - self.Layout() - - self.handle = None - - def set_handler(self, handle=None): - self.handle = handle - - def set_tps(self, tps): - self.tps = tps - self.grid.set_tps(tps) - - def __del__( self ): - print('Table Panel Del') - - def set_info(self, tps): - self.lab_info.SetLabel('%sx%s; %.2fK'%(tps.data.shape+(tps.get_nbytes()/1024.0,))) - if not self.handle is None: self.handle(tps) - -class TableFrame(wx.Frame): - """CanvasFrame: derived from the wx.core.Frame""" - ## TODO: Main frame ??? - def __init__(self, parent=None): - wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, - title = wx.EmptyString, - pos = wx.DefaultPosition, - size = wx.Size( -1,-1 ), - style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) - self.tablepanel = TablePanel(self) - logopath = os.path.join(root_dir, 'data/logo.ico') - self.SetIcon(wx.Icon(logopath, wx.BITMAP_TYPE_ICO)) - self.Bind(wx.EVT_ACTIVATE, self.on_valid) - #self.SetAcceleratorTable(IPy.curapp.shortcut) - self.Bind(wx.EVT_CLOSE, self.on_close) - self.tablepanel.set_handler(self.set_title) - #self.canvaspanel.set_handler(self.set_title) - - def set_tps(self, tps): - self.tablepanel.set_tps(tps) - - def set_title(self, tps): - self.SetTitle(tps.title) - - def on_valid(self, event): - if event.GetActive(): - TableManager.add(self.tablepanel.tps) - - def on_close(self, event): - self.tablepanel.set_handler() - self.tablepanel.grid.set_handler() - WTableManager.remove(self.tablepanel) - event.Skip() - -class TableNoteBook(aui.AuiNotebook): - def __init__(self, parent): - aui.AuiNotebook.__init__( self, parent, wx.ID_ANY, - wx.DefaultPosition, wx.DefaultSize, aui.AUI_NB_DEFAULT_STYLE ) - self.Bind( aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_pagevalid) - self.Bind( aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close) - - def add_page(self, panel, tps): - self.AddPage(panel, tps.title, True, wx.NullBitmap ) - panel.set_handler(lambda tps, pan=panel: self.set_title(tps, pan)) - - def set_title(self, tps, panel): - title = tps.title - self.SetPageText(self.GetPageIndex(panel), title) - - def on_pagevalid(self, event): - TableManager.add(event.GetEventObject().GetPage(event.GetSelection()).tps) - - def on_close(self, event): - event.GetEventObject().GetPage(event.GetSelection()).set_handler() - event.GetEventObject().GetPage(event.GetSelection()).grid.set_handler() - WTableManager.remove(event.GetEventObject().GetPage(event.GetSelection())) \ No newline at end of file diff --git a/imagepy/ui/toolsloader.py b/imagepy/ui/toolsloader.py deleted file mode 100644 index 76871b67..00000000 --- a/imagepy/ui/toolsloader.py +++ /dev/null @@ -1,125 +0,0 @@ -# -*- coding: utf-8 -* -# load and build the toolbar -import wx -import os -from .. import IPy -from .. import root_dir -from ..core.engine import Tool, Macros -from ..core.loader import loader -from imagepy.core.manager import ConfigManager, DocumentManager -from glob import glob - -def make_bitmap(bmp): - img = bmp.ConvertToImage() - img.Resize((20, 20), (2, 2)) - return img.ConvertToBitmap() - -def build_tools(parent, toolspath, extends, bar=None, report=False): - global host - host = parent - ## get tool datas from the loader.build_tools(toolspath) - ## then generate toolsbar - datas = loader.build_tools(toolspath, report) - extends = glob(extends+'/*/tools') - for i in extends: - tols = loader.build_tools(i, report) - if len(tols)!=0: datas[1].extend(tols[1]) - for i in datas[1]: - if i[0].title == ConfigManager.get('tools'): - datas[1].remove(i) - datas[1].insert(1, i) - toolsbar = buildToolsBar(parent, datas, bar) - #gifpath = os.path.join(root_dir, "tools/drop.gif") - #btn = wx.BitmapButton(parent, wx.ID_ANY, wx.Bitmap(gifpath), wx.DefaultPosition, (30,30), wx.BU_AUTODRAW) - #btn.Bind(wx.EVT_LEFT_DOWN, lambda x:menu_drop(parent, toolsbar, datas, btn, x)) - return toolsbar#, btn - -def buildToolsBar(parent, datas, toolsbar=None): - #if not toolsbar is None:toolsbar.BeginRepositioningChildren() - #toolsbar = wx.ToolBar( parent, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TB_HORIZONTAL ) - - if toolsbar is None: - box = wx.BoxSizer( wx.HORIZONTAL ) - toolsbar = wx.Panel( parent, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) - toolsbar.SetSizer( box ) - else: - box = toolsbar.GetSizer() - toolsbar.DestroyChildren() - box.Clear() - - - add_tools(toolsbar, datas[1][0][1], clear=True) - - if len(datas[1]) > 1: - gifpath = os.path.join(root_dir, "tools/drop.gif") - btn = wx.BitmapButton(toolsbar, wx.ID_ANY, make_bitmap(wx.Bitmap(gifpath)), - wx.DefaultPosition, (32, 32), wx.BU_AUTODRAW|wx.RAISED_BORDER) - sp = wx.StaticLine( toolsbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL ) - box.Add( sp, 0, wx.ALL|wx.EXPAND, 2 ) - box.AddStretchSpacer(1) - box.Add(btn) - btn.Bind(wx.EVT_LEFT_DOWN, lambda x, ds=datas, b=btn:menu_drop(parent, toolsbar, ds, b, x)) - add_tools(toolsbar, datas[1][1][1]) - - toolsbar.GetSizer().Layout() - #toolsbar.Fit() - - return toolsbar - -def menu_drop(parent, toolbar, datas, btn, e): - menu = wx.Menu() - for data in datas[1][1:]: - item = wx.MenuItem(menu, wx.ID_ANY, data[0].title, wx.EmptyString, wx.ITEM_NORMAL ) - - menu.Append(item) - parent.Bind(wx.EVT_MENU, lambda x,p=data[1]:add_tools(toolbar, p), item) - parent.PopupMenu( menu ) - menu.Destroy() - -def f(plg, e): - ##! TODO: What's this? for wx.EVT_LEFT_DOWN - plg.start() - #print e.GetEventObject().SetBackgroundColour( - # wx.SystemSettings.GetColour( wx.SYS_COLOUR_HIGHLIGHT ) ) - if isinstance(plg, Tool): - e.Skip() - -def set_info(value): - IPy.curapp.set_info(value) - -def setting(tol, btn): - if not hasattr(tol, 'view'):return - if isinstance(tol.view, list): - para = dict(tol.para) - rst = IPy.getpara(tol.title, tol.view, para) - if rst!=None: tol.para = rst - else: - tol().view(btn) - -def add_tools(bar, datas, clear=False, curids=[]): - box = bar.GetSizer() - if not clear: - for curid in curids: - #bar.RemoveChild(curid) - #box.Hide(curid) - curid.Destroy() - #box.Detach(curid) - del curids[:] - - for data in datas: - btn = wx.BitmapButton(bar, wx.ID_ANY, make_bitmap(wx.Bitmap(data[1])), - wx.DefaultPosition, (32,32), wx.BU_AUTODRAW|wx.RAISED_BORDER ) - if not clear:curids.append(btn) - if clear: box.Add(btn) - else: - box.Insert(len(box.GetChildren())-2, btn) - - btn.Bind( wx.EVT_LEFT_DOWN, lambda x, p=data[0]:f(p(), x)) - btn.Bind( wx.EVT_RIGHT_DOWN, lambda x, p=data[0]: IPy.show_md(p.title, DocumentManager.get(p.title))) - btn.Bind( wx.EVT_ENTER_WINDOW, - lambda x, p='"{}" Tool'.format(data[0].title): set_info(p)) - if not isinstance(data[0], Macros) and issubclass(data[0], Tool): - btn.Bind(wx.EVT_LEFT_DCLICK, lambda x, p=data[0]:p().show()) - btn.SetDefault() - box.Layout() - bar.Refresh() \ No newline at end of file diff --git a/imagepy/ui/widgets/__init__.py b/imagepy/ui/widgets/__init__.py deleted file mode 100644 index 4acdb646..00000000 --- a/imagepy/ui/widgets/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from .curvepanel import CurvePanel -from .histpanel import HistCanvas -from .cmappanel import CMapPanel -from .normal import * -from .advanced import * -from .colormap import CMapSelCtrl -from .viewport import ViewPort \ No newline at end of file diff --git a/imagepy/ui/widgets/advanced.py b/imagepy/ui/widgets/advanced.py deleted file mode 100644 index d7a436d3..00000000 --- a/imagepy/ui/widgets/advanced.py +++ /dev/null @@ -1,29 +0,0 @@ -from . normal import Choice, Choices -from . colormap import CMapSelPanel -from ...core.manager import ImageManager, TableManager, ColorManager - -class ImageList(Choice): - def __init__(self, parent, title, unit): - Choice.__init__(self, parent, ImageManager.get_titles(), str, title, unit) - -class TableList(Choice): - def __init__(self, parent, title, unit): - Choice.__init__(self, parent, TableManager.get_titles(), str, title, unit) - -class TableField(Choice): - def __init__(self, parent, title, unit): - self.tps = TableManager.get() - Choice.__init__(self, parent, ['None'] + list(self.tps.data.columns), lambda x:x, title, unit) - -class TableFields(Choices): - def __init__(self, parent, title): - self.tps = TableManager.get() - Choices.__init__(self, parent, self.tps.data.columns, title) - - def SetValue(self, value): - Choices.SetValue(self, self.tps.colmsk) - -class ColorMap(CMapSelPanel): - def __init__(self, parent, title): - CMapSelPanel.__init__(self, parent, title) - self.SetItems(ColorManager.luts) diff --git a/imagepy/ui/widgetsloader.py b/imagepy/ui/widgetsloader.py deleted file mode 100644 index d75cfe7f..00000000 --- a/imagepy/ui/widgetsloader.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -* -# load and build the toolbar -import wx -import numpy as np -from ..core.loader import loader -from glob import glob - -def build_widget(parent, datas): - for i in datas[1]: - parent.AddPage(i(parent), i.title, False ) - - -def build_widgets_panel(parent, datas, wpanel): - if wpanel is None: - wpanel = wx.ScrolledWindow( parent, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.HSCROLL|wx.VSCROLL ) - else: wpanel.DestroyChildren() - wpanel.SetScrollRate( 5, 5 ) - sizer = wx.BoxSizer( wx.VERTICAL ) - for i in datas[1]: - choicebook = wx.Choicebook( wpanel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.CHB_DEFAULT ) - build_widget(choicebook, i) - sizer.Add( choicebook, 0, wx.EXPAND |wx.ALL, 0 ) - wpanel.SetSizer( sizer ) - wpanel.Layout() - sizer.Fit( wpanel ) - return wpanel - -def build_widgets(parent, toolspath, extends, panel=None): - datas = loader.build_widgets(toolspath) - extends = glob(extends+'/*/widgets') - for i in extends: - wgts = loader.build_widgets(i) - if len(wgts)!=0: datas[1].extend(wgts[1]) - return build_widgets_panel(parent, datas, panel) \ No newline at end of file diff --git a/imagepy/ui/workflowwindow.py b/imagepy/ui/workflowwindow.py deleted file mode 100644 index 803f1d5d..00000000 --- a/imagepy/ui/workflowwindow.py +++ /dev/null @@ -1,120 +0,0 @@ -workflow = {'title': '硬币分割演示', 'chapter': [{'title': '打开图像', 'section': [{'title': 'coins', 'hint': '这是一个硬币图像,我们现在来对每个硬币进行计数及测量'}]}, {'title': '图像分割', 'section': [{'title': 'Up And Down Watershed', 'hint': '使用高低阈值分水岭,调整高低阈值,使得每个硬币都或多或少染上红色,而背景染上绿色,而目标是高亮的,我们选择 up area'}]}, {'title': '掩膜修复', 'section': [{'title': 'Fill Holes', 'hint': '我们看到有些硬币上带了缝隙,这显然不是我们想要的,我们用二值填充进行修复'}, {'title': 'Geometry Filter', 'hint': '我们看到还有一些小块噪声,这些碎片面积比较小,我们用面积进行过滤'}]}, {'title': '区域测量', 'section': [{'title': 'Geometry Analysis', 'hint': '选择需要的指标,生成结果'}]}, {'title': '称多导出', 'section': [{'title': 'CSV Save', 'hint': ''}, {'title': 'CSV Save', 'hint': ''}]}]} - -import wx -from imagepy.core.manager import PluginsManager -from imagepy import IPy - -class WorkFlowPanel ( wx.Panel ): - def __init__( self, parent ): - wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.TAB_TRAVERSAL ) - - - def load(self, cont, wf): - self.workflow, self.cont = wf, cont - sizer_scroll = wx.BoxSizer( wx.HORIZONTAL ) - - self.scr_workflow = wx.ScrolledCanvas( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize) - self.scr_workflow.SetScrollRate( 30, 0 ) - self.scr_workflow.ShowScrollbars(wx.SHOW_SB_NEVER, wx.SHOW_SB_NEVER) - self.scr_workflow.SetMinSize((600,-1)) - - sizer_chapter = wx.BoxSizer( wx.HORIZONTAL ) - self.spn_scroll = wx.SpinButton( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.SP_HORIZONTAL ) - sizer_scroll.Add( self.spn_scroll, 0, wx.ALL|wx.EXPAND, 3 ) - - for chapter in wf['chapter']: - self.pan_chapter = wx.Panel( self.scr_workflow, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER|wx.TAB_TRAVERSAL ) - sizer_frame = wx.BoxSizer( wx.VERTICAL ) - - self.lab_chapter = wx.StaticText( self.pan_chapter, wx.ID_ANY, chapter['title'], wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE ) - self.lab_chapter.Wrap( -1 ) - self.lab_chapter.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_INACTIVECAPTION ) ) - - sizer_frame.Add( self.lab_chapter, 0, wx.ALL|wx.EXPAND, 0 ) - - sizer_section = wx.BoxSizer( wx.HORIZONTAL ) - for section in chapter['section']: - btn = wx.Button( self.pan_chapter, wx.ID_ANY, section['title'], wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) - sizer_section.Add( btn, 0, wx.ALL, 3 ) - btn.Bind(wx.EVT_BUTTON, lambda e, x=section['title']: PluginsManager.get(x)().start()) - btn.Bind( wx.EVT_ENTER_WINDOW, lambda e, info=section['hint']: self.set_info(info)) - #self.m_button1.Bind( wx.EVT_LEAVE_WINDOW, self.on_out ) - - - sizer_frame.Add( sizer_section, 0, wx.EXPAND, 3 ) - - sizer_btn = wx.BoxSizer( wx.HORIZONTAL ) - - - sizer_btn.AddStretchSpacer(1) - - self.btn_snap = wx.StaticText( self.pan_chapter, wx.ID_ANY, u" Snap ", wx.DefaultPosition, wx.DefaultSize, 0|wx.SIMPLE_BORDER ) - self.btn_snap.Wrap( -1 ) - sizer_btn.Add( self.btn_snap, 0, wx.ALL, 3 ) - - self.btn_load = wx.StaticText( self.pan_chapter, wx.ID_ANY, u" Load ", wx.DefaultPosition, wx.DefaultSize, 0|wx.SIMPLE_BORDER ) - self.btn_load.Wrap( -1 ) - sizer_btn.Add( self.btn_load, 0, wx.ALL, 3 ) - - self.btn_step = wx.StaticText( self.pan_chapter, wx.ID_ANY, u" >> ", wx.DefaultPosition, wx.DefaultSize, 0|wx.SIMPLE_BORDER ) - self.btn_step.Wrap( -1 ) - sizer_btn.Add( self.btn_step, 0, wx.ALL, 3 ) - - - sizer_frame.Add( sizer_btn, 0, wx.EXPAND, 3 ) - - - self.pan_chapter.SetSizer( sizer_frame ) - self.pan_chapter.Layout() - sizer_frame.Fit( self.pan_chapter ) - sizer_chapter.Add( self.pan_chapter, 0, wx.EXPAND |wx.ALL, 3 ) - - sizer_scroll.Add( self.scr_workflow, 1, wx.EXPAND |wx.ALL, 0) - - sizer_info = wx.BoxSizer( wx.VERTICAL ) - - sizer_info.SetMinSize( wx.Size( 260,-1 ) ) - self.btn_help = wx.StaticText( self, wx.ID_ANY, u" Click For Detail Document ", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE|wx.SIMPLE_BORDER ) - self.btn_help.Wrap( -1 ) - self.btn_help.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_INACTIVECAPTION ) ) - - sizer_info.Add( self.btn_help, 0, wx.ALL|wx.EXPAND, 0 ) - - self.txt_info = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_AUTO_URL|wx.TE_MULTILINE|wx.TE_READONLY ) - sizer_info.Add( self.txt_info, 1, wx.TOP|wx.EXPAND, 3 ) - - sizer_scroll.Add( sizer_info, 0, wx.EXPAND |wx.ALL, 3) - - self.scr_workflow.SetSizer( sizer_chapter ) - self.scr_workflow.Layout() - - self.SetSizer( sizer_scroll ) - - #self.Fit() - self.Layout() - - self.spn_scroll.Bind( wx.EVT_SPIN, self.on_spn ) - self.btn_help.Bind( wx.EVT_LEFT_DOWN, self.on_help ) - - def set_info(self, info): - self.txt_info.SetValue(info) - - def on_spn(self, event): - v = self.spn_scroll.GetValue() - self.scr_workflow.Scroll(v, 0) - self.spn_scroll.SetValue(self.scr_workflow.GetViewStart()[0]) - - def on_help(self, event): - IPy.show_md(self.workflow['title'], self.cont) - - - -if __name__ == '__main__': - app = wx.App(False) - frame = wx.Frame(None) - wfp = WorkFlowPanel(frame) - wfp.load(None, workflow) - frame.Fit() - frame.Show() - app.MainLoop() - \ No newline at end of file diff --git a/imagepy/widgets/__init__.py b/imagepy/widgets/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/imagepy/widgets/histogram/__init__.py b/imagepy/widgets/histogram/__init__.py deleted file mode 100644 index a0a7e74e..00000000 --- a/imagepy/widgets/histogram/__init__.py +++ /dev/null @@ -1 +0,0 @@ -catlog = ['histogram_wgt', 'curve_wgt'] \ No newline at end of file diff --git a/imagepy/widgets/navigator/__init__.py b/imagepy/widgets/navigator/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/sciapp/__init__.py b/sciapp/__init__.py new file mode 100644 index 00000000..7e408ad0 --- /dev/null +++ b/sciapp/__init__.py @@ -0,0 +1 @@ +from .app import App, Manager, Source \ No newline at end of file diff --git a/sciapp/action/__init__.py b/sciapp/action/__init__.py new file mode 100644 index 00000000..deecfb48 --- /dev/null +++ b/sciapp/action/__init__.py @@ -0,0 +1,5 @@ +from .action import SciAction +from .imgact import ImgAction +from .tolact import Tool, DefaultTool, ImageTool, ShapeTool, TableTool +from .shpact import * +from .roiact import * \ No newline at end of file diff --git a/sciapp/action/action.py b/sciapp/action/action.py new file mode 100644 index 00000000..be4ecef0 --- /dev/null +++ b/sciapp/action/action.py @@ -0,0 +1,6 @@ +class SciAction: + name = 'SciAction' + def __init__(self): pass + def start(self, app): + self.app = app + print(self.name, 'started!') diff --git a/sciapp/action/free.py b/sciapp/action/free.py new file mode 100644 index 00000000..c8ae5020 --- /dev/null +++ b/sciapp/action/free.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +""" +Created on Sat Dec 3 03:57:53 2016 +@author: yxl +""" +import threading +from time import time + +class Free: + title = 'Free' + view = None + para = None + prgs = (None, 1) + asyn = True + + def progress(self, i, n): + self.prgs = (i, n) + + def run(self, para=None): + print('this is a plugin') + + def runasyn(self, para, callback=None): + start = time() + self.run(para) + self.app.info('%s: cost %.3fs'%(self.title, time()-start)) + if callback!=None:callback() + + def load(self):return True + + def show(self): + if self.view==None:return True + return self.app.show_para(self.title, self.view, self.para, None) + + def start(self, app, para=None, callback=None): + self.app = app + if not self.load():return + if para!=None or self.show(): + if para==None:para = self.para + threading.Thread(target = self.runasyn, args = (para, callback)).start() diff --git a/sciapp/action/imgact.py b/sciapp/action/imgact.py new file mode 100644 index 00000000..7c90f030 --- /dev/null +++ b/sciapp/action/imgact.py @@ -0,0 +1,35 @@ +from .action import SciAction + +class ImgAction(SciAction): + title = 'Image Action' + note, para, view = [], None, None + + def __init__(self): pass + + def show(self): + ips, img, snap = self.ips, self.ips.img, self.ips.snap + f = lambda p: self.run(ips, img, snap, p) or self.ips.update() + return self.app.show_para(self.title, self.view, self.para, f, on_ok=None, + on_cancel=lambda x=self.ips:self.cancel(x), + preview='preview' in self.note, modal=True) + + def cancel(self, ips): + ips.img[:] = ips.snap + ips.update() + + def run(self, ips, img, snap, para): + print('I am running!!!') + + def start(self, app, para=None, callback=None): + print('Image Action Started!') + self.app = app + self.ips = app.get_img() + if 'auto_snap' in self.note: self.ips.snapshot() + if para!=None: + self.run(self.ips, self.ips.img, self.ips.snap, para) + elif self.view==None and self.__class__.show is ImgAction.show: + self.run(self.ips, self.ips.img, self.ips.snap, para) + elif self.show(): + self.run(self.ips, self.ips.img, self.ips.snap, self.para) + elif 'auto_snap' in self.note: self.cancel(self.ips) + self.ips.update() diff --git a/sciapp/action/roiact.py b/sciapp/action/roiact.py new file mode 100644 index 00000000..3bbf1c5b --- /dev/null +++ b/sciapp/action/roiact.py @@ -0,0 +1,61 @@ +from .shpact import * +from .shpbase import BaseEditor +from .tolact import ImageTool +from ..object import ROI + +class BaseROI(ImageTool): + def __init__(self, base): + base.__init__(self) + self.base = base + + def mouse_down(self, img, x, y, btn, **key): + if img.roi is None: img.roi = ROI() + else: img.roi.msk = None + self.base.mouse_down(self, img.roi, x, y, btn, **key) + + def mouse_up(self, img, x, y, btn, **key): + self.base.mouse_up(self, img.roi, x, y, btn, **key) + if not img.roi is None: + if len(img.roi.body)==0: img.roi = None + else: img.roi.msk = None + + def mouse_move(self, img, x, y, btn, **key): + self.base.mouse_move(self, img.roi, x, y, btn, **key) + + def mouse_wheel(self, img, x, y, d, **key): + self.base.mouse_wheel(self, img.roi, x, y, d, **key) + +class PolygonROI(BaseROI): + title = 'Polygon ROI' + def __init__(self): + BaseROI.__init__(self, PolygonEditor) + +class LineROI(BaseROI): + title = 'Line ROI' + def __init__(self): + BaseROI.__init__(self, LineEditor) + +class PointROI(BaseROI): + title = 'Point ROI' + def __init__(self): + BaseROI.__init__(self, PointEditor) + +class RectangleROI(BaseROI): + title = 'Rectangle ROI' + def __init__(self): + BaseROI.__init__(self, RectangleEditor) + +class EllipseROI(BaseROI): + title = 'Ellipse ROI' + def __init__(self): + BaseROI.__init__(self, EllipseEditor) + +class FreeLineROI(BaseROI): + title = 'Free Line ROI' + def __init__(self): + BaseROI.__init__(self, FreeLineEditor) + +class FreePolygonROI(BaseROI): + title = 'Free Polygon ROI' + def __init__(self): + BaseROI.__init__(self, FreePolygonEditor) \ No newline at end of file diff --git a/sciapp/action/shpact.py b/sciapp/action/shpact.py new file mode 100644 index 00000000..947e30c0 --- /dev/null +++ b/sciapp/action/shpact.py @@ -0,0 +1,242 @@ +from .shpbase import * + +def inbase(key, btn): + status = key['ctrl'], key['alt'], key['shift'] + return status == (1,1,0) or btn in {2,3} + +class PointEditor(BaseEditor): + title = 'Point Tool' + def __init__(self): + BaseEditor.__init__(self) + + def mouse_down(self, shp, x, y, btn, **key): + if inbase(key, btn): + BaseEditor.mouse_down(self, shp, x, y, btn, **key) + if btn==1 and not key['alt'] and not key['ctrl']: + shp.body.append(Point([x,y])) + shp.dirty = True + +class LineEditor(BaseEditor): + title = 'Line Tool' + def __init__(self): + BaseEditor.__init__(self) + self.cur, self.n, self.obj = 0, 0, None + + def mouse_down(self, shp, x, y, btn, **key): + if inbase(key, btn) and self.obj is None: + BaseEditor.mouse_down(self, shp, x, y, btn, **key) + if key['alt'] and key['ctrl']: return + if btn==1: + if self.obj is None: + self.obj = Line([(x,y)]) + shp.body.append(self.obj) + else: + self.obj.body = np.vstack((self.obj.body, [(x,y)])) + anchor = Points(mark(self.obj)[0], color=(255,0,0)) + key['canvas'].marks['buffer'] = anchor + if btn==3 and not self.obj is None : + self.obj.body = np.vstack((self.obj.body, [(x,y)])) + self.obj.dirty, shp.dirty, self.obj = True, True, None + del key['canvas'].marks['buffer'] + shp.dirty = True + +class PolygonEditor(BaseEditor): + title = 'Polygon Tool' + def __init__(self): + BaseEditor.__init__(self) + self.cur, self.n, self.obj = 0, 0, None + + def mouse_down(self, shp, x, y, btn, **key): + if inbase(key, btn) and self.obj is None: + BaseEditor.mouse_down(self, shp, x, y, btn, **key) + if key['alt'] and key['ctrl']: return + if btn==1: + if self.obj is None: + self.obj = Line([(x,y)]) + shp.body.append(self.obj) + else: + self.obj.body = np.vstack((self.obj.body, [(x,y)])) + anchor = Points(mark(self.obj)[0], color=(255,0,0)) + key['canvas'].marks['buffer'] = anchor + if btn==3 and not self.obj is None : + body = np.vstack((self.obj.body, [(x,y)])) + shp.body[-1] = Polygon(body) + if key['alt'] or key['shift']: + obj = shp.body.pop(-1) + rst = geom_union(shp.to_geom()) + if key['alt'] and not key['shift']: + rst = rst.difference(obj.to_geom()) + if key['shift'] and not key['alt']: + rst = rst.union(obj.to_geom()) + if key['shift'] and key['alt']: + rst = rst.intersection(obj.to_geom()) + layer = geom2shp(geom_flatten(rst)) + shp.body = layer.body + self.obj, shp.dirty = None, True + del key['canvas'].marks['buffer'] + shp.dirty = True + +class RectangleEditor(BaseEditor): + title = 'Rectangle Tool' + def __init__(self): + BaseEditor.__init__(self) + self.obj, self.p = None, None + + def mouse_down(self, shp, x, y, btn, **key): + if inbase(key, btn): + BaseEditor.mouse_down(self, shp, x, y, btn, **key) + if key['alt'] and key['ctrl']: return + if btn==1: + self.obj = Rectangle([x, y, 0, 0]) + self.p = x, y + shp.body.append(self.obj) + + def mouse_up(self, shp, x, y, btn, **key): + if inbase(key, btn): + BaseEditor.mouse_up(self, shp, x, y, btn, **key) + if key['alt'] and key['ctrl']: return + if self.p == (x, y) and not self.obj is None: + self.obj = self.p = None + return shp.body.pop(-1) + if (key['alt'] or key['shift']) and not self.obj is None: + obj = shp.body.pop(-1) + rst = geom_union(shp.to_geom()) + if key['alt'] and not key['shift']: + rst = rst.difference(obj.to_geom()) + if key['shift'] and not key['alt']: + rst = rst.union(obj.to_geom()) + if key['shift'] and key['alt']: + rst = rst.intersection(obj.to_geom()) + layer = geom2shp(geom_flatten(rst)) + shp.body = layer.body + self.obj, shp.dirty = None, True + if 'buffer' in key['canvas'].marks: + del key['canvas'].marks['buffer'] + + def mouse_move(self, shp, x, y, btn, **key): + BaseEditor.mouse_move(self, shp, x, y, btn, **key) + if key['alt'] and key['ctrl']: return + if not self.obj is None: + self.obj.body[2:] = (x, y) - self.obj.body[:2] + anchor = Points(mark(self.obj)[0], color=(255,0,0)) + key['canvas'].marks['buffer'] = anchor + self.obj.dirty = shp.dirty = True + +class EllipseEditor(BaseEditor): + title = 'Ellipse Tool' + def __init__(self): + BaseEditor.__init__(self) + self.obj, self.p = None, None + + def mouse_down(self, shp, x, y, btn, **key): + if inbase(key, btn): + BaseEditor.mouse_down(self, shp, x, y, btn, **key) + if key['alt'] and key['ctrl']: return + if btn==1: + self.p = (x, y) + self.obj = Ellipse([x, y, 0, 0, 0]) + shp.body.append(self.obj) + + def mouse_up(self, shp, x, y, btn, **key): + if inbase(key, btn): + BaseEditor.mouse_up(self, shp, x, y, btn, **key) + if key['alt'] and key['ctrl']: return + if self.p == (x, y) and not self.obj is None: + self.obj = self.p = None + return shp.body.pop(-1) + if (key['alt'] or key['shift']) and not self.obj is None: + obj = shp.body.pop(-1) + rst = geom_union(shp.to_geom()) + if key['alt'] and not key['shift']: + rst = rst.difference(obj.to_geom()) + if key['shift'] and not key['alt']: + rst = rst.union(obj.to_geom()) + if key['shift'] and key['alt']: + rst = rst.intersection(obj.to_geom()) + layer = geom2shp(geom_flatten(rst)) + shp.body = layer.body + self.obj, shp.dirty = None, True + if 'buffer' in key['canvas'].marks: + del key['canvas'].marks['buffer'] + + def mouse_move(self, shp, x, y, btn, **key): + BaseEditor.mouse_move(self, shp, x, y, btn, **key) + if key['alt'] and key['ctrl']: return + if not self.obj is None: + ox, oy = self.p + self.obj.body[:] = [(x+ox)/2, (y+oy)/2, (x-ox)/2, (y-oy)/2, 0] + anchor = Points(mark(self.obj)[0], color=(255,0,0)) + key['canvas'].marks['buffer'] = anchor + shp.dirty = self.obj.dirty = True + +class FreeLineEditor(BaseEditor): + title = 'Free Line Tool' + def __init__(self): + BaseEditor.__init__(self) + self.cur, self.n, self.obj = 0, 0, None + + def mouse_down(self, shp, x, y, btn, **key): + if inbase(key, btn): + BaseEditor.mouse_down(self, shp, x, y, btn, **key) + if key['alt'] and key['ctrl']: return + if btn==1 and self.obj is None: + self.obj = Line([(x,y)]) + shp.body.append(self.obj) + shp.dirty = True + + def mouse_up(self, shp, x, y, btn, **key): + if inbase(key, btn): + BaseEditor.mouse_up(self, shp, x, y, btn, **key) + if key['alt'] and key['ctrl']: return + self.obj = None + + def mouse_move(self, shp, x, y, btn, **key): + BaseEditor.mouse_move(self, shp, x, y, btn, **key) + if key['alt'] and key['ctrl']: return + if not self.obj is None: + self.obj.body = np.vstack((self.obj.body, [(x,y)])) + self.obj.dirty = shp.dirty = True + +class FreePolygonEditor(BaseEditor): + title = 'Free Polygon Tool' + def __init__(self): + BaseEditor.__init__(self) + self.cur, self.n, self.obj = 0, 0, None + + def mouse_down(self, shp, x, y, btn, **key): + if inbase(key, btn): + BaseEditor.mouse_down(self, shp, x, y, btn, **key) + if key['alt'] and key['ctrl']: return + if btn==1 and self.obj is None: + self.obj = Line([(x,y)]) + shp.body.append(self.obj) + shp.dirty = True + + def mouse_up(self, shp, x, y, btn, **key): + if inbase(key, btn): + BaseEditor.mouse_up(self, shp, x, y, btn, **key) + if key['alt'] and key['ctrl']: return + if btn==1 and not self.obj is None: + body = np.vstack((self.obj.body, self.obj.body[0])) + shp.body[-1] = Polygon(body) + body = np.vstack((self.obj.body, [(x,y)])) + shp.body[-1] = Polygon(body) + if key['alt'] or key['shift']: + obj = shp.body.pop(-1) + rst = geom_union(shp.to_geom()) + if key['alt'] and not key['shift']: + rst = rst.difference(obj.to_geom()) + if key['shift'] and not key['alt']: + rst = rst.union(obj.to_geom()) + if key['shift'] and key['alt']: + rst = rst.intersection(obj.to_geom()) + layer = geom2shp(geom_flatten(rst)) + shp.body = layer.body + self.obj, shp.dirty = None, True + + def mouse_move(self, shp, x, y, btn, **key): + BaseEditor.mouse_move(self, shp, x, y, btn, **key) + if key['alt'] and key['ctrl']: return + if not self.obj is None: + self.obj.body = np.vstack((self.obj.body, [(x,y)])) + self.obj.dirty = shp.dirty = True \ No newline at end of file diff --git a/sciapp/action/shpbase.py b/sciapp/action/shpbase.py new file mode 100644 index 00000000..6435049a --- /dev/null +++ b/sciapp/action/shpbase.py @@ -0,0 +1,299 @@ +from .tolact import ShapeTool +from sciapp.object import * +from numpy.linalg import norm +import numpy as np + +def mark(shp, types = 'all'): + pts = [] + if not (types=='all' or shp.dtype in types): return pts + if shp.dtype == 'point': + pts.append([shp.body]) + if shp.dtype == 'points': + pts.append(shp.body) + if shp.dtype == 'line': + pts.append(shp.body) + if shp.dtype == 'lines': + pts.extend(shp.body) + if shp.dtype == 'polygon' and len(shp.body)==1: + pts.append(shp.body[0]) + if shp.dtype == 'polygons': + for i in shp.body: + if len(i) != 1: continue + pts.append(i[0]) + if shp.dtype == 'rectangle': + l,t,w,h = shp.body + ps = np.mgrid[l:l+w:3j, t:t+h:3j].T.reshape((-1,2)) + pts.append(ps) + if shp.dtype == 'rectangles': + for i in range(len(shp.body)): + l,t,w,h = shp.body[i] + ps = np.mgrid[l:l+w:3j, t:t+h:3j].T.reshape((-1,2)) + pts.append(ps) + if shp.dtype == 'ellipse': + x0, y0, l1, l2, ang = shp.body + mat = np.array([[np.cos(-ang),-np.sin(-ang)], + [np.sin(-ang),np.cos(-ang)]]) + ps = np.mgrid[-l1:l1:3j, -l2:l2:3j].T.reshape((-1,2)) + pts.append(mat.dot(ps.T).T + (x0, y0)) + if shp.dtype == 'ellipses': + for i in range(len(shp.body)): + x0, y0, l1, l2, ang = shp.body[i] + mat = np.array([[np.cos(-ang),-np.sin(-ang)], + [np.sin(-ang),np.cos(-ang)]]) + ps = np.mgrid[-l1:l1:3j, -l2:l2:3j].T.reshape((-1,2)) + pts.append(mat.dot(ps.T).T + (x0, y0)) + if shp.dtype == 'layer': + minl, obj = 1e8, None + for i in shp.body: + pts.extend(mark(i, types)) + return pts + +def pick_obj(shp, x, y, lim, types='all'): + obj, minl = None, lim + if not (types=='all' or shp.dtype in types): + return m, obj, minl + if shp.dtype == 'layer': + for i in shp.body: + o, l = pick_obj(i, x, y, lim, types) + if l < minl: + obj, minl = o, l + elif shp.dtype in 'polygons': + b = shp.to_geom().contains(Point([x, y]).to_geom()) + if b : return shp, 0 + else: + d = shp.to_geom().distance(Point([x, y]).to_geom()) + if d0: + pts = Points(np.vstack(pts), color=(255,0,0)) + key['canvas'].marks['anchor'] = pts + shp.dirty = True + + def mouse_move(self, shp, x, y, btn, **key): + self.cursor = 'arrow' + if self.status == 'move': + ox, oy = self.oldxy + up = (1,-1)[key['canvas'].up] + key['canvas'].move(key['px']-ox, (key['py']-oy)*up) + self.oldxy = key['px'], key['py'] + if key['alt'] and key['ctrl']: + self.status = 'pick' + if not 'anchor' in key['canvas'].marks: + pts = mark(shp) + if len(pts)>0: + pts = Points(np.vstack(pts), color=(255,0,0)) + key['canvas'].marks['anchor'] = pts + if 'anchor' in key['canvas'].marks: + m, obj, l = pick_point(key['canvas'].marks['anchor'], x, y, 5) + if not m is None: self.cursor = 'hand' + elif 'anchor' in key['canvas'].marks: + self.status = '' + del key['canvas'].marks['anchor'] + shp.dirty = True + if not self.pick_obj is None and not self.pick_m is None: + drag(self.pick_m, self.pick_obj, x, y) + pts = mark(self.pick_m) + if len(pts)>0: + pts = np.vstack(pts) + key['canvas'].marks['anchor'] = Points(pts, color=(255,0,0)) + self.pick_m.dirty = True + shp.dirty = True + if self.pick_obj is None and not self.pick_m is None: + offset(self.pick_m, x-self.p[0], y-self.p[1]) + pts = mark(self.pick_m) + if len(pts)>0: + pts = np.vstack(pts) + key['canvas'].marks['anchor'] = Points(pts, color=(255,0,0)) + self.p = x, y + self.pick_m.dirty =shp.dirty = True + + def mouse_wheel(self, shp, x, y, d, **key): + if d>0: key['canvas'].zoomout(x, y, coord='data') + if d<0: key['canvas'].zoomin(x, y, coord='data') \ No newline at end of file diff --git a/sciapp/action/simple.py b/sciapp/action/simple.py new file mode 100644 index 00000000..1a207eb5 --- /dev/null +++ b/sciapp/action/simple.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- +""" +Created on Sat Dec 3 03:32:05 2016 +@author: yxl +""" +import wx +import threading +import numpy as np +from time import time + +class Simple: + title = 'SimpleFilter' + asyn = True + note = [] + para = None + 'all, 8-bit, 16-bit, rgb, float, req_roi, stack, stack2d, stack3d, preview' + view = None + prgs = (None, 1) + modal = True + + def __init__(self): pass + + def progress(self, i, n): + self.prgs = (i, n) + + def load(self, ips):return True + + def preview(self, ips, para):pass + + def show(self): + preview = lambda para, ips=self.ips: self.preview(ips, para) or ips.update() + return self.app.show_para(self.title, self.view, self.para, preview, + on_ok=lambda : self.ok(self.ips), on_cancel=lambda : self.cancel(self.ips) or self.ips.update(), + preview='preview' in self.note, modal=self.modal) + + def run(self, ips, imgs, para = None):pass + + def cancel(self, ips):pass + + def ok(self, ips, para=None, callafter=None): + if para == None: para = self.para + if self.asyn : + threading.Thread(target = self.runasyn, + args = (ips, ips.imgs, para, callafter)).start() + else: self.runasyn(ips, ips.imgs, para, callafter) + + def runasyn(self, ips, imgs, para = None, callback = None): + start = time() + self.run(ips, imgs, para) + self.app.info('%s: cost %.3fs'%(ips.title, time()-start)) + ips.update() + if callback!=None:callback() + + def check(self, ips): + note = self.note + if ips == None: + self.app.alert('No image opened!') + return False + if 'req_roi' in note and ips.roi == None: + self.app.alert('No Roi found!') + return False + if not 'all' in note: + if ips.dtype==np.uint8 and ips.channels==3 and not 'rgb' in note: + self.app.alert('Do not surport rgb image') + return False + elif ips.dtype==np.uint8 and ips.channels==1 and not '8-bit' in note: + self.app.alert('Do not surport 8-bit image') + return False + elif ips.dtype==np.uint16 and not '16-bit' in note: + self.app.alert('Do not surport 16-bit uint image') + return False + elif ips.dtype in [np.int32, np.int64] and not 'int' in note: + self.app.alert('Do not surport 32-bit int uint image') + return False + elif ips.dtype in [np.float32, np.float64] and not 'float' in note: + self.app.alert('Do not surport float image') + return False + if sum([i in note for i in ('stack','stack2d','stack3d')])>0: + if ips.get_nslices()==1: + self.app.alert('Stack required!') + return False + elif 'stack2d' in note and ips.is3d: + self.app.alert('Stack2d required!') + return False + elif 'stack3d' in note and not ips.is3d: + self.app.alert('Stack3d required!') + return False + return True + + def start(self, app, para=None, callback=None): + #print self.title, para + self.app, self.ips = app, app.get_img() + if not self.check(self.ips):return + if not self.load(self.ips):return + + if para!=None: + self.ok(self.ips, para, callback) + elif self.view==None: + if not self.__class__.show is Simple.show: + if self.show(): + self.ok(self.ips, para, callback) + else: self.ok(self.ips, para, callback) + elif self.modal: + if self.show(): + self.ok(self.ips, para, callback) + else: + self.cancel(self.ips) + self.ips.update() + else: self.show() \ No newline at end of file diff --git a/sciapp/action/tolact.py b/sciapp/action/tolact.py new file mode 100644 index 00000000..3d68f129 --- /dev/null +++ b/sciapp/action/tolact.py @@ -0,0 +1,73 @@ +from .action import SciAction + +class Tool(SciAction): + title = 'Base Tool' + default = None + cursor = 'arrow' + def mouse_down(self, canvas, x, y, btn, **key): pass + def mouse_up(self, canvas, x, y, btn, **key): pass + def mouse_move(self, canvas, x, y, btn, **key): pass + def mouse_wheel(self, canvas, x, y, d, **key): pass + def start(self, app): + self.app, self.default = app, self + if not app is None: app.tool = self + +class DefaultTool(Tool): + title = 'Move And Scale' + def __init__(self): + self.oldxy = None + + def mouse_down(self, obj, x, y, btn, **key): + if btn==1: self.oldxy = key['px'], key['py'] + if btn==3: key['canvas'].fit() + + def mouse_up(self, obj, x, y, btn, **key): + self.oldxy = None + + def mouse_move(self, obj, x, y, btn, **key): + if self.oldxy is None: return + ox, oy = self.oldxy + up = (1,-1)[key['canvas'].up] + key['canvas'].move(key['px']-ox, (key['py']-oy)*up) + self.oldxy = key['px'], key['py'] + + def mouse_wheel(self, obj, x, y, d, **key): + if d>0: key['canvas'].zoomout(x, y, coord='data') + if d<0: key['canvas'].zoomin(x, y, coord='data') + + def start(self, app): + self.app = app + Tool.default = self + if not app is None: app.tool = self + +class ImageTool(DefaultTool): + default = None + title = 'Image Tool' + + def start(self, app): + self.app = app + ImageTool.default = self + if not app is None: app.tool = self + +class ShapeTool(DefaultTool): + default = None + title = 'Image Tool' + + def start(self, app): + self.app = app + ShapeTool.default = self + if not app is None: app.tool = self + +class TableTool(DefaultTool): + default = None + title = 'Table Tool' + + def start(self, app): + self.app = app + TableTool.default = self + if not app is None: app.tool = self + +DefaultTool().start(None) +ImageTool().start(None) +ShapeTool().start(None) +TableTool().start(None) \ No newline at end of file diff --git a/sciapp/app.py b/sciapp/app.py new file mode 100644 index 00000000..16051eea --- /dev/null +++ b/sciapp/app.py @@ -0,0 +1,149 @@ +import json, os.path as osp + +class Manager: + def __init__(self, unique=True, path=None): + self.objs, self.unique, self.path = [], unique, path + if not path is None: self.read(path) + + def add(self, name, obj, tag=None): + if self.unique: self.remove(name, tag) + self.objs.insert(0, (name, obj, tag)) + + def adds(self, objs): + for i in objs: self.add(*i) + + def get(self, name=None, tag=None, obj=None): + rst = self.gets(name, tag, obj) + return None if len(rst)==0 else rst[0][1] + + def gets(self, name=None, tag=None, obj=None): + rst = [i for i in self.objs if name is None or name == i[0]] + rst = [i for i in rst if obj is None or obj is i[1]] + return [i for i in rst if tag is None or tag == i[2]] + + def has(self, name=None, tag=None, obj=None): + return len(self.gets(name, tag, obj))>0 + + def remove(self, name=None, tag=None, obj=None): + for i in self.gets(name, tag, obj): self.objs.remove(i) + + def names(self, tag=None): + return [i[0] for i in self.gets(tag=tag)] + + def name(self, name): + names = self.names() + if not name in names : return name + for i in range(1, 100) : + n = "%s-%s"%(name, i) + if not n in names: return n + + def write(self, path=None): + with open(path or self.path, 'w') as f: + f.write(json.dumps(self.objs)) + + def read(self, path): + self.path = path + if not osp.exists(path): return + with open(path) as f: + self.adds(json.loads(f.read())) + +class Source: + managers = {} + + @classmethod + def manager(cls, name, value=None): + if not name in cls.managers: + cls.managers[name] = Manager() + return cls.managers[name] + +class App(): + def __init__(self): + self.img_manager = Manager() + self.wimg_manager = Manager() + self.tab_manager = Manager() + self.wtab_manager = Manager() + self.mesh_manager = Manager() + self.wmesh_manager = Manager() + self.managers = {} + + def manager(self, name, value=None): + if not name in self.managers: + self.managers[name] = Manager() + return self.managers[name] + + def show_img(self, img): pass + def show_table(self, img): pass + def show_md(self, img, title=''): pass + def show_txt(self, img, title=''): pass + def show_plot(self): pass + def show_mesh(self): pass + + def add_img(self, img): + if not self.img_manager.has(img.name, obj=img): + img.name = self.img_manager.name(img.name) + self.img_manager.add(img.name, img) + + def remove_img(self, img): + print('remove', img.name) + self.img_manager.remove(obj=img) + + def add_img_win(self, win): + self.wimg_manager.add(win.name, win) + + def remove_img_win(self, win): + self.wimg_manager.remove(obj=win) + + def get_img(self, name=None): + return self.img_manager.get(name) + + def get_img_name(self): + return self.img_manager.names() + + def get_img_win(self, name=None): + return self.wimg_manager.get(name) + + def add_tab(self, tab): + if not self.tab_manager.has(tab.name, obj=tab): + tab.name = self.tab_manager.name(tab.name) + self.tab_manager.add(tab.name, tab) + + def remove_tab(self, tab): + self.tab_manager.remove(obj=tab) + + def add_tab_win(self, win): + self.wtab_manager.add(win.name, win) + + def remove_tab_win(self, win): + self.wtab_manager.remove(obj=win) + + def get_tab(self, name=None): + return self.tab_manager.get(name) + + def get_tab_name(self): + return self.tab_manager.names() + + def get_tab_win(self, name=None): + return self.wtab_manager.get(name) + + def add_mesh(self, mesh): + mesh.name = self.mesh_manager.name('name', obj=tab, name=mesh.name) + self.mesh_manager.add(obj=mesh, name=mesh.name) + + def remove_mesh(self, mesh): + print('remove', mesh.name) + self.mesh_manager.remove(obj=mesh) + + def add_mesh_win(self, win): + self.wmesh_manager.add(obj=win, name=win.name, check=False) + + def remove_mesh_win(self, win): + self.wmesh_manager.remove(obj=win) + + def get_mesh(self, name=None): + return self.mesh_manager.get(name=name) + + def get_mesh_name(self): + return self.mesh_manager.gets('name') + + def get_mesh_win(self, name=None): + return self.wmesh_manager.get(name=name) diff --git a/sciapp/manager.py b/sciapp/manager.py new file mode 100644 index 00000000..3bd14db7 --- /dev/null +++ b/sciapp/manager.py @@ -0,0 +1,48 @@ +class Manager: + def __init__(self, unique=True, path=None): + self.objs, self.unique, self.path = [], unique, path + if not path is None: self.read(path) + + def add(self, name, obj, tag=None): + if self.unique: self.remove(name, tag, obj) + self.objs.insert(0, (name, obj, tag)) + + def adds(self, objs): for i in objs: self.add(*i) + + def get(self, name=None, tag=None, obj=None): + rst = self.filter(name, tag, obj) + return None if len(rst)==0 else rst[0] + + def filter(self, name=None, tag=None, obj=None): + rst = [i for i in self.objs if name is None or name = i[0]] + rst = [i for i in self.objs if obj is None or obj = i[1]] + return [i for i in self.objs if tag is None or tag = i[2]] + + def gets(self, name=None, tag=None): + return self.filter(name, tag) + + def has(self, name=None, tag=None, obj=None): + return len(self.filter(name, tag, obj))>0 + + def remove(self, name=None, tag=None, obj=None): + for i in self.filter(name, tag, obj): self.objs.remove(i) + + def names(self, tag=None): + return [i[0] for i in self.filter(tag=tag)] + + def name(self, name, obj=None): + names = self.names() + if not name in names : return name + for i in range(1, 100) : + n = "%s-%s"%(name, i) + if not n in names: return n + + def write(self, path=None): + with open(path or self.path, 'w') as f: + f.write(json.dumps(self.objs)) + + def read(self, path): + if not osp.exists(path): return + self.path = path + with open(path) as f: + self.adds(json.loads(f.read())) diff --git a/sciapp/object/__init__.py b/sciapp/object/__init__.py new file mode 100644 index 00000000..0f118bae --- /dev/null +++ b/sciapp/object/__init__.py @@ -0,0 +1,5 @@ +from .shape import * +from .image import Image +from .table import Table +from .roi import ROI +from .surface import Surface, MarkText, MeshSet \ No newline at end of file diff --git a/sciapp/object/image.py b/sciapp/object/image.py new file mode 100644 index 00000000..b8f97279 --- /dev/null +++ b/sciapp/object/image.py @@ -0,0 +1,174 @@ +import numpy as np + +default_lut = np.arange(256*3, dtype=np.uint8).reshape((3,-1)).T + +def get_updown(imgs, slices='all', chans='all', step=1): + c = chans if isinstance(chans, int) else slice(None) + if isinstance(slices, int): imgs = [imgs[slices]] + if step<=1: step = int(1/step+0.5) + else: step = int(min(imgs[0].shape[:2])/step+0.5) + s = slice(None, None, max(step,1)) + s = (s,s,c)[:imgs[0].ndim] + mins = [i[s].min(axis=(0,1)) for i in imgs] + maxs = [i[s].max(axis=(0,1)) for i in imgs] + mins = np.array(mins).reshape((len(mins),-1)) + maxs = np.array(maxs).reshape((len(maxs),-1)) + mins, maxs = mins.min(axis=0), maxs.max(axis=0) + if np.iscomplexobj(mins): + mins, maxs = np.zeros(mins.shape), np.abs(maxs) + if chans!='all': return mins.min(), maxs.max() + return [(i,j) for i,j in zip(mins, maxs)] + +def lookup(img, cn, rgs, lut): + if isinstance(cn, int): cn = [cn] + img = img.reshape(img.shape[:2]+(-1,)) + buf = np.zeros(img.shape[:2]+(len(cn),), dtype=np.uint8) + for i in range(len(cn)): + rg = rgs[cn[i]] + k = 255.0/(max(1e-10, rg[1]-rg[0])) + bf = np.clip(img[:,:,cn[i]], rg[0], rg[1]) + np.subtract(bf, rg[0], out=bf, casting='unsafe') + np.multiply(bf, k, out=buf[:,:,i], casting='unsafe') + return buf if len(cn)==3 else lut[buf.reshape(img.shape[:2])] + +def histogram(imgs, rg=(0,256), slices='all', chans='all', step=1): + c = chans if isinstance(chans, int) else slice(None) + if isinstance(slices, int): imgs = [imgs[slices]] + if step<=1: step = int(1/step+0.5) + else: step = int(min(imgs[0].shape[:2])/step+0.5) + s = slice(None, None, max(step,1)) + s = (s,s,c)[:imgs[0].ndim] + rg = np.linspace(rg[0], rg[1], 257) + hist = [np.histogram(i[s], rg)[0] for i in imgs] + return np.sum(hist, axis=0) + +class Image: + def __init__(self, imgs=None, name='Image'): + self.name = name + self.cur = 0 + self.set_imgs(imgs) + self.roi = None + self.mark = None + self.unit = 1, 'pix' + self.msk = None + self.pos = (0,0) + self.cn = 0 + self.rg = (0,255) + self.lut = default_lut + self.log = False + self.mode = 'set' + self.dirty = False + self.snap = None + self.back = None + + @property + def box(self): + (x, y), (h, w) = self.pos, self.shape + return [x, y, x+w, y+h] + + @property + def title(self): return self.name + + @property + def img(self): return self.imgs[self.cur] + + @img.setter + def img(self, value): + self.imgs[self.cur] = value + self.reset() + + def set_imgs(self, imgs): + self.imgs = [imgs] if imgs is None else imgs + if not imgs is None: self.reset() + + @property + def channels(self): + if self.img.ndim==2: return 1 + else: return self.img.shape[2] + + @property + def slices(self): return len(self.imgs) + + @property + def isarray(self): return isinstance(self.imgs, np.ndarray) + + @property + def nbytes(self): + return sum([i.nbytes for i in self.imgs]) + + @property + def dtype(self): return self.img.dtype + + @property + def shape(self): return self.img.shape[:2] + + @property + def info(self): + return '%s %sx%s S:%s/%s C:%s/%s %.2fM'%(str(self.dtype).upper(), *self.shape, + self.cur+1, self.slices, self.cn, self.channels, self.nbytes/1024/1024) + + @property + def range(self): + rg = np.array(self.rg).reshape((-1,2)) + return (rg[:,0].min(), rg[:,1].max()) + + @range.setter + def range(self, value): + self.rg = [value] * len(self.rg) + + def mask(self, mode='in'): + if self.roi==None: return None + if self.roi.msk != mode: + self.msk = self.roi.to_mask(self.shape, mode) + return self.msk + + @property + def rect(self): + if self.roi is None: return slice(None), slice(None) + box, shape = self.roi.box, self.shape + l, r = max(0, int(box[0])), min(shape[1], int(box[2])) + t, b = max(0, int(box[1])), min(shape[0], int(box[3])) + return slice(t,b), slice(l,r) + + def subimg(self, s1=None, s2=None): + s1 = s1 or self.rect[0] + s2 = s2 or self.rect[1] + if self.isarray: return self.imgs[s1, s2] + else: return [i[s1, s2] for i in self.imgs] + + def update(self): self.dirty = True + + def reset(self): + self.cn = [0, (0,1,2)][self.channels==3] + if self.dtype == np.uint8: + self.rg = [(0, 255)] * self.channels + else: + self.rg = self.get_updown('all', 'all', step=512) + + def snapshot(self): + if self.snap is None: + self.snap = self.img.copy() + else: self.snap[:] = self.img + + def swap(self): + if self.snap is None:return + buf = self.img.copy() + self.img[:], self.snap[:] = self.snap, buf + + def histogram(self, rg=None, slices=None, chans=None, step=1): + if slices is None: slices = self.cur + if chans is None: chans = self.cn + if rg is None: rg = self.range + return histogram(self.imgs, rg, slices, chans, step) + + def get_updown(self, slices='all', chans='all', step=512): + if slices is None: slices = self.cur + if chans is None: chans = self.cn + return get_updown(self.imgs, slices, chans, step) + + def lookup(self, img=None): + if img is None: img = self.img + return lookup(img, self.cn, self.rg, self.lut) + +if __name__ == '__main__': + img = Image(np.zeros((5,5))) diff --git a/sciapp/object/roi.py b/sciapp/object/roi.py new file mode 100644 index 00000000..f81f9ce3 --- /dev/null +++ b/sciapp/object/roi.py @@ -0,0 +1,107 @@ +from .shape import * +import numpy as np +from skimage import draw +import shapely.geometry as geom + +def circle(r): + xs, ys = np.mgrid[-r:r+1, -r:r+1] + rcs = np.where((xs**2 + ys**2)=0) & (rr=0) & (cc0: rr, cc = draw_lines(rr, cc, img.shape, lw) + else: rr, cc = draw.polygon(rr, cc, img.shape) + img[rr, cc] += color + if type(shp) is geom.MultiLineString: + for i in shp: draw_shp(i, img, color, lw) + if type(shp) is geom.Polygon and lw>0: + draw_shp(shp.exterior, img, color, lw) + for i in shp.interiors: draw_shp(i, img, color, lw) + if type(shp) is geom.Polygon and lw==0: + draw_shp(shp.exterior, img, color, lw) + for i in shp.interiors: draw_shp(i, img, -color, lw) + if type(shp) is geom.MultiPolygon: + for i in shp: draw_shp(i, img, color, lw) + if type(shp) is geom.GeometryCollection: + for i in shp: draw_shp(i, img, color, lw) + return img + +class ROI(Layer): + def __init__(self, body=None, **key): + if isinstance(body, Layer): body = body.body + if not body is None and not isinstance(body, list): + body = [body] + Layer.__init__(self, body, **key) + self.fill = False + self.msk = None + + @property + def roitype(self): + roitype = '' + for i in self.body: + if roitype == '': roitype = i.dtype + if roitype != i.dtype: return 'multi' + return roitype + + def to_mask(self, msk, mode): + if isinstance(msk, tuple): + msk = np.zeros(msk, dtype=np.int8) + else: msk[:] = 0 + msk.dtype = np.int8 + if mode=='in': + draw_shp(self.to_geom(), msk, 1, 0) + np.clip(msk, 0, 1, out=msk) + if mode=='out': + draw_shp(self.to_geom(), msk, 1, 0) + np.clip(msk, 0, 1, out=msk) + msk -= 1 + if isinstance(mode, int): + draw_shp(self.to_geom(), msk, 1, mode) + np.clip(msk, 0, 1, out=msk) + msk.dtype, self.msk = np.bool, mode + return msk + +if __name__ == '__main__': + pts = Points([(10,10),(15,20)]) + line = Line([(10,10),(15,20),(20,20)]) + lines = Lines([[(10,10),(15,20),(20,20)]]) + polygon = Polygon([[(1,1),(1,20),(20,20),(20,1)],[(5,5),(5,10),(10,10),(10,5)]]) + + im = draw_shp(pts.to_geom(), (30,30), lw=0) + plt.imshow(im) + plt.show() diff --git a/sciapp/object/shape.py b/sciapp/object/shape.py new file mode 100644 index 00000000..edb737f4 --- /dev/null +++ b/sciapp/object/shape.py @@ -0,0 +1,374 @@ +import numpy as np +from time import time +from collections import Iterable +import shapely.geometry as geom +from shapely.ops import unary_union + +def merge(a, b): + x1, y1 = min(a[0],b[0]), min(a[1],b[1]) + x2, y2 = max(a[2],b[2]), max(a[3],b[3]) + return [x1, y1, x2, y2] + +default_style = {'color':(255,255,0), 'fcolor':(255,255,255), + 'fill':False, 'lw':1, 'tcolor':(255,0,0), 'size':8} + +class Shape: + dtype = 'shape' + def __init__(self, body=None, **key): + self.name = 'shape' + self.body = [] if body is None else body + self.color = key['color'] if 'color' in key else None + self.fcolor = key['fcolor'] if 'fcolor' in key else None + self.lstyle = key['lstyle'] if 'lstyle' in key else None + self.lw = key['lw'] if 'lw' in key else None + self.r = key['r'] if 'r' in key else None + self.fill = key['fill'] if 'fill' in key else None + self._box = None + self.dirty = True + + @property + def box(self): + if self._box is None or self.dirty: + self._box = self.count_box() + return self._box + + @property + def style(self): + styledic = {'type':self.dtype} + if not self.color is None: styledic['color']=self.color + if not self.fcolor is None: styledic['fcolor']=self.fcolor + if not self.lstyle is None: styledic['lstyle']=self.lstyle + if not self.lw is None: styledic['lw']=self.lw + if not self.fill is None: styledic['fill']=self.fill + return styledic + + def count_box(self, body=None, box=None): + if body is None: + box = [1e10, 1e10,-1e10,-1e10] + self.count_box(self.body, box) + return box + if isinstance(body, np.ndarray): + body = body.reshape((-1,2)) + minx, miny = body.min(axis=0) + maxx, maxy = body.max(axis=0) + newbox = [minx, miny, maxx, maxy] + box.extend(merge(box, newbox)) + del box[:4] + else: + for i in body: self.count_box(i, box) + + @property + def info(self): + minx, miny, maxx, maxy = self.box + return 'Type:%s minX:%.3f maxX:%.3f, minY:%.3f maxY%.3f'%( + self.dtype, minx, maxx, miny, maxy) + + def to_mark(self, body): + mark = self.style + mark['body'] = body + return mark + + def to_json(self): + return geom.mapping(self.to_geom()) + + def to_geom(self): pass + + def __str__(self): + return str(self.to_mark()) + +class Point(Shape): + dtype = 'point' + def __init__(self, body=[], **key): + Shape.__init__(self, body, **key) + self.body = np.array(body, dtype=np.float32) + + def to_mark(self): + return Shape.to_mark(self, tuple(self.body.tolist())) + + def to_geom(self): + return geom.Point(self.body) + +class Points(Shape): + dtype = 'points' + def __init__(self, body=[], **key): + Shape.__init__(self, body, **key) + self.body = np.array(body, dtype=np.float32) + + def to_mark(self): + return Shape.to_mark(self, [tuple(i.tolist()) for i in self.body]) + + def to_geom(self): + return geom.MultiPoint(self.body) + +class Line(Shape): + dtype = 'line' + def __init__(self, body=[], **key): + Shape.__init__(self, body, **key) + self.body = np.array(body, dtype=np.float32) + + def to_mark(self): + return Shape.to_mark(self, [tuple(i.tolist()) for i in self.body]) + + def to_geom(self): + return geom.LineString(self.body) + +class Lines(Shape): + dtype = 'lines' + def __init__(self, body=[], **key): + Shape.__init__(self, body, **key) + self.body = [np.array(i, dtype=np.float32) for i in body] + + def to_mark(self): + return Shape.to_mark(self, [[tuple(i.tolist()) for i in j] for j in self.body]) + + def to_geom(self): + return geom.MultiLineString(self.body) + +class Polygon(Shape): + dtype = 'polygon' + def __init__(self, body=[], **key): + Shape.__init__(self, body, **key) + if len(body)>0 and not isinstance(body[0][0], Iterable): body = [body] + self.body = [np.array(i, dtype=np.float32) for i in body] + + def to_mark(self): + return Shape.to_mark(self, [[tuple(i.tolist()) for i in j] for j in self.body]) + + def to_geom(self): + return geom.Polygon(self.body[0], self.body[1:]) + +class Polygons(Shape): + dtype = 'polygons' + def __init__(self, body=[], **key): + Shape.__init__(self, body, **key) + for i in range(len(body)): + if not isinstance(body[i][0][0], Iterable): body[i] = [body[i]] + self.body = [[np.array(i, dtype=np.float32) for i in j] for j in body] + + def to_mark(self): + mark = self.style + f = lambda x:[[tuple(i.tolist()) for i in j] for j in x] + return Shape.to_mark(self, [[[tuple(i.tolist()) for i in j] for j in k] for k in self.body]) + + def to_geom(self): + return geom.MultiPolygon([[i[0], i[1:]] for i in self.body]) + +class Circle(Shape): + dtype = 'circle' + def __init__(self, body=[], **key): + Shape.__init__(self, body, **key) + self.body = np.array(body, dtype=np.float32) + + def count_box(self): + pts = self.body + return [pts[0]-pts[2], pts[1]-pts[2], pts[0]+pts[2], pts[1]+pts[2]] + + def to_mark(self): + return Shape.to_mark(self, tuple(self.body.tolist())) + + def to_geom(self): + return geom.Point(self.body[:2]).buffer(self.body[2]) + +class Circles(Shape): + dtype = 'circles' + def __init__(self, body=[], **key): + Shape.__init__(self, body, **key) + self.body = np.array(body, dtype=np.float32) + + def count_box(self): + pts = self.body.T + return [(pts[0]-pts[2]).min(), (pts[1]-pts[2]).min(), (pts[0]+pts[2]).max(), (pts[1]+pts[2]).max()] + + def to_mark(self): + return Shape.to_mark(self, [tuple(i.tolist()) for i in self.body]) + + def to_geom(self): + return geom.MultiPolygon([geom.Point(i[:2]).buffer(i[2]) for i in self.body]) + +class Rectangle(Shape): + dtype = 'rectangle' + def __init__(self, body=[], **key): + Shape.__init__(self, body, **key) + self.body = np.array(body, dtype=np.float32) + + def count_box(self): + return [self.body[0], self.body[1], self.body[0]+self.body[2], self.body[1]+self.body[3]] + + def to_mark(self): + return Shape.to_mark(self, tuple(self.body.tolist())) + + def to_geom(self): + f = lambda x:[(x[0],x[1]),(x[0],x[1]+x[3]),(x[0]+x[2],x[1]+x[3]),(x[0]+x[2],x[1])] + return geom.Polygon(f(self.body)) + +class Rectangles(Shape): + dtype = 'rectangles' + def __init__(self, body=[], **key): + Shape.__init__(self, body, **key) + self.body = np.array(body, dtype=np.float32) + + def count_box(self, body=None, box=None): + self.body[:,2:] += self.body[:,:2] + minx, miny = self.body.reshape((-1,2)).min(axis=0) + maxx, maxy = self.body.reshape((-1,2)).max(axis=0) + self.body[:,2:] -= self.body[:,:2] + return [minx, miny, maxx, maxy] + + def to_mark(self): + return Shape.to_mark(self, [tuple(i.tolist()) for i in self.body]) + + def to_geom(self): + f = lambda x:[(x[0],x[1]),(x[0],x[1]+x[3]),(x[0]+x[2],x[1]+x[3]),(x[0]+x[2],x[1])] + return geom.MultiPolygon([[f(i),[]] for i in self.body]) + +def make_ellipse(x0, y0, l1, l2, ang): + m = np.array([[l1*np.cos(-ang),-l2*np.sin(-ang)], + [l1*np.sin(-ang),l2*np.cos(-ang)]]) + a = np.linspace(0, np.pi*2, 36) + xys = np.array((np.cos(a), np.sin(a))) + return np.dot(m, xys).T + (x0, y0) + +class Ellipse(Shape): + dtype = 'ellipse' + def __init__(self, body=[], **key): + Shape.__init__(self, body, **key) + self.body = np.array(body, dtype=np.float32) + + def count_box(self): + xy = make_ellipse(*self.body) + (minx, miny), (maxx, maxy) = xy.min(axis=0), xy.max(axis=0) + return [minx, miny, maxx, maxy] + + def to_mark(self): + return Shape.to_mark(self, tuple(self.body.tolist())) + + def to_geom(self): + return geom.Polygon(make_ellipse(*self.body)) + +class Ellipses(Shape): + dtype = 'ellipses' + def __init__(self, body=[], **key): + Shape.__init__(self, body, **key) + self.body = np.array(body, dtype=np.float32) + + def count_box(self): + xy = np.vstack([make_ellipse(*i) for i in self.body]) + (minx, miny), (maxx, maxy) = xy.min(axis=0), xy.max(axis=0) + return [minx, miny, maxx, maxy] + + def to_mark(self): + return Shape.to_mark(self, [tuple(i.tolist()) for i in self.body]) + + def to_geom(self): + return geom.MultiPolygon([[make_ellipse(*i),[]] for i in self.body]) + +class Text(Shape): + dtype = 'text' + def __init__(self, body=[], **key): + Shape.__init__(self, body, **key) + self.body = np.array(body[:2], dtype=np.float32) + self.txt = body[2] + + def to_mark(self): + return Shape.to_mark(self, tuple(self.body.tolist())+(self.txt,)) + + def to_geom(self): + return geom.Point(self.body) + +class Texts(Shape): + dtype = 'texts' + def __init__(self, body=[], **key): + Shape.__init__(self, body, **key) + body = np.array(body, dtype=object) + self.body = body[:,:2].astype(np.float32) + self.txt = body[:,2] + + def to_mark(self): + return Shape.to_mark(self, [(i,j,t) for (i,j), t in zip(self.body.tolist(), self.txt.tolist())]) + + def to_geom(self): + return geom.MultiPoint(self.body) + +class Layer(Shape): + dtype = 'layer' + def __init__(self, body=None, **key): + Shape.__init__(self, body, **key) + + @property + def box(self): + if len(self.body)==0: return None + boxs = np.array([i.box for i in self.body]).T + return [boxs[0].min(), boxs[1].min(), boxs[2].max(), boxs[3].max()] + + def to_mark(self): + return Shape.to_mark(self, [i.to_mark() for i in self.body]) + + def to_geom(self): + return geom.GeometryCollection([i.to_geom() for i in self.body]) + +class Layers: + dtype = 'layers' + def __init__(self, body=None, **key): + Shape.__init__(self, body, **key) + + + @property + def box(self): + if len(self.body)==0: return None + boxs = np.array([i.box for i in self.body.values()]).T + return [boxs[0].min(), boxs[1].min(), boxs[2].max(), boxs[3].max()] + + def to_mark(self): + body = dict(zip(self.body.keys(), [i.to_mark() for i in self.body.values()])) + return Shape.to_mark(self, body) + +def mark2shp(mark): + style = mark.copy() + style.pop('body') + keys = {'point':Point, 'points':Points, 'line':Line, 'lines':Lines, + 'polygon':Polygon, 'polygons':Polygons, 'circle':Circle, + 'circles':Circles, 'rectangle':Rectangle, 'rectangles':Rectangles, + 'ellipse':Ellipse, 'ellipses':Ellipses, 'text':Text, 'texts':Texts} + if mark['type'] in keys: return keys[mark['type']](mark['body'], **style) + if mark['type']=='layer': + return Layer([mark2shp(i) for i in mark['body']], **style) + if mark['type']=='layers': + return Layers(dict(zip(mark['body'].keys(), + [mark2shp(i) for i in mark['body'].values()])), **style) + +def json2shp(obj): + if obj['type']=='Point': + return Point(obj['coordinates']) + if obj['type']=='MultiPoint': + return Points(obj['coordinates']) + if obj['type']=='LineString': + return Line(obj['coordinates']) + if obj['type']=='MultiLineString': + return Lines(obj['coordinates']) + if obj['type']=='Polygon': + return Polygon(obj['coordinates']) + if obj['type']=='MultiPolygon': + return Polygons(obj['coordinates']) + if obj['type']=='GeometryCollection': + return Layer([json2shp(i) for i in obj['geometries']]) + +def geom2shp(obj): return json2shp(geom.mapping(obj)) + +def geom_flatten(obj, geoms=None): + geoms, root = ([], True) if geoms is None else (geoms, False) + if isinstance(obj, geom.GeometryCollection): + for i in obj: geom_flatten(i, geoms) + elif type(obj) in {geom.MultiPolygon, geom.MultiPoint, geom.MultiLineString}: + geoms.extend(list(obj)) + else: geoms.append(obj) + if root: return geom.GeometryCollection(geoms) + +def geom_union(obj): + return geom_flatten(unary_union(geom_flatten(obj))) + +if __name__ == '__main__': + import json + txt = mark2shp({'type':'texts', 'body':[(0,0,'a'),(1,1,'b')]}) + #import geonumpy.io as gio + #shp = gio.read_shp('C:/Users/54631/Documents/projects/huangqu/demo/shape/province.shp') + #feas = json.loads(shp.to_json())['features'] diff --git a/sciapp/object/surface.py b/sciapp/object/surface.py new file mode 100644 index 00000000..2d9e4881 --- /dev/null +++ b/sciapp/object/surface.py @@ -0,0 +1,68 @@ +import numpy as np +import moderngl +import numpy as np +from math import sin, cos, tan, pi + +class Surface: + def __init__(self, vts, ids, ns, cs=(0,0,1), **key): + self.vts = vts + self.ids = ids + self.ns = ns + self.cs = cs + self.box = np.vstack( + (vts.min(axis=0), + vts.max(axis=0))) + self.mode = 'mesh' + self.alpha = 1 + self.visible = True + self.width = 1 + self.update = False + self.set_style(**key) + + def set_style(self, **key): + if 'mode' in key: self.mode = key['mode'] + if 'alpha' in key: self.alpha = key['alpha'] + if 'visible' in key: self.visible = key['visible'] + if 'color' in key: + self.cs = key['color'] + self.update = True + +class MarkText(Surface): + def __init__(self, vts, ids, os, h, color): + self.vts = vts + self.ids = ids + self.cs = color + self.os = os + self.h = h + self.box = None + self.visible = True + self.width = 1 + self.mode = 'grid' + self.update = False + +class MeshSet: + def __init__(self, name='meshset', objs=None): + self.name = name + self.objs = objs or {} + + @property + def box(self): + minb = [i.box[0] for i in self.objs.values() if not i.box is None] + maxb = [i.box[1] for i in self.objs.values() if not i.box is None] + minb, maxb = np.array(minb).min(axis=0), np.array(maxb).max(axis=0) + return np.vstack((minb, maxb)) + + @property + def center(self): return self.box.mean(axis=0) + + @property + def dial(self): return np.linalg.norm(self.box[1]-self.box[0]) + + def add_surf(self, name, obj): + self.objs[name] = obj + + def get_obj(self, key): + if not key in self.objs: return None + return self.objs[key] + + diff --git a/sciapp/object/table.py b/sciapp/object/table.py new file mode 100644 index 00000000..d98fdeee --- /dev/null +++ b/sciapp/object/table.py @@ -0,0 +1,79 @@ +class Table(): + def __init__(self, df=None): + self.name = 'Table' + self.df = df + self.rg = None + self.props = None + self.snap = None + self.dirty = False + if not df is None: + self.data = df + + @property + def title(self): return self.name + + @property + def nbytes(self): + return self.data.memory_usage().sum() + + @property + def columns(self):return self.data.columns + + @property + def index(self):return self.data.index + + @property + def shape(self): return self.data.shape + + @property + def data(self): return self.df + + def update(self): self.dirty = True + + @data.setter + def data(self, df): + self.df, self.props = df, None + self.rowmsk, self.colmsk = [], [] + self.count_range() + + @property + def style(self): + props, data = self.props, self.data + if props is None or set(props)!=set(data.columns): + ps = [[3, (0,0,0), (0,0,255), 'Text'] for i in data.columns] + self.props = dict(zip(data.columns, ps)) + return self.props + + def subtab(self, mode=True, num=False): + rs, cs = len(self.rowmsk), len(self.colmsk) + mskr = slice(None) if rs==0 else self.rowmsk + mskc = slice(None) if cs==0 else self.colmsk + if mode is None: mskr = mskc = slice(None) + mskr, mskc = self.data.index[mskr], self.data.columns[mskc] + if mode is False and rs>0: mskr = self.data.index.difference(mskr) + if mode is False and cs>0: mskc = self.data.columns.difference(mskc) + subtab = self.data.loc[mskr, mskc] + if num: subtab = subtab.select_dtypes(include=[np.number]) + return subtab + + def snapshot(self, mode=True, num=False): + self.snap = self.subtab(mode, num).copy() + + def set_style(self, col, **key): + for i, name in enumerate(['accu', 'tc', 'lc', 'ln']): + if name in key: self.style[col][i] = key[name] + + def count_range(self): + rg = list(zip(self.data.min(), self.data.max())) + self.rg = dict(zip(self.data.columns, rg)) + + def select(self, rs=[], cs=[], byidx=False): + if byidx: rs, cs = self.df.index[rs], self.df.columns[cs] + self.rowmsk, self.colmsk = rs, cs + +if __name__ == '__main__': + import numpy as np + import pandas as pd + df = pd.DataFrame(np.zeros((10,5)), columns=list('abcde')) + table = Table(df) + diff --git a/sciapp/object/test.txt b/sciapp/object/test.txt new file mode 100644 index 00000000..e30fa1f6 --- /dev/null +++ b/sciapp/object/test.txt @@ -0,0 +1 @@ +{'type': 'layer', 'body': [{'type': 'texts', 'body': [(276.27072, 133.78365, 'id=0'), (245.45842, 128.19148, 'id=1'), (215.51852, 120.03704, 'id=2'), (270.0, 121.0, 'id=3'), (223.38889, 123.27778, 'id=4'), (262.0, 121.5, 'id=5'), (286.81937, 130.94838, 'id=6'), (218.11111, 129.5, 'id=7'), (267.0, 127.0, 'id=8'), (251.0, 139.0, 'id=9'), (296.26666, 140.46666, 'id=10'), (272.0, 139.0, 'id=11'), (204.0, 151.0351, 'id=12'), (265.61047, 181.41685, 'id=13'), (270.0, 142.0, 'id=14'), (274.5, 141.0, 'id=15'), (310.54544, 142.81818, 'id=16'), (509.63635, 143.90909, 'id=17'), (296.88235, 148.64706, 'id=18'), (248.0, 147.0, 'id=19'), (492.0, 157.0, 'id=20'), (304.4, 158.8, 'id=21'), (204.14285, 161.0, 'id=22'), (288.2, 161.6, 'id=23'), (206.71428, 171.14285, 'id=24'), (500.0, 169.0, 'id=25'), (508.0, 171.0, 'id=26'), (207.0, 179.0, 'id=27'), (510.2857, 179.0, 'id=28'), (252.0, 183.5, 'id=29'), (504.25, 197.75, 'id=30'), (497.8, 204.6, 'id=31'), (202.12195, 215.56097, 'id=32'), (502.0, 205.0, 'id=33'), (504.33334, 211.0, 'id=34'), (511.0, 211.0, 'id=35'), (256.93332, 216.8, 'id=36'), (502.0, 217.0, 'id=37'), (509.9375, 224.1875, 'id=38'), (261.25, 225.875, 'id=39'), (270.5, 223.0, 'id=40'), (274.83673, 226.65306, 'id=41'), (502.0, 227.0, 'id=42'), (505.5, 229.0, 'id=43'), (256.9091, 240.54546, 'id=44'), (280.0, 237.0, 'id=45'), (508.0, 237.0, 'id=46'), (268.3349, 291.66824, 'id=47'), (287.31097, 293.64563, 'id=48'), (254.0, 255.0, 'id=49'), (225.41493, 294.4707, 'id=50'), (252.0, 265.0, 'id=51'), (250.0, 275.0, 'id=52'), (254.6807, 300.78946, 'id=53'), (248.0, 285.5, 'id=54'), (301.5, 288.5, 'id=55'), (246.0, 295.5, 'id=56'), (307.92307, 305.01923, 'id=57'), (244.0, 306.0, 'id=58'), (25.01087, 314.92392, 'id=59'), (500.83334, 314.10416, 'id=60'), (242.0, 317.0, 'id=61'), (3.1666667, 315.83334, 'id=62'), (51.5, 318.5, 'id=63'), (218.28915, 323.06024, 'id=64'), (252.63414, 322.36584, 'id=65'), (315.77777, 326.77777, 'id=66'), (341.5, 332.5, 'id=67'), (240.0, 328.0, 'id=68'), (316.23785, 373.0073, 'id=69'), (246.64119, 372.9535, 'id=70'), (247.33333, 335.46667, 'id=71'), (321.55554, 335.33334, 'id=72'), (207.86792, 336.75473, 'id=73'), (238.0, 338.5, 'id=74'), (212.54167, 345.3447, 'id=75'), (54.18182, 338.68182, 'id=76'), (244.5, 338.5, 'id=77'), (40.875, 339.0, 'id=78'), (4.5, 339.0, 'id=79'), (9.8, 339.93332, 'id=80'), (16.5, 339.0, 'id=81'), (20.2, 339.4, 'id=82'), (31.0, 339.3, 'id=83'), (46.5, 339.0, 'id=84'), (218.0, 339.0, 'id=85'), (294.5, 339.5, 'id=86'), (360.7647, 344.05884, 'id=87'), (268.94766, 371.49036, 'id=88'), (15.342857, 350.1143, 'id=89'), (311.5, 344.5, 'id=90'), (32.0, 345.5, 'id=91'), (46.408604, 349.01074, 'id=92'), (236.0, 348.5, 'id=93'), (243.43478, 351.80435, 'id=94'), (353.5, 345.0, 'id=95'), (4.0, 346.5, 'id=96'), (210.0332, 362.87085, 'id=97'), (329.4, 354.2, 'id=98'), (27.666666, 354.83334, 'id=99'), (347.0, 354.7143, 'id=100'), (48.0, 355.0, 'id=101'), (234.0, 358.5, 'id=102'), (396.01352, 358.92792, 'id=103'), (5.8333335, 357.16666, 'id=104'), (420.0, 356.5, 'id=105'), (429.2857, 356.85715, 'id=106'), (257.0, 357.0, 'id=107'), (318.0, 357.5, 'id=108'), (331.03125, 361.21875, 'id=109'), (465.0, 357.0, 'id=110'), (472.0, 357.0, 'id=111'), (490.5, 357.0, 'id=112'), (49.52941, 360.64706, 'id=113'), (350.34784, 361.43478, 'id=114'), (421.5, 360.5, 'id=115'), (358.0, 359.0, 'id=116'), (240.78787, 364.69696, 'id=117'), (296.0, 361.5, 'id=118'), (478.0, 360.75, 'id=119'), (282.5263, 362.5263, 'id=120'), (317.07144, 362.57144, 'id=121'), (433.83334, 362.0, 'id=122'), (452.0, 361.5, 'id=123'), (361.6, 362.8, 'id=124'), (294.0, 365.0, 'id=125'), (301.5, 365.0, 'id=126'), (13.208333, 370.27777, 'id=127'), (232.0, 369.5, 'id=128'), (2.5, 367.0, 'id=129'), (36.794117, 370.20587, 'id=130'), (191.58333, 374.08334, 'id=131'), (45.5, 375.0, 'id=132'), (210.0, 375.0, 'id=133'), (229.24138, 383.48276, 'id=134'), (214.28572, 385.0, 'id=135'), (208.0, 385.0, 'id=136'), (199.80724, 396.24097, 'id=137'), (52.0, 390.0, 'id=138'), (34.0, 391.25, 'id=139'), (40.625, 393.25, 'id=140'), (251.85715, 394.2857, 'id=141'), (257.5, 394.0, 'id=142'), (363.83334, 394.83334, 'id=143'), (246.85715, 394.2857, 'id=144'), (280.0, 393.0, 'id=145'), (326.42856, 393.57144, 'id=146'), (295.2, 394.8, 'id=147'), (307.66666, 396.0, 'id=148'), (321.5, 395.0, 'id=149'), (347.0, 395.5, 'id=150'), (31.515625, 399.57812, 'id=151'), (226.0, 399.5, 'id=152'), (241.2, 397.0, 'id=153'), (378.0, 396.0, 'id=154'), (9.166667, 398.0, 'id=155'), (43.133335, 398.73334, 'id=156'), (325.66666, 398.66666, 'id=157'), (65.28814, 405.0, 'id=158'), (289.8, 401.2, 'id=159'), (389.1, 400.45, 'id=160'), (377.0, 401.625, 'id=161'), (14.0, 402.0, 'id=162'), (22.5, 401.0, 'id=163'), (280.5, 401.0, 'id=164'), (259.2, 404.2, 'id=165'), (315.66666, 404.66666, 'id=166'), (372.75, 404.75, 'id=167'), (183.25287, 412.7701, 'id=168'), (198.28, 407.86, 'id=169'), (209.375, 406.625, 'id=170'), (211.85754, 471.1382, 'id=171'), (248.05556, 405.44446, 'id=172'), (264.5, 405.0, 'id=173'), (286.87195, 410.59756, 'id=174'), (6.0, 407.5, 'id=175'), (15.125, 407.79166, 'id=176'), (34.0, 407.0, 'id=177'), (364.3143, 409.65714, 'id=178'), (254.33333, 408.66666, 'id=179'), (241.6, 411.66666, 'id=180'), (334.36365, 411.54544, 'id=181'), (370.0, 410.0, 'id=182'), (208.0, 411.0, 'id=183'), (320.125, 411.875, 'id=184'), (324.0, 411.5, 'id=185'), (380.0, 411.0, 'id=186'), (205.0, 412.5, 'id=187'), (257.0625, 412.75, 'id=188'), (316.0, 412.5, 'id=189'), (0.0, 414.5, 'id=190'), (4.0, 413.0, 'id=191'), (8.25, 413.875, 'id=192'), (13.8, 413.4, 'id=193'), (330.4, 414.2, 'id=194'), (373.0, 414.2, 'id=195'), (382.0, 413.0, 'id=196'), (398.0, 413.0, 'id=197'), (201.1, 419.075, 'id=198'), (266.0, 414.5, 'id=199'), (38.0, 415.0, 'id=200'), (53.5, 415.0, 'id=201'), (247.18182, 417.63635, 'id=202'), (42.0, 416.0, 'id=203'), (230.5, 417.0, 'id=204'), (382.0, 416.75, 'id=205'), (501.57144, 416.7143, 'id=206'), (34.0, 417.0, 'id=207'), (46.0, 417.0, 'id=208'), (62.658226, 421.41772, 'id=209'), (254.33333, 417.33334, 'id=210'), (342.5, 417.0, 'id=211'), (481.72726, 418.0909, 'id=212'), (508.5, 417.0, 'id=213'), (26.68, 421.12, 'id=214'), (44.0, 419.8, 'id=215'), (15.714286, 420.14285, 'id=216'), (48.5, 419.0, 'id=217'), (276.4, 420.2, 'id=218'), (374.0, 419.0, 'id=219'), (418.0, 419.0, 'id=220'), (234.0, 420.5, 'id=221'), (493.66666, 420.66666, 'id=222'), (266.0, 421.0, 'id=223'), (312.0, 422.0, 'id=224'), (320.0, 421.0, 'id=225'), (502.0, 421.0, 'id=226'), (244.0, 422.5, 'id=227'), (302.47058, 423.7353, 'id=228'), (341.66666, 422.66666, 'id=229'), (46.0, 423.0, 'id=230'), (282.0, 424.0, 'id=231'), (294.0, 423.5, 'id=232'), (330.7143, 423.14285, 'id=233'), (472.0, 423.0, 'id=234'), (15.666667, 424.66666, 'id=235'), (233.0, 425.92, 'id=236'), (287.5, 424.5, 'id=237'), (323.75, 425.0, 'id=238'), (9.0, 425.0, 'id=239'), (204.6, 425.2, 'id=240'), (28.0, 427.0, 'id=241'), (55.5, 427.0, 'id=242'), (320.33334, 427.33334, 'id=243'), (392.0, 427.5, 'id=244'), (362.0, 431.875, 'id=245'), (14.0, 429.0, 'id=246'), (58.0, 429.0, 'id=247'), (63.5, 429.0, 'id=248'), (295.77084, 433.89584, 'id=249'), (314.33334, 430.91666, 'id=250'), (320.0, 431.5, 'id=251'), (329.8, 431.8, 'id=252'), (348.0, 431.0, 'id=253'), (430.0, 431.0, 'id=254'), (452.0, 431.0, 'id=255'), (464.0, 431.0, 'id=256'), (268.7143, 432.85715, 'id=257'), (306.66666, 433.44446, 'id=258'), (35.363636, 434.36365, 'id=259'), (56.5, 433.875, 'id=260'), (63.644444, 436.84445, 'id=261'), (279.5, 433.5, 'id=262'), (324.0, 433.0, 'id=263'), (352.73077, 434.96155, 'id=264'), (390.64706, 436.29413, 'id=265'), (185.0, 434.66666, 'id=266'), (339.125, 436.20834, 'id=267'), (382.0, 435.7143, 'id=268'), (404.0, 435.8, 'id=269'), (26.0, 435.0, 'id=270'), (40.0, 435.0, 'id=271'), (253.5, 435.33334, 'id=272'), (313.625, 437.5, 'id=273'), (28.5, 436.5, 'id=274'), (45.75, 437.58334, 'id=275'), (318.0, 436.5, 'id=276'), (386.0, 438.0, 'id=277'), (6.0, 437.0, 'id=278'), (54.583332, 438.25, 'id=279'), (204.0, 437.0, 'id=280'), (307.66666, 438.83334, 'id=281'), (410.0, 437.0, 'id=282'), (445.66666, 437.33334, 'id=283'), (460.2857, 438.2857, 'id=284'), (472.5, 437.0, 'id=285'), (481.66666, 437.33334, 'id=286'), (234.0, 438.0, 'id=287'), (322.58334, 439.08334, 'id=288'), (327.0, 444.15216, 'id=289'), (357.2857, 439.5, 'id=290'), (366.77777, 440.44446, 'id=291'), (0.5, 439.0, 'id=292'), (31.4, 439.8, 'id=293'), (335.66666, 441.16666, 'id=294'), (343.8, 442.32, 'id=295'), (397.66666, 439.33334, 'id=296'), (281.77777, 441.1111, 'id=297'), (408.0, 440.0, 'id=298'), (193.25, 441.25, 'id=299'), (299.6, 442.2, 'id=300'), (402.5, 441.0, 'id=301'), (486.0, 441.0, 'id=302'), (502.0, 441.0, 'id=303'), (316.93332, 442.6, 'id=304'), (294.0, 443.5, 'id=305'), (351.5, 443.0, 'id=306'), (247.66667, 444.66666, 'id=307'), (310.6, 445.8, 'id=308'), (65.07692, 446.6154, 'id=309'), (267.5, 445.0, 'id=310'), (306.0, 445.0, 'id=311'), (363.375, 446.375, 'id=312'), (369.41666, 447.16666, 'id=313'), (424.5, 445.0, 'id=314'), (244.0, 446.5, 'id=315'), (254.0, 446.0, 'id=316'), (31.5, 447.0, 'id=317'), (52.333332, 447.33334, 'id=318'), (304.0, 447.0, 'id=319'), (394.0, 447.5, 'id=320'), (260.25, 449.5, 'id=321'), (343.66666, 448.66666, 'id=322'), (390.0, 448.5, 'id=323'), (14.5, 449.0, 'id=324'), (60.68421, 453.42105, 'id=325'), (300.0, 449.0, 'id=326'), (316.33334, 451.79166, 'id=327'), (340.45, 453.7, 'id=328'), (348.75, 450.0, 'id=329'), (356.0, 449.0, 'id=330'), (456.75, 450.125, 'id=331'), (20.0, 450.5, 'id=332'), (394.32248, 477.52304, 'id=333'), (64.0, 451.0, 'id=334'), (190.0, 451.25, 'id=335'), (208.0, 451.0, 'id=336'), (224.66667, 452.08334, 'id=337'), (248.0, 451.5, 'id=338'), (418.0, 451.5, 'id=339'), (440.5, 451.0, 'id=340'), (16.3, 453.0, 'id=341'), (31.125, 453.125, 'id=342'), (39.57143, 452.7143, 'id=343'), (46.42857, 452.85715, 'id=344'), (53.333332, 453.41666, 'id=345'), (265.91306, 454.34784, 'id=346'), (286.41666, 452.91666, 'id=347'), (346.0, 452.5, 'id=348'), (466.16666, 452.83334, 'id=349'), (26.0, 453.25, 'id=350'), (65.5, 453.5, 'id=351'), (116.75, 454.25, 'id=352'), (172.66667, 453.66666, 'id=353'), (178.5, 453.0, 'id=354'), (244.4, 453.8, 'id=355'), (272.63635, 454.54544, 'id=356'), (330.72726, 454.0, 'id=357'), (402.0, 453.5, 'id=358'), (35.666668, 454.66666, 'id=359'), (371.83334, 454.66666, 'id=360'), (497.36365, 454.9091, 'id=361'), (230.0, 455.0, 'id=362'), (252.25, 455.75, 'id=363'), (302.5, 455.0, 'id=364'), (405.0, 456.42856, 'id=365'), (430.33334, 455.33334, 'id=366'), (462.0, 455.0, 'id=367'), (504.0, 455.0, 'id=368'), (240.72728, 458.0909, 'id=369'), (291.375, 456.75, 'id=370'), (39.2, 457.8, 'id=371'), (54.0, 457.0, 'id=372'), (180.82353, 457.88235, 'id=373'), (246.4, 458.2, 'id=374'), (274.7143, 458.2857, 'id=375'), (284.0, 457.0, 'id=376'), (345.0, 458.0, 'id=377'), (352.7143, 458.2857, 'id=378'), (362.0, 457.0, 'id=379'), (450.25, 457.75, 'id=380'), (502.0, 457.0, 'id=381'), (22.8, 458.6, 'id=382'), (117.666664, 458.66666, 'id=383'), (172.5, 458.5, 'id=384'), (188.5, 458.5, 'id=385'), (359.0, 458.0, 'id=386'), (435.2, 458.6, 'id=387'), (442.0, 458.0, 'id=388'), (506.5, 458.5, 'id=389'), (2.0, 459.0, 'id=390'), (6.0, 459.0, 'id=391'), (229.0, 459.0, 'id=392'), (260.0, 459.0, 'id=393'), (288.0, 459.0, 'id=394'), (304.0, 459.0, 'id=395'), (336.0, 459.0, 'id=396'), (423.0, 459.0, 'id=397'), (358.1, 465.525, 'id=398'), (65.5, 463.0, 'id=399'), (115.95918, 469.85715, 'id=400'), (170.5, 463.0, 'id=401'), (178.63333, 466.76666, 'id=402'), (188.0, 463.0, 'id=403'), (192.5, 464.0, 'id=404'), (223.66667, 463.33334, 'id=405'), (257.0, 464.16666, 'id=406'), (265.6, 463.8, 'id=407'), (272.5, 463.0, 'id=408'), (298.0, 463.0, 'id=409'), (314.0, 463.0, 'id=410'), (320.0, 463.5, 'id=411'), (325.5, 463.0, 'id=412'), (338.16666, 463.33334, 'id=413'), (362.0, 463.0, 'id=414'), (398.5, 464.0, 'id=415'), (404.0, 463.0, 'id=416'), (424.54544, 464.0, 'id=417'), (500.33334, 463.33334, 'id=418'), (329.5, 465.0, 'id=419'), (280.5, 465.0, 'id=420'), (364.5, 465.0, 'id=421'), (412.0, 465.0, 'id=422'), (41.666668, 466.66666, 'id=423'), (64.666664, 467.77777, 'id=424'), (170.5, 467.9375, 'id=425'), (38.0, 467.0, 'id=426'), (232.0, 467.5, 'id=427'), (236.0, 467.5, 'id=428'), (248.0, 467.0, 'id=429'), (269.0, 467.5, 'id=430'), (295.2, 468.2, 'id=431'), (306.5, 467.0, 'id=432'), (341.0, 468.6, 'id=433'), (490.0, 467.0, 'id=434'), (56.0, 468.5, 'id=435'), (376.33334, 468.66666, 'id=436'), (18.75, 472.45, 'id=437'), (24.5, 469.0, 'id=438'), (46.22222, 470.33334, 'id=439'), (196.5, 469.0, 'id=440'), (243.92308, 474.57693, 'id=441'), (256.29413, 470.58823, 'id=442'), (262.0, 469.0, 'id=443'), (336.0, 469.0, 'id=444'), (33.0, 470.83334, 'id=445'), (228.66667, 470.83334, 'id=446'), (498.0, 470.0, 'id=447'), (40.0, 471.0, 'id=448'), (287.5, 472.0, 'id=449'), (349.5, 471.0, 'id=450'), (501.7143, 472.85715, 'id=451'), (507.75, 472.25, 'id=452'), (170.0, 472.75, 'id=453'), (284.2857, 473.0, 'id=454'), (495.66666, 472.66666, 'id=455'), (8.207547, 476.56604, 'id=456'), (50.0, 474.16666, 'id=457'), (60.0, 473.0, 'id=458'), (266.0, 473.5, 'id=459'), (273.84616, 474.30768, 'id=460'), (492.33334, 473.33334, 'id=461'), (65.333336, 475.44446, 'id=462'), (180.0, 474.0, 'id=463'), (308.0, 474.0, 'id=464'), (333.75, 475.0, 'id=465'), (194.16667, 475.33334, 'id=466'), (255.16667, 476.0, 'id=467'), (298.0, 475.0, 'id=468'), (317.1111, 477.22223, 'id=469'), (329.5, 475.0, 'id=470'), (398.0, 475.0, 'id=471'), (402.0, 475.5, 'id=472'), (434.0, 475.0, 'id=473'), (62.0, 477.25, 'id=474'), (164.5, 477.0, 'id=475'), (218.75, 478.66666, 'id=476'), (250.0, 477.0, 'id=477'), (440.5, 477.0, 'id=478'), (347.83334, 479.16666, 'id=479'), (431.75, 479.0, 'id=480'), (493.2, 478.6, 'id=481'), (21.0, 480.0, 'id=482'), (42.0, 479.0, 'id=483'), (193.17647, 480.2353, 'id=484'), (224.0, 479.0, 'id=485'), (354.33334, 479.33334, 'id=486'), (447.5, 479.0, 'id=487'), (64.63636, 482.45456, 'id=488'), (164.0, 480.5, 'id=489'), (188.0, 480.5, 'id=490'), (234.5, 481.7, 'id=491'), (247.4, 481.9, 'id=492'), (256.42105, 482.10526, 'id=493'), (334.96295, 481.2963, 'id=494'), (376.0, 481.0, 'id=495'), (111.0, 481.0, 'id=496'), (116.0, 481.0, 'id=497'), (263.0, 481.0, 'id=498'), (284.0, 481.0, 'id=499'), (310.0, 481.5, 'id=500'), (324.0, 481.0, 'id=501'), (358.0, 481.0, 'id=502'), (118.75, 483.0, 'id=503'), (184.71428, 482.57144, 'id=504'), (321.0, 483.0, 'id=505'), (434.0, 482.0, 'id=506'), (14.0, 483.0, 'id=507'), (170.33333, 483.33334, 'id=508'), (288.0, 483.0, 'id=509'), (303.5, 483.0, 'id=510'), (345.6, 484.2, 'id=511'), (386.0, 484.0, 'id=512'), (453.5, 483.0, 'id=513'), (492.0, 483.0, 'id=514'), (37.25, 486.83334, 'id=515'), (52.0, 485.0, 'id=516'), (312.33334, 484.66666, 'id=517'), (114.0, 485.0, 'id=518'), (278.0, 485.0, 'id=519'), (332.0, 485.0, 'id=520'), (354.0, 485.0, 'id=521'), (358.0, 485.0, 'id=522'), (376.0, 486.0, 'id=523'), (484.16666, 485.33334, 'id=524'), (2.0, 486.7143, 'id=525'), (22.0, 489.5, 'id=526'), (350.0, 486.5, 'id=527'), (64.0, 487.0, 'id=528'), (111.5, 487.33334, 'id=529'), (118.46154, 488.6154, 'id=530'), (176.0, 488.0, 'id=531'), (260.0, 487.0, 'id=532'), (301.0, 487.0, 'id=533'), (372.0, 487.0, 'id=534'), (439.0, 487.0, 'id=535'), (286.63635, 489.27274, 'id=536'), (310.33334, 488.66666, 'id=537'), (32.0, 489.0, 'id=538'), (262.1111, 491.8889, 'id=539'), (315.5, 490.0, 'id=540'), (374.0, 489.0, 'id=541'), (17.0, 490.66666, 'id=542'), (303.6842, 491.78946, 'id=543'), (323.0, 490.875, 'id=544'), (349.5, 491.75, 'id=545'), (353.75, 491.0, 'id=546'), (167.0, 492.0, 'id=547'), (232.5, 494.125, 'id=548'), (293.5, 491.0, 'id=549'), (312.8, 492.2, 'id=550'), (384.92307, 493.0, 'id=551'), (422.63635, 492.18182, 'id=552'), (184.0, 492.0, 'id=553'), (221.8, 493.2, 'id=554'), (30.0, 493.0, 'id=555'), (40.0, 493.5, 'id=556'), (112.53333, 495.6, 'id=557'), (239.66667, 493.33334, 'id=558'), (256.25, 493.91666, 'id=559'), (295.5, 493.0, 'id=560'), (345.3125, 495.0625, 'id=561'), (358.42105, 494.10526, 'id=562'), (436.0, 493.5, 'id=563'), (462.0, 493.0, 'id=564'), (325.06668, 496.06668, 'id=565'), (337.91666, 495.58334, 'id=566'), (485.77777, 495.8889, 'id=567'), (3.5, 495.0, 'id=568'), (44.0, 495.0, 'id=569'), (50.0, 496.0, 'id=570'), (64.0, 495.0, 'id=571'), (106.0, 495.0, 'id=572'), (182.0, 495.0, 'id=573'), (331.2, 495.4, 'id=574'), (412.0, 495.0, 'id=575'), (456.5, 495.0, 'id=576'), (460.0, 495.0, 'id=577'), (496.33334, 495.33334, 'id=578'), (504.0, 495.0, 'id=579'), (40.333332, 496.66666, 'id=580'), (186.73334, 498.73334, 'id=581'), (248.0, 497.0, 'id=582'), (266.0, 496.0, 'id=583'), (22.0, 497.0, 'id=584'), (217.38889, 498.83334, 'id=585'), (227.0, 497.0, 'id=586'), (352.0, 497.0, 'id=587'), (367.0, 498.16666, 'id=588'), (418.0, 497.5, 'id=589'), (422.0, 497.0, 'id=590'), (435.625, 498.375, 'id=591'), (441.2857, 497.14285, 'id=592'), (502.0, 497.0, 'id=593'), (31.375, 499.0, 'id=594'), (63.0, 498.5, 'id=595'), (105.2, 499.7, 'id=596'), (116.0, 498.0, 'id=597'), (384.0, 498.0, 'id=598'), (0.0, 499.0, 'id=599'), (7.6666665, 499.33334, 'id=600'), (44.0, 499.0, 'id=601'), (273.0, 499.0, 'id=602'), (280.875, 500.125, 'id=603'), (494.0, 499.0, 'id=604'), (498.0, 499.0, 'id=605'), (165.66667, 500.66666, 'id=606'), (441.42856, 501.0, 'id=607'), (458.0, 500.5, 'id=608'), (59.384617, 503.80768, 'id=609'), (232.0, 501.0, 'id=610'), (244.0, 501.0, 'id=611'), (262.0, 501.0, 'id=612'), (299.7, 502.2, 'id=613'), (308.0, 501.0, 'id=614'), (330.0, 501.0, 'id=615'), (426.0, 501.0, 'id=616'), (470.0, 501.0, 'id=617'), (349.66666, 503.16666, 'id=618'), (38.0, 504.0, 'id=619'), (117.5, 503.0, 'id=620'), (222.0, 503.0, 'id=621'), (236.5, 503.0, 'id=622'), (242.92308, 504.46155, 'id=623'), (260.0, 503.0, 'id=624'), (311.2, 503.2, 'id=625'), (320.33334, 504.66666, 'id=626'), (343.5, 503.0, 'id=627'), (420.0, 503.0, 'id=628'), (431.6, 503.2, 'id=629'), (450.0, 503.0, 'id=630'), (175.375, 505.5, 'id=631'), (254.33333, 504.66666, 'id=632'), (395.75, 505.5, 'id=633'), (16.0, 505.0, 'id=634'), (26.0, 505.0, 'id=635'), (189.0, 505.0, 'id=636'), (234.0, 505.0, 'id=637'), (305.07693, 506.46155, 'id=638'), (316.0, 505.0, 'id=639'), (340.33334, 506.0, 'id=640'), (371.66666, 505.33334, 'id=641'), (468.0, 505.0, 'id=642'), (47.54286, 509.22858, 'id=643'), (274.0, 506.5, 'id=644'), (11.294118, 509.29413, 'id=645'), (17.95, 509.4, 'id=646'), (62.0, 507.0, 'id=647'), (117.0, 507.25, 'id=648'), (180.0, 507.5, 'id=649'), (236.0, 507.0, 'id=650'), (270.0, 507.0, 'id=651'), (282.0, 507.5, 'id=652'), (354.0, 507.0, 'id=653'), (362.0, 507.0, 'id=654'), (484.0, 508.0, 'id=655'), (503.5, 507.0, 'id=656'), (27.8, 509.6, 'id=657'), (59.57143, 509.7143, 'id=658'), (263.57144, 509.7143, 'id=659'), (332.0909, 509.63635, 'id=660'), (5.5, 510.0, 'id=661'), (237.5, 510.0, 'id=662'), (324.0, 510.0, 'id=663'), (356.5, 510.0, 'id=664'), (390.0, 510.0, 'id=665'), (397.0, 510.0, 'id=666'), (414.0, 510.0, 'id=667'), (479.7143, 510.0, 'id=668'), (368.0, 510.0, 'id=669'), (476.5, 510.0, 'id=670'), (484.0, 511.0, 'id=671')]}, {'type': 'ellipses', 'body': [(276.2707214355469, 133.7836456298828, 328.0933837890625, 179.8610076904297, 3.018669843673706), (245.4584197998047, 128.1914825439453, 16.651796340942383, 14.02171802520752, 0.690736711025238), (215.51852416992188, 120.03704071044922, 4.608210563659668, 2.1209144592285156, 1.512969732284546), (270.0, 121.0, 1.2649110555648804, 1.2649110555648804, 2.356194496154785), (223.38888549804688, 123.27777862548828, 2.8466944694519043, 1.9676482677459717, 1.357241153717041), (262.0, 121.5, 1.0, 0.0, 1.5707963705062866), (286.8193664550781, 130.94837951660156, 11.525166511535645, 7.204635143280029, 0.19030477106571198), (218.11111450195312, 129.5, 3.530224084854126, 1.6463161706924438, 2.209132194519043), (267.0, 127.0, 1.632993221282959, 0.0, 3.1415927410125732), (251.0, 139.0, 2.2563042640686035, 1.4770978689193726, 3.1415927410125732), (296.26666259765625, 140.46665954589844, 3.669114828109741, 2.2019782066345215, 2.0455899238586426), (272.0, 139.0, 0.0, 0.0, 2.356194496154785), (204.0, 151.03509521484375, 11.96079158782959, 3.9852099418640137, 2.3145058155059814), (265.6104736328125, 181.41685485839844, 36.509002685546875, 24.22110366821289, 2.4562292098999023), (270.0, 142.0, 1.632993221282959, 0.0, 1.5707963705062866), (274.5, 141.0, 1.0, 0.0, 3.1415927410125732), (310.5454406738281, 142.81817626953125, 3.917346239089966, 1.4970556497573853, 1.1298604011535645), (509.6363525390625, 143.90908813476562, 2.173064947128296, 1.5336781740188599, 2.8785593509674072), (296.8823547363281, 148.64706420898438, 3.017346143722534, 2.2204110622406006, 1.3552871942520142), (248.0, 147.0, 1.632993221282959, 0.0, 3.1415927410125732), (492.0, 157.0, 0.0, 0.0, 2.356194496154785), (304.3999938964844, 158.8000030517578, 1.5491933822631836, 0.8944271802902222, 1.249045729637146), (204.14285278320312, 161.0, 3.4621500968933105, 1.2830312252044678, 2.407599925994873), (288.20001220703125, 161.60000610351562, 2.0466690063476562, 0.7817580103874207, 1.660723090171814), (206.7142791748047, 171.14285278320312, 3.1949262619018555, 1.589698076248169, 1.5920600891113281), (500.0, 169.0, 0.0, 0.0, 2.356194496154785), (508.0, 171.0, 0.0, 0.0, 2.356194496154785), (207.0, 179.0, 2.6422383785247803, 1.0092452764511108, 1.0172219276428223), (510.28570556640625, 179.0, 1.5118578672409058, 1.3997083902359009, 1.5707963705062866), (252.0, 183.5, 1.0, 0.0, 1.5707963705062866), (504.25, 197.75, 1.7320507764816284, 0.7071067690849304, 1.249045729637146), (497.79998779296875, 204.60000610351562, 2.0466690063476562, 0.7817580103874207, 1.4808695316314697), (202.1219482421875, 215.56097412109375, 14.900699615478516, 0.9197909235954285, 0.8312706351280212), (502.0, 205.0, 0.0, 0.0, 2.356194496154785), (504.3333435058594, 211.0, 1.8856180906295776, 1.154700517654419, 3.1415927410125732), (511.0, 211.0, 0.0, 0.0, 2.356194496154785), (256.9333190917969, 216.8000030517578, 4.393863201141357, 1.1472523212432861, 1.26802396774292), (502.0, 217.0, 0.0, 0.0, 2.356194496154785), (509.9375, 224.1875, 4.256257057189941, 2.0562164783477783, 1.5921796560287476), (261.25, 225.875, 4.26151704788208, 2.0067317485809326, 1.251846432685852), (270.5, 223.0, 1.0, 0.0, 3.1415927410125732), (274.83673095703125, 226.65306091308594, 6.297449588775635, 3.5912249088287354, 0.4991496503353119), (502.0, 227.0, 0.0, 0.0, 2.356194496154785), (505.5, 229.0, 1.0, 0.0, 3.1415927410125732), (256.9090881347656, 240.5454559326172, 4.349363803863525, 0.7662689685821533, 1.2407492399215698), (280.0, 237.0, 0.0, 0.0, 2.356194496154785), (508.0, 237.0, 1.632993221282959, 0.0, 3.1415927410125732), (268.33489990234375, 291.6682434082031, 50.4422721862793, 4.219693660736084, 1.5738857984542847), (287.31097412109375, 293.6456298828125, 43.98822784423828, 15.397847175598145, 1.7467771768569946), (254.0, 255.0, 1.632993221282959, 0.0, 1.5707963705062866), (225.41493225097656, 294.470703125, 30.942045211791992, 11.906290054321289, 1.2717796564102173), (252.0, 265.0, 1.632993221282959, 0.0, 1.5707963705062866), (250.0, 275.0, 2.8284270763397217, 0.0, 1.5707963705062866), (254.68069458007812, 300.7894592285156, 23.173208236694336, 4.579484462738037, 1.4632377624511719), (248.0, 285.5, 2.2360680103302, 0.0, 1.5707963705062866), (301.5, 288.5, 1.0, 1.0, 2.356194496154785), (246.0, 295.5, 3.4156503677368164, 0.0, 1.5707963705062866), (307.9230651855469, 305.01922607421875, 10.396947860717773, 1.9497177600860596, 1.963199496269226), (244.0, 306.0, 4.0, 0.0, 1.5707963705062866), (25.0108699798584, 314.9239196777344, 11.416547775268555, 3.321161985397339, 0.05657835304737091), (500.8333435058594, 314.1041564941406, 10.488977432250977, 2.7221622467041016, 0.07842276245355606), (242.0, 317.0, 4.0, 0.0, 1.5707963705062866), (3.1666667461395264, 315.8333435058594, 2.4274654388427734, 1.597452998161316, 3.1083085536956787), (51.5, 318.5, 2.2360680103302, 1.0, 3.1415927410125732), (218.28915405273438, 323.06024169921875, 14.48153018951416, 2.037936210632324, 0.04470852017402649), (252.63414001464844, 322.3658447265625, 6.495011329650879, 2.0865750312805176, 0.04781278222799301), (315.77777099609375, 326.77777099609375, 7.65121603012085, 1.228119969367981, 1.606326699256897), (341.5, 332.5, 12.152097702026367, 0.9965852499008179, 1.5776140689849854), (240.0, 328.0, 4.0, 0.0, 1.5707963705062866), (316.23785400390625, 373.0072937011719, 72.41310119628906, 2.6567816734313965, 2.3347272872924805), (246.6411895751953, 372.9534912109375, 45.860260009765625, 4.149299144744873, 1.1657755374908447), (247.3333282470703, 335.4666748046875, 3.895936965942383, 1.2804112434387207, 1.7241970300674438), (321.5555419921875, 335.3333435058594, 3.02614164352417, 0.8479448556900024, 1.7501816749572754), (207.867919921875, 336.7547302246094, 7.077769756317139, 2.428297996520996, 3.062228202819824), (238.0, 338.5, 4.582575798034668, 0.0, 1.5707963705062866), (212.5416717529297, 345.3446960449219, 16.940698623657227, 6.012630939483643, 0.13033059239387512), (54.181819915771484, 338.68182373046875, 5.486020565032959, 1.5084699392318726, 3.1005523204803467), (244.5, 338.5, 2.2360680103302, 1.0, 1.5707963705062866), (40.875, 339.0, 3.2307119369506836, 1.0, 3.1415927410125732), (4.5, 339.0, 1.0, 0.0, 3.1415927410125732), (9.800000190734863, 339.9333190917969, 2.8676531314849854, 1.825410008430481, 2.986464262008667), (16.5, 339.0, 1.0, 0.0, 3.1415927410125732), (20.200000762939453, 339.3999938964844, 1.5491933822631836, 0.8944271802902222, 2.8198421001434326), (31.0, 339.29998779296875, 3.464101552963257, 0.9165151119232178, 0.0), (46.5, 339.0, 1.0, 0.0, 3.1415927410125732), (218.0, 339.0, 0.0, 0.0, 2.356194496154785), (294.5, 339.5, 3.0204479694366455, 0.9364264011383057, 0.122489333152771), (360.76470947265625, 344.058837890625, 3.5653164386749268, 1.6786692142486572, 0.2687702178955078), (268.9476623535156, 371.4903564453125, 30.55106544494629, 3.991187334060669, 1.56861412525177), (15.342857360839844, 350.1142883300781, 7.075631141662598, 4.737252712249756, 0.2264050394296646), (311.5, 344.5, 1.0, 1.0, 2.356194496154785), (32.0, 345.5, 1.0, 0.0, 1.5707963705062866), (46.40860366821289, 349.0107421875, 9.882936477661133, 4.109147548675537, 0.06696691364049911), (236.0, 348.5, 4.582575798034668, 0.0, 1.5707963705062866), (243.43478393554688, 351.8043518066406, 7.805454730987549, 2.3201303482055664, 1.6924065351486206), (353.5, 345.0, 1.0, 0.0, 3.1415927410125732), (4.0, 346.5, 1.0, 0.0, 1.5707963705062866), (210.033203125, 362.870849609375, 16.036827087402344, 11.903205871582031, 3.1290462017059326), (329.3999938964844, 354.20001220703125, 1.5491933822631836, 0.8944271802902222, 1.8925468921661377), (27.66666603088379, 354.8333435058594, 3.0057432651519775, 0.6402745246887207, 3.01129150390625), (347.0, 354.71429443359375, 1.5118578672409058, 1.3997083902359009, 3.1415927410125732), (48.0, 355.0, 0.0, 0.0, 2.356194496154785), (234.0, 358.5, 4.582575798034668, 0.0, 1.5707963705062866), (396.0135192871094, 358.92791748046875, 26.802236557006836, 3.429692029953003, 0.0005554801900871098), (5.833333492279053, 357.1666564941406, 1.5634719133377075, 1.154700517654419, 2.356194496154785), (420.0, 356.5, 1.0, 0.0, 1.5707963705062866), (429.28570556640625, 356.8571472167969, 3.3395602703094482, 0.6627020239830017, 3.0727992057800293), (257.0, 357.0, 0.0, 0.0, 2.356194496154785), (318.0, 357.5, 1.0, 0.0, 1.5707963705062866), (331.03125, 361.21875, 3.761357307434082, 3.2644875049591064, 2.275275707244873), (465.0, 357.0, 1.632993221282959, 0.0, 3.1415927410125732), (472.0, 357.0, 0.0, 0.0, 2.356194496154785), (490.5, 357.0, 2.2360680103302, 0.0, 3.1415927410125732), (49.52941131591797, 360.6470642089844, 3.883671283721924, 2.0588581562042236, 0.716254711151123), (350.34783935546875, 361.4347839355469, 4.9199748039245605, 2.420355796813965, 2.0395541191101074), (421.5, 360.5, 3.0, 1.5275251865386963, 1.1071487665176392), (358.0, 359.0, 0.0, 0.0, 2.356194496154785), (240.78787231445312, 364.69696044921875, 7.721569061279297, 1.7955548763275146, 1.2201000452041626), (296.0, 361.5, 1.7320507764816284, 1.4142135381698608, 1.5707963705062866), (478.0, 360.75, 1.4142135381698608, 0.8660253882408142, 3.1415927410125732), (282.52630615234375, 362.52630615234375, 9.875061988830566, 2.8752524852752686, 0.20441024005413055), (317.0714416503906, 362.5714416503906, 2.71681809425354, 1.7339707612991333, 1.1632500886917114), (433.8333435058594, 362.0, 1.9148541688919067, 0.9428090453147888, 0.9272952079772949), (452.0, 361.5, 1.0, 0.0, 1.5707963705062866), (361.6000061035156, 362.79998779296875, 2.0466690063476562, 0.7817580103874207, 0.08992674946784973), (294.0, 365.0, 0.0, 0.0, 2.356194496154785), (301.5, 365.0, 1.0, 0.0, 3.1415927410125732), (13.208333015441895, 370.27777099609375, 8.673247337341309, 4.257447242736816, 2.975245237350464), (232.0, 369.5, 4.582575798034668, 0.0, 1.5707963705062866), (2.5, 367.0, 1.0, 0.0, 3.1415927410125732), (36.79411697387695, 370.20587158203125, 13.951617240905762, 2.103579044342041, 0.03469857573509216), (191.5833282470703, 374.0833435058594, 4.460892200469971, 1.1739752292633057, 1.273051381111145), (45.5, 375.0, 1.6180340051651, 0.6180340051651001, 1.0172219276428223), (210.0, 375.0, 0.0, 0.0, 2.356194496154785), (229.2413787841797, 383.4827575683594, 11.773774147033691, 1.0200730562210083, 1.3749644756317139), (214.2857208251953, 385.0, 2.5555062294006348, 1.0690449476242065, 3.1415927410125732), (208.0, 385.0, 0.0, 0.0, 2.356194496154785), (199.80723571777344, 396.240966796875, 18.581249237060547, 4.572801113128662, 0.10806237906217575), (52.0, 390.0, 0.0, 0.0, 2.356194496154785), (34.0, 391.25, 1.4142135381698608, 0.8660253882408142, 3.1415927410125732), (40.625, 393.25, 2.0602023601531982, 1.2012770175933838, 2.8042221069335938), (251.85714721679688, 394.28570556640625, 3.3395602703094482, 0.6627020239830017, 1.6395896673202515), (257.5, 394.0, 2.8284270763397217, 1.0, 1.5707963705062866), (363.8333435058594, 394.8333435058594, 4.31289005279541, 1.0847845077514648, 2.026791572570801), (246.85714721679688, 394.28570556640625, 1.7910350561141968, 1.2356728315353394, 2.60974383354187), (280.0, 393.0, 0.0, 0.0, 2.356194496154785), (326.4285583496094, 393.5714416503906, 1.842853307723999, 1.411257266998291, 0.3101247549057007), (295.20001220703125, 394.79998779296875, 2.371682643890381, 0.6746264696121216, 2.9513394832611084), (307.6666564941406, 396.0, 1.838544249534607, 0.41870102286338806, 1.0793994665145874), (321.5, 395.0, 1.0, 0.0, 3.1415927410125732), (347.0, 395.5, 1.0, 0.0, 1.5707963705062866), (31.515625, 399.578125, 6.429301738739014, 3.3931822776794434, 0.0843939408659935), (226.0, 399.5, 4.582575798034668, 0.0, 1.5707963705062866), (241.1999969482422, 397.0, 1.9595917463302612, 1.5491933822631836, 3.1415927410125732), (378.0, 396.0, 0.0, 0.0, 2.356194496154785), (9.166666984558105, 398.0, 1.9148541688919067, 0.9428090453147888, 0.9272952079772949), (43.13333511352539, 398.73333740234375, 3.728624105453491, 1.319775104522705, 0.31590285897254944), (325.6666564941406, 398.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (65.28813934326172, 405.0, 7.849324703216553, 6.4693603515625, 1.442439317703247), (289.79998779296875, 401.20001220703125, 3.8199872970581055, 1.0429270267486572, 2.4758102893829346), (389.1000061035156, 400.45001220703125, 5.017458915710449, 1.6047134399414062, 0.02567523717880249), (377.0, 401.625, 3.016545295715332, 0.9153981804847717, 2.482353448867798), (14.0, 402.0, 1.632993221282959, 0.0, 1.5707963705062866), (22.5, 401.0, 1.0, 0.0, 3.1415927410125732), (280.5, 401.0, 2.2360680103302, 0.0, 3.1415927410125732), (259.20001220703125, 404.20001220703125, 2.0, 0.6928203105926514, 2.356194496154785), (315.6666564941406, 404.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (372.75, 404.75, 1.7320507764816284, 0.7071067690849304, 2.8198421001434326), (183.25286865234375, 412.7701110839844, 12.662545204162598, 4.396671772003174, 1.150771975517273), (198.27999877929688, 407.8599853515625, 7.715367794036865, 3.249784231185913, 3.138390302658081), (209.375, 406.625, 2.6609344482421875, 0.8913065195083618, 1.4193538427352905), (211.8575439453125, 471.1382141113281, 56.3292350769043, 6.48135232925415, 1.3676340579986572), (248.05555725097656, 405.4444580078125, 5.356261253356934, 0.9759311079978943, 0.03563787788152695), (264.5, 405.0, 1.0, 0.0, 3.1415927410125732), (286.8719482421875, 410.5975646972656, 21.919391632080078, 3.4390146732330322, 3.110524892807007), (6.0, 407.5, 1.0, 0.0, 1.5707963705062866), (15.125, 407.7916564941406, 7.512304782867432, 1.4127393960952759, 0.07800251245498657), (34.0, 407.0, 0.0, 0.0, 2.356194496154785), (364.3143005371094, 409.6571350097656, 3.8871781826019287, 2.8926451206207275, 0.48186013102531433), (254.3333282470703, 408.6666564941406, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (241.60000610351562, 411.6666564941406, 4.794924736022949, 1.5245505571365356, 0.18482209742069244), (334.3636474609375, 411.5454406738281, 2.583918809890747, 1.5853852033615112, 0.4095466434955597), (370.0, 410.0, 0.0, 0.0, 2.356194496154785), (208.0, 411.0, 0.0, 0.0, 2.356194496154785), (320.125, 411.875, 1.7320507764816284, 1.3693064451217651, 0.7853981852531433), (324.0, 411.5, 1.0, 0.0, 1.5707963705062866), (380.0, 411.0, 0.0, 0.0, 2.356194496154785), (205.0, 412.5, 1.632993221282959, 1.0, 3.1415927410125732), (257.0625, 412.75, 5.0270233154296875, 1.1015492677688599, 0.03900860995054245), (316.0, 412.5, 1.0, 0.0, 1.5707963705062866), (0.0, 414.5, 2.2360680103302, 0.0, 1.5707963705062866), (4.0, 413.0, 0.0, 0.0, 2.356194496154785), (8.25, 413.875, 2.2539470195770264, 1.0522466897964478, 0.6170607805252075), (13.800000190734863, 413.3999938964844, 1.5491933822631836, 0.8944271802902222, 0.32175055146217346), (330.3999938964844, 414.20001220703125, 1.5491933822631836, 0.8944271802902222, 1.8925468921661377), (373.0, 414.20001220703125, 1.9595917463302612, 1.5491933822631836, 1.5707963705062866), (382.0, 413.0, 0.0, 0.0, 2.356194496154785), (398.0, 413.0, 0.0, 0.0, 2.356194496154785), (201.10000610351562, 419.07501220703125, 9.548724174499512, 3.80254864692688, 0.06443893164396286), (266.0, 414.5, 1.0, 0.0, 1.5707963705062866), (38.0, 415.0, 0.0, 0.0, 2.356194496154785), (53.5, 415.0, 2.2360680103302, 0.0, 3.1415927410125732), (247.18182373046875, 417.6363525390625, 3.8367364406585693, 2.31204891204834, 2.435870409011841), (42.0, 416.0, 0.0, 0.0, 2.356194496154785), (230.5, 417.0, 1.632993221282959, 1.0, 1.5707963705062866), (382.0, 416.75, 1.4142135381698608, 0.8660253882408142, 3.1415927410125732), (501.5714416503906, 416.71429443359375, 2.836226224899292, 0.7803092002868652, 2.97377347946167), (34.0, 417.0, 0.0, 0.0, 2.356194496154785), (46.0, 417.0, 0.0, 0.0, 2.356194496154785), (62.658226013183594, 421.417724609375, 7.935923099517822, 5.156559467315674, 2.957583427429199), (254.3333282470703, 417.3333435058594, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (342.5, 417.0, 1.0, 0.0, 3.1415927410125732), (481.7272644042969, 418.0909118652344, 2.5983684062957764, 1.282644271850586, 2.716827630996704), (508.5, 417.0, 1.0, 0.0, 3.1415927410125732), (26.68000030517578, 421.1199951171875, 4.543673992156982, 2.3680851459503174, 2.7220959663391113), (44.0, 419.79998779296875, 1.9595917463302612, 1.5491933822631836, 1.5707963705062866), (15.714285850524902, 420.1428527832031, 1.7910350561141968, 1.2356728315353394, 2.1026451587677), (48.5, 419.0, 1.0, 0.0, 3.1415927410125732), (276.3999938964844, 420.20001220703125, 1.5491933822631836, 0.8944271802902222, 1.8925468921661377), (374.0, 419.0, 0.0, 0.0, 2.356194496154785), (418.0, 419.0, 0.0, 0.0, 2.356194496154785), (234.0, 420.5, 1.0, 0.0, 1.5707963705062866), (493.6666564941406, 420.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (266.0, 421.0, 0.0, 0.0, 2.356194496154785), (312.0, 422.0, 1.632993221282959, 0.0, 1.5707963705062866), (320.0, 421.0, 0.0, 0.0, 2.356194496154785), (502.0, 421.0, 0.0, 0.0, 2.356194496154785), (244.0, 422.5, 1.0, 0.0, 1.5707963705062866), (302.4705810546875, 423.73529052734375, 5.682929515838623, 2.18483829498291, 0.09358145296573639), (341.6666564941406, 422.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (46.0, 423.0, 0.0, 0.0, 2.356194496154785), (282.0, 424.0, 1.632993221282959, 0.0, 1.5707963705062866), (294.0, 423.5, 1.0, 0.0, 1.5707963705062866), (330.71429443359375, 423.1428527832031, 3.3395602703094482, 0.6627020239830017, 3.0727992057800293), (472.0, 423.0, 0.0, 0.0, 2.356194496154785), (15.666666984558105, 424.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (233.0, 425.9200134277344, 7.000832557678223, 1.3574758768081665, 2.935690402984619), (287.5, 424.5, 1.0, 1.0, 2.356194496154785), (323.75, 425.0, 1.4142135381698608, 0.8660253882408142, 1.5707963705062866), (9.0, 425.0, 1.632993221282959, 0.0, 3.1415927410125732), (204.60000610351562, 425.20001220703125, 2.0466690063476562, 0.7817580103874207, 3.051666021347046), (28.0, 427.0, 0.0, 0.0, 2.356194496154785), (55.5, 427.0, 1.0, 0.0, 3.1415927410125732), (320.3333435058594, 427.3333435058594, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (392.0, 427.5, 1.0, 0.0, 1.5707963705062866), (362.0, 431.875, 4.239621639251709, 2.4075798988342285, 1.7823402881622314), (14.0, 429.0, 0.0, 0.0, 2.356194496154785), (58.0, 429.0, 0.0, 0.0, 2.356194496154785), (63.5, 429.0, 1.0, 0.0, 3.1415927410125732), (295.7708435058594, 433.8958435058594, 11.167255401611328, 5.215898513793945, 2.835841655731201), (314.3333435058594, 430.9166564941406, 2.1820225715637207, 1.7606500387191772, 2.5355799198150635), (320.0, 431.5, 1.0, 0.0, 1.5707963705062866), (329.79998779296875, 431.79998779296875, 2.0, 0.6928203105926514, 2.356194496154785), (348.0, 431.0, 0.0, 0.0, 2.356194496154785), (430.0, 431.0, 0.0, 0.0, 2.356194496154785), (452.0, 431.0, 0.0, 0.0, 2.356194496154785), (464.0, 431.0, 0.0, 0.0, 2.356194496154785), (268.71429443359375, 432.8571472167969, 2.138089895248413, 1.1428571939468384, 2.8198421001434326), (306.6666564941406, 433.4444580078125, 2.138831377029419, 1.3215097188949585, 1.4652496576309204), (35.3636360168457, 434.3636474609375, 2.906628131866455, 1.4594606161117554, 0.9015426635742188), (56.5, 433.875, 1.8760998249053955, 1.3848283290863037, 2.5355799198150635), (63.64444351196289, 436.8444519042969, 4.856428623199463, 3.8974876403808594, 1.4479074478149414), (279.5, 433.5, 1.0, 1.0, 2.356194496154785), (324.0, 433.0, 0.0, 0.0, 2.356194496154785), (352.73077392578125, 434.9615478515625, 5.746734619140625, 1.815093994140625, 0.323661208152771), (390.6470642089844, 436.29412841796875, 3.5611495971679688, 1.910600185394287, 1.3197776079177856), (185.0, 434.6666564941406, 1.838544249534607, 0.41870102286338806, 2.650195837020874), (339.125, 436.2083435058594, 4.302449703216553, 2.5663490295410156, 2.431013822555542), (382.0, 435.71429443359375, 2.3983514308929443, 0.8823395371437073, 2.1537485122680664), (404.0, 435.79998779296875, 1.9595917463302612, 1.5491933822631836, 1.5707963705062866), (26.0, 435.0, 0.0, 0.0, 2.356194496154785), (40.0, 435.0, 0.0, 0.0, 2.356194496154785), (253.5, 435.3333435058594, 1.9148541688919067, 0.9428090453147888, 3.1415927410125732), (313.625, 437.5, 3.316624879837036, 1.5612494945526123, 1.919567346572876), (28.5, 436.5, 1.0, 1.0, 2.356194496154785), (45.75, 437.5833435058594, 3.9764370918273926, 1.3820888996124268, 2.7135417461395264), (318.0, 436.5, 1.0, 0.0, 1.5707963705062866), (386.0, 438.0, 2.0, 1.4142135381698608, 1.5707963705062866), (6.0, 437.0, 0.0, 0.0, 2.356194496154785), (54.58333206176758, 438.25, 2.209036111831665, 1.6859365701675415, 0.5596716403961182), (204.0, 437.0, 0.0, 0.0, 2.356194496154785), (307.6666564941406, 438.8333435058594, 2.181668996810913, 0.8275048732757568, 1.3450697660446167), (410.0, 437.0, 0.0, 0.0, 2.356194496154785), (445.6666564941406, 437.3333435058594, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (460.28570556640625, 438.28570556640625, 2.227902889251709, 1.11379075050354, 0.454876571893692), (472.5, 437.0, 1.0, 0.0, 3.1415927410125732), (481.6666564941406, 437.3333435058594, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (234.0, 438.0, 0.0, 0.0, 2.356194496154785), (322.5833435058594, 439.0833435058594, 2.522012710571289, 1.5001877546310425, 3.025660276412964), (327.0, 444.15216064453125, 6.355008602142334, 3.0856504440307617, 1.1840088367462158), (357.28570556640625, 439.5, 4.642357349395752, 2.824735641479492, 0.18299315869808197), (366.77777099609375, 440.4444580078125, 2.764883279800415, 1.070300817489624, 2.0390031337738037), (0.5, 439.0, 1.0, 0.0, 3.1415927410125732), (31.399999618530273, 439.79998779296875, 1.5491933822631836, 0.8944271802902222, 1.249045729637146), (335.6666564941406, 441.1666564941406, 2.8306636810302734, 2.0244638919830322, 1.2689216136932373), (343.79998779296875, 442.32000732421875, 4.2982001304626465, 3.393505096435547, 0.4271944463253021), (397.6666564941406, 439.3333435058594, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (281.77777099609375, 441.1111145019531, 4.31389045715332, 1.8345733880996704, 0.09615650773048401), (408.0, 440.0, 0.0, 0.0, 2.356194496154785), (193.25, 441.25, 1.7320507764816284, 0.7071067690849304, 2.8198421001434326), (299.6000061035156, 442.20001220703125, 1.5491933822631836, 0.8944271802902222, 1.249045729637146), (402.5, 441.0, 1.0, 0.0, 3.1415927410125732), (486.0, 441.0, 0.0, 0.0, 2.356194496154785), (502.0, 441.0, 0.0, 0.0, 2.356194496154785), (316.9333190917969, 442.6000061035156, 3.897742509841919, 1.2178502082824707, 0.02724572829902172), (294.0, 443.5, 1.0, 0.0, 1.5707963705062866), (351.5, 443.0, 1.0, 0.0, 3.1415927410125732), (247.6666717529297, 444.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (310.6000061035156, 445.79998779296875, 2.3015334606170654, 1.379472255706787, 2.285245895385742), (65.07691955566406, 446.6153869628906, 3.035376787185669, 1.5744192600250244, 1.94112229347229), (267.5, 445.0, 1.0, 0.0, 3.1415927410125732), (306.0, 445.0, 1.632993221282959, 0.0, 3.1415927410125732), (363.375, 446.375, 1.9848051071166992, 1.3912400007247925, 0.03120940551161766), (369.4166564941406, 447.1666564941406, 3.2609434127807617, 1.492434024810791, 1.8730461597442627), (424.5, 445.0, 1.0, 0.0, 3.1415927410125732), (244.0, 446.5, 1.0, 0.0, 1.5707963705062866), (254.0, 446.0, 0.0, 0.0, 2.356194496154785), (31.5, 447.0, 1.0, 0.0, 3.1415927410125732), (52.33333206176758, 447.3333435058594, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (304.0, 447.0, 0.0, 0.0, 2.356194496154785), (394.0, 447.5, 1.0, 0.0, 1.5707963705062866), (260.25, 449.5, 2.363572597503662, 1.0786677598953247, 1.1980866193771362), (343.6666564941406, 448.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (390.0, 448.5, 1.0, 0.0, 1.5707963705062866), (14.5, 449.0, 1.0, 0.0, 3.1415927410125732), (60.68421173095703, 453.4210510253906, 4.030118942260742, 1.9242504835128784, 1.6641217470169067), (300.0, 449.0, 0.0, 0.0, 2.356194496154785), (316.3333435058594, 451.7916564941406, 8.5687255859375, 2.731095314025879, 3.016052007675171), (340.45001220703125, 453.70001220703125, 5.850510597229004, 1.673776388168335, 1.4335259199142456), (348.75, 450.0, 2.050309181213379, 1.2434756755828857, 2.715609550476074), (356.0, 449.0, 0.0, 0.0, 2.356194496154785), (456.75, 450.125, 2.2539470195770264, 1.0522466897964478, 0.6170607805252075), (20.0, 450.5, 1.0, 0.0, 1.5707963705062866), (394.3224792480469, 477.5230407714844, 40.061378479003906, 4.337221622467041, 1.9766758680343628), (64.0, 451.0, 0.0, 0.0, 2.356194496154785), (190.0, 451.25, 1.4142135381698608, 0.8660253882408142, 3.1415927410125732), (208.0, 451.0, 0.0, 0.0, 2.356194496154785), (224.6666717529297, 452.0833435058594, 2.8841052055358887, 1.695989966392517, 0.3836748003959656), (248.0, 451.5, 1.0, 0.0, 1.5707963705062866), (418.0, 451.5, 1.0, 0.0, 1.5707963705062866), (440.5, 451.0, 1.0, 0.0, 3.1415927410125732), (16.299999237060547, 453.0, 3.3526108264923096, 1.2649110555648804, 3.1415927410125732), (31.125, 453.125, 1.7320507764816284, 1.3693064451217651, 2.356194496154785), (39.57143020629883, 452.71429443359375, 2.836226224899292, 0.7803092002868652, 2.97377347946167), (46.42856979370117, 452.8571472167969, 1.8243328332901, 1.2529041767120361, 0.19025318324565887), (53.33333206176758, 453.4166564941406, 3.8327527046203613, 1.082181692123413, 0.4414491355419159), (265.9130554199219, 454.34783935546875, 3.5588207244873047, 2.6447174549102783, 1.1431825160980225), (286.4166564941406, 452.9166564941406, 2.522012710571289, 1.5001877546310425, 3.025660276412964), (346.0, 452.5, 1.0, 0.0, 1.5707963705062866), (466.1666564941406, 452.8333435058594, 1.5634719133377075, 1.154700517654419, 0.7853981852531433), (26.0, 453.25, 1.4142135381698608, 0.8660253882408142, 3.1415927410125732), (65.5, 453.5, 1.0, 1.0, 2.356194496154785), (116.75, 454.25, 1.7320507764816284, 0.7071067690849304, 1.249045729637146), (172.6666717529297, 453.6666564941406, 2.3543941974639893, 1.252173900604248, 2.90976881980896), (178.5, 453.0, 1.0, 0.0, 3.1415927410125732), (244.39999389648438, 453.79998779296875, 1.5491933822631836, 0.8944271802902222, 1.249045729637146), (272.6363525390625, 454.5454406738281, 2.46323561668396, 1.5476692914962769, 3.023469924926758), (330.7272644042969, 454.0, 3.6742780208587646, 1.0541592836380005, 2.7507803440093994), (402.0, 453.5, 1.0, 0.0, 1.5707963705062866), (35.66666793823242, 454.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (371.8333435058594, 454.6666564941406, 2.181668996810913, 0.8275048732757568, 0.22572654485702515), (497.3636474609375, 454.9090881347656, 3.238178014755249, 1.2959524393081665, 0.10974163562059402), (230.0, 455.0, 0.0, 0.0, 2.356194496154785), (252.25, 455.75, 1.7320507764816284, 0.7071067690849304, 1.249045729637146), (302.5, 455.0, 1.0, 0.0, 3.1415927410125732), (405.0, 456.4285583496094, 2.335300922393799, 1.113663673400879, 0.4752734303474426), (430.3333435058594, 455.3333435058594, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (462.0, 455.0, 0.0, 0.0, 2.356194496154785), (504.0, 455.0, 0.0, 0.0, 2.356194496154785), (240.72727966308594, 458.0909118652344, 2.8973710536956787, 1.477752685546875, 0.5654768943786621), (291.375, 456.75, 2.0602023601531982, 1.2012770175933838, 2.8042221069335938), (39.20000076293945, 457.79998779296875, 2.0, 0.6928203105926514, 0.7853981852531433), (54.0, 457.0, 0.0, 0.0, 2.356194496154785), (180.8235321044922, 457.8823547363281, 3.8237884044647217, 1.3800703287124634, 0.17671020328998566), (246.39999389648438, 458.20001220703125, 1.5491933822631836, 0.8944271802902222, 1.8925468921661377), (274.71429443359375, 458.28570556640625, 2.3173680305480957, 0.9131739735603333, 0.5213609337806702), (284.0, 457.0, 1.632993221282959, 0.0, 3.1415927410125732), (345.0, 458.0, 1.851640224456787, 1.0690449476242065, 0.7853981852531433), (352.71429443359375, 458.28570556640625, 2.8284270763397217, 0.6998541951179504, 0.7853981852531433), (362.0, 457.0, 0.0, 0.0, 2.356194496154785), (450.25, 457.75, 1.7320507764816284, 0.7071067690849304, 1.249045729637146), (502.0, 457.0, 0.0, 0.0, 2.356194496154785), (22.799999237060547, 458.6000061035156, 1.5491933822631836, 0.8944271802902222, 2.8198421001434326), (117.66666412353516, 458.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (172.5, 458.5, 2.2360680103302, 1.0, 3.1415927410125732), (188.5, 458.5, 1.0, 1.0, 2.356194496154785), (359.0, 458.0, 0.0, 0.0, 2.356194496154785), (435.20001220703125, 458.6000061035156, 1.5491933822631836, 0.8944271802902222, 0.32175055146217346), (442.0, 458.0, 0.0, 0.0, 2.356194496154785), (506.5, 458.5, 1.0, 1.0, 2.356194496154785), (2.0, 459.0, 0.0, 0.0, 2.356194496154785), (6.0, 459.0, 0.0, 0.0, 2.356194496154785), (229.0, 459.0, 1.632993221282959, 0.0, 3.1415927410125732), (260.0, 459.0, 0.0, 0.0, 2.356194496154785), (288.0, 459.0, 0.0, 0.0, 2.356194496154785), (304.0, 459.0, 0.0, 0.0, 2.356194496154785), (336.0, 459.0, 0.0, 0.0, 2.356194496154785), (423.0, 459.0, 1.632993221282959, 0.0, 3.1415927410125732), (358.1000061035156, 465.5249938964844, 7.150650501251221, 2.534895896911621, 2.602750539779663), (65.5, 463.0, 1.0, 0.0, 3.1415927410125732), (115.95918273925781, 469.8571472167969, 7.767367362976074, 3.462350606918335, 1.694848656654358), (170.5, 463.0, 3.4156503677368164, 0.0, 3.1415927410125732), (178.63333129882812, 466.76666259765625, 4.681983947753906, 3.3847899436950684, 0.426662415266037), (188.0, 463.0, 0.0, 0.0, 2.356194496154785), (192.5, 464.0, 1.6180340051651, 0.6180340051651001, 2.124370574951172), (223.6666717529297, 463.3333435058594, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (257.0, 464.1666564941406, 1.9148541688919067, 0.9428090453147888, 0.6435011029243469), (265.6000061035156, 463.79998779296875, 1.5491933822631836, 0.8944271802902222, 1.8925468921661377), (272.5, 463.0, 2.2360680103302, 0.0, 3.1415927410125732), (298.0, 463.0, 0.0, 0.0, 2.356194496154785), (314.0, 463.0, 1.632993221282959, 0.0, 3.1415927410125732), (320.0, 463.5, 1.0, 0.0, 1.5707963705062866), (325.5, 463.0, 1.0, 0.0, 3.1415927410125732), (338.1666564941406, 463.3333435058594, 2.181668996810913, 0.8275048732757568, 0.22572654485702515), (362.0, 463.0, 0.0, 0.0, 2.356194496154785), (398.5, 464.0, 2.424830675125122, 0.6734462976455688, 0.6927241683006287), (404.0, 463.0, 0.0, 0.0, 2.356194496154785), (424.5454406738281, 464.0, 3.5694267749786377, 1.438440203666687, 3.038642168045044), (500.3333435058594, 463.3333435058594, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (329.5, 465.0, 2.2360680103302, 1.632993221282959, 3.1415927410125732), (280.5, 465.0, 1.0, 0.0, 3.1415927410125732), (364.5, 465.0, 1.0, 0.0, 3.1415927410125732), (412.0, 465.0, 0.0, 0.0, 2.356194496154785), (41.66666793823242, 466.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (64.66666412353516, 467.77777099609375, 2.37044620513916, 1.477651596069336, 0.624522864818573), (170.5, 467.9375, 3.1655523777008057, 1.7926665544509888, 3.086390733718872), (38.0, 467.0, 0.0, 0.0, 2.356194496154785), (232.0, 467.5, 1.0, 0.0, 1.5707963705062866), (236.0, 467.5, 1.0, 0.0, 1.5707963705062866), (248.0, 467.0, 0.0, 0.0, 2.356194496154785), (269.0, 467.5, 1.632993221282959, 1.0, 3.1415927410125732), (295.20001220703125, 468.20001220703125, 2.0, 0.6928203105926514, 2.356194496154785), (306.5, 467.0, 1.0, 0.0, 3.1415927410125732), (341.0, 468.6000061035156, 2.629790782928467, 0.6664835214614868, 2.2817494869232178), (490.0, 467.0, 0.0, 0.0, 2.356194496154785), (56.0, 468.5, 1.0, 0.0, 1.5707963705062866), (376.3333435058594, 468.6666564941406, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (18.75, 472.45001220703125, 5.080057621002197, 2.516547918319702, 1.960953712463379), (24.5, 469.0, 1.0, 0.0, 3.1415927410125732), (46.22222137451172, 470.3333435058594, 2.7329318523406982, 1.1056408882141113, 0.9462734460830688), (196.5, 469.0, 1.0, 0.0, 3.1415927410125732), (243.92308044433594, 474.5769348144531, 5.853890419006348, 1.5417311191558838, 1.3942232131958008), (256.29412841796875, 470.5882263183594, 3.40588116645813, 1.7044541835784912, 2.6652090549468994), (262.0, 469.0, 0.0, 0.0, 2.356194496154785), (336.0, 469.0, 0.0, 0.0, 2.356194496154785), (33.0, 470.8333435058594, 1.9148541688919067, 0.9428090453147888, 0.6435011029243469), (228.6666717529297, 470.8333435058594, 1.5275251865386963, 1.3333333730697632, 0.46364760398864746), (498.0, 470.0, 0.0, 0.0, 2.356194496154785), (40.0, 471.0, 0.0, 0.0, 2.356194496154785), (287.5, 472.0, 1.632993221282959, 1.0, 1.5707963705062866), (349.5, 471.0, 1.0, 0.0, 3.1415927410125732), (501.71429443359375, 472.8571472167969, 2.4969637393951416, 0.8863298296928406, 1.645982027053833), (507.75, 472.25, 1.7320507764816284, 0.7071067690849304, 1.249045729637146), (170.0, 472.75, 1.4142135381698608, 0.8660253882408142, 3.1415927410125732), (284.28570556640625, 473.0, 1.5118578672409058, 1.3997083902359009, 1.5707963705062866), (495.6666564941406, 472.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (8.207547187805176, 476.5660400390625, 8.482768058776855, 3.5639808177948, 2.9865691661834717), (50.0, 474.1666564941406, 3.04949951171875, 0.7677510380744934, 0.5821147561073303), (60.0, 473.0, 0.0, 0.0, 2.356194496154785), (266.0, 473.5, 1.0, 0.0, 1.5707963705062866), (273.8461608886719, 474.30767822265625, 3.624140501022339, 2.058734178543091, 0.5916865468025208), (492.3333435058594, 473.3333435058594, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (65.33333587646484, 475.4444580078125, 2.138831377029419, 1.3215097188949585, 1.6763429641723633), (180.0, 474.0, 0.0, 0.0, 2.356194496154785), (308.0, 474.0, 0.0, 0.0, 2.356194496154785), (333.75, 475.0, 1.4142135381698608, 0.8660253882408142, 1.5707963705062866), (194.1666717529297, 475.3333435058594, 2.181668996810913, 0.8275048732757568, 0.22572654485702515), (255.1666717529297, 476.0, 1.9148541688919067, 0.9428090453147888, 0.9272952079772949), (298.0, 475.0, 0.0, 0.0, 2.356194496154785), (317.1111145019531, 477.22222900390625, 2.6297597885131836, 1.4733576774597168, 1.549975037574768), (329.5, 475.0, 1.0, 0.0, 3.1415927410125732), (398.0, 475.0, 0.0, 0.0, 2.356194496154785), (402.0, 475.5, 1.0, 0.0, 1.5707963705062866), (434.0, 475.0, 0.0, 0.0, 2.356194496154785), (62.0, 477.25, 1.4142135381698608, 0.8660253882408142, 3.1415927410125732), (164.5, 477.0, 1.0, 0.0, 3.1415927410125732), (218.75, 478.6666564941406, 2.0838100910186768, 1.815660834312439, 1.9164648056030273), (250.0, 477.0, 0.0, 0.0, 2.356194496154785), (440.5, 477.0, 1.0, 0.0, 3.1415927410125732), (347.8333435058594, 479.1666564941406, 1.5634719133377075, 1.154700517654419, 2.356194496154785), (431.75, 479.0, 1.4142135381698608, 0.8660253882408142, 1.5707963705062866), (493.20001220703125, 478.6000061035156, 1.5491933822631836, 0.8944271802902222, 0.32175055146217346), (21.0, 480.0, 3.045031785964966, 1.435658574104309, 0.2940013110637665), (42.0, 479.0, 0.0, 0.0, 2.356194496154785), (193.1764678955078, 480.23529052734375, 3.009546995162964, 2.002091646194458, 0.03291938453912735), (224.0, 479.0, 0.0, 0.0, 2.356194496154785), (354.3333435058594, 479.3333435058594, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (447.5, 479.0, 1.0, 0.0, 3.1415927410125732), (64.63636016845703, 482.4545593261719, 2.933816432952881, 2.0540614128112793, 1.0543159246444702), (164.0, 480.5, 1.0, 0.0, 1.5707963705062866), (188.0, 480.5, 1.0, 0.0, 1.5707963705062866), (234.5, 481.70001220703125, 2.362816333770752, 1.362754225730896, 2.4864957332611084), (247.39999389648438, 481.8999938964844, 2.5690464973449707, 1.3856406211853027, 2.158798933029175), (256.4210510253906, 482.1052551269531, 4.539236068725586, 1.9761154651641846, 0.12538598477840424), (334.96295166015625, 481.2962951660156, 7.561940670013428, 1.7472813129425049, 3.088681936264038), (376.0, 481.0, 1.2649110555648804, 1.2649110555648804, 2.356194496154785), (111.0, 481.0, 0.0, 0.0, 2.356194496154785), (116.0, 481.0, 0.0, 0.0, 2.356194496154785), (263.0, 481.0, 0.0, 0.0, 2.356194496154785), (284.0, 481.0, 0.0, 0.0, 2.356194496154785), (310.0, 481.5, 1.632993221282959, 1.0, 3.1415927410125732), (324.0, 481.0, 0.0, 0.0, 2.356194496154785), (358.0, 481.0, 0.0, 0.0, 2.356194496154785), (118.75, 483.0, 1.4142135381698608, 0.8660253882408142, 1.5707963705062866), (184.7142791748047, 482.5714416503906, 2.0776872634887695, 0.9527355432510376, 0.14572839438915253), (321.0, 483.0, 2.0466690063476562, 0.7817580103874207, 0.5535743832588196), (434.0, 482.0, 0.0, 0.0, 2.356194496154785), (14.0, 483.0, 0.0, 0.0, 2.356194496154785), (170.3333282470703, 483.3333435058594, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (288.0, 483.0, 0.0, 0.0, 2.356194496154785), (303.5, 483.0, 1.0, 0.0, 3.1415927410125732), (345.6000061035156, 484.20001220703125, 1.5491933822631836, 0.8944271802902222, 1.249045729637146), (386.0, 484.0, 1.632993221282959, 0.0, 1.5707963705062866), (453.5, 483.0, 1.0, 0.0, 3.1415927410125732), (492.0, 483.0, 0.0, 0.0, 2.356194496154785), (37.25, 486.8333435058594, 3.5929245948791504, 1.9312641620635986, 2.257200002670288), (52.0, 485.0, 1.2649110555648804, 1.2649110555648804, 2.356194496154785), (312.3333435058594, 484.6666564941406, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (114.0, 485.0, 0.0, 0.0, 2.356194496154785), (278.0, 485.0, 0.0, 0.0, 2.356194496154785), (332.0, 485.0, 0.0, 0.0, 2.356194496154785), (354.0, 485.0, 0.0, 0.0, 2.356194496154785), (358.0, 485.0, 0.0, 0.0, 2.356194496154785), (376.0, 486.0, 1.632993221282959, 0.0, 1.5707963705062866), (484.1666564941406, 485.3333435058594, 2.7725658416748047, 0.651144802570343, 2.885814666748047), (2.0, 486.71429443359375, 1.5118578672409058, 1.3997083902359009, 3.1415927410125732), (22.0, 489.5, 5.122788429260254, 1.547061800956726, 1.9594523906707764), (350.0, 486.5, 1.0, 0.0, 1.5707963705062866), (64.0, 487.0, 0.0, 0.0, 2.356194496154785), (111.5, 487.3333435058594, 1.9148541688919067, 0.9428090453147888, 3.1415927410125732), (118.46154022216797, 488.6153869628906, 3.1322600841522217, 1.7907254695892334, 0.1756102442741394), (176.0, 488.0, 1.632993221282959, 0.0, 1.5707963705062866), (260.0, 487.0, 0.0, 0.0, 2.356194496154785), (301.0, 487.0, 1.632993221282959, 0.0, 3.1415927410125732), (372.0, 487.0, 0.0, 0.0, 2.356194496154785), (439.0, 487.0, 1.632993221282959, 0.0, 3.1415927410125732), (286.6363525390625, 489.2727355957031, 3.8838398456573486, 1.0863935947418213, 0.15711595118045807), (310.3333435058594, 488.6666564941406, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (32.0, 489.0, 0.0, 0.0, 2.356194496154785), (262.1111145019531, 491.8888854980469, 3.0470075607299805, 1.1329809427261353, 1.5769689083099365), (315.5, 490.0, 1.632993221282959, 1.0, 1.5707963705062866), (374.0, 489.0, 0.0, 0.0, 2.356194496154785), (17.0, 490.6666564941406, 1.838544249534607, 0.41870102286338806, 2.650195837020874), (303.6842041015625, 491.7894592285156, 4.148355484008789, 1.920586347579956, 2.7018847465515137), (323.0, 490.875, 2.4918975830078125, 1.108127474784851, 0.20656441152095795), (349.5, 491.75, 2.8160905838012695, 0.9053365588188171, 2.4445488452911377), (353.75, 491.0, 1.4142135381698608, 0.8660253882408142, 1.5707963705062866), (167.0, 492.0, 1.851640224456787, 1.0690449476242065, 0.7853981852531433), (232.5, 494.125, 3.9687576293945312, 0.828530490398407, 1.8705511093139648), (293.5, 491.0, 1.0, 0.0, 3.1415927410125732), (312.79998779296875, 492.20001220703125, 2.0, 0.6928203105926514, 0.7853981852531433), (384.9230651855469, 493.0, 3.169369697570801, 1.3897579908370972, 0.5718429684638977), (422.6363525390625, 492.18182373046875, 2.486326217651367, 1.372697114944458, 2.4805495738983154), (184.0, 492.0, 0.0, 0.0, 2.356194496154785), (221.8000030517578, 493.20001220703125, 2.371682643890381, 0.6746264696121216, 1.761049509048462), (30.0, 493.0, 0.0, 0.0, 2.356194496154785), (40.0, 493.5, 1.0, 0.0, 1.5707963705062866), (112.53333282470703, 495.6000061035156, 3.3109238147735596, 1.6512638330459595, 1.227020263671875), (239.6666717529297, 493.3333435058594, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (256.25, 493.9166564941406, 2.3472275733947754, 1.8831032514572144, 2.92242431640625), (295.5, 493.0, 1.0, 0.0, 3.1415927410125732), (345.3125, 495.0625, 3.428466796875, 1.685041904449463, 1.6181890964508057), (358.4210510253906, 494.1052551269531, 4.050511360168457, 1.8631893396377563, 0.1459745466709137), (436.0, 493.5, 1.0, 0.0, 1.5707963705062866), (462.0, 493.0, 0.0, 0.0, 2.356194496154785), (325.0666809082031, 496.0666809082031, 3.1383025646209717, 1.9955371618270874, 0.0030302659142762423), (337.9166564941406, 495.5833435058594, 2.7688746452331543, 1.5092308521270752, 0.36717382073402405), (485.77777099609375, 495.8888854980469, 2.4079763889312744, 1.228939414024353, 1.442720651626587), (3.5, 495.0, 1.0, 0.0, 3.1415927410125732), (44.0, 495.0, 0.0, 0.0, 2.356194496154785), (50.0, 496.0, 1.632993221282959, 0.0, 1.5707963705062866), (64.0, 495.0, 0.0, 0.0, 2.356194496154785), (106.0, 495.0, 0.0, 0.0, 2.356194496154785), (182.0, 495.0, 0.0, 0.0, 2.356194496154785), (331.20001220703125, 495.3999938964844, 1.5491933822631836, 0.8944271802902222, 2.8198421001434326), (412.0, 495.0, 0.0, 0.0, 2.356194496154785), (456.5, 495.0, 1.0, 0.0, 3.1415927410125732), (460.0, 495.0, 0.0, 0.0, 2.356194496154785), (496.3333435058594, 495.3333435058594, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (504.0, 495.0, 0.0, 0.0, 2.356194496154785), (40.33333206176758, 496.6666564941406, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (186.73333740234375, 498.73333740234375, 4.207228660583496, 1.209823727607727, 1.3306480646133423), (248.0, 497.0, 1.2649110555648804, 1.2649110555648804, 2.356194496154785), (266.0, 496.0, 0.0, 0.0, 2.356194496154785), (22.0, 497.0, 0.0, 0.0, 2.356194496154785), (217.38888549804688, 498.8333435058594, 4.064578056335449, 1.911032795906067, 0.5153529644012451), (227.0, 497.0, 0.0, 0.0, 2.356194496154785), (352.0, 497.0, 0.0, 0.0, 2.356194496154785), (367.0, 498.1666564941406, 2.5377321243286133, 0.884385883808136, 2.1860034465789795), (418.0, 497.5, 1.0, 0.0, 1.5707963705062866), (422.0, 497.0, 0.0, 0.0, 2.356194496154785), (435.625, 498.375, 2.226987838745117, 1.3840250968933105, 0.9527665972709656), (441.28570556640625, 497.1428527832031, 3.3395602703094482, 0.6627020239830017, 0.06879337131977081), (502.0, 497.0, 0.0, 0.0, 2.356194496154785), (31.375, 499.0, 3.169861316680908, 0.9431217312812805, 0.11007466167211533), (63.0, 498.5, 1.0, 0.0, 1.5707963705062866), (105.19999694824219, 499.70001220703125, 2.2706925868988037, 1.387067198753357, 1.2527586221694946), (116.0, 498.0, 0.0, 0.0, 2.356194496154785), (384.0, 498.0, 0.0, 0.0, 2.356194496154785), (0.0, 499.0, 0.0, 0.0, 2.356194496154785), (7.666666507720947, 499.3333435058594, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (44.0, 499.0, 0.0, 0.0, 2.356194496154785), (273.0, 499.0, 0.0, 0.0, 2.356194496154785), (280.875, 500.125, 1.7320507764816284, 1.3693064451217651, 0.7853981852531433), (494.0, 499.0, 0.0, 0.0, 2.356194496154785), (498.0, 499.0, 0.0, 0.0, 2.356194496154785), (165.6666717529297, 500.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (441.4285583496094, 501.0, 2.9965968132019043, 1.0690449476242065, 3.1415927410125732), (458.0, 500.5, 1.0, 0.0, 1.5707963705062866), (59.38461685180664, 503.80767822265625, 5.830495357513428, 2.260936737060547, 0.5940226912498474), (232.0, 501.0, 0.0, 0.0, 2.356194496154785), (244.0, 501.0, 0.0, 0.0, 2.356194496154785), (262.0, 501.0, 0.0, 0.0, 2.356194496154785), (299.70001220703125, 502.20001220703125, 5.720977783203125, 1.5331051349639893, 0.22328518331050873), (308.0, 501.0, 0.0, 0.0, 2.356194496154785), (330.0, 501.0, 0.0, 0.0, 2.356194496154785), (426.0, 501.0, 1.632993221282959, 0.0, 3.1415927410125732), (470.0, 501.0, 0.0, 0.0, 2.356194496154785), (349.6666564941406, 503.1666564941406, 1.5275251865386963, 1.3333333730697632, 2.677945137023926), (38.0, 504.0, 1.632993221282959, 0.0, 1.5707963705062866), (117.5, 503.0, 1.0, 0.0, 3.1415927410125732), (222.0, 503.0, 0.0, 0.0, 2.356194496154785), (236.5, 503.0, 1.0, 0.0, 3.1415927410125732), (242.92308044433594, 504.4615478515625, 3.489720106124878, 1.6243219375610352, 2.99562668800354), (260.0, 503.0, 0.0, 0.0, 2.356194496154785), (311.20001220703125, 503.20001220703125, 2.371682643890381, 0.6746264696121216, 0.19025318324565887), (320.3333435058594, 504.6666564941406, 3.354182004928589, 1.092660665512085, 1.1780972480773926), (343.5, 503.0, 1.0, 0.0, 3.1415927410125732), (420.0, 503.0, 0.0, 0.0, 2.356194496154785), (431.6000061035156, 503.20001220703125, 2.0466690063476562, 0.7817580103874207, 3.051666021347046), (450.0, 503.0, 0.0, 0.0, 2.356194496154785), (175.375, 505.5, 2.401470184326172, 1.0818690061569214, 0.6827004551887512), (254.3333282470703, 504.6666564941406, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (395.75, 505.5, 2.3401029109954834, 0.523372232913971, 1.2634648084640503), (16.0, 505.0, 0.0, 0.0, 2.356194496154785), (26.0, 505.0, 0.0, 0.0, 2.356194496154785), (189.0, 505.0, 0.0, 0.0, 2.356194496154785), (234.0, 505.0, 0.0, 0.0, 2.356194496154785), (305.0769348144531, 506.4615478515625, 2.20717191696167, 1.9470783472061157, 2.6076161861419678), (316.0, 505.0, 0.0, 0.0, 2.356194496154785), (340.3333435058594, 506.0, 1.838544249534607, 0.41870102286338806, 1.0793994665145874), (371.6666564941406, 505.3333435058594, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (468.0, 505.0, 0.0, 0.0, 2.356194496154785), (47.5428581237793, 509.22857666015625, 6.4331955909729, 2.719865560531616, 3.0719077587127686), (274.0, 506.5, 1.0, 0.0, 1.5707963705062866), (11.29411792755127, 509.29412841796875, 2.7760891914367676, 1.958719253540039, 2.0294370651245117), (17.950000762939453, 509.3999938964844, 2.6561691761016846, 2.4687578678131104, 2.371814489364624), (62.0, 507.0, 0.0, 0.0, 2.356194496154785), (117.0, 507.25, 1.4142135381698608, 0.8660253882408142, 3.1415927410125732), (180.0, 507.5, 1.0, 0.0, 1.5707963705062866), (236.0, 507.0, 0.0, 0.0, 2.356194496154785), (270.0, 507.0, 0.0, 0.0, 2.356194496154785), (282.0, 507.5, 1.0, 0.0, 1.5707963705062866), (354.0, 507.0, 0.0, 0.0, 2.356194496154785), (362.0, 507.0, 0.0, 0.0, 2.356194496154785), (484.0, 508.0, 1.632993221282959, 0.0, 1.5707963705062866), (503.5, 507.0, 1.0, 0.0, 3.1415927410125732), (27.799999237060547, 509.6000061035156, 2.0466690063476562, 0.7817580103874207, 1.4808695316314697), (59.57143020629883, 509.71429443359375, 2.0776872634887695, 0.9527355432510376, 1.4250679016113281), (263.5714416503906, 509.71429443359375, 2.0776872634887695, 0.9527355432510376, 1.4250679016113281), (332.0909118652344, 509.6363525390625, 2.173064947128296, 1.5336781740188599, 1.3077630996704102), (5.5, 510.0, 1.632993221282959, 1.0, 1.5707963705062866), (237.5, 510.0, 1.632993221282959, 1.0, 1.5707963705062866), (324.0, 510.0, 1.632993221282959, 0.0, 1.5707963705062866), (356.5, 510.0, 1.632993221282959, 1.0, 1.5707963705062866), (390.0, 510.0, 1.632993221282959, 0.0, 1.5707963705062866), (397.0, 510.0, 1.632993221282959, 0.0, 1.5707963705062866), (414.0, 510.0, 1.632993221282959, 1.632993221282959, 2.356194496154785), (479.71429443359375, 510.0, 1.5118578672409058, 1.3997083902359009, 1.5707963705062866), (368.0, 510.0, 0.0, 0.0, 2.356194496154785), (476.5, 510.0, 1.0, 0.0, 3.1415927410125732), (484.0, 511.0, 0.0, 0.0, 2.356194496154785)]}]} \ No newline at end of file diff --git a/sciapp/util/__init__.py b/sciapp/util/__init__.py new file mode 100644 index 00000000..cc56ca62 --- /dev/null +++ b/sciapp/util/__init__.py @@ -0,0 +1,2 @@ +from .surfutil import * +from .shputil import * \ No newline at end of file diff --git a/sciapp/util/shputil.py b/sciapp/util/shputil.py new file mode 100644 index 00000000..dadb5528 --- /dev/null +++ b/sciapp/util/shputil.py @@ -0,0 +1,56 @@ +import numpy as np +from ..object.shape import * + +def offset(shp, dx, dy): + if shp.dtype in {'rectangle', 'ellipse', 'circle'}: + shp.body[:2] += dx, dy + elif shp.dtype in {'rectangles', 'ellipses', 'circles'}: + shp.body[:,:2] += dx, dy + elif isinstance(shp, np.ndarray): + shp += dx, dy + elif isinstance(shp.body, list): + for i in shp.body: offset(i, dx, dy) + +def mark2shp(mark): + style = mark.copy() + style.pop('body') + keys = {'point':Point, 'points':Points, 'line':Line, 'lines':Lines, + 'polygon':Polygon, 'polygons':Polygons, 'circle':Circle, + 'circles':Circles, 'rectangle':Rectangle, 'rectangles':Rectangles, + 'ellipse':Ellipse, 'ellipses':Ellipses, 'text':Text, 'texts':Texts} + if mark['type'] in keys: return keys[mark['type']](mark['body'], **style) + if mark['type']=='layer': + return Layer([mark2shp(i) for i in mark['body']], **style) + if mark['type']=='layers': + return Layers(dict(zip(mark['body'].keys(), + [mark2shp(i) for i in mark['body'].values()])), **style) + +def json2shp(obj): + if obj['type']=='Point': + return Point(obj['coordinates']) + if obj['type']=='MultiPoint': + return Points(obj['coordinates']) + if obj['type']=='LineString': + return Line(obj['coordinates']) + if obj['type']=='MultiLineString': + return Lines(obj['coordinates']) + if obj['type']=='Polygon': + return Polygon(obj['coordinates']) + if obj['type']=='MultiPolygon': + return Polygons(obj['coordinates']) + if obj['type']=='GeometryCollection': + return Layer([json2shp(i) for i in obj['geometries']]) + +def geom2shp(obj): return json2shp(geom.mapping(obj)) + +def geom_flatten(obj, geoms=None): + geoms, root = ([], True) if geoms is None else (geoms, False) + if isinstance(obj, geom.GeometryCollection): + for i in obj: geom_flatten(i, geoms) + elif type(obj) in {geom.MultiPolygon, geom.MultiPoint, geom.MultiLineString}: + geoms.extend(list(obj)) + else: geoms.append(obj) + if root: return geom.GeometryCollection(geoms) + +def geom_union(obj): + return geom_flatten(unary_union(geom_flatten(obj))) \ No newline at end of file diff --git a/imagepy/core/myvi/util.py b/sciapp/util/surfutil.py similarity index 91% rename from imagepy/core/myvi/util.py rename to sciapp/util/surfutil.py index a70d1cfd..607b45c5 100644 --- a/imagepy/core/myvi/util.py +++ b/sciapp/util/surfutil.py @@ -1,7 +1,20 @@ from time import time import numpy as np from math import pi -from .txtmark import lib + +lib = {'0':([(0,0.5,0.5,0,0)],[(1,1,0,0,1)],0.5), + '1':([(0.25,0.25)], [(0,1)], 0.5), + '2':([(0,0.5,0.5,0,0,0.5)], [(1,1,0.5,0.5,0,0)], 0.5), + '3':([(0,0.5,0.5,0),(0,0.5)],[(1,1,0,0),(0.5,0.5)], 0.5), + '4':([(0,0,0.5),(0.5,0.5)],[(1,0.5,0.5),(1,0)],0.5), + '5':([(0.5,0,0,0.5,0.5,0)], [(1,1,0.5,0.5,0,0)], 0.5), + '6':([(0.5,0,0,0.5,0.5,0,0)], [(1,1,0.5,0.5,0,0,0.5)], 0.5), + '7':([(0,0.5,0.5)], [(1,1,0)], 0.5), + '8':([(0.5,0.5,0,0,0.5,0.5,0,0)], [(0.5,1,1,0.5,0.5,0,0,0.5)], 0.5), + '9':([(0.5,0.5,0,0,0.5,0.5,0)], [(0.5,1,1,0.5,0.5,0,0)], 0.5), + 'I':([(0,0.5),(0.25,0.25),(0,0.5)],[(1,1),(1,0),(0,0)],0.5), + 'D':([(0,0.25,0.4,0.5,0.5,0.4,0.25,0),(0.1,0.1)],[(1,1,0.9,0.75,0.25,0.1,0,0),(0,1)],0.5), + ':':([(0.2,0.3),(0.2,0.3)],[(0.75,0.75),(0.25,0.25)],0.5)} def count_ns(vts, fs): dv1 = vts[fs[:,1]] - vts[fs[:,2]] diff --git a/sciwx/__init__.py b/sciwx/__init__.py new file mode 100644 index 00000000..3322da8c --- /dev/null +++ b/sciwx/__init__.py @@ -0,0 +1,13 @@ +from sciapp import Source +import numpy as np + +import matplotlib.pyplot as plt +for i in plt.colormaps()[::-1]: + cm = plt.get_cmap(i) + if i[-2:]=='_r': continue + vs = np.linspace(0, cm.N, 256, endpoint=False) + lut = cm(vs.astype(np.int), bytes=True)[:,:3] + Source.manager('colormap').add(i, lut) +graylut = Source.manager('colormap').get('gray') +Source.manager('colormap').add('Grays', graylut) +Source.manager('colormap').remove('gray') \ No newline at end of file diff --git a/sciwx/app/__init__.py b/sciwx/app/__init__.py new file mode 100644 index 00000000..b27ca44f --- /dev/null +++ b/sciwx/app/__init__.py @@ -0,0 +1,2 @@ +from sciapp import App, Source +from .sciapp import SciApp \ No newline at end of file diff --git a/sciwx/app/sciapp.py b/sciwx/app/sciapp.py new file mode 100644 index 00000000..92178f3d --- /dev/null +++ b/sciwx/app/sciapp.py @@ -0,0 +1,222 @@ +import wx, os, sys +import time, threading + +import wx.lib.agw.aui as aui +from sciwx.widgets import MenuBar, ToolBar, ChoiceBook, ParaDialog +from sciwx.canvas import CanvasNoteBook +from sciwx.grid import GridNoteBook +from sciwx.text import MDNoteFrame, TextNoteFrame +from skimage.data import camera +from sciapp import App + +class SciApp(wx.Frame, App): + def __init__( self, parent ): + wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = 'SciApp', + size = wx.Size(-1,-1), pos = wx.DefaultPosition, + style = wx.RESIZE_BORDER|wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + App.__init__(self) + self.auimgr = aui.AuiManager() + self.auimgr.SetManagedWindow( self ) + self.SetSizeHints( wx.Size(1024,768) ) + + self.init_menu() + self.init_tool() + self.init_canvas() + self.init_table() + self.init_widgets() + self.init_text() + self.init_status() + + self.Layout() + self.auimgr.Update() + self.Fit() + self.Centre( wx.BOTH ) + + self.Bind(wx.EVT_CLOSE, self.on_close) + self.Bind(aui.EVT_AUI_PANE_CLOSE, self.on_pan_close) + + def init_status(self): + self.stapanel = stapanel = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + sizersta = wx.BoxSizer( wx.HORIZONTAL ) + self.txt_info = wx.StaticText( stapanel, wx.ID_ANY, "ImagePy v0.2", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.txt_info.Wrap( -1 ) + sizersta.Add( self.txt_info, 1, wx.ALIGN_BOTTOM|wx.BOTTOM|wx.LEFT|wx.RIGHT, 2 ) + self.pro_bar = wx.Gauge( stapanel, wx.ID_ANY, 100, wx.DefaultPosition, wx.Size( 100,15 ), wx.GA_HORIZONTAL ) + sizersta.Add( self.pro_bar, 0, wx.ALIGN_BOTTOM|wx.BOTTOM|wx.LEFT|wx.RIGHT, 2 ) + stapanel.SetSizer(sizersta) + self.auimgr.AddPane( stapanel, aui.AuiPaneInfo() .Bottom() .CaptionVisible( False ).PinButton( True ) + .PaneBorder( False ).Dock().Resizable().FloatingSize( wx.DefaultSize ).DockFixed( True ) + . MinSize(wx.Size(-1, 20)). MaxSize(wx.Size(-1, 20)).Layer( 10 ) ) + + def load_menu(self, data): + self.menubar.load(data) + + def load_tool(self, data, default=None): + for i, (name, tols) in enumerate(data[1]): + self.toolbar.add_tools(name, tols, i==0) + if not default is None: self.toolbar.add_pop('P', default) + self.toolbar.Layout() + + def load_widget(self, data): + self.widgets.load(data) + + def init_menu(self): + self.menubar = MenuBar(self) + + def init_tool(self): + sizer = wx.BoxSizer(wx.VERTICAL) + self.toolbar = ToolBar(self, True) + self.toolbar.Fit() + + self.auimgr.AddPane(self.toolbar, aui.AuiPaneInfo() .Left() .PinButton( True ) + .CaptionVisible( True ).Dock().Resizable().FloatingSize( wx.DefaultSize ).MaxSize(wx.Size( 32,-1 )) + . BottomDockable( True ).TopDockable( False ).Layer( 10 ) ) + + def init_canvas(self): + self.canvasnbwrap = wx.Panel(self) + sizer = wx.BoxSizer( wx.VERTICAL ) + self.canvasnb = CanvasNoteBook( self.canvasnbwrap) + sizer.Add( self.canvasnb, 1, wx.EXPAND |wx.ALL, 0 ) + self.canvasnbwrap.SetSizer( sizer ) + self.canvasnbwrap.Layout() + self.auimgr.AddPane( self.canvasnbwrap, aui.AuiPaneInfo() .Center() .CaptionVisible( False ).PinButton( True ).Dock() + .PaneBorder( False ).Resizable().FloatingSize( wx.DefaultSize ). BottomDockable( True ).TopDockable( False ) + .LeftDockable( True ).RightDockable( True ) ) + self.canvasnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_new_img) + self.canvasnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close_img) + + def init_table(self): + self.tablenbwrap = wx.Panel(self) + sizer = wx.BoxSizer( wx.VERTICAL ) + self.tablenb = GridNoteBook( self.tablenbwrap) + sizer.Add( self.tablenb, 1, wx.EXPAND |wx.ALL, 0 ) + self.tablenbwrap.SetSizer( sizer ) + self.tablenbwrap.Layout() + + self.auimgr.AddPane( self.tablenbwrap, aui.AuiPaneInfo() .Bottom() .CaptionVisible( True ).PinButton( True ).Dock().Hide() + .MaximizeButton( True ).Resizable().FloatingSize((800, 600)).BestSize(( 120,120 )). Caption('Tables') . + BottomDockable( True ).TopDockable( False ).LeftDockable( True ).RightDockable( True ) ) + + def init_widgets(self): + self.widgets = ChoiceBook(self) + self.auimgr.AddPane( self.widgets, aui.AuiPaneInfo() .Right().Caption('Widgets') .PinButton( True ) + .Dock().Resizable().FloatingSize( wx.DefaultSize ).MinSize( wx.Size( 266,-1 ) ).Layer( 10 ) ) + + def init_text(self): + self.mdframe = MDNoteFrame(self, 'Sci Document') + self.txtframe = TextNoteFrame(self, 'Sci Text') + + def on_pan_close(self, event): + if event.GetPane().window in [self.toolbar, self.widgets]: + event.Veto() + if hasattr(event.GetPane().window, 'close'): + event.GetPane().window.close() + + def on_new_img(self, event): + self.add_img_win(self.canvasnb.canvas()) + self.add_img(self.canvasnb.canvas().image) + + def on_close_img(self, event): + canvas = event.GetEventObject().GetPage(event.GetSelection()) + self.remove_img_win(canvas) + self.remove_img(canvas.image) + + def info(self, value): + wx.CallAfter(self.txt_info.SetLabel, value) + + def set_progress(self, value): + v = max(min(value, 100), 0) + self.pro_bar.SetValue(v) + if value==-1: + self.pro_bar.Hide() + elif not self.pro_bar.IsShown(): + self.pro_bar.Show() + self.stapanel.GetSizer().Layout() + self.pro_bar.Update() + + def on_close(self, event): + print('close') + #ConfigManager.write() + self.auimgr.UnInit() + del self.auimgr + self.Destroy() + sys.exit() + + def show_img(self, img): + canvas = self.canvasnb.add_canvas() + canvas.set_img(img) + self.add_img_win(canvas) + self.add_img(canvas.image) + + def show_tab(self, tab): + import pandas as pd + grid = self.tablenb.add_grid() + grid.set_data(pd.DataFrame([[1,1],[2,2]])) + info = self.auimgr.GetPane(self.tablenbwrap) + info.Show(True) + self.auimgr.Update() + + def show_md(self, cont, title='Document'): + page = self.mdframe.add_page() + page.set_cont(cont) + self.mdframe.Show() + + def show_txt(self, cont, title='Text'): + page = self.txtframe.add_notepad() + page.append(cont) + self.txtframe.Show() + + def alert(self, info, title='SciWx'): + dialog=wx.MessageDialog(self, info, title, wx.OK) + dialog.ShowModal() == wx.ID_OK + dialog.Destroy() + + def getpath(self, title, filt, io, name=''): + filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in filt]) + dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} + dialog = wx.FileDialog(self, title, '', name, filt, dic[io]) + rst = dialog.ShowModal() + path = dialog.GetPath() if rst == wx.ID_OK else None + dialog.Destroy() + return path + + def show_para(self, title, view, para, on_handle, on_ok=None, on_cancel=None, preview=False, modal=True): + dialog = ParaDialog(self, title) + dialog.init_view(view, para, preview, modal=True) + dialog.Bind('cancel', on_cancel) + dialog.Bind('parameter', on_handle) + return dialog.show() + +class P: + def __init__(self, name): + self.name = name + + def start(self): + print(self.name) + + def __call__(self): + return self + +data = ('menu', [ + ('File', [('Open', P('O')), + '-', + ('Close', P('C'))]), + ('Edit', [('Copy', P('C')), + ('A', [('B', P('B')), + ('C', P('C'))]), + ('Paste', P('P'))])]) + +if __name__ == '__main__': + app = wx.App(False) + frame = SciApp(None) + frame.Show() + frame.show_img(None) + frame.show_img(None) + frame.show_tab(None) + ''' + frame.show_md('abcdefg', 'md') + frame.show_md('ddddddd', 'md') + frame.show_txt('abcdefg', 'txt') + frame.show_txt('ddddddd', 'txt') + ''' + app.MainLoop() diff --git a/sciwx/canvas/__init__.py b/sciwx/canvas/__init__.py new file mode 100644 index 00000000..5e7a0026 --- /dev/null +++ b/sciwx/canvas/__init__.py @@ -0,0 +1,4 @@ +#from .canvas import Canvas +from .mcanvas import MCanvas, ICanvas, VCanvas +from .widget import CanvasFrame, CanvasNoteBook, CanvasNoteFrame +from .vwidget import VectorFrame, VectorNoteBook, VectorNoteFrame \ No newline at end of file diff --git a/imagepy/ui/canvas/boxutil.py b/sciwx/canvas/boxutil.py similarity index 64% rename from imagepy/ui/canvas/boxutil.py rename to sciwx/canvas/boxutil.py index 12338b60..0bc0f93f 100644 --- a/imagepy/ui/canvas/boxutil.py +++ b/sciwx/canvas/boxutil.py @@ -6,6 +6,12 @@ def cross(winbox, conbox): x2, y2 = two[:,2:].min(axis=0) return [x1, y1, x2, y2] +def merge(winbox, conbox): + two = np.array([winbox, conbox]) + x1, y1 = two[:,:2].min(axis=0) + x2, y2 = two[:,2:].max(axis=0) + return [x1, y1, x2, y2] + def multiply(rect, kx, ky): return rect * [kx, ky, kx, ky] @@ -41,9 +47,17 @@ def lay(winbox, conbox): layx(winbox, conbox) layy(winbox, conbox) -def mat(ori, vir, cros): - kx = (ori[2]-ori[0])/(vir[2]-vir[0]) - ky = (ori[3]-ori[1])/(vir[3]-vir[1]) - ox = (cros[1]-vir[1])*ky - oy = (cros[0]-vir[0])*kx - return (ox, oy), (kx, ky) +def like(ori, cont, cell): + kx = (cont[2]-cont[0])/(ori[2]-ori[0]) + ky = (cont[3]-cont[1])/(ori[3]-ori[1]) + ox = cont[0] - ori[0]*kx + oy = cont[1] - ori[1]*kx + return [cell[0]*kx+ox, cell[1]*ky+oy, + cell[2]*kx+ox, cell[3]*kx+oy] + +def mat(ori, con, cell, cros): + kx = (ori[2]-ori[0])/(con[2]-con[0]) + ky = (ori[3]-ori[1])/(con[3]-con[1]) + ox = (cros[1]-cell[1])*ky + oy = (cros[0]-cell[0])*kx + return (ox, oy), (kx, ky) \ No newline at end of file diff --git a/sciwx/canvas/canvas.py b/sciwx/canvas/canvas.py new file mode 100644 index 00000000..3acedc74 --- /dev/null +++ b/sciwx/canvas/canvas.py @@ -0,0 +1,307 @@ +import wx, numpy as np +from .boxutil import cross, multiply, merge, lay, mat, like +from .imutil import mix_img +from .mark import drawmark +from sciapp.object import Image, mark2shp, Layer, json2shp +from sciapp.action import Tool, DefaultTool +from time import time + +class Canvas (wx.Panel): + scales = [0.03125, 0.0625, 0.125, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4, 5, 8, 10, 15, 20, 30, 50] + + def __init__(self, parent, autofit=False, ingrade=False, up=False): + wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, + pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.TAB_TRAVERSAL ) + + self.winbox = None + self.conbox = [0,0,1,1] + self.oribox = [0,0,1,1] + + self.outbak = None + self.outimg = None + self.outrgb = None + self.outbmp = None + self.outint = None + self.buffer = None + + self.first = True + + self.images = [] + self.marks = {} + self.tool = None + + self.scaidx = 6 + self.autofit = autofit + self.ingrade = ingrade + self.up = up + self.scrbox = wx.DisplaySize() + self.bindEvents() + + def get_obj_tol(self): return self, Tool.default + + def bindEvents(self): + for event, handler in [ \ + (wx.EVT_SIZE, self.on_size), + (wx.EVT_MOUSE_EVENTS, self.on_mouse), + (wx.EVT_IDLE, self.on_idle), + (wx.EVT_PAINT, self.on_paint)]: + self.Bind(event, handler) + + def on_mouse(self, me): + px, py = me.GetX(), me.GetY() + x, y = self.to_data_coor(px, py) + obj, tol = self.get_obj_tol() + btn, tool = me.GetButton(), self.tool or tol + ld, rd, md = me.LeftIsDown(), me.RightIsDown(), me.MiddleIsDown() + if me.Moving() and not (ld or md or rd): pass + sta = [me.AltDown(), me.ControlDown(), me.ShiftDown()] + others = {'alt':sta[0], 'ctrl':sta[1], + 'shift':sta[2], 'px':px, 'py':py, 'canvas':self} + if me.ButtonDown(): + self.SetFocus() + tool.mouse_down(obj, x, y, btn, **others) + if me.ButtonUp(): + tool.mouse_up(obj, x, y, btn, **others) + if me.Moving(): + tool.mouse_move(obj, x, y, None, **others) + btn = [ld, md, rd,True] + btn = (btn.index(True) +1) %4 + wheel = np.sign(me.GetWheelRotation()) + if me.Dragging(): + tool.mouse_move(obj, x, y, btn, **others) + if wheel!=0: + tool.mouse_wheel(obj, x, y, wheel, **others) + ckey = {'arrow':1,'cross':5,'hand':6} + cursor = ckey[tool.cursor] if tool.cursor in ckey else 1 + self.SetCursor(wx.Cursor(cursor)) + + def initBuffer(self): + box = self.GetClientSize() + self.buffer = wx.Bitmap(*box) + self.winbox = [0, 0, *box] + lay(self.winbox, self.conbox) + + def fit(self): + self.update_box() + oriw = self.oribox[2]-self.oribox[0] + orih = self.oribox[3]-self.oribox[1] + if not self.autofit: a,b,c,d = self.winbox + else: + (a,b),(c,d) = (0,0), self.scrbox + c, d = c*0.9, d*0.9 + if not self.ingrade: + for i in self.scales[6::-1]: + if oriw*i20: + self.initBuffer() + if len(self.images)+len(self.marks)==0: return + self.update() + + def on_idle(self, event): + need = sum([i.dirty for i in self.images]) + need += sum([i.dirty for i in self.marks.values()]) + + if need==0: return + else: + for i in self.images: i.dirty = False + for i in self.marks.values(): i.dirty = False + return self.update() + + def on_paint(self, event): + if self.buffer is None: return + wx.BufferedPaintDC(self, self.buffer) + + def center(self, x, y, coord='win'): + if coord=='data': + x,y = self.to_panel_coor(x, y) + dx = (self.winbox[2]-self.winbox[0])/2 - x + dy = (self.winbox[3]-self.winbox[1])/2 - y + for i,j in zip((0,1,2,3),(dx,dy,dx,dy)): + self.conbox[i] += j + lay(self.winbox, self.conbox) + + def zoom(self, k, x, y, coord='win'): + if coord=='data': + x,y = self.to_panel_coor(x, y) + if self.up: y = (self.winbox[3]-self.winbox[1]) - y + box = np.array(self.conbox).reshape((2,2)) + box = (box - (x,y)) / self.scale * k + (x, y) + self.conbox = box.ravel().tolist() + if not self.autofit: return + a,b,c,d = self.conbox + if c-awin[2] or cell[2]win[3] or cell[3]sap: + idx = np.linspace(0, len(i), min(len(i), sap), False, dtype=np.uint16) + i = i[idx] + x, y = i.T[:2] + xy = np.vstack(f(x, y)).T + lst.append(np.hstack((xy-r, np.ones((len(x),2))*(r*2)))) + plst.append(xy) + isline = pts.lstyle and '-' in pts.lstyle + ispoint = pts.lstyle and 'o' in pts.lstyle + if pts.dtype in {'polygon', 'polygons'}: dc.DrawPolygonList(plst) - if isline or pts['type'] == 'lines': + if isline or pts.dtype == 'lines': for line in plst: dc.DrawLines(line) - if pts['type']=='points' or ispoint: + if ispoint: pen.SetWidth(1) brush.SetStyle(100) brush.SetColour(pen.GetColour()) dc.SetPen(pen) dc.SetBrush(brush) - dc.DrawEllipseList(lst) - pen.SetWidth(pts['lw'] if 'lw' in pts else width) - brush.SetStyle((106,100)[pts['fill']] if 'fill' in pts else style) - brush.SetColour(pts['fc'] if 'fc' in pts else fcolor) + for i in lst: dc.DrawEllipseList(i.round()) + pen.SetWidth(pts.lw or width) + brush.SetStyle((106,100)[pts.fill] if pts.fill != None else style) + brush.SetColour(pts.fcolor or fcolor) dc.SetPen(pen) dc.SetBrush(brush) @@ -124,28 +145,28 @@ def draw_circle(pts, dc, f, **key): width, color = pen.GetWidth(), pen.GetColour() fcolor, style = brush.GetColour(), brush.GetStyle() - if 'color' in pts: - pen.SetColour(pts['color']) - if 'fcolor' in pts: - brush.SetColour(pts['fcolor']) - if 'lw' in pts: - pen.SetWidth(pts['lw']) - if 'fill' in pts: - brush.SetStyle((106,100)[pts['fill']]) + if not pts.color is None: + pen.SetColour(pts.color) + if not pts.fcolor is None: + brush.SetColour(pts.fcolor) + if not pts.lw is None: + pen.SetWidth(pts.lw) + if not pts.fill is None: + brush.SetStyle((106,100)[pts.fill]) dc.SetPen(pen) dc.SetBrush(brush) - if pts['type'] == 'circle': - x, y ,r = pts['body'] + if pts.dtype == 'circle': + x, y ,r = pts.body x, y = f(x, y) dc.DrawCircle(x, y, r*key['k']) - if pts['type'] == 'circles': + if pts.dtype == 'circles': lst = [] - for x, y ,r in pts['body']: - x, y = f(x, y) - r *= key['k'] - lst.append((x-r,y-r,r*2,r*2)) + x, y, r = pts.body.T + x, y = f(x, y) + r = r * key['k'] + lst = np.vstack([x-r, y-r, r*2, r*2]).T dc.DrawEllipseList(lst) pen.SetWidth(width) @@ -167,31 +188,30 @@ def draw_ellipse(pts, dc, f, **key): width, color = pen.GetWidth(), pen.GetColour() fcolor, style = brush.GetColour(), brush.GetStyle() - if 'color' in pts: - pen.SetColour(pts['color']) - if 'fcolor' in pts: - brush.SetColour(pts['fcolor']) - if 'lw' in pts: - pen.SetWidth(pts['lw']) - if 'fill' in pts: - brush.SetStyle((106,100)[pts['fill']]) + if not pts.color is None: + pen.SetColour(pts.color) + if not pts.fcolor is None: + brush.SetColour(pts.fcolor) + if not pts.lw is None: + pen.SetWidth(pts.lw) + if not pts.fill is None: + brush.SetStyle((106,100)[pts.fill]) dc.SetPen(pen) dc.SetBrush(brush) - if pts['type'] == 'ellipse': - x, y ,l1, l2, a = pts['body'] - elp = make_ellipse(l1,l2,a) - elp = elp*key['k']+f(x,y) + if pts.dtype == 'ellipse': + x, y ,l1, l2, a = pts.body + elp = make_ellipse(l1,l2,a) + (x,y) + elp = np.vstack(f(*elp.T[:2])).T dc.DrawPolygon(elp) - if pts['type'] == 'ellipses': + if pts.dtype == 'ellipses': lst = [] - for x, y, l1, l2, a in pts['body']: - elp = make_ellipse(l1,l2,a) - lst.append(elp*key['k']+f(x,y)) + for x, y, l1, l2, a in pts.body: + elp = make_ellipse(l1,l2,a) + (x,y) + lst.append(np.vstack(f(*elp.T[:2])).T) dc.DrawPolygonList(lst) - pen.SetWidth(width) pen.SetColour(color) brush.SetColour(fcolor) @@ -204,30 +224,30 @@ def draw_rectangle(pts, dc, f, **key): width, color = pen.GetWidth(), pen.GetColour() fcolor, style = brush.GetColour(), brush.GetStyle() - if 'color' in pts: - pen.SetColour(pts['color']) - if 'fcolor' in pts: - brush.SetColour(pts['fcolor']) - if 'lw' in pts: - pen.SetWidth(pts['lw']) - if 'fill' in pts: - brush.SetStyle((106,100)[pts['fill']]) + if not pts.color is None: + pen.SetColour(pts.color) + if not pts.fcolor is None: + brush.SetColour(pts.fcolor) + if not pts.lw is None: + pen.SetWidth(pts.lw) + if not pts.fill is None: + brush.SetStyle((106,100)[pts.fill]) dc.SetPen(pen) dc.SetBrush(brush) - if pts['type'] == 'rectangle': - x, y, w, h = pts['body'] + if pts.dtype == 'rectangle': + x, y, w, h = pts.body + w, h = f(x+w, y+h) x, y = f(x, y) - w, h = w*key['k'], h*key['k'] - dc.DrawRectangle(x-w/2, y-h/2, w, h) - if pts['type'] == 'rectangles': - lst = [] - for x, y, w, h in pts['body']: - x, y = f(x, y) - w, h = w*key['k'], h*key['k'] - lst.append((x-w/2, y-h/2, w, h)) - dc.DrawRectangleList(lst) + dc.DrawRectangle(x.round(), (y).round(), + (w-x).round(), (h-y).round()) + if pts.dtype == 'rectangles': + x, y, w, h = pts.body.T + w, h = f(x+w, y+h) + x, y = f(x, y) + lst = np.vstack((x,y,w-x,h-y)).T + dc.DrawRectangleList(lst.round()) pen.SetWidth(width) pen.SetColour(color) @@ -244,37 +264,35 @@ def draw_text(pts, dc, f, **key): tcolor = dc.GetTextForeground() bcolor = dc.GetTextBackground() - if 'color' in pts: - pen.SetColour(pts['color']) - dc.SetTextForeground(pts['color']) + if not pts.color is None: + pen.SetColour(pts.color) + dc.SetTextForeground(pts.color) brush.SetColour(pen.GetColour()) brush.SetStyle(100) - if 'fcolor' in pts: - print('hahaha') - dc.SetTextBackground(pts['fcolor']) - if 'size' in pts: - font.SetPointSize(pts['size']) + if not pts.color is None: + dc.SetTextBackground(pts.fcolor) + if not pts.lw is None: + font.SetPointSize(pts.lw) dc.SetPen(pen) dc.SetBrush(brush) dc.SetFont(font) - if pts['type'] == 'text': - x, y, text = pts['body'] + if pts.dtype == 'text': + (x, y), text = pts.body, pts.txt x, y = f(x, y) dc.DrawText(text, x+3, y+3) - if not 'pt' in pts or pts['pt']: + if pts.fill: dc.DrawEllipse(x-2,y-2,4,4) - if pts['type'] == 'texts': + if pts.dtype == 'texts': tlst, clst, elst = [], [], [] - for x, y, text in pts['body']: - x, y = f(x, y) - tlst.append(text) - clst.append((x+3, y+3)) - elst.append((x-2, y-2, 4, 4)) - dc.DrawTextList(tlst, clst) - if not 'pt' in pts or pts['pt']: - dc.DrawEllipseList(elst) + x, y = pts.body.T + tlst = pts.txt + x, y = f(x, y) + r = x * 0 + 4 + dc.DrawTextList(tlst, np.vstack((x,y)).T) + if pts.fill: + dc.DrawEllipseList(np.vstack((x,y,r,r)).T) font.SetPointSize(size) pen.SetColour(color) @@ -286,30 +304,36 @@ def draw_text(pts, dc, f, **key): dc.SetTextForeground(tcolor) dc.SetTextBackground(bcolor) -draw_dic = {'points':plot, 'point':plot, 'line':plot, 'polygon':plot, 'lines':plot, 'polygons':plot, - 'circle':draw_circle, 'circles':draw_circle, 'ellipse':draw_ellipse, 'ellipses':draw_ellipse, - 'rectangle':draw_rectangle, 'rectangles':draw_rectangle, 'text':draw_text, 'texts':draw_text} +draw_dic = {'points':plot, 'point':plot, 'line':plot, + 'polygon':plot, 'lines':plot, 'polygons':plot, + 'circle':draw_circle, 'circles':draw_circle, + 'ellipse':draw_ellipse, 'ellipses':draw_ellipse, + 'rectangle':draw_rectangle, 'rectangles':draw_rectangle, + 'text':draw_text, 'texts':draw_text} -def draw(obj, dc, f, **key): draw_dic[obj['type']](obj, dc, f, **key) +def draw(obj, dc, f, **key): + if len(obj.body)==0: return + draw_dic[obj.dtype](obj, dc, f, **key) def draw_layer(pts, dc, f, **key): pen, brush = dc.GetPen(), dc.GetBrush() width, color = pen.GetWidth(), pen.GetColour() fcolor, style = brush.GetColour(), brush.GetStyle() - - if 'color' in pts: - pen.SetColour(pts['color']) - if 'fcolor' in pts: - brush.SetColour(pts['fcolor']) - if 'lw' in pts: - pen.SetWidth(pts['lw']) - if 'fill' in pts: - brush.SetStyle((106,100)[pts['fill']]) + if pts.color: + pen.SetColour(pts.color) + if pts.fcolor: + brush.SetColour(pts.fcolor) + if pts.lw != None: + pen.SetWidth(pts.lw) + if pts.fill: + brush.SetStyle((106,100)[pts.fill]) + if not pts.fill is None: + brush.SetStyle((106,100)[pts.fill]) dc.SetPen(pen) dc.SetBrush(brush) - for i in pts['body']:draw(i, dc, f, **key) + for i in pts.body:draw(i, dc, f, **key) pen.SetWidth(width) pen.SetColour(color) @@ -325,19 +349,20 @@ def draw_layers(pts, dc, f, **key): width, color = pen.GetWidth(), pen.GetColour() fcolor, style = brush.GetColour(), brush.GetStyle() - if 'color' in pts: - pen.SetColour(pts['color']) - if 'fcolor' in pts: - brush.SetColour(pts['fcolor']) - if 'lw' in pts: - pen.SetWidth(pts['lw']) - if 'fill' in pts: - brush.SetStyle((106,100)[pts['fill']]) + if pts.color: + pen.SetColour(pts.color) + if pts.fcolor: + brush.SetColour(pts.fcolor) + if pts.lw != None: + pen.SetWidth(pts.lw) + if pts.fill: + brush.SetStyle((106,100)[pts.fill]) dc.SetPen(pen) dc.SetBrush(brush) - if key['cur'] in pts['body']: - draw(pts['body'][key['cur']], dc, f, **key) + print(pts.body.keys(), key['cur']) + if key['cur'] in pts.body: + draw(pts.body[key['cur']], dc, f, **key) pen.SetWidth(width) pen.SetColour(color) @@ -348,21 +373,16 @@ def draw_layers(pts, dc, f, **key): draw_dic['layers'] = draw_layers -default_color = (255, 255, 0) -default_face = (255, 255, 255) -default_fill = True -default_lw = 1 -default_tcolor = (255, 0, 0) -default_tsize = 8 +from sciapp.object.shape import default_style def drawmark(dc, f, body, **key): pen, brush, font = dc.GetPen(), dc.GetBrush(), dc.GetFont() - pen.SetColour(default_color or (255,255,0)) - brush.SetColour(default_face or (255,255,255)) - brush.SetStyle((106,100)[default_fill or False]) - pen.SetWidth(default_lw or 1) - dc.SetTextForeground(default_tcolor or (255,0,0)) - font.SetPointSize(default_tsize or 8) + pen.SetColour(default_style['color']) + brush.SetColour(default_style['fcolor']) + brush.SetStyle((106,100)[default_style['fill']]) + pen.SetWidth(default_style['lw']) + dc.SetTextForeground(default_style['tcolor']) + font.SetPointSize(default_style['size']) dc.SetPen(pen); dc.SetBrush(brush); dc.SetFont(font); draw(body, dc, f, **key) @@ -375,4 +395,4 @@ def draw(self, dc, f, **key): if __name__ == '__main__': pass - # print(make_ellipse(0,0,2,1,0)) + # print(make_ellipse(0,0,2,1,0)) \ No newline at end of file diff --git a/sciwx/canvas/mcanvas.py b/sciwx/canvas/mcanvas.py new file mode 100644 index 00000000..6148d405 --- /dev/null +++ b/sciwx/canvas/mcanvas.py @@ -0,0 +1,261 @@ +import wx +from numpy import ndarray +import wx.lib.agw.aui as aui +from .canvas import Canvas +from sciapp.object.image import Image +from sciapp.action import Tool, ImageTool, ShapeTool + +class ICanvas(Canvas): + def __init__(self, parent, autofit=False): + Canvas.__init__(self, parent, autofit) + self.images.append(Image()) + self.images[0].back = Image() + self.Bind(wx.EVT_IDLE, self.on_idle) + + def get_obj_tol(self): + return self.image, ImageTool.default + + def set_img(self, img, b=False): + isarr = isinstance(img, ndarray) + if b and not isarr: self.images[0].back = img + if not b and not isarr: self.images[0] = img + if b and isarr: self.images[0].back.img = img + if not b and isarr: self.images[0].img = img + self.update() + + def set_log(self, log, b=False): + if b: self.back.log = log + else: self.image.log = log + + def set_rg(self, rg, b=False): + if b: self.back.rg = rg + else: self.image.rg = rg + + def set_lut(self, lut, b=False): + if b: self.back.lut = lut + else: self.image.lut = lut + + def set_cn(self, cn, b=False): + if b: self.back.cn = cn + else: self.image.cn = cn + + def set_mode(self, mode): + self.image.mode = mode + + def set_tool(self, tool): + self.tool = tool + + @property + def image(self): return self.images[0] + + @property + def back(self): return self.images[0].back + + def on_idle(self, event): + ''' + roi = None if not 'roi' in self.marks else self.marks['roi'] + mark = None if not 'mark' in self.marks else self.marks['mark'] + imark, cur = self.image.mark, self.image.cur + if not imark is None and imark.dtype=='layers': + imark = imark.body[cur] if cur in imark.body else None + + if not (roi is self.image.roi and mark is imark): + print(roi is self.image.roi, mark is imark) + self.image.dirty = True + ''' + if self.image.roi is None: + if 'roi' in self.marks: del self.marks['roi'] + else: self.marks['roi'] = self.image.roi + if self.image.mark is None: + if 'mark' in self.marks: del self.marks['mark'] + elif self.image.mark.dtype=='layers': + if self.image.cur in self.image.mark.body: + self.marks['mark'] = self.image.mark.body[self.image.cur] + elif 'mark' in self.marks: del self.marks['mark'] + else: self.marks['mark'] = self.image.mark + + Canvas.on_idle(self, event) + +class VCanvas(Canvas): + def __init__(self, parent, autofit=False, ingrade=True, up=True): + Canvas.__init__(self, parent, autofit, ingrade, up) + + def get_obj_tol(self): + return self.shape, ShapeTool.default + + def set_shp(self, shp): + self.marks['shape'] = shp + self.update() + + def set_tool(self, tool): self.tool = tool + + @property + def shape(self): + if not 'shape' in self.marks: return None + return self.marks['shape'] + +class SCanvas(wx.Panel): + def __init__(self, parent, autofit=False): + wx.Panel.__init__(self, parent) + self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) + self.SetBackgroundColour( wx.Colour( 255, 255, 255 ) ) + + sizer = wx.BoxSizer( wx.VERTICAL ) + self.lab_info = wx.StaticText( self, wx.ID_ANY, + 'information', wx.DefaultPosition, wx.DefaultSize, 0 ) + self.lab_info.Wrap( -1 ) + # self.lab_info.Hide() + sizer.Add( self.lab_info, 0, wx.ALL, 0 ) + + self.canvas = VCanvas(self, autofit = autofit) + sizer.Add( self.canvas, 1, wx.EXPAND |wx.ALL, 0 ) + self.SetSizer(sizer) + self.set_shp = self.canvas.set_shp + + self.Bind(wx.EVT_IDLE, self.on_idle) + + def on_idle(self, event): + if self.shape is None: return + if self.shape.dirty: self.update() + self.shape.dirty = False + + def update(self): + if self.shape is None: return + self.canvas.update() + if self.lab_info.GetLabel()!=self.shape.info: + self.lab_info.SetLabel(self.shape.info) + + def Fit(self): + wx.Panel.Fit(self) + self.GetParent().Fit() + + @property + def shape(self): return self.canvas.shape + + @property + def name(self): return self.canvas.shape.name + + +class MCanvas(wx.Panel): + def __init__(self, parent=None, autofit=False): + wx.Panel.__init__ ( self, parent) + + self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) + self.SetBackgroundColour( wx.Colour( 255, 255, 255 ) ) + + sizer = wx.BoxSizer( wx.VERTICAL ) + self.lab_info = wx.StaticText( self, wx.ID_ANY, + 'information', wx.DefaultPosition, wx.DefaultSize, 0 ) + self.lab_info.Wrap( -1 ) + # self.lab_info.Hide() + sizer.Add( self.lab_info, 0, wx.EXPAND, 0 ) + + self.canvas = ICanvas(self, autofit = autofit) + sizer.Add( self.canvas, 1, wx.EXPAND |wx.ALL, 0 ) + + self.sli_chan = wx.Slider( self, wx.ID_ANY, 0, 0, 100, wx.DefaultPosition, wx.DefaultSize, + wx.SL_HORIZONTAL| wx.SL_SELRANGE| wx.SL_TOP) + sizer.Add( self.sli_chan, 0, wx.ALL|wx.EXPAND, 0 ) + self.sli_chan.SetMaxSize( wx.Size( -1,18 ) ) + self.sli_chan.Hide() + + self.sli_page = wx.ScrollBar( self, wx.ID_ANY, + wx.DefaultPosition, wx.DefaultSize, wx.SB_HORIZONTAL) + self.sli_page.SetScrollbar(0,0,0,0, refresh=True) + sizer.Add( self.sli_page, 0, wx.ALL|wx.EXPAND, 0 ) + self.sli_page.Hide() + + self.SetSizer(sizer) + self.Layout() + self.sli_page.Bind(wx.EVT_SCROLL, self.on_scroll) + self.sli_chan.Bind(wx.EVT_SCROLL, self.on_scroll) + self.Bind(wx.EVT_IDLE, self.on_idle) + #self.Fit() + self.set_rg = self.canvas.set_rg + self.set_lut = self.canvas.set_rg + self.set_log = self.canvas.set_log + self.set_mode = self.canvas.set_mode + self.set_tool = self.canvas.set_tool + + self.chans, self.pages, self.cn = -1, -1, -1 + + def set_img(self, img, b=False): + self.canvas.set_img(img, b) + self.update() + + def set_cn(self, cn, b=False): + self.canvas.set_cn(cn, b) + self.update() + + @property + def image(self): return self.canvas.image + + @property + def name(self): return self.canvas.image.name + + def set_imgs(self, imgs, b=False): + if b: self.canvas.back.imgs = imgs + else: self.canvas.image.imgs = imgs + self.canvas.update_box() + self.update() + + def Fit(self): + wx.Panel.Fit(self) + self.GetParent().Fit() + + def slider(self): + slices = self.image.slices + channels = self.image.channels + if slices != self.pages: + print('set slices') + if slices==1 and self.sli_page.Shown: + self.sli_page.Hide() + if slices>1 and not self.sli_page.Shown: + self.sli_page.Show() + self.sli_page.SetScrollbar(0, 0, slices-1, 0) + self.pages = slices + if channels != self.chans or self.cn != self.image.cn: + print('set channels') + if not isinstance(self.image.cn, int) and self.sli_chan.Shown: + self.sli_chan.Hide() + if isinstance(self.image.cn, int) and channels>1: + if not self.sli_chan.Shown: self.sli_chan.Show() + self.sli_chan.SetMax(channels-1) + self.chans, self.cn = channels, self.canvas.image.cn + + def update(self): + if self.image.img is None: return + self.slider() + if self.lab_info.GetLabel()!=self.image.info: + self.lab_info.SetLabel(self.image.info) + # self.canvas.update() + self.Layout() + + def on_scroll(self, event): + self.image.cur = self.sli_page.GetThumbPosition() + self.image.dirty = True + if isinstance(self.image.cn, int): + self.image.cn = self.sli_chan.GetValue() + self.canvas.on_idle(event) + + def on_idle(self, event): + image, info = self.image, self.lab_info.GetLabel() + imgs = image.slices, image.channels, image.cn + selfs = self.pages ,self.chans, self.cn + if imgs != selfs or info!=self.image.info: + self.update() + + def __del__(self): + print('canvas panel del') + +if __name__=='__main__': + from skimage.data import camera, astronaut + from skimage.io import imread + + app = wx.App() + frame = wx.Frame(None, title='MCanvas') + mc = MCanvas(frame, autofit=False) + mc.set_img(astronaut()) + mc.set_cn(0) + frame.Show() + app.MainLoop() diff --git a/imagepy/ui/canvas/canvas.py b/sciwx/canvas/test.py similarity index 93% rename from imagepy/ui/canvas/canvas.py rename to sciwx/canvas/test.py index 00a6b696..d29f05c3 100644 --- a/imagepy/ui/canvas/canvas.py +++ b/sciwx/canvas/test.py @@ -1,7 +1,7 @@ import wx, numpy as np -from .boxutil import cross, multiply, lay, mat -from .imutil import mix_img -from .mark import drawmark +from boxutil import cross, multiply, lay, mat +from imutil import mix_img +from mark import drawmark from time import time class Canvas (wx.Panel): @@ -132,14 +132,14 @@ def move(self, dx, dy): arr = np.array(self.conbox) arr = arr.reshape((2,2))+(dx, dy) self.conbox = arr.ravel().tolist() - self.update() + #self.update() def on_size(self, event): if self.img is None: return self.initBuffer() self.update() - def on_idle(self, event):pass + def on_idle(self, event):self.update() def on_paint(self, event): if self.buffer is None: return @@ -189,7 +189,7 @@ def update(self, counter = [0,0]): dc.UnMask() counter[1] += time()-start if counter[0] == 10: - print('frame rate:',int(10/max(0.001,counter[1]))) + #print('frame rate:',int(10/max(0.001,counter[1]))) counter[0] = counter[1] = 0 def center(self, x, y, coord='win'): @@ -244,21 +244,15 @@ def __del__(self): img = camera() img = fftshift(fft2(img)) - farr = img.view(dtype=np.float64) - #a = farr.reshape((512,2,512)).transpose(0,2,1) - # farr.shape = img.shape+(-1,) - #plt.imshow(np.log(np.abs(a[:,:,1]))) - #plt.show() - + app = wx.App() frame = wx.Frame(None) canvas = Canvas(frame) - canvas.set_img(img) - # canvas.set_rg((-128, 127)) - canvas.set_rg((0,31015306)) + canvas.set_img(camera()) + #canvas.set_rg((0,31015306)) canvas.set_cn(0) - canvas.set_log(True) + #canvas.set_log(True) frame.Show(True) frame.SetSize(512,512) app.MainLoop() diff --git a/sciwx/canvas/vwidget.py b/sciwx/canvas/vwidget.py new file mode 100644 index 00000000..fb67079e --- /dev/null +++ b/sciwx/canvas/vwidget.py @@ -0,0 +1,131 @@ +import wx, wx.lib.agw.aui as aui +from .mcanvas import SCanvas as Canvas +from ..widgets import ToolBar, MenuBar + +class VectorFrame(wx.Frame): + def __init__(self, parent=None, autofit=False): + wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, + title = 'VectorFrame', + pos = wx.DefaultPosition, + size = wx.Size( 800, 600 ), + style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + sizer = wx.BoxSizer(wx.VERTICAL) + self.canvas =Canvas(self, autofit=autofit) + sizer.Add(self.canvas, 1, wx.EXPAND|wx.ALL, 0) + self.SetSizer(sizer) + self.set_shp = self.canvas.set_shp + self.Bind(wx.EVT_IDLE, self.on_idle) + self.Bind(wx.EVT_ACTIVATE, self.on_valid) + self.Bind(wx.EVT_CLOSE, self.on_close) + + def on_idle(self, event): + if self.GetTitle()!=self.canvas.shape.name: + self.SetTitle(self.canvas.shape.name) + + def set_title(self, title): self.SetTitle(title) + + def on_valid(self, event): pass + + def on_close(self, event): + event.Skip() + + def add_toolbar(self): + toolbar = ToolBar(self) + self.GetSizer().Insert(0, toolbar, 0, wx.EXPAND | wx.ALL, 0) + return toolbar + + def add_menubar(self): + menubar = MenuBar() + self.SetMenuBar(menubar) + return menubar + +class VectorNoteBook(wx.lib.agw.aui.AuiNotebook): + def __init__(self, parent): + wx.lib.agw.aui.AuiNotebook.__init__( self, parent, wx.ID_ANY, + wx.DefaultPosition, wx.DefaultSize, wx.lib.agw.aui.AUI_NB_DEFAULT_STYLE ) + self.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_valid) + self.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close) + self.Bind( wx.EVT_IDLE, self.on_idle) + self.SetArtProvider(aui.AuiSimpleTabArt()) + + def on_idle(self, event): + for i in range(self.GetPageCount()): + title = self.GetPage(i).shape.name + if self.GetPageText(i) != title: + self.SetPageText(i, title) + + def canvas(self, i=None): + if not i is None: return self.GetPage(i) + else: return self.GetCurrentPage() + + def set_background(self, img): + self.GetAuiManager().SetArtProvider(ImgArtProvider(img)) + + def add_canvas(self, vcanvas=None): + if vcanvas is None: vcanvas = Canvas(self) + self.AddPage(vcanvas, 'Vector', True, wx.NullBitmap ) + return vcanvas + + def set_title(self, panel, title): + self.SetPageText(self.GetPageIndex(panel), title) + + def on_valid(self, event): pass + + def on_close(self, event): pass + +class VectorNoteFrame(wx.Frame): + def __init__(self, parent): + wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, + title = 'VectorNoteFrame', + pos = wx.DefaultPosition, + size = wx.Size( 800, 600 ), + style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + sizer = wx.BoxSizer(wx.VERTICAL) + self.notebook = VectorNoteBook(self) + self.canvas = self.notebook.canvas + sizer.Add( self.notebook, 1, wx.EXPAND |wx.ALL, 0 ) + self.SetSizer(sizer) + self.add_canvas = self.notebook.add_canvas + self.Layout() + self.Bind(wx.EVT_CLOSE, self.on_close) + + def add_toolbar(self): + toolbar = ToolBar(self) + self.GetSizer().Insert(0, toolbar, 0, wx.EXPAND | wx.ALL, 0) + return toolbar + + def add_menubar(self): + menubar = MenuBar() + self.SetMenuBar(menubar) + return menubar + + def on_close(self, event): + while self.notebook.GetPageCount()>0: + self.notebook.DeletePage(0) + event.Skip() + +if __name__=='__main__': + from skimage.data import camera, astronaut + from skimage.io import imread + + + app = wx.App() + cf = CanvasFrame(None, autofit=False) + cf.set_imgs([astronaut(), 255-astronaut()]) + cf.set_cn(0) + cf.Show() + app.MainLoop() + + ''' + app = wx.App() + cnf = CanvasNoteFrame(None) + canvas = cnf.add_img() + canvas.set_img(camera()) + + canvas = cnf.add_img() + canvas.set_img(camera()) + canvas.set_cn(0) + + cnf.Show() + app.MainLoop() + ''' diff --git a/sciwx/canvas/widget.py b/sciwx/canvas/widget.py new file mode 100644 index 00000000..e5c80047 --- /dev/null +++ b/sciwx/canvas/widget.py @@ -0,0 +1,143 @@ +import wx, wx.lib.agw.aui as aui +from .mcanvas import MCanvas +from ..widgets import ToolBar, MenuBar +from sciapp import App + +class CanvasFrame(wx.Frame, App): + def __init__(self, parent=None, autofit=False): + App.__init__(self) + wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, + title = 'CanvasFrame', + pos = wx.DefaultPosition, + size = wx.Size( 800, 600 ), + style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + sizer = wx.BoxSizer(wx.VERTICAL) + self.canvas = MCanvas(self, autofit=autofit) + sizer.Add(self.canvas, 1, wx.EXPAND|wx.ALL, 0) + self.SetSizer(sizer) + + self.set_rg = self.canvas.set_rg + self.set_lut = self.canvas.set_rg + self.set_log = self.canvas.set_log + self.set_mode = self.canvas.set_mode + self.set_tool = self.canvas.set_tool + self.set_imgs = self.canvas.set_imgs + self.set_img = self.canvas.set_img + self.set_cn = self.canvas.set_cn + + self.Bind(wx.EVT_IDLE, self.on_idle) + self.Bind(wx.EVT_ACTIVATE, self.on_valid) + self.Bind(wx.EVT_CLOSE, self.on_close) + + def on_idle(self, event): + if self.GetTitle()!=self.canvas.image.title: + self.SetTitle(self.canvas.image.title) + + def set_title(self, ips): self.SetTitle(ips.title) + + def on_valid(self, event): pass + + def on_close(self, event): + event.Skip() + + def add_toolbar(self): + toolbar = ToolBar(self) + self.GetSizer().Insert(0, toolbar, 0, wx.EXPAND | wx.ALL, 0) + return toolbar + + def add_menubar(self): + menubar = MenuBar() + self.SetMenuBar(menubar) + return menubar + +class CanvasNoteBook(wx.lib.agw.aui.AuiNotebook): + def __init__(self, parent): + wx.lib.agw.aui.AuiNotebook.__init__( self, parent, wx.ID_ANY, + wx.DefaultPosition, wx.DefaultSize, wx.lib.agw.aui.AUI_NB_DEFAULT_STYLE ) + self.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_valid) + self.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close) + self.Bind( wx.EVT_IDLE, self.on_idle) + self.SetArtProvider(aui.AuiSimpleTabArt()) + + def on_idle(self, event): + for i in range(self.GetPageCount()): + title = self.GetPage(i).image.title + if self.GetPageText(i) != title: + self.SetPageText(i, title) + + def canvas(self, i=None): + if not i is None: return self.GetPage(i) + else: return self.GetCurrentPage() + + def set_background(self, img): + self.GetAuiManager().SetArtProvider(ImgArtProvider(img)) + + def add_canvas(self, mcanvas=None): + if mcanvas is None: mcanvas = MCanvas(self) + self.AddPage(mcanvas, 'Image', True, wx.NullBitmap ) + return mcanvas + + def set_title(self, panel, title): + self.SetPageText(self.GetPageIndex(panel), title) + + def on_valid(self, event): pass + + def on_close(self, event): pass + +class CanvasNoteFrame(wx.Frame, App): + def __init__(self, parent): + App.__init__(self) + wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, + title = 'CanvasNoteFrame', + pos = wx.DefaultPosition, + size = wx.Size( 800, 600 ), + style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + sizer = wx.BoxSizer(wx.VERTICAL) + self.notebook = CanvasNoteBook(self) + self.canvas = self.notebook.canvas + sizer.Add( self.notebook, 1, wx.EXPAND |wx.ALL, 0 ) + self.SetSizer(sizer) + self.add_canvas = self.notebook.add_canvas + self.Layout() + self.Bind(wx.EVT_CLOSE, self.on_close) + + def add_toolbar(self): + toolbar = ToolBar(self) + self.GetSizer().Insert(0, toolbar, 0, wx.EXPAND | wx.ALL, 0) + return toolbar + + def add_menubar(self): + menubar = MenuBar() + self.SetMenuBar(menubar) + return menubar + + def on_close(self, event): + while self.notebook.GetPageCount()>0: + self.notebook.DeletePage(0) + event.Skip() + +if __name__=='__main__': + from skimage.data import camera, astronaut + from skimage.io import imread + + + app = wx.App() + cf = CanvasFrame(None, autofit=False) + cf.set_imgs([astronaut(), 255-astronaut()]) + cf.set_cn(0) + cf.Show() + app.MainLoop() + + ''' + app = wx.App() + cnf = CanvasNoteFrame(None) + canvas = cnf.add_img() + canvas.set_img(camera()) + + canvas = cnf.add_img() + canvas.set_img(camera()) + canvas.set_cn(0) + + cnf.Show() + app.MainLoop() + ''' diff --git a/sciwx/demo/aipen_demo.py b/sciwx/demo/aipen_demo.py new file mode 100644 index 00000000..9cfa9a5d --- /dev/null +++ b/sciwx/demo/aipen_demo.py @@ -0,0 +1,60 @@ +import sys, wx +sys.path.append('../../') +from skimage.draw import line +from sciwx.canvas import CanvasFrame +from sciapp.action import Tool, DefaultTool + +from skimage.morphology import flood_fill, flood +from skimage.draw import line, circle +from skimage.segmentation import felzenszwalb +import numpy as np + +class AIPen(Tool): + title = 'AI Pen' + para = {'win':48, 'ms':30} + view = [(int, 'win', (28, 64), 0, 'window', 'size'), + (float, 'ms', (10, 50), 0, 'stickiness', 'pix')] + + def __init__(self): self.status = None + + def mouse_down(self, ips, x, y, btn, **key): + self.status = 'down' + self.oldp = (y, x) + ips.snapshot() + + def mouse_up(self, ips, x, y, btn, **key): self.status = None + + def mouse_move(self, ips, x, y, btn, **key): + if self.status == None: return + img, color = ips.img, (255,255,0) + rs, cs = line(*[int(round(i)) for i in self.oldp + (y, x)]) + np.clip(rs, 0, img.shape[0]-1, out=rs) + np.clip(cs, 0, img.shape[1]-1, out=cs) + color = (np.mean(color), color)[img.ndim==3] + w = self.para['win'] + + for r,c in zip(rs, cs): + sr = (max(0,r-w), min(img.shape[0], r+w)) + sc = (max(0,c-w), min(img.shape[1], c+w)) + r, c = min(r, w), min(c, w) + clip = img[slice(*sr), slice(*sc)] + if (clip[r,c] - color).sum()==0: continue + lab = felzenszwalb(clip, 1, 0, self.para['ms']) + clip[flood(lab, (r, c), connectivity=2)] = color + + self.oldp = (y, x) + ips.update() + +if __name__=='__main__': + from skimage.data import camera, astronaut + from skimage.io import imread + + app = wx.App() + cf = CanvasFrame(None, autofit=False) + cf.set_imgs([astronaut(), 255-astronaut()]) + cf.set_cn((0,1,2)) + bar = cf.add_toolbar() + bar.add_tool(DefaultTool, 'M') + bar.add_tool(AIPen, 'A') + cf.Show() + app.MainLoop() diff --git a/sciwx/demo/app_demo.py b/sciwx/demo/app_demo.py new file mode 100644 index 00000000..f0720d98 --- /dev/null +++ b/sciwx/demo/app_demo.py @@ -0,0 +1,38 @@ +import wx, sys +sys.path.append('../../') +from sciwx.app import SciApp +from sciwx.canvas import CanvasFrame +from sciapp.action import ImgAction, Tool, DefaultTool +from sciwx.plugins.curve import Curve +from sciwx.plugins.channels import Channels +from sciwx.plugins.histogram import Histogram +from sciwx.plugins.viewport import ViewPort + +from sciwx.plugins.filters import Gaussian, Undo +from sciwx.plugins.pencil import Pencil +from sciwx.plugins.io import Open, Save + +if __name__ == '__main__': + from skimage.data import camera + + app = wx.App(False) + frame = SciApp(None) + + logo = 'C:/Users/54631/Documents/projects/imagepy/imagepy/tools/Standard/magic.gif' + frame.load_menu(('menu',[('File',[('Open', Open), + ('Save', Save)]), + ('Filters', [('Gaussian', Gaussian), + ('Undo', Undo)])])) + frame.load_tool(('tools',[('standard', [('P', Pencil), + ('D', DefaultTool)]), + ('draw', [('X', Pencil), + ('X', Pencil)])]), 'draw') + frame.load_widget(('widgets', [('Histogram', [('Histogram', Histogram), + ('Curve', Curve), + ('Channels', Channels)]), + ('Navigator', [('Viewport', ViewPort)])])) + + frame.show_img(camera()) + frame.show_img(camera()) + frame.Show() + app.MainLoop() diff --git a/sciwx/demo/canvas1_demo.py b/sciwx/demo/canvas1_demo.py new file mode 100644 index 00000000..09f30381 --- /dev/null +++ b/sciwx/demo/canvas1_demo.py @@ -0,0 +1,59 @@ +import sys +sys.path.append('../../') +from skimage.data import astronaut, camera +from numpy.fft import fft2, fftshift +from sciwx.canvas import Canvas as Canvas +from sciapp.object import Image +import wx + +def gray_test(): + frame = wx.Frame(None, title='gray test') + canvas = Canvas(frame, autofit=False) + canvas.set_img(camera()) + frame.Show() + +def rgb_test(): + frame = wx.Frame(None, title='gray test') + canvas = Canvas(frame, autofit=True) + canvas.set_img(astronaut()) + canvas.set_cn((0,1,2)) + frame.Show() + +def rgb_gray_blend(): + frame = wx.Frame(None, title='blend') + canvas = Canvas(frame, autofit=True) + canvas.set_img(astronaut()) + canvas.set_cn((0,1,2)) + canvas.set_img(camera(), True) + canvas.set_cn(0, True) + canvas.set_mode(0.5) + frame.Show() + +def complex_test(): + frame = wx.Frame(None, title='blend') + canvas = Canvas(frame, autofit=True) + canvas.set_img(fftshift(fft2(camera()))) + canvas.set_rg((0,31015306)) + canvas.set_log(True) + frame.Show() + +if __name__ == '__main__': + app = wx.App() + #gray_test() + #rgb_test() + #rgb_gray_blend() + #complex_test() + frame = wx.Frame(None, title='blend') + canvas = Canvas(frame, autofit=True) + image = Image() + image.img = camera() + image.pos = (0,0) + canvas.images.append(image) + + image = Image() + image.img = astronaut() + image.pos = (100,200) + image.cn = (0,1,2) + canvas.images.append(image) + frame.Show() + app.MainLoop() diff --git a/sciwx/demo/canvas2_m_demo.py b/sciwx/demo/canvas2_m_demo.py new file mode 100644 index 00000000..efa4ef91 --- /dev/null +++ b/sciwx/demo/canvas2_m_demo.py @@ -0,0 +1,33 @@ +import sys +sys.path.append('../../') +from skimage.data import astronaut, camera +from sciwx.canvas import MCanvas +import wx + +def mcanvas_test(): + frame = wx.Frame(None, title='gray test') + canvas = MCanvas(frame, autofit=True) + canvas.set_img(astronaut()) + canvas.set_cn((0,1,2)) + frame.Show() + +def channels_test(): + frame = wx.Frame(None, title='gray test') + canvas = MCanvas(frame, autofit=True) + canvas.set_img(astronaut()) + canvas.set_cn(0) + frame.Show() + +def sequence_test(): + frame = wx.Frame(None, title='gray test') + canvas = MCanvas(frame, autofit=True) + canvas.set_imgs([astronaut(), 255-astronaut()]) + canvas.set_cn(0) + frame.Show() + +if __name__ == '__main__': + app = wx.App() + mcanvas_test() + channels_test() + sequence_test() + app.MainLoop() diff --git a/sciwx/demo/canvas3_image_obj.py b/sciwx/demo/canvas3_image_obj.py new file mode 100644 index 00000000..ef138373 --- /dev/null +++ b/sciwx/demo/canvas3_image_obj.py @@ -0,0 +1,31 @@ +import sys +sys.path.append('../../') +from skimage.data import astronaut, camera +from sciwx.canvas import Canvas, Image, MCanvas +import wx + +def image_canvas_test(): + obj = Image() + obj.img = camera() + obj.cn = 0 + + frame = wx.Frame(None, title='gray test') + canvas = Canvas(frame, autofit=True) + canvas.set_img(obj) + frame.Show() + +def image_mcanvas_test(): + obj = Image() + obj.imgs = [astronaut(), 255-astronaut()] + obj.cn = 0 + + frame = wx.Frame(None, title='gray test') + canvas = MCanvas(frame, autofit=True) + canvas.set_img(obj) + frame.Show() + +if __name__ == '__main__': + app = wx.App() + image_canvas_test() + image_mcanvas_test() + app.MainLoop() diff --git a/sciwx/demo/canvas4_tool.py b/sciwx/demo/canvas4_tool.py new file mode 100644 index 00000000..ef8e171a --- /dev/null +++ b/sciwx/demo/canvas4_tool.py @@ -0,0 +1,33 @@ +import sys +sys.path.append('../../') +from skimage.data import astronaut, camera +from sciwx.canvas import ICanvas +from sciapp.action import Tool +import wx + +class TestTool(Tool): + def __init__(self): Tool.__init__(self) + + def mouse_down(self, image, x, y, btn, **key): + print('x:%d y:%d btn:%d ctrl:%s alt:%s shift:%s'% + (x, y, btn, key['ctrl'], key['alt'], key['shift'])) + + def mouse_up(self, image, x, y, btn, **key): + pass + + def mouse_move(self, image, x, y, btn, **key): + pass + + def mouse_wheel(self, image, x, y, d, **key): + image.img[:] = image.img + d + key['canvas'].update() + + +if __name__ == '__main__': + app = wx.App() + frame = wx.Frame(None) + canvas = ICanvas(frame, autofit=True) + canvas.set_img(camera()) + canvas.set_tool(TestTool()) + frame.Show() + app.MainLoop() diff --git a/sciwx/demo/canvas5_frame_demo.py b/sciwx/demo/canvas5_frame_demo.py new file mode 100644 index 00000000..5d41b812 --- /dev/null +++ b/sciwx/demo/canvas5_frame_demo.py @@ -0,0 +1,26 @@ +import sys +sys.path.append('../../') + +from skimage.data import astronaut, camera +from sciwx.canvas import CanvasFrame, CanvasNoteFrame +import wx + +def canvas_frame_test(): + cf = CanvasFrame(None, autofit=True) + cf.set_imgs([camera(), 255-camera()]) + cf.Show() + +def canvas_note_test(): + cnf = CanvasNoteFrame(None) + cv1 = cnf.add_canvas() + cv1.set_img(camera()) + cv2 = cnf.add_canvas() + cv2.set_img(astronaut()) + cv2.set_cn((2,1,0)) + cnf.Show() + +if __name__ == '__main__': + app = wx.App() + canvas_frame_test() + canvas_note_test() + app.MainLoop() diff --git a/sciwx/demo/canvas6_frame_toolbar.py b/sciwx/demo/canvas6_frame_toolbar.py new file mode 100644 index 00000000..9ef2dd95 --- /dev/null +++ b/sciwx/demo/canvas6_frame_toolbar.py @@ -0,0 +1,45 @@ +import sys, wx +sys.path.append('../../') +from skimage.draw import line +from sciwx.canvas import CanvasFrame +from sciapp.action import Tool, ImageTool + +class Pencil(ImageTool): + title = 'Pencil' + + def __init__(self): + self.status = False + self.oldp = (0,0) + + def mouse_down(self, ips, x, y, btn, **key): + self.status = True + self.oldp = (y, x) + + def mouse_up(self, ips, x, y, btn, **key): + self.status = False + + def mouse_move(self, ips, x, y, btn, **key): + if not self.status:return + se = self.oldp + (y,x) + rs,cs = line(*[int(i) for i in se]) + rs.clip(0, ips.shape[1], out=rs) + cs.clip(0, ips.shape[0], out=cs) + ips.img[rs,cs] = (255, 0, 0) + self.oldp = (y, x) + key['canvas'].update() + + def mouse_wheel(self, ips, x, y, d, **key):pass + +if __name__=='__main__': + from skimage.data import camera, astronaut + from skimage.io import imread + + app = wx.App() + cf = CanvasFrame(None, autofit=False) + cf.set_imgs([astronaut(), 255-astronaut()]) + cf.set_cn((0,1,2)) + bar = cf.add_toolbar() + bar.add_tool('M', ImageTool) + bar.add_tool('P', Pencil) + cf.Show() + app.MainLoop() diff --git a/sciwx/demo/canvas7_frame_menu.py b/sciwx/demo/canvas7_frame_menu.py new file mode 100644 index 00000000..db860247 --- /dev/null +++ b/sciwx/demo/canvas7_frame_menu.py @@ -0,0 +1,35 @@ +import sys, wx +sys.path.append('../../') +from scipy.ndimage import gaussian_filter +from sciwx.canvas import CanvasFrame +from sciwx.event import ImgEvent + +class Gaussian(ImgEvent): + title = 'Gaussian' + note = ['auto_snap', 'preview'] + para = {'sigma':2} + view = [(float, 'sigma', (0, 30), 1, 'sigma', 'pix')] + + def run(self, ips, img, snap, para): + gaussian_filter(snap, para['sigma'], output=img) + +class Undo(ImgEvent): + title = 'Undo' + def run(self, ips, img, snap, para): + print(ips.img.mean(), ips.snap.mean()) + ips.swap() + +if __name__=='__main__': + from skimage.data import camera, astronaut + from skimage.io import imread + + app = wx.App() + cf = CanvasFrame(None, autofit=False) + cf.set_imgs([camera(), 255-camera()]) + cf.set_cn(0) + bar = cf.add_menubar() + bar.load(('menu',[('Filter',[('Gaussian', Gaussian), + ('Unto', Undo)]), + ])) + cf.Show() + app.MainLoop() diff --git a/sciwx/demo/canvas7_frame_menu_tool.py b/sciwx/demo/canvas7_frame_menu_tool.py new file mode 100644 index 00000000..fe6d6dd6 --- /dev/null +++ b/sciwx/demo/canvas7_frame_menu_tool.py @@ -0,0 +1,63 @@ +import sys, wx +sys.path.append('../../') +from scipy.ndimage import gaussian_filter +from skimage.draw import line +from sciwx.canvas import CanvasFrame +from sciwx.event import ImgEvent, Tool, DefaultTool + +class Gaussian(ImgEvent): + title = 'Gaussian' + note = ['auto_snap', 'preview'] + para = {'sigma':2} + view = [(float, 'sigma', (0, 30), 1, 'sigma', 'pix')] + + def run(self, ips, img, snap, para): + gaussian_filter(snap, para['sigma'], output=img) + +class Undo(ImgEvent): + title = 'Undo' + def run(self, ips, img, snap, para): ips.swap() + +class Pencil(Tool): + title = 'Pencil' + def __init__(self): + self.status = False + self.oldp = (0,0) + + def mouse_down(self, ips, x, y, btn, **key): + self.status = True + self.oldp = (y, x) + ips.snapshot() + + def mouse_up(self, ips, x, y, btn, **key): + self.status = False + + def mouse_move(self, ips, x, y, btn, **key): + if not self.status:return + se = self.oldp + (y,x) + rs,cs = line(*[int(i) for i in se]) + rs.clip(0, ips.shape[1], out=rs) + cs.clip(0, ips.shape[0], out=cs) + ips.img[rs,cs] = 255 + self.oldp = (y, x) + key['canvas'].update() + + def mouse_wheel(self, ips, x, y, d, **key):pass + +if __name__=='__main__': + from skimage.data import camera, astronaut + from skimage.io import imread + + app = wx.App() + cf = CanvasFrame(None, autofit=False) + cf.set_imgs([camera(), 255-camera()]) + cf.set_cn(0) + bar = cf.add_menubar() + bar.load(('menu',[('Filter',[('Gaussian', Gaussian), + ('Unto', Undo)]),])) + + bar = cf.add_toolbar() + bar.add_tool(DefaultTool, 'M') + bar.add_tool(Pencil, 'P') + cf.Show() + app.MainLoop() diff --git a/sciwx/demo/canvas9_image_roi.py b/sciwx/demo/canvas9_image_roi.py new file mode 100644 index 00000000..f3094295 --- /dev/null +++ b/sciwx/demo/canvas9_image_roi.py @@ -0,0 +1,17 @@ +import sys +sys.path.append('../../') +from skimage.data import astronaut, camera +from sciwx.canvas import ICanvas +from sciapp.action import Tool +from sciapp.object import ROI, Line +import wx + +if __name__ == '__main__': + app = wx.App() + frame = wx.Frame(None) + canvas = ICanvas(frame, autofit=True) + canvas.set_img(camera()) + roi = ROI([Line([(0,0),(100,100),(300,500)])]) + canvas.image.roi = roi + frame.Show() + app.MainLoop() \ No newline at end of file diff --git a/sciwx/demo/dialog_demo.py b/sciwx/demo/dialog_demo.py new file mode 100644 index 00000000..bb5ce9b4 --- /dev/null +++ b/sciwx/demo/dialog_demo.py @@ -0,0 +1,24 @@ +import sys, wx +sys.path.append('../../') +from sciwx.widgets import ParaDialog + +if __name__ == '__main__': + para = {'name':'yxdragon', 'age':10, 'h':1.72, 'w':70, 'sport':True, 'sys':'Mac', 'lan':['C/C++', 'Python'], 'c':(255,0,0)} + + view = [('lab', 'lab', 'This is a questionnaire'), + (str, 'name', 'name', 'please'), + (int, 'age', (0,150), 0, 'age', 'years old'), + (float, 'h', (0.3, 2.5), 2, 'height', 'm'), + ('slide', 'w', (1, 150), 0, 'weight','kg'), + (bool, 'sport', 'do you like sport'), + (list, 'sys', ['Windows','Mac','Linux'], str, 'favourite', 'system'), + ('chos', 'lan', ['C/C++','Java','Python'], 'lanuage you like(multi)'), + ('color', 'c', 'which', 'you like')] + + app = wx.App() + pd = ParaDialog(None, 'Test') + pd.init_view(view, para, preview=True, modal=False) + pd.pack() + pd.ShowModal() + print(para) + app.MainLoop() diff --git a/sciwx/demo/grid_demo.py b/sciwx/demo/grid_demo.py new file mode 100644 index 00000000..0605cc63 --- /dev/null +++ b/sciwx/demo/grid_demo.py @@ -0,0 +1,70 @@ +import sys, wx +sys.path.append('../../') +import pandas as pd +import numpy as np + +from sciwx.grid import Grid +from sciapp.object import Table +from sciwx.grid import GridFrame, GridNoteBook, GridNoteFrame + +def grid_test(): + df = pd.DataFrame(np.random.randn(100,5)) + frame = wx.Frame(None, title='Grid') + grid = Grid(frame) + grid.set_data(df) + frame.Show() + +def grid_style_test(): + df = pd.DataFrame(np.random.randn(100,5)) + frame = wx.Frame(None, title='Grid') + grid = Grid(frame) + grid.set_data(df) + grid.set_style(1, tc=(255,0,0), round=5) + grid.set_style(2, lc=(255,0,255), ln='both') + frame.Show() + +def grid_frame_test(): + df = pd.DataFrame(np.random.randn(100,5)) + cf = GridFrame(None) + cf.set_data(df) + cf.Show() + +def grid_note_book(): + df = pd.DataFrame(np.random.randn(100,5)) + frame = wx.Frame(None, title='GridNoteBook') + gnb = GridNoteBook(frame) + grid1 = gnb.add_grid() + grid1.set_data(df) + grid2 = gnb.add_grid() + grid2.set_data(df) + frame.Show() + +def grid_note_frame(): + df = pd.DataFrame(np.random.randn(6,4)) + gnf = GridNoteFrame(None) + grid1 = gnf.add_grid() + grid1.set_data(df) + grid2 = gnf.add_grid() + grid2.set_data(df) + gnf.Show() + +def table_obj_test(): + df = pd.DataFrame(np.random.randn(100,5)) + table = Table() + table.data = df + table.name = 'Table Object' + table.set_style(1, tc=(255,0,0), round=5) + table.set_style(2, lc=(255,0,255), ln='both') + cf = GridFrame(None) + cf.set_data(table) + cf.Show() + +if __name__=='__main__': + app = wx.App() + grid_test() + #grid_style_test() + #grid_frame_test() + #grid_note_book() + #grid_note_frame() + #table_obj_test() + app.MainLoop() diff --git a/sciwx/demo/markdown_demo.py b/sciwx/demo/markdown_demo.py new file mode 100644 index 00000000..501b748b --- /dev/null +++ b/sciwx/demo/markdown_demo.py @@ -0,0 +1,46 @@ +import sys, wx +sys.path.append('../../') + +from sciwx.text import MDPad, MDNoteBook, MDFrame, MDNoteFrame + +mdstr ='''## Markdown Test +### Title +1. paragraph 1 +2. paragraph 2 +''' + +def md_pad_test(): + frame = wx.Frame(None, title='Text Pad') + mdpad = MDPad(frame) + mdpad.set_cont(mdstr) + frame.Show() + +def md_frame_test(): + mdframe = MDFrame(None) + mdframe.set_cont(mdstr) + mdframe.Show() + +def md_note_book(): + frame = wx.Frame(None, title='Text Note Book') + mnb = MDNoteBook(frame) + md1 = mnb.add_page() + md1.set_cont(mdstr) + md2 = mnb.add_page() + md2.set_cont(mdstr) + frame.Show() + +def md_note_frame(): + mnf = MDNoteFrame(None) + md1 = mnf.add_page() + md1.set_cont(mdstr) + md2 = mnf.add_page() + md2.set_cont(mdstr) + mnf.Show() + +if __name__ == '__main__': + app = wx.App() + md_pad_test() + md_frame_test() + md_note_book() + md_note_frame() + app.MainLoop() diff --git a/sciwx/demo/mesh1_demo.py b/sciwx/demo/mesh1_demo.py new file mode 100644 index 00000000..f8a4e168 --- /dev/null +++ b/sciwx/demo/mesh1_demo.py @@ -0,0 +1,50 @@ +import sys, wx +sys.path.append('../../') + +from sciapp.object import Surface, MarkText, MeshSet +from sciwx.mesh import Canvas3DFrame, Canvas3DNoteBook, Canvas3DNoteFrame +from sciapp.util import surfutil +vts, fs, ns, cs = surfutil.build_ball((100,100,100),50, (1,0,0)) + +def canvas3d_test(): + frame = wx.Frame(None, title='Canvas3D') + canvas3d = Canvas3D(frame) + canvas3d.add_surf('ball', vts, fs, ns, cs) + frame.Show() + +def mcanvas3d_test(): + frame = wx.Frame(None, title='MCanvas3D') + canvas3d = MCanvas3D(frame) + canvas3d.add_surf('ball', vts, fs, ns, cs) + frame.Show() + +def canvas3d_frame_test(): + cnf = Canvas3DFrame(None) + cnf.add_surf('ball', vts, fs, ns, cs) + cnf.Show() + +def canvas3d_note_book(): + frame = wx.Frame(None, title='Canvas3D NoteBook') + cnb = Canvas3DNoteBook(frame) + canvas1 = cnb.add_canvas() + canvas1.add_surf('ball', vts, fs, ns, cs) + canvas2 = cnb.add_canvas() + canvas2.add_surf('ball', vts, fs, ns, cs, mode='grid') + frame.Show() + +def canvas3d_note_frame(): + cnf = Canvas3DNoteFrame(None) + canvas1 = cnf.add_canvas() + ball = Surface(vts, fs, ns, cs) + canvas1.add_surf('ball', ball) + canvas2 = cnf.add_canvas() + ball = Surface(vts, fs, ns, cs) + meshset = MeshSet('ABC', {'ball':ball}) + ball.mode = 'grid' + canvas2.set_mesh(meshset) + cnf.Show() + +if __name__ == '__main__': + app = wx.App() + canvas3d_note_frame() + app.MainLoop() diff --git a/sciwx/demo/mesh2_mesh_demo.py b/sciwx/demo/mesh2_mesh_demo.py new file mode 100644 index 00000000..564f2e7c --- /dev/null +++ b/sciwx/demo/mesh2_mesh_demo.py @@ -0,0 +1,33 @@ +import sys, wx +sys.path.append('../../') + +from sciwx.mesh import Canvas3D, MCanvas3D, MeshSet, geoutil +from sciwx.mesh import Canvas3DFrame, Canvas3DNoteBook, Canvas3DNoteFrame + +vts, fs, ns, cs = geoutil.build_ball((100,100,100),50, (1,0,0)) + +def add_with_para(): + cnf = Canvas3DFrame(None) + cnf.add_surf('gridball', vts, fs, ns, cs, mode='grid') + cnf.Show() + +def mesh_obj_test(): + cnf = Canvas3DFrame(None) + meshes = MeshSet() + vts, fs, ns, cs = geoutil.build_ball((100,100,100),50, (1,0,0)) + redball = meshes.add_surf('redball', vts, fs, ns, cs) + vts, fs, ns, cs = geoutil.build_ball((300,100,100),50, (1,1,0)) + yellowball = meshes.add_surf('yellowball', vts, fs, ns, cs) + yellowball.mode = 'grid' + vts, fs, ns, cs = geoutil.build_ball((300,-300,100),50, (0,1,0)) + hideball = meshes.add_surf('hideball', vts, fs, ns, cs) + hideball.visible = False + meshes.background = (0, 0, 0.3) + cnf.set_mesh(meshes) + cnf.Show() + +if __name__ == '__main__': + app = wx.App() + add_with_para() + mesh_obj_test() + app.MainLoop() diff --git a/sciwx/demo/mesh3_geoutil.py b/sciwx/demo/mesh3_geoutil.py new file mode 100644 index 00000000..16d3163a --- /dev/null +++ b/sciwx/demo/mesh3_geoutil.py @@ -0,0 +1,160 @@ +import sys, wx +sys.path.append('../../') + +from sciwx.mesh import Canvas3D, MCanvas3D +from sciapp.util import surfutil +from sciapp.object import Surface, MarkText +from sciwx.mesh import Canvas3DFrame, Canvas3DNoteBook, Canvas3DNoteFrame +import sys, wx + +import scipy.ndimage as ndimg +from skimage.data import moon, camera +import numpy as np + +def dem_test(): + cnf = Canvas3DFrame(None) + vts, fs, ns, cs = geoutil.build_surf2d(moon(), ds=1, k=0.3, sigma=2) + cnf.add_surf('dem', vts, fs, ns, cs) + cnf.Show() + +def ball_test(): + cnf = Canvas3DFrame(None) + vts, fs, ns, cs = geoutil.build_ball((100,100,100),50, (1,0,0)) + cnf.add_surf('ball', vts, fs, ns, cs) + cnf.Show() + +def random_ball_test(): + cnf = Canvas3DFrame(None) + os = np.random.rand(30).reshape((-1,3)) + rs = np.random.rand(10)/5 + cs = (np.random.rand(10)*255).astype(np.uint8) + cs = geoutil.linear_color('jet')[cs]/255 + vts, fs, ns, cs = geoutil.build_balls(os, rs, cs) + cnf.add_surf('ball', vts, fs, ns, cs) + cnf.Show() + +def line_test(): + cnf = Canvas3DFrame(None) + vts = np.array([(0,0,0),(1,1,0),(2,1,0),(1,0,0)], dtype=np.float32) + fs = np.array([(0,1,2),(1,2,3)], dtype=np.uint32) + ns = np.ones((4,3), dtype=np.float32) + + n_mer, n_long = 6, 11 + pi = np.pi + dphi = pi / 1000.0 + phi = np.arange(0.0, 2 * pi + 0.5 * dphi, dphi) + mu = phi * n_mer + x = np.cos(mu) * (1 + np.cos(n_long * mu / n_mer) * 0.5) + y = np.sin(mu) * (1 + np.cos(n_long * mu / n_mer) * 0.5) + z = np.sin(n_long * mu / n_mer) * 0.5 + + vts, fs, ns, cs = geoutil.build_line(x, y, z, (1, 0, 0)) + cs[:] = geoutil.auto_lookup(vts[:,2], geoutil.linear_color('jet'))/255 + cnf.add_surf('ball', vts, fs, ns, cs, mode='grid') + cnf.Show() + +def mesh_test(): + cnf = Canvas3DFrame(None) + dphi, dtheta = np.pi/16.0, np.pi/16.0 + [phi,theta] = np.mgrid[0:np.pi+dphi*1.5:dphi,0:2*np.pi+dtheta*1.5:dtheta] + m0 = 4; m1 = 3; m2 = 2; m3 = 3; m4 = 6; m5 = 2; m6 = 6; m7 = 4; + r = np.sin(m0*phi)**m1 + np.cos(m2*phi)**m3 + np.sin(m4*theta)**m5 + np.cos(m6*theta)**m7 + x = r*np.sin(phi)*np.cos(theta) + y = r*np.cos(phi) + z = r*np.sin(phi)*np.sin(theta) + vts, fs, ns, cs = geoutil.build_mesh(x, y, z) + cs[:] = geoutil.auto_lookup(vts[:,2], geoutil.linear_color('jet'))/255 + cnf.add_surf('ball', vts, fs, ns, cs) + cnf.Show() + + +def ball_ring_test(): + cnf = Canvas3DFrame(None) + os = np.random.rand(30).reshape((-1,3)) + rs = np.random.rand(10)/7 + cs = (np.random.rand(10)*255).astype(np.uint8) + cs = geoutil.linear_color('jet')[cs]/255 + + vts_b, fs_b, ns_b, cs_b = geoutil.build_balls(list(os), list(rs), list(cs)) + vts_l, fs_l, ns_l, cs_l = geoutil.build_line(os[:,0], os[:,1], os[:,2], list(cs)) + vts_c, fs_c, ns_c, cs_c = geoutil.build_cube((0,0,0), (1,1,1)) + cnf.add_surf('balls', vts_b, fs_b, ns_b, cs_b) + cnf.add_surf('line', vts_l, fs_l, ns_l, cs_l, mode='grid') + cnf.add_surf('box', vts_c, fs_c, ns_c, cs_c, mode='grid') + cnf.Show() + +def balls_mark_rest(): + cnf = Canvas3DFrame(None) + os = np.random.rand(30).reshape((-1,3)) + rs = np.random.rand(10)/7+0.01 + cs = (np.random.rand(10)*255).astype(np.uint8) + cs = surfutil.linear_color('jet')[cs]/255 + + vts_b, fs_b, ns_b, cs_b = surfutil.build_balls(os, rs, cs) + cont = ['ID:%s'%i for i in range(10)] + vtss, fss, pps, h, color = surfutil.build_marks(cont, os, rs, 0.05, (1,1,1)) + cnf.add_surf('balls', Surface(vts_b, fs_b, ns_b, cs_b)) + cnf.add_surf('line', MarkText(vtss, fss, pps, h, color)) + cnf.Show() + +def surface2d_test(): + cnf = Canvas3DFrame(None) + x, y = np.ogrid[-2:2:20j, -2:2:20j] + z = x * np.exp( - x**2 - y**2) + vts, fs, ns, cs = geoutil.build_surf2d(z, ds=1, k=20, sigma=2) + cs[:] = geoutil.auto_lookup(vts[:,2], geoutil.linear_color('jet'))/255 + cnf.add_surf('dem', vts, fs, ns, cs) + cnf.Show() + +def arrow_test(): + cnf = Canvas3DFrame(None) + v1, v2 = np.array([[[0,0,0],[5,5,5]],[[0,15,5],[2,8,3]]], dtype=np.float32) + vts, fs, ns, cs = geoutil.build_arrows(v1, v2, 1, 1, 1, 1, (1,0,0)) + cnf.add_surf('arrow', vts, fs, ns, cs) + cnf.Show() + +def cube_test(): + cnf = Canvas3DFrame(None) + vts, fs, ns, cs = geoutil.build_cube((0,0,0), (1,1,1)) + cnf.add_surf('box', vts, fs, ns, cs, mode='grid') + cnf.Show() + +def cube_surf_test(): + cnf = Canvas3DFrame(None) + lut = np.zeros((256,3), dtype=np.uint8) + lut[:,0] = np.arange(256) + imgs = np.array([camera()[:300,::]]*256) + vts, fs, ns, cs = geoutil.build_img_cube(imgs) + obj = cnf.add_surf('cube', vts, fs, ns, cs) + vts, fs, ns, cs = geoutil.build_img_box(imgs) + cnf.add_surf('box', vts, fs, ns, cs, mode='grid') + cnf.Show() + +def volume_test(): + cnf = Canvas3DFrame(None) + cube = np.zeros((100,100,100), dtype=np.float32) + x,y,z = np.random.randint(10,90,900).reshape(3,-1) + cube[x,y,z] = 1000 + cube = ndimg.gaussian_filter(cube, 3) + vts, fs, ns, vs = geoutil.build_surf3d(cube, 1, 2) + cnf.add_surf('volume', vts, fs, ns, (1,0,0)) + cnf.Show() + +if __name__ == '__main__': + app = wx.App() + balls_mark_rest() + ''' + dem_test() + ball_test() + random_ball_test() + line_test() + mesh_test() + ball_ring_test() + balls_mark_rest() + surface2d_test() + arrow_test() + cube_test() + cube_surf_test() + volume_test() + ''' + app.MainLoop() diff --git a/sciwx/demo/plot_demo.py b/sciwx/demo/plot_demo.py new file mode 100644 index 00000000..71452a2e --- /dev/null +++ b/sciwx/demo/plot_demo.py @@ -0,0 +1,52 @@ +import wx, sys +sys.path.append('../../') + +import numpy as np +from sciwx.plot import PlotCanvas, PlotFrame, PlotNoteBook, PlotNoteFrame + +x = np.linspace(0, 10, 100) +y = np.sin(x) + +def plot_canvas_test(): + frame = wx.Frame(None, title='PlotCanvas') + pcanvas = PlotCanvas(frame) + ax = pcanvas.add_subplot() + ax.plot(x, y) + frame.Show() + +def plot_frame_test(): + pframe = PlotFrame(None) + ax = pframe.add_subplot() + ax.plot(x, y) + pframe.Show() + +def plot_note_book(): + frame = wx.Frame(None) + pnb = PlotNoteBook(frame) + figure1 = pnb.add_figure() + ax = figure1.add_subplot() + ax.plot(x, y) + figure2 = pnb.add_figure() + ax = figure2.add_subplot() + ax.plot(x, y, 'r-.') + frame.Show() + +def plot_note_frame(): + pnf = PlotNoteFrame(None) + figure1 = pnf.add_figure() + ax = figure1.add_subplot() + ax.plot(x, y) + ax.grid() + ax.set_title('abc') + figure2 = pnf.add_figure() + ax = figure2.add_subplot() + ax.plot(x, y) + pnf.Show() + +if __name__ == '__main__': + app = wx.App() + plot_canvas_test() + plot_frame_test() + plot_note_book() + plot_note_frame() + app.MainLoop() diff --git a/sciwx/demo/plt_demo.py b/sciwx/demo/plt_demo.py new file mode 100644 index 00000000..7c6adbe4 --- /dev/null +++ b/sciwx/demo/plt_demo.py @@ -0,0 +1,30 @@ +import sys +sys.path.append('../../') + +from skimage.data import astronaut, camera +from sciwx import plt + +import numpy as np +import pandas as pd + +if __name__ == '__main__': + plt.imshow(camera(), cn=0) + + plt.imstackshow([astronaut(), 255-astronaut()], cn=(0,1,2)) + + plt.tabshow(pd.DataFrame(np.zeros((100,5)))) + + plt.txtshow('abcdefg') + + plt.mdshow('#Markdown\n## paragraph') + + fg = plt.figure() + ax = fg.add_subplot() + ax.plot(np.random.rand(100)) + + mesh = plt.meshshow() + vts, fs, ns, cs = plt.build_ball((100,100,100),50, (1,0,0)) + mesh.add_surf('ball',vts, fs, ns, cs) + + plt.parashow({'c':(255,0,0)}, [('color', 'c', 'select', 'color')], False) + plt.show() diff --git a/sciwx/demo/roi_edit_demo.py b/sciwx/demo/roi_edit_demo.py new file mode 100644 index 00000000..ad428c7d --- /dev/null +++ b/sciwx/demo/roi_edit_demo.py @@ -0,0 +1,31 @@ +import sys +sys.path.append('../../') +from sciapp.object import mark2shp, Layer, json2shp +from sciapp.action import PolygonROI, LineROI, PointROI, EllipseROI, RectangleROI, FreeLineROI, FreePolygonROI +from sciwx.canvas import CanvasFrame +from skimage.data import astronaut, camera +from sciapp.object import ROI, Line +import wx + +ellipse = {'type':'ellipse', 'body':(100,100,100,-50,1)} +rectangles = {'type':'rectangles', 'body':[(100,100,80,50),(200,200,80,100)]} +layer = {'type':'layer', 'num':-1, 'color':(0,0,255), 'fill':False, 'body':[rectangles, ellipse]} + +if __name__ == '__main__': + app = wx.App() + frame = CanvasFrame(None) + frame.canvas.set_img(camera()) + roi = ROI([Line([(0,0),(100,100),(300,500)])]) + frame.canvas.image.roi = roi + bar = frame.add_toolbar() + bar = frame.add_toolbar() + + bar.add_tool('P', PointROI) + bar.add_tool('L', LineROI) + bar.add_tool('M', PolygonROI) + bar.add_tool('R', RectangleROI) + bar.add_tool('O', EllipseROI) + bar.add_tool('S', FreeLineROI) + bar.add_tool('&', FreePolygonROI) + frame.Show() + app.MainLoop() \ No newline at end of file diff --git a/sciwx/demo/shape_demo.py b/sciwx/demo/shape_demo.py new file mode 100644 index 00000000..1d5c5538 --- /dev/null +++ b/sciwx/demo/shape_demo.py @@ -0,0 +1,57 @@ +import sys +sys.path.append('../../') +from sciapp.object import mark2shp, Layer, json2shp +from sciapp.action import PointEditor, LineEditor, PolygonEditor, \ +RectangleEditor, EllipseEditor, FreeLineEditor, FreePolygonEditor, BaseEditor +from sciwx.canvas import VCanvas as Canvas +import wx + +point = {'type':'point', 'color':(255,0,0), 'lw':1, 'body':(10,10)} +points = {'type':'points', 'color':(255,0,0), 'lw':1, 'body':[(10,10),(100,200)]} +line = {'type':'line', 'color':(255,0,0), 'lw':1, 'lstyle':'-', 'body':[(10,10),(100,200),(200,200)]} +lines = {'type':'lines', 'color':(255,0,0), 'lw':1, 'lstyle':'-o', 'body':[[(10,10),(100,200),(200,200)],[(150,10),(50,250)]]} +polygon = {'type':'polygon', 'color':(255,0,0), 'fcolor':(255,255,0), 'lw':1, 'fill':False, 'lstyle':'o', 'body':[[(10,10),(100,200),(200,200)]]} +polygons = {'type':'polygons', 'color':(255,0,0), 'fcolor':(255,255,0,30), 'fill':True, 'lw':1, 'lstyle':'o', 'body':[[(10,10),(100,200),(200,200)],[(150,10),(50,250),(288,0)]]} +circle = {'type':'circle', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':False, 'body':(100,100,50)} +circles = {'type':'circles', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':True, 'lw':2, 'body':[(100,100,50),(300,300,100)]} +ellipse = {'type':'ellipse', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':False, 'body':(100,100,100,-50,1)} +ellipses = {'type':'ellipses', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':False, 'body':[(100,100,100,50,1),(200,250,50,100,3.14)]} +rectangle = {'type':'rectangle', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':True, 'body':(100,100,80,50)} +rectangles = {'type':'rectangles', 'fcolor':(255,255,0), 'fill':False, 'body':[(100,100,80,50),(200,200,80,100)]} +text = {'type':'text', 'color':(255,0,0), 'fcolor':(0,0,0), 'lw':8, 'fill':True, 'body':(100,200,'id=0')} +texts = {'type':'texts', 'color':(255,0,0), 'fcolor':(0,0,0), 'lw':8, 'fill':True, 'body':[(100,200,'id=0'),(180,250,'id=1')]} + +layer = {'type':'layer', 'num':-1, 'color':(255,255,0), 'fcolor':(255,255,255), 'fill':False, + 'body':[point, points, line, lines, polygon, polygons, circle, circles, ellipse, ellipses, rectangle, rectangles, text, texts]} + +layers = {'type':'layers', 'num':-1, 'color':(255,255,0), 'fcolor':(255,255,255), 'fill':False, 'body':{2:points, 1:line, 0:layer}} + +layer = {'type':'layer', 'num':-1, 'color':(0,0,255), 'fcolor':(255,255,255), 'fill':False, 'body':[ellipses, rectangles, ellipse]} + +def mark_test(mark): + frame = wx.Frame(None, title='gray test') + canvas = Canvas(frame, autofit=False, up=True) + canvas.set_shp(mark2shp(mark)) + frame.Show() + +if __name__ == '__main__': + app = wx.App() + #ShapeEditor(dtype={'layer', 'rectangles'}).start(None) + FreePolygonEditor().start(None) + #mark_test(point) + #mark_test(points) + #mark_test(line) + #mark_test(lines) + #mark_test(polygon) + #mark_test(polygons) + #mark_test(circle) + #mark_test(circles) + #mark_test(ellipse) + #mark_test(ellipses) + #mark_test(rectangle) + #mark_test(rectangles) + #mark_test(text) + #mark_test(texts) + mark_test(layer) + #mark_test(layers) + app.MainLoop() diff --git a/sciwx/demo/shape_edite_demo.py b/sciwx/demo/shape_edite_demo.py new file mode 100644 index 00000000..db45a13c --- /dev/null +++ b/sciwx/demo/shape_edite_demo.py @@ -0,0 +1,30 @@ +import sys +sys.path.append('../../') +from sciapp.object import mark2shp, Layer, json2shp +from sciapp.action import PointEditor, LineEditor, PolygonEditor, \ +RectangleEditor, EllipseEditor, FreeLineEditor, FreePolygonEditor, BaseEditor +from sciwx.canvas import VectorFrame +from sciwx.plugins.filters import Gaussian +import wx + +ellipse = {'type':'ellipse', 'body':(100,100,100,-50,1)} +rectangles = {'type':'rectangles', 'body':[(100,100,80,50),(200,200,80,100)]} +layer = {'type':'layer', 'num':-1, 'color':(0,0,255), 'fill':False, 'body':[rectangles, ellipse]} + +if __name__ == '__main__': + app = wx.App() + frame = VectorFrame(None) + frame.set_shp(mark2shp(layer)) + bar = frame.add_toolbar() + + bar.add_tool('E', BaseEditor) + bar.add_tool('P', PointEditor) + bar.add_tool('L', LineEditor) + bar.add_tool('M', PolygonEditor) + bar.add_tool('R', RectangleEditor) + bar.add_tool('O', EllipseEditor) + bar.add_tool('S', FreeLineEditor) + bar.add_tool('&', FreePolygonEditor) + + frame.Show() + app.MainLoop() diff --git a/sciwx/demo/shape_frame_demo.py b/sciwx/demo/shape_frame_demo.py new file mode 100644 index 00000000..f1be3a01 --- /dev/null +++ b/sciwx/demo/shape_frame_demo.py @@ -0,0 +1,55 @@ +import sys +sys.path.append('../../') +from sciapp.object import mark2shp, Layer, json2shp +from sciwx.canvas import VectorFrame, VectorNoteBook, VectorNoteFrame +import wx + +point = {'type':'point', 'color':(255,0,0), 'lw':1, 'body':(10,10)} +points = {'type':'points', 'color':(255,0,0), 'lw':1, 'body':[(10,10),(100,200)]} +line = {'type':'line', 'color':(255,0,0), 'lw':1, 'lstyle':'-', 'body':[(10,10),(100,200),(200,200)]} +lines = {'type':'lines', 'color':(255,0,0), 'lw':1, 'lstyle':'-o', 'body':[[(10,10),(100,200),(200,200)],[(150,10),(50,250)]]} +polygon = {'type':'polygon', 'color':(255,0,0), 'fcolor':(255,255,0), 'lw':1, 'fill':False, 'lstyle':'o', 'body':[(10,10),(100,200),(200,200)]} +polygons = {'type':'polygons', 'color':(255,0,0), 'fcolor':(255,255,0,30), 'fill':True, 'lw':1, 'lstyle':'o', 'body':[[(10,10),(100,200),(200,200)],[(150,10),(50,250),(288,0)]]} +circle = {'type':'circle', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':False, 'body':(100,100,50)} +circles = {'type':'circles', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':True, 'lw':2, 'body':[(100,100,50),(300,300,100)]} +ellipse = {'type':'ellipse', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':False, 'body':(100,100,100,50,1)} +ellipses = {'type':'ellipses', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':False, 'body':[(100,100,100,50,1),(200,250,50,100,3.14)]} +rectangle = {'type':'rectangle', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':True, 'body':(100,100,80,50)} +rectangles = {'type':'rectangles', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':False, 'body':[(100,100,80,50),(200,200,80,100)]} +text = {'type':'text', 'color':(255,0,0), 'fcolor':(0,0,0), 'lw':8, 'fill':True, 'body':(100,200,'id=0')} +texts = {'type':'texts', 'color':(255,0,0), 'fcolor':(0,0,0), 'lw':8, 'fill':True, 'body':[(100,200,'id=0'),(180,250,'id=1')]} + +layer = {'type':'layer', 'num':-1, 'clolor':(255,255,0), 'fcolor':(255,255,255), 'fill':False, + 'body':[point, points, line, lines, polygon, polygons, circle, circles, ellipse, ellipses, rectangle, rectangles, text, texts]} + +layers = {'type':'layers', 'num':-1, 'clolor':(255,255,0), 'fcolor':(255,255,255), 'fill':False, + 'body':{2:points, 1:line, 0:layer}} + +def frame_test(): + frame = VectorFrame(None) + frame.set_shp(mark2shp(layer)) + frame.Show() + +def note_test(): + frame = wx.Frame(None) + notebook = VectorNoteBook(frame) + canvas = notebook.add_canvas() + canvas.set_shp(mark2shp(layer)) + canvas = notebook.add_canvas() + canvas.set_shp(mark2shp(ellipses)) + frame.Show() + +def note_frame_test(): + frame = VectorNoteFrame(None) + canvas = frame.add_canvas() + canvas.set_shp(mark2shp(layer)) + canvas = frame.add_canvas() + canvas.set_shp(mark2shp(ellipses)) + frame.Show() + +if __name__ == '__main__': + app = wx.App() + # frame_test() + # note_test() + note_frame_test() + app.MainLoop() diff --git a/sciwx/demo/text_demo.py b/sciwx/demo/text_demo.py new file mode 100644 index 00000000..afa23112 --- /dev/null +++ b/sciwx/demo/text_demo.py @@ -0,0 +1,40 @@ +import sys, wx +sys.path.append('../../') + +from sciwx.text import TextPad, TextNoteBook, TextFrame, TextNoteFrame + +def text_pad_test(): + frame = wx.Frame(None, title='Text Pad') + textpad = TextPad(frame) + textpad.append('abcdefg') + frame.Show() + +def text_frame_test(): + textframe = TextFrame(None) + textframe.append('abcdefg') + textframe.Show() + +def text_note_book(): + frame = wx.Frame(None, title='Text Note Book') + tnb = TextNoteBook(frame) + note1 = tnb.add_notepad() + note1.append('abc') + note1 = tnb.add_notepad() + note1.append('def') + frame.Show() + +def text_note_frame(): + npbf = TextNoteFrame(None) + note1 = npbf.add_notepad() + note1.append('abc') + note1 = npbf.add_notepad() + note1.append('def') + npbf.Show() + +if __name__ == '__main__': + app = wx.App() + text_pad_test() + text_frame_test() + text_note_book() + text_note_frame() + app.MainLoop() diff --git a/sciwx/demo/widget_demo.py b/sciwx/demo/widget_demo.py new file mode 100644 index 00000000..78967f5b --- /dev/null +++ b/sciwx/demo/widget_demo.py @@ -0,0 +1,30 @@ +import sys, wx, numpy as np +sys.path.append('../../') +from sciwx.widgets import CMapPanel, CMapSelPanel, CurvePanel, HistPanel + +if __name__ == '__main__': + app = wx.App() + frame = wx.Frame(None) + sizer = wx.BoxSizer(wx.VERTICAL) + + cmap = CMapPanel(frame) + cmap.set_hist(np.random.rand(256)+2) + sizer.Add(cmap, 0, 0, 0) + + cmapsel = CMapSelPanel(frame, 'color map') + cmap = np.arange(256*3, dtype=np.uint8).reshape((3,-1)).T + cmapsel.SetItems({'gray':cmap}) + sizer.Add(cmapsel, 0, 0, 0) + + hist = HistPanel(frame) + hist.SetValue(np.random.rand(256)) + sizer.Add(hist, 0, 0, 0) + + curve = CurvePanel(frame, l=230) + curve.set_hist(np.random.rand(256)+2) + sizer.Add(curve, 0, 0, 0) + + frame.SetSizer(sizer) + frame.Fit() + frame.Show() + app.MainLoop() diff --git a/sciwx/grid/__init__.py b/sciwx/grid/__init__.py new file mode 100644 index 00000000..697596a3 --- /dev/null +++ b/sciwx/grid/__init__.py @@ -0,0 +1,2 @@ +from .grid import Grid +from .widget import GridFrame, GridNoteBook, GridNoteFrame, MGrid \ No newline at end of file diff --git a/imagepy/ui/tablewindow.py b/sciwx/grid/grid.py similarity index 54% rename from imagepy/ui/tablewindow.py rename to sciwx/grid/grid.py index 08330a86..605309d6 100644 --- a/imagepy/ui/tablewindow.py +++ b/sciwx/grid/grid.py @@ -1,64 +1,63 @@ -#!/usr/bin/env python - +from sciapp.object import Table +from sciapp.action import TableTool import wx, os -import wx.grid as Grid +import wx.grid import sys -from imagepy import IPy, root_dir import numpy as np import pandas as pd -from imagepy.core.manager import TableManager, WTableManager +from ..widgets import get_para -class TableBase(Grid.GridTableBase): - """ - A custom wx.Grid Table using user supplied data - """ +class TableBase(wx.grid.GridTableBase): def __init__(self): - Grid.GridTableBase.__init__(self) - self.tps = None + wx.grid.GridTableBase.__init__(self) + self.table = None - def set_tps(self, tps): - self.tps = tps - self._rows = tps.data.shape[0] - self._cols = tps.data.shape[1] + def set_data(self, table): + self.table = table + self._rows, self._cols = table.shape def GetNumberCols(self): - if self.tps is None: return 0 - return self.tps.data.shape[1] + if self.table is None: return 0 + return self.table.shape[1] def GetNumberRows(self): - if self.tps is None: return 0 - return self.tps.data.shape[0] + if self.table is None: return 0 + return self.table.shape[0] def GetColLabelValue(self, col): - return str(self.tps.data.columns[col]) + return str(self.table.columns[col]) def GetRowLabelValue(self, row): - return str(self.tps.data.index[row]) + return str(self.table.index[row]) def GetValue(self, row, col): - return self.tps.data.iat[row, col] + return self.table.data.iat[row, col] def GetRawValue(self, row, col): return 'x' def SetValue(self, row, col, value): - col = self.tps.data.columns[col] - self.tps.data[col][row] = value + col = self.table.columns[col] + self.table.data[col,row] = value def ResetView(self, grid): grid.BeginBatch() for current, new, delmsg, addmsg in [ - (self._rows, self.GetNumberRows(), Grid.GRIDTABLE_NOTIFY_ROWS_DELETED, Grid.GRIDTABLE_NOTIFY_ROWS_APPENDED), - (self._cols, self.GetNumberCols(), Grid.GRIDTABLE_NOTIFY_COLS_DELETED, Grid.GRIDTABLE_NOTIFY_COLS_APPENDED), + (self._rows, self.GetNumberRows(), + wx.grid.GRIDTABLE_NOTIFY_ROWS_DELETED, + wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED), + (self._cols, self.GetNumberCols(), + wx.grid.GRIDTABLE_NOTIFY_COLS_DELETED, + wx.grid.GRIDTABLE_NOTIFY_COLS_APPENDED) ]: if new < current: - msg = Grid.GridTableMessage(self,delmsg,new,current-new) + msg = wx.grid.GridTableMessage(self,delmsg,new,current-new) grid.ProcessTableMessage(msg) elif new > current: - msg = Grid.GridTableMessage(self,addmsg,new-current) + msg = wx.grid.GridTableMessage(self,addmsg,new-current) grid.ProcessTableMessage(msg) self.UpdateValues(grid) @@ -77,7 +76,7 @@ def ResetView(self, grid): def UpdateValues(self, grid): """Update all displayed values""" # This sends an event to the grid table to update all of the values - msg = Grid.GridTableMessage(self, Grid.GRIDTABLE_REQUEST_VIEW_GET_VALUES) + msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_REQUEST_VIEW_GET_VALUES) grid.ProcessTableMessage(msg) def _updateColAttrs(self, grid): @@ -90,27 +89,16 @@ def _updateColAttrs(self, grid): """ col = 0 - for colname in self.tps.data.columns: - attr = Grid.GridCellAttr() - renderer = MegaFontRenderer(self.tps) + for colname in self.table.columns: + attr = wx.grid.GridCellAttr() + renderer = MegaFontRenderer(self.table) attr.SetRenderer(renderer) grid.SetColAttr(col, attr) col += 1 - - - # end table manipulation code - # ---------------------------------------------------------- - - -# -------------------------------------------------------------------- -# Sample wx.Grid renderers - - - -class MegaFontRenderer(Grid.GridCellRenderer): - def __init__(self, tps): - Grid.GridCellRenderer.__init__(self) +class MegaFontRenderer(wx.grid.GridCellRenderer): + def __init__(self, table): + wx.grid.GridCellRenderer.__init__(self) n_back = wx.SystemSettings.GetColour( wx.SYS_COLOUR_WINDOW ) l_back = wx.SystemSettings.GetColour( wx.SYS_COLOUR_HIGHLIGHT ) n_text = wx.SystemSettings.GetColour( wx.SYS_COLOUR_WINDOWTEXT ) @@ -120,12 +108,12 @@ def __init__(self, tps): self.selectedBrush = wx.Brush("blue", wx.BRUSHSTYLE_SOLID) self.normalBrush = wx.Brush(wx.WHITE, wx.BRUSHSTYLE_SOLID) self.font = wx.Font(10, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, 0, "ARIAL") - self.tps = tps + self.table = table def Draw(self, grid, attr, dc, rect, row, col, isSelected): dc.SetClippingRegion(rect) - cn = self.tps.data.columns[col] - rd, tc, lc, ln = self.tps.get_props()[cn] + cn = self.table.columns[col] + rd, tc, lc, ln = self.table.style[cn] bcolor, tcolor = self.colors[isSelected] if isSelected:tc = tcolor @@ -136,11 +124,11 @@ def Draw(self, grid, attr, dc, rect, row, col, isSelected): dc.SetPen(wx.Pen(bcolor, 1, wx.PENSTYLE_SOLID)) dc.DrawRectangle(rect) - isnum = np.issubdtype(self.tps.data[cn].dtype, np.number) + isnum = np.issubdtype(self.table.data[cn].dtype, np.number) if ln!='Line': if isnum: - text = str(round(self.tps.data.iat[row, col], rd)) - else: text = str(self.tps.data.iat[row, col]) + text = str(round(self.table.data.iat[row, col], rd)) + else: text = str(self.table.data.iat[row, col]) dc.SetBackgroundMode(wx.SOLID) # change the text background based on whether the grid is selected @@ -161,10 +149,10 @@ def Draw(self, grid, attr, dc, rect, row, col, isSelected): dc.DrawText("...", x, rect.y+1) if isnum and ln!='Text': - data = self.tps.data + data = self.table.data cn = data.columns[col] rn = data.index[row] - minv, maxv = self.tps.range[cn] + minv, maxv = self.table.rg[cn] dc.SetPen(wx.Pen(wx.Colour(lc), 1, wx.PENSTYLE_SOLID)) dc.SetBrush(wx.Brush(wx.Colour(lc), wx.BRUSHSTYLE_SOLID)) v = rect.x + (data[cn][rn]-minv)/(maxv-minv)*rect.width @@ -182,109 +170,106 @@ def Draw(self, grid, attr, dc, rect, row, col, isSelected): # Sample Grid using a specialized table and renderers that can # be plugged in based on column names -class GridBase(Grid.Grid): +class Grid(wx.grid.Grid): def __init__(self, parent): - Grid.Grid.__init__(self, parent, -1) - self.table = TableBase() - self.Bind(Grid.EVT_GRID_RANGE_SELECT, self.on_select) + wx.grid.Grid.__init__(self, parent, -1) + self.tablebase = TableBase() + self.table = Table() + self.tool = None + + self.Bind(wx.grid.EVT_GRID_RANGE_SELECT, self.on_select) + self.Bind(wx.grid.EVT_GRID_LABEL_RIGHT_CLICK, self.on_label) + self.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.on_cell) + self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self.on_cell) self.Bind(wx.EVT_IDLE, self.on_idle) - self.tps = None - self.handle = None - self.Bind(Grid.EVT_GRID_LABEL_RIGHT_CLICK, self.on_label) def on_label(self, evt): - # Did we click on a row or a column? row, col = evt.GetRow(), evt.GetCol() if row==-1: - props = self.tps.props + props = self.table.props - cur = props.iloc[:,col] + cur = props[self.table.columns[col]] + print(cur) para = {'accu':cur[0], 'tc':cur[1], 'lc':cur[2], 'ln':cur[3]} view = [(int, 'accu', (0, 10), 0, 'accuracy', ''), ('color', 'tc', 'text color', ''), ('color', 'lc', 'line color', ''), (list, 'ln', ['Text', 'Line', 'Both'], str, 'draw', '')] - rst = IPy.get_para('Table Properties', view, para) + rst = get_para(para, view, 'Table Properties', self) if not rst :return + print(para) if col!=-1: - props.iloc[:,col] = [para[i] for i in ['accu', 'tc', 'lc', 'ln']] + props[self.table.columns[col]] = [para[i] for i in ['accu', 'tc', 'lc', 'ln']] + print(props[self.table.columns[col]]) + print('===========') if col==-1: - for c in range(props.shape[1]): - props.iloc[:,c] = [para[i] for i in ['accu', 'tc', 'lc', 'ln']] - ''' - if row==-1 and col>-1: - props.ix['ln',col] = (props.ix['ln',col]+1)%3 - if row==-1 and col==-1: - cn = self.tps.data.columns[col] - props.ix['ln'] = (props.ix['ln'].min()+1)%3 - ''' - self.tps.update() - - - def set_handler(self, handle=None): - self.handle = handle - - def set_tps(self, tps): - self.tps = tps - self.table.set_tps(tps) - self._rows, self._cols = tps.data.shape - self.SetTable(self.table) - self.Reset() + for c in self.table.columns: + props[c] = [para[i] for i in ['accu', 'tc', 'lc', 'ln']] + self.update() + + def on_cell(self, me): + x, y = me.GetCol(), me.GetRow() + obj, tol = self.table, TableTool.default + tool = self.tool or tol + #ld, rd, md = me.LeftIsDown(), me.RightIsDown(), me.MiddleIsDown() + sta = [me.AltDown(), me.ControlDown(), me.ShiftDown()] + others = {'alt':sta[0], 'ctrl':sta[1], + 'shift':sta[2], 'grid':self} + tool.mouse_down(self.table, x, y, 0, **others) + me.Skip() + + def set_data(self, tab): + if isinstance(tab, Table): + self.table = tab + else: self.table.data = tab + self.tablebase.set_data(self.table) + self._rows, self._cols = tab.shape + self.SetTable(self.tablebase) + self.update() + + def set_style(self, name, **key): + self.table.set_style(name, **key) + self.update() def on_select(self, event): rs, cs = self.GetSelectedRows(), self.GetSelectedCols() lt, rb = self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight() if len(lt)==1 and len(rb)==1: - return self.tps.select(range(lt[0][0],rb[0][0]+1), range(lt[0][1],rb[0][1]+1)) - else: self.tps.select(rs, cs, True) - #self.tps.select() + return self.table.select(range(lt[0][0],rb[0][0]+1), range(lt[0][1],rb[0][1]+1)) + else: self.table.select(list(rs), list(cs), True) - def Reset(self): - """reset the view based on the data in the table. Call - this when rows are added or destroyed""" - self.table.ResetView(self) - if not self.handle is None: - self.handle(self.tps) + def update(self): + self.tablebase.ResetView(self) def select(self): - self.Bind(Grid.EVT_GRID_RANGE_SELECT, None) + self.Bind(wx.grid.EVT_GRID_RANGE_SELECT, None) self.ClearSelection() - for i in self.tps.data.index.get_indexer(self.tps.rowmsk): + for i in self.table.index.get_indexer(self.table.rowmsk): self.SelectRow(i, True) - for i in self.tps.data.columns.get_indexer(self.tps.colmsk): + for i in self.table.columns.get_indexer(self.table.colmsk): self.SelectCol(i, True) - self.Bind(Grid.EVT_GRID_RANGE_SELECT, self.on_select) - - def __del__(self): - print('grid deleted!!!') + self.Bind(wx.grid.EVT_GRID_RANGE_SELECT, self.on_select) def on_idle(self, event): - if not self.IsShown() or self.tps is None\ - or self.tps.dirty == False: return - if self.tps.dirty == 'shp': - self.select() - self.Reset() - if self.tps.dirty == True: - self.select() - self.ForceRefresh() - self.tps.dirty = False + if not self.IsShown() or self.table is None\ + or self.table.dirty == False: return + self.select() + self.tablebase.ResetView(self) + self.table.dirty = False print('update') - -#--------------------------------------------------------------------------- - + def __del__(self): + print('grid deleted!!!') if __name__ == '__main__': dates = pd.date_range('20170220',periods=6) - data = pd.DataFrame(np.random.randn(6,4),index=dates,columns=list('ABCD')) + df = pd.DataFrame(np.random.randn(6,4),index=dates,columns=list('ABCD')) app = wx.App(False) - tf = TableFrame() - from imagepy import TablePlus - tps = TablePlus('table1', data) - tf.set_tps(tps) - tf.Show() - + frame = wx.Frame(None) + grid = Grid(frame) + grid.set_data(df) + frame.Show() app.MainLoop() diff --git a/sciwx/grid/widget.py b/sciwx/grid/widget.py new file mode 100644 index 00000000..9acf595e --- /dev/null +++ b/sciwx/grid/widget.py @@ -0,0 +1,140 @@ +import wx, wx.lib.agw.aui as aui +from .grid import Grid + +class MGrid(wx.Panel): + def __init__(self, parent=None, autofit=False): + wx.Frame.__init__ ( self, parent) + self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) + + self.SetBackgroundColour( wx.Colour( 255, 255, 255 ) ) + sizer = wx.BoxSizer( wx.VERTICAL ) + self.lab_info = wx.StaticText( self, wx.ID_ANY, + 'information', wx.DefaultPosition, wx.DefaultSize, 0 ) + self.lab_info.Wrap( -1 ) + # self.lab_info.Hide() + sizer.Add( self.lab_info, 0, wx.EXPAND, 0 ) + + self.grid = Grid(self) + sizer.Add( self.grid, 1, wx.EXPAND |wx.ALL, 0 ) + self.SetSizer(sizer) + self.select = self.grid.select + self.set_data = self.grid.set_data + + @property + def table(self): return self.grid.table + + @property + def name(self): return self.grid.table.name + +class GridFrame(wx.Frame): + def __init__(self, parent=None): + wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, + title = 'GridFrame', + pos = wx.DefaultPosition, + size = wx.Size( 800, 600 ), + style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + sizer = wx.BoxSizer(wx.VERTICAL) + self.grid = MGrid(self) + sizer.Add(self.grid, 1, wx.EXPAND|wx.ALL, 0) + self.SetSizer(sizer) + self.set_data = self.grid.set_data + self.Bind(wx.EVT_IDLE, self.on_idle) + + def on_idle(self, event): + if self.GetTitle()!=self.grid.table.title: + self.SetTitle(self.grid.table.title) + + def set_title(self, tab): self.SetTitle(tab.title) + + def on_valid(self, event): event.Skip() + + def on_close(self, event): event.Skip() + + def Show(self): + self.Fit() + wx.Frame.Show(self) + +class GridNoteBook(wx.lib.agw.aui.AuiNotebook): + def __init__(self, parent): + wx.lib.agw.aui.AuiNotebook.__init__( self, parent, wx.ID_ANY, + wx.DefaultPosition, wx.DefaultSize, wx.lib.agw.aui.AUI_NB_DEFAULT_STYLE ) + self.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_valid) + self.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close) + self.Bind( wx.EVT_IDLE, self.on_idle) + self.SetArtProvider(aui.AuiSimpleTabArt()) + + def on_idle(self, event): + for i in range(self.GetPageCount()): + title = self.GetPage(i).table.title + if self.GetPageText(i) != title: + self.SetPageText(i, title) + + def grid(self, i=None): + if not i is None: return self.GetPage(i) + else: return self.GetCurrentPage() + + def set_background(self, img): + self.GetAuiManager().SetArtProvider(ImgArtProvider(img)) + + def add_grid(self, grid=None): + if grid is None: grid = MGrid(self) + self.AddPage(grid, 'Image', True, wx.NullBitmap ) + return grid + + def set_title(self, panel, title): + self.SetPageText(self.GetPageIndex(panel), title) + + def on_valid(self, event): pass + + def on_close(self, event): pass + +class GridNoteFrame(wx.Frame): + def __init__(self, parent): + wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, + title = 'CanvasNoteFrame', + pos = wx.DefaultPosition, + size = wx.Size( 800, 600 ), + style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + sizer = wx.BoxSizer(wx.VERTICAL) + self.notebook = GridNoteBook(self) + self.grid = self.notebook.grid + sizer.Add( self.notebook, 1, wx.EXPAND |wx.ALL, 0 ) + self.SetSizer(sizer) + self.add_grid = self.notebook.add_grid + self.Layout() + self.Bind(wx.EVT_CLOSE, self.on_close) + + def add_toolbar(self): + toolbar = ToolBar(self) + self.GetSizer().Insert(0, toolbar, 0, wx.EXPAND | wx.ALL, 0) + return toolbar + + def on_close(self, event): + while self.notebook.GetPageCount()>0: + self.notebook.DeletePage(0) + event.Skip() + +if __name__=='__main__': + from skimage.data import camera, astronaut + from skimage.io import imread + + df = pd.DataFrame(np.arange(100).reshape((20,5))) + app = wx.App() + cf = GridFrame(None) + cf.set_data(df) + cf.Show() + app.MainLoop() + + ''' + app = wx.App() + cnf = CanvasNoteFrame(None) + canvas = cnf.add_img() + canvas.set_img(camera()) + + canvas = cnf.add_img() + canvas.set_img(camera()) + canvas.set_cn(0) + + cnf.Show() + app.MainLoop() + ''' diff --git a/sciwx/mesh/__init__.py b/sciwx/mesh/__init__.py new file mode 100644 index 00000000..a9007f9b --- /dev/null +++ b/sciwx/mesh/__init__.py @@ -0,0 +1,4 @@ +from .canvas import Canvas3D +from .mcanvas import MCanvas3D +from .widget import Canvas3DFrame, Canvas3DNoteBook, Canvas3DNoteFrame +from .scene import Surface, MarkText, MeshSet \ No newline at end of file diff --git a/sciwx/mesh/canvas.py b/sciwx/mesh/canvas.py new file mode 100644 index 00000000..7598bbf2 --- /dev/null +++ b/sciwx/mesh/canvas.py @@ -0,0 +1,173 @@ +import sys, platform +import moderngl +import numpy as np +import wx, math +import wx.glcanvas as glcanvas +from .scene import Scene +import os.path as osp +from pubsub import pub +from sciapp.util.surfutil import * + +class Canvas3D(glcanvas.GLCanvas): + def __init__(self, parent, scene=None): + attribList = (glcanvas.WX_GL_CORE_PROFILE, glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24) + glcanvas.GLCanvas.__init__(self, parent, -1, attribList=attribList[platform.system() == 'Windows':]) + self.init = False + self.context = glcanvas.GLContext(self) + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) + self.scene = self.scene = Scene() if scene is None else scene + self.size = None + + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) + + self.Bind(wx.EVT_SIZE, self.OnSize) + self.Bind(wx.EVT_PAINT, self.OnPaint) + self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseDown) + self.Bind(wx.EVT_LEFT_UP, self.OnMouseUp) + self.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseDown) + self.Bind(wx.EVT_RIGHT_UP, self.OnMouseUp) + self.Bind(wx.EVT_MOTION, self.OnMouseMotion) + self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel) + self.lastx, self.lasty = None, None + #self.update() + #print('init===========') + pub.subscribe(self.add_surf, 'add_surf') + # pub.subscribe(self.add_mark, 'add_mark') + + def InitGL(self): + self.scene.on_ctx(moderngl.create_context()) + self.DoSetViewport() + self.scene.reset() + + def OnDraw(self): + self.scene.set_viewport(0, 0, self.Size.width, self.Size.height) + #self.meshset.count_mvp() + self.scene.draw() + self.SwapBuffers() + + def OnSize(self, event): + self.scene.set_pers() + self.Refresh(False) + + def DoSetViewport(self): + size = self.size = self.GetClientSize() + self.SetCurrent(self.context) + if not self.scene is None and not self.scene.ctx is None: + self.scene.set_viewport(0, 0, self.Size.width, self.Size.height) + + def OnPaint(self, event): + self.SetCurrent(self.context) + #print(self, '=====', self.init) + if not self.init: + self.InitGL() + self.init = True + self.OnDraw() + + def OnMouseDown(self, evt): + self.CaptureMouse() + self.lastx, self.lasty = evt.GetPosition() + + def OnMouseUp(self, evt): + self.ReleaseMouse() + + def OnMouseMotion(self, evt): + self.SetFocus() + if evt.Dragging() and evt.LeftIsDown(): + x, y = evt.GetPosition() + dx, dy = x-self.lastx, y-self.lasty + self.lastx, self.lasty = x, y + angx = self.scene.angx - dx/200 + angy = self.scene.angy + dy/200 + self.scene.set_pers(angx=angx, angy=angy) + self.Refresh(False) + if evt.Dragging() and evt.RightIsDown(): + light = self.scene.light + x, y = evt.GetPosition() + dx, dy = x-self.lastx, y-self.lasty + self.lastx, self.lasty = x, y + angx, angy = dx/200, dy/200 + vx, vy, vz = self.scene.light + ay = math.asin(vz/math.sqrt(vx**2+vy**2+vz**2))-angy + xx = math.cos(angx)*vx - math.sin(angx)*vy + yy = math.sin(angx)*vx + math.cos(angx)*vy + ay = max(min(math.pi/2-1e-4, ay), -math.pi/2+1e-4) + zz, k = math.sin(ay), math.cos(ay)/math.sqrt(vx**2+vy**2) + self.scene.set_light((xx*k, yy*k, zz)) + self.Refresh(False) + + def save_bitmap(self, path): + context = wx.ClientDC( self ) + memory = wx.MemoryDC( ) + x, y = self.ClientSize + bitmap = wx.Bitmap( x, y, -1 ) + memory.SelectObject( bitmap ) + memory.Blit( 0, 0, x, y, context, 0, 0) + memory.SelectObject( wx.NullBitmap) + bitmap.SaveFile( path, wx.BITMAP_TYPE_PNG ) + + def save_stl(self, path): + from stl import mesh + objs = [i for i in self.scene.objs.values() if i.visible] + vers = [i.vts[i.ids] for i in objs if isinstance(i, Surface)] + vers = np.vstack(vers) + model = mesh.Mesh(np.zeros(vers.shape[0], dtype=mesh.Mesh.dtype)) + model.vectors = vers + model.save(path) + + def OnMouseWheel(self, evt): + k = 0.9 if evt.GetWheelRotation()>0 else 1/0.9 + self.scene.set_pers(l=self.scene.l*k) + self.Refresh(False) + #self.update() + + def set_mesh(self, mesh): + self.scene.set_mesh(mesh) + self.Refresh() + + def view_x(self, evt): + self.scene.reset(angx=0) + self.Refresh(False) + + def view_y(self, evt): + self.scene.reset(angx=pi/2) + self.Refresh(False) + + def view_z(self, evt): + self.scene.reset(angy=pi/2-1e-4) + self.Refresh(False) + + def set_pers(self, b): + self.scene.set_pers(pers=b) + self.Refresh(False) + + def set_background(self, c): + self.scene.set_background(c) + self.Refresh(False) + + def set_scatter(self, scatter): + self.scene.set_bright_scatter(scatter=scatter) + self.Refresh(False) + + def set_bright(self, bright): + self.scene.set_bright_scatter(bright=bright) + self.Refresh(False) + + def get_obj(self, name): + return self.scene.get_obj(name) + + def add_surf_asyn(self, name, obj): + wx.CallAfter(pub.sendMessage, 'add_surf', obj=obj) + + def add_surf(self, name, obj): + surf = self.scene.add_surf(name, obj) + if len(self.scene.objs)==1: + self.scene.reset() + self.Refresh(False) + +if __name__ == '__main__': + app = wx.App(False) + frm = wx.Frame(None, title='GLCanvas Sample') + canvas = Canvas3D(frm) + + frm.Show() + app.MainLoop() diff --git a/imagepy/core/myvi/imgs/__init__.py b/sciwx/mesh/imgs/__init__.py similarity index 100% rename from imagepy/core/myvi/imgs/__init__.py rename to sciwx/mesh/imgs/__init__.py diff --git a/imagepy/core/myvi/imgs/configure.png b/sciwx/mesh/imgs/configure.png similarity index 100% rename from imagepy/core/myvi/imgs/configure.png rename to sciwx/mesh/imgs/configure.png diff --git a/imagepy/core/myvi/imgs/isometric.png b/sciwx/mesh/imgs/isometric.png similarity index 100% rename from imagepy/core/myvi/imgs/isometric.png rename to sciwx/mesh/imgs/isometric.png diff --git a/imagepy/core/myvi/imgs/logo.ico b/sciwx/mesh/imgs/logo.ico similarity index 100% rename from imagepy/core/myvi/imgs/logo.ico rename to sciwx/mesh/imgs/logo.ico diff --git a/imagepy/core/myvi/imgs/open.png b/sciwx/mesh/imgs/open.png similarity index 100% rename from imagepy/core/myvi/imgs/open.png rename to sciwx/mesh/imgs/open.png diff --git a/imagepy/core/myvi/imgs/parallel.png b/sciwx/mesh/imgs/parallel.png similarity index 100% rename from imagepy/core/myvi/imgs/parallel.png rename to sciwx/mesh/imgs/parallel.png diff --git a/imagepy/core/myvi/imgs/save.png b/sciwx/mesh/imgs/save.png similarity index 100% rename from imagepy/core/myvi/imgs/save.png rename to sciwx/mesh/imgs/save.png diff --git a/imagepy/core/myvi/imgs/stl.png b/sciwx/mesh/imgs/stl.png similarity index 100% rename from imagepy/core/myvi/imgs/stl.png rename to sciwx/mesh/imgs/stl.png diff --git a/imagepy/core/myvi/imgs/x-axis.png b/sciwx/mesh/imgs/x-axis.png similarity index 100% rename from imagepy/core/myvi/imgs/x-axis.png rename to sciwx/mesh/imgs/x-axis.png diff --git a/imagepy/core/myvi/imgs/y-axis.png b/sciwx/mesh/imgs/y-axis.png similarity index 100% rename from imagepy/core/myvi/imgs/y-axis.png rename to sciwx/mesh/imgs/y-axis.png diff --git a/imagepy/core/myvi/imgs/z-axis.png b/sciwx/mesh/imgs/z-axis.png similarity index 100% rename from imagepy/core/myvi/imgs/z-axis.png rename to sciwx/mesh/imgs/z-axis.png diff --git a/sciwx/mesh/matutil.py b/sciwx/mesh/matutil.py new file mode 100644 index 00000000..0743bcca --- /dev/null +++ b/sciwx/mesh/matutil.py @@ -0,0 +1,49 @@ +import numpy as np + +def look_at(eye, target, up, dtype=None): + forward = (target - eye)/np.linalg.norm(target - eye) + side = (np.cross(forward, up))/np.linalg.norm(np.cross(forward, up)) + up = (np.cross(side, forward)/np.linalg.norm(np.cross(side, forward))) + + return np.array(( + (side[0], up[0], -forward[0], 0.), + (side[1], up[1], -forward[1], 0.), + (side[2], up[2], -forward[2], 0.), + (-np.dot(side, eye), -np.dot(up, eye), np.dot(forward, eye), 1.0) + ), dtype=np.float32) + +def perspective(xmax, ymax, near, far): + left, right = -xmax, xmax + bottom, top = -ymax, ymax + + A = (right + left) / (right - left) + B = (top + bottom) / (top - bottom) + C = -(far + near) / (far - near) + D = -2. * far * near / (far - near) + E = 2. * near / (right - left) + F = 2. * near / (top - bottom) + return np.array(( + ( E, 0., 0., 0.), + ( 0., F, 0., 0.), + ( A, B, C,-1.), + ( 0., 0., D, 0.), + ), dtype=np.float32) + +def orthogonal(xmax, ymax, near, far): + rml = xmax * 2 + tmb = ymax * 2 + fmn = far - near + + A = 2. / rml + B = 2. / tmb + C = -2. / fmn + Tx = 0 + Ty = 0 + Tz = -(far + near) / fmn + + return np.array(( + ( A, 0., 0., 0.), + (0., B, 0., 0.), + (0., 0., C, 0.), + (Tx, Ty, Tz, 1.), + ), dtype=np.float32) \ No newline at end of file diff --git a/imagepy/core/myvi/canvas3d.py b/sciwx/mesh/mcanvas.py similarity index 54% rename from imagepy/core/myvi/canvas3d.py rename to sciwx/mesh/mcanvas.py index ea778f86..802fd783 100644 --- a/imagepy/core/myvi/canvas3d.py +++ b/sciwx/mesh/mcanvas.py @@ -1,135 +1,23 @@ -import sys, platform -import moderngl -import numpy as np -import wx, math -import wx.glcanvas as glcanvas -from .manager import * +import wx import os.path as osp +import platform, math from pubsub import pub -from .util import * - -class Canvas3D(glcanvas.GLCanvas): - def __init__(self, parent, manager=None): - attribList = (glcanvas.WX_GL_CORE_PROFILE, glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24) - glcanvas.GLCanvas.__init__(self, parent, -1, attribList=attribList[platform.system() == 'Windows':]) - self.init = False - self.context = glcanvas.GLContext(self) - self.SetBackgroundStyle(wx.BG_STYLE_PAINT) - self.manager = self.manager = Manager() if manager is None else manager - self.size = None - - self.SetBackgroundStyle(wx.BG_STYLE_PAINT) - - self.Bind(wx.EVT_SIZE, self.OnSize) - self.Bind(wx.EVT_PAINT, self.OnPaint) - self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseDown) - self.Bind(wx.EVT_LEFT_UP, self.OnMouseUp) - self.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseDown) - self.Bind(wx.EVT_RIGHT_UP, self.OnMouseUp) - self.Bind(wx.EVT_MOTION, self.OnMouseMotion) - self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel) - self.lastx, self.lasty = None, None - #self.update() - #print('init===========') - - def InitGL(self): - self.manager.on_ctx() - self.DoSetViewport() - self.manager.reset() - - def OnDraw(self): - self.manager.set_viewport(0, 0, self.Size.width, self.Size.height) - #self.manager.count_mvp() - self.manager.draw() - self.SwapBuffers() - - def OnSize(self, event): - wx.CallAfter(self.DoSetViewport) - event.Skip() - - def DoSetViewport(self): - size = self.size = self.GetClientSize() - self.SetCurrent(self.context) - if not self.manager is None and not self.manager.ctx is None: - self.manager.set_viewport(0, 0, self.Size.width, self.Size.height) - - def OnPaint(self, event): - dc = wx.PaintDC(self) - self.SetCurrent(self.context) - #print(self, '=====', self.init) - if not self.init: - self.InitGL() - self.init = True - self.OnDraw() - - def OnMouseDown(self, evt): - self.CaptureMouse() - self.lastx, self.lasty = evt.GetPosition() - - def OnMouseUp(self, evt): - self.ReleaseMouse() - - def OnMouseMotion(self, evt): - self.SetFocus() - if evt.Dragging() and evt.LeftIsDown(): - x, y = evt.GetPosition() - dx, dy = x-self.lastx, y-self.lasty - self.lastx, self.lasty = x, y - angx = self.manager.angx - dx/200 - angy = self.manager.angy + dy/200 - self.manager.set_pers(angx=angx, angy=angy) - self.Refresh(False) - if evt.Dragging() and evt.RightIsDown(): - light = self.manager.light - x, y = evt.GetPosition() - dx, dy = x-self.lastx, y-self.lasty - self.lastx, self.lasty = x, y - angx, angy = dx/200, dy/200 - vx, vy, vz = self.manager.light - ay = math.asin(vz/math.sqrt(vx**2+vy**2+vz**2))-angy - xx = math.cos(angx)*vx - math.sin(angx)*vy - yy = math.sin(angx)*vx + math.cos(angx)*vy - ay = max(min(math.pi/2-1e-4, ay), -math.pi/2+1e-4) - zz, k = math.sin(ay), math.cos(ay)/math.sqrt(vx**2+vy**2) - self.manager.set_light((xx*k, yy*k, zz)) - self.Refresh(False) - - def save_bitmap(self, path): - context = wx.ClientDC( self ) - memory = wx.MemoryDC( ) - x, y = self.ClientSize - bitmap = wx.Bitmap( x, y, -1 ) - memory.SelectObject( bitmap ) - memory.Blit( 0, 0, x, y, context, 0, 0) - memory.SelectObject( wx.NullBitmap) - bitmap.SaveFile( path, wx.BITMAP_TYPE_PNG ) - - def save_stl(self, path): - from stl import mesh - objs = self.manager.objs.values() - vers = [i.vts[i.ids] for i in objs if isinstance(i, Surface)] - vers = np.vstack(vers) - model = mesh.Mesh(np.zeros(vers.shape[0], dtype=mesh.Mesh.dtype)) - model.vectors = vers - model.save(path) +from .canvas import Canvas3D +from sciapp.util import count_ns +import numpy as np +import math - def OnMouseWheel(self, evt): - k = 0.9 if evt.GetWheelRotation()>0 else 1/0.9 - self.manager.set_pers(l=self.manager.l*k) - self.Refresh(False) - #self.update() - def make_bitmap(bmp): img = bmp.ConvertToImage() img.Resize((20, 20), (2, 2)) return img.ConvertToBitmap() -class Viewer3D(wx.Panel): - def __init__( self, parent, manager=None): +class MCanvas3D(wx.Panel): + def __init__( self, parent, scene=None): wx.Panel.__init__(self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.TAB_TRAVERSAL ) #self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) sizer = wx.BoxSizer( wx.VERTICAL ) - self.canvas = Canvas3D(self, manager) + self.canvas = Canvas3D(self, scene) self.toolbar = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize) tsizer = wx.BoxSizer( wx.HORIZONTAL ) @@ -185,12 +73,12 @@ def __init__( self, parent, manager=None): self.col_color = wx.ColourPickerCtrl( self.settingbar, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE ) ssizer.Add( self.col_color, 0, wx.ALIGN_CENTER|wx.ALL, 1 ) - self.m_staticText2 = wx.StaticText( self.settingbar, wx.ID_ANY, u"Blend:", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText2 = wx.StaticText( self.settingbar, wx.ID_ANY, u"Alpha:", wx.DefaultPosition, wx.DefaultSize, 0 ) self.m_staticText2.Wrap( -1 ) ssizer.Add( self.m_staticText2, 0, wx.ALIGN_CENTER|wx.LEFT, 10 ) - self.sli_blend = wx.Slider( self.settingbar, wx.ID_ANY, 10, 0, 10, wx.DefaultPosition, wx.DefaultSize, wx.SL_HORIZONTAL ) - ssizer.Add( self.sli_blend, 0, wx.ALIGN_CENTER|wx.ALL, 1 ) + self.sli_alpha = wx.Slider( self.settingbar, wx.ID_ANY, 10, 0, 10, wx.DefaultPosition, wx.DefaultSize, wx.SL_HORIZONTAL ) + ssizer.Add( self.sli_alpha, 0, wx.ALIGN_CENTER|wx.ALL, 1 ) self.settingbar.SetSizer(ssizer) self.m_staticText2 = wx.StaticText( self.settingbar, wx.ID_ANY, u"Mode:", wx.DefaultPosition, wx.DefaultSize, 0 ) @@ -210,13 +98,25 @@ def __init__( self, parent, manager=None): self.Layout() self.Centre( wx.BOTH ) + self.view_x = self.canvas.view_x + self.view_y = self.canvas.view_y + self.view_z = self.canvas.view_z + self.set_pers = self.canvas.set_pers + self.set_background = self.canvas.set_background + self.set_scatter = self.canvas.set_scatter + self.set_bright = self.canvas.set_bright + + self.on_bgcolor = lambda e: self.canvas.set_background(np.array(e.GetColour()[:3])/255) + self.on_bg = lambda e: self.canvas.set_scatter((3-e.GetSelection())/3) + self.on_light = lambda e: self.canvas.set_bright((3-e.GetSelection())/3) + self.btn_x.Bind( wx.EVT_BUTTON, self.view_x) self.btn_y.Bind( wx.EVT_BUTTON, self.view_y) self.btn_z.Bind( wx.EVT_BUTTON, self.view_z) self.btn_open.Bind( wx.EVT_BUTTON, self.on_open) self.btn_stl.Bind( wx.EVT_BUTTON, self.on_stl) - self.btn_pers.Bind( wx.EVT_BUTTON, lambda evt, f=self.on_pers:f(True)) - self.btn_orth.Bind( wx.EVT_BUTTON, lambda evt, f=self.on_pers:f(False)) + self.btn_pers.Bind( wx.EVT_BUTTON, lambda evt, f=self.set_pers:f(True)) + self.btn_orth.Bind( wx.EVT_BUTTON, lambda evt, f=self.set_pers:f(False)) self.btn_color.Bind( wx.EVT_COLOURPICKER_CHANGED, self.on_bgcolor ) self.cho_obj.Bind( wx.EVT_CHOICE, self.on_select ) @@ -224,44 +124,20 @@ def __init__( self, parent, manager=None): self.cho_light.Bind( wx.EVT_CHOICE, self.on_light ) self.cho_bg.Bind( wx.EVT_CHOICE, self.on_bg ) self.chk_visible.Bind( wx.EVT_CHECKBOX, self.on_visible) - self.sli_blend.Bind( wx.EVT_SCROLL, self.on_blend ) + self.sli_alpha.Bind( wx.EVT_SCROLL, self.on_alpha ) self.col_color.Bind( wx.EVT_COLOURPICKER_CHANGED, self.on_color ) - if manager!=None: self.cho_obj.Set(list(manager.objs.keys())) - pub.subscribe(self.add_surf, 'add_surf') - pub.subscribe(self.add_mark, 'add_mark') + if scene!=None: self.cho_obj.Set(list(scene.objs.keys())) + + @property + def name(self): return self.canvas.scene.meshset.name - def view_x(self, evt): - self.canvas.manager.reset(angx=0) - self.canvas.Refresh(False) + def set_mesh(self, mesh): + self.canvas.set_mesh(mesh) + self.cho_obj.Set(list(mesh.objs.keys())) - - def view_y(self, evt): - self.canvas.manager.reset(angx=pi/2) - self.canvas.Refresh(False) - - def view_z(self, evt): - self.canvas.manager.reset(angy=pi/2-1e-4) - self.canvas.Refresh(False) - - def on_pers(self, b): - self.canvas.manager.set_pers(pers=b) - self.canvas.Refresh(False) - - def on_bgcolor(self, event): - c = tuple(np.array(event.GetColour()[:3])/255) - self.canvas.manager.set_background(c) - self.canvas.Refresh(False) - - def on_bg(self, event): - scatter = 3 - self.cho_bg.GetSelection() - self.canvas.manager.set_bright_scatter(scatter=scatter/3) - self.canvas.Refresh(False) - - def on_light(self, event): - bright = 3 - self.cho_light.GetSelection() - self.canvas.manager.set_bright_scatter(bright=bright/3) - self.canvas.Refresh(False) + @property + def mesh(self): return self.canvas.scene.meshset def on_save(self, evt): dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} @@ -298,14 +174,14 @@ def on_open(self, evt): dialog.Destroy() def get_obj(self, name): - return self.canvas.manager.get_obj(name) + return self.canvas.scene.meshset.get_obj(name) def on_visible(self, evt): self.curobj.set_style(visible=evt.IsChecked()) self.canvas.Refresh(False) - def on_blend(self, evt): - self.curobj.set_style(blend=evt.GetInt()/10.0) + def on_alpha(self, evt): + self.curobj.set_style(alpha=evt.GetInt()/10.0) self.canvas.Refresh(False) def on_mode(self, evt): @@ -323,39 +199,13 @@ def on_select(self, evt): self.chk_visible.SetValue(self.curobj.visible) color = (np.array(self.curobj.color)*255).astype(np.uint8) self.col_color.SetColour((tuple(color))) - self.sli_blend.SetValue(int(self.curobj.blend*10)) + self.sli_alpha.SetValue(int(self.curobj.alpha*10)) self.cho_mode.SetSelection(['mesh', 'grid'].index(self.curobj.mode)) - def add_surf_asyn(self, name, vts, fs, ns, cs, mode=None, blend=None, color=None, visible=None): - wx.CallAfter(pub.sendMessage, 'add_surf', name=name, vts=vts, fs=fs, ns=ns, cs=cs, obj=self, - mode=mode, blend=blend, color=color, visible=visible) - - def add_surf(self, name, vts, fs, ns, cs, obj=None, mode=None, blend=None, color=None, visible=None): - if obj!=None and not obj is self:return - manager = self.canvas.manager - surf = manager.add_surf(name, vts, fs, ns, cs) - - surf.set_style(mode=mode, blend=blend, color=color, visible=visible) - if len(manager.objs)==1: - manager.reset() + def add_surf_asyn(self, name, obj): + self.canvas.add_surf_asyn(name, obj) self.cho_obj.Append(name) - self.canvas.Refresh(False) - - def add_mark_asyn(self, name, vts, fs, ps, h, cs): - wx.CallAfter(pub.sendMessage, 'add_mark', name=name, vts=vts, fs=fs, ps=ps, h=h, cs=cs) - - def add_mark(self, name, vts, fs, ps, h, cs): - manager = self.canvas.manager - surf = manager.add_mark(name, vts, fs, ps, h, cs) - if len(manager.objs)==1: - manager.reset() - self.cho_obj.Append(name) - self.canvas.Refresh(False) - -if __name__ == '__main__': - app = wx.App(False) - frm = wx.Frame(None, title='GLCanvas Sample') - canvas = Canvas3D(frm) - frm.Show() - app.MainLoop() \ No newline at end of file + def add_surf(self, name, obj): + self.canvas.add_surf(name, obj) + self.cho_obj.Append(name) \ No newline at end of file diff --git a/sciwx/mesh/scene.py b/sciwx/mesh/scene.py new file mode 100644 index 00000000..2e949996 --- /dev/null +++ b/sciwx/mesh/scene.py @@ -0,0 +1,219 @@ +import numpy as np +import moderngl +from .matutil import * +import numpy as np +from math import sin, cos, tan, pi +from sciapp.object import Surface, MarkText, MeshSet + +class Scene: + def __init__(self): + self.title = 'Scene' + self.h, self.v, self.r = 1.5, 0, 300 + self.ratio, self.dial = 1.0, 1.0 + self.pers, self.center = True, (0,0,0) + self.angx = self.angy = 0 + self.fovy = self.l = 1 + self.background = 0.4, 0.4, 0.4 + self.light = (1,0,0) + self.bright, self.scatter = 0.66, 0.66 + self.meshset = MeshSet() + self.vabo = {} + self.ctx = None + + def set_mesh(self, mesh): + self.meshset = mesh + self.vabo = {} + for i in self.objs: + self.add_surf(i, self.objs[i]) + + def on_ctx(self, ctx): + self.ctx = ctx + self.prog_suf = self.ctx.program( + vertex_shader=''' + #version 330 + uniform mat4 Mvp; + in vec3 v_vert; + in vec3 v_norm; + in vec3 v_color; + out vec3 f_norm; + out vec3 f_color; + void main() { + gl_Position = Mvp * vec4(v_vert, 1); + f_norm = v_norm; + f_color = v_color; + } + ''', + fragment_shader=''' + #version 330 + uniform vec3 light; + uniform float blend; + uniform float scatter; + uniform float bright; + in vec3 f_norm; + in vec3 f_color; + out vec4 color; + void main() { + float d = clamp(dot(light, f_norm)*bright+scatter, 0, 1); + color = vec4(f_color*d, blend); + } + ''' + ) + + self.prog_txt = self.ctx.program( + vertex_shader=''' + #version 330 + uniform mat4 mv; + uniform mat4 proj; + uniform float h; + in vec3 v_vert; + in vec3 v_pos; + void main() { + vec4 o = mv * vec4(v_pos, 1); + gl_Position = proj *(o + vec4(v_vert.x*h, v_vert.y*h, v_vert.z, 0)); + } + ''', + fragment_shader=''' + #version 330 + uniform vec3 f_color; + out vec4 color; + void main() { + color = vec4(f_color, 1); + } + ''') + for i in self.objs: + if isinstance(self.objs[i], Surface): + self.ctx_obj(self.ctx, self.prog_suf, self.objs[i], i) + if isinstance(self.objs[i], MarkText): + self.ctx_txt(self.ctx, self.prog_txt, self.objs[i], i) + + @property + def objs(self): return self.meshset.objs + + def ctx_obj(self, ctx, prog, obj, name): + vts, ids, ns, cs = obj.vts, obj.ids, obj.ns, obj.cs + buf = np.zeros((len(vts), 9), dtype=np.float32) + buf[:,0:3], buf[:,3:6], buf[:,6:9] = vts, ns, cs + vbo = ctx.buffer(buf.tobytes()) + ibo = ctx.buffer(ids.tobytes()) + content = [(vbo, '3f 3f 3f', 'v_vert', 'v_norm', 'v_color')] + vao = ctx.vertex_array(prog, content, ibo) + self.vabo[name] = [buf, vao, vbo] + + def ctx_txt(self, ctx, prog, obj, name): + vts, ids, os, cs = obj.vts, obj.ids, obj.os, obj.cs + buf = self.buf = np.zeros((len(vts), 6), dtype=np.float32) + buf[:,0:3], buf[:,3:6] = vts, os + vbo = ctx.buffer(buf.tobytes()) + ibo = ctx.buffer(ids.tobytes()) + content = [(vbo, '3f 3f', 'v_vert', 'v_pos')] + vao = ctx.vertex_array(prog, content, ibo) + self.vabo[name] = [buf, vao, vbo] + + def add_surf(self, name, obj): + if not self.ctx is None: + if isinstance(obj, Surface): + self.ctx_obj(self.ctx, self.prog_suf, obj, name) + if isinstance(obj, MarkText): + self.ctx_txt(self.ctx, self.prog_txt, obj, name) + self.objs[name] = obj + self.count_box() + + def get_obj(self, key): return self.meshset.get_obj(key) + + def draw_obj(self, name, obj, ctx, prog, mvp, light, bright, scatter): + if not obj.visible: return + ctx.line_width = obj.width + mvp = np.dot(*mvp) + prog['Mvp'].write(mvp.astype(np.float32).tobytes()) + prog['blend'].value = obj.alpha + prog['scatter'].value = scatter + prog['light'].value = tuple(light) + prog['bright'].value = bright + mode = {'mesh':moderngl.TRIANGLES, 'grid':moderngl.LINES}[obj.mode] + if obj.update: + self.vabo[name][0][:,6:9] = obj.cs + self.vabo[name][2].write(self.vabo[name][0].tobytes()) + obj.update = False + self.vabo[name][1].render(mode) + + def draw_txt(self, name, obj, ctx, prog, mvp, light, bright, scatter): + if not obj.visible: return + ctx.line_width = obj.width + prog['mv'].write(mvp[0].astype(np.float32).tobytes()) + prog['proj'].write(mvp[1].astype(np.float32).tobytes()) + prog['f_color'].write(np.array(obj.cs).astype(np.float32).tobytes()) + prog['h'].value = obj.h + self.vabo[name][1].render(moderngl.LINES) + + def draw(self): + self.ctx.clear(*self.background) + self.ctx.enable(moderngl.DEPTH_TEST) + #self.ctx.enable(ModernGL.CULL_FACE) + self.ctx.enable(moderngl.BLEND) + for i in self.objs: + prog = self.prog_suf if isinstance(self.objs[i], Surface) else self.prog_txt + draw = self.draw_obj if isinstance(self.objs[i], Surface) else self.draw_txt + draw(i, self.objs[i], self.ctx, prog, self.mvp, self.light, self.bright, self.scatter) + + def count_box(self): + minb = [i.box[0] for i in self.objs.values() if not i.box is None] + minb = np.array(minb).min(axis=0) + maxb = [i.box[1] for i in self.objs.values() if not i.box is None] + maxb = np.array(maxb).max(axis=0) + self.box = np.vstack((minb, maxb)) + #print(self.box) + self.center = self.box.mean(axis=0) + self.dial = np.linalg.norm(self.box[1]-self.box[0]) + + def count_mvp(self): + #print('mvp') + ymax = (1.0 if self.pers else self.l) * np.tan(self.fovy * np.pi / 360.0) + xmax = ymax * self.ratio + proj = (perspective if self.pers else orthogonal)(xmax, ymax, 1.0, 100000) + lookat = look_at(self.eye, self.center, (0.0,0.0,1.0)) + self.mvp = (lookat, proj) + + def set_viewport(self, x, y, width, height): + self.ctx.viewport = (x, y, width, height) + self.ratio = width*1.0/height + + def set_background(self, rgb): self.background = rgb + + def set_light(self, light): self.light = light + + def set_bright_scatter(self, bright=None, scatter=None): + if not bright is None: self.bright = bright + if not scatter is None: self.scatter = scatter + + def reset(self, fovy=45, angx=0, angy=0): + self.fovy, self.angx, self.angy = fovy, angx, angy + self.l = self.dial/2/(tan(fovy*pi/360)) + v = np.array([cos(angy)*cos(angx), cos(angy)*sin(angx), sin(angy)]) + self.eye = self.center + v*self.l*1 + self.count_mvp() + #print('reset', self.eye, self.center) + + def set_pers(self, fovy=None, angx=None, angy=None, l=None, pers=None): + if not pers is None: self.pers = pers + if not fovy is None: self.fovy = fovy + if not angx is None: self.angx = angx + if not angy is None: self.angy = angy + self.angx %= 2*pi + self.angy = max(min(pi/2-1e-4, self.angy), -pi/2+1e-4) + if not l is None: self.l = l + v = np.array([cos(self.angy)*cos(self.angx), + cos(self.angy)*sin(self.angx), sin(self.angy)]) + self.eye = self.center + v*self.l*1 + self.count_mvp() + + def show(self, title='Myvi'): + import wx + from .frame3d import Frame3D + app = wx.App(False) + self.locale = wx.Locale(wx.LANGUAGE_ENGLISH) + Frame3D(None, title, self).Show() + app.MainLoop() + +if __name__ == '__main__': + img = imread('gis.png') + build_surf2d(img) \ No newline at end of file diff --git a/sciwx/mesh/widget.py b/sciwx/mesh/widget.py new file mode 100644 index 00000000..eabd1f9c --- /dev/null +++ b/sciwx/mesh/widget.py @@ -0,0 +1,113 @@ +import wx, wx.lib.agw.aui as aui +from .mcanvas import MCanvas3D + +class Canvas3DFrame(wx.Frame): + def __init__(self, parent=None): + wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, + title = 'Canvas3DFrame', + pos = wx.DefaultPosition, + size = wx.Size( 800, 600 ), + style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + sizer = wx.BoxSizer(wx.VERTICAL) + self.canvas = MCanvas3D(self) + sizer.Add(self.canvas, 1, wx.EXPAND|wx.ALL, 0) + self.SetSizer(sizer) + self.Bind(wx.EVT_IDLE, self.on_idle) + + self.view_x = self.canvas.view_x + self.view_y = self.canvas.view_y + self.view_z = self.canvas.view_z + self.on_pers = self.canvas.set_pers + self.set_background = self.canvas.set_background + self.set_scatter = self.canvas.set_scatter + self.set_bright = self.canvas.set_bright + self.add_surf_asyn = self.canvas.add_surf_asyn + self.add_surf = self.canvas.add_surf + self.add_mark_asyn = self.canvas.add_mark_asyn + self.add_mark = self.canvas.add_mark + self.set_mesh = self.canvas.set_mesh + + def on_idle(self, event): + if self.GetTitle()!=self.canvas.meshset.title: + self.SetTitle(self.canvas.meshset.title) + + def set_title(self, tab): self.SetTitle(tab.title) + + def on_valid(self, event): event.Skip() + + def on_close(self, event): event.Skip() + +class Canvas3DNoteBook(wx.lib.agw.aui.AuiNotebook): + def __init__(self, parent): + wx.lib.agw.aui.AuiNotebook.__init__( self, parent, wx.ID_ANY, + wx.DefaultPosition, wx.DefaultSize, wx.lib.agw.aui.AUI_NB_DEFAULT_STYLE ) + self.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_valid) + self.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close) + self.Bind( wx.EVT_IDLE, self.on_idle) + self.SetArtProvider(aui.AuiSimpleTabArt()) + + def on_idle(self, event): + for i in range(self.GetPageCount()): + title = self.GetPage(i).mesh.name + if self.GetPageText(i) != title: + self.SetPageText(i, title) + + def canvas(self, i=None): + if not i is None: return self.GetPage(i) + else: return self.GetCurrentPage() + + def set_background(self, img): + self.GetAuiManager().SetArtProvider(ImgArtabtProvider(img)) + + def add_canvas(self, canvas=None): + if canvas is None: canvas = MCanvas3D(self) + self.AddPage(canvas, 'Image', True, wx.NullBitmap ) + return canvas + + def set_title(self, panel, title): + self.SetPageText(self.GetPageIndex(panel), title) + + def on_valid(self, event): pass + + def on_close(self, event): pass + +class Canvas3DNoteFrame(wx.Frame): + def __init__(self, parent): + wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, + title = 'CanvasNoteFrame', + pos = wx.DefaultPosition, + size = wx.Size( 800, 600 ), + style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + sizer = wx.BoxSizer(wx.VERTICAL) + self.notebook = Canvas3DNoteBook(self) + self.canvas = self.notebook.canvas + sizer.Add( self.notebook, 1, wx.EXPAND |wx.ALL, 0 ) + self.SetSizer(sizer) + self.add_canvas = self.notebook.add_canvas + self.Layout() + + +if __name__=='__main__': + from skimage.data import camera, astronaut + from skimage.io import imread + + df = pd.DataFrame(np.arange(100).reshape((20,5))) + app = wx.App() + cf = GridFrame(None) + cf.set_data(df) + cf.Show() + app.MainLoop() + + ''' + app = wx.App() + cnf = CanvasNoteFrame(None) + canvas = cnf.add_img() + canvas.set_img(camera()) + + canvas = cnf.add_img() + canvas.set_img(camera()) + canvas.set_cn(0) + + cnf.Show() + app.MainLoop() + ''' diff --git a/sciwx/plot/__init__.py b/sciwx/plot/__init__.py new file mode 100644 index 00000000..ac75c242 --- /dev/null +++ b/sciwx/plot/__init__.py @@ -0,0 +1 @@ +from .plot import PlotCanvas, PlotFrame, PlotNoteBook, PlotNoteFrame diff --git a/sciwx/plot/plot.py b/sciwx/plot/plot.py new file mode 100644 index 00000000..b8745ec5 --- /dev/null +++ b/sciwx/plot/plot.py @@ -0,0 +1,112 @@ +from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas +from matplotlib.backends.backend_wx import NavigationToolbar2Wx as NavigationToolbar +from matplotlib.figure import Figure + +import wx, wx.lib.agw.aui as aui +import numpy as np + +class PlotCanvas(FigureCanvas): + def __init__(self, parent, id=-1, fig=None, title='Plot'): + self.figure = fig or Figure() + self.title = title + FigureCanvas.__init__(self, parent, id, self.figure) + + def add_subplot(self, n=111, **key): + return self.figure.add_subplot(n, **key) + +class PlotFrame(wx.Frame): + def __init__(self, parent, toolbar=True): + wx.Frame.__init__(self, parent, -1, + 'CanvasFrame', size=(550, 350)) + self.figure = PlotCanvas(self) + self.sizer = wx.BoxSizer(wx.VERTICAL) + self.sizer.Add(self.figure, 1, wx.LEFT | wx.TOP | wx.EXPAND) + self.SetSizer(self.sizer) + self.Fit() + self.add_subplot = self.figure.add_subplot + if toolbar: self.add_toolbar() + self.Bind(wx.EVT_IDLE, self.on_idle) + + def on_idle(self, event): + if self.GetTitle()!=self.figure.title: + self.SetTitle(self.figure.title) + + def add_toolbar(self): + self.toolbar = NavigationToolbar(self.figure) + self.toolbar.Realize() + self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND) + self.toolbar.update() + +class PlotNoteBook(wx.lib.agw.aui.AuiNotebook): + def __init__(self, parent): + wx.lib.agw.aui.AuiNotebook.__init__( self, parent, wx.ID_ANY, + wx.DefaultPosition, wx.DefaultSize, wx.lib.agw.aui.AUI_NB_DEFAULT_STYLE ) + self.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_valid) + self.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close) + self.Bind( wx.EVT_IDLE, self.on_idle) + self.SetArtProvider(aui.AuiSimpleTabArt()) + + def on_idle(self, event): + for i in range(self.GetPageCount()): + title = self.GetPage(i).title + if self.GetPageText(i) != title: + self.SetPageText(i, title) + + def figure(self, i=None): + if not i is None: return self.GetPage(i) + else: return self.GetCurrentPage() + + def set_background(self, img): + self.GetAuiManager().SetArtProvider(ImgArtProvider(img)) + + def add_figure(self, figure=None): + if figure is None: figure = PlotCanvas(self) + self.AddPage(figure, 'Figure', True, wx.NullBitmap ) + return figure + + def set_title(self, panel, title): + self.SetPageText(self.GetPageIndex(panel), title) + + def on_valid(self, event): pass + + def on_close(self, event): pass + + def mpl_connect(self, evt, method): + if self.figure() is None: return + self.figure().mpl_connect( + 'motion_notify_event', self.mouse_move) + +class PlotNoteFrame(wx.Frame): + def __init__(self, parent, toolbar=True): + wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, + title = 'CanvasNoteFrame', + pos = wx.DefaultPosition, + size = wx.Size( 800, 600 ), + style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + sizer = wx.BoxSizer(wx.VERTICAL) + self.notebook = PlotNoteBook(self) + self.figure = self.notebook.figure + sizer.Add( self.notebook, 1, wx.EXPAND |wx.ALL, 0 ) + self.SetSizer(sizer) + self.add_figure = self.notebook.add_figure + #if toolbar: self.add_toolbar() + self.Layout() + + def add_toolbar(self): + self.toolbar = NavigationToolbar(self.notebook) + self.toolbar.Realize() + self.GetSizer().Add(self.toolbar, 0, wx.LEFT | wx.EXPAND) + self.toolbar.update() + +if __name__ == '__main__': + app = wx.App() + pframe = PlotFrame(None) + ax = pframe.add_subplot() + x = np.linspace(0,10,100) + y = np.sin(x) + ax.plot(x, y) + ax.grid() + ax.set_title('abc') + pframe.Show() + app.MainLoop() + diff --git a/sciwx/plt.py b/sciwx/plt.py new file mode 100644 index 00000000..dc8f8266 --- /dev/null +++ b/sciwx/plt.py @@ -0,0 +1,77 @@ +import wx +from .canvas import CanvasFrame +from .grid import GridFrame +from .text import MDFrame, TextFrame +from .plot import PlotFrame +from .mesh import Canvas3DFrame +from .widgets import ParaDialog +from .mesh.geoutil import * + +app = None + +def new_app(): + global app + if app is None: app = wx.App() + +def imshow(img, cn=0, log=False, autofit=True): + new_app() + cf = CanvasFrame(None, autofit) + cf.set_img(img) + cf.set_cn(cn) + cf.set_log(log) + cf.Show() + return cf + +def imstackshow(imgs, cn=0, log=False, autofit=True): + new_app() + cf = CanvasFrame(None, autofit) + cf.set_imgs(imgs) + cf.set_cn(cn) + cf.set_log(log) + cf.Show() + return cf + +def tabshow(tab): + new_app() + gf = GridFrame(None) + gf.set_data(tab) + gf.Show() + return gf + +def meshshow(): + new_app() + cf = Canvas3DFrame(None) + cf.Show() + return cf + +def txtshow(txt): + new_app() + tf = TextFrame(None) + tf.append(txt) + tf.Show() + return tf + +def mdshow(txt): + new_app() + new_app() + mf = MDFrame(None) + mf.set_cont(txt) + mf.Show() + return mf + +def figure(): + new_app() + pf = PlotFrame(None) + pf.Show() + return pf + +def parashow(para, view, modal=True): + new_app() + pd = ParaDialog(None, 'Test') + pd.init_view(view, para, preview=True, modal=modal) + pd.pack() + if modal: pd.ShowModal() + else: pd.Show() + return para + +def show(): app.MainLoop() diff --git a/imagepy/data/__init__.py b/sciwx/plugins/__init__.py similarity index 100% rename from imagepy/data/__init__.py rename to sciwx/plugins/__init__.py diff --git a/imagepy/widgets/histogram/channels_wgt.py b/sciwx/plugins/channels.py similarity index 89% rename from imagepy/widgets/histogram/channels_wgt.py rename to sciwx/plugins/channels.py index 9ea50f45..d2e64922 100644 --- a/imagepy/widgets/histogram/channels_wgt.py +++ b/sciwx/plugins/channels.py @@ -1,14 +1,13 @@ -from ...ui.widgets import HistCanvas, CMapPanel, CMapSelCtrl, FloatSlider -from imagepy.core.manager import ColorManager, ImageManager, WindowsManager -from imagepy import IPy +from ..widgets import HistPanel, CMapPanel, FloatSlider import numpy as np import wx -class Plugin( wx.Panel ): +class Channels( wx.Panel ): title = 'Channels RGB' - def __init__( self, parent ): + def __init__( self, parent , app): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 255,0 ), style = wx.TAB_TRAVERSAL ) - + self.app = app + bSizer1 = wx.BoxSizer( wx.VERTICAL ) sizer_chans = wx.BoxSizer( wx.HORIZONTAL ) @@ -54,7 +53,7 @@ def __init__( self, parent ): bSizer1.Add(sizer_chans, 0, wx.ALL|wx.EXPAND, 2) - self.histpan = HistCanvas(self) + self.histpan = HistPanel(self) bSizer1.Add(self.histpan, 0, wx.ALL|wx.EXPAND, 5 ) self.sli_high = FloatSlider(self, (0,255), 0, '') @@ -158,15 +157,15 @@ def __init__( self, parent ): self.active = 0 def on_back(self, event): - self.com_back.SetItems(['None']+ImageManager.get_titles()) - cur = WindowsManager.get() + self.com_back.SetItems(['None']+self.app.get_img_name()) + cur = self.app.get_img_win() if not cur is None: cur = cur.back if not cur is None: cur = cur.title self.com_back.SetValue(str(cur)) modes = ['set', 'max', 'min', 'msk', 0.2, 0.4, 0.5, 0.6, 0.8] - ips = ImageManager.get() + ips = self.app.get_img() if ips is None: self.com_mode.Select(0) - else: self.com_mode.Select(modes.index(ips.chan_mode)) + else: self.com_mode.Select(modes.index(ips.mode)) def on_setback(self, event): name = self.com_back.GetValue() @@ -175,20 +174,20 @@ def on_setback(self, event): curwin.ips.update() def on_mode(self, event): - ips = ImageManager.get() + ips = self.app.get_img() if ips is None: return modes = ['set', 'max', 'min', 'msk', 0.2, 0.4, 0.5, 0.6, 0.8] - ips.chan_mode = modes[self.com_mode.GetSelection()] + ips.mode = modes[self.com_mode.GetSelection()] ips.update() # Virtual event handlers, overide them in your derived class def on_low( self, event ): - ips = IPy.get_ips() + ips = self.app.get_img() if ips is None: return if self.sli_low.GetValue()>self.sli_high.GetValue(): self.sli_high.SetValue(self.sli_low.GetValue()) rg = (self.sli_low.GetValue(), self.sli_high.GetValue()) - ips.chan_range[self.active] = rg + ips.rg[self.active] = rg minv, maxv = self.sli_low.min, self.sli_high.max lim1 = 1.0 * (self.sli_low.GetValue() - minv)/(maxv-minv) lim2 = 1.0 * (self.sli_high.GetValue() - minv)/(maxv-minv) @@ -196,12 +195,12 @@ def on_low( self, event ): ips.update() def on_high( self, event ): - ips = IPy.get_ips() + ips = self.app.get_img() if ips is None: return if self.sli_low.GetValue()>self.sli_high.GetValue(): self.sli_low.SetValue(self.sli_high.GetValue()) rg = (self.sli_low.GetValue(), self.sli_high.GetValue()) - ips.chan_range[self.active] = rg + ips.rg[self.active] = rg minv, maxv = self.sli_low.min, self.sli_high.max lim1 = 1.0 * (self.sli_low.GetValue() - minv)/(maxv-minv) lim2 = 1.0 * (self.sli_high.GetValue() - minv)/(maxv-minv) @@ -209,18 +208,18 @@ def on_high( self, event ): ips.update() def on_rgb( self, event, color): - ips = IPy.get_ips() + ips = self.app.get_img() if ips is None: return - if isinstance(ips.chan, int): - ips.chan = [0, 1, 2] + if isinstance(ips.cn, int): + ips.cn = [0, 1, 2] chs = ['C:%d'%i for i in range(ips.channels)] for i in (self.com_r, self.com_g, self.com_b): chn = i.GetValue() i.SetItems(chs) idx = chs.index(chn) if chn in chs else 0 i.Select(idx) - chanred = ips.chan['rgb'.index(color)] - chanrg = ips.chan_range[chanred] + chanred = ips.cn['rgb'.index(color)] + chanrg = ips.rg[chanred] rg = ips.get_updown('all', chanred, 512) if (rg[0]==rg[1]): rg = (rg[0]-1e-4, rg[1]+1e-4) slis = 'all' if self.chk_stack.GetValue() else ips.cur @@ -230,7 +229,7 @@ def on_rgb( self, event, color): self.sli_high.set_para(rg, 10) self.sli_low.SetValue(chanrg[0]) self.sli_high.SetValue(chanrg[1]) - self.active = ips.chan['rgb'.index(color)] + self.active = ips.cn['rgb'.index(color)] self.range = chanrg lim1 = (chanrg[0]-rg[0])/(rg[1]-rg[0]) lim2 = (chanrg[1]-rg[0])/(rg[1]-rg[0]) @@ -238,39 +237,39 @@ def on_rgb( self, event, color): ips.update() def on_gray(self, event): - ips = IPy.get_ips() + ips = self.app.get_img() if ips is None: return - if not isinstance(ips.chan, int): ips.chan = 0 - chanrg = ips.chan_range[ips.chan] - rg = ips.get_updown('all', ips.chan, 512) + if not isinstance(ips.cn, int): ips.cn = 0 + chanrg = ips.rg[ips.cn] + rg = ips.get_updown('all', ips.cn, 512) if (rg[0]==rg[1]): rg = (rg[0]-1e-4, rg[1]+1e-4) slis = 'all' if self.chk_stack.GetValue() else ips.cur - hist = ips.histogram(rg, slis, ips.chan, 512) + hist = ips.histogram(rg, slis, ips.cn, 512) self.histpan.SetValue(hist) self.sli_low.set_para(rg, 10) self.sli_high.set_para(rg, 10) self.sli_low.SetValue(chanrg[0]) self.sli_high.SetValue(chanrg[1]) - self.active = ips.chan + self.active = ips.cn lim1 = (chanrg[0]-rg[0])/(rg[1]-rg[0]) lim2 = (chanrg[1]-rg[0])/(rg[1]-rg[0]) self.histpan.set_lim(lim1*255, lim2*255) ips.update() def on_chan(self, event, color): - ips = IPy.get_ips() + ips = self.app.get_img() if ips is None: return C = (self.com_r, self.com_g, self.com_b) host = C['rgb'.index(color)] chnidx = host.GetSelection() - ips.chan['rgb'.index(color)] = chnidx + ips.cn['rgb'.index(color)] = chnidx self.on_rgb(event, color) def on_8bit(self, event): - ips = IPy.get_ips() + ips = self.app.get_img() if ips is None: return rg = (0,255) - ips.chan_range[self.active] = rg + ips.rg[self.active] = rg if (rg[0]==rg[1]): rg = (rg[0]-1e-4, rg[1]+1e-4) slis = 'all' if self.chk_stack.GetValue() else ips.cur hist = ips.histogram(rg, slis, self.active, 512) @@ -283,7 +282,7 @@ def on_8bit(self, event): ips.update() def on_p(self, event, k): - ips = IPy.get_ips() + ips = self.app.get_img() if ips is None: return rg = ips.get_updown('all', self.active, 512) if (rg[0]==rg[1]): rg = (rg[0]-1e-4, rg[1]+1e-4) @@ -293,11 +292,10 @@ def on_p(self, event, k): idx = np.where(msk)[0] vs = np.array([idx.min(), idx.max()]) vs = vs*(rg[1]-rg[0])/255+rg[0] - ips.chan_range[self.active] = tuple(vs) + ips.rg[self.active] = tuple(vs) self.histpan.SetValue(hist) self.sli_low.set_para(rg, 10) self.sli_high.set_para(rg, 10) - print(rg, vs) self.sli_low.SetValue(vs[0]) self.sli_high.SetValue(vs[1]) lim1 = (vs[0]-rg[0])/(rg[1]-rg[0]) diff --git a/imagepy/widgets/histogram/curve_wgt.py b/sciwx/plugins/curve.py similarity index 88% rename from imagepy/widgets/histogram/curve_wgt.py rename to sciwx/plugins/curve.py index dd699d5d..90092ff4 100644 --- a/imagepy/widgets/histogram/curve_wgt.py +++ b/sciwx/plugins/curve.py @@ -1,13 +1,12 @@ -from ...ui.widgets import CurvePanel -from imagepy.core.manager import ColorManager -from imagepy import IPy +from ..widgets import CurvePanel import numpy as np, wx -class Plugin(wx.Panel): +class Curve(wx.Panel): title = 'Curve Adjust' - def __init__(self, parent): + def __init__(self, parent, app): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 255,0 ), style = wx.TAB_TRAVERSAL ) + self.app = app bSizer1 = wx.BoxSizer( wx.VERTICAL ) self.curvepan = CurvePanel(self, l=240) @@ -20,31 +19,26 @@ def __init__(self, parent): bSizer2.Add( self.btn_apply, 0, wx.ALIGN_CENTER|wx.ALL, 0 ) - bSizer2.AddStretchSpacer(prop=1) self.btn_clear = wx.Button( self, wx.ID_ANY, u"clear", wx.DefaultPosition, wx.Size( -1,-1 ), wx.BU_EXACTFIT ) self.btn_clear.SetMaxSize( wx.Size( -1,40 ) ) bSizer2.Add( self.btn_clear, 0, wx.ALIGN_CENTER|wx.ALL, 0 ) - bSizer2.AddStretchSpacer(prop=1) self.btn_reset = wx.Button( self, wx.ID_ANY, u"reset", wx.DefaultPosition, wx.Size( -1,-1 ), wx.BU_EXACTFIT ) self.btn_reset.SetMaxSize( wx.Size( -1,40 ) ) bSizer2.Add( self.btn_reset, 0, wx.ALIGN_CENTER|wx.ALL, 0 ) - bSizer2.AddStretchSpacer(prop=1) self.btn_invert = wx.Button( self, wx.ID_ANY, u"invert", wx.DefaultPosition, wx.Size( -1,-1 ), wx.BU_EXACTFIT ) self.btn_invert.SetMaxSize( wx.Size( -1,40 ) ) bSizer2.Add( self.btn_invert, 0, wx.ALIGN_CENTER|wx.ALL, 0 ) - bSizer1.Add( bSizer2, 0, wx.EXPAND |wx.ALL, 5 ) - self.SetSizer( bSizer1 ) self.Layout() @@ -55,7 +49,7 @@ def __init__(self, parent): self.btn_invert.Bind( wx.EVT_BUTTON, self.on_invert ) def handle(self, event): - ips = IPy.get_ips() + ips = self.app.get_img() if ips is None:return lut = CurvePanel.lookup(self.curvepan.pts) lut = np.vstack((lut,lut,lut)).T @@ -63,23 +57,23 @@ def handle(self, event): ips.update() def on_apply(self, event): - ips = IPy.get_ips() + ips = self.app.get_img() if ips is None:return hist = ips.histogram() self.curvepan.set_hist(hist) self.handle(None) def on_clear(self, event): - ips = IPy.get_ips() + ips = self.app.get_img() if ips is None:return hist = ips.histogram() self.curvepan.set_hist(hist) - ips.lut = ColorManager.get_lut() + ips.lut = np.arange(256*3, dtype=np.uint8).reshape((3,-1)).T ips.update() def on_reset(self, event): self.curvepan.SetValue() - ips = IPy.get_ips() + ips = self.app.get_img() if ips is None:return hist = ips.histogram() self.curvepan.set_hist(hist) @@ -87,7 +81,7 @@ def on_reset(self, event): def on_invert(self, event): self.curvepan.SetValue([(0,255),(255,0)]) - ips = IPy.get_ips() + ips = self.app.get_img() if ips is None:return hist = ips.histogram() self.curvepan.set_hist(hist) diff --git a/sciwx/plugins/filters.py b/sciwx/plugins/filters.py new file mode 100644 index 00000000..de607ef5 --- /dev/null +++ b/sciwx/plugins/filters.py @@ -0,0 +1,15 @@ +from sciapp.action import ImgAction +from scipy.ndimage import gaussian_filter + +class Gaussian(ImgAction): + name = 'Gaussian' + note = ['auto_snap', 'preview'] + para = {'sigma':2} + view = [(float, 'sigma', (0, 30), 1, 'sigma', 'pix')] + + def run(self, ips, img, snap, para): + gaussian_filter(snap, para['sigma'], output=img) + +class Undo(ImgAction): + name = 'Undo' + def run(self, ips, img, snap, para): ips.swap() \ No newline at end of file diff --git a/imagepy/widgets/histogram/histogram_wgt.py b/sciwx/plugins/histogram.py similarity index 83% rename from imagepy/widgets/histogram/histogram_wgt.py rename to sciwx/plugins/histogram.py index 3ecf9351..7e4fb7d6 100644 --- a/imagepy/widgets/histogram/histogram_wgt.py +++ b/sciwx/plugins/histogram.py @@ -1,17 +1,16 @@ -from ...ui.widgets import HistCanvas, CMapPanel, CMapSelCtrl, FloatSlider -from imagepy.core.manager import ColorManager -from imagepy import IPy -import numpy as np -import wx +from ..widgets import HistPanel, CMapPanel, FloatSlider, CMapSelCtrl +import wx, numpy as np +from sciapp import Source -class Plugin( wx.Panel ): +class Histogram( wx.Panel ): title = 'Histogram' - def __init__( self, parent ): + + def __init__( self, parent, app): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 255,0 ), style = wx.TAB_TRAVERSAL ) - + self.app = app bSizer1 = wx.BoxSizer( wx.VERTICAL ) - self.histpan = HistCanvas(self) + self.histpan = HistPanel(self) bSizer1.Add(self.histpan, 0, wx.ALL|wx.EXPAND, 5 ) self.sli_high = FloatSlider(self, (0,255), 0, '') @@ -53,13 +52,9 @@ def __init__( self, parent ): bSizer1.Add( bSizer2, 0, wx.EXPAND |wx.ALL, 5 ) - #line = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - #txtlut = wx.StaticText( self, wx.ID_ANY, 'Look Up Table', wx.DefaultPosition, wx.DefaultSize) - #bSizer1.Add( line, 0, wx.EXPAND |wx.ALL, 5 ) - #bSizer1.Add( txtlut, 0, wx.EXPAND |wx.ALL, 5 ) - self.cmapsel = CMapSelCtrl(self) - self.cmapsel.SetItems(ColorManager.luts) + + self.cmapsel.SetItems(Source.manager('colormap').gets()) bSizer1.Add(self.cmapsel, 0, wx.ALL|wx.EXPAND, 5 ) self.cmap = CMapPanel(self) @@ -81,22 +76,22 @@ def __init__( self, parent ): self.range = (0, 255) def on_cmap(self): - ips = IPy.get_ips() + ips = self.app.get_img() if ips is None: return cmap = CMapPanel.linear_color(self.cmap.GetValue()) ips.lut = cmap ips.update() def on_cmapsel(self, event): - ips = IPy.get_ips() + ips = self.app.get_img() if ips is None: return - key = self.cmapsel.GetValue() - ips.lut = ColorManager.get_lut(key) + key = self.cmapsel.GetSelection() + ips.lut = self.cmapsel.vs[key] ips.update() # Virtual event handlers, overide them in your derived class def on_low( self, event ): - ips = IPy.get_ips() + ips = self.app.get_img() if ips is None: return if self.sli_high.GetValue()self.sli_high.GetValue(): self.sli_low.SetValue(self.sli_high.GetValue()) @@ -120,7 +115,7 @@ def on_high( self, event ): ips.update() def on_8bit( self, event ): - ips = IPy.get_ips() + ips = self.app.get_img() if ips is None: return self.range = ips.range = (0,255) hist = ips.histogram() @@ -133,7 +128,7 @@ def on_8bit( self, event ): ips.update() def on_minmax( self, event ): - ips = IPy.get_ips() + ips = self.app.get_img() if ips is None: return minv, maxv = ips.get_updown() self.range = ips.range = (minv, maxv) @@ -147,13 +142,13 @@ def on_minmax( self, event ): ips.update() def on_slice( self, event ): - ips = IPy.get_ips() + ips = self.app.get_img() if ips is None: return hist = ips.histogram() self.histpan.SetValue(hist) def on_stack( self, event ): - ips = IPy.get_ips() + ips = self.app.get_img() if ips is None: return - hists = ips.histogram(slices=True) + hists = ips.histogram(slices='all', chans='all', step=512) self.histpan.SetValue(hists) \ No newline at end of file diff --git a/sciwx/plugins/io.py b/sciwx/plugins/io.py new file mode 100644 index 00000000..b6b5068f --- /dev/null +++ b/sciwx/plugins/io.py @@ -0,0 +1,22 @@ +from sciapp.action import SciAction, ImgAction +from skimage.io import imread, imsave + +class Open(SciAction): + name = 'Open' + def start(self, app, para=None): + path = app.getpath('Open', ['png','bmp','jpg'], 'open') + if path is None: return + app.show_img(imread(path)) + +class Save(ImgAction): + name = 'Save' + para = {'path':''} + + def show(self): + path = self.app.getpath('Open', ['png','bmp','jpg'], 'save') + if path is None: return + self.para['path'] = path + return True + + def run(self, ips, img, snap, para): + imsave(para['path'], img) \ No newline at end of file diff --git a/sciwx/plugins/pencil.py b/sciwx/plugins/pencil.py new file mode 100644 index 00000000..f9134d50 --- /dev/null +++ b/sciwx/plugins/pencil.py @@ -0,0 +1,29 @@ +from sciapp.action import Tool +from skimage.draw import line + +class Pencil(Tool): + title = 'Pencil' + + def __init__(self): + self.status = False + self.oldp = (0,0) + + def mouse_down(self, ips, x, y, btn, **key): + self.status = True + self.oldp = (y, x) + ips.snapshot() + + def mouse_up(self, ips, x, y, btn, **key): + self.status = False + + def mouse_move(self, ips, x, y, btn, **key): + if not self.status:return + se = self.oldp + (y,x) + rs,cs = line(*[int(i) for i in se]) + rs.clip(0, ips.shape[1], out=rs) + cs.clip(0, ips.shape[0], out=cs) + ips.img[rs,cs] = 255 + self.oldp = (y, x) + key['canvas'].update() + + def mouse_wheel(self, ips, x, y, d, **key):pass \ No newline at end of file diff --git a/imagepy/widgets/navigator/navigator_wgt.py b/sciwx/plugins/viewport.py similarity index 81% rename from imagepy/widgets/navigator/navigator_wgt.py rename to sciwx/plugins/viewport.py index f0c49a5c..ea02019c 100644 --- a/imagepy/widgets/navigator/navigator_wgt.py +++ b/sciwx/plugins/viewport.py @@ -1,21 +1,18 @@ -from ...ui.widgets import ViewPort -from imagepy import IPy -import numpy as np -import wx +from ..widgets import ViewPort as ViewPortCtrl +import wx, numpy as np -class Plugin ( wx.Panel ): +class ViewPort ( wx.Panel ): title = 'Navigator' scales = [0.03125, 0.0625, 0.125, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4, 5, 8, 10, 15, 20, 30, 50] - def __init__( self, parent ): - - + def __init__( self, parent , app): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 255,200 ), style = wx.TAB_TRAVERSAL ) + self.app = app bSizer1 = wx.BoxSizer( wx.VERTICAL ) bSizer3 = wx.BoxSizer( wx.HORIZONTAL ) - self.viewport = ViewPort( self) + self.viewport = ViewPortCtrl( self) bSizer3.Add( self.viewport, 1, wx.EXPAND |wx.ALL, 5 ) self.slider = wx.Slider( self, wx.ID_ANY, 6, 0, len(self.scales), wx.DefaultPosition, wx.DefaultSize, wx.SL_LEFT|wx.SL_VERTICAL|wx.SL_SELRANGE|wx.SL_INVERSE ) @@ -59,35 +56,34 @@ def __init__( self, parent ): self.btn_apply.Bind( wx.EVT_BUTTON, self.on_apply ) self.btn_fit.Bind( wx.EVT_BUTTON, self.on_fit ) self.btn_one.Bind( wx.EVT_BUTTON, self.on_one ) - self.viewport.set_handle(self.on_handle) + self.viewport.Bind('View_EVT', self.on_handle) def on_apply(self, event): - win = IPy.get_window() + win = self.app.get_img_win() if win is None: return - img = win.ips.img - step = max(max(img.shape[:2])//300,1) - self.viewport.set_img(win.ips.lookup(img[::step,::step]), img.shape) + image = win.image + step = max(max(image.shape[:2])//300,1) + self.viewport.set_img(image.lookup(image.img[::step,::step]), image.shape) self.viewport.set_box(win.canvas.conbox, win.canvas.winbox) def on_zoom(self, event): self.on_apply(event) k = self.scales[self.slider.GetValue()] self.label.SetLabel('%.2f%%'%(k*100)) - win = IPy.get_window() + win = self.app.get_img_win() if win is None: return a,b,c,d = win.canvas.winbox win.canvas.scaidx = self.slider.GetValue() win.canvas.zoom(k, (a+c)/2, (b+d)/2) - win.ips.update() + win.image.update() self.viewport.set_box(win.canvas.conbox, win.canvas.winbox) def on_fit(self, event): self.on_apply(event) - win = IPy.get_window() + win = self.app.get_img_win() if win is None: return win.canvas.fit() - win.ips.update() - print(type(win.canvas)) + win.image.update() self.slider.SetValue(win.canvas.scaidx) k = self.scales[self.slider.GetValue()] self.label.SetLabel('%.2f%%'%(k*100)) @@ -95,21 +91,20 @@ def on_fit(self, event): def on_one(self, event): self.on_apply(event) - win = IPy.get_window() + win = self.app.get_img_win() if win is None: return a,b,c,d = win.canvas.winbox win.canvas.scaidx = self.scales.index(1) win.canvas.zoom(1, (a+c)/2, (b+d)/2) - win.ips.update() + win.image.update() self.slider.SetValue(win.canvas.scaidx) self.label.SetLabel('%.2f%%'%100) self.viewport.set_box(win.canvas.conbox, win.canvas.winbox) - def on_handle(self, update=False): + def on_handle(self, loc, update=False): if update: self.on_apply(update) - win = IPy.get_window() + win = self.app.get_img_win() if win is None: return - x, y = self.viewport.GetValue() - win.canvas.center(x, y, 'data') - win.ips.update() + win.canvas.center(*loc, 'data') + win.image.update() self.viewport.set_box(win.canvas.conbox, win.canvas.winbox) \ No newline at end of file diff --git a/sciwx/text/__init__.py b/sciwx/text/__init__.py new file mode 100644 index 00000000..90d079bd --- /dev/null +++ b/sciwx/text/__init__.py @@ -0,0 +1,3 @@ +from .mdutil import md2html +from .mdpad import MDPad, MDFrame, MDNoteBook, MDNoteFrame +from .textpad import TextPad, TextFrame, TextNoteBook, TextNoteFrame \ No newline at end of file diff --git a/imagepy/doc/File/File.md b/sciwx/text/index1.htm similarity index 100% rename from imagepy/doc/File/File.md rename to sciwx/text/index1.htm diff --git a/imagepy/doc/root.md b/sciwx/text/index2.htm similarity index 100% rename from imagepy/doc/root.md rename to sciwx/text/index2.htm diff --git a/sciwx/text/index3.htm b/sciwx/text/index3.htm new file mode 100644 index 00000000..59668291 --- /dev/null +++ b/sciwx/text/index3.htm @@ -0,0 +1,133 @@ + + + + + + + + + + + + + +

Demo Plugin

+

Path: https://github.com/Image-Py/demoplugin

+

Version: 0.1

+

Author: YXDragon

+

Email: yxdragon@imagepy.org

+

Keyword: demo, tutorial

+

Description: a friendly development tutorial.

+

English Document | ĵ

+

This is a demo project to show How to write ImagePy plugin. Including the usage of all kinds of plugin, with document wrote in detail. Developers can take this project as example.

+

Install

+

ImagePy MenuPlugins > Manager > Plugins Manager input demo, and select the Demo Plugin, then click Install/Update. When complete the installing, the user interface would be changed. New plugins' menu, tool, and widget would be loaded in place.

+

06 +

Install DemoPlugin

+

Basic

+

Start here

+
    +
  1. What is plugin
  2. +
  3. Hello Worldmy first plugin
  4. +
  5. Who Are Youinteractive
  6. +
  7. Questionnaireparameter dialog in detail
  8. +
  9. Multi plugin in one file
  10. +
+

Plugin development

+

Markdown: document

+
    +
  1. Markdown Demo
  2. +
+

Macros: serialise existing function

+
    +
  1. Gaussian blur - Invert
  2. +
  3. Coins Segmentation Macros
  4. +
+

Workflow: interactive macros

+
    +
  1. Coins Segment Workflow
  2. +
+

Report: generate report

+
    +
  1. Personal Information
  2. +
  3. Coins Report: report for coins segment
  4. +
  5. Rule of Report design
  6. +
+

Filter: image filter in 2d

+
    +
  1. Invert Demo: without parameter
  2. +
  3. Gaussian Demo: with parameter
  4. +
  5. Filter operating mechanism
  6. +
+

Simple: treat sequence and other attributes

+
    +
  1. Gaussian 3D Demo: filter in 3d
  2. +
  3. Red Lut Demo: operate color lookup table
  4. +
  5. ROI Inflate Demo: operate ROI
  6. +
  7. Unit Demo: set unit and scale
  8. +
  9. Draw Mark Demo: Set Mark
  10. +
  11. Simple operating mechanism
  12. +
+

Table: treat dataframe

+
    +
  1. Generate Table Demo: generate table
  2. +
  3. Sort By Key Demo: sort
  4. +
  5. Table Plot Demo: plot
  6. +
  7. Table operation mechanism
  8. +
+

Free: depend on nothing

+
    +
  1. New Image Demo: creat image
  2. +
  3. About Demo: the about dialog
  4. +
  5. Close Demo: quit program
  6. +
  7. Free operating mechanism
  8. +
+

Tool: mouse interaction

+
    +
  1. Painter Demo: draw with mouse
  2. +
  3. Tool operating mechanism
  4. +
+

Widget: customed panel

+
    +
  1. Widget Demo
  2. +
  3. Widget opterating mechanism
  4. +
+

Plugin Release

+

Function Organization

+
    +
  1. Functional partitioning
  2. +
  3. Set Order
  4. +
+

Plugin project creation

+
    +
  1. Create a plugin project repository
  2. +
  3. Write requirements
  4. +
  5. Write readme
  6. +
  7. Install Plugin
  8. +
+

Release to ImagePy

+
    +
  1. Send Pull Request to ImagePy
  2. +
  3. About the top-level menu
  4. +
+

Write Document

+

Write The Opteration Manual

+

View The Operation Manual

+

Attention

+

User Friendliness

+

Developer Friendliness

+

Communicate Timely

+

This document introduces how to write ImagePy plugin. More questions not exhaustive hereplease post in forum.Image.sc

+ + + \ No newline at end of file diff --git a/imagepy/data/markdown.css b/sciwx/text/markdown.css similarity index 100% rename from imagepy/data/markdown.css rename to sciwx/text/markdown.css diff --git a/sciwx/text/mdpad.py b/sciwx/text/mdpad.py new file mode 100644 index 00000000..fac0361f --- /dev/null +++ b/sciwx/text/mdpad.py @@ -0,0 +1,122 @@ +import wx, os, time, wx.html2 as webview +import os.path as osp +import wx.lib.agw.aui as aui +from markdown import markdown +from .mdutil import md2html + +class MDPad(wx.Panel): + def __init__(self, parent, cont='', url='', title='markdown pad'): + wx.Panel.__init__(self, parent) + sizer = wx.BoxSizer(wx.VERTICAL) + self.wv = webview.WebView.New(self) + self.Bind(webview.EVT_WEBVIEW_TITLE_CHANGED, self.OnWebViewTitleChanged, self.wv) + sizer.Add(self.wv, 1, wx.EXPAND) + self.SetSizer(sizer) + self.num = 0 + if url != '': self.load_url(url) + elif cont!='': self.set_cont(cont) + self.title = title + + def set_cont(self, value, url=''): + # self.wv.SetPage(md2html(value), url) + # I do not know why use SetPage the js would not run, So I write a file here + here = osp.split(osp.abspath(__file__)) + for n in range(1,10): + path = osp.join(here[0], 'index%s.htm'%n) + if not osp.exists(path):break + self.num = n + with open(path, 'w') as f: + f.write(md2html(value)) + self.load_url(path) + + def load_url(self, url): + self.wv.LoadURL(url) + + def OnWebViewTitleChanged(self, evt): + if evt.GetString() == 'about:blank': return + if evt.GetString() == 'http:///': return + here = osp.split(osp.abspath(__file__)) + path = osp.join(here[0], 'index%s.htm'%self.num) + if osp.exists(path): os.remove(path) + +class MDFrame(wx.Frame): + def __init__(self, parent, title='MarkDownFrame', cont='', url=''): + wx.Frame.__init__ (self, parent, id = wx.ID_ANY, title = title, size = wx.Size(500,500)) + cont = '\n'.join([i.strip() for i in cont.split('\n')]) + self.mdpad = MDPad(self, cont, url, title) + self.set_cont, self.load_url = self.mdpad.set_cont, self.mdpad.load_url + self.Bind( wx.EVT_IDLE, self.on_idle) + + def on_idle(self, event): + if self.GetTitle()!=self.mdpad.title: + self.SetTitle(self.mdpad.title) + +class MDNoteBook(wx.lib.agw.aui.AuiNotebook): + def __init__(self, parent): + wx.lib.agw.aui.AuiNotebook.__init__( self, parent, wx.ID_ANY, + wx.DefaultPosition, wx.DefaultSize, wx.lib.agw.aui.AUI_NB_DEFAULT_STYLE ) + self.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_valid) + self.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close) + self.SetArtProvider(aui.AuiSimpleTabArt()) + self.Bind( wx.EVT_IDLE, self.on_idle) + + def on_idle(self, event): + for i in range(self.GetPageCount()): + title = self.GetPage(i).title + if self.GetPageText(i) != title: + self.SetPageText(i, title) + + def page(self, i=None): + if not i is None: return self.GetPage(i) + else: return self.GetCurrentPage() + + def set_background(self, img): + self.GetAuiManager().SetArtProvider(ImgArtProvider(img)) + + def add_page(self, mdpanel=None): + if mdpanel is None: mdpanel = MDPad(self) + self.AddPage(mdpanel, 'markdown', True, wx.NullBitmap ) + return mdpanel + + def set_title(self, panel, title): + self.SetPageText(self.GetPageIndex(panel), title) + + def on_valid(self, event): pass + + def on_close(self, event): pass + +class MDNoteFrame(wx.Frame): + def __init__(self, parent, title='MarkDownNoteFrame'): + wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, + title = title, + pos = wx.DefaultPosition, + size = wx.Size( 500,500), + style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + sizer = wx.BoxSizer(wx.VERTICAL) + self.notebook = MDNoteBook(self) + self.page = self.notebook.page + sizer.Add( self.notebook, 1, wx.EXPAND |wx.ALL, 0 ) + self.SetSizer(sizer) + self.add_page = self.notebook.add_page + self.Layout() + +if __name__ == '__main__': + ''' + app = wx.App() + frame = wx.Frame(None) + mnb = MDNoteBook(frame) + mdpanel1 = mnb.add_page('markdown1') + mdpanel1.set_cont('abc') + mdpanel2 = mnb.add_page('markdown2') + mdpanel2.set_cont('def') + frame.Show() + app.MainLoop() + ''' + app = wx.App() + mnf = MDNoteFrame(None) + mdpanel1 = mnf.add_page('markdown1') + mdpanel1.set_cont('abc') + mdpanel2 = mnf.add_page('markdown2') + mdpanel2.set_cont('def') + mnf.Show() + app.MainLoop() diff --git a/sciwx/text/mdutil.py b/sciwx/text/mdutil.py new file mode 100644 index 00000000..3a58d9a2 --- /dev/null +++ b/sciwx/text/mdutil.py @@ -0,0 +1,38 @@ +from markdown import markdown +import os.path as osp + +def md2html(mdstr, css=None): + exts = ['markdown.extensions.extra', 'markdown.extensions.codehilite', + 'markdown.extensions.tables','markdown.extensions.toc', 'mdx_math'] + html = ''' + + + + + + + + + + + + + %s + + + ''' + css = css or osp.join(osp.split(osp.abspath(__file__))[0], 'markdown.css') + return html % (css, markdown(mdstr, extensions=exts)) + +if __name__ == '__main__': + print(md2html('#abc')) diff --git a/sciwx/text/textpad.py b/sciwx/text/textpad.py new file mode 100644 index 00000000..5daddab0 --- /dev/null +++ b/sciwx/text/textpad.py @@ -0,0 +1,217 @@ +import wx, wx.lib.agw.aui as aui + +class TextPad(wx.Panel): + def __init__(self, parent, cont='', title='no name'): + wx.Panel.__init__(self, parent, size=(500,300)) + self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_3DLIGHT ) ) + self.title = title + + sizer = wx.BoxSizer( wx.VERTICAL ) + self.text= wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, + wx.DefaultPosition, wx.DefaultSize, wx.TE_MULTILINE ) + self.text.SetValue(cont) + sizer.Add( self.text, 1, wx.ALL|wx.EXPAND, 1 ) + self.SetSizer( sizer ) + + self.Bind(wx.EVT_RIGHT_DOWN,self.OnRClick) + + def OnOpen(self,event): + dialog=wx.FileDialog(self,'wxpython Notebook(o)',style=wx.FD_OPEN) + if dialog.ShowModal()==wx.ID_OK: + self.title=dialog.GetPath() + title=open(self.title) + self.text.write(title.read()) + title.close() + dialog.Destroy() + + def OnSave(self,event): + if self.title=='': + dialog=wx.FileDialog(self,'wxpython Notebook(s)',style=wx.FD_SAVE) + if dialog.ShowModal()==wx.ID_OK: + self.filtitlee=dialog.GetPath() + self.text.SaveFile(self.title) + dialog.Destroy() + else: + self.text.SaveFile(self.fititlele) + + def OnSaveAs(self,event): + dialog=wx.FileDialog(self,'wxpython notebook',style=wx.FD_SAVE) + if dialog.ShowModal()==wx.ID_OK: + self.title=dialog.GetPath() + self.text.SaveFile(self.title) + dialog.Destroy() + + def OnAbout(self,event): + wx.MessageBox('Text Log Window!','ImagePy',wx.OK) + + def OnRClick(self,event): + pos=(event.GetX(),event.GetY()) + self.panel.PopupMenu(self.menu.edit,pos) + + def OnUndo(self,event): self.text.Undo() + + def OnRedo(self,event): self.text.Redo() + + def OnCut(self,event): self.text.Cut() + + def OnCopy(self,event): self.text.Copy() + + def OnPaste(self,event): self.text.Paste() + + def OnSelectAll(self,event): self.text.SelectAll() + + def append(self, cont): + self.text.AppendText(cont+'\r\n') + +class TextFrame(wx.Frame): + def __init__(self, parent, title='no name', cont=''): + wx.Frame.__init__(self, parent, title=title, size=(500,300)) + self.title = title + self.textpad = TextPad(self, cont, title) + self.append = self.textpad.append + self.append(cont) + ### Create menus (name:event) k-v pairs + menus = [ + ## File + ('File(&F)',[('Open', self.textpad.OnOpen), + ('Save', self.textpad.OnSave), + ('Save as', self.textpad.OnSaveAs), + ('-'), + ('Exit', self.OnClose) + ]), + ## Edit + ('Edit(&E)', [ ('Undo', self.textpad.OnUndo), + ('Redo', self.textpad.OnRedo), + ('-'), + ('Cut', self.textpad.OnCut), + ('Copy', self.textpad.OnCopy), + ('Paste', self.textpad.OnPaste), + ('-'), + ('All', self.textpad.OnSelectAll) + ]), + ## Help + ('Help(&H)', [('About', self.textpad.OnAbout)]) + ] + + ### Bind menus with the corresponding events + self.menuBar=wx.MenuBar() + for menu in menus: + m = wx.Menu() + for item in menu[1]: + if item[0]=='-': + m.AppendSeparator() + else: + i = m.Append(-1, item[0]) + self.Bind(wx.EVT_MENU,item[1], i) + self.menuBar.Append(m,menu[0]) + self.SetMenuBar(self.menuBar) + self.Bind(wx.EVT_CLOSE, self.OnClosing) + + def OnClose(self,event): + self.Destroy() + + def OnClosing(self, event): + event.Skip() + +class TextNoteBook(wx.lib.agw.aui.AuiNotebook): + def __init__(self, parent): + wx.lib.agw.aui.AuiNotebook.__init__( self, parent, wx.ID_ANY, + wx.DefaultPosition, wx.DefaultSize, wx.lib.agw.aui.AUI_NB_DEFAULT_STYLE ) + self.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_valid) + self.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close) + self.Bind( wx.EVT_IDLE, self.on_idle) + self.SetArtProvider(aui.AuiSimpleTabArt()) + + def on_idle(self, event): + for i in range(self.GetPageCount()): + title = self.GetPage(i).title + if self.GetPageText(i) != title: + self.SetPageText(i, title) + + def textpad(self, i=None): + if not i is None: return self.GetPage(i) + else: return self.GetCurrentPage() + + def set_background(self, img): + self.GetAuiManager().SetArtProvider(ImgArtProvider(img)) + + def add_notepad(self, textpad=None): + if textpad is None: textpad = TextPad(self) + self.AddPage(textpad, 'Text', True, wx.NullBitmap ) + return textpad + + def set_title(self, panel, title): + self.SetPageText(self.GetPageIndex(panel), title) + + def on_valid(self, event): pass + + def on_close(self, event): pass + +class TextNoteFrame(wx.Frame): + def __init__(self, parent, title='TextPadBookFrame'): + wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, + title = title, + pos = wx.DefaultPosition, + size = wx.Size( 500, 500 ), + style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + sizer = wx.BoxSizer(wx.VERTICAL) + self.notebook = TextNoteBook(self) + self.textpad = self.notebook.textpad + sizer.Add( self.notebook, 1, wx.EXPAND |wx.ALL, 0 ) + self.SetSizer(sizer) + self.add_notepad = self.notebook.add_notepad + self.Layout() + + ### Create menus (name:event) k-v pairs + menus = [ + ## File + ('File(&F)',[('Open', lambda e: self.textpad().OnOpen(e)), + ('Save', lambda e: self.textpad().OnSave(e)), + ('Save as', lambda e: self.textpad().OnSaveAs(e)), + ('-'), + ('Exit', self.OnClose) + ]), + ## Edit + ('Edit(&E)', [ ('Undo', lambda e: self.textpad().OnUndo(e)), + ('Redo', lambda e: self.textpad().OnRedo(e)), + ('-'), + ('Cut', lambda e: self.textpad().OnCut(e)), + ('Copy', lambda e: self.textpad().OnCopy(e)), + ('Paste', lambda e: self.textpad().OnPaste(e)), + ('-'), + ('All', lambda e: self.textpad().OnSelectAll(e)) + ]), + ## Help + ('Help(&H)', [('About', lambda e: self.textpad().OnAbout(e))]) + ] + + ### Bind menus with the corresponding events + self.menuBar=wx.MenuBar() + for menu in menus: + m = wx.Menu() + for item in menu[1]: + if item[0]=='-': + m.AppendSeparator() + else: + i = m.Append(-1, item[0]) + self.Bind(wx.EVT_MENU,item[1], i) + self.menuBar.Append(m,menu[0]) + self.SetMenuBar(self.menuBar) + self.Bind(wx.EVT_CLOSE, self.OnClosing) + + def OnClose(self,event): + self.Destroy() + + def OnClosing(self, event): + event.Skip() + +if __name__ == '__main__': + app = wx.App() + npbf = TextNoteFrame(None) + note1 = npbf.add_notepad() + note1.append('abc') + note1 = npbf.add_notepad() + note1.append('def') + npbf.Show() + app.MainLoop() + diff --git a/sciwx/widgets/__init__.py b/sciwx/widgets/__init__.py new file mode 100644 index 00000000..d39955d8 --- /dev/null +++ b/sciwx/widgets/__init__.py @@ -0,0 +1,11 @@ +from .paradialog import ParaDialog, get_para +from .cmappanel import CMapPanel +from .colormap import CMapSelPanel, CMapSelCtrl +from .curvepanel import CurvePanel +from .histpanel import HistPanel +from .normal import * +from .toolbar import ToolBar +from .menubar import MenuBar +from .viewport import ViewPort +from .choicebook import ChoiceBook +from . import util \ No newline at end of file diff --git a/sciwx/widgets/advanced.py b/sciwx/widgets/advanced.py new file mode 100644 index 00000000..451e07a2 --- /dev/null +++ b/sciwx/widgets/advanced.py @@ -0,0 +1,28 @@ +from . normal import Choice, Choices +from . colormap import CMapSelPanel +# from ...core.manager import ImageManager, TableManager, ColorManager + +class ImageList(Choice): + def __init__(self, parent, title, unit, app=None): + Choice.__init__(self, parent, app.get_img_name(), str, title, unit) + +class TableList(Choice): + def __init__(self, parent, title, unit, app=None): + Choice.__init__(self, parent, app.get_tab_name(), str, title, unit) + +class TableField(Choice): + def __init__(self, parent, title, unit, app=None): + Choice.__init__(self, parent, ['None'] + list(app.get_tab().data.columns), lambda x:x, title, unit) + +class TableFields(Choices): + def __init__(self, parent, title, app=None): + self.tps = app.get_tab() + Choices.__init__(self, parent, app.get_tab().data.columns, title) + + def SetValue(self, value): + Choices.SetValue(self, self.tps.colmsk) + +class ColorMap(CMapSelPanel): + def __init__(self, parent, title, app=None): + CMapSelPanel.__init__(self, parent, title) + self.SetItems(ColorManager.luts) \ No newline at end of file diff --git a/sciwx/widgets/choicebook.py b/sciwx/widgets/choicebook.py new file mode 100644 index 00000000..d607c122 --- /dev/null +++ b/sciwx/widgets/choicebook.py @@ -0,0 +1,27 @@ +import wx + +class ChoiceBook(wx.ScrolledWindow): + def __init__(self, parent): + wx.ScrolledWindow.__init__(self, parent, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.HSCROLL|wx.VSCROLL ) + self.SetSizer(wx.BoxSizer( wx.VERTICAL )) + + def add_wgts(self, name, wgts): + book = wx.Choicebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.CHB_DEFAULT ) + for name, wgt in wgts: + book.AddPage(wgt(book, self.GetParent()), name, False ) + self.GetSizer().Add( book, 0, wx.EXPAND |wx.ALL, 0 ) + self.Layout() + self.GetSizer().Fit(self) + + def load(self, data): + for name, wgts in data[1]: + self.add_wgts(name, wgts) + +if __name__ == '__main__': + app = wx.App() + frame = wx.Frame(None) + book = ChoiceBook(frame) + book.load(('widgets', [('panels', [('A', wx.Panel), ('B', wx.Panel)]), + ('panels2', [('A', wx.Panel), ('B', wx.Panel)])])) + frame.Show() + app.MainLoop() diff --git a/imagepy/ui/widgets/cmappanel.py b/sciwx/widgets/cmappanel.py similarity index 97% rename from imagepy/ui/widgets/cmappanel.py rename to sciwx/widgets/cmappanel.py index 4ce2c5ed..8c79f033 100644 --- a/imagepy/ui/widgets/cmappanel.py +++ b/sciwx/widgets/cmappanel.py @@ -3,8 +3,6 @@ from numpy.linalg import norm from scipy import interpolate -if sys.version_info[0]==2:memoryview=np.getbuffer - class CMapPanel(wx.Panel): """ HistCanvas: diverid from wx.core.Panel """ def __init__(self, parent ): @@ -145,20 +143,19 @@ def draw(self): brushes = [wx.Brush(i[1:]) for i in self.pts] dc.DrawPolygonList(polys,brushes=brushes) - def handle_(self):pass def set_handle(self, handle):self.handle = handle - def SetValue(self, value):pass + def SetValue(self, value):self.pts = value def GetValue(self): return sorted(self.pts) if __name__ == '__main__': - app = wx.PySimpleApp() + app = wx.App() frame = wx.Frame(None) hist = CMapPanel(frame) + hist.set_hist(np.random.rand(256)+2) frame.Fit() frame.Show(True) - hist.set_hist(np.random.rand(256)+2) - app.MainLoop() \ No newline at end of file + app.MainLoop() diff --git a/imagepy/ui/widgets/colormap.py b/sciwx/widgets/colormap.py similarity index 85% rename from imagepy/ui/widgets/colormap.py rename to sciwx/widgets/colormap.py index b5b419af..5a8ab817 100644 --- a/imagepy/ui/widgets/colormap.py +++ b/sciwx/widgets/colormap.py @@ -2,7 +2,6 @@ import wx.adv import sys, wx -if sys.version_info[0]==2:memoryview=np.getbuffer class CMapSelCtrl(wx.adv.OwnerDrawnComboBox): def __init__(self, parent): wx.adv.OwnerDrawnComboBox.__init__(self, parent, choices=[], @@ -10,11 +9,7 @@ def __init__(self, parent): def SetItems(self, kvs): self.Clear() - ks, vs = list(kvs.keys()), list(kvs.values()) - if 'Grays' in ks: - i = ks.index('Grays') - ks.insert(0, ks.pop(i)) - vs.insert(0, vs.pop(i)) + ks, vs = [i[0] for i in kvs], [i[1] for i in kvs] self.AppendItems(ks) self.Select(0) self.ks, self.vs = ks, vs @@ -66,10 +61,8 @@ def OnDrawBackground(self, dc, rect, item, flags): def OnMeasureItem(self, item): return 30 # Simply demonstrate the ability to have variable-height items - if item & 1: - return 36 - else: - return 24 + if item & 1: return 36 + else: return 24 # Overridden from OwnerDrawnComboBox. Callback for item width, or # -1 for default/undetermined @@ -91,8 +84,20 @@ def __init__( self, parent, title): self.SetValue = self.ctrl.SetValue self.SetItems = self.ctrl.SetItems self.ctrl.Bind( wx.EVT_COMBOBOX, self.on_sel) + self.on_select = print + self.Fit() - def Bind(self, z, f): self.f = f + def Bind(self, event, f): self.on_select = f def on_sel(self, event): - self.f(self) \ No newline at end of file + self.on_select(self.GetValue()) + +if __name__ == '__main__': + app = wx.App() + frame = wx.Frame(None) + cmapsel = CMapSelPanel(frame, 'color map') + cmap = np.arange(256*3, dtype=np.uint8).reshape((3,-1)).T + cmapsel.SetItems({'gray':cmap}) + frame.Fit() + frame.Show(True) + app.MainLoop() diff --git a/imagepy/ui/widgets/curvepanel.py b/sciwx/widgets/curvepanel.py similarity index 94% rename from imagepy/ui/widgets/curvepanel.py rename to sciwx/widgets/curvepanel.py index e1455bd0..7208e6c1 100644 --- a/imagepy/ui/widgets/curvepanel.py +++ b/sciwx/widgets/curvepanel.py @@ -3,11 +3,9 @@ from numpy.linalg import norm from scipy import interpolate -if sys.version_info[0]==2:memoryview=np.getbuffer - class CurvePanel(wx.Panel): """ HistCanvas: diverid from wx.core.Panel """ - def __init__(self, parent, hist=None, l=255): + def __init__(self, parent, hist=None, l=255, app=None): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size(l+25, l+25), style = wx.TAB_TRAVERSAL ) @@ -61,7 +59,7 @@ def on_ld(self, event): self.pts.append((x, 255-y)) self.idx = len(self.pts)-1 self.update() - self.handle(self) + self.on_curve(self.GetValue()) def on_lu(self, event): self.idx = -1 @@ -75,7 +73,7 @@ def on_rd(self, event): del self.pts[self.idx] self.idx = -1 self.update() - self.handle(self) + self.on_curve(self.GetValue()) def on_mv(self, event): x = (event.GetX()-self.offset[0])/self.k @@ -91,7 +89,7 @@ def on_mv(self, event): y = np.clip(y, 0, 255) self.pts[self.idx] = (x, 255-y) self.update() - self.handle(self) + self.on_curve(self.GetValue()) def on_paint(self, event): @@ -151,9 +149,9 @@ def draw(self): dc.DrawBitmap(bmp, -15+ox, 0+oy) dc.DrawRectangle(-15+ox, 0+oy, 10, self.l+1) - def handle(self):pass + def on_curve(self, event): print(event) - def Bind(self, z, handle):self.handle = handle + def Bind(self, event, f):self.on_curve = f def SetValue(self, value=None): print('here', value) @@ -165,10 +163,10 @@ def SetValue(self, value=None): def GetValue(self): return sorted(self.pts) if __name__ == '__main__': - app = wx.PySimpleApp() + app = wx.App() frame = wx.Frame(None) hist = CurvePanel(frame, l=255) + hist.set_hist(np.random.rand(256)+2) frame.Fit() frame.Show(True) - hist.set_hist(np.random.rand(256)+2) - app.MainLoop() \ No newline at end of file + app.MainLoop() diff --git a/imagepy/ui/widgets/histpanel.py b/sciwx/widgets/histpanel.py similarity index 60% rename from imagepy/ui/widgets/histpanel.py rename to sciwx/widgets/histpanel.py index 24fbd23e..4f3bd48d 100644 --- a/imagepy/ui/widgets/histpanel.py +++ b/sciwx/widgets/histpanel.py @@ -1,21 +1,22 @@ import wx import numpy as np -class HistCanvas(wx.Panel): +class HistPanel(wx.Panel): """ HistCanvas: diverid from wx.core.Panel """ - def __init__(self, parent, hist=None): + def __init__(self, parent, hist=None, size=(256, 80), app=None): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, - pos = wx.DefaultPosition, size = wx.Size(256,81), + pos = wx.DefaultPosition, size = (size[0], size[1]+1), style = wx.TAB_TRAVERSAL ) self.init_buf() self.hist = None + self.w, self.h = size if not hist is None: self.SetValue(hist) self.dirty = False - self.x1, self.x2 = 0, 255 + self.x1, self.x2 = 0, self.w-1 self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.EVT_IDLE, self.on_idle) self.Bind(wx.EVT_PAINT, self.on_paint) - self.Bind = lambda z, x:0 + # self.Bind = lambda z, x:0 def update(self): self.dirty = True @@ -36,8 +37,8 @@ def on_paint(self, event): wx.BufferedPaintDC(self, self.buffer) def SetValue(self, hist): - self.hist = (hist*80.0/hist.max()) - self.logh = (np.log(self.hist+1.0))*(80/(np.log(81))) + self.hist = (hist*self.h/hist.max()) + self.logh = (np.log(self.hist+1.0))*(self.h/(np.log(self.h+1))) self.update() def set_lim(self, x1, x2): @@ -51,15 +52,24 @@ def draw(self): # w, h = self.GetClientSize() # the main draw process - print("drawing histogram") if not self.hist is None: + hist = self.hist[np.linspace(0, len(self.hist)-1, self.w, dtype=np.int16)] dc.SetPen(wx.Pen((200,200,200), width=1, style=wx.SOLID)) - for i in range(256): - dc.DrawLine(i,80,i,80-self.logh[i]) + for i in range(self.w): + dc.DrawLine(i,self.h,i,self.h-self.logh[i]) dc.SetPen(wx.Pen((100,100,100), width=1, style=wx.SOLID)) - for i in range(256): - dc.DrawLine(i,80,i,80-self.hist[i]) + for i in range(self.w): + dc.DrawLine(i,self.h,i,self.h-self.hist[i]) dc.SetPen(wx.Pen((0,0,0), width=1, style=wx.SOLID)) - dc.DrawLine(self.x1, 80, self.x2, 0) - dc.DrawLines([(0,0),(255,0),(255,80),(0,80),(0,0)]) \ No newline at end of file + dc.DrawLine(self.x1, self.h, self.x2, 0) + dc.DrawLines([(0,0),(self.w-1,0),(self.w-1,self.h),(0,self.h),(0,0)]) + +if __name__ == '__main__': + app = wx.App() + frame = wx.Frame(None) + hist = HistPanel(frame) + hist.SetValue(np.random.rand(256)) + frame.Fit() + frame.Show(True) + app.MainLoop() diff --git a/sciwx/widgets/menubar.py b/sciwx/widgets/menubar.py new file mode 100644 index 00000000..04f1c32f --- /dev/null +++ b/sciwx/widgets/menubar.py @@ -0,0 +1,59 @@ +import wx + +class MenuBar(wx.MenuBar): + def __init__(self, app): + wx.MenuBar.__init__(self) + self.app = app + app.SetMenuBar(self) + + def parse(self, ks, vs, pt): + if isinstance(vs, list): + menu = wx.Menu() + for kv in vs: + if kv == '-': menu.AppendSeparator() + else: self.parse(*kv, menu) + pt.Append(1, ks, menu) + else: + item = wx.MenuItem(pt, -1, ks) + f = lambda e, p=vs: p().start(self.app) + self.Bind(wx.EVT_MENU, f, item) + pt.Append(item) + + def Append(self, id, item, menu): + wx.MenuBar.Append(self, menu, item) + + def load(self, data): + for k,v in data[1]: self.parse(k, v, self) + + def on_menu(self, event): + print('here') + + +if __name__ == '__main__': + class P: + def __init__(self, name): + self.name = name + + def start(self): + print(self.name) + + def __call__(self): + return self + + data = ('menu', [ + ('File', [('Open', P('O')), + '-', + ('Close', P('C'))]), + ('Edit', [('Copy', P('C')), + ('A', [('B', P('B')), + ('C', P('C'))]), + ('Paste', P('P'))])]) + + app = wx.App() + frame = wx.Frame(None) + menubar = MenuBar() + menubar.load(data) + frame.SetMenuBar(menubar) + frame.Show() + app.MainLoop() + diff --git a/imagepy/ui/widgets/normal.py b/sciwx/widgets/normal.py similarity index 89% rename from imagepy/ui/widgets/normal.py rename to sciwx/widgets/normal.py index a27194e4..9b854944 100644 --- a/imagepy/ui/widgets/normal.py +++ b/sciwx/widgets/normal.py @@ -3,7 +3,7 @@ class NumCtrl(wx.Panel): """NumCtrl: diverid from wx.core.TextCtrl """ - def __init__(self, parent, rang, accury, title, unit): + def __init__(self, parent, rang, accury, title, unit, app=None): wx.Panel.__init__(self, parent) sizer = wx.BoxSizer( wx.HORIZONTAL ) self.prefix = lab_title = wx.StaticText( self, wx.ID_ANY, title, @@ -53,8 +53,7 @@ def GetValue(self): return num class TextCtrl(wx.Panel): - """NumCtrl: diverid from wx.core.TextCtrl """ - def __init__(self, parent, title, unit): + def __init__(self, parent, title, unit, app=None): wx.Panel.__init__(self, parent) sizer = wx.BoxSizer( wx.HORIZONTAL ) self.prefix = lab_title = wx.StaticText( self, wx.ID_ANY, title, @@ -87,8 +86,7 @@ def GetValue(self): return self.ctrl.GetValue() class ColorCtrl(wx.Panel): - """ColorCtrl: deverid fron wx.coreTextCtrl""" - def __init__(self, parent, title, unit): + def __init__(self, parent, title, unit, app=None): wx.Panel.__init__(self, parent) sizer = wx.BoxSizer( wx.HORIZONTAL ) self.prefix = lab_title = wx.StaticText( self, wx.ID_ANY, title, @@ -104,13 +102,18 @@ def __init__(self, parent, title, unit): self.SetSizer(sizer) self.ctrl.Bind(wx.EVT_KEY_UP, self.ontext) - self.ctrl.Bind( wx.EVT_LEFT_DOWN, self.oncolor) + self.ctrl.Bind( wx.EVT_LEFT_DCLICK, self.oncolor) def Bind(self, z, f): self.f = f def ontext(self, event): - print('ColorCtrl') + self.f(self) + if self.GetValue()==None: + self.ctrl.SetBackgroundColour((255,255,255)) + else: + self.ctrl.SetBackgroundColour(self.GetValue()) + self.Refresh() def oncolor(self, event): rst = None @@ -129,11 +132,14 @@ def SetValue(self, color): self.ctrl.SetValue(des) def GetValue(self): - return self.ctrl.GetBackgroundColour().Get(False) + rgb = self.ctrl.GetValue() + if len(rgb)!=7 or rgb[0]!='#': return None + try: rgb = int(rgb[1:], 16) + except: return None + return wx.Colour(rgb).Get(False)[::-1] class PathCtrl(wx.Panel): - """ColorCtrl: deverid fron wx.coreTextCtrl""" - def __init__(self, parent, title, filt): + def __init__(self, parent, title, filt, app=None): wx.Panel.__init__(self, parent) sizer = wx.BoxSizer( wx.HORIZONTAL ) self.prefix = lab_title = wx.StaticText( self, wx.ID_ANY, title, @@ -172,8 +178,7 @@ def GetValue(self): return self.ctrl.GetValue() class Choice(wx.Panel): - """ColorCtrl: deverid fron wx.coreTextCtrl""" - def __init__(self, parent, choices, tp, title, unit): + def __init__(self, parent, choices, tp, title, unit, app=None): wx.Panel.__init__(self, parent) self.tp, self.choices = tp, choices sizer = wx.BoxSizer( wx.HORIZONTAL ) @@ -209,7 +214,7 @@ def GetValue(self): return self.tp(self.choices[self.ctrl.GetSelection()]) class AnyType( wx.Panel ): - def __init__( self, parent, title, types = ['Int', 'Float', 'Str']): + def __init__( self, parent, title, app=None): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size(-1, -1), style = wx.TAB_TRAVERSAL ) sizer = wx.BoxSizer( wx.HORIZONTAL ) @@ -220,7 +225,7 @@ def __init__( self, parent, title, types = ['Int', 'Float', 'Str']): self.txt_value = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) sizer.Add( self.txt_value, 1, wx.ALIGN_CENTER|wx.ALL, 5 ) - com_typeChoices = types + com_typeChoices = ['Int', 'Float', 'Str'] self.postfix = self.com_type = wx.ComboBox( self, wx.ID_ANY, 'Float', wx.DefaultPosition, wx.DefaultSize, com_typeChoices, 0 ) sizer.Add( self.com_type, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) @@ -273,7 +278,7 @@ def on_type( self, event ): self.Refresh() class Choices(wx.Panel): - def __init__( self, parent, choices, title): + def __init__( self, parent, choices, title, app=None): self.choices = list(choices) wx.Panel.__init__(self, parent) @@ -303,8 +308,7 @@ def SetValue(self, value): self.choices.index(i) for i in value if i in self.choices]) class FloatSlider(wx.Panel): - - def __init__( self, parent, rang, accury, title): + def __init__( self, parent, rang, accury, title, unit='', app=None): self.linux = platform.system() == 'Linux' wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size(-1,-1), style = wx.TAB_TRAVERSAL ) sizer = wx.BoxSizer( wx.VERTICAL ) @@ -313,18 +317,24 @@ def __init__( self, parent, rang, accury, title): subsizer = wx.BoxSizer( wx.HORIZONTAL ) self.lab_min = wx.StaticText( self, wx.ID_ANY, '0', wx.DefaultPosition, wx.DefaultSize, 0 ) self.lab_min.Wrap( -1 ) - subsizer.Add( self.lab_min, 0, wx.BOTTOM|wx.LEFT|wx.ALIGN_CENTER, 5 ) - subsizer.AddStretchSpacer(prop=1) + subsizer.Add( self.lab_min, 1, wx.BOTTOM|wx.LEFT|wx.ALIGN_CENTER, 5 ) + #subsizer.AddStretchSpacer(prop=1) + self.lab_title = wx.StaticText( self, wx.ID_ANY, title, wx.DefaultPosition, wx.DefaultSize, 0 ) + self.lab_title.Wrap( -1 ) + subsizer.Add( self.lab_title, 0, wx.ALIGN_CENTER|wx.BOTTOM|wx.RIGHT, 5 ) self.text = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(50,-1), 0 ) subsizer.Add( self.text, 0, wx.BOTTOM|wx.ALIGN_CENTER, 5 ) self.spin = wx.SpinButton( self, wx.ID_ANY, wx.DefaultPosition, wx.Size([20,-1][self.linux],-1), [0, wx.SP_HORIZONTAL][self.linux]) self.spin.SetRange(0, 255) self.spin.SetValue(128) subsizer.Add( self.spin, 0, wx.ALIGN_CENTER|wx.BOTTOM|wx.EXPAND, 5 ) - subsizer.AddStretchSpacer(prop=1) - self.lab_max = wx.StaticText( self, wx.ID_ANY, '0', wx.DefaultPosition, wx.DefaultSize, 0 ) + self.lab_unit = wx.StaticText( self, wx.ID_ANY, unit, wx.DefaultPosition, wx.DefaultSize, 0 ) + self.lab_unit.Wrap( -1 ) + subsizer.Add( self.lab_unit, 0, wx.ALIGN_CENTER|wx.BOTTOM|wx.LEFT, 5 ) + #subsizer.AddStretchSpacer(prop=1) + self.lab_max = wx.StaticText( self, wx.ID_ANY, '0', wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT ) self.lab_max.Wrap( -1 ) - subsizer.Add( self.lab_max, 0, wx.ALIGN_CENTER|wx.BOTTOM|wx.RIGHT, 5 ) + subsizer.Add( self.lab_max, 1, wx.ALIGN_CENTER|wx.BOTTOM|wx.RIGHT, 5 ) sizer.Add( subsizer, 0, wx.EXPAND, 0 ) @@ -391,7 +401,7 @@ def GetValue(self): return num class Label(wx.Panel): - def __init__(self, parent, title): + def __init__(self, parent, title, app=None): wx.Panel.__init__(self, parent) sizer = wx.BoxSizer(wx.VERTICAL) lab_title = wx.StaticText( self, wx.ID_ANY, title, @@ -405,7 +415,7 @@ def SetValue(self, v): pass def GetValue(self, v): pass class Check(wx.Panel): - def __init__(self, parent, title): + def __init__(self, parent, title, app=None): wx.Panel.__init__(self, parent) sizer = wx.BoxSizer(wx.VERTICAL) check = wx.CheckBox(self, -1, title) @@ -423,4 +433,4 @@ def Bind(self, z, f): self.f = f app = wx.App() frame = wx.Frame(None) frame.Show() - app.MainLoop() \ No newline at end of file + app.MainLoop() diff --git a/imagepy/ui/panelconfig.py b/sciwx/widgets/paradialog.py similarity index 64% rename from imagepy/ui/panelconfig.py rename to sciwx/widgets/paradialog.py index 36f1764d..bbf4cd15 100644 --- a/imagepy/ui/panelconfig.py +++ b/sciwx/widgets/paradialog.py @@ -1,37 +1,36 @@ -# -*- coding: utf-8 -*- -# ConfigPanel used for parameters setting import wx, platform -from ..core.manager import ImageManager, WindowsManager, TableManager -from .widgets import * -import weakref +from .normal import * +from .advanced import * +from .histpanel import HistPanel +from .curvepanel import CurvePanel +from .threpanel import ThresholdPanel widgets = { 'ctrl':None, 'slide':FloatSlider, int:NumCtrl, 'path':PathCtrl, - float:NumCtrl, 'lab':Label, bool:Check, str:TextCtrl, - list:Choice, 'img':ImageList, 'tab':TableList, 'color':ColorCtrl, - 'any':AnyType, 'chos':Choices, 'fields':TableFields, - 'field':TableField, 'hist':HistCanvas, 'cmap':ColorMap} + float:NumCtrl, 'lab':Label, bool:Check, str:TextCtrl, list:Choice, + 'color':ColorCtrl, 'any':AnyType, 'chos':Choices, 'hist':ThresholdPanel, + 'curve':CurvePanel, 'img':ImageList, 'tab':TableList, 'field':TableField, 'fields':TableFields} + +def add_widget(key, value): widgets[key] = value class ParaDialog (wx.Dialog): def __init__( self, parent, title): wx.Dialog.__init__ (self, parent, -1, title, style = wx.DEFAULT_DIALOG_STYLE) self.lst = wx.BoxSizer( wx.VERTICAL ) self.tus = [] - self.on_ok = self.on_cancel = self.on_help = None + self.handle = print self.ctrl_dic = {} boxBack = wx.BoxSizer() boxBack.Add(self.lst, 0, wx.ALL, 10) self.SetSizer( boxBack ) self.Layout() - #self.handle = self.handle_ def commit(self, state): self.Destroy() if state=='ok' and self.on_ok:self.on_ok() if state=='cancel' and self.on_cancel:self.on_cancel() - def add_confirm(self, modal=True): - # self.lst.AddStretchSpacer(1) + def add_confirm(self, modal): sizer = wx.BoxSizer( wx.HORIZONTAL ) self.btn_ok = wx.Button( self, wx.ID_OK, 'OK', wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) sizer.Add( self.btn_ok, 0, wx.ALIGN_RIGHT|wx.ALL, 5 ) @@ -46,21 +45,19 @@ def add_confirm(self, modal=True): if not modal: self.btn_ok.Bind( wx.EVT_BUTTON, lambda e:self.commit('ok')) self.btn_cancel.Bind( wx.EVT_BUTTON, lambda e:self.commit('cancel')) - #self.lst.Add() - def init_view(self, items, para, preview=False, modal = True): - self.para = para + def init_view(self, items, para, preview=False, modal=True, app=None): + self.para, self.modal = para, modal for item in items: - self.add_ctrl_(widgets[item[0]], item[1], item[2:]) - if preview:self.add_ctrl_(Check, 'preview', ('preview',)) + self.add_ctrl_(widgets[item[0]], item[1], item[2:], app=app) + if preview:self.add_ctrl_(Check, 'preview', ('preview',), app=app) self.reset(para) self.add_confirm(modal) self.pack() self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy) print('bind close') - def OnDestroy( self, event ): self.set_handle(None) self.on_cancel = self.on_ok = self.on_help = None @@ -69,8 +66,9 @@ def OnDestroy( self, event ): def parse(self, para) : self.add_ctrl_(widgets[para[0]], *para[1:]) - def add_ctrl_(self, Ctrl, key, p): - ctrl = Ctrl(self, *p) + def add_ctrl_(self, Ctrl, key, p, app=None): + ctrl = Ctrl(self, *p, app=app) + if not p[0] is None: self.ctrl_dic[key] = ctrl if hasattr(ctrl, 'Bind'): @@ -114,32 +112,52 @@ def para_changed(self, obj): def reset(self, para=None): if para!=None:self.para = para - #print(para, '====') - for p in list(self.para.keys()): + for p in self.para.keys(): if p in self.ctrl_dic: self.ctrl_dic[p].SetValue(self.para[p]) - def get_para(self): - return self.para + def get_para(self): return self.para + + def Bind(self, tag, f): + if tag == 'parameter': self.handle = f if not f is None else print + if tag == 'commit': self.on_ok = f + if tag == 'cancel': self.on_cancel = f - def set_handle(self, handle=None): - self.handle = handle - # if handle==None: self.handle = self.handle_ + def show(self): + if self.modal: + status = self.ShowModal() == 5100 + self.Destroy() + return status + else: self.Show() def __del__( self ): print('panel config deleted!') -if __name__ == '__main__': - view = [(float, 'r', (0,20), 1, '半径', 'mm'), - ('slide', 'mm', (-20,20), '亮度', 'slide'), - ('color', 'color', '颜色', 'rgb'), - (bool, 'preview', 'preview')] - - data = {'r':1.2, 'slide':0, 'preview':True, 'color':(0,255,0)} +def get_para(para, view, title='Parameter', parent=None): + pd = ParaDialog(parent, title) + pd.init_view(view, para) + pd.pack() + rst = pd.ShowModal() + pd.Destroy() + return rst == 5100 - app = wx.PySimpleApp() +if __name__ == '__main__': + para = {'name':'yxdragon', 'age':10, 'h':1.72, 'w':70, 'sport':True, 'sys':'Mac', 'lan':['C/C++', 'Python'], 'c':(255,0,0)} + + view = [('lab', 'lab', 'This is a questionnaire'), + (str, 'name', 'name', 'please'), + (int, 'age', (0,150), 0, 'age', 'years old'), + (float, 'h', (0.3, 2.5), 2, 'height', 'm'), + ('slide', 'w', (1, 150), 0, 'weight','kg'), + (bool, 'sport', 'do you like sport'), + (list, 'sys', ['Windows','Mac','Linux'], str, 'favourite', 'system'), + ('chos', 'lan', ['C/C++','Java','Python'], 'lanuage you like(multi)'), + ('color', 'c', 'which', 'you like')] + + app = wx.App() pd = ParaDialog(None, 'Test') - pd.init_view(view, para) + pd.init_view(view, para, preview=True, modal=False) pd.pack() pd.ShowModal() - app.MainLoop() \ No newline at end of file + print(para) + app.MainLoop() diff --git a/sciwx/widgets/threpanel.py b/sciwx/widgets/threpanel.py new file mode 100644 index 00000000..f1b0c591 --- /dev/null +++ b/sciwx/widgets/threpanel.py @@ -0,0 +1,52 @@ +import wx, math +from .histpanel import HistPanel +from .normal import FloatSlider + +class ThresholdPanel( wx.Panel ): + def __init__( self, parent, mode, hist, rang, accury, app=None): + wx.Panel.__init__ ( self, parent) + (self.lim1, self.lim2), self.mode = rang, mode + bSizer1 = wx.BoxSizer( wx.VERTICAL ) + + self.histpan = HistPanel(self) + self.histpan.SetValue(hist) + bSizer1.Add(self.histpan, 0, wx.ALL|wx.EXPAND, 5 ) + + self.sli_high = FloatSlider(self, rang, accury, '', '') + self.sli_high.SetValue(rang[0]) + bSizer1.Add( self.sli_high, 0, wx.ALL|wx.EXPAND, 0 ) + if mode == 'bc': rang, accury = (1, 89), 0 + self.sli_low = FloatSlider(self, rang, accury, '', '') + self.sli_low.SetValue(rang[1]) + bSizer1.Add( self.sli_low, 0, wx.ALL|wx.EXPAND, 0 ) + self.SetSizer(bSizer1) + + def on_threshold(self, dir, event): + if self.f is None: return + a, b = self.GetValue() + if self.mode == 'lh': + if dir: b = max(a, b) + else: a = min(a, b) + self.SetValue((a,b)) + a = int((a-self.lim1)/(self.lim2-self.lim1)*255) + b = int((b-self.lim1)/(self.lim2-self.lim1)*255) + self.histpan.set_lim(a, b) + if self.mode == 'bc': + mid = 128-a/(self.lim2-self.lim1)*255 + length = 255/math.tan(b/180.0*math.pi) + self.histpan.set_lim(mid-length/2, mid+length/2) + self.f(self) + + def Bind(self, z, f): + self.f = f + self.sli_high.Bind(z, lambda e: self.on_threshold(True, e)) + self.sli_low.Bind(z, lambda e: self.on_threshold(False, e)) + + def SetValue(self, n): + self.sli_high.SetValue(n[0]) + self.sli_low.SetValue(n[1]) + + def GetValue(self): + b = self.sli_low.GetValue() + a = self.sli_high.GetValue() + return None if None in (a,b) else (a,b) \ No newline at end of file diff --git a/sciwx/widgets/toolbar.py b/sciwx/widgets/toolbar.py new file mode 100644 index 00000000..06f52921 --- /dev/null +++ b/sciwx/widgets/toolbar.py @@ -0,0 +1,108 @@ +import wx + +def make_logo(obj): + if isinstance(obj, str) and len(obj)>1: + bmp = wx.Bitmap(obj) + if isinstance(obj, str) and len(obj)==1: + bmp = wx.Bitmap(16, 16) + dc = wx.MemoryDC() + dc.SelectObject(bmp) + dc.SetBackground(wx.Brush((255,255,255))) + dc.Clear() + dc.SetTextForeground((0,0,150)) + font = dc.GetFont() + font.SetPointSize(12) + dc.SetFont(font) + w, h = dc.GetTextExtent(obj) + dc.DrawText(obj, 8-w//2, 8-h//2) + rgb = bytes(768) + bmp.CopyToBuffer(rgb) + a = memoryview(rgb[::3]).tolist() + a = bytes([255-i for i in a]) + bmp = wx.Bitmap.FromBufferAndAlpha(16, 16, rgb, a) + img = bmp.ConvertToImage() + img.Resize((20,20), (2,2)) + return img.ConvertToBitmap() + +class ToolBar(wx.Panel): + def __init__(self, parent, vertical=False): + wx.Panel.__init__( self, parent, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + sizer = wx.BoxSizer( (wx.HORIZONTAL, wx.VERTICAL)[vertical] ) + self.SetSizer( sizer ) + self.app = parent + self.toolset = [] + self.curbtn = None + + def on_tool(self, evt, tol): + tol().start(self.app) + evt.Skip() + btn = evt.GetEventObject() + #print(self.GetBackgroundColour()) + #print(btn.GetClassDefaultAttributes().colFg) + if not self.curbtn is None: + self.curbtn.SetBackgroundColour(self.GetBackgroundColour()) + self.curbtn = btn + btn.SetBackgroundColour(wx.SystemSettings.GetColour( wx.SYS_COLOUR_ACTIVECAPTION ) ) + + def bind(self, btn, tol): + btn.SetBackgroundColour(self.GetBackgroundColour()) + btn.Bind( wx.EVT_LEFT_DOWN, lambda e, obj=tol: self.on_tool(e, obj)) + + def add_tool(self, logo, tool): + btn = wx.BitmapButton(self, wx.ID_ANY, make_logo(logo), + wx.DefaultPosition, (32,32), wx.BU_AUTODRAW|wx.RAISED_BORDER ) + self.bind(btn, tool) + self.GetSizer().Add(btn, 0, wx.ALL, 1) + + def add_tools(self, name, tools, fixed=True): + if not fixed: self.toolset.append((name, [])) + for logo, tool in tools: + btn = wx.BitmapButton(self, wx.ID_ANY, make_logo(logo), + wx.DefaultPosition, (32,32), wx.BU_AUTODRAW|wx.RAISED_BORDER ) + self.bind(btn, tool) + self.GetSizer().Add(btn, 0, wx.ALL, 1) + if not fixed: self.toolset[-1][1].append(btn) + if fixed: + line = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL ) + self.GetSizer().Add( line, 0, wx.ALL|wx.EXPAND, 2 ) + + def active_set(self, name): + for n, tools in self.toolset: + print('select', name, n) + for btn in tools: + if n==name: btn.Show() + if n!=name: btn.Hide() + self.Layout() + + def add_pop(self, logo, default): + self.GetSizer().AddStretchSpacer(1) + btn = wx.BitmapButton(self, wx.ID_ANY, make_logo(logo), + wx.DefaultPosition, (32,32), wx.BU_AUTODRAW|wx.RAISED_BORDER ) + btn.Bind(wx.EVT_LEFT_DOWN, self.menu_drop) + self.GetSizer().Add(btn, 0, wx.ALL, 1) + self.active_set(default) + + def menu_drop(self, event): + menu = wx.Menu() + for name, item in self.toolset: + item = wx.MenuItem(menu, wx.ID_ANY, name, wx.EmptyString, wx.ITEM_NORMAL ) + menu.Append(item) + f = lambda e, name=name:self.active_set(name) + menu.Bind(wx.EVT_MENU, f, id=item.GetId()) + self.PopupMenu( menu ) + menu.Destroy() + +if __name__ == '__main__': + path = 'C:/Users/54631/Documents/projects/imagepy/imagepy/tools/drop.gif' + app = wx.App() + frame = wx.Frame(None) + tool = ToolBar(frame, vertical=True) + path = 'C:/Users/54631/Documents/projects/imagepy2/fucai/imgs/_help.png' + tool.add_tools('A', [('A', None)] * 3) + tool.add_tools('B', [('B', None)] * 3) + tool.add_tools('C', [('C', None)] * 3) + tool.add_pop('P', 'B') + tool.Layout() + frame.Fit() + frame.Show() + app.MainLoop() diff --git a/sciwx/widgets/util.py b/sciwx/widgets/util.py new file mode 100644 index 00000000..8495c0c0 --- /dev/null +++ b/sciwx/widgets/util.py @@ -0,0 +1,27 @@ +import wx + +def alert(info, title='SciWx'): + dialog=wx.MessageDialog(None, info, title, wx.OK) + dialog.ShowModal() == wx.ID_OK + dialog.Destroy() + +def getpath(title, filt, io, name=''): + filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in filt]) + dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} + dialog = wx.FileDialog(None, title, '', name, filt, dic[io]) + rst = dialog.ShowModal() + path = dialog.GetPath() if rst == wx.ID_OK else None + dialog.Destroy() + return path + +if __name__ == '__main__': + app = wx.App() + frame = wx.Frame(None) + frame.Show() + + frame2 = wx.Frame(None) + frame2.Show() + + path = getpath('file', ['png','bmp'], 'save') + print(path) + app.MainLoop() \ No newline at end of file diff --git a/imagepy/ui/widgets/viewport.py b/sciwx/widgets/viewport.py similarity index 82% rename from imagepy/ui/widgets/viewport.py rename to sciwx/widgets/viewport.py index 5ea28fae..60811fcc 100644 --- a/imagepy/ui/widgets/viewport.py +++ b/sciwx/widgets/viewport.py @@ -1,17 +1,13 @@ import wx, sys import numpy as np from numpy.linalg import norm -from scipy import interpolate - -if sys.version_info[0]==2:memoryview=np.getbuffer class ViewPort(wx.Panel): """ HistCanvas: diverid from wx.core.Panel """ def __init__(self, parent): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, - pos = wx.DefaultPosition, size = wx.Size(-1,-1), + pos = wx.DefaultPosition, size = wx.Size(100,100), style = wx.TAB_TRAVERSAL ) - self.init_buf() self.img = None self.boximg = None self.boxpan = None @@ -21,18 +17,20 @@ def __init__(self, parent): self.loc = (0,0) self.drag = False - self.Bind(wx.EVT_SIZE, self.on_size) - self.Bind(wx.EVT_IDLE, self.on_idle) - self.Bind(wx.EVT_PAINT, self.on_paint) - self.Bind(wx.EVT_LEFT_DOWN, self.on_ld) - self.Bind(wx.EVT_LEFT_UP, self.on_lu) - self.Bind(wx.EVT_MOTION, self.on_mv) + wx.Panel.Bind(self, wx.EVT_SIZE, self.on_size) + wx.Panel.Bind(self, wx.EVT_IDLE, self.on_idle) + wx.Panel.Bind(self, wx.EVT_PAINT, self.on_paint) + wx.Panel.Bind(self, wx.EVT_LEFT_DOWN, self.on_ld) + wx.Panel.Bind(self, wx.EVT_LEFT_UP, self.on_lu) + wx.Panel.Bind(self, wx.EVT_MOTION, self.on_mv) + self.init_buf() def update(self): self.dirty = True def init_buf(self): self.box = box = self.GetClientSize() self.buffer = wx.Bitmap(box.width, box.height) + self.update() def on_size(self, event): self.init_buf() @@ -47,7 +45,7 @@ def on_paint(self, event): wx.BufferedPaintDC(self, self.buffer) def on_ld(self, event): - self.handle(True) + self.on_view(self.GetValue(), True) x, y = event.GetX(), event.GetY() x = 1.0*(x-self.offx)/self.imgw y = 1.0*(y-self.offy)/self.imgh @@ -65,7 +63,7 @@ def on_mv(self, event): y = 1.0*(y-self.offy)/self.imgh if x<0 or x>1 or y<0 or y>1:return self.loc = (x*self.ibox[0], y*self.ibox[1]) - self.handle() + self.on_view(self.GetValue()) def GetValue(self):return self.loc @@ -81,10 +79,8 @@ def set_img(self, img, size): k = 1.0*self.box[1]/self.ibox[1] self.imgw, self.imgh = self.ibox[0]*k, self.box[1] self.offx, self.offy = (self.box[0]-self.imgw)/2.0, 0 - print(self.imgw, self.imgh) self.img = bmp.ConvertToImage().Rescale(self.imgw, self.imgh).ConvertToBitmap() - def set_box(self, boximg, boxpan): self.boximg, self.boxpan = boximg, boxpan self.update() @@ -115,16 +111,18 @@ def draw(self): dc.DrawRectangle(x,y,w,h) - def handle(self):pass + def on_view(self, event): print(event) - def set_handle(self, handle):self.handle = handle + def Bind(self, tag, f): self.on_view = f if __name__ == '__main__': - app = wx.PySimpleApp() - frame = wx.Frame(None) - hist = ViewPort(frame) - #hist.set_img(np.zeros((6,10))) - hist.set_box([0,0,10,6],[0,0,5,5]) + app = wx.App() + frame = wx.Frame(None, size=(300, 300)) + + view = ViewPort(frame) + img = np.random.randint(0,255, (512, 512, 3), dtype=np.uint8) frame.Fit() frame.Show(True) - app.MainLoop() \ No newline at end of file + view.set_img(img, (200,200)) + view.set_box([0,0,10,6],[0,0,5,5]) + app.MainLoop() From ef07309e63506c8110da717f5372af222108a4b0 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 2 Jun 2020 00:03:06 +0800 Subject: [PATCH 206/343] remove all manager --- imagepy/__main__.py | 2 +- imagepy/core/__init__.py | 2 - imagepy/core/{loader => app}/loader.py | 1 - imagepy/core/draw/__init__.py | 1 - imagepy/core/draw/fill.py | 15 - imagepy/core/draw/paint.py | 69 ---- imagepy/core/draw/polygonfill.py | 69 ---- imagepy/core/engine/filter.py | 10 +- imagepy/core/engine/free.py | 5 +- imagepy/core/engine/report.py | 14 +- imagepy/core/engine/simple.py | 6 +- imagepy/core/engine/table.py | 7 +- imagepy/core/engine/workflow.py | 4 +- imagepy/core/loader/__init__.py | 1 - imagepy/core/manager/__init__.py | 10 - imagepy/core/manager/clipbdmanager.py | 8 - imagepy/core/manager/colormanager.py | 62 --- imagepy/core/manager/configmanager.py | 44 --- imagepy/core/manager/documentmanager.py | 11 - imagepy/core/manager/iomanager.py | 48 --- imagepy/core/manager/languagemanager.py | 86 ---- imagepy/core/manager/pluginmanager.py | 10 - imagepy/core/manager/roimanager.py | 41 -- imagepy/core/manager/shotcutmanager.py | 45 --- imagepy/core/manager/taskmanager.py | 15 - imagepy/core/mark/__init__.py | 1 - imagepy/core/mark/mark.py | 367 ------------------ imagepy/core/roi/__init__.py | 5 - imagepy/core/roi/convert.py | 97 ----- imagepy/core/roi/lineroi.py | 77 ---- imagepy/core/roi/ovalroi.py | 95 ----- imagepy/core/roi/pointroi.py | 87 ----- imagepy/core/roi/polygonroi.py | 153 -------- imagepy/core/roi/rectangleroi.py | 96 ----- imagepy/core/roi/roi.py | 47 --- imagepy/core/roi/roiio.py | 28 -- imagepy/core/util/__init__.py | 3 +- imagepy/core/util/fileio.py | 21 +- imagepy/core/util/tableio.py | 6 +- imagepy/core/util/testdata.py | 41 -- imagepy/core/wraper/__init__.py | 0 imagepy/core/wraper/imageplus.py | 92 ----- imagepy/core/wraper/tableplus.py | 72 ---- imagepy/menus/Edit/edit_plg.py | 5 +- imagepy/menus/File/BMP/bmp_plgs.py | 6 +- imagepy/menus/File/DAT/dat_plgs.py | 6 +- imagepy/menus/File/DICOM/dicom_plgs.py | 4 +- imagepy/menus/File/Export/sequence_plg.py | 4 +- imagepy/menus/File/GIF/gif_plgs.py | 6 +- imagepy/menus/File/Import/sequence_plg.py | 6 +- imagepy/menus/File/JPG/jpg_plgs.py | 10 +- imagepy/menus/File/MAT/mat_plgs.py | 6 +- imagepy/menus/File/Numpy/ndarray_plgs.py | 6 +- imagepy/menus/File/PNG/png_plgs.py | 6 +- imagepy/menus/File/TIF/tif_plgs.py | 8 +- imagepy/menus/File/open_plg.py | 5 +- imagepy/menus/File/save_plg.py | 4 +- imagepy/menus/Help/Language/language_plgs.py | 6 +- .../menus/Kit3D/Viewer 3D/tablepoints_plg.py | 2 +- imagepy/menus/Plugins/Macros/recorder_plg.py | 4 +- imagepy/menus/Plugins/Manager/plgtree_wgt.py | 6 +- imagepy/menus/Plugins/Manager/shotcut_wgt.py | 11 +- imagepy/menus/Plugins/Manager/toltree_wgt.py | 6 +- imagepy/menus/Plugins/detect_plg.py | 161 -------- imagepy/menus/Plugins/mser_plg.py | 147 ------- imagepy/menus/Plugins/temporal_plg.py | 11 +- imagepy/menus/Process/Classify/io_plgs.py | 4 +- imagepy/menus/Process/Classify/predict_plg.py | 2 +- imagepy/menus/Table/Table IO/tableio_plgs.py | 14 +- .../menus/Window/Windows Style/style_plgs.py | 10 +- imagepy/tools/Draw/Route_tol.py | 6 +- imagepy/tools/Draw/aibrush_tol.py | 4 +- imagepy/tools/Draw/flood3d_tol.py | 4 +- imagepy/tools/Draw/floodfill_tol.py | 4 +- imagepy/tools/Draw/magic_tol.py | 3 +- imagepy/tools/Standard/colorpicker_tol.py | 2 +- imagepy/tools/Standard/magic_tol.py | 4 +- imagepy/tools/Standard/painter_tol.py | 4 +- imagepy/tools/Toolkit3D/flood3d_tol.py | 4 +- 79 files changed, 127 insertions(+), 2248 deletions(-) rename imagepy/core/{loader => app}/loader.py (99%) delete mode 100644 imagepy/core/draw/__init__.py delete mode 100644 imagepy/core/draw/fill.py delete mode 100644 imagepy/core/draw/paint.py delete mode 100644 imagepy/core/draw/polygonfill.py delete mode 100644 imagepy/core/loader/__init__.py delete mode 100644 imagepy/core/manager/__init__.py delete mode 100644 imagepy/core/manager/clipbdmanager.py delete mode 100644 imagepy/core/manager/colormanager.py delete mode 100644 imagepy/core/manager/configmanager.py delete mode 100644 imagepy/core/manager/documentmanager.py delete mode 100644 imagepy/core/manager/iomanager.py delete mode 100644 imagepy/core/manager/languagemanager.py delete mode 100644 imagepy/core/manager/pluginmanager.py delete mode 100644 imagepy/core/manager/roimanager.py delete mode 100644 imagepy/core/manager/shotcutmanager.py delete mode 100644 imagepy/core/manager/taskmanager.py delete mode 100644 imagepy/core/mark/__init__.py delete mode 100644 imagepy/core/mark/mark.py delete mode 100644 imagepy/core/roi/__init__.py delete mode 100644 imagepy/core/roi/convert.py delete mode 100644 imagepy/core/roi/lineroi.py delete mode 100644 imagepy/core/roi/ovalroi.py delete mode 100644 imagepy/core/roi/pointroi.py delete mode 100644 imagepy/core/roi/polygonroi.py delete mode 100644 imagepy/core/roi/rectangleroi.py delete mode 100644 imagepy/core/roi/roi.py delete mode 100644 imagepy/core/roi/roiio.py delete mode 100644 imagepy/core/util/testdata.py delete mode 100644 imagepy/core/wraper/__init__.py delete mode 100644 imagepy/core/wraper/imageplus.py delete mode 100644 imagepy/core/wraper/tableplus.py delete mode 100644 imagepy/menus/Plugins/detect_plg.py delete mode 100644 imagepy/menus/Plugins/mser_plg.py diff --git a/imagepy/__main__.py b/imagepy/__main__.py index 3064e253..00b990b4 100644 --- a/imagepy/__main__.py +++ b/imagepy/__main__.py @@ -1,7 +1,7 @@ import wx, sys sys.path.append('../') from imagepy.core.app import ImagePy -from imagepy.core.loader import loader +from imagepy.core.app import loader from sciapp.action import ImgAction, Tool, DefaultTool from sciwx.plugins.curve import Curve diff --git a/imagepy/core/__init__.py b/imagepy/core/__init__.py index 9d6d2792..e69de29b 100644 --- a/imagepy/core/__init__.py +++ b/imagepy/core/__init__.py @@ -1,2 +0,0 @@ -#from . wraper.imageplus import ImagePlus -from . wraper.tableplus import TablePlus \ No newline at end of file diff --git a/imagepy/core/loader/loader.py b/imagepy/core/app/loader.py similarity index 99% rename from imagepy/core/loader/loader.py rename to imagepy/core/app/loader.py index 32098160..ab3c696c 100644 --- a/imagepy/core/loader/loader.py +++ b/imagepy/core/app/loader.py @@ -6,7 +6,6 @@ """ import os, sys from ..engine import Macros, MkDown, Widget, WorkFlow, Report -from ..manager import DocumentManager from sciapp import Source from ... import root_dir from codecs import open diff --git a/imagepy/core/draw/__init__.py b/imagepy/core/draw/__init__.py deleted file mode 100644 index 4287ca86..00000000 --- a/imagepy/core/draw/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# \ No newline at end of file diff --git a/imagepy/core/draw/fill.py b/imagepy/core/draw/fill.py deleted file mode 100644 index fda9c7d9..00000000 --- a/imagepy/core/draw/fill.py +++ /dev/null @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- -import numpy as np -from scipy.ndimage import label, generate_binary_structure - -def floodfill2(img, x, y, thr, con): - color = img[int(y), int(x)] - buf = np.subtract(img, color, dtype=np.int16) - msk = np.abs(buf)<=thr - if buf.ndim==3: - msk = np.min(msk, axis=2) - buf = buf[:,:,0] - strc = generate_binary_structure(2, con+1) - label(msk, strc, output = buf) - msk = buf == buf[int(y), int(x)] - return msk \ No newline at end of file diff --git a/imagepy/core/draw/paint.py b/imagepy/core/draw/paint.py deleted file mode 100644 index b54746d8..00000000 --- a/imagepy/core/draw/paint.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Oct 19 15:03:15 2016 -@author: yxl -""" -from __future__ import absolute_import -import numpy as np -from ..draw import polygonfill -from ..manager import ColorManager - -def match_color(img, color): - if hasattr(color, '__iter__') and len(img.shape)==2: - return np.mean(color) - return color - -class Paint: - def __init__(self, width=1): - self.width = 1 - self.curpt = (0,0) - - def set_curpt(self, x,y): - self.curpt = x,y - - def draw_pixs(self, img, xs, ys, color=None): - mskx = (xs>=0) * (xs=0) * (ys=shape[1] or y>=shape[0]: return - if color == None:color = ColorManager.get_front() - color = match_color(img, color) - if r==1: img[y,x] = color - n = int(r) - xs,ys = np.mgrid[-n:n+1,-n:n+1] - msk = np.sqrt(xs**2+ys**2) abs(p2[1]-y):p1,p2 = p2,p1 - k =1.0* (p1[1]-y)/(y-p2[1]) - return round((p1[0]+k*p2[0])/(1+k),4) - -def scan(polys, idx, ys, st, y, cur, buf): - while cur= shape[1] or x2 < 0: continue - #rst.extend([(x,y) for x in range(max(x1,o[0]), min(x2, shape[2]))]) - img[y,x1:x2] = color - - return np.array(rst).T - -if __name__ == '__main__': - import matplotlib.pyplot as plt - from time import time - # pg.shape = (1,4,2) - pg = np.array([[(-300,-100),(1100,100),(400,1300),(100,100)]]) - # img.shape = (1000, 500) - img = np.zeros((1000, 500)) - a = time() - rc= fill(pg, img) - print(time() - a) - plt.imshow(img, interpolation='nearest',cmap='gray') - plt.show() - print("Done!") diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index a75acdd8..133dae41 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -8,7 +8,7 @@ import threading import numpy as np -from ...core.manager import TaskManager, DocumentManager +from sciapp import Source from time import time def process_channels(plg, ips, src, des, para): @@ -24,7 +24,7 @@ def process_channels(plg, ips, src, des, para): return des def process_one(plg, ips, src, img, para, callafter=None): - TaskManager.add(plg) + Source.manager('task').add(plt.title, plg) start = time() transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16) transfloat = '2float' in plg.note and not ips.dtype in (np.complex128, np.float32, np.float64) @@ -43,11 +43,11 @@ def process_one(plg, ips, src, img, para, callafter=None): img[msk] = src[msk] plg.app.info('%s: cost %.3fs'%(ips.title, time()-start)) ips.update() - TaskManager.remove(plg) + Source.manager('task').remove(plg) if not callafter is None:callafter() def process_stack(plg, ips, src, imgs, para, callafter=None): - TaskManager.add(plg) + Source.manager('task').add(plt.title, plg) start = time() transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16) @@ -74,7 +74,7 @@ def process_stack(plg, ips, src, imgs, para, callafter=None): i[msk] = src[msk] plg.app.info('%s: cost %.3fs'%(ips.title, time()-start)) ips.update() - TaskManager.remove(plg) + Source.manager('task').remove(plg) if not callafter is None:callafter() diff --git a/imagepy/core/engine/free.py b/imagepy/core/engine/free.py index bba815d2..72175183 100644 --- a/imagepy/core/engine/free.py +++ b/imagepy/core/engine/free.py @@ -4,7 +4,6 @@ @author: yxl """ import threading -from ...core.manager import TaskManager, DocumentManager from time import time class Free: @@ -21,11 +20,11 @@ def run(self, para=None): print('this is a plugin') def runasyn(self, para, callback=None): - TaskManager.add(self) + self.app.manager('task').add(self,title, self) start = time() self.run(para) self.app.info('%s: cost %.3fs'%(self.title, time()-start)) - TaskManager.remove(self) + self.app.manager('task').remove(self) if callback!=None:callback() def load(self):return True diff --git a/imagepy/core/engine/report.py b/imagepy/core/engine/report.py index d801ab75..0df31300 100644 --- a/imagepy/core/engine/report.py +++ b/imagepy/core/engine/report.py @@ -4,13 +4,12 @@ @author: yxl """ import wx -from imagepy.core.manager import TaskManager from sciapp import Source -from imagepy.core.manager import ReaderManager #from imagepy.ui.propertygrid import GridDialog from imagepy.core.util import xlreport from time import time import openpyxl as pyxl +from sciapp import Source class Report: def __init__(self, title, cont): @@ -20,7 +19,7 @@ def __init__(self, title, cont): def __call__(self): return self def runasyn(self, wb, info, key, para = None, callback = None): - TaskManager.add(self) + Source.manager('task').add(self) for i in para: if i in key and key[i][0] == 'img': ips = ImageManager.get(para[i]) @@ -33,8 +32,8 @@ def runasyn(self, wb, info, key, para = None, callback = None): start = time() xlreport.fill_value(wb, info, para) wb.save(para['path']) - IPy.set_info('%s: cost %.3fs'%(self.title, time()-start)) - TaskManager.remove(self) + self.app.set_info('%s: cost %.3fs'%(self.title, time()-start)) + Source.manager('task').remove(self) if callback!=None:callback() def start(self, para=None, callafter=None): @@ -49,7 +48,7 @@ def start(self, para=None, callafter=None): dialog.Destroy() if rst != 5100: return filt = '|'.join(['%s files (*.%s)|*.%s'%('XLSX', 'xlsx', 'xlsx')]) - if not IPy.getpath('Save..', filt, 'save', para): return + if not self.app.getpath('Save..', filt, 'save', para): return win = Source.manager('widget').get('obj', name='Macros Recorder') if win!=None: win.write('{}>{}'.format(self.title, para)) self.runasyn(wb, info, key, para, callafter) @@ -59,4 +58,5 @@ def show_rpt(data, title): # ViewerManager.add('rpt', show_rpt) def read_rpt(path): return path -ReaderManager.add(name='rpt', obj=read_rpt, tag='rpt') \ No newline at end of file + +Source.manager('reader').add(name='rpt', obj=read_rpt, tag='rpt') \ No newline at end of file diff --git a/imagepy/core/engine/simple.py b/imagepy/core/engine/simple.py index b4076b44..b863f257 100644 --- a/imagepy/core/engine/simple.py +++ b/imagepy/core/engine/simple.py @@ -6,7 +6,7 @@ import wx import threading -from ..manager import TaskManager, DocumentManager +from sciapp import Source from time import time import numpy as np @@ -48,12 +48,12 @@ def ok(self, ips, para=None, callafter=None): self.app.record_macros('{}>{}'.format(self.title, para)) def runasyn(self, ips, imgs, para = None, callback = None): - TaskManager.add(self) + self.app.manager('task').add(self,title, self) start = time() self.run(ips, imgs, para) self.app.info('%s: cost %.3fs'%(ips.title, time()-start)) ips.update() - TaskManager.remove(self) + self.app.manager('task').remove(self) if callback!=None:callback() def check(self, ips): diff --git a/imagepy/core/engine/table.py b/imagepy/core/engine/table.py index 27069ff0..7dfe461f 100644 --- a/imagepy/core/engine/table.py +++ b/imagepy/core/engine/table.py @@ -5,9 +5,6 @@ """ import wx import threading - -from ..manager import TaskManager, DocumentManager -from sciapp import Source from time import time class Table: @@ -56,12 +53,12 @@ def ok(self, tps, para=None, callafter=None): if win!=None: win.write('{}>{}'.format(self.title, para)) def runasyn(self, tps, snap, data, para = None, callback = None): - TaskManager.add(self) + self.app.manager('task').add(self,title, self) start = time() self.run(tps, data, snap, para) self.app.set_info('%s: cost %.3fs'%(tps.title, time()-start)) tps.update() - TaskManager.remove(self) + self.app.manager('task').remove(self) if callback!=None:callback() def check(self, tps): diff --git a/imagepy/core/engine/workflow.py b/imagepy/core/engine/workflow.py index fb740d27..d31a6cf1 100644 --- a/imagepy/core/engine/workflow.py +++ b/imagepy/core/engine/workflow.py @@ -1,5 +1,5 @@ import threading, wx, os, wx.lib.agw.aui as aui -from imagepy.core.manager import ReaderManager +from sciapp import Source def parse(cont): ls = cont.split('\n') @@ -49,4 +49,4 @@ def read_wf(path): print(cont) return cont -ReaderManager.add(name='wf', obj=read_wf, tag='wf') \ No newline at end of file +Source.manager('reader').add(name='wf', obj=read_wf, tag='wf') \ No newline at end of file diff --git a/imagepy/core/loader/__init__.py b/imagepy/core/loader/__init__.py deleted file mode 100644 index 4287ca86..00000000 --- a/imagepy/core/loader/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# \ No newline at end of file diff --git a/imagepy/core/manager/__init__.py b/imagepy/core/manager/__init__.py deleted file mode 100644 index d6c18fe0..00000000 --- a/imagepy/core/manager/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -from .colormanager import * -from .pluginmanager import * -from .roimanager import * -from .clipbdmanager import * -from .configmanager import * -from .shotcutmanager import * -from .taskmanager import * -from .iomanager import * -from .languagemanager import * -from .documentmanager import * diff --git a/imagepy/core/manager/clipbdmanager.py b/imagepy/core/manager/clipbdmanager.py deleted file mode 100644 index e197b6c5..00000000 --- a/imagepy/core/manager/clipbdmanager.py +++ /dev/null @@ -1,8 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Sun Jan 15 01:10:37 2017 -@author: yxl -""" -class ClipBoardManager: - roi = None - img = None \ No newline at end of file diff --git a/imagepy/core/manager/colormanager.py b/imagepy/core/manager/colormanager.py deleted file mode 100644 index 71fa4358..00000000 --- a/imagepy/core/manager/colormanager.py +++ /dev/null @@ -1,62 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Sat Jan 14 23:24:32 2017 -@author: yxl -""" -#from __future__ import absolute_import -from ... import root_dir - -import numpy as np -import os, wx -import sys -from glob import glob - -filenames = glob(os.path.join(root_dir,'data/luts/*.lut')) -filenames.extend(glob(os.path.join(root_dir,'data/luts/*/*.lut'))) -keys = [os.path.split(filename)[-1][:-4] for filename in filenames] -values = [np.fromfile(filename, dtype=np.uint8).reshape((3,256)).T.copy() for filename in filenames] - -class ColorManager: - luts = dict(zip(keys, values)) - frontcolor = (255,255,0) - backcolor = (0,0,0) - wr, wg, wb = 1.0/3, 1.0/3, 1.0/3 - - @classmethod - def get_color(cls, app=None): - rst = None - dialog = wx.ColourDialog(app) - dialog.GetColourData().SetChooseFull(True) - if dialog.ShowModal() == wx.ID_OK: - rst = dialog.GetColourData().GetColour() - dialog.Destroy() - return rst - - @classmethod - def set_front(cls, color): - if not hasattr(color, '__len__'): - color = (color, color, color) - cls.frontcolor=tuple(color) - - @classmethod - def set_back(cls, color): - if not hasattr(color, '__len__'): - color = (color, color, color) - cls.backcolor=tuple(color) - - @classmethod - def get_front(cls, one=False): - if not one:return cls.frontcolor - return np.dot((cls.wr,cls.wg,cls.wb), cls.frontcolor) - - @classmethod - def get_back(cls, one=False): - if not one:return cls.backcolor - return np.dot((cls.wr,cls.wg,cls.wb), cls.backcolor) - - @classmethod - def get_lut(cls, name='grays', set='all'): - if name=='grays': - lut = np.arange(256).reshape((-1,1)) - return (lut*np.ones((1,3))).astype(np.uint8) - else: return cls.luts[name].copy() \ No newline at end of file diff --git a/imagepy/core/manager/configmanager.py b/imagepy/core/manager/configmanager.py deleted file mode 100644 index 0f9dbb93..00000000 --- a/imagepy/core/manager/configmanager.py +++ /dev/null @@ -1,44 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Sat Jan 14 23:23:30 2017 -@author: yxl -""" -import pickle, os -from ... import root_dir - -class ConfigManager: - cfg = {} - filename = os.path.join(root_dir, "preference.cfg") - #filename = os.path.join("/home/auss/Programs/Python/ImagePy/ImagePy3", "preference.cfg") - - @classmethod - def read(cls): - """Read from the congigure file: preference.cfg """ - if os.path.exists(cls.filename): - pkl_file = open(cls.filename,'r') - cls.cfg = eval(pkl_file.read().replace("\n","").encode('utf8')) - pkl_file.close() - - @classmethod - def write(cls): - pkl_file = open(cls.filename, 'w') - pkl_file.write(str(cls.cfg)) - pkl_file.close() - - @classmethod - def get(cls, key): - """Get the congigure item """ - return cls.cfg[key] if key in cls.cfg else None - - @classmethod - def set(cls, key, value): - """Set the congigure item """ - cls.cfg[key] = value - -# call the read function so that initial the ConfigManager -ConfigManager.read() - -if __name__ == '__main__': - ConfigManager.set('b',[1,2,3]) - print(ConfigManager.cfg) - ConfigManager.write() diff --git a/imagepy/core/manager/documentmanager.py b/imagepy/core/manager/documentmanager.py deleted file mode 100644 index 4e68f0e7..00000000 --- a/imagepy/core/manager/documentmanager.py +++ /dev/null @@ -1,11 +0,0 @@ -class DocumentManager: - docs = {} - - @classmethod - def add(cls, name, cont):cls.docs[name] = cont - - @classmethod - def get(cls, name=None): - if not name in cls.docs: - return '# Sorry, No Document yet.' - return cls.docs[name] \ No newline at end of file diff --git a/imagepy/core/manager/iomanager.py b/imagepy/core/manager/iomanager.py deleted file mode 100644 index 0f63292f..00000000 --- a/imagepy/core/manager/iomanager.py +++ /dev/null @@ -1,48 +0,0 @@ -''' -class ReaderManager: - reader = [] - - @classmethod - def add(cls, ext, read, tag='img', note=''): - if isinstance(ext, str): ext = [ext] - for i in ext: - obj = (i, read, tag, note) - if not obj in cls.reader: cls.reader.append(obj) - - @classmethod - def get(cls, ext=None, tag=None, note=None): - msk = [True for i in cls.reader] - if not ext is None: - for i in range(len(msk)): msk[i] &= cls.reader[i][0]==ext - if not tag is None: - for i in range(len(msk)): msk[i] &= cls.reader[i][2]==tag - if not note is None: - for i in range(len(msk)): msk[i] &= cls.reader[i][3]==note - return [cls.reader[i] for i in range(len(msk)) if msk[i]] - -class WriterManager: - writer = [] - - @classmethod - def add(cls, ext, read, tag='img', note=''): - if isinstance(ext, str): ext = [ext] - for i in ext: - obj = (i, read, tag, note) - if not obj in cls.writer: cls.writer.append(obj) - - @classmethod - def get(cls, ext=None, tag=None, note=None): - msk = [True for i in cls.writer] - if not ext is None: - for i in range(len(msk)): msk[i] &= cls.writer[i][0]==ext - if not tag is None: - for i in range(len(msk)): msk[i] &= cls.writer[i][2]==tag - if not note is None: - for i in range(len(msk)): msk[i] &= cls.writer[i][3]==note - return [cls.writer[i] for i in range(len(msk)) if msk[i]] -''' - -from sciapp import Manager - -ReaderManager = Manager() -WriterManager = Manager() \ No newline at end of file diff --git a/imagepy/core/manager/languagemanager.py b/imagepy/core/manager/languagemanager.py deleted file mode 100644 index 4c997ebf..00000000 --- a/imagepy/core/manager/languagemanager.py +++ /dev/null @@ -1,86 +0,0 @@ -import os -from ... import root_dir -from glob import glob -from .configmanager import ConfigManager -from codecs import open - -class LanguageManager: - plgs = [] - langs = {} - cur = None - filename = os.path.join(root_dir,'data/language/*.dic') - - @classmethod - def set(cls, cur): - cls.cur = None if cur=='English' else cls.langs[cur] - ConfigManager.set('language', cur) - - @classmethod - def read(cls): - path = os.path.join(root_dir,'data/language/*.dic') - for name in glob(path): - pkl_file = open(name, 'r', 'utf-8') - fp, fn = os.path.split(name) - fn, fe = os.path.splitext(fn) - cls.langs[fn] = {} - for line in pkl_file.readlines(): - k,v = line.replace('\n', '').replace('\r', '').split(':') - cls.langs[fn][k] = v - pkl_file.close() - - cur = ConfigManager.get('language') - if cur is None: return - if cur in cls.langs: cls.cur = cls.langs[cur] - - @classmethod - def write(cls): - for key in cls.langs: - dic = cls.langs[key] - titles = sorted(dic.keys()) - pkl_file = open(os.path.join(root_dir,'data/language/%s.dic'%key), 'w', 'utf-8') - for i in titles: - pkl_file.write('%s:%s\n'%(i,dic[i])) - pkl_file.close() - - @classmethod - def add(cls, key=None): - if not key is None and not ':' in key: - if not key in cls.plgs:cls.plgs.append(key) - return - titles = cls.plgs - for key in cls.langs: - dic = cls.langs[key] - for i in titles: - if not ':' in i and not i in dic: dic[i] = '--' - cls.write() - - @classmethod - def rm(cls): - titles = cls.plgs - for key in cls.langs: - dic = cls.langs[key] - for i in list(dic.keys()): - if not i in titles: del dic[i] - cls.write() - - @classmethod - def newdic(cls, key): - cls.langs[key] = {} - for i in cls.plgs: - if not ':' in i: cls.langs[key][i] = '--' - - @classmethod - def get(cls, key): - if not cls.cur is None and key in cls.cur: - if cls.cur[key]!='--': - return cls.cur[key] - return key - -LanguageManager.read() - -if __name__ == '__main__': - #ShotcutManager.set('c',[1,2,3]) - ShotcutManager.rm('c') - print(ShotcutManager.shotcuts) - ShotcutManager.write() - \ No newline at end of file diff --git a/imagepy/core/manager/pluginmanager.py b/imagepy/core/manager/pluginmanager.py deleted file mode 100644 index 1efa4370..00000000 --- a/imagepy/core/manager/pluginmanager.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Sat Jan 14 23:23:30 2017 -@author: yxl -""" -from sciapp import Manager - -# PluginsManager = Manager() -# WidgetsManager = Manager() -# ToolsManager = Manager() \ No newline at end of file diff --git a/imagepy/core/manager/roimanager.py b/imagepy/core/manager/roimanager.py deleted file mode 100644 index e93705b2..00000000 --- a/imagepy/core/manager/roimanager.py +++ /dev/null @@ -1,41 +0,0 @@ -from .configmanager import ConfigManager - -class RoiManager: - rois = {} - @classmethod - def add(cls, name, roi): - cls.rois[name] = roi - - @classmethod - def remove(cls, name): - if name in cls.rois: del cls.rois[name] - - @classmethod - def get(cls, name): - if name not in cls.rois: - return None - return cls.rois[name] - - @classmethod - def get_titles(cls): - return sorted(list(cls.rois.keys())) - - @classmethod - def get_color(cls): - color = ConfigManager.get('roicolor') - if color is None:color = (255,255,0) - return color - - @classmethod - def set_color(cls, color): - ConfigManager.set('roicolor', color) - - @classmethod - def get_lw(cls): - lw = ConfigManager.get('roilw') - if lw is None:lw = 1 - return lw - - @classmethod - def set_lw(cls, lw): - ConfigManager.set('roilw', lw) \ No newline at end of file diff --git a/imagepy/core/manager/shotcutmanager.py b/imagepy/core/manager/shotcutmanager.py deleted file mode 100644 index 478178be..00000000 --- a/imagepy/core/manager/shotcutmanager.py +++ /dev/null @@ -1,45 +0,0 @@ -from sciapp import Manager -from ... import root_dir -import os - -ShotcutManager = Manager(path=os.path.join(root_dir,'data/shotcut.cfg')) -''' -class ShotcutManager: - shotcuts = {} - filename = os.path.join(root_dir,'data/shotcut.cfg') - @classmethod - def read(cls): - if os.path.exists(cls.filename): - pkl_file = open(cls.filename,'r') - cls.shotcuts = eval(pkl_file.read().replace("\n","").encode('utf8')) - pkl_file.close() - - @classmethod - def write(cls): - pkl_file = open(cls.filename, 'w') - pkl_file.write(str(cls.shotcuts)) - pkl_file.close() - - @classmethod - def get(cls, key): - if key in cls.shotcuts: - return cls.shotcuts[key] - return None - - @classmethod - def set(cls, key, value): - cls.shotcuts[key] = value - - @classmethod - def rm(cls, key): - if key in cls.shotcuts: - cls.shotcuts.pop(key) - -ShotcutManager.read() - -if __name__ == '__main__': - #ShotcutManager.set('c',[1,2,3]) - ShotcutManager.rm('c') - print(ShotcutManager.shotcuts) - ShotcutManager.write() -''' \ No newline at end of file diff --git a/imagepy/core/manager/taskmanager.py b/imagepy/core/manager/taskmanager.py deleted file mode 100644 index 6a7953a5..00000000 --- a/imagepy/core/manager/taskmanager.py +++ /dev/null @@ -1,15 +0,0 @@ -class TaskManager: - tasks = {} - - @classmethod - def add(cls, key): - cls.tasks[key] = 1 - - @classmethod - def remove(cls, key): - cls.tasks.pop(key) - - @classmethod - def get(cls, key=None): - if key==None:return cls.tasks - return cls.tasks[key] \ No newline at end of file diff --git a/imagepy/core/mark/__init__.py b/imagepy/core/mark/__init__.py deleted file mode 100644 index 22057e4d..00000000 --- a/imagepy/core/mark/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .mark import * \ No newline at end of file diff --git a/imagepy/core/mark/mark.py b/imagepy/core/mark/mark.py deleted file mode 100644 index b02bd877..00000000 --- a/imagepy/core/mark/mark.py +++ /dev/null @@ -1,367 +0,0 @@ -import numpy as np -from math import sin, cos -from ..manager import ConfigManager - -point = {'type':'point', 'color':(255,0,0), 'lw':1, 'body':(10,10)} -points = {'type':'points', 'color':(255,0,0), 'lw':1, 'body':[(10,10),(100,200)]} -line = {'type':'line', 'color':(255,0,0), 'lw':1, 'style':'-', 'body':[(10,10),(100,200),(200,200)]} -lines = {'type':'lines', 'color':(255,0,0), 'lw':1, 'style':'-', 'body':[[(10,10),(100,200),(200,200)],[(150,10),(50,250)]]} -polygon = {'type':'polygon', 'color':(255,0,0), 'fcolor':(255,255,0), 'lw':1, 'style':'o', 'body':[(10,10),(100,200),(200,200)]} -polygons = {'type':'polygons', 'color':(255,0,0), 'fcolor':(255,255,0,30), 'fill':False, 'lw':1, 'style':'o', 'body':[[(10,10),(100,200),(200,200)],[(150,10),(50,250),(288,0)]]} -circle = {'type':'circle', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':False, 'body':(100,100,50)} -circles = {'type':'circles', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':False, 'body':[(100,100,50),(300,300,100)]} -ellipse = {'type':'ellipse', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':False, 'body':(100,100,100,50,1)} -ellipses = {'type':'ellipses', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':False, 'body':[(100,100,100,50,1),(200,250,50,100,3.14)]} -rectangle = {'type':'rectangle', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':True, 'body':(100,100,80,50)} -rectangles = {'type':'rectangles', 'color':(255,0,0), 'fcolor':(255,255,0), 'fill':False, 'body':[(100,100,80,50),(200,200,80,100)]} -text = {'type':'text', 'color':(255,255,0), 'fcolor':(0,0,0), 'size':8, 'pt':True, 'body':(100,200,'id=0')} -texts = {'type':'texts', 'color':(255,255,0), 'fcolor':(0,0,0), 'size':8, 'pt':True, 'body':[(100,200,'id=0'),(180,250,'id=1')]} - -layer = {'type':'layer', 'num':-1, 'clolor':(255,255,0), 'fcolor':(255,255,255), 'fill':False, - 'body':[point, points, line, lines, polygon, polygons, circle, circles, ellipse, ellipses, rectangle, rectangles, text, texts]} - -layers = {'type':'layers', 'num':-1, 'clolor':(255,255,0), 'fcolor':(255,255,255), 'fill':False, - 'body':{1:points, 2:line, 3:layer}} - -def plot(pts, dc, f, **key): - pen, brush = dc.GetPen(), dc.GetBrush() - width, color = pen.GetWidth(), pen.GetColour() - fcolor, style = brush.GetColour(), brush.GetStyle() - - if 'color' in pts: - pen.SetColour(pts['color']) - if 'fcolor' in pts: - brush.SetColour(pts['fcolor']) - if 'lw' in pts: - pen.SetWidth(pts['lw']) - if 'fill' in pts: - brush.SetStyle((106,100)[pts['fill']]) - - dc.SetPen(pen) - dc.SetBrush(brush) - - if pts['type'] == 'point': - pen.SetWidth(1) - brush.SetStyle(100) - brush.SetColour(pen.GetColour()) - dc.SetPen(pen) - dc.SetBrush(brush) - r = pts['r'] if 'r' in pts else 2 - x, y = f(*pts['body']) - dc.DrawEllipse (x-r,y-r,r*2,r*2) - pen.SetWidth(pts['lw'] if 'lw' in pts else width) - brush.SetStyle((106,100)[pts['fill']] if 'fill' in pts else style) - brush.SetColour(pts['fc'] if 'fc' in pts else fcolor) - dc.SetPen(pen) - dc.SetBrush(brush) - elif pts['type'] in {'points','line','polygon'}: - lst, plst = [], [] - r = pts['r'] if 'r' in pts else 2 - for p in pts['body']: - x, y = f(*p) - lst.append((x-r,y-r,r*2,r*2)) - plst.append((x,y)) - isline = 'style' in pts and '-' in pts['style'] - ispoint = 'style' in pts and 'o' in pts['style'] - if pts['type'] == 'polygon': - dc.DrawPolygon(plst) - - if isline or pts['type'] == 'line': - dc.DrawLines(plst) - - if pts['type']=='points' or ispoint: - pen.SetWidth(1) - brush.SetStyle(100) - brush.SetColour(pen.GetColour()) - dc.SetPen(pen) - dc.SetBrush(brush) - dc.DrawEllipseList(lst) - pen.SetWidth(pts['lw'] if 'lw' in pts else width) - brush.SetStyle((106,100)[pts['fill']] if 'fill' in pts else style) - brush.SetColour(pts['fc'] if 'fc' in pts else fcolor) - dc.SetPen(pen) - dc.SetBrush(brush) - elif pts['type'] in {'lines','polygons'}: - lst, plst = [], [] - r = pts['r'] if 'r' in pts else 2 - for i in pts['body']: - line = [] - for p in i: - x, y = f(*p) - lst.append((x-r,y-r,r*2,r*2)) - line.append((x,y)) - plst.append(line) - isline = 'style' in pts and '-' in pts['style'] - ispoint = 'style' in pts and 'o' in pts['style'] - if pts['type'] == 'polygons': - dc.DrawPolygonList(plst) - - if isline or pts['type'] == 'lines': - for line in plst: - dc.DrawLines(line) - - if pts['type']=='points' or ispoint: - pen.SetWidth(1) - brush.SetStyle(100) - brush.SetColour(pen.GetColour()) - dc.SetPen(pen) - dc.SetBrush(brush) - dc.DrawEllipseList(lst) - pen.SetWidth(pts['lw'] if 'lw' in pts else width) - brush.SetStyle((106,100)[pts['fill']] if 'fill' in pts else style) - brush.SetColour(pts['fc'] if 'fc' in pts else fcolor) - dc.SetPen(pen) - dc.SetBrush(brush) - - pen.SetWidth(width) - pen.SetColour(color) - brush.SetColour(fcolor) - brush.SetStyle(style) - dc.SetPen(pen) - dc.SetBrush(brush) - -def draw_circle(pts, dc, f, **key): - pen, brush = dc.GetPen(), dc.GetBrush() - width, color = pen.GetWidth(), pen.GetColour() - fcolor, style = brush.GetColour(), brush.GetStyle() - - if 'color' in pts: - pen.SetColour(pts['color']) - if 'fcolor' in pts: - brush.SetColour(pts['fcolor']) - if 'lw' in pts: - pen.SetWidth(pts['lw']) - if 'fill' in pts: - brush.SetStyle((106,100)[pts['fill']]) - - dc.SetPen(pen) - dc.SetBrush(brush) - - if pts['type'] == 'circle': - x, y ,r = pts['body'] - x, y = f(x, y) - dc.DrawCircle(x, y, r*key['k']) - if pts['type'] == 'circles': - lst = [] - for x, y ,r in pts['body']: - x, y = f(x, y) - r *= key['k'] - lst.append((x-r,y-r,r*2,r*2)) - dc.DrawEllipseList(lst) - - pen.SetWidth(width) - pen.SetColour(color) - brush.SetColour(fcolor) - brush.SetStyle(style) - dc.SetPen(pen) - dc.SetBrush(brush) - -def make_ellipse(l1, l2, ang): - m = np.array([[l1*cos(-ang),-l2*sin(-ang)], - [l1*sin(-ang),l2*cos(-ang)]]) - a = np.linspace(0, np.pi*2, 36) - xys = np.array((np.cos(a), np.sin(a))) - return np.dot(m, xys).T - -def draw_ellipse(pts, dc, f, **key): - pen, brush = dc.GetPen(), dc.GetBrush() - width, color = pen.GetWidth(), pen.GetColour() - fcolor, style = brush.GetColour(), brush.GetStyle() - - if 'color' in pts: - pen.SetColour(pts['color']) - if 'fcolor' in pts: - brush.SetColour(pts['fcolor']) - if 'lw' in pts: - pen.SetWidth(pts['lw']) - if 'fill' in pts: - brush.SetStyle((106,100)[pts['fill']]) - - dc.SetPen(pen) - dc.SetBrush(brush) - - if pts['type'] == 'ellipse': - x, y ,l1, l2, a = pts['body'] - elp = make_ellipse(l1,l2,a) - elp = elp*key['k']+f(x,y) - dc.DrawPolygon(elp) - if pts['type'] == 'ellipses': - lst = [] - for x, y, l1, l2, a in pts['body']: - elp = make_ellipse(l1,l2,a) - lst.append(elp*key['k']+f(x,y)) - dc.DrawPolygonList(lst) - - - pen.SetWidth(width) - pen.SetColour(color) - brush.SetColour(fcolor) - brush.SetStyle(style) - dc.SetPen(pen) - dc.SetBrush(brush) - -def draw_rectangle(pts, dc, f, **key): - pen, brush = dc.GetPen(), dc.GetBrush() - width, color = pen.GetWidth(), pen.GetColour() - fcolor, style = brush.GetColour(), brush.GetStyle() - - if 'color' in pts: - pen.SetColour(pts['color']) - if 'fcolor' in pts: - brush.SetColour(pts['fcolor']) - if 'lw' in pts: - pen.SetWidth(pts['lw']) - if 'fill' in pts: - brush.SetStyle((106,100)[pts['fill']]) - - dc.SetPen(pen) - dc.SetBrush(brush) - - if pts['type'] == 'rectangle': - x, y, w, h = pts['body'] - x, y = f(x, y) - w, h = w*key['k'], h*key['k'] - dc.DrawRectangle(x-w/2, y-h/2, w, h) - if pts['type'] == 'rectangles': - lst = [] - for x, y, w, h in pts['body']: - x, y = f(x, y) - w, h = w*key['k'], h*key['k'] - lst.append((x-w/2, y-h/2, w, h)) - dc.DrawRectangleList(lst) - - pen.SetWidth(width) - pen.SetColour(color) - brush.SetColour(fcolor) - brush.SetStyle(style) - dc.SetPen(pen) - dc.SetBrush(brush) - -def draw_text(pts, dc, f, **key): - pen, brush, font = dc.GetPen(), dc.GetBrush(), dc.GetFont() - width, color = pen.GetWidth(), pen.GetColour() - fcolor, style = brush.GetColour(), brush.GetStyle() - size = font.GetPointSize() - tcolor = dc.GetTextForeground() - bcolor = dc.GetTextBackground() - - if 'color' in pts: - pen.SetColour(pts['color']) - dc.SetTextForeground(pts['color']) - brush.SetColour(pen.GetColour()) - brush.SetStyle(100) - if 'fcolor' in pts: - dc.SetTextBackground(pts['fcolor']) - if 'size' in pts: - font.SetPointSize(pts['size']) - - dc.SetPen(pen) - dc.SetBrush(brush) - dc.SetFont(font) - - if pts['type'] == 'text': - x, y, text = pts['body'] - x, y = f(x, y) - dc.DrawText(text, x+3, y+3) - if not 'pt' in pts or pts['pt']: - dc.DrawEllipse(x-2,y-2,4,4) - if pts['type'] == 'texts': - tlst, clst, elst = [], [], [] - for x, y, text in pts['body']: - x, y = f(x, y) - tlst.append(text) - clst.append((x+3, y+3)) - elst.append((x-2, y-2, 4, 4)) - dc.DrawTextList(tlst, clst) - if not 'pt' in pts or pts['pt']: - dc.DrawEllipseList(elst) - - font.SetPointSize(size) - pen.SetColour(color) - brush.SetColour(fcolor) - brush.SetStyle(style) - dc.SetPen(pen) - dc.SetBrush(brush) - dc.SetFont(font) - dc.SetTextForeground(tcolor) - dc.SetTextBackground(bcolor) - -draw_dic = {'points':plot, 'point':plot, 'line':plot, 'polygon':plot, 'lines':plot, 'polygons':plot, - 'circle':draw_circle, 'circles':draw_circle, 'ellipse':draw_ellipse, 'ellipses':draw_ellipse, - 'rectangle':draw_rectangle, 'rectangles':draw_rectangle, 'text':draw_text, 'texts':draw_text} - -def draw(obj, dc, f, **key): draw_dic[obj['type']](obj, dc, f, **key) - -def draw_layer(pts, dc, f, **key): - pen, brush = dc.GetPen(), dc.GetBrush() - width, color = pen.GetWidth(), pen.GetColour() - fcolor, style = brush.GetColour(), brush.GetStyle() - - if 'color' in pts: - pen.SetColour(pts['color']) - if 'fcolor' in pts: - brush.SetColour(pts['fcolor']) - if 'lw' in pts: - pen.SetWidth(pts['lw']) - if 'fill' in pts: - brush.SetStyle((106,100)[pts['fill']]) - - dc.SetPen(pen) - dc.SetBrush(brush) - - for i in pts['body']:draw(i, dc, f, **key) - - pen.SetWidth(width) - pen.SetColour(color) - brush.SetColour(fcolor) - brush.SetStyle(style) - dc.SetPen(pen) - dc.SetBrush(brush) - -draw_dic['layer'] = draw_layer - -def draw_layers(pts, dc, f, **key): - pen, brush = dc.GetPen(), dc.GetBrush() - width, color = pen.GetWidth(), pen.GetColour() - fcolor, style = brush.GetColour(), brush.GetStyle() - - if 'color' in pts: - pen.SetColour(pts['color']) - if 'fcolor' in pts: - brush.SetColour(pts['fcolor']) - if 'lw' in pts: - pen.SetWidth(pts['lw']) - if 'fill' in pts: - brush.SetStyle((106,100)[pts['fill']]) - - dc.SetPen(pen) - dc.SetBrush(brush) - if key['cur'] in pts['body']: - draw(pts['body'][key['cur']], dc, f, **key) - - pen.SetWidth(width) - pen.SetColour(color) - brush.SetColour(fcolor) - brush.SetStyle(style) - dc.SetPen(pen) - dc.SetBrush(brush) - -draw_dic['layers'] = draw_layers - -class GeometryMark: - def __init__(self, body): - self.body = body - - def draw(self, dc, f, **key): - pen, brush, font = dc.GetPen(), dc.GetBrush(), dc.GetFont() - pen.SetColour(ConfigManager.get('mark_color') or (255,255,0)) - brush.SetColour(ConfigManager.get('mark_fcolor') or (255,255,255)) - brush.SetStyle((106,100)[ConfigManager.get('mark_fill') or False]) - pen.SetWidth(ConfigManager.get('mark_lw') or 1) - dc.SetTextForeground(ConfigManager.get('mark_tcolor') or (255,0,0)) - font.SetPointSize(ConfigManager.get('mark_tsize') or 8) - dc.SetPen(pen); dc.SetBrush(brush); dc.SetFont(font); - draw(self.body, dc, f, **key) - -if __name__ == '__main__': - print(make_ellipse(0,0,2,1,0)) \ No newline at end of file diff --git a/imagepy/core/roi/__init__.py b/imagepy/core/roi/__init__.py deleted file mode 100644 index 53edc9b4..00000000 --- a/imagepy/core/roi/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .lineroi import LineRoi -from .ovalroi import OvalRoi -from .pointroi import PointRoi -from .polygonroi import PolygonRoi -from .rectangleroi import RectangleRoi \ No newline at end of file diff --git a/imagepy/core/roi/convert.py b/imagepy/core/roi/convert.py deleted file mode 100644 index 487de78e..00000000 --- a/imagepy/core/roi/convert.py +++ /dev/null @@ -1,97 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Mon Dec 19 03:05:20 2016 -@author: yxl -""" -from __future__ import absolute_import -from shapely.geometry import * - -def r2s_point(mp): - return MultiPoint(mp.body) - -def s2r_points(mp): - from .pointroi import PointRoi - body = list(LineString(mp).coords) - return PointRoi(body) - -def s2r_point(p): - from .pointroi import PointRoi - return PointRoi(list(p.coords)) - -def r2s_line(ml): - return MultiLineString(ml.body) - -def s2r_lines(ml): - from .lineroi import LineRoi - body = [list(i.coords for i in ml)] - return LineRoi(body) - -def s2r_line(ml): - from .lineroi import LineRoi - return LineRoi([list(ml.coords)]) - -def r2s_polygon(pg): - pgs = [] - for i in pg.body: - pgs.append(Polygon(i[0], i[1])) - return MultiPolygon(pgs) - -def s2r_polygon(pg): - from .polygonroi import PolygonRoi - if pg.exterior == None:return None - out = list(pg.exterior.coords) - inn = [list(i.coords) for i in list(pg.interiors)] - return PolygonRoi([[out, inn]]) - -def s2r_polygons(pg): - from .polygonroi import PolygonRoi - body = [] - for p in pg: - if p.exterior == None: continue - out = list(p.exterior.coords) - inn = [list(i.coords) for i in list(p.interiors)] - body.append([out, inn]) - if len(body)==0:return None - return PolygonRoi(body) - -def roi2shape(roi): - from .lineroi import LineRoi - from .polygonroi import PolygonRoi - from .pointroi import PointRoi - from .rectangleroi import RectangleRoi - from .ovalroi import OvalRoi - - if isinstance(roi, PointRoi): - return r2s_point(roi) - if isinstance(roi, LineRoi): - return r2s_line(roi) - if isinstance(roi, PolygonRoi): - return r2s_polygon(roi) - if isinstance(roi, RectangleRoi): - return r2s_polygon(roi.topolygon()) - if isinstance(roi, OvalRoi): - return r2s_polygon(roi.topolygon()) - -def shape2roi(shape): - if isinstance(shape, Point): - return s2r_point(shape) - if isinstance(shape, MultiPoint): - return s2r_points(shape) - if isinstance(shape, LineString): - return s2r_line(shape) - if isinstance(shape, MultiLineString): - return s2r_lines(shape) - if isinstance(shape, Polygon): - return s2r_polygon(shape) - if isinstance(shape, MultiPolygon): - return s2r_polygons(shape) - -if __name__ == '__main__': - import sys - sys.path.append('../../') - from .rectangleroi import RectangleRoi - from .ovalroi import OvalRoi - r = RectangleRoi(0,0,101,101) - o = OvalRoi(40,40,60,60) - sr = roi2shape(r) - so = roi2shape(o) \ No newline at end of file diff --git a/imagepy/core/roi/lineroi.py b/imagepy/core/roi/lineroi.py deleted file mode 100644 index 2adfbbf6..00000000 --- a/imagepy/core/roi/lineroi.py +++ /dev/null @@ -1,77 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Nov 11 12:10:17 2016 - -@author: yxl -""" -from __future__ import absolute_import -import wx -from ..draw import paint -from .roi import ROI -from ..manager import RoiManager - -class LineRoi(ROI): - dtype = 'line' - def __init__(self, body=None): - self.body = body if body!=None else [] - self.dirty = body!=None - self.infoupdate = body!=None - self.box = [1000,1000,-1000,-1000] - - def addline(self, line): - if len(line)!=2 or line[0] !=line[-1]: - self.body.append(line) - self.update, self.infoupdate = True, True - return True - - def snap(self, x, y, z, lim): - minl, idx = 1e8, None - for i in self.body: - for j in i: - d = (j[0]-x)**2+(j[1]-y)**2 - if d < minl:minl,idx = d,(i, i.index(j)) - return idx if minl**0.5self.box[2]:self.box[2]=x - if yself.box[3]:self.box[3]=y - - def get_box(self): - if self.infoupdate: - self.countbox() - self.infoupdate=False - return self.box - - def pick(self, x, y, z, lim): - return self.snap(x, y, z, lim) - - def draged(self, ox, oy, nx, ny, nz, i): - i[0][i[1]] = (nx, ny) - self.update, self.infoupdate = True, True - - def info(self, ips, cur): - k, u = ips.unit - if cur==None:return - x, y = cur[0][cur[1]] - IPy.set_info('Line : points:%.0f x:%.1f y:%.1f'%(len(cur[0]), x*k, y*k)) - - def draw(self, dc, f, **key): - dc.SetPen(wx.Pen(RoiManager.get_color(), width=RoiManager.get_lw(), style=wx.SOLID)) - for line in self.body: - if len(line)>1: - dc.DrawLines([f(*i) for i in line]) - for i in line:dc.DrawCircle(f(*i),2) - - def sketch(self, img, w=1, color=None): - pen = paint.Paint() - for i in self.body: - xs, ys = [x[0] for x in i], [x[1] for x in i] - pen.draw_path(img, xs, ys, w, color) - - def fill(self, img, color=None): - self.sketch(img, 1, color) \ No newline at end of file diff --git a/imagepy/core/roi/ovalroi.py b/imagepy/core/roi/ovalroi.py deleted file mode 100644 index 754071f3..00000000 --- a/imagepy/core/roi/ovalroi.py +++ /dev/null @@ -1,95 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Nov 11 21:29:59 2016 -@author: yxl -""" -import wx -import numpy as np -from ..draw import paint -from .roi import ROI -from .polygonroi import PolygonRoi -from ..manager import RoiManager - -class OvalRoi(ROI): - dtype = 'rect' - def __init__(self, l=0, t=0, r=0, b=0): - self.body = [] - self.update = False - self.lt, self.tp, self.rt, self.bm = l, t, r, b - self.commit() - - def snap(self, x, y, z, lim): - if abs(x-self.lt)1: - dc.DrawLines([f(*i) for i in self.body]) - for i in [self.lt, (self.lt+self.rt)/2, self.rt]: - for j in [self.tp, (self.tp+self.bm)/2, self.bm]: - dc.DrawCircle(f(i,j),2) - - def sketch(self, img, w=1, color=None): - pen = paint.Paint() - xs, ys = [x[0] for x in self.body], [x[1] for x in self.body] - pen.draw_path(img, xs, ys, w, color) - - def fill(self, img, color=None): - pen = paint.Paint() - pen.fill_polygon(self.body, img, [], color) \ No newline at end of file diff --git a/imagepy/core/roi/pointroi.py b/imagepy/core/roi/pointroi.py deleted file mode 100644 index 91e31bd6..00000000 --- a/imagepy/core/roi/pointroi.py +++ /dev/null @@ -1,87 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Tue Nov 8 22:35:55 2016 -@author: yxl -""" -import wx -from ..draw import paint -from .roi import ROI -from ..manager import RoiManager -import numpy as np - -class PointRoi(ROI): - dtype = 'point' - def __init__(self, body=None): - self.body = body if body!=None else [] - self.body = [i if len(i)==3 else tuple(i) + (0,) for i in self.body] - self.update = body!=[] - self.infoupdate = body!=[] - - def add(self, p): - self.body.append(p) - self.update, self.infoupdate = True, True - - def snap(self, x, y, z, lim): - cur, minl = None, 1e8 - for i in self.body: - if z!=i[2]:continue - d = (i[0]-x)**2+(i[1]-y)**2 - if d < minl:cur,minl = i,d - if minl**0.5>lim:return None - return self.body.index(cur) - - def pick(self, x, y, z, lim): - return self.snap(x, y, z, lim) - - def draged(self, ox, oy, nx, ny, nz, i): - self.body[i] = (nx, ny, nz) - self.update = False - - def countbox(self): - self.box = [1000,1000,-1000,-1000] - for x, y in self.body: - if xself.box[2]:self.box[2]=x - if yself.box[3]:self.box[3]=y - - def get_box(self): - if self.infoupdate: - self.countbox() - self.infoupdate=False - return self.box - - def info(self, ips, cur): - k, u = ips.unit - if cur==None:return - x, y, z = self.body[cur] - IPy.set_info('points:%.0f x:%.1f y:%.1f z:%.1f'%(len(self.body), x*k, y*k, z*k)) - - def draw(self, dc, f, **key): - - - - colormap = (tuple(np.array(RoiManager.get_color())//2), RoiManager.get_color()) - font = wx.Font(8, wx.FONTFAMILY_DEFAULT, - wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) - - dc.SetFont(font) - for c,r,z in self.body: - pos = f(*(c,r)) - dc.SetPen(wx.Pen(colormap[z==key['cur']], width=RoiManager.get_lw(), style=wx.SOLID)) - dc.SetTextForeground(colormap[z==key['cur']]) - dc.DrawCircle(pos[0], pos[1], 2) - dc.DrawText('z={}'.format(z), pos[0], pos[1]) - - def sketch(self, img, w=1, color=None): - pen = paint.Paint() - for i in self.body: - pen.draw_point(img, i[0], i[1], w, color) - - def fill(self, img, color=None): - self.sketch(img, 1, color) - -if __name__ == '__main__': - seq = [(0,0),(1,0),(0,1),(1,1),(0,0)] - p = Polygon(seq) - mp = toSegment(seq) \ No newline at end of file diff --git a/imagepy/core/roi/polygonroi.py b/imagepy/core/roi/polygonroi.py deleted file mode 100644 index 7aeaa472..00000000 --- a/imagepy/core/roi/polygonroi.py +++ /dev/null @@ -1,153 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Nov 11 12:10:33 2016 -@author: yxl -""" -import wx -from shapely.geometry import Polygon, Point, LineString -from shapely.ops import cascaded_union -from shapely.ops import polygonize -from ..draw import paint -from .roi import ROI -from ..manager import RoiManager - -def parse_poly(geom): - out = list(geom.exterior.coords) - inn = [list(i.coords) for i in list(geom.interiors)] - return [out, inn] - -def parse_mpoly(geom): - if geom.geom_type == 'Polygon': - return[i for i in [parse_poly(geom)] if i != None] - return [parse_poly(i) for i in list(geom)] - -def to_segment(pg): - p = Polygon(pg) if len(pg)>2 else Polygon(*pg) - if p.is_valid: return [p] - if len(pg)<3: pg = pg[0] - line = LineString(pg) - return polygonize([line.intersection(line)]) - -class PolygonRoi(ROI): - dtype = 'polygon' - def __init__(self, body=None): - self.body = body if body!=None else [] - self.dirty = body!=None - self.infoupdate = body!=None - self.box = [1000,1000,-1000,-1000] - - def update(self): self.dirty = True - - def addpoint(self, p): - self.buf[0].append(p) - - def commit(self, buf, oper): - pgs = [] - if len(buf[0])==0:return False - if buf[0][0]!=buf[0][-1]: - buf[0].append(buf[0][0]) - if len(self.body)==0: - self.body.append(buf) - buf = [[],[]] - self.update, self.infoupdate = True, True - return True - for i in self.body: - pgs.extend(to_segment(i)) - unin = cascaded_union(pgs) - cur = cascaded_union(list(to_segment(buf))) - if oper=='+':rst = unin.union(cur) - if oper=='-':rst = unin.difference(cur) - self.body = parse_mpoly(rst) - self.update, self.infoupdate = True, True - - def snap(self, x, y, z, lim): - if not self.issimple():return None - cur, minl = None, 1e8 - for i in self.body[0][0]: - d = (i[0]-x)**2+(i[1]-y)**2 - if d < minl:cur,minl = i,d - if minl**0.5>lim:return None - return self.body[0][0], self.body[0][0].index(cur) - - def pick(self, x, y, z, lim): - rst = self.snap(x, y, z, lim) - if rst!=None:return rst - - pgs = [] - for i in self.body: - pgs.extend(to_segment(i)) - unin = cascaded_union(pgs) - if unin.contains(Point(x,y)): - return True - return None - - def draged(self, ox, oy, nx, ny, nz, i): - self.update, self.infoupdate = True, True - if i==True: - for pg in self.body: - pg[0] = [(p[0]+(nx-ox), p[1]+(ny-oy)) for p in pg[0]] - for i in range(len(pg[1])): - pg[1][i] = [(p[0]+(nx-ox), p[1]+(ny-oy)) for p in pg[1][i]] - else: - i[0][i[1]] = (nx, ny) - #print 'drag,',i,self.body[0][0][i] - if i[1]==0:i[0][-1] = (nx,ny) - if i[1]==len(i[0])-1: - i[0][0] = (nx, ny) - - def info(self, ips, cur): - if cur==None:return - IPy.set_info('Polygon: %.0f fragments'%len(self.body)) - - def countbox(self): - self.box = [1000,1000,-1000,-1000] - for i in self.body: - for x,y in i[0]: - if xself.box[2]:self.box[2]=x - if yself.box[3]:self.box[3]=y - - def get_box(self): - if self.infoupdate: - self.countbox() - self.infoupdate=False - return self.box - - def issimple(self): - if len(self.body)==1 and len(self.body[0][1])==0: - return True - - def topolygon(self):return self - - ''' - def affine(self, m, o): - plg = PolygonRoi() - plg.body = affine(self.body, m, o) - plg.update() - plg.infoupdate() - return plg - ''' - - def draw(self, dc, f, **key): - dc.SetPen(wx.Pen(RoiManager.get_color(), width=RoiManager.get_lw(), style=wx.SOLID)) - for pg in self.body: - dc.DrawLines([f(*i) for i in pg[0]]) - if self.issimple(): - for i in pg[0]: dc.DrawCircle(f(*i),2) - for hole in pg[1]: - dc.DrawLines([f(*i) for i in hole]) - - def sketch(self, img, w=1, color=None): - pen = paint.Paint() - for i in self.body: - xs, ys = [x[0] for x in i[0]], [x[1] for x in i[0]] - pen.draw_path(img, xs, ys, w) - for j in i[1]: - xs, ys = [x[0] for x in j], [x[1] for x in j] - pen.draw_path(img, xs, ys, w, color) - - def fill(self, img, color=None): - pen = paint.Paint() - for i in self.body: - pen.fill_polygon(i[0], img, i[1], color) \ No newline at end of file diff --git a/imagepy/core/roi/rectangleroi.py b/imagepy/core/roi/rectangleroi.py deleted file mode 100644 index 53164742..00000000 --- a/imagepy/core/roi/rectangleroi.py +++ /dev/null @@ -1,96 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Nov 11 21:29:59 2016 -@author: yxl -""" -import wx -from ..draw import paint -from .polygonroi import PolygonRoi -from .roi import ROI -from ..manager import RoiManager - -class RectangleRoi(ROI): - dtype = 'rect' - def __init__(self, l=0, t=0, r=0, b=0): - self.body = [] - self.dirty = False - self.lt, self.tp, self.rt, self.bm = l, t, r, b - self.commit() - - def snap(self, x, y, z,lim): - if abs(x-self.lt)1): - dc.DrawLines([f(*i) for i in self.body]) - for i in self.body:dc.DrawCircle(f(*i),2) - dc.DrawCircle(f(self.lt, (self.tp+self.bm)/2),2) - dc.DrawCircle(f(self.rt, (self.tp+self.bm)/2),2) - dc.DrawCircle(f((self.lt+self.rt)/2, self.tp),2) - dc.DrawCircle(f((self.lt+self.rt)/2, self.bm),2) - - def sketch(self, img, w=1, color=None): - pen = paint.Paint() - xs, ys = [x[0] for x in self.body], [x[1] for x in self.body] - pen.draw_path(img, xs, ys, w, color) - - def fill(self, img, color=None): - pen = paint.Paint() - for i in self.body: - pen.fill_polygon(self.body, img, [], color) \ No newline at end of file diff --git a/imagepy/core/roi/roi.py b/imagepy/core/roi/roi.py deleted file mode 100644 index d12f6514..00000000 --- a/imagepy/core/roi/roi.py +++ /dev/null @@ -1,47 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Dec 21 15:05:16 2016 -@author: yxl -""" -from .convert import roi2shape, shape2roi -from shapely.affinity import affine_transform -from shapely.ops import transform - -class ROI: - def __init__(self):pass - - def buffer(self, r): - return shape2roi(roi2shape(self).buffer(r, 4)) - - def convex(self): - return shape2roi(roi2shape(self).convex_hull) - - def bounds(self): - from .rectangleroi import RectangleRoi - box = roi2shape(self).bounds - return RectangleRoi(*box) - - def clip(self, rect): - return shape2roi(roi2shape(rect).intersection(roi2shape(self))) - - def invert(self, rect): - return shape2roi(roi2shape(rect).difference(roi2shape(self))) - - def union(self, roi): - return shape2roi(roi2shape(roi).union(roi2shape(self))) - - def intersect(self, roi): - return shape2roi(roi2shape(roi).intersection(roi2shape(self))) - - def symmetric_diff(self, roi): - return shape2roi(roi2shape(roi).symmetric_difference(roi2shape(self))) - - def diff(self, roi): - return shape2roi(roi2shape(self).difference(roi2shape(roi))) - - def affine(self, m, o): - mat = [m[0,0], m[0,1], m[1,0], m[1,1], o[0], o[1]] - return shape2roi(affine_transform(roi2shape(self), mat)) - - def transform(self, f): - return shape2roi(transform(f, roi2shape(self))) \ No newline at end of file diff --git a/imagepy/core/roi/roiio.py b/imagepy/core/roi/roiio.py deleted file mode 100644 index 67a1738c..00000000 --- a/imagepy/core/roi/roiio.py +++ /dev/null @@ -1,28 +0,0 @@ -from .convert import roi2shape, shape2roi -import pickle -from shapely import wkt - -def roi2wkt(roi): return wkt.dumps(roi2shape(roi)) -def wkt2roi(con): return shape2roi(wkt.loads(con)) - -def readroi(path): - f = open(path, 'rb') - roi = pickle.load(f) - f.close() - return roi - -def readwkt(path): - f = open(path) - roi = wkt2roi(f.read()) - f.close() - return roi - -def saveroi(roi, path): - f = open(path, 'wb') - pickle.dump(roi, f) - f.close() - -def savewkt(roi, path): - f = open(path, 'w') - f.write(roi2wkt(roi)) - f.close() diff --git a/imagepy/core/util/__init__.py b/imagepy/core/util/__init__.py index f3425184..f8491474 100644 --- a/imagepy/core/util/__init__.py +++ b/imagepy/core/util/__init__.py @@ -1,2 +1 @@ -from .fileio import * -from .testdata import * \ No newline at end of file +from .fileio import * \ No newline at end of file diff --git a/imagepy/core/util/fileio.py b/imagepy/core/util/fileio.py index 45f23b58..44c6faf5 100644 --- a/imagepy/core/util/fileio.py +++ b/imagepy/core/util/fileio.py @@ -1,21 +1,12 @@ import os -from ..manager import ConfigManager -from ..manager import ReaderManager, WriterManager +from sciapp import Source from ... import root_dir from ..engine import Free, Simple, Macros import numpy as np -def show_img(img, title): - if isinstance(img, list): - return IPy.show_img(img, title) - if img.ndim>2 and img.shape[2]!=3: - return IPy.show_img(img, title) - if img.dtype==np.uint8 and img.ndim==3 and img.shape[2]==4: - img = img[:,:,:3].copy() - IPy.show_img([img], title) # ViewerManager.add('imgs', IPy.show_img) -recent = ConfigManager.get('recent') +recent = Source.manager('config').get('recent') if recent==None : recent = [] def f(path): @@ -36,7 +27,7 @@ def add_recent(path): recent.pop(-1) rlist.pop(-1) - ConfigManager.set('recent', recent) + Source.manager('config').add('recent', recent) #IPy.curapp.reload_plugins() class Reader(Free): @@ -53,7 +44,7 @@ def run(self, para = None): fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) - reader = ReaderManager.gets(name=fe[1:], tag=self.tag, note=self.note) + reader = Source.manager('reader').gets(name=fe[1:], tag=self.tag, note=self.note) print(fe, self.tag, self.note, reader) ''' if len(reader) == 0: @@ -78,7 +69,7 @@ def show(self): def run(self, ips, imgs, para = None): fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) - writer = WriterManager.get(ext=fe[1:], tag='img') + writer = Source.manager('writer').get(ext=fe[1:], tag='img') if len(writer)==1: return writer[0][1](para['path'], ips.img) - writer = WriterManager.get(fe[1:], 'imgs') + writer = Source.manager('writer').get(fe[1:], 'imgs') if len(writer)==1: return writer[0][1](para['path'], imgs) \ No newline at end of file diff --git a/imagepy/core/util/tableio.py b/imagepy/core/util/tableio.py index 11d7f013..0595095c 100644 --- a/imagepy/core/util/tableio.py +++ b/imagepy/core/util/tableio.py @@ -1,7 +1,7 @@ import os -from ..manager import ReaderManager, WriterManager from ... import root_dir from ..engine import Free, Table, Macros +from sciapp import Source import numpy as np class Reader(Free): @@ -16,7 +16,7 @@ def run(self, para = None): fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) - read = ReaderManager.get(fe[1:], tag = 'tab') + read = Source.manager('reader').get(fe[1:], tag = 'tab') table = read(para['path']) ViewerManager.get(fe[1:])(table, fn) @@ -33,5 +33,5 @@ def show(self): def run(self, tps, snap, data, para = None): fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) - write = WriterManager.get(fe[1:], tag='tab') + write = Source.manager('writer').get(fe[1:], tag='tab') write(para['path'], data) \ No newline at end of file diff --git a/imagepy/core/util/testdata.py b/imagepy/core/util/testdata.py deleted file mode 100644 index 975fc92a..00000000 --- a/imagepy/core/util/testdata.py +++ /dev/null @@ -1,41 +0,0 @@ -#from imagepy import IPy -from imagepy.core.engine import Free -from imagepy.core.manager import ReaderManager, WriterManager -from skimage.io import imread -import os.path as osp, os -import numpy as np - -class TestData(Free): - def __init__(self, path, name): - self.name = name - self.path = path - self.title = name.split('.')[0].replace('\\', '/').split('/')[-1] - - def run(self, para = None): - root_dir = osp.abspath(osp.dirname(self.path)) - print(root_dir) - path = osp.join(root_dir, self.name) - if osp.isfile(path): - fp, fn = osp.split(path) - fn, fe = osp.splitext(fn) - read = ReaderManager.get(fe[1:]) - view = ViewerManager.get(fe[1:]) - - group, read = (True, read[0]) if isinstance(read, tuple) else (False, read) - img = read(path) - if img.dtype==np.uint8 and img.ndim==3 and img.shape[2]==4: - img = img[:,:,:3].copy() - if not group: img = [img] - else: - names = [i for i in os.listdir(path) if '.' in i] - read = ReaderManager.get(names[0].split('.')[1]) - view = ViewerManager.get(names[0].split('.')[1]) - imgs = [] - for i in range(len(names)): - self.progress(i, len(names)) - imgs.append(read(osp.join(path,names[i]))) - img = imgs - view(img, self.title) - - def __call__(self): - return self \ No newline at end of file diff --git a/imagepy/core/wraper/__init__.py b/imagepy/core/wraper/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/imagepy/core/wraper/imageplus.py b/imagepy/core/wraper/imageplus.py deleted file mode 100644 index c55cea30..00000000 --- a/imagepy/core/wraper/imageplus.py +++ /dev/null @@ -1,92 +0,0 @@ -import numpy as np -from time import time -from sciapp.object import Image - -def get_img_type(imgs): - if imgs[0].ndim==3 and imgs[0].dtype==np.uint8:return 'rgb' - if imgs[0].dtype == np.uint8:return '8-bit' - if imgs[0].dtype == np.uint16:return '16-bit' - if imgs[0].dtype == np.int32:return '32-int' - if imgs[0].dtype == np.float32:return '32-float' - if imgs[0].dtype == np.float64:return '64-float' - if imgs[0].dtype == np.complex128:return '128-complex' - if imgs[0].dtype == np.complex64:return '64-complex' - -class ImagePlus(Image): - """ImagePlus: a class to make operation more flexible """ - def __init__(self, imgs, title=None, is3d=False): - self.set_title(title) - self.snap = None - self.cur = self.chan = 0 - self.dirty = True - self.scrchanged = False - self.roi = None - self.mark = None - self.msk = None - self.mskmode = None - self.lut = ColorManager.get_lut('grays') - self.log = False - - self.tool = None - self.data = {} - self.unit = (1, 'pix') - - self.back = None - self.chan_range = [] - self.chan_mode = 'min' - self.set_imgs(imgs) - - @property - def itype(self): - return get_img_type(self.imgs[0]) - - def get_msk(self, mode='in'): - if self.roi==None:return None - if self.msk is None: - self.msk = np.zeros(self.size, dtype=np.bool) - if self.roi.update or mode!=self.mskmode: - self.msk[:] = 0 - if isinstance(mode, int): - self.roi.sketch(self.msk, w=mode, color=True) - else: self.roi.fill(self.msk, color=True) - if mode=='out':self.msk^=True - self.roi.update = False - self.mskmode=mode - return self.msk - - def get_rect(self): - if self.roi==None:return slice(None), slice(None) - box = self.roi.get_box() - l, r = max(0, int(box[0])), min(self.size[1], int(box[2])) - t, b = max(0, int(box[1])), min(self.size[0], int(box[3])) - return slice(t,b), slice(l,r) - - def get_subimg(self, s1=None, s2=None): - if s1==None: - s = self.get_rect() - if s==None:return self.img - return self.img[s[0], s[1]] - - def reset(self, msk=False): - if not self.snap is None: - if msk and not self.get_msk('out') is None: - msk = self.get_msk('out') - self.imgs[self.cur][msk] = self.snap[msk] - else : self.imgs[self.cur][:] = self.snap - - def __del__(self): - print(self.title, '>>> delete ips') - -if __name__=='__main__': - from skimage.io import imread - img = imread('results.bmp') - ips = ImagePlus([img, 255-img]) - - from ui.canvasframe import CanvasFrame - import wx - - app = wx.PySimpleApp() - frame = CanvasFrame() - frame.set_ips(ips) - frame.Show() - app.MainLoop() \ No newline at end of file diff --git a/imagepy/core/wraper/tableplus.py b/imagepy/core/wraper/tableplus.py deleted file mode 100644 index 97a91d7d..00000000 --- a/imagepy/core/wraper/tableplus.py +++ /dev/null @@ -1,72 +0,0 @@ -import numpy as np -import pandas as pd - -class TablePlus(): - def __init__(self, data, title=None): - self.set_title(title) - self.dirty, self.range = None, None - self.default = [3, (0,0,0), (0,0,255), 'Text'] - self.set_data(data) - self.snap = None - - def update(self, value=True): self.dirty=value - - def set_title(self, title): - self.title = TableManager.name(title) - - def get_nbytes(self): - return self.data.memory_usage().sum() - - def get_str(self, row, col): - ser = self.data[self.columns[col]] - - def set_data(self, data): - self.data, self.props = data, None - self.rowmsk, self.colmsk = [],[] - self.count_range() - - def get_subtab(self, mskr=True, mskc=True, num=False): - if mskr==None:rmsk = slice(None) - rowmsk = self.rowmsk if len(self.rowmsk)>0 else self.data.index - if mskr==True:rmsk = rowmsk - if mskr==False:rmsk = self.data.index.difference(rowmsk) - - if mskc==None:cmsk = slice(None) - colmsk = self.colmsk if len(self.colmsk)>0 else self.data.columns - if mskc==True:cmsk = colmsk - if mskc==False:cmsk = self.data.columns.difference(colmsk) - - subtab = self.data.loc[rmsk, cmsk] - if num: subtab = subtab.select_dtypes(include=[np.number]) - return subtab - - def snapshot(self, mskr=None, mskc=None, num=False): - self.snap = self.get_subtab(mskr, mskc, num).copy() - - def select(self, rs=[], cs=[], byidx=False): - if byidx: rs, cs = self.data.index[rs], self.data.columns[cs] - self.rowmsk = pd.Index(rs).astype(self.data.index.dtype) - self.colmsk = pd.Index(cs).astype(self.data.columns.dtype) - # print('tps select', rs, cs, self.rowmsk, self.colmsk) - - def get_titles(self):return self.data.columns - - def get_index(self):return self.data.index - - def get_props(self): - props, data = self.props, self.data - if self.props is None or len(props.columns)!=len(data.columns)\ - or (props.columns != data.columns).sum()>0: - ndata = [[i]*data.shape[1] for i in self.default] - newprop = pd.DataFrame(ndata, ['round', 'tc', 'lc', 'ln'], data.columns) - if not self.props is None: - inter = self.props.columns.intersection(newprop.columns) - newprop[inter] = self.props[inter] - self.props = newprop - return self.props - - def count_range(self): - self.range = pd.DataFrame([self.data.min(), self.data.max()]) - - def __del__(self): - print(self.title, '>>> deleted tps') \ No newline at end of file diff --git a/imagepy/menus/Edit/edit_plg.py b/imagepy/menus/Edit/edit_plg.py index bb00993b..34adda30 100644 --- a/imagepy/menus/Edit/edit_plg.py +++ b/imagepy/menus/Edit/edit_plg.py @@ -6,8 +6,7 @@ import numpy as np from imagepy.core.pixel import bliter from imagepy.core.engine import Simple, Tool, Filter -from imagepy.core.roi.rectangleroi import RectangleRoi -from imagepy.core.manager import ClipBoardManager, ColorManager +from sciapp import Source class PasteMove(Tool): def __init__(self): @@ -26,7 +25,7 @@ def mouse_down(self, ips, x, y, btn, **key): def mouse_up(self, ips, x, y, btn, **key): if self.moving == True: self.moving = False - ci = ClipBoardManager.img + ci = self.app.manager('xxxxxx') img = ips.img #ips.roi.draged(ci.shape[1]/2,ci.shape[0]/2, ips.size[1]/2, ips.size[0]/2, True) #ips.roi = IPy.clipboard[1].affine(np.eye(2), ((np.array(ips.size)-ci.shape[:2])[::-1]/2)) diff --git a/imagepy/menus/File/BMP/bmp_plgs.py b/imagepy/menus/File/BMP/bmp_plgs.py index 31885945..5c556e0b 100644 --- a/imagepy/menus/File/BMP/bmp_plgs.py +++ b/imagepy/menus/File/BMP/bmp_plgs.py @@ -1,9 +1,9 @@ from imagepy.core.util import fileio from skimage.io import imread, imsave -from imagepy.core.manager import ReaderManager, WriterManager +from sciapp import Source -ReaderManager.add(name='bmp', obj=imread) -WriterManager.add(name='bmp', obj=imsave) +Source.manager('reader').add(name='bmp', obj=imread) +Source.manager('writer').add(name='bmp', obj=imsave) class OpenFile(fileio.Reader): title = 'BMP Open' diff --git a/imagepy/menus/File/DAT/dat_plgs.py b/imagepy/menus/File/DAT/dat_plgs.py index d89e1057..0b57fb25 100644 --- a/imagepy/menus/File/DAT/dat_plgs.py +++ b/imagepy/menus/File/DAT/dat_plgs.py @@ -1,6 +1,6 @@ from imagepy.core.util import fileio import numpy as np -from imagepy.core.manager import ReaderManager, WriterManager +from sciapp import Source def imread(path): return np.loadtxt(path,dtype=float) @@ -8,8 +8,8 @@ def imread(path): def imsave(path,img): np.savetxt(path,img) -ReaderManager.add(name='dat', obj=imread) -WriterManager.add(name='dat', obj=imsave) +Source.manager('reader').add(name='dat', obj=imread) +Source.manager('writer').add(name='dat', obj=imsave) class OpenFile(fileio.Reader): title = 'DAT Open' diff --git a/imagepy/menus/File/DICOM/dicom_plgs.py b/imagepy/menus/File/DICOM/dicom_plgs.py index 6e7a3f63..af7dcb48 100644 --- a/imagepy/menus/File/DICOM/dicom_plgs.py +++ b/imagepy/menus/File/DICOM/dicom_plgs.py @@ -1,13 +1,13 @@ from imagepy.core.util import fileio import pydicom -from imagepy.core.manager import ReaderManager, WriterManager +from sciapp import Source def imread(path): return pydicom.read_file(path, force=True).pixel_array -ReaderManager.add(name='dcm', obj=imread) +Source.manager('reader').add(name='dcm', obj=imread) class OpenFile(fileio.Reader): title = 'DCM Open' diff --git a/imagepy/menus/File/Export/sequence_plg.py b/imagepy/menus/File/Export/sequence_plg.py index 6729c4a8..0f852fa9 100644 --- a/imagepy/menus/File/Export/sequence_plg.py +++ b/imagepy/menus/File/Export/sequence_plg.py @@ -7,7 +7,7 @@ import wx from skimage.io import imsave from imagepy.core.engine import Simple -from imagepy.core.manager import WriterManager +from sciapp import Source class Plugin(Simple): title = 'Save Sequence' @@ -29,7 +29,7 @@ def show(self): #process def run(self, ips, imgs, para = None): path = para['path']+'/'+para['name'] - write = WriterManager.get(para['format']) + write = Source.manager('writer').get(para['format']) print(path) for i in range(len(imgs)): self.progress(i, len(imgs)) diff --git a/imagepy/menus/File/GIF/gif_plgs.py b/imagepy/menus/File/GIF/gif_plgs.py index caad9f3e..260ab267 100644 --- a/imagepy/menus/File/GIF/gif_plgs.py +++ b/imagepy/menus/File/GIF/gif_plgs.py @@ -1,9 +1,9 @@ from imagepy.core.util import fileio from skimage.io import imread, imsave -from imagepy.core.manager import ReaderManager, WriterManager +from sciapp import Source -ReaderManager.add(name='gif', obj=imread) -WriterManager.add(name='gif', obj=imsave) +Source.manager('reader').add(name='gif', obj=imread) +Source.manager('writer').add(name='gif', obj=imsave) class OpenFile(fileio.Reader): title = 'GIF Open' diff --git a/imagepy/menus/File/Import/sequence_plg.py b/imagepy/menus/File/Import/sequence_plg.py index f9cf5ec6..d3532acf 100644 --- a/imagepy/menus/File/Import/sequence_plg.py +++ b/imagepy/menus/File/Import/sequence_plg.py @@ -6,7 +6,7 @@ from imagepy.core.util import fileio from skimage.io import imread -from imagepy.core.manager import ReaderManager +from sciapp import Source from imagepy.core.engine import Free from glob import glob import wx, os @@ -16,7 +16,7 @@ class Plugin(Free): para = {'path':'', 'start':0, 'end':0, 'step':1, 'title':'sequence'} def load(self): - self.filt = ReaderManager.gets('name') + self.filt = Source.manager('reader').names() return True def show(self): @@ -53,7 +53,7 @@ def readimgs(self, names, read, shape, dtype): def run(self, para = None): fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) - read = ReaderManager.get(name=fe[1:]) + read = Source.manager('reader').get(name=fe[1:]) try: img = read(para['path']) except: return self.app.alert('unknown img format!') files = self.getfiles(para['path']) diff --git a/imagepy/menus/File/JPG/jpg_plgs.py b/imagepy/menus/File/JPG/jpg_plgs.py index 177a75c6..34de84e3 100644 --- a/imagepy/menus/File/JPG/jpg_plgs.py +++ b/imagepy/menus/File/JPG/jpg_plgs.py @@ -1,11 +1,11 @@ from imagepy.core.util import fileio from imageio import imread, imsave -from imagepy.core.manager import ReaderManager, WriterManager +from sciapp import Source -ReaderManager.add(name='jpg', obj=imread) -WriterManager.add(name='jpg', obj=imsave) -ReaderManager.add(name='jpeg', obj=imread) -WriterManager.add(name='jpeg', obj=imsave) +Source.manager('reader').add(name='jpg', obj=imread) +Source.manager('writer').add(name='jpg', obj=imsave) +Source.manager('reader').add(name='jpeg', obj=imread) +Source.manager('writer').add(name='jpeg', obj=imsave) class OpenFile(fileio.Reader): title = 'JPG Open' diff --git a/imagepy/menus/File/MAT/mat_plgs.py b/imagepy/menus/File/MAT/mat_plgs.py index 4d4f93dd..7b8a1096 100644 --- a/imagepy/menus/File/MAT/mat_plgs.py +++ b/imagepy/menus/File/MAT/mat_plgs.py @@ -1,10 +1,10 @@ from imagepy.core.util import fileio from scipy.io import savemat, loadmat -from imagepy.core.manager import ReaderManager, WriterManager +from sciapp import Source import os -ReaderManager.add(name='mat', obj=lambda path: loadmat(path)['img']) -WriterManager.add(name='mat', obj=lambda path, img: savemat(path, {'img':img})) +Source.manager('reader').add(name='mat', obj=lambda path: loadmat(path)['img']) +Source.manager('writer').add(name='mat', obj=lambda path, img: savemat(path, {'img':img})) class OpenFile(fileio.Reader): title = 'Mat Open' diff --git a/imagepy/menus/File/Numpy/ndarray_plgs.py b/imagepy/menus/File/Numpy/ndarray_plgs.py index 527c3762..aa1e3d70 100644 --- a/imagepy/menus/File/Numpy/ndarray_plgs.py +++ b/imagepy/menus/File/Numpy/ndarray_plgs.py @@ -1,10 +1,10 @@ from imagepy.core.util import fileio import numpy as np -from imagepy.core.manager import ReaderManager, WriterManager +from sciapp import Source import os -ReaderManager.add(name='npy', obj=np.load) -WriterManager.add(name='npy', obj=np.save) +Source.manager('reader').add(name='npy', obj=np.load) +Source.manager('writer').add(name='npy', obj=np.save) class OpenFile(fileio.Reader): title = 'Numpy Open' diff --git a/imagepy/menus/File/PNG/png_plgs.py b/imagepy/menus/File/PNG/png_plgs.py index a73b8fa5..b13cba78 100644 --- a/imagepy/menus/File/PNG/png_plgs.py +++ b/imagepy/menus/File/PNG/png_plgs.py @@ -1,9 +1,9 @@ from imagepy.core.util import fileio from skimage.io import imread, imsave -from imagepy.core.manager import ReaderManager, WriterManager +from sciapp import Source -ReaderManager.add(name='png', obj=imread) -WriterManager.add(name='png', obj=imsave) +Source.manager('reader').add(name='png', obj=imread) +Source.manager('writer').add(name='png', obj=imsave) class OpenFile(fileio.Reader): title = 'PNG Open' diff --git a/imagepy/menus/File/TIF/tif_plgs.py b/imagepy/menus/File/TIF/tif_plgs.py index 6efb9ea5..7cd14de0 100644 --- a/imagepy/menus/File/TIF/tif_plgs.py +++ b/imagepy/menus/File/TIF/tif_plgs.py @@ -1,10 +1,10 @@ from imagepy.core.util import fileio from skimage.io import imread, imsave -from imagepy.core.manager import ReaderManager, WriterManager +from sciapp import Source -ReaderManager.add(name='tif', obj=imread) -ReaderManager.add(name='tiff', obj=imread) -WriterManager.add(name='tif', obj=imsave) +Source.manager('reader').add(name='tif', obj=imread) +Source.manager('reader').add(name='tiff', obj=imread) +Source.manager('writer').add(name='tif', obj=imsave) class OpenFile(fileio.Reader): title = 'TIF Open' diff --git a/imagepy/menus/File/open_plg.py b/imagepy/menus/File/open_plg.py index a9041b9a..0f96544b 100644 --- a/imagepy/menus/File/open_plg.py +++ b/imagepy/menus/File/open_plg.py @@ -4,16 +4,15 @@ from urllib.request import urlopen from io import BytesIO as StringIO -from imagepy.core import manager from imagepy.core.engine import Free from imagepy.core.util import fileio -from imagepy.core.manager import ReaderManager +from sciapp import Source class OpenFile(fileio.Reader): title = 'Open' def load(self): - self.filt = [i for i in sorted(ReaderManager.gets('name'))] + self.filt = [i for i in sorted(Source.manager('reader').names())] return True class OpenUrl(Free): diff --git a/imagepy/menus/File/save_plg.py b/imagepy/menus/File/save_plg.py index 52a60268..802a7944 100644 --- a/imagepy/menus/File/save_plg.py +++ b/imagepy/menus/File/save_plg.py @@ -4,14 +4,14 @@ @author: yxl """ from imagepy.core.util import fileio -from imagepy.core.manager import WriterManager +from sciapp import Source from imagepy.core.engine import Simple class SaveImage(fileio.Writer): title = 'Save' def load(self, ips): - self.filt = [i[0] for i in sorted(WriterManager.get(tag='img'))] + self.filt = [i[0] for i in sorted(Source.manager('writer').names())] return True class WindowCapture(fileio.Writer): diff --git a/imagepy/menus/Help/Language/language_plgs.py b/imagepy/menus/Help/Language/language_plgs.py index 69e30ead..a39b9922 100644 --- a/imagepy/menus/Help/Language/language_plgs.py +++ b/imagepy/menus/Help/Language/language_plgs.py @@ -1,4 +1,4 @@ -from imagepy.core.manager import ColorManager, LanguageManager +from sciapp import Source from imagepy.core.engine import Free class Language(Free): @@ -8,13 +8,13 @@ def __init__(self, key): #process def run(self, para = None): - LanguageManager.set(self.title) + Manager.set(self.title) self.app.reload_plugins() def __call__(self): return self -plgs = [Language(i) for i in list(LanguageManager.langs.keys())] +plgs = [Language(i) for i in list(Source.manager('language').names())] plgs.insert(0, Language('English')) plgs.append('-') diff --git a/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py b/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py index b186ffca..65556ef8 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py +++ b/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py @@ -1,5 +1,5 @@ from imagepy.core.engine import Table -from imagepy.core.manager import ColorManager +from sciapp import Source import numpy as np class Plugin(Table): diff --git a/imagepy/menus/Plugins/Macros/recorder_plg.py b/imagepy/menus/Plugins/Macros/recorder_plg.py index 39be2e9a..394e5962 100644 --- a/imagepy/menus/Plugins/Macros/recorder_plg.py +++ b/imagepy/menus/Plugins/Macros/recorder_plg.py @@ -1,5 +1,5 @@ from imagepy.core.util import fileio -from imagepy.core.manager import ReaderManager +from sciapp import Source def readmc(path): with open(path) as f: @@ -10,4 +10,4 @@ class Plugin(fileio.Reader): tag = 'macros' filt = ['MC'] -ReaderManager.add(name='mc', tag='macros', obj=readmc) \ No newline at end of file +Source.manager('reader').add(name='mc', tag='macros', obj=readmc) \ No newline at end of file diff --git a/imagepy/menus/Plugins/Manager/plgtree_wgt.py b/imagepy/menus/Plugins/Manager/plgtree_wgt.py index 1a47fe40..f297f7e5 100644 --- a/imagepy/menus/Plugins/Manager/plgtree_wgt.py +++ b/imagepy/menus/Plugins/Manager/plgtree_wgt.py @@ -8,10 +8,10 @@ from imagepy.core.engine import Free import wx,os from imagepy import root_dir -from imagepy.core.loader import loader +from imagepy.core.app import loader from wx.py.editor import EditorFrame from sciwx.text import MDPad -from imagepy.core.manager import DocumentManager +from sciapp import Source from glob import glob class Plugin ( wx.Panel ): @@ -102,7 +102,7 @@ def on_select( self, event ): if plg!=None: self.plg = plg name = self.tre_plugins.GetItemText(event.GetItem()) - self.txt_info.set_cont(DocumentManager.get(name=name)) + self.txt_info.set_cont(Source.manager('document').get(name)) def on_source(self, event): ## TODO: should it be absolute path ? diff --git a/imagepy/menus/Plugins/Manager/shotcut_wgt.py b/imagepy/menus/Plugins/Manager/shotcut_wgt.py index c143662c..7925e7fd 100644 --- a/imagepy/menus/Plugins/Manager/shotcut_wgt.py +++ b/imagepy/menus/Plugins/Manager/shotcut_wgt.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- import wx, os from imagepy.core.engine import Free -from imagepy.core.manager import ShotcutManager from sciapp import Source from imagepy import root_dir @@ -65,8 +64,8 @@ def __init__( self, parent): #def list_plg(self, lst, items def load(self): - lst = Source.manager('plugin').gets(item='name') - self.plgs = [[i, ShotcutManager.get(item='shotcut', name=i)] for i in lst] + lst = Source.manager('plugin').names() + self.plgs = [[i, Source.manager('shotcut').get(item='shotcut', name=i)] for i in lst] for i in self.plgs: if i[1]==None:i[1]='' self.plgs.sort() @@ -117,9 +116,9 @@ def on_run(self, event): if len(txt)>0 and txt[-1]=='-':txt=txt[:-1] self.buf[event.GetIndex()][1] = txt self.lst_plgs.RefreshItem(event.GetIndex()) - ShotcutManager.remove(name=title) - if txt!='': ShotcutManager.add(name=title, shotcut=txt) + Source.manager('shotcut').remove(name=title) + if txt!='': Source.manager('shotcut').add(title, txt) #PluginsManager.plgs[self.buf[event.GetIndex()][0]]().start() def close(self): - ShotcutManager.write(os.path.join(root_dir,'data/shotcut.cfg')) + Source.manager('shotcut').write(os.path.join(root_dir,'data/shotcut.cfg')) diff --git a/imagepy/menus/Plugins/Manager/toltree_wgt.py b/imagepy/menus/Plugins/Manager/toltree_wgt.py index 1c0029c8..ca9f0351 100644 --- a/imagepy/menus/Plugins/Manager/toltree_wgt.py +++ b/imagepy/menus/Plugins/Manager/toltree_wgt.py @@ -8,11 +8,11 @@ from imagepy.core.engine import Free import wx,os from imagepy import root_dir -from imagepy.core.loader import loader +from imagepy.core.app import loader from wx.py.editor import EditorFrame +from sciapp import Source #from imagepy.ui.mkdownwindow import HtmlPanel, md2html from sciwx.text import MDPad -from imagepy.core.manager import DocumentManager from glob import glob class Plugin ( wx.Panel ): @@ -102,7 +102,7 @@ def on_select( self, event ): if plg!=None: self.plg = plg name = self.tre_plugins.GetItemText(event.GetItem()) - self.txt_info.set_cont(DocumentManager.get(name=name)) + self.txt_info.set_cont(Source.manager('document').get(name)) def on_source(self, event): ## TODO: should it be absolute path ? diff --git a/imagepy/menus/Plugins/detect_plg.py b/imagepy/menus/Plugins/detect_plg.py deleted file mode 100644 index b1bb0d8b..00000000 --- a/imagepy/menus/Plugins/detect_plg.py +++ /dev/null @@ -1,161 +0,0 @@ -import numpy as np -from numba import jit - -def neighbors(shape): # 邻居坐标转索引内存 - dim = len(shape) - block = np.ones([3]*dim) - block[tuple([1]*dim)] = 0 - idx = np.where(block>0) - idx = np.array(idx, dtype=np.uint8).T - idx = np.array(idx-[1]*dim) - acc = np.cumprod((1,)+shape[::-1][:-1]) - return np.dot(idx, acc[::-1]) - -@jit # trans index to r, c... -def idx2rc(idx, acc): # 索引转坐标 - rst = np.zeros((len(idx), len(acc)), dtype=np.int16) - for i in range(len(idx)): - for j in range(len(acc)): - rst[i,j] = idx[i]//acc[j] - idx[i] -= rst[i,j]*acc[j] - return rst - -@jit # fill a node (may be two or more points) -def fill(img, msk, p, nbs, buf): - buf[0] = p - back = img[p] - cur = 0; s = 1; - - while curthr:continue # 大于阈值,不继续 - sta = 0 - for dp in nbs: # 获取邻居像素索引 - if img[p+dp]==img[p]:sta+=1 # 如果周围的有一样大的,标记+1 - if mode and img[p+dp]>img[p]: # 如果周围的有更大的,标记为100 - sta = 100 - break - elif not mode and img[p+dp]0: - fill(img, msk, p, nbs, buf) # 如果有若干一样大的,将一样大的区域填充(对于大块平坦区域,可以有效提升性能) - idx[s] = p # 装入极值点 - s += 1 - if s==len(idx):break # 装满退出(异常情况) - return idx[:s].copy() # 截断拷贝 - -@jit # 3 max 2 zmd b4 ptd -def filter(img, msk, idx, bur, tor, area, mode): - nbs = neighbors(img.shape) - img = img.ravel() - msk = msk.ravel() - - arg = np.argsort(img[idx])[::-1 if mode else 1] # 将极值点排序 - - for i in arg: # 从最小的点开始遍历 - if msk[idx[i]]!=3: # 如果标记位置已经不是3,则跳过 - idx[i] = 0 - continue - cur = 0; s = 1; - bur[0] = idx[i] - while cur area: # 面积超出,不符合,擦除退出 - idx[i]=0 - break - for dp in nbs: # 邻居压栈 - cp = p+dp - if msk[cp]==0 or cp==idx[i] or msk[cp] == 4: continue # 边缘不加,自己不加,已经标记不加 - if mode and img[cp] < img[idx[i]]-tor: continue # 超过水位高度不加 - if not mode and img[cp] > img[idx[i]]+tor: continue # 极小值情况 - bur[s] = cp - s += 1 - if s==msk.size//3: # 装满清空 - cut = cur//2 - msk[bur[:cut]] = 2 # 被填充的点设定为2 (占领) - bur[:s-cut] = bur[cut:] - cur -= cut - s -= cut - - if msk[cp]!=2:msk[cp] = 4 # 本次标记 - cur += 1 - msk[bur[:s]] = 2 # 将领地标记为2 - return idx # 返回有效极值坐标 - -def find_maximum(img, tor, thr, area, mode = True, mar=1): - msk = np.zeros_like(img, dtype=np.uint8) # 复制掩膜数组 - msk[tuple([slice(mar,-mar)]*img.ndim)] = 1 # 中间部分设定为1,边缘是0 - buf = np.zeros(img.size//2, dtype=np.int64) # 缓冲数组 - idx = mark(img, msk, buf, thr, mode) # 图像预标记 - acc = np.cumprod((1,)+img.shape[::-1][:-1])[::-1] - idx = filter(img, msk, idx, buf, tor, area, mode) - #idx2 = filter(img, msk.copy(), idx.copy(), buf, tor2, area, mode) - pts = idx2rc(idx[idx>0], acc) - #pts2 = idx2rc(idx2[idx2>0], acc) - return pts - -from imagepy.core.engine import Simple -from imagepy.core.mark import GeometryMark - -class Plugin(Simple): - title = 'Desys' - note = ['8-bit', 'auto_snap', 'preview'] - para = {'tol':60, 'thr':180, 'area':256, 'mode':False} - view = [(int, 'tol', (0,100), 0, 'tolerance', 'no wrong'), - # (int, 'tol2', (0,100), 0, 'tolerance2', 'no left'), - (int, 'thr', (1,255), 0, 'threshold', ''), - (int, 'area', (0,10240), 0, 'area', 'max'), - (bool, 'mode', 'white')] - - def preview(self, ips, para): - pts = find_maximum(ips.img, para['tol'], para['thr'], para['area'], para['mode']) - sure = {'type':'circles', 'color':(255,0,0), 'body':[list(i[::-1])+[20] for i in pts], 'lw':2} - ips.mark = GeometryMark(sure) - - def run(self, ips, imgs, para = None): - marks = {'type':'layers', 'body':{}} - for i in range(len(imgs)): - pts = find_maximum(imgs[i], para['tol'], para['thr'], para['area'], para['mode']) - sure = {'type':'circles', 'color':(255,0,0), 'body':[list(i[::-1])+[20] for i in pts], 'lw':2} - marks['body'][i] = sure - ips.mark = GeometryMark(marks) - -if __name__ == '__main__': - from skimage.io import imread - from scipy.ndimage import gaussian_filter - from time import time - import matplotlib.pyplot as plt - img = gaussian_filter(imread('test.png'), 0) - pts = find_maximum(img, 20, True) - start = time() - pts = find_maximum(img, 10, True) - print(time()-start) - plt.imshow(img, cmap='gray') - plt.plot(pts[:,1], pts[:,0], 'y.') - plt.show() diff --git a/imagepy/menus/Plugins/mser_plg.py b/imagepy/menus/Plugins/mser_plg.py deleted file mode 100644 index b7533f8b..00000000 --- a/imagepy/menus/Plugins/mser_plg.py +++ /dev/null @@ -1,147 +0,0 @@ -import scipy.ndimage as ndimg -import numpy as np -from numba import jit - -def neighbors(shape): - dim = len(shape) - block = np.ones([3]*dim) - block[tuple([1]*dim)] = 0 - idx = np.where(block>0) - idx = np.array(idx, dtype=np.uint8).T - idx = np.array(idx-[1]*dim) - acc = np.cumprod((1,)+shape[::-1][:-1]) - return np.dot(idx, acc[::-1]) - -@jit(nopython=True) # trans index to r, c... -def idx2rc(idx, acc): - rst = np.zeros((len(idx), len(acc)), dtype=np.int16) - for i in range(len(idx)): - for j in range(len(acc)): - rst[i,j] = idx[i]//acc[j] - idx[i] -= rst[i,j]*acc[j] - return rst - -@jit(nopython=True) # fill a node (may be two or more points) -def fill(img, msk, p, nbs, buf): - buf[0] = p - back = img[p] - cur = 0; s = 1; - - while curimg[p]: - sta = 100 - break - elif not mode and img[p+dp]0: - fill(img, msk, p, nbs, buf) - - idx[s] = p - s += 1 - if s==len(idx):break - return idx[:s].copy() - -@jit(nopython=True) # 3 max 2 zmd b4 ptd -def filter(img, msk, nbs, acc, idx, bur, tor, mode): - img = img.ravel() - msk = msk.ravel() - - arg = np.argsort(img[idx])[::-1 if mode else 1] - - for i in arg: - if msk[idx[i]]!=3: - idx[i] = 0 - continue - cur = 0; s = 1; - bur[0] = idx[i] - while cur img[idx[i]]+tor: continue - bur[s] = cp - s += 1 - if s==msk.size//3: - cut = cur//2 - msk[bur[:cut]] = 2 - bur[:s-cut] = bur[cut:] - cur -= cut - s -= cut - - if msk[cp]!=2:msk[cp] = 4 - cur += 1 - msk[bur[:s]] = 2 - return idx[idx>0] - - -def mser(img, tor, mode = True): - msk = np.zeros_like(img, dtype=np.uint8) - msk[tuple([slice(1,-1)]*img.ndim)] = 1 - buf = np.zeros(img.size//3, dtype=np.int64) - nbs = neighbors(img.shape) - acc = np.cumprod((1,)+img.shape[::-1][:-1])[::-1] - mskfilter = msk.copy() - idx = mark(img, nbs, mskfilter, buf, mode) - idx = filter(img, mskfilter, nbs, acc, idx, buf, tor, mode) - mark(img, nbs, msk, buf, mode) - idx = filter(img, msk, nbs, acc, idx, buf, tor, mode) - idx = idx2rc(idx, acc) - return (msk==2) * 255 - -from imagepy.core.engine import Filter -from imagepy.core.mark import GeometryMark - -class Plugin(Filter): - title = 'Mser' - note = ['8-bit', 'auto_snap', 'preview'] - para = {'tol':60} - view = [(int, 'tol', (0,100), 0, 'tolerance', '')] - - def run(self, ips, snap, img, para = None): - img[:] = mser(snap, para['tol'], False) - -if __name__ == '__main__': - from skimage.io import imread - from scipy.ndimage import gaussian_filter - from time import time - import matplotlib.pyplot as plt - img = gaussian_filter(imread('test.png'), 0) - pts = find_maximum(img, 20, True) - start = time() - pts = find_maximum(img, 10, True) - print(time()-start) - plt.imshow(img, cmap='gray') - plt.plot(pts[:,1], pts[:,0], 'y.') - plt.show() diff --git a/imagepy/menus/Plugins/temporal_plg.py b/imagepy/menus/Plugins/temporal_plg.py index b6f0c0a2..9f4352cb 100644 --- a/imagepy/menus/Plugins/temporal_plg.py +++ b/imagepy/menus/Plugins/temporal_plg.py @@ -3,8 +3,8 @@ @author: weisong """ from imagepy.core.engine import Simple +from sciapp import Source import numpy as np -from imagepy.core.manager import ColorManager def color_code(img, lut): idx = np.linspace(0,255,len(img)).astype(int) @@ -22,20 +22,21 @@ class Plugin(Simple): 'Start image':1, 'End image': 2, 'Creatbar':True} + def load(self, ips): self.slength = len(ips.imgs) self.para['End image'] = self.slength - self.view = [(list, 'LUT', list(ColorManager.luts.keys()), str, 'LUT',''), + self.view = [(list, 'LUT', Source.manager('colormap').names(), str, 'LUT',''), (int, 'Start image', (1,self.slength),0,'Start image','1~%d'%self.slength), (int, 'End image', (2,self.slength),0,'End image','start~%d'%self.slength), (bool, 'Creatbar', 'Creat time color scale bar')] return True def run(self, ips, imgs, para = None): - cmap = ColorManager.luts[ para['LUT'] ] + cmap = Source.manager('colormap').get(para['LUT']) imglut = color_code(imgs[para['Start image']-1: para['End image']], cmap) - IPy.show_img([imglut],'Color-coded %s'%ips.title) + self.app.show_img([imglut],'Color-coded %s'%ips.title) if para['Creatbar']: cmapshow = np.ones([32,256,3])*cmap - IPy.show_img([cmapshow.astype('uint8')],'Color bar') + self.app.show_img([cmapshow.astype('uint8')],'Color bar') diff --git a/imagepy/menus/Process/Classify/io_plgs.py b/imagepy/menus/Process/Classify/io_plgs.py index e5336f9a..5f537a86 100644 --- a/imagepy/menus/Process/Classify/io_plgs.py +++ b/imagepy/menus/Process/Classify/io_plgs.py @@ -1,5 +1,5 @@ from imagepy.core.engine import Filter, Simple -from imagepy.core.manager import ColorManager +from sciapp import Source # from imagepy.core import ImagePlus import numpy as np @@ -21,7 +21,7 @@ def run(self, ips, imgs, para = None): newips.back = ips idx = ['None', 'Max', 'Min', 'Mask', '2-8mix', '4-6mix', '5-5mix', '6-4mix', '8-2mix'] modes = ['set', 'max', 'min', 'msk', 0.2, 0.4, 0.5, 0.6, 0.8] - newips.lut = ColorManager.get_lut(para['cm']) + newips.lut = Source.manager('colormap').get(para['cm']) newips.chan_mode = modes[idx.index(para['mode'])] #newips.range = (0, para['n']) IPy.show_ips(newips) diff --git a/imagepy/menus/Process/Classify/predict_plg.py b/imagepy/menus/Process/Classify/predict_plg.py index 5dc4bde4..de920644 100644 --- a/imagepy/menus/Process/Classify/predict_plg.py +++ b/imagepy/menus/Process/Classify/predict_plg.py @@ -4,7 +4,7 @@ import os.path as osp import joblib from imagepy.ipyalg import feature -from imagepy.core.manager import ReaderManager +from sciapp import Source class Plugin(Simple): title = 'Feature Predictor' diff --git a/imagepy/menus/Table/Table IO/tableio_plgs.py b/imagepy/menus/Table/Table IO/tableio_plgs.py index 289d4e9d..9ec82ea7 100644 --- a/imagepy/menus/Table/Table IO/tableio_plgs.py +++ b/imagepy/menus/Table/Table IO/tableio_plgs.py @@ -1,6 +1,6 @@ from imagepy.core.util import tableio from pandas import read_csv, read_excel, read_hdf -from imagepy.core.manager import ReaderManager, WriterManager +from sciapp import Source def show(data, title): IPy.show_table(data, title) @@ -8,8 +8,8 @@ def show(data, title): # ViewerManager.add('tab', show) save_csv = lambda path, data:data.to_csv(path) -ReaderManager.add(name='csv', obj=read_csv, tag='tab') -WriterManager.add(name='csv', obj=save_csv, tag='tab') +Source.manager('reader').add(name='csv', obj=read_csv, tag='tab') +Source.manager('writer').add(name='csv', obj=save_csv, tag='tab') class OpenCSV(tableio.Reader): title = 'CSV Open' @@ -20,10 +20,10 @@ class SaveCSV(tableio.Writer): filt = ['csv'] save_excel = lambda path, data:data.to_excel(path) -ReaderManager.add(name='xls', obj=read_excel, tag='tab') -ReaderManager.add(name='xlsx', obj=read_excel, tag='tab') -WriterManager.add(name='xls', obj=save_excel, tag='tab') -WriterManager.add(name='xlsx', obj=save_excel, tag='tab') +Source.manager('reader').add(name='xls', obj=read_excel, tag='tab') +Source.manager('writer').add(name='xlsx', obj=read_excel, tag='tab') +Source.manager('reader').add(name='xls', obj=save_excel, tag='tab') +Source.manager('writer').add(name='xlsx', obj=save_excel, tag='tab') class OpenExcel(tableio.Reader): title = 'Excel Open' diff --git a/imagepy/menus/Window/Windows Style/style_plgs.py b/imagepy/menus/Window/Windows Style/style_plgs.py index 8e71f279..bddce12b 100644 --- a/imagepy/menus/Window/Windows Style/style_plgs.py +++ b/imagepy/menus/Window/Windows Style/style_plgs.py @@ -1,18 +1,18 @@ from imagepy.core.engine import Free -from imagepy.core.manager import ConfigManager +from sciapp import Source class ImageJStyle(Free): title = 'Pay Tribute To ImageJ' asyn = False def run(self, para = None): - ConfigManager.set('uistyle', 'ij') - IPy.alert('Shown in ImageJ style when next setup!') + Source.manager('config').set('uistyle', 'ij') + self.app.alert('Shown in ImageJ style when next setup!') class ImagePyStyle(Free): title = 'Elegant ImagePy Style' asyn = False def run(self, para = None): - ConfigManager.set('uistyle', 'ipy') - IPy.alert('Shown in ImagePy style when next setup!') + Source.manager('config').set('uistyle', 'ipy') + self.app.alert('Shown in ImagePy style when next setup!') plgs = [ImageJStyle, ImagePyStyle] \ No newline at end of file diff --git a/imagepy/tools/Draw/Route_tol.py b/imagepy/tools/Draw/Route_tol.py index ccdcac3d..0dd3f6e3 100644 --- a/imagepy/tools/Draw/Route_tol.py +++ b/imagepy/tools/Draw/Route_tol.py @@ -7,9 +7,9 @@ import wx from imagepy.core.engine import Tool,Filter import numpy as np -from imagepy.core.manager import ColorManager -from imagepy.core.roi import lineroi -from imagepy.core.mark import GeometryMark +#from imagepy.core.manager import ColorManager +#from imagepy.core.roi import lineroi +#from imagepy.core.mark import GeometryMark from skimage.graph import route_through_array import scipy.ndimage as ndimg def route_through(ips, snap,poins,para): diff --git a/imagepy/tools/Draw/aibrush_tol.py b/imagepy/tools/Draw/aibrush_tol.py index 83c192a3..eedf277f 100644 --- a/imagepy/tools/Draw/aibrush_tol.py +++ b/imagepy/tools/Draw/aibrush_tol.py @@ -1,11 +1,11 @@ from imagepy.core.engine import Tool import numpy as np from time import time -from imagepy.core.manager import ColorManager +from sciapp import Source from skimage.morphology import flood_fill, flood from skimage.draw import line, circle from skimage.segmentation import felzenszwalb -from imagepy.core.mark import GeometryMark +# from imagepy.core.mark import GeometryMark from scipy.ndimage import binary_fill_holes, binary_dilation, binary_erosion def fill_normal(img, r, c, color, con, tor): diff --git a/imagepy/tools/Draw/flood3d_tol.py b/imagepy/tools/Draw/flood3d_tol.py index b4cb05f9..c5326662 100644 --- a/imagepy/tools/Draw/flood3d_tol.py +++ b/imagepy/tools/Draw/flood3d_tol.py @@ -7,7 +7,7 @@ from imagepy.core.engine import Tool import numpy as np -from imagepy.core.manager import ColorManager +from sciapp import Source from skimage.morphology import flood_fill, flood from imagepy.core.engine import Filter, Simple @@ -25,7 +25,7 @@ class Plugin(Tool): (list, 'con', ['4-connect', '8-connect'], str, 'fill', 'pix')] def mouse_down(self, ips, x, y, btn, **key): - FloodFill3D().start({'seed':(ips.cur, int(y), int(x)), 'color':np.mean(ColorManager.get_front()), + FloodFill3D().start({'seed':(ips.cur, int(y), int(x)), 'color':np.mean(Source.manager('color').get('front')), 'conn':(self.para['con']=='8-connect')+1, 'tor':self.para['tor']}) def mouse_up(self, ips, x, y, btn, **key): diff --git a/imagepy/tools/Draw/floodfill_tol.py b/imagepy/tools/Draw/floodfill_tol.py index f78c7f3f..f2dc581b 100644 --- a/imagepy/tools/Draw/floodfill_tol.py +++ b/imagepy/tools/Draw/floodfill_tol.py @@ -7,7 +7,7 @@ from imagepy.core.engine import Tool import numpy as np -from imagepy.core.manager import ColorManager +from sciapp import Source # from imagepy.core.draw.fill import floodfill from skimage.morphology import flood_fill, flood @@ -19,7 +19,7 @@ class Plugin(Tool): def mouse_down(self, ips, x, y, btn, **key): - img, color = ips.img, ColorManager.get_front() + img, color = ips.img, Source.manager('color').get('front') if int(y)<0 or int(x)<0: return if int(y)>=img.shape[0] or int(x)>=img.shape[1]: return diff --git a/imagepy/tools/Draw/magic_tol.py b/imagepy/tools/Draw/magic_tol.py index 14af9daf..f694724a 100644 --- a/imagepy/tools/Draw/magic_tol.py +++ b/imagepy/tools/Draw/magic_tol.py @@ -7,10 +7,9 @@ import wx from imagepy.core.engine import Tool import numpy as np -from imagepy.core.manager import ColorManager from skimage.morphology import flood_fill, flood from skimage.measure import find_contours -from imagepy.core.roi.convert import shape2roi, roi2shape +# from imagepy.core.roi.convert import shape2roi, roi2shape from shapely.geometry import Polygon, Point from shapely.ops import cascaded_union import matplotlib.pyplot as plt diff --git a/imagepy/tools/Standard/colorpicker_tol.py b/imagepy/tools/Standard/colorpicker_tol.py index b13bcd27..e0b2590e 100644 --- a/imagepy/tools/Standard/colorpicker_tol.py +++ b/imagepy/tools/Standard/colorpicker_tol.py @@ -5,7 +5,7 @@ @author: yxl """ from imagepy.core.engine import Tool -from imagepy.core.manager import ColorManager +from sciapp import Source class Plugin(Tool): """ColorPicker class plugin with events callbacks""" diff --git a/imagepy/tools/Standard/magic_tol.py b/imagepy/tools/Standard/magic_tol.py index 14af9daf..edf6b971 100644 --- a/imagepy/tools/Standard/magic_tol.py +++ b/imagepy/tools/Standard/magic_tol.py @@ -7,10 +7,10 @@ import wx from imagepy.core.engine import Tool import numpy as np -from imagepy.core.manager import ColorManager +#from imagepy.core.manager import ColorManager from skimage.morphology import flood_fill, flood from skimage.measure import find_contours -from imagepy.core.roi.convert import shape2roi, roi2shape +# from imagepy.core.roi.convert import shape2roi, roi2shape from shapely.geometry import Polygon, Point from shapely.ops import cascaded_union import matplotlib.pyplot as plt diff --git a/imagepy/tools/Standard/painter_tol.py b/imagepy/tools/Standard/painter_tol.py index da7f8d7c..51fe6ced 100644 --- a/imagepy/tools/Standard/painter_tol.py +++ b/imagepy/tools/Standard/painter_tol.py @@ -1,5 +1,5 @@ from imagepy.core.engine import Tool -from imagepy.core.manager import ColorManager +from sciapp import Source from skimage.draw import line, circle def drawline(img, oldp, newp, w, value): @@ -31,7 +31,7 @@ def mouse_up(self, ips, x, y, btn, **key): def mouse_move(self, ips, x, y, btn, **key): if not self.status:return w = self.para['width'] - value = ColorManager.get_front() + value = Source.manager('color').get('front') drawline(ips.img, self.oldp, (y, x), w, value) self.oldp = (y, x) ips.update() diff --git a/imagepy/tools/Toolkit3D/flood3d_tol.py b/imagepy/tools/Toolkit3D/flood3d_tol.py index b4cb05f9..c5326662 100644 --- a/imagepy/tools/Toolkit3D/flood3d_tol.py +++ b/imagepy/tools/Toolkit3D/flood3d_tol.py @@ -7,7 +7,7 @@ from imagepy.core.engine import Tool import numpy as np -from imagepy.core.manager import ColorManager +from sciapp import Source from skimage.morphology import flood_fill, flood from imagepy.core.engine import Filter, Simple @@ -25,7 +25,7 @@ class Plugin(Tool): (list, 'con', ['4-connect', '8-connect'], str, 'fill', 'pix')] def mouse_down(self, ips, x, y, btn, **key): - FloodFill3D().start({'seed':(ips.cur, int(y), int(x)), 'color':np.mean(ColorManager.get_front()), + FloodFill3D().start({'seed':(ips.cur, int(y), int(x)), 'color':np.mean(Source.manager('color').get('front')), 'conn':(self.para['con']=='8-connect')+1, 'tor':self.para['tor']}) def mouse_up(self, ips, x, y, btn, **key): From fff7017562377aa208b6a0035c64f2518ab2e0b1 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Tue, 2 Jun 2020 21:52:55 +0800 Subject: [PATCH 207/343] delete rough wall image --- imagepy/menus/File/Samples Local/samples_plgs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/menus/File/Samples Local/samples_plgs.py b/imagepy/menus/File/Samples Local/samples_plgs.py index abe0ef0a..457e9f16 100644 --- a/imagepy/menus/File/Samples Local/samples_plgs.py +++ b/imagepy/menus/File/Samples Local/samples_plgs.py @@ -25,6 +25,6 @@ def __call__(self): return self 'camera', 'cell', 'checkerboard', 'chelsea', 'clock', 'coffee', 'coins', 'colorwheel', 'grass', 'gravel', 'horse', 'hubble_deep_field', 'immunohistochemistry', 'microaneurysms', 'moon', 'page', - 'text', 'retina', 'rocket', 'rough_wall', 'shepp_logan_phantom', 'stereo_motorcycle'] + 'text', 'retina', 'rocket', 'shepp_logan_phantom', 'stereo_motorcycle'] plgs = [i if i=='-' else Data(i) for i in datas] \ No newline at end of file From fdd9ff21dd2607048258caf40703470c1f512309 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Tue, 2 Jun 2020 22:04:23 +0800 Subject: [PATCH 208/343] Modify the comma symbol to dot in free engine --- imagepy/core/engine/free.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/core/engine/free.py b/imagepy/core/engine/free.py index 72175183..150bea41 100644 --- a/imagepy/core/engine/free.py +++ b/imagepy/core/engine/free.py @@ -20,7 +20,7 @@ def run(self, para=None): print('this is a plugin') def runasyn(self, para, callback=None): - self.app.manager('task').add(self,title, self) + self.app.manager('task').add(self.title, self) start = time() self.run(para) self.app.info('%s: cost %.3fs'%(self.title, time()-start)) From d4b80a45cbf7cf3d9d17c06750522d61346243a6 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Tue, 2 Jun 2020 22:23:51 +0800 Subject: [PATCH 209/343] Correct the comma to dot symbol in simple.py --- imagepy/core/engine/simple.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/core/engine/simple.py b/imagepy/core/engine/simple.py index b863f257..82abe296 100644 --- a/imagepy/core/engine/simple.py +++ b/imagepy/core/engine/simple.py @@ -48,7 +48,7 @@ def ok(self, ips, para=None, callafter=None): self.app.record_macros('{}>{}'.format(self.title, para)) def runasyn(self, ips, imgs, para = None, callback = None): - self.app.manager('task').add(self,title, self) + self.app.manager('task').add(self.title, self) start = time() self.run(ips, imgs, para) self.app.info('%s: cost %.3fs'%(ips.title, time()-start)) From c607c94d5ce8fbd8ad08bf4dced7dbba19167ab6 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Tue, 2 Jun 2020 22:37:26 +0800 Subject: [PATCH 210/343] plg is misspelled as plt in filter.py --- imagepy/core/engine/filter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index 133dae41..3665f73d 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -24,7 +24,7 @@ def process_channels(plg, ips, src, des, para): return des def process_one(plg, ips, src, img, para, callafter=None): - Source.manager('task').add(plt.title, plg) + Source.manager('task').add(plg.title, plg) start = time() transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16) transfloat = '2float' in plg.note and not ips.dtype in (np.complex128, np.float32, np.float64) @@ -47,7 +47,7 @@ def process_one(plg, ips, src, img, para, callafter=None): if not callafter is None:callafter() def process_stack(plg, ips, src, imgs, para, callafter=None): - Source.manager('task').add(plt.title, plg) + Source.manager('task').add(plg.title, plg) start = time() transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16) From ecbb8cb8d07f463aa3e0e439b4fb06145b7421e9 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 4 Jun 2020 14:01:18 +0800 Subject: [PATCH 211/343] nothing --- imagepy/core/engine/filter.py | 4 ++-- imagepy/core/engine/simple.py | 2 +- imagepy/menus/File/Samples Local/samples_plgs.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index 3665f73d..133dae41 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -24,7 +24,7 @@ def process_channels(plg, ips, src, des, para): return des def process_one(plg, ips, src, img, para, callafter=None): - Source.manager('task').add(plg.title, plg) + Source.manager('task').add(plt.title, plg) start = time() transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16) transfloat = '2float' in plg.note and not ips.dtype in (np.complex128, np.float32, np.float64) @@ -47,7 +47,7 @@ def process_one(plg, ips, src, img, para, callafter=None): if not callafter is None:callafter() def process_stack(plg, ips, src, imgs, para, callafter=None): - Source.manager('task').add(plg.title, plg) + Source.manager('task').add(plt.title, plg) start = time() transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16) diff --git a/imagepy/core/engine/simple.py b/imagepy/core/engine/simple.py index 82abe296..b863f257 100644 --- a/imagepy/core/engine/simple.py +++ b/imagepy/core/engine/simple.py @@ -48,7 +48,7 @@ def ok(self, ips, para=None, callafter=None): self.app.record_macros('{}>{}'.format(self.title, para)) def runasyn(self, ips, imgs, para = None, callback = None): - self.app.manager('task').add(self.title, self) + self.app.manager('task').add(self,title, self) start = time() self.run(ips, imgs, para) self.app.info('%s: cost %.3fs'%(ips.title, time()-start)) diff --git a/imagepy/menus/File/Samples Local/samples_plgs.py b/imagepy/menus/File/Samples Local/samples_plgs.py index 457e9f16..abe0ef0a 100644 --- a/imagepy/menus/File/Samples Local/samples_plgs.py +++ b/imagepy/menus/File/Samples Local/samples_plgs.py @@ -25,6 +25,6 @@ def __call__(self): return self 'camera', 'cell', 'checkerboard', 'chelsea', 'clock', 'coffee', 'coins', 'colorwheel', 'grass', 'gravel', 'horse', 'hubble_deep_field', 'immunohistochemistry', 'microaneurysms', 'moon', 'page', - 'text', 'retina', 'rocket', 'shepp_logan_phantom', 'stereo_motorcycle'] + 'text', 'retina', 'rocket', 'rough_wall', 'shepp_logan_phantom', 'stereo_motorcycle'] plgs = [i if i=='-' else Data(i) for i in datas] \ No newline at end of file From 25ca3052461a08e0e8019f38c20f91460d73aabe Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 4 Jun 2020 23:14:56 +0800 Subject: [PATCH 212/343] widgets --- imagepy/__main__.py | 23 +++--- imagepy/core/app/loader.py | 4 +- imagepy/core/engine/simple.py | 2 +- imagepy/core/pixel/__init__.py | 1 - imagepy/core/pixel/bliter.py | 66 ----------------- imagepy/menus/Edit/edit_plg.py | 1 - imagepy/menus/Image/canvassize_plg.py | 57 ++++++--------- imagepy/menus/Plugins/Manager/console_wgt.py | 2 - imagepy/menus/Plugins/Manager/toltree_wgt.py | 2 - imagepy/menus/Process/calculator_plg.py | 75 +++++++++++--------- imagepy/widgets/__init__.py | 0 imagepy/widgets/histogram/__init__.py | 1 + imagepy/widgets/histogram/channels_wgt.py | 1 + imagepy/widgets/histogram/curve_wgt.py | 1 + imagepy/widgets/histogram/histogram_wgt.py | 1 + imagepy/widgets/navigator/__init__.py | 0 imagepy/widgets/navigator/navigator_wgt.py | 1 + sciapp/manager.py | 48 ------------- 18 files changed, 82 insertions(+), 204 deletions(-) delete mode 100644 imagepy/core/pixel/__init__.py delete mode 100644 imagepy/core/pixel/bliter.py create mode 100644 imagepy/widgets/__init__.py create mode 100644 imagepy/widgets/histogram/__init__.py create mode 100644 imagepy/widgets/histogram/channels_wgt.py create mode 100644 imagepy/widgets/histogram/curve_wgt.py create mode 100644 imagepy/widgets/histogram/histogram_wgt.py create mode 100644 imagepy/widgets/navigator/__init__.py create mode 100644 imagepy/widgets/navigator/navigator_wgt.py delete mode 100644 sciapp/manager.py diff --git a/imagepy/__main__.py b/imagepy/__main__.py index 00b990b4..b6dbcbf0 100644 --- a/imagepy/__main__.py +++ b/imagepy/__main__.py @@ -4,6 +4,7 @@ from imagepy.core.app import loader from sciapp.action import ImgAction, Tool, DefaultTool +''' from sciwx.plugins.curve import Curve from sciwx.plugins.channels import Channels from sciwx.plugins.histogram import Histogram @@ -11,6 +12,7 @@ from sciwx.plugins.filters import Gaussian, Undo from sciwx.plugins.pencil import Pencil from sciwx.plugins.io import Open, Save +''' def extend_plgs(plg): if isinstance(plg, tuple): @@ -27,30 +29,35 @@ def extend_tols(tol): return (tol[1], tol[0]) elif isinstance(tol, list): return [extend_tols(i) for i in tol] +def extend_wgts(wgt): + if isinstance(wgt, tuple) and isinstance(wgt[1], list): + return (wgt[0].title, extend_wgts(wgt[1])) + elif isinstance(wgt, list): return [extend_wgts(i) for i in wgt] + else: return (wgt.title, wgt) + if __name__ == '__main__': from skimage.data import camera, astronaut app = wx.App(False) frame = ImagePy(None) + frame.load_menu(extend_plgs(loader.build_plugins('menus'))) #frame.load_menu(('menu',[('File',[('Open', Open), # ('Save', Save)]), # ('Filters', [('Gaussian', Gaussian), # ('Undo', Undo)])])) - - frame.load_menu(extend_plgs(loader.build_plugins('menus'))) + frame.load_tool(extend_tols(loader.build_tools('tools')), 'Transform') #frame.load_tool(('tools',[('standard', [('P', Pencil), # ('D', DefaultTool)]), # ('draw', [('X', Pencil), # ('X', Pencil)])]), 'draw') - frame.load_tool(extend_tols(loader.build_tools('tools')), 'Transform') - - frame.load_widget(('widgets', [('Histogram', [('Histogram', Histogram), - ('Curve', Curve), - ('Channels', Channels)]), - ('Navigator', [('Viewport', ViewPort)])])) + frame.load_widget(extend_wgts(loader.build_widgets('widgets'))) + # ('widgets', [('Histogram', [('Histogram', Histogram), + # ('Curve', Curve), + # ('Channels', Channels)]), + # ('Navigator', [('Viewport', ViewPort)])]) frame.show_img([camera()], 'camera') frame.show_img([astronaut()], 'astronaut') diff --git a/imagepy/core/app/loader.py b/imagepy/core/app/loader.py index ab3c696c..669baace 100644 --- a/imagepy/core/app/loader.py +++ b/imagepy/core/app/loader.py @@ -25,7 +25,6 @@ def extend_plugins(path, lst, err): rst.append(i) elif i[-3:] == 'rpt': pt = os.path.join(root_dir,path) - print(pt+'/'+i) rst.append(Report(i[:-4], pt+'/'+i)) Source.manager('plugin').add(obj=rst[-1], name=rst[-1].title) elif i[-3:] == '.mc': @@ -56,7 +55,6 @@ def extend_plugins(path, lst, err): if hasattr(plg, 'wgts'): rst.extend([j if j=='-' else Widget(j) for j in plg.wgts]) for p in plg.wgts: - print(p) if not isinstance(p, str):Source.manager('widget').add(obj=p, name=p.title) else: rst.append(Widget(plg.Plugin)) @@ -195,7 +193,7 @@ def extend_widgets(path, lst, err): rst.append(plg.Plugin) except Exception as e: err.append((path, i, sys.exc_info()[1])) - for i in rst:Source.manager('widget').add(obj=i, name=i.name) + for i in rst:Source.manager('widget').add(obj=i, name=i.title) return rst def sort_widgets(catlog, lst): diff --git a/imagepy/core/engine/simple.py b/imagepy/core/engine/simple.py index b863f257..82abe296 100644 --- a/imagepy/core/engine/simple.py +++ b/imagepy/core/engine/simple.py @@ -48,7 +48,7 @@ def ok(self, ips, para=None, callafter=None): self.app.record_macros('{}>{}'.format(self.title, para)) def runasyn(self, ips, imgs, para = None, callback = None): - self.app.manager('task').add(self,title, self) + self.app.manager('task').add(self.title, self) start = time() self.run(ips, imgs, para) self.app.info('%s: cost %.3fs'%(ips.title, time()-start)) diff --git a/imagepy/core/pixel/__init__.py b/imagepy/core/pixel/__init__.py deleted file mode 100644 index 4287ca86..00000000 --- a/imagepy/core/pixel/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# \ No newline at end of file diff --git a/imagepy/core/pixel/bliter.py b/imagepy/core/pixel/bliter.py deleted file mode 100644 index ed972f19..00000000 --- a/imagepy/core/pixel/bliter.py +++ /dev/null @@ -1,66 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Sun Dec 11 22:58:58 2016 -@author: yxl -""" -import numpy as np - -def count_box(s1, s2, c, r): - box1c = slice(max(0, c), min(s1[1], c+s2[1])) - box1r = slice(max(0, r), min(s1[0], r+s2[0])) - box2c = slice(max(-c, 0), min(s2[1], s1[1]-c)) - box2r = slice(max(-r, 0), min(s2[0], s1[0]-r)) - return (box1r, box1c), (box2r, box2c) - -def blit_copy(img1, img2): - img1[:] = img2 - -def blit_max(img1, img2): - msk = img2>img1 - img1[msk] = img2[msk] - -def blit_min(img1, img2): - msk = img2img1 - umsk = True ^ msk - img1[msk] = img2[msk] - img1[msk] - img1[umsk] = img1[umsk] - img2[umsk] - -def blit_add(img1, img2): - if img1.dtype == np.uint8: - msk = img2 > 255-img1 - img1 += img2 - img1[msk] = 255 - else: img1 += img2 - -def blit_substract(img1, img2): - if img1.dtype == np.uint8: - msk = img11:shp = (shp[1], shp[0], chns) - - if para['hor'] == 'left':c=0 - if para['ver'] == 'top':r=0 - if para['hor'] == 'center':c=(shp[1]-old[1])//2 - if para['ver'] == 'center':r=(shp[0]-old[0])//2 - if para['hor'] == 'right':c=shp[1]-old[1] - if para['ver'] == 'bottom':r=shp[0]-old[0] - - if ips.slices>1: - s = list(imgs.shape) - s[1], s[2] = shp[0], shp[1] - rst = np.zeros(s, dtype=ips.dtype) - for i in range(len(imgs)): - self.progress(i, len(imgs)) - bliter.blit(rst[i], imgs[i], c, r) - else: - rst = [] - for i in range(len(imgs)): - self.progress(i, len(imgs)) - rst.append(np.zeros(shp, ips.dtype)) - bliter.blit(rst[-1], imgs[i], c, r) - ips.roi = None - ips.set_imgs(rst) - ''' - if ips.back is None: return - nbc = np.zeros(shp, dtype=np.uint8) - bliter.blit(nbc, ips.backimg, c, r) - ips.backimg = nbc - ''' + (o_r, o_c), n, n_r, n_c = ips.shape, ips.channels, para['h'], para['w'] + key = {'left':0, 'center':1, 'right':2, 'top':0, 'bottom':1} + or_sli, nr_sli = make_slice(o_r, n_r, key[para['ver']]) + oc_sli, nc_sli = make_slice(o_c, n_c, key[para['hor']]) + shp = (ips.slices, n_r, n_c, ips.channels)[:3+(ips.channels>1)] + if ips.isarray: buf = np.zeros(shp, dtype=ips.dtype) + else: buf = [np.zeros(shp[1:], dtype=ips.dtype) for i in range(shp[0])] + + for i in range(ips.slices): + self.progress(i, ips.slices) + buf[i][nr_sli, nc_sli] = imgs[i][or_sli, oc_sli] + ips.set_imgs(buf) \ No newline at end of file diff --git a/imagepy/menus/Plugins/Manager/console_wgt.py b/imagepy/menus/Plugins/Manager/console_wgt.py index 68985a57..76611209 100644 --- a/imagepy/menus/Plugins/Manager/console_wgt.py +++ b/imagepy/menus/Plugins/Manager/console_wgt.py @@ -19,8 +19,6 @@ def __init__(self): name = ''.join(list(filter(str.isalnum, i))) exec("self.run_%s = lambda para=None, plg=Source.manager('plugin').get(i):plg().start(cmds['app'], para)"%name) - print(self) - class Plugin(wx.Panel): title = 'Command Line' single = None diff --git a/imagepy/menus/Plugins/Manager/toltree_wgt.py b/imagepy/menus/Plugins/Manager/toltree_wgt.py index ca9f0351..b43c452b 100644 --- a/imagepy/menus/Plugins/Manager/toltree_wgt.py +++ b/imagepy/menus/Plugins/Manager/toltree_wgt.py @@ -65,9 +65,7 @@ def __init__( self, parent ): self.load() def addnode(self, parent, data): - print('aaa', data) for i in data: - print(i) if i=='-':continue if isinstance(i, tuple): item = self.tre_plugins.AppendItem(parent, i[0].title) diff --git a/imagepy/menus/Process/calculator_plg.py b/imagepy/menus/Process/calculator_plg.py index f72b01f0..286a52c8 100644 --- a/imagepy/menus/Process/calculator_plg.py +++ b/imagepy/menus/Process/calculator_plg.py @@ -1,49 +1,54 @@ -# -*- coding: utf-8 -*- -""" -Created on Thu Dec 1 01:22:19 2016 -@author: yxl -""" from imagepy.core.engine import Simple -from imagepy.core.pixel import bliter +import numpy as np class Plugin(Simple): - """Calculator Plugin derived from imagepy.core.engine.Simple """ title = 'Image Calculator' note = ['all'] - para = {'img1':None,'op':'add','img2':None} + para = {'op':'add','img':None} - view = [('img', 'img1', 'image1', ''), - (list, 'op', ['max', 'min', 'diff', 'add', 'substract'], str, 'operator', ''), - ('img', 'img2', 'image2', '')] + view = [(list, 'op', ['max', 'min', 'diff', 'add', 'substract'], str, 'operator', ''), + ('img', 'img', 'image', '')] def run(self, ips, imgs, para = None): - ips1 = self.app.get_img(para['img1']) - ips2 = self.app.get_img(para['img2']) - ips1.snapshot() + down, up = ips.range + ips2 = self.app.get_img(para['img']) + ips.snapshot() - sl1, sl2 = ips1.slices, ips2.slices - cn1, cn2 = ips1.channels, ips2.channels - if ips1.dtype != ips2.dtype: + sl1, sl2 = ips.slices, ips2.slices + cn1, cn2 = ips.channels, ips2.channels + + if ips.dtype != ips2.dtype: return self.app.alert('Two stack must be equal dtype!') elif sl1>1 and sl2>1 and sl1!=sl2: return self.app.alert('Two stack must have equal slices!') elif cn1>1 and cn2>1 and cn1!=cn2: return self.app.alert('Two stack must have equal channels!') - - w, h = ips1.shape, ips2.shape - w, h = min(w[0], h[0]), min(w[1], h[1]) - if sl1 == 1: - bliter.blit(ips1.get_subimg(), ips2.get_subimg(), mode=para['op']) - elif sl1>1 and sl2==1: - for i in range(sl1): - self.progress(i, sl1) - ss1, se1 = ips1.get_rect() - bliter.blit(ips1.imgs[i][ss1, se1], ips2.get_subimg(), mode=para['op']) - elif sl1>1 and sl2>1: - for i in range(sl1): - self.progress(i, sl1) - ss1, se1 = ips1.get_rect() - ss2, se2 = ips2.get_rect() - bliter.blit(ips1.imgs[i][ss1, se1], ips2.imgs[i][ss2, se2], mode=para['op']) - ips1.update() - \ No newline at end of file + + imgs1, imgs2 = ips.subimg(), ips2.subimg() + if len(imgs1)==1: imgs1 = imgs1 * len(imgs2) + if len(imgs2)==1: imgs2 = imgs2 * len(imgs1) + if imgs1[0].shape[:2] != imgs2[0].shape[:2]: + return self.app.alert('Two image must be in equal shape') + + for i in range(len(imgs1)): + im1, im2 = imgs1[i], imgs2[i] + if cn1==1 and cn2>1: + im2 = im2.mean(axis=-1, dtype=np.float32) + if cn2==1 and cn1>1: + im2 = im2[:,:,None] * np.ones(cn1) + + if para['op'] == 'max': + msk = im1 < im2 + im1[msk] = im2[msk] + if para['op'] == 'min': + msk = im1 > im2 + im1[msk] = im2[msk] + if para['op'] == 'diff': + dif = np.abs(im1.astype(np.float32) - im2) + np.clip(dif, down, up, out=im1) + if para['op'] == 'add': + dif = im1.astype(np.float32) + im2 + np.clip(dif, down, up, out=im1) + if para['op'] == 'substract': + dif = im1.astype(np.float32) - im2 + np.clip(dif, down, up, out=im1) \ No newline at end of file diff --git a/imagepy/widgets/__init__.py b/imagepy/widgets/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/widgets/histogram/__init__.py b/imagepy/widgets/histogram/__init__.py new file mode 100644 index 00000000..a0a7e74e --- /dev/null +++ b/imagepy/widgets/histogram/__init__.py @@ -0,0 +1 @@ +catlog = ['histogram_wgt', 'curve_wgt'] \ No newline at end of file diff --git a/imagepy/widgets/histogram/channels_wgt.py b/imagepy/widgets/histogram/channels_wgt.py new file mode 100644 index 00000000..5851dd95 --- /dev/null +++ b/imagepy/widgets/histogram/channels_wgt.py @@ -0,0 +1 @@ +from sciwx.plugins.channels import Channels as Plugin \ No newline at end of file diff --git a/imagepy/widgets/histogram/curve_wgt.py b/imagepy/widgets/histogram/curve_wgt.py new file mode 100644 index 00000000..b2823628 --- /dev/null +++ b/imagepy/widgets/histogram/curve_wgt.py @@ -0,0 +1 @@ +from sciwx.plugins.curve import Curve as Plugin \ No newline at end of file diff --git a/imagepy/widgets/histogram/histogram_wgt.py b/imagepy/widgets/histogram/histogram_wgt.py new file mode 100644 index 00000000..699ee663 --- /dev/null +++ b/imagepy/widgets/histogram/histogram_wgt.py @@ -0,0 +1 @@ +from sciwx.plugins.histogram import Histogram as Plugin \ No newline at end of file diff --git a/imagepy/widgets/navigator/__init__.py b/imagepy/widgets/navigator/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/widgets/navigator/navigator_wgt.py b/imagepy/widgets/navigator/navigator_wgt.py new file mode 100644 index 00000000..aa344463 --- /dev/null +++ b/imagepy/widgets/navigator/navigator_wgt.py @@ -0,0 +1 @@ +from sciwx.plugins.viewport import ViewPort as Plugin \ No newline at end of file diff --git a/sciapp/manager.py b/sciapp/manager.py deleted file mode 100644 index 3bd14db7..00000000 --- a/sciapp/manager.py +++ /dev/null @@ -1,48 +0,0 @@ -class Manager: - def __init__(self, unique=True, path=None): - self.objs, self.unique, self.path = [], unique, path - if not path is None: self.read(path) - - def add(self, name, obj, tag=None): - if self.unique: self.remove(name, tag, obj) - self.objs.insert(0, (name, obj, tag)) - - def adds(self, objs): for i in objs: self.add(*i) - - def get(self, name=None, tag=None, obj=None): - rst = self.filter(name, tag, obj) - return None if len(rst)==0 else rst[0] - - def filter(self, name=None, tag=None, obj=None): - rst = [i for i in self.objs if name is None or name = i[0]] - rst = [i for i in self.objs if obj is None or obj = i[1]] - return [i for i in self.objs if tag is None or tag = i[2]] - - def gets(self, name=None, tag=None): - return self.filter(name, tag) - - def has(self, name=None, tag=None, obj=None): - return len(self.filter(name, tag, obj))>0 - - def remove(self, name=None, tag=None, obj=None): - for i in self.filter(name, tag, obj): self.objs.remove(i) - - def names(self, tag=None): - return [i[0] for i in self.filter(tag=tag)] - - def name(self, name, obj=None): - names = self.names() - if not name in names : return name - for i in range(1, 100) : - n = "%s-%s"%(name, i) - if not n in names: return n - - def write(self, path=None): - with open(path or self.path, 'w') as f: - f.write(json.dumps(self.objs)) - - def read(self, path): - if not osp.exists(path): return - self.path = path - with open(path) as f: - self.adds(json.loads(f.read())) From 4289527fe54f81b313f98978d2afd8b357b1ec74 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Fri, 5 Jun 2020 10:29:00 +0800 Subject: [PATCH 213/343] open file fixed --- imagepy/core/util/fileio.py | 4 ++-- imagepy/menus/File/Samples Local/samples_plgs.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/imagepy/core/util/fileio.py b/imagepy/core/util/fileio.py index 44c6faf5..ad06be77 100644 --- a/imagepy/core/util/fileio.py +++ b/imagepy/core/util/fileio.py @@ -44,7 +44,7 @@ def run(self, para = None): fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) - reader = Source.manager('reader').gets(name=fe[1:], tag=self.tag, note=self.note) + reader = Source.manager('reader').gets(name=fe[1:], tag=self.tag) print(fe, self.tag, self.note, reader) ''' if len(reader) == 0: @@ -55,7 +55,7 @@ def run(self, para = None): return self.app.alert('No reader found for %s'%fe[1:]) # ext, read, tag, note = reader ''' - self.app.show(self.tag, reader[0](para['path'])) + self.app.show(self.tag, reader[0][1](para['path']), fn) class Writer(Simple): note = ['all'] diff --git a/imagepy/menus/File/Samples Local/samples_plgs.py b/imagepy/menus/File/Samples Local/samples_plgs.py index abe0ef0a..457e9f16 100644 --- a/imagepy/menus/File/Samples Local/samples_plgs.py +++ b/imagepy/menus/File/Samples Local/samples_plgs.py @@ -25,6 +25,6 @@ def __call__(self): return self 'camera', 'cell', 'checkerboard', 'chelsea', 'clock', 'coffee', 'coins', 'colorwheel', 'grass', 'gravel', 'horse', 'hubble_deep_field', 'immunohistochemistry', 'microaneurysms', 'moon', 'page', - 'text', 'retina', 'rocket', 'rough_wall', 'shepp_logan_phantom', 'stereo_motorcycle'] + 'text', 'retina', 'rocket', 'shepp_logan_phantom', 'stereo_motorcycle'] plgs = [i if i=='-' else Data(i) for i in datas] \ No newline at end of file From 1d8023b861cddacf152688ff817b41d9a74b8481 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Fri, 5 Jun 2020 11:04:03 +0800 Subject: [PATCH 214/343] fix the extension name of file when saving --- imagepy/menus/File/save_plg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/menus/File/save_plg.py b/imagepy/menus/File/save_plg.py index 802a7944..2818e71b 100644 --- a/imagepy/menus/File/save_plg.py +++ b/imagepy/menus/File/save_plg.py @@ -11,7 +11,7 @@ class SaveImage(fileio.Writer): title = 'Save' def load(self, ips): - self.filt = [i[0] for i in sorted(Source.manager('writer').names())] + self.filt = [i for i in sorted(Source.manager('writer').names())] return True class WindowCapture(fileio.Writer): From 936b6e76011f672c86684c7285d42ac5f7c5d5c9 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 6 Jun 2020 00:16:09 +0800 Subject: [PATCH 215/343] canvas center and rgb default --- imagepy/__main__.py | 5 ++--- imagepy/core/app/imagepy.py | 4 ++-- imagepy/core/engine/filter.py | 2 +- sciapp/object/image.py | 1 + sciwx/canvas/canvas.py | 2 +- sciwx/canvas/mcanvas.py | 5 +++-- sciwx/demo/canvas1_demo.py | 4 +++- sciwx/demo/canvas3_image_obj.py | 5 +++-- sciwx/demo/canvas5_frame_demo.py | 2 +- 9 files changed, 17 insertions(+), 13 deletions(-) diff --git a/imagepy/__main__.py b/imagepy/__main__.py index b6dbcbf0..f927f064 100644 --- a/imagepy/__main__.py +++ b/imagepy/__main__.py @@ -1,10 +1,9 @@ import wx, sys sys.path.append('../') -from imagepy.core.app import ImagePy -from imagepy.core.app import loader +from imagepy.core.app import loader, ImagePy -from sciapp.action import ImgAction, Tool, DefaultTool ''' +from sciapp.action import ImgAction, Tool, DefaultTool from sciwx.plugins.curve import Curve from sciwx.plugins.channels import Channels from sciwx.plugins.histogram import Histogram diff --git a/imagepy/core/app/imagepy.py b/imagepy/core/app/imagepy.py index f5770cf4..15ec9419 100644 --- a/imagepy/core/app/imagepy.py +++ b/imagepy/core/app/imagepy.py @@ -15,7 +15,7 @@ class ImagePy(wx.Frame, App): def __init__( self, parent ): - wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = 'SciApp', + wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = 'ImagePy', size = wx.Size(-1,-1), pos = wx.DefaultPosition, style = wx.RESIZE_BORDER|wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) App.__init__(self) @@ -59,7 +59,7 @@ def load_menu(self, data): def load_tool(self, data, default=None): for i, (name, tols) in enumerate(data[1]): self.toolbar.add_tools(name, tols, i==0) - if not default is None: self.toolbar.add_pop('P', default) + if not default is None: self.toolbar.add_pop('./tools/drop.gif', default) self.toolbar.Layout() def load_widget(self, data): diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index 133dae41..4fcdbe8e 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -24,7 +24,7 @@ def process_channels(plg, ips, src, des, para): return des def process_one(plg, ips, src, img, para, callafter=None): - Source.manager('task').add(plt.title, plg) + Source.manager('task').add(plg.title, plg) start = time() transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16) transfloat = '2float' in plg.note and not ips.dtype in (np.complex128, np.float32, np.float64) diff --git a/sciapp/object/image.py b/sciapp/object/image.py index b8f97279..a5b2d905 100644 --- a/sciapp/object/image.py +++ b/sciapp/object/image.py @@ -144,6 +144,7 @@ def reset(self): self.rg = [(0, 255)] * self.channels else: self.rg = self.get_updown('all', 'all', step=512) + print(self.cn, self.rg, '==========') def snapshot(self): if self.snap is None: diff --git a/sciwx/canvas/canvas.py b/sciwx/canvas/canvas.py index 3acedc74..8c5f4ad3 100644 --- a/sciwx/canvas/canvas.py +++ b/sciwx/canvas/canvas.py @@ -191,7 +191,7 @@ def on_size(self, event): if max(self.GetClientSize())>20: self.initBuffer() if len(self.images)+len(self.marks)==0: return - self.update() + if self.conbox[2] - self.conbox[0] > 1: self.update() def on_idle(self, event): need = sum([i.dirty for i in self.images]) diff --git a/sciwx/canvas/mcanvas.py b/sciwx/canvas/mcanvas.py index 6148d405..5c64dd37 100644 --- a/sciwx/canvas/mcanvas.py +++ b/sciwx/canvas/mcanvas.py @@ -21,6 +21,7 @@ def set_img(self, img, b=False): if not b and not isarr: self.images[0] = img if b and isarr: self.images[0].back.img = img if not b and isarr: self.images[0].img = img + [self.images[0], self.images[0].back][b].reset() self.update() def set_log(self, log, b=False): @@ -194,8 +195,8 @@ def image(self): return self.canvas.image def name(self): return self.canvas.image.name def set_imgs(self, imgs, b=False): - if b: self.canvas.back.imgs = imgs - else: self.canvas.image.imgs = imgs + if b: self.canvas.back.set_imgs(imgs) + else: self.canvas.image.set_imgs(imgs) self.canvas.update_box() self.update() diff --git a/sciwx/demo/canvas1_demo.py b/sciwx/demo/canvas1_demo.py index 09f30381..ff351308 100644 --- a/sciwx/demo/canvas1_demo.py +++ b/sciwx/demo/canvas1_demo.py @@ -2,7 +2,7 @@ sys.path.append('../../') from skimage.data import astronaut, camera from numpy.fft import fft2, fftshift -from sciwx.canvas import Canvas as Canvas +from sciwx.canvas import ICanvas as Canvas from sciapp.object import Image import wx @@ -45,6 +45,7 @@ def complex_test(): #complex_test() frame = wx.Frame(None, title='blend') canvas = Canvas(frame, autofit=True) + image = Image() image.img = camera() image.pos = (0,0) @@ -57,3 +58,4 @@ def complex_test(): canvas.images.append(image) frame.Show() app.MainLoop() + diff --git a/sciwx/demo/canvas3_image_obj.py b/sciwx/demo/canvas3_image_obj.py index ef138373..3c8cd9c6 100644 --- a/sciwx/demo/canvas3_image_obj.py +++ b/sciwx/demo/canvas3_image_obj.py @@ -1,7 +1,8 @@ import sys sys.path.append('../../') from skimage.data import astronaut, camera -from sciwx.canvas import Canvas, Image, MCanvas +from sciapp.object import Image +from sciwx.canvas import ICanvas, MCanvas import wx def image_canvas_test(): @@ -10,7 +11,7 @@ def image_canvas_test(): obj.cn = 0 frame = wx.Frame(None, title='gray test') - canvas = Canvas(frame, autofit=True) + canvas = ICanvas(frame, autofit=True) canvas.set_img(obj) frame.Show() diff --git a/sciwx/demo/canvas5_frame_demo.py b/sciwx/demo/canvas5_frame_demo.py index 5d41b812..83ea52ca 100644 --- a/sciwx/demo/canvas5_frame_demo.py +++ b/sciwx/demo/canvas5_frame_demo.py @@ -21,6 +21,6 @@ def canvas_note_test(): if __name__ == '__main__': app = wx.App() - canvas_frame_test() + # canvas_frame_test() canvas_note_test() app.MainLoop() From 2aaf8a08dd42300ea68ce4fe49b2cb08eb6680ed Mon Sep 17 00:00:00 2001 From: qixinbo Date: Sun, 7 Jun 2020 12:40:33 +0800 Subject: [PATCH 216/343] fix bugs of 'save' and 'save with mark' --- imagepy/core/app/imagepy.py | 2 +- imagepy/core/util/fileio.py | 2 +- imagepy/menus/File/save_plg.py | 2 +- sciwx/canvas/canvas.py | 37 ++++++++++++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/imagepy/core/app/imagepy.py b/imagepy/core/app/imagepy.py index 15ec9419..ceea41b3 100644 --- a/imagepy/core/app/imagepy.py +++ b/imagepy/core/app/imagepy.py @@ -59,7 +59,7 @@ def load_menu(self, data): def load_tool(self, data, default=None): for i, (name, tols) in enumerate(data[1]): self.toolbar.add_tools(name, tols, i==0) - if not default is None: self.toolbar.add_pop('./tools/drop.gif', default) + if not default is None: self.toolbar.add_pop('./imagepy/tools/drop.gif', default) self.toolbar.Layout() def load_widget(self, data): diff --git a/imagepy/core/util/fileio.py b/imagepy/core/util/fileio.py index ad06be77..ee39cd1d 100644 --- a/imagepy/core/util/fileio.py +++ b/imagepy/core/util/fileio.py @@ -69,7 +69,7 @@ def show(self): def run(self, ips, imgs, para = None): fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) - writer = Source.manager('writer').get(ext=fe[1:], tag='img') + writer = Source.manager('writer').gets(name=fe[1:]) if len(writer)==1: return writer[0][1](para['path'], ips.img) writer = Source.manager('writer').get(fe[1:], 'imgs') if len(writer)==1: return writer[0][1](para['path'], imgs) \ No newline at end of file diff --git a/imagepy/menus/File/save_plg.py b/imagepy/menus/File/save_plg.py index 2818e71b..cf6d0399 100644 --- a/imagepy/menus/File/save_plg.py +++ b/imagepy/menus/File/save_plg.py @@ -19,6 +19,6 @@ class WindowCapture(fileio.Writer): filt = ['PNG'] def run(self, ips, imgs, para = None): - WindowsManager.get().canvas.save_buffer(para['path']) + self.app.get_img_win().canvas.save_buffer(para['path']) plgs = [SaveImage, WindowCapture] \ No newline at end of file diff --git a/sciwx/canvas/canvas.py b/sciwx/canvas/canvas.py index 8c5f4ad3..6ccc95cd 100644 --- a/sciwx/canvas/canvas.py +++ b/sciwx/canvas/canvas.py @@ -162,6 +162,8 @@ def update(self, counter = [0,0]): winbox=self.winbox, oribox=self.oribox, conbox=self.conbox) dc.UnMask() + self.dc = dc + counter[1] += time()-start if counter[0] == 50: print('frame rate:',int(50/max(0.001,counter[1]))) @@ -260,6 +262,41 @@ def to_panel_coor(self, x, y): if self.up: y = (self.winbox[3]-self.winbox[1]) - y return x, y + def save_buffer(self, path): + dcSource = self.dc + # based largely on code posted to wxpython-users by Andrea Gavana 2006-11-08 + size = dcSource.Size + + # Create a Bitmap that will later on hold the screenshot image + # Note that the Bitmap must have a size big enough to hold the screenshot + # -1 means using the current default colour depth + bmp = wx.Bitmap(size.width, size.height) + + # Create a memory DC that will be used for actually taking the screenshot + memDC = wx.MemoryDC() + + # Tell the memory DC to use our Bitmap + # all drawing action on the memory DC will go to the Bitmap now + memDC.SelectObject(bmp) + + # Blit (in this case copy) the actual screen on the memory DC + # and thus the Bitmap + memDC.Blit( 0, # Copy to this X coordinate + 0, # Copy to this Y coordinate + size.width, # Copy this width + size.height, # Copy this height + dcSource, # From where do we copy? + 0, # What's the X offset in the original DC? + 0 # What's the Y offset in the original DC? + ) + + # Select the Bitmap out of the memory DC by selecting a new + # uninitialized Bitmap + memDC.SelectObject(wx.NullBitmap) + + img = bmp.ConvertToImage() + img.SaveFile(path, wx.BITMAP_TYPE_PNG) + def __del__(self): # self.img = self.back = None print('========== canvas del') From 5255e335f041ff13ceb9860cfec6509fb2eeaca7 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 7 Jun 2020 14:49:48 +0800 Subject: [PATCH 217/343] stadard tool ok --- imagepy/__main__.py | 11 -- imagepy/core/app/imagepy.py | 4 + imagepy/tools/Standard/colorpicker_tol.py | 22 +--- imagepy/tools/Standard/magic_tol.py | 148 ++++++++++++---------- imagepy/tools/Standard/move_tol.py | 50 +++----- imagepy/tools/Standard/painter_tol.py | 7 +- imagepy/tools/Standard/scale_tol.py | 10 +- sciapp/action/shpbase.py | 3 +- sciapp/action/tolact.py | 4 + sciwx/widgets/toolbar.py | 22 +++- 10 files changed, 144 insertions(+), 137 deletions(-) diff --git a/imagepy/__main__.py b/imagepy/__main__.py index f927f064..17d715dd 100644 --- a/imagepy/__main__.py +++ b/imagepy/__main__.py @@ -2,17 +2,6 @@ sys.path.append('../') from imagepy.core.app import loader, ImagePy -''' -from sciapp.action import ImgAction, Tool, DefaultTool -from sciwx.plugins.curve import Curve -from sciwx.plugins.channels import Channels -from sciwx.plugins.histogram import Histogram -from sciwx.plugins.viewport import ViewPort -from sciwx.plugins.filters import Gaussian, Undo -from sciwx.plugins.pencil import Pencil -from sciwx.plugins.io import Open, Save -''' - def extend_plgs(plg): if isinstance(plg, tuple): return (plg[0].title, extend_plgs(plg[1])) diff --git a/imagepy/core/app/imagepy.py b/imagepy/core/app/imagepy.py index 15ec9419..b32ccdec 100644 --- a/imagepy/core/app/imagepy.py +++ b/imagepy/core/app/imagepy.py @@ -40,6 +40,10 @@ def __init__( self, parent ): self.Bind(wx.EVT_CLOSE, self.on_close) self.Bind(aui.EVT_AUI_PANE_CLOSE, self.on_pan_close) + def source(self): + self.manager('color').add('front', (255, 255, 255)) + self.manager('color').add('back', (0, 0, 0)) + def init_status(self): self.stapanel = stapanel = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) sizersta = wx.BoxSizer( wx.HORIZONTAL ) diff --git a/imagepy/tools/Standard/colorpicker_tol.py b/imagepy/tools/Standard/colorpicker_tol.py index e0b2590e..4b3c8028 100644 --- a/imagepy/tools/Standard/colorpicker_tol.py +++ b/imagepy/tools/Standard/colorpicker_tol.py @@ -1,13 +1,6 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Oct 19 17:35:09 2016 +from sciapp.action import ImageTool -@author: yxl -""" -from imagepy.core.engine import Tool -from sciapp import Source - -class Plugin(Tool): +class Plugin(ImageTool): """ColorPicker class plugin with events callbacks""" title = 'Color Picker' para = {'front':(255,255,255), 'back':(0,0,0)} @@ -15,14 +8,13 @@ class Plugin(Tool): ('color', 'back', 'back', 'color')] def config(self): - ColorManager.set_front(self.para['front']) - ColorManager.set_back(self.para['back']) + self.app.manager('color').add('front', self.para['front']) + self.app.manager('color').add('back', self.para['back']) def mouse_down(self, ips, x, y, btn, **key): - if btn == 1:ColorManager.set_front(ips.img[int(y), int(x)]) - if btn == 3:ColorManager.set_back(ips.img[int(y), int(x)]) - print(ips.img[int(y), int(x)]) - print(ColorManager.get_front()) + manager = self.app.manager('color') + if btn == 1: manager.add('front', ips.img[int(y), int(x)]) + if btn == 3: manager.add('back', ips.img[int(y), int(x)]) def mouse_up(self, ips, x, y, btn, **key): pass diff --git a/imagepy/tools/Standard/magic_tol.py b/imagepy/tools/Standard/magic_tol.py index edf6b971..1ae90ac1 100644 --- a/imagepy/tools/Standard/magic_tol.py +++ b/imagepy/tools/Standard/magic_tol.py @@ -5,17 +5,17 @@ @author: yxl """ import wx -from imagepy.core.engine import Tool +from sciapp.action import ImageTool, BaseROI, BaseEditor +from sciapp.util import geom_union, geom_flatten, geom2shp +from sciapp.object import ROI import numpy as np -#from imagepy.core.manager import ColorManager from skimage.morphology import flood_fill, flood from skimage.measure import find_contours -# from imagepy.core.roi.convert import shape2roi, roi2shape from shapely.geometry import Polygon, Point from shapely.ops import cascaded_union -import matplotlib.pyplot as plt -def polygonize(conts, withholes = False): + +def polygonize(conts, withholes = True): for i in conts:i[:,[0,1]] = i[:,[1,0]] polygon = Polygon(conts[0]).buffer(0) if not withholes:return polygon @@ -26,73 +26,87 @@ def polygonize(conts, withholes = False): hole = cascaded_union(holes) return polygon.difference(hole) - -class Plugin(Tool): +def magic_cont(img, x, y, conn, tor): + img = img.reshape((img.shape+(1,))[:3]) + msk = np.ones(img.shape[:2], dtype=np.bool) + for i in range(img.shape[2]): + msk &= flood(img[:,:,i], (int(y),int(x)), + connectivity=conn, tolerance=tor) + return find_contours(msk, 0, 'high') + +def inbase(key, btn): + status = key['ctrl'], key['alt'], key['shift'] + return status == (1,1,0) or btn in {2,3} + +class Plugin(BaseROI): title = 'Magic Stick' para = {'tor':10, 'con':'8-connect'} view = [(int, 'tor', (0,1000), 0, 'torlorance', 'value'), (list, 'con', ['4-connect', '8-connect'], str, 'fill', 'pix')] - def __init__(self): - self.curobj = None - self.oper = '' - - def mouse_down(self, ips, x, y, btn, **key): - lim = 5.0/key['canvas'].scale - if btn==1 or btn==3: - if ips.roi!= None: - self.curobj = ips.roi.pick(x, y, ips.cur, lim) - ips.roi.info(ips, self.curobj) - if not self.curobj in (None,True):return - if ips.roi == None: - connectivity=(self.para['con']=='8-connect')+1 - img = ips.img.reshape((ips.img.shape+(1,))[:3]) - msk = np.ones(img.shape[:2], dtype=np.bool) - for i in range(img.shape[2]): - msk &= flood(img[:,:,i], (int(y),int(x)), - connectivity=connectivity, tolerance=self.para['tor']) - conts = find_contours(msk, 0, 'high') - ips.roi = shape2roi(polygonize(conts, btn==3)) - elif hasattr(ips.roi, 'topolygon'): - shp = roi2shape(ips.roi.topolygon()) - oper = '' - if key['shift']: oper = '+' - elif key['ctrl']: oper = '-' - elif self.curobj: return - else: ips.roi=None + + def __init__(self): + BaseROI.__init__(self, BaseEditor) + + def mouse_down(self, ips, x, y, btn, **key): + if ips.roi is None: ips.roi = ROI() + else: ips.roi.msk = None + if inbase(key, btn): + BaseEditor.mouse_down(self, ips.roi, x, y, btn, **key) + if key['alt'] and key['ctrl']: return + if btn==1: + conts = magic_cont(ips.img, x, y, + (self.para['con']=='8-connect')+1, self.para['tor']) + ips.roi.body.append(geom2shp(polygonize(conts))) + ips.roi.dirty = True - connectivity=(self.para['con']=='8-connect')+1 - img = ips.img.reshape((ips.img.shape+(1,))[:3]) - msk = np.ones(img.shape[:2], dtype=np.bool) - for i in range(img.shape[2]): - msk &= flood(img[:,:,i], (int(y),int(x)), - connectivity=connectivity, tolerance=self.para['tor']) - conts = find_contours(msk, 0, 'high') - cur = polygonize(conts, btn==3) - if oper == '+': - ips.roi = shape2roi(shp.union(cur)) - elif oper == '-': - ips.roi = shape2roi(shp.difference(cur)) - else: ips.roi = shape2roi(cur) + if key['alt'] or key['shift']: + obj = ips.roi.body.pop(-1) + rst = geom_union(ips.roi.to_geom()) + if key['alt'] and not key['shift']: + rst = rst.difference(obj.to_geom()) + if key['shift'] and not key['alt']: + rst = rst.union(obj.to_geom()) + if key['shift'] and key['alt']: + rst = rst.intersection(obj.to_geom()) + layer = geom2shp(geom_flatten(rst)) + ips.roi.body = layer.body + ips.roi.dirty = True - else: ips.roi = None + ''' - ips.update() - - def mouse_up(self, ips, x, y, btn, **key): - ips.update() - - def mouse_move(self, ips, x, y, btn, **key): - if ips.roi==None:return - lim = 5.0/key['canvas'].scale - if btn==None: - self.cursor = wx.CURSOR_CROSS - if ips.roi.snap(x, y, ips.cur, lim)!=None: - self.cursor = wx.CURSOR_HAND - elif btn==1: - if self.curobj: ips.roi.draged(self.odx, self.ody, x, y, ips.cur, self.curobj) - ips.update() - self.odx, self.ody = x, y - - def mouse_wheel(self, ips, x, y, d, **key): - pass \ No newline at end of file + def __init__(self): + BaseEditor.__init__(self) + self.cur, self.n, self.obj = 0, 0, None + + def mouse_down(self, shp, x, y, btn, **key): + if inbase(key, btn) and self.obj is None: + BaseEditor.mouse_down(self, shp, x, y, btn, **key) + if key['alt'] and key['ctrl']: return + if btn==1: + if self.obj is None: + self.obj = Line([(x,y)]) + shp.body.append(self.obj) + else: + self.obj.body = np.vstack((self.obj.body, [(x,y)])) + anchor = Points(mark(self.obj)[0], color=(255,0,0)) + key['canvas'].marks['buffer'] = anchor + if btn==3 and not self.obj is None : + body = np.vstack((self.obj.body, [(x,y)])) + shp.body[-1] = Polygon(body) + if key['alt'] or key['shift']: + obj = shp.body.pop(-1) + rst = geom_union(shp.to_geom()) + if key['alt'] and not key['shift']: + rst = rst.difference(obj.to_geom()) + if key['shift'] and not key['alt']: + rst = rst.union(obj.to_geom()) + if key['shift'] and key['alt']: + rst = rst.intersection(obj.to_geom()) + layer = geom2shp(geom_flatten(rst)) + shp.body = layer.body + self.obj, shp.dirty = None, True + del key['canvas'].marks['buffer'] + shp.dirty = True + + ''' \ No newline at end of file diff --git a/imagepy/tools/Standard/move_tol.py b/imagepy/tools/Standard/move_tol.py index bcb5c49b..c1f6c7ca 100644 --- a/imagepy/tools/Standard/move_tol.py +++ b/imagepy/tools/Standard/move_tol.py @@ -1,32 +1,24 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Nov 16 12:12:43 2016 +from sciapp.action import ImageTool -@author: yxl -""" -import wx -from imagepy.core.engine import Tool - -class Plugin(Tool): - title = 'Move' - def __init__(self): - self.ox, self.oy = 0, 0 - self.cursor = wx.CURSOR_HAND - - def mouse_down(self, ips, x, y, btn, **key): - self.ox, self.oy = key['canvas'].to_panel_coor(x,y) +class Plugin(ImageTool): + title = 'Move And Scale' + def __init__(self): + self.oldxy = None + + def mouse_down(self, obj, x, y, btn, **key): + if btn==1: self.oldxy = key['px'], key['py'] + if btn==3: key['canvas'].fit() + + def mouse_up(self, obj, x, y, btn, **key): + self.oldxy = None - def mouse_up(self, ips, x, y, btn, **key): - pass + def mouse_move(self, obj, x, y, btn, **key): + if self.oldxy is None: return + ox, oy = self.oldxy + up = (1,-1)[key['canvas'].up] + key['canvas'].move(key['px']-ox, (key['py']-oy)*up) + self.oldxy = key['px'], key['py'] - def mouse_move(self, ips, x, y, btn, **key): - if btn==None:return - x,y = key['canvas'].to_panel_coor(x,y) - key['canvas'].move(x-self.ox, y-self.oy) - self.ox, self.oy = x,y - ips.update() - - def mouse_wheel(self, ips, x, y, d, **key): - if d>0:key['canvas'].zoomout(x, y, 'data') - if d<0:key['canvas'].zoomin(x, y, 'data') - ips.update() \ No newline at end of file + def mouse_wheel(self, obj, x, y, d, **key): + if d>0: key['canvas'].zoomout(x, y, coord='data') + if d<0: key['canvas'].zoomin(x, y, coord='data') \ No newline at end of file diff --git a/imagepy/tools/Standard/painter_tol.py b/imagepy/tools/Standard/painter_tol.py index 51fe6ced..ad820717 100644 --- a/imagepy/tools/Standard/painter_tol.py +++ b/imagepy/tools/Standard/painter_tol.py @@ -1,5 +1,4 @@ -from imagepy.core.engine import Tool -from sciapp import Source +from sciapp.action import ImageTool from skimage.draw import line, circle def drawline(img, oldp, newp, w, value): @@ -10,7 +9,7 @@ def drawline(img, oldp, newp, w, value): xs = (ox.reshape((-1,1))+cx).clip(0, img.shape[1]-1) img[ys.ravel(), xs.ravel()] = value -class Plugin(Tool): +class Plugin(ImageTool): title = 'Pencil' para = {'width':1} @@ -31,7 +30,7 @@ def mouse_up(self, ips, x, y, btn, **key): def mouse_move(self, ips, x, y, btn, **key): if not self.status:return w = self.para['width'] - value = Source.manager('color').get('front') + value = self.app.manager('color').get('front') drawline(ips.img, self.oldp, (y, x), w, value) self.oldp = (y, x) ips.update() diff --git a/imagepy/tools/Standard/scale_tol.py b/imagepy/tools/Standard/scale_tol.py index 8f64c1dc..7a7ef570 100644 --- a/imagepy/tools/Standard/scale_tol.py +++ b/imagepy/tools/Standard/scale_tol.py @@ -1,12 +1,6 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Oct 19 17:35:09 2016 +from sciapp.action import ImageTool -@author: yxl -""" -from imagepy.core.engine import Tool - -class Plugin(Tool): +class Plugin(ImageTool): title = 'Scope' def __init__(self): self.ox, self.oy = 0, 0 diff --git a/sciapp/action/shpbase.py b/sciapp/action/shpbase.py index 6435049a..e8ab631b 100644 --- a/sciapp/action/shpbase.py +++ b/sciapp/action/shpbase.py @@ -1,5 +1,6 @@ from .tolact import ShapeTool from sciapp.object import * +from sciapp.util import geom_union from numpy.linalg import norm import numpy as np @@ -239,7 +240,7 @@ def mouse_down(self, shp, x, y, btn, **key): else: shp.body.remove(obj) shp.dirty = True if key['shift'] and not key['alt'] and not key['ctrl']: - layer = geom2shp(union(shp.to_geom())) + layer = geom2shp(geom_union(shp.to_geom())) shp.body = layer.body shp.dirty = True if not (key['shift'] or key['alt'] or key['ctrl']): diff --git a/sciapp/action/tolact.py b/sciapp/action/tolact.py index 3d68f129..c09c9fc7 100644 --- a/sciapp/action/tolact.py +++ b/sciapp/action/tolact.py @@ -4,6 +4,10 @@ class Tool(SciAction): title = 'Base Tool' default = None cursor = 'arrow' + view = None + para = None + + def config(self): pass def mouse_down(self, canvas, x, y, btn, **key): pass def mouse_up(self, canvas, x, y, btn, **key): pass def mouse_move(self, canvas, x, y, btn, **key): pass diff --git a/sciwx/widgets/toolbar.py b/sciwx/widgets/toolbar.py index 06f52921..cf3d9f1a 100644 --- a/sciwx/widgets/toolbar.py +++ b/sciwx/widgets/toolbar.py @@ -1,4 +1,5 @@ import wx +from .paradialog import ParaDialog def make_logo(obj): if isinstance(obj, str) and len(obj)>1: @@ -34,7 +35,7 @@ def __init__(self, parent, vertical=False): self.curbtn = None def on_tool(self, evt, tol): - tol().start(self.app) + tol.start(self.app) evt.Skip() btn = evt.GetEventObject() #print(self.GetBackgroundColour()) @@ -44,9 +45,26 @@ def on_tool(self, evt, tol): self.curbtn = btn btn.SetBackgroundColour(wx.SystemSettings.GetColour( wx.SYS_COLOUR_ACTIVECAPTION ) ) + def on_config(self, evt, tol): + self.app.show_para(tol.title, tol.view, tol.para) + tol.config() + + def on_help(self, evt, tol): + pass + + def on_info(self, event, tol): + pass + def bind(self, btn, tol): + obj = tol() btn.SetBackgroundColour(self.GetBackgroundColour()) - btn.Bind( wx.EVT_LEFT_DOWN, lambda e, obj=tol: self.on_tool(e, obj)) + btn.Bind( wx.EVT_LEFT_DOWN, lambda e, obj=obj: self.on_tool(e, obj)) + + #btn.Bind( wx.EVT_RIGHT_DOWN, lambda x, p=data[0]: IPy.show_md(p.title, DocumentManager.get(p.title))) + #btn.Bind( wx.EVT_ENTER_WINDOW, + # lambda x, p='"{}" Tool'.format(data[0].title): set_info(p)) + #if not isinstance(data[0], Macros) and issubclass(data[0], Tool): + btn.Bind(wx.EVT_LEFT_DCLICK, lambda e, obj=obj: self.on_config(e, obj)) def add_tool(self, logo, tool): btn = wx.BitmapButton(self, wx.ID_ANY, make_logo(logo), From b5dc3a525ab462244ae5c04aaeaa1f22955e9068 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 7 Jun 2020 15:37:13 +0800 Subject: [PATCH 218/343] stack tools ok --- imagepy/core/app/imagepy.py | 3 ++- imagepy/menus/Image/Stack/stack_plgs.py | 11 ++++++++++- sciwx/canvas/mcanvas.py | 15 ++++++++------- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/imagepy/core/app/imagepy.py b/imagepy/core/app/imagepy.py index 70a22491..8f2ebbd4 100644 --- a/imagepy/core/app/imagepy.py +++ b/imagepy/core/app/imagepy.py @@ -39,6 +39,7 @@ def __init__( self, parent ): self.Bind(wx.EVT_CLOSE, self.on_close) self.Bind(aui.EVT_AUI_PANE_CLOSE, self.on_pan_close) + self.source() def source(self): self.manager('color').add('front', (255, 255, 255)) @@ -63,7 +64,7 @@ def load_menu(self, data): def load_tool(self, data, default=None): for i, (name, tols) in enumerate(data[1]): self.toolbar.add_tools(name, tols, i==0) - if not default is None: self.toolbar.add_pop('./imagepy/tools/drop.gif', default) + if not default is None: self.toolbar.add_pop('./tools/drop.gif', default) self.toolbar.Layout() def load_widget(self, data): diff --git a/imagepy/menus/Image/Stack/stack_plgs.py b/imagepy/menus/Image/Stack/stack_plgs.py index 2470d05f..c373414f 100644 --- a/imagepy/menus/Image/Stack/stack_plgs.py +++ b/imagepy/menus/Image/Stack/stack_plgs.py @@ -11,8 +11,10 @@ class SetSlice(Simple): #parameter para = {'num':0} - view = [(int, 'num', (0,999), 0, 'Num', '')] + def load(self, ips): + self.view = [(int, 'num', (0, ips.slices-1), 0, 'Num', '0~%d'%(ips.slices-1))] + return True #process def run(self, ips, imgs, para = None): if para['num']>=0 and para['num']1 and not self.sli_page.Shown: self.sli_page.Show() - self.sli_page.SetScrollbar(0, 0, slices-1, 0) - self.pages = slices + self.sli_page.SetScrollbar(self.image.cur, 0, slices-1, 0, True) + self.pages, self.cur = slices, self.image.cur if channels != self.chans or self.cn != self.image.cn: print('set channels') if not isinstance(self.image.cn, int) and self.sli_chan.Shown: self.sli_chan.Hide() if isinstance(self.image.cn, int) and channels>1: if not self.sli_chan.Shown: self.sli_chan.Show() + self.sli_chan.SetValue(self.image.cn) self.sli_chan.SetMax(channels-1) - self.chans, self.cn = channels, self.canvas.image.cn + self.chans, self.cn = channels, self.image.cn def update(self): if self.image.img is None: return @@ -241,8 +242,8 @@ def on_scroll(self, event): def on_idle(self, event): image, info = self.image, self.lab_info.GetLabel() - imgs = image.slices, image.channels, image.cn - selfs = self.pages ,self.chans, self.cn + imgs = image.slices, image.channels, image.cn, image.cur + selfs = self.pages ,self.chans, self.cn, self.cur if imgs != selfs or info!=self.image.info: self.update() From 096446476cc6f54221b5af145c0c113080d388de Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 7 Jun 2020 16:11:46 +0800 Subject: [PATCH 219/343] draw tools ok --- imagepy/tools/Draw/Route.gif | Bin 98 -> 82 bytes imagepy/tools/Draw/Route_tol.py | 30 ++++++--- imagepy/tools/Draw/__init__.py | 2 +- imagepy/tools/Draw/aibrush_tol.py | 19 +++--- imagepy/tools/Draw/flood3d_tol.py | 8 +-- imagepy/tools/Draw/floodfill_tol.py | 7 +- imagepy/tools/Draw/magic.gif | Bin 98 -> 0 bytes imagepy/tools/Draw/magic_tol.py | 97 ---------------------------- sciwx/widgets/toolbar.py | 1 + 9 files changed, 38 insertions(+), 126 deletions(-) delete mode 100644 imagepy/tools/Draw/magic.gif delete mode 100644 imagepy/tools/Draw/magic_tol.py diff --git a/imagepy/tools/Draw/Route.gif b/imagepy/tools/Draw/Route.gif index d4cbd24b77d9361d7c0e10f6734910eec5831d7d..e0f0ca2f23b3c188f4d489cf244e1fb0e28bab80 100644 GIT binary patch literal 82 zcmZ?wbhEHb6krfwXkY+>Y5)KKSNzGs$iTqJpaT*B$uKZ!vNV;Qs63X?efjE!ti0Lx i=f&JmZ&EPTym-lWox82_nMHB3S=Z*xlMi5Eum%9v`Wr|9 literal 98 zcmZ?wbhEHb6krfwXkcVuV3_v*|9{1Qg3d*$i6yBi3gww484B*6z5xu1KUo;L7#JCJ zfQo>M1sE8ZR9y0wcgvm>X$fSKdVhP@qIZ@HpESfJ^)1@9DKjJCgL1f`H!~}PH2|Uj BA7KCh diff --git a/imagepy/tools/Draw/Route_tol.py b/imagepy/tools/Draw/Route_tol.py index 0dd3f6e3..f1772eae 100644 --- a/imagepy/tools/Draw/Route_tol.py +++ b/imagepy/tools/Draw/Route_tol.py @@ -4,15 +4,17 @@ @author: yxl """ -import wx from imagepy.core.engine import Tool,Filter import numpy as np +from sciapp.util import mark2shp +from sciapp.action import ImageTool #from imagepy.core.manager import ColorManager #from imagepy.core.roi import lineroi #from imagepy.core.mark import GeometryMark from skimage.graph import route_through_array import scipy.ndimage as ndimg -def route_through(ips, snap,poins,para): + +def route_through(ips, snap, poins,para): para['max']=True para['lcost']=2 img = snap.astype('float32') @@ -27,7 +29,7 @@ def route_through(ips, snap,poins,para): routes.append(indices) return routes -class Plugin(Tool): +class Plugin(ImageTool): title = 'Route Toolk' note = ['auto_snap','8-bit', '16-bit','int', 'float','2int', 'preview'] para = {'fully connected':True, 'lcost':0, 'max':False, 'geometric':True, 'type':'ROI'} @@ -36,13 +38,14 @@ class Plugin(Tool): (bool, 'fully connected', 'fully connected'), (bool, 'geometric', 'geometric'), (list, 'type', ['white line', 'ROI'], str, 'output', '')] + def __init__(self): self.curobj = None self.doing = 'Nothing' #初始化线条缓存 # self.helper = AnchorLine() self.odx,self.ody = 0, 0 - self.cursor = wx.CURSOR_CROSS + self.cursor = 'cross' self.buf=[] def mouse_down(self, ips, x, y, btn, **key): @@ -75,7 +78,7 @@ def mouse_down(self, ips, x, y, btn, **key): if self.doing=='Nothing': if len(self.buf) == 0: self.doing ='draw' - elif self.doing=='draw' and self.cursor==wx.CURSOR_HAND: + elif self.doing=='draw' and self.cursor=='hand': a=self.in_points(x,y,lim) if a!=(-1,-1): # print('doing to move') @@ -93,7 +96,7 @@ def mouse_down(self, ips, x, y, btn, **key): self.doing='Nothing' self.draw(ips) return - if self.doing=='draw' and self.cursor==wx.CURSOR_CROSS: + if self.doing=='draw' and self.cursor=='cross': self.addpoint((x,y)) self.draw(ips) elif btn == 3: @@ -109,11 +112,11 @@ def mouse_down(self, ips, x, y, btn, **key): self.doing='down' def mouse_up(self, ips, x, y, btn, **key): - if self.doing == 'move' and btn == 1: self.doing = 'draw' elif self.doing == 'all_move' and btn ==2: self.doing = self.do_old + def mouse_move(self, ips, x, y, btn, **key): if len(self.buf)==0:return lim = 5.0/key['canvas'].scale @@ -125,10 +128,10 @@ def mouse_move(self, ips, x, y, btn, **key): self.draw(ips) elif btn==None: #鼠标变成一个十字架 - self.cursor = wx.CURSOR_CROSS + self.cursor = 'cross' a=self.in_points(x,y,lim) if a!=(-1,-1): - self.cursor = wx.CURSOR_HAND + self.cursor = 'hand' if self.doing == 'all_move': x,y = key['canvas'].to_panel_coor(x,y) key['canvas'].move(x-self.oldp[0], y-self.oldp[1]) @@ -136,6 +139,7 @@ def mouse_move(self, ips, x, y, btn, **key): ips.update() # print('move') self.odx, self.ody = x, y + def in_points(self,x,y,range): for i in self.buf: if ((i[0]>x-range) and (i[0]y-range) and (i[1]=img.shape[0] or int(x)>=img.shape[1]: return diff --git a/imagepy/tools/Draw/magic.gif b/imagepy/tools/Draw/magic.gif deleted file mode 100644 index d4cbd24b77d9361d7c0e10f6734910eec5831d7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 98 zcmZ?wbhEHb6krfwXkcVuV3_v*|9{1Qg3d*$i6yBi3gww484B*6z5xu1KUo;L7#JCJ zfQo>M1sE8ZR9y0wcgvm>X$fSKdVhP@qIZ@HpESfJ^)1@9DKjJCgL1f`H!~}PH2|Uj BA7KCh diff --git a/imagepy/tools/Draw/magic_tol.py b/imagepy/tools/Draw/magic_tol.py deleted file mode 100644 index f694724a..00000000 --- a/imagepy/tools/Draw/magic_tol.py +++ /dev/null @@ -1,97 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Oct 19 17:35:09 2016 - -@author: yxl -""" -import wx -from imagepy.core.engine import Tool -import numpy as np -from skimage.morphology import flood_fill, flood -from skimage.measure import find_contours -# from imagepy.core.roi.convert import shape2roi, roi2shape -from shapely.geometry import Polygon, Point -from shapely.ops import cascaded_union -import matplotlib.pyplot as plt - -def polygonize(conts, withholes = False): - for i in conts:i[:,[0,1]] = i[:,[1,0]] - polygon = Polygon(conts[0]).buffer(0) - if not withholes:return polygon - holes = [] - for i in conts[1:]: - if len(i)<4:continue - holes.append(Polygon(i).buffer(0)) - hole = cascaded_union(holes) - return polygon.difference(hole) - - -class Plugin(Tool): - title = 'Magic Stick' - para = {'tor':10, 'con':'8-connect'} - view = [(int, 'tor', (0,1000), 0, 'torlorance', 'value'), - (list, 'con', ['4-connect', '8-connect'], str, 'fill', 'pix')] - - def __init__(self): - self.curobj = None - self.oper = '' - - def mouse_down(self, ips, x, y, btn, **key): - lim = 5.0/key['canvas'].scale - if btn==1 or btn==3: - if ips.roi!= None: - self.curobj = ips.roi.pick(x, y, ips.cur, lim) - ips.roi.info(ips, self.curobj) - if not self.curobj in (None,True):return - if ips.roi == None: - connectivity=(self.para['con']=='8-connect')+1 - img = ips.img.reshape((ips.img.shape+(1,))[:3]) - msk = np.ones(img.shape[:2], dtype=np.bool) - for i in range(img.shape[2]): - msk &= flood(img[:,:,i], (int(y),int(x)), - connectivity=connectivity, tolerance=self.para['tor']) - conts = find_contours(msk, 0, 'high') - ips.roi = shape2roi(polygonize(conts, btn==3)) - elif hasattr(ips.roi, 'topolygon'): - shp = roi2shape(ips.roi.topolygon()) - oper = '' - if key['shift']: oper = '+' - elif key['ctrl']: oper = '-' - elif self.curobj: return - else: ips.roi=None - - connectivity=(self.para['con']=='8-connect')+1 - img = ips.img.reshape((ips.img.shape+(1,))[:3]) - msk = np.ones(img.shape[:2], dtype=np.bool) - for i in range(img.shape[2]): - msk &= flood(img[:,:,i], (int(y),int(x)), - connectivity=connectivity, tolerance=self.para['tor']) - conts = find_contours(msk, 0, 'high') - cur = polygonize(conts, btn==3) - if oper == '+': - ips.roi = shape2roi(shp.union(cur)) - elif oper == '-': - ips.roi = shape2roi(shp.difference(cur)) - else: ips.roi = shape2roi(cur) - - else: ips.roi = None - - ips.update() - - def mouse_up(self, ips, x, y, btn, **key): - ips.update() - - def mouse_move(self, ips, x, y, btn, **key): - if ips.roi==None:return - lim = 5.0/key['canvas'].scale - if btn==None: - self.cursor = wx.CURSOR_CROSS - if ips.roi.snap(x, y, ips.cur, lim)!=None: - self.cursor = wx.CURSOR_HAND - elif btn==1: - if self.curobj: ips.roi.draged(self.odx, self.ody, x, y, ips.cur, self.curobj) - ips.update() - self.odx, self.ody = x, y - - def mouse_wheel(self, ips, x, y, d, **key): - pass \ No newline at end of file diff --git a/sciwx/widgets/toolbar.py b/sciwx/widgets/toolbar.py index cf3d9f1a..5de10ce1 100644 --- a/sciwx/widgets/toolbar.py +++ b/sciwx/widgets/toolbar.py @@ -46,6 +46,7 @@ def on_tool(self, evt, tol): btn.SetBackgroundColour(wx.SystemSettings.GetColour( wx.SYS_COLOUR_ACTIVECAPTION ) ) def on_config(self, evt, tol): + if not hasattr(tol, 'view'): return self.app.show_para(tol.title, tol.view, tol.para) tol.config() From f7fd55f907474fbfd0241ca96fddf7e22407d22e Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 7 Jun 2020 19:39:54 +0800 Subject: [PATCH 220/343] network tools ok --- .../Analysis/Skeleton Network/graph_plgs.py | 2 +- imagepy/menus/Edit/edit_plg.py | 20 ++++-- imagepy/tools/Network/graphcut_tol.py | 31 +++------ imagepy/tools/Network/graphpen_tol.py | 64 ++++--------------- sciapp/object/roi.py | 60 +---------------- sciapp/util/shputil.py | 62 +++++++++++++++++- 6 files changed, 102 insertions(+), 137 deletions(-) diff --git a/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py b/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py index 4339631c..b3bdd95a 100644 --- a/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py +++ b/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py @@ -31,7 +31,7 @@ class Statistic(Simple): def load(self, ips): if not isinstance(ips.data, nx.MultiGraph): - IPy.alert("Please build graph!"); + self.app.alert("Please build graph!"); return False; return True; diff --git a/imagepy/menus/Edit/edit_plg.py b/imagepy/menus/Edit/edit_plg.py index f9ef7a6b..35f0e925 100644 --- a/imagepy/menus/Edit/edit_plg.py +++ b/imagepy/menus/Edit/edit_plg.py @@ -67,14 +67,20 @@ class Clear(Filter): note = ['req_roi', 'all', 'auto_snap', 'not_channel'] def run(self, ips, snap, img, para=None): - img[ips.mask()] = ColorManager.get_back(ips.channels!=3) + color = self.app.manager('color').get('back') + color = np.array([color]).ravel() + if ips.channels != len(color): color = color.mean() + img[ips.mask()] = color class ClearOut(Filter): title = 'Clear Out' note = ['req_roi', 'all', 'auto_snap', 'not_channel'] def run(self, ips, snap, img, para=None): - img[ips.mask('out')] = ColorManager.get_back(ips.channels!=3) + color = self.app.manager('color').get('back') + color = np.array([color]).ravel() + if ips.channels != len(color): color = color.mean() + img[ips.mask('out')] = color class Copy(Simple): title = 'Copy' @@ -97,14 +103,20 @@ class Sketch(Filter): view = [(int, 'width', (0,30), 0, 'width', 'pix')] def run(self, ips, snap, img, para = None): - img[ips.mask(para['width'])] = ColorManager.get_front(ips.channels!=3) + color = self.app.manager('color').get('front') + color = np.array([color]).ravel() + if ips.channels != len(color): color = color.mean() + img[ips.mask(para['width'])] = color class Fill(Filter): title = 'Fill' note = ['req_roi', 'all', 'auto_snap', 'not_channel'] def run(self, ips, snap, img, para=None): - img[ips.mask()] = ColorManager.get_front(ips.channels!=3) + color = self.app.manager('color').get('front') + color = np.array([color]).ravel() + if ips.channels != len(color): color = color.mean() + img[ips.mask()] = color class Undo(Simple): title = 'Undo' diff --git a/imagepy/tools/Network/graphcut_tol.py b/imagepy/tools/Network/graphcut_tol.py index 2acad92b..d7f9ee71 100644 --- a/imagepy/tools/Network/graphcut_tol.py +++ b/imagepy/tools/Network/graphcut_tol.py @@ -1,14 +1,7 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Oct 19 17:35:09 2016 -@author: yxl -""" -from imagepy.core.engine import Tool +from sciapp.action import ImageTool +from sciapp.object import mark2shp import numpy as np -import wx -from numba import jit -@jit(nopython=True) def floodfill(img, x, y): buf = np.zeros((131072,2), dtype=np.uint16) color = img[int(y), int(x)] @@ -53,36 +46,30 @@ def cut(img, lines): floodfill(img, xx, yy) ox, oy = i -class Mark(): - def __init__(self, line): - self.line = line - - def draw(self, dc, f, **key): - dc.SetPen(wx.Pen((255,0,0), width=2, style=wx.SOLID)) - dc.DrawLines([f(*i) for i in self.line]) - -class Plugin(Tool): +class Plugin(ImageTool): title = 'Graph Cut' def __init__(self): self.status = 0 + self.line = {'type':'line', 'body':[]} def mouse_down(self, ips, x, y, btn, **key): if btn==1: ips.snapshot() self.status = 1 - self.cur = [(x, y)] - ips.mark = Mark(self.cur) + self.line['body'] = [(x, y)] + ips.mark = mark2shp(self.line) ips.update() def mouse_up(self, ips, x, y, btn, **key): ips.mark = None self.status = 0 - cut(ips.img, self.cur) + cut(ips.img, self.line['body']) ips.update() def mouse_move(self, ips, x, y, btn, **key): if self.status==1: - self.cur.append((x, y)) + self.line['body'].append((x, y)) + ips.mark = mark2shp(self.line) ips.update() def mouse_wheel(self, ips, x, y, d, **key): diff --git a/imagepy/tools/Network/graphpen_tol.py b/imagepy/tools/Network/graphpen_tol.py index 0ca8584a..c14d4f72 100644 --- a/imagepy/tools/Network/graphpen_tol.py +++ b/imagepy/tools/Network/graphpen_tol.py @@ -1,37 +1,6 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Oct 19 17:35:09 2016 -@author: yxl -""" -from imagepy.core.engine import Tool +from sciapp.action import ImageTool +from sciapp.object import mark2shp import numpy as np -import wx -from numba import jit - -@jit(nopython=True) -def floodfill(img, x, y): - buf = np.zeros((131072,2), dtype=np.uint16) - color = img[int(y), int(x)] - img[int(y), int(x)] = 0 - buf[0,0] = x; buf[0,1] = y; - cur = 0; s = 1; - - while True: - xy = buf[cur] - for dx in (-1,0,1): - for dy in (-1,0,1): - cx = xy[0]+dx; cy = xy[1]+dy - if cx<0 or cx>=img.shape[1]:continue - if cy<0 or cy>=img.shape[0]:continue - if img[cy, cx]!=color:continue - img[cy, cx] = 0 - buf[s,0] = cx; buf[s,1] = cy - s+=1 - if s==len(buf): - buf[:len(buf)-cur] = buf[cur:] - s -= cur; cur=0 - cur += 1 - if cur==s:break def draw(img, lines): if len(lines)<2:return @@ -50,46 +19,41 @@ def draw(img, lines): xys.append((y,x)) ox, oy = i cur = 0 + neibs = [(-1,-1),(-1,0),(-1,1),(0,1),(1,1),(1,0),(1,-1),(0,-1)] for y,x in xys: - for dx, dy in [(0,1),(0,-1),(1,0),(-1,0)]: + for dx, dy in neibs: if img[y+dy, x+dx]>0: mark.append(cur) - continue + img[y+dy, x+dx] = 255 cur += 1 if len(mark)<4:return - for y,x in xys[mark[0]+1:mark[-1]-1]: - img[y,x] = 128 - -class Mark(): - def __init__(self, line): - self.line = line - - def draw(self, dc, f, **key): - dc.SetPen(wx.Pen((255,0,0), width=2, style=wx.SOLID)) - dc.DrawLines([f(*i) for i in self.line]) + for y,x in xys[mark[0]+1:mark[-1]-1]: img[y,x] = 128 + for cur in mark: img[tuple(xys[cur])] = 255 -class Plugin(Tool): +class Plugin(ImageTool): title = 'Graph Cut' def __init__(self): self.status = 0 + self.line = {'type':'line', 'body':[]} def mouse_down(self, ips, x, y, btn, **key): if btn==1: ips.snapshot() self.status = 1 - self.cur = [(x, y)] - ips.mark = Mark(self.cur) + self.line['body'] = [(x, y)] + ips.mark = mark2shp(self.line) ips.update() def mouse_up(self, ips, x, y, btn, **key): ips.mark = None self.status = 0 - draw(ips.img, self.cur) + draw(ips.img, self.line['body']) ips.update() def mouse_move(self, ips, x, y, btn, **key): if self.status==1: - self.cur.append((x, y)) + self.line['body'].append((x, y)) + ips.mark = mark2shp(self.line) ips.update() def mouse_wheel(self, ips, x, y, d, **key): diff --git a/sciapp/object/roi.py b/sciapp/object/roi.py index f81f9ce3..4dea347c 100644 --- a/sciapp/object/roi.py +++ b/sciapp/object/roi.py @@ -1,66 +1,8 @@ from .shape import * +from ..util import draw_shp import numpy as np -from skimage import draw import shapely.geometry as geom -def circle(r): - xs, ys = np.mgrid[-r:r+1, -r:r+1] - rcs = np.where((xs**2 + ys**2)=0) & (rr=0) & (cc0: rr, cc = draw_lines(rr, cc, img.shape, lw) - else: rr, cc = draw.polygon(rr, cc, img.shape) - img[rr, cc] += color - if type(shp) is geom.MultiLineString: - for i in shp: draw_shp(i, img, color, lw) - if type(shp) is geom.Polygon and lw>0: - draw_shp(shp.exterior, img, color, lw) - for i in shp.interiors: draw_shp(i, img, color, lw) - if type(shp) is geom.Polygon and lw==0: - draw_shp(shp.exterior, img, color, lw) - for i in shp.interiors: draw_shp(i, img, -color, lw) - if type(shp) is geom.MultiPolygon: - for i in shp: draw_shp(i, img, color, lw) - if type(shp) is geom.GeometryCollection: - for i in shp: draw_shp(i, img, color, lw) - return img - class ROI(Layer): def __init__(self, body=None, **key): if isinstance(body, Layer): body = body.body diff --git a/sciapp/util/shputil.py b/sciapp/util/shputil.py index dadb5528..d30f60a5 100644 --- a/sciapp/util/shputil.py +++ b/sciapp/util/shputil.py @@ -1,4 +1,5 @@ import numpy as np +from skimage import draw from ..object.shape import * def offset(shp, dx, dy): @@ -53,4 +54,63 @@ def geom_flatten(obj, geoms=None): if root: return geom.GeometryCollection(geoms) def geom_union(obj): - return geom_flatten(unary_union(geom_flatten(obj))) \ No newline at end of file + return geom_flatten(unary_union(geom_flatten(obj))) + + +def draw_circle(r): + xs, ys = np.mgrid[-r:r+1, -r:r+1] + rcs = np.where((xs**2 + ys**2)=0) & (rr=0) & (cc0: rr, cc = draw_lines(rr, cc, img.shape, lw) + else: rr, cc = draw.polygon(rr, cc, img.shape) + img[rr, cc] += color + if type(shp) is geom.MultiLineString: + for i in shp: draw_shp(i, img, color, lw) + if type(shp) is geom.Polygon and lw>0: + draw_shp(shp.exterior, img, color, lw) + for i in shp.interiors: draw_shp(i, img, color, lw) + if type(shp) is geom.Polygon and lw==0: + draw_shp(shp.exterior, img, color, lw) + for i in shp.interiors: draw_shp(i, img, -color, lw) + if type(shp) is geom.MultiPolygon: + for i in shp: draw_shp(i, img, color, lw) + if type(shp) is geom.GeometryCollection: + for i in shp: draw_shp(i, img, color, lw) + return img \ No newline at end of file From 675c79b1a1ee5632060e0dd0e358e12b5ef8ba20 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 7 Jun 2020 20:02:00 +0800 Subject: [PATCH 221/343] duplicate ok --- imagepy/core/engine/filter.py | 2 +- imagepy/menus/Image/duplicate_plg.py | 2 +- sciapp/object/test.txt | 1 - sciwx/canvas/mcanvas.py | 5 ++++- sciwx/plugins/channels.py | 4 ++-- 5 files changed, 8 insertions(+), 6 deletions(-) delete mode 100644 sciapp/object/test.txt diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index 4fcdbe8e..3665f73d 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -47,7 +47,7 @@ def process_one(plg, ips, src, img, para, callafter=None): if not callafter is None:callafter() def process_stack(plg, ips, src, imgs, para, callafter=None): - Source.manager('task').add(plt.title, plg) + Source.manager('task').add(plg.title, plg) start = time() transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16) diff --git a/imagepy/menus/Image/duplicate_plg.py b/imagepy/menus/Image/duplicate_plg.py index 023b4175..dacea799 100644 --- a/imagepy/menus/Image/duplicate_plg.py +++ b/imagepy/menus/Image/duplicate_plg.py @@ -32,7 +32,7 @@ def run(self, ips, imgs, para = None): new.roi = ROI(mark2shp(ips.roi.to_mark())) offset(new.roi, new.roi.box[0]*-1, new.roi.box[1]*-1) new.roi.dirty = True - if not ips.back is None: + if not ips.back is None and not ips.back.imgs is None: back = [i[sli].copy() for i in ips.back.imgs] if ips.isarray: back = np.array(back) back = Image(back, ips.back.name+'-duplicate') diff --git a/sciapp/object/test.txt b/sciapp/object/test.txt deleted file mode 100644 index e30fa1f6..00000000 --- a/sciapp/object/test.txt +++ /dev/null @@ -1 +0,0 @@ -{'type': 'layer', 'body': [{'type': 'texts', 'body': [(276.27072, 133.78365, 'id=0'), (245.45842, 128.19148, 'id=1'), (215.51852, 120.03704, 'id=2'), (270.0, 121.0, 'id=3'), (223.38889, 123.27778, 'id=4'), (262.0, 121.5, 'id=5'), (286.81937, 130.94838, 'id=6'), (218.11111, 129.5, 'id=7'), (267.0, 127.0, 'id=8'), (251.0, 139.0, 'id=9'), (296.26666, 140.46666, 'id=10'), (272.0, 139.0, 'id=11'), (204.0, 151.0351, 'id=12'), (265.61047, 181.41685, 'id=13'), (270.0, 142.0, 'id=14'), (274.5, 141.0, 'id=15'), (310.54544, 142.81818, 'id=16'), (509.63635, 143.90909, 'id=17'), (296.88235, 148.64706, 'id=18'), (248.0, 147.0, 'id=19'), (492.0, 157.0, 'id=20'), (304.4, 158.8, 'id=21'), (204.14285, 161.0, 'id=22'), (288.2, 161.6, 'id=23'), (206.71428, 171.14285, 'id=24'), (500.0, 169.0, 'id=25'), (508.0, 171.0, 'id=26'), (207.0, 179.0, 'id=27'), (510.2857, 179.0, 'id=28'), (252.0, 183.5, 'id=29'), (504.25, 197.75, 'id=30'), (497.8, 204.6, 'id=31'), (202.12195, 215.56097, 'id=32'), (502.0, 205.0, 'id=33'), (504.33334, 211.0, 'id=34'), (511.0, 211.0, 'id=35'), (256.93332, 216.8, 'id=36'), (502.0, 217.0, 'id=37'), (509.9375, 224.1875, 'id=38'), (261.25, 225.875, 'id=39'), (270.5, 223.0, 'id=40'), (274.83673, 226.65306, 'id=41'), (502.0, 227.0, 'id=42'), (505.5, 229.0, 'id=43'), (256.9091, 240.54546, 'id=44'), (280.0, 237.0, 'id=45'), (508.0, 237.0, 'id=46'), (268.3349, 291.66824, 'id=47'), (287.31097, 293.64563, 'id=48'), (254.0, 255.0, 'id=49'), (225.41493, 294.4707, 'id=50'), (252.0, 265.0, 'id=51'), (250.0, 275.0, 'id=52'), (254.6807, 300.78946, 'id=53'), (248.0, 285.5, 'id=54'), (301.5, 288.5, 'id=55'), (246.0, 295.5, 'id=56'), (307.92307, 305.01923, 'id=57'), (244.0, 306.0, 'id=58'), (25.01087, 314.92392, 'id=59'), (500.83334, 314.10416, 'id=60'), (242.0, 317.0, 'id=61'), (3.1666667, 315.83334, 'id=62'), (51.5, 318.5, 'id=63'), (218.28915, 323.06024, 'id=64'), (252.63414, 322.36584, 'id=65'), (315.77777, 326.77777, 'id=66'), (341.5, 332.5, 'id=67'), (240.0, 328.0, 'id=68'), (316.23785, 373.0073, 'id=69'), (246.64119, 372.9535, 'id=70'), (247.33333, 335.46667, 'id=71'), (321.55554, 335.33334, 'id=72'), (207.86792, 336.75473, 'id=73'), (238.0, 338.5, 'id=74'), (212.54167, 345.3447, 'id=75'), (54.18182, 338.68182, 'id=76'), (244.5, 338.5, 'id=77'), (40.875, 339.0, 'id=78'), (4.5, 339.0, 'id=79'), (9.8, 339.93332, 'id=80'), (16.5, 339.0, 'id=81'), (20.2, 339.4, 'id=82'), (31.0, 339.3, 'id=83'), (46.5, 339.0, 'id=84'), (218.0, 339.0, 'id=85'), (294.5, 339.5, 'id=86'), (360.7647, 344.05884, 'id=87'), (268.94766, 371.49036, 'id=88'), (15.342857, 350.1143, 'id=89'), (311.5, 344.5, 'id=90'), (32.0, 345.5, 'id=91'), (46.408604, 349.01074, 'id=92'), (236.0, 348.5, 'id=93'), (243.43478, 351.80435, 'id=94'), (353.5, 345.0, 'id=95'), (4.0, 346.5, 'id=96'), (210.0332, 362.87085, 'id=97'), (329.4, 354.2, 'id=98'), (27.666666, 354.83334, 'id=99'), (347.0, 354.7143, 'id=100'), (48.0, 355.0, 'id=101'), (234.0, 358.5, 'id=102'), (396.01352, 358.92792, 'id=103'), (5.8333335, 357.16666, 'id=104'), (420.0, 356.5, 'id=105'), (429.2857, 356.85715, 'id=106'), (257.0, 357.0, 'id=107'), (318.0, 357.5, 'id=108'), (331.03125, 361.21875, 'id=109'), (465.0, 357.0, 'id=110'), (472.0, 357.0, 'id=111'), (490.5, 357.0, 'id=112'), (49.52941, 360.64706, 'id=113'), (350.34784, 361.43478, 'id=114'), (421.5, 360.5, 'id=115'), (358.0, 359.0, 'id=116'), (240.78787, 364.69696, 'id=117'), (296.0, 361.5, 'id=118'), (478.0, 360.75, 'id=119'), (282.5263, 362.5263, 'id=120'), (317.07144, 362.57144, 'id=121'), (433.83334, 362.0, 'id=122'), (452.0, 361.5, 'id=123'), (361.6, 362.8, 'id=124'), (294.0, 365.0, 'id=125'), (301.5, 365.0, 'id=126'), (13.208333, 370.27777, 'id=127'), (232.0, 369.5, 'id=128'), (2.5, 367.0, 'id=129'), (36.794117, 370.20587, 'id=130'), (191.58333, 374.08334, 'id=131'), (45.5, 375.0, 'id=132'), (210.0, 375.0, 'id=133'), (229.24138, 383.48276, 'id=134'), (214.28572, 385.0, 'id=135'), (208.0, 385.0, 'id=136'), (199.80724, 396.24097, 'id=137'), (52.0, 390.0, 'id=138'), (34.0, 391.25, 'id=139'), (40.625, 393.25, 'id=140'), (251.85715, 394.2857, 'id=141'), (257.5, 394.0, 'id=142'), (363.83334, 394.83334, 'id=143'), (246.85715, 394.2857, 'id=144'), (280.0, 393.0, 'id=145'), (326.42856, 393.57144, 'id=146'), (295.2, 394.8, 'id=147'), (307.66666, 396.0, 'id=148'), (321.5, 395.0, 'id=149'), (347.0, 395.5, 'id=150'), (31.515625, 399.57812, 'id=151'), (226.0, 399.5, 'id=152'), (241.2, 397.0, 'id=153'), (378.0, 396.0, 'id=154'), (9.166667, 398.0, 'id=155'), (43.133335, 398.73334, 'id=156'), (325.66666, 398.66666, 'id=157'), (65.28814, 405.0, 'id=158'), (289.8, 401.2, 'id=159'), (389.1, 400.45, 'id=160'), (377.0, 401.625, 'id=161'), (14.0, 402.0, 'id=162'), (22.5, 401.0, 'id=163'), (280.5, 401.0, 'id=164'), (259.2, 404.2, 'id=165'), (315.66666, 404.66666, 'id=166'), (372.75, 404.75, 'id=167'), (183.25287, 412.7701, 'id=168'), (198.28, 407.86, 'id=169'), (209.375, 406.625, 'id=170'), (211.85754, 471.1382, 'id=171'), (248.05556, 405.44446, 'id=172'), (264.5, 405.0, 'id=173'), (286.87195, 410.59756, 'id=174'), (6.0, 407.5, 'id=175'), (15.125, 407.79166, 'id=176'), (34.0, 407.0, 'id=177'), (364.3143, 409.65714, 'id=178'), (254.33333, 408.66666, 'id=179'), (241.6, 411.66666, 'id=180'), (334.36365, 411.54544, 'id=181'), (370.0, 410.0, 'id=182'), (208.0, 411.0, 'id=183'), (320.125, 411.875, 'id=184'), (324.0, 411.5, 'id=185'), (380.0, 411.0, 'id=186'), (205.0, 412.5, 'id=187'), (257.0625, 412.75, 'id=188'), (316.0, 412.5, 'id=189'), (0.0, 414.5, 'id=190'), (4.0, 413.0, 'id=191'), (8.25, 413.875, 'id=192'), (13.8, 413.4, 'id=193'), (330.4, 414.2, 'id=194'), (373.0, 414.2, 'id=195'), (382.0, 413.0, 'id=196'), (398.0, 413.0, 'id=197'), (201.1, 419.075, 'id=198'), (266.0, 414.5, 'id=199'), (38.0, 415.0, 'id=200'), (53.5, 415.0, 'id=201'), (247.18182, 417.63635, 'id=202'), (42.0, 416.0, 'id=203'), (230.5, 417.0, 'id=204'), (382.0, 416.75, 'id=205'), (501.57144, 416.7143, 'id=206'), (34.0, 417.0, 'id=207'), (46.0, 417.0, 'id=208'), (62.658226, 421.41772, 'id=209'), (254.33333, 417.33334, 'id=210'), (342.5, 417.0, 'id=211'), (481.72726, 418.0909, 'id=212'), (508.5, 417.0, 'id=213'), (26.68, 421.12, 'id=214'), (44.0, 419.8, 'id=215'), (15.714286, 420.14285, 'id=216'), (48.5, 419.0, 'id=217'), (276.4, 420.2, 'id=218'), (374.0, 419.0, 'id=219'), (418.0, 419.0, 'id=220'), (234.0, 420.5, 'id=221'), (493.66666, 420.66666, 'id=222'), (266.0, 421.0, 'id=223'), (312.0, 422.0, 'id=224'), (320.0, 421.0, 'id=225'), (502.0, 421.0, 'id=226'), (244.0, 422.5, 'id=227'), (302.47058, 423.7353, 'id=228'), (341.66666, 422.66666, 'id=229'), (46.0, 423.0, 'id=230'), (282.0, 424.0, 'id=231'), (294.0, 423.5, 'id=232'), (330.7143, 423.14285, 'id=233'), (472.0, 423.0, 'id=234'), (15.666667, 424.66666, 'id=235'), (233.0, 425.92, 'id=236'), (287.5, 424.5, 'id=237'), (323.75, 425.0, 'id=238'), (9.0, 425.0, 'id=239'), (204.6, 425.2, 'id=240'), (28.0, 427.0, 'id=241'), (55.5, 427.0, 'id=242'), (320.33334, 427.33334, 'id=243'), (392.0, 427.5, 'id=244'), (362.0, 431.875, 'id=245'), (14.0, 429.0, 'id=246'), (58.0, 429.0, 'id=247'), (63.5, 429.0, 'id=248'), (295.77084, 433.89584, 'id=249'), (314.33334, 430.91666, 'id=250'), (320.0, 431.5, 'id=251'), (329.8, 431.8, 'id=252'), (348.0, 431.0, 'id=253'), (430.0, 431.0, 'id=254'), (452.0, 431.0, 'id=255'), (464.0, 431.0, 'id=256'), (268.7143, 432.85715, 'id=257'), (306.66666, 433.44446, 'id=258'), (35.363636, 434.36365, 'id=259'), (56.5, 433.875, 'id=260'), (63.644444, 436.84445, 'id=261'), (279.5, 433.5, 'id=262'), (324.0, 433.0, 'id=263'), (352.73077, 434.96155, 'id=264'), (390.64706, 436.29413, 'id=265'), (185.0, 434.66666, 'id=266'), (339.125, 436.20834, 'id=267'), (382.0, 435.7143, 'id=268'), (404.0, 435.8, 'id=269'), (26.0, 435.0, 'id=270'), (40.0, 435.0, 'id=271'), (253.5, 435.33334, 'id=272'), (313.625, 437.5, 'id=273'), (28.5, 436.5, 'id=274'), (45.75, 437.58334, 'id=275'), (318.0, 436.5, 'id=276'), (386.0, 438.0, 'id=277'), (6.0, 437.0, 'id=278'), (54.583332, 438.25, 'id=279'), (204.0, 437.0, 'id=280'), (307.66666, 438.83334, 'id=281'), (410.0, 437.0, 'id=282'), (445.66666, 437.33334, 'id=283'), (460.2857, 438.2857, 'id=284'), (472.5, 437.0, 'id=285'), (481.66666, 437.33334, 'id=286'), (234.0, 438.0, 'id=287'), (322.58334, 439.08334, 'id=288'), (327.0, 444.15216, 'id=289'), (357.2857, 439.5, 'id=290'), (366.77777, 440.44446, 'id=291'), (0.5, 439.0, 'id=292'), (31.4, 439.8, 'id=293'), (335.66666, 441.16666, 'id=294'), (343.8, 442.32, 'id=295'), (397.66666, 439.33334, 'id=296'), (281.77777, 441.1111, 'id=297'), (408.0, 440.0, 'id=298'), (193.25, 441.25, 'id=299'), (299.6, 442.2, 'id=300'), (402.5, 441.0, 'id=301'), (486.0, 441.0, 'id=302'), (502.0, 441.0, 'id=303'), (316.93332, 442.6, 'id=304'), (294.0, 443.5, 'id=305'), (351.5, 443.0, 'id=306'), (247.66667, 444.66666, 'id=307'), (310.6, 445.8, 'id=308'), (65.07692, 446.6154, 'id=309'), (267.5, 445.0, 'id=310'), (306.0, 445.0, 'id=311'), (363.375, 446.375, 'id=312'), (369.41666, 447.16666, 'id=313'), (424.5, 445.0, 'id=314'), (244.0, 446.5, 'id=315'), (254.0, 446.0, 'id=316'), (31.5, 447.0, 'id=317'), (52.333332, 447.33334, 'id=318'), (304.0, 447.0, 'id=319'), (394.0, 447.5, 'id=320'), (260.25, 449.5, 'id=321'), (343.66666, 448.66666, 'id=322'), (390.0, 448.5, 'id=323'), (14.5, 449.0, 'id=324'), (60.68421, 453.42105, 'id=325'), (300.0, 449.0, 'id=326'), (316.33334, 451.79166, 'id=327'), (340.45, 453.7, 'id=328'), (348.75, 450.0, 'id=329'), (356.0, 449.0, 'id=330'), (456.75, 450.125, 'id=331'), (20.0, 450.5, 'id=332'), (394.32248, 477.52304, 'id=333'), (64.0, 451.0, 'id=334'), (190.0, 451.25, 'id=335'), (208.0, 451.0, 'id=336'), (224.66667, 452.08334, 'id=337'), (248.0, 451.5, 'id=338'), (418.0, 451.5, 'id=339'), (440.5, 451.0, 'id=340'), (16.3, 453.0, 'id=341'), (31.125, 453.125, 'id=342'), (39.57143, 452.7143, 'id=343'), (46.42857, 452.85715, 'id=344'), (53.333332, 453.41666, 'id=345'), (265.91306, 454.34784, 'id=346'), (286.41666, 452.91666, 'id=347'), (346.0, 452.5, 'id=348'), (466.16666, 452.83334, 'id=349'), (26.0, 453.25, 'id=350'), (65.5, 453.5, 'id=351'), (116.75, 454.25, 'id=352'), (172.66667, 453.66666, 'id=353'), (178.5, 453.0, 'id=354'), (244.4, 453.8, 'id=355'), (272.63635, 454.54544, 'id=356'), (330.72726, 454.0, 'id=357'), (402.0, 453.5, 'id=358'), (35.666668, 454.66666, 'id=359'), (371.83334, 454.66666, 'id=360'), (497.36365, 454.9091, 'id=361'), (230.0, 455.0, 'id=362'), (252.25, 455.75, 'id=363'), (302.5, 455.0, 'id=364'), (405.0, 456.42856, 'id=365'), (430.33334, 455.33334, 'id=366'), (462.0, 455.0, 'id=367'), (504.0, 455.0, 'id=368'), (240.72728, 458.0909, 'id=369'), (291.375, 456.75, 'id=370'), (39.2, 457.8, 'id=371'), (54.0, 457.0, 'id=372'), (180.82353, 457.88235, 'id=373'), (246.4, 458.2, 'id=374'), (274.7143, 458.2857, 'id=375'), (284.0, 457.0, 'id=376'), (345.0, 458.0, 'id=377'), (352.7143, 458.2857, 'id=378'), (362.0, 457.0, 'id=379'), (450.25, 457.75, 'id=380'), (502.0, 457.0, 'id=381'), (22.8, 458.6, 'id=382'), (117.666664, 458.66666, 'id=383'), (172.5, 458.5, 'id=384'), (188.5, 458.5, 'id=385'), (359.0, 458.0, 'id=386'), (435.2, 458.6, 'id=387'), (442.0, 458.0, 'id=388'), (506.5, 458.5, 'id=389'), (2.0, 459.0, 'id=390'), (6.0, 459.0, 'id=391'), (229.0, 459.0, 'id=392'), (260.0, 459.0, 'id=393'), (288.0, 459.0, 'id=394'), (304.0, 459.0, 'id=395'), (336.0, 459.0, 'id=396'), (423.0, 459.0, 'id=397'), (358.1, 465.525, 'id=398'), (65.5, 463.0, 'id=399'), (115.95918, 469.85715, 'id=400'), (170.5, 463.0, 'id=401'), (178.63333, 466.76666, 'id=402'), (188.0, 463.0, 'id=403'), (192.5, 464.0, 'id=404'), (223.66667, 463.33334, 'id=405'), (257.0, 464.16666, 'id=406'), (265.6, 463.8, 'id=407'), (272.5, 463.0, 'id=408'), (298.0, 463.0, 'id=409'), (314.0, 463.0, 'id=410'), (320.0, 463.5, 'id=411'), (325.5, 463.0, 'id=412'), (338.16666, 463.33334, 'id=413'), (362.0, 463.0, 'id=414'), (398.5, 464.0, 'id=415'), (404.0, 463.0, 'id=416'), (424.54544, 464.0, 'id=417'), (500.33334, 463.33334, 'id=418'), (329.5, 465.0, 'id=419'), (280.5, 465.0, 'id=420'), (364.5, 465.0, 'id=421'), (412.0, 465.0, 'id=422'), (41.666668, 466.66666, 'id=423'), (64.666664, 467.77777, 'id=424'), (170.5, 467.9375, 'id=425'), (38.0, 467.0, 'id=426'), (232.0, 467.5, 'id=427'), (236.0, 467.5, 'id=428'), (248.0, 467.0, 'id=429'), (269.0, 467.5, 'id=430'), (295.2, 468.2, 'id=431'), (306.5, 467.0, 'id=432'), (341.0, 468.6, 'id=433'), (490.0, 467.0, 'id=434'), (56.0, 468.5, 'id=435'), (376.33334, 468.66666, 'id=436'), (18.75, 472.45, 'id=437'), (24.5, 469.0, 'id=438'), (46.22222, 470.33334, 'id=439'), (196.5, 469.0, 'id=440'), (243.92308, 474.57693, 'id=441'), (256.29413, 470.58823, 'id=442'), (262.0, 469.0, 'id=443'), (336.0, 469.0, 'id=444'), (33.0, 470.83334, 'id=445'), (228.66667, 470.83334, 'id=446'), (498.0, 470.0, 'id=447'), (40.0, 471.0, 'id=448'), (287.5, 472.0, 'id=449'), (349.5, 471.0, 'id=450'), (501.7143, 472.85715, 'id=451'), (507.75, 472.25, 'id=452'), (170.0, 472.75, 'id=453'), (284.2857, 473.0, 'id=454'), (495.66666, 472.66666, 'id=455'), (8.207547, 476.56604, 'id=456'), (50.0, 474.16666, 'id=457'), (60.0, 473.0, 'id=458'), (266.0, 473.5, 'id=459'), (273.84616, 474.30768, 'id=460'), (492.33334, 473.33334, 'id=461'), (65.333336, 475.44446, 'id=462'), (180.0, 474.0, 'id=463'), (308.0, 474.0, 'id=464'), (333.75, 475.0, 'id=465'), (194.16667, 475.33334, 'id=466'), (255.16667, 476.0, 'id=467'), (298.0, 475.0, 'id=468'), (317.1111, 477.22223, 'id=469'), (329.5, 475.0, 'id=470'), (398.0, 475.0, 'id=471'), (402.0, 475.5, 'id=472'), (434.0, 475.0, 'id=473'), (62.0, 477.25, 'id=474'), (164.5, 477.0, 'id=475'), (218.75, 478.66666, 'id=476'), (250.0, 477.0, 'id=477'), (440.5, 477.0, 'id=478'), (347.83334, 479.16666, 'id=479'), (431.75, 479.0, 'id=480'), (493.2, 478.6, 'id=481'), (21.0, 480.0, 'id=482'), (42.0, 479.0, 'id=483'), (193.17647, 480.2353, 'id=484'), (224.0, 479.0, 'id=485'), (354.33334, 479.33334, 'id=486'), (447.5, 479.0, 'id=487'), (64.63636, 482.45456, 'id=488'), (164.0, 480.5, 'id=489'), (188.0, 480.5, 'id=490'), (234.5, 481.7, 'id=491'), (247.4, 481.9, 'id=492'), (256.42105, 482.10526, 'id=493'), (334.96295, 481.2963, 'id=494'), (376.0, 481.0, 'id=495'), (111.0, 481.0, 'id=496'), (116.0, 481.0, 'id=497'), (263.0, 481.0, 'id=498'), (284.0, 481.0, 'id=499'), (310.0, 481.5, 'id=500'), (324.0, 481.0, 'id=501'), (358.0, 481.0, 'id=502'), (118.75, 483.0, 'id=503'), (184.71428, 482.57144, 'id=504'), (321.0, 483.0, 'id=505'), (434.0, 482.0, 'id=506'), (14.0, 483.0, 'id=507'), (170.33333, 483.33334, 'id=508'), (288.0, 483.0, 'id=509'), (303.5, 483.0, 'id=510'), (345.6, 484.2, 'id=511'), (386.0, 484.0, 'id=512'), (453.5, 483.0, 'id=513'), (492.0, 483.0, 'id=514'), (37.25, 486.83334, 'id=515'), (52.0, 485.0, 'id=516'), (312.33334, 484.66666, 'id=517'), (114.0, 485.0, 'id=518'), (278.0, 485.0, 'id=519'), (332.0, 485.0, 'id=520'), (354.0, 485.0, 'id=521'), (358.0, 485.0, 'id=522'), (376.0, 486.0, 'id=523'), (484.16666, 485.33334, 'id=524'), (2.0, 486.7143, 'id=525'), (22.0, 489.5, 'id=526'), (350.0, 486.5, 'id=527'), (64.0, 487.0, 'id=528'), (111.5, 487.33334, 'id=529'), (118.46154, 488.6154, 'id=530'), (176.0, 488.0, 'id=531'), (260.0, 487.0, 'id=532'), (301.0, 487.0, 'id=533'), (372.0, 487.0, 'id=534'), (439.0, 487.0, 'id=535'), (286.63635, 489.27274, 'id=536'), (310.33334, 488.66666, 'id=537'), (32.0, 489.0, 'id=538'), (262.1111, 491.8889, 'id=539'), (315.5, 490.0, 'id=540'), (374.0, 489.0, 'id=541'), (17.0, 490.66666, 'id=542'), (303.6842, 491.78946, 'id=543'), (323.0, 490.875, 'id=544'), (349.5, 491.75, 'id=545'), (353.75, 491.0, 'id=546'), (167.0, 492.0, 'id=547'), (232.5, 494.125, 'id=548'), (293.5, 491.0, 'id=549'), (312.8, 492.2, 'id=550'), (384.92307, 493.0, 'id=551'), (422.63635, 492.18182, 'id=552'), (184.0, 492.0, 'id=553'), (221.8, 493.2, 'id=554'), (30.0, 493.0, 'id=555'), (40.0, 493.5, 'id=556'), (112.53333, 495.6, 'id=557'), (239.66667, 493.33334, 'id=558'), (256.25, 493.91666, 'id=559'), (295.5, 493.0, 'id=560'), (345.3125, 495.0625, 'id=561'), (358.42105, 494.10526, 'id=562'), (436.0, 493.5, 'id=563'), (462.0, 493.0, 'id=564'), (325.06668, 496.06668, 'id=565'), (337.91666, 495.58334, 'id=566'), (485.77777, 495.8889, 'id=567'), (3.5, 495.0, 'id=568'), (44.0, 495.0, 'id=569'), (50.0, 496.0, 'id=570'), (64.0, 495.0, 'id=571'), (106.0, 495.0, 'id=572'), (182.0, 495.0, 'id=573'), (331.2, 495.4, 'id=574'), (412.0, 495.0, 'id=575'), (456.5, 495.0, 'id=576'), (460.0, 495.0, 'id=577'), (496.33334, 495.33334, 'id=578'), (504.0, 495.0, 'id=579'), (40.333332, 496.66666, 'id=580'), (186.73334, 498.73334, 'id=581'), (248.0, 497.0, 'id=582'), (266.0, 496.0, 'id=583'), (22.0, 497.0, 'id=584'), (217.38889, 498.83334, 'id=585'), (227.0, 497.0, 'id=586'), (352.0, 497.0, 'id=587'), (367.0, 498.16666, 'id=588'), (418.0, 497.5, 'id=589'), (422.0, 497.0, 'id=590'), (435.625, 498.375, 'id=591'), (441.2857, 497.14285, 'id=592'), (502.0, 497.0, 'id=593'), (31.375, 499.0, 'id=594'), (63.0, 498.5, 'id=595'), (105.2, 499.7, 'id=596'), (116.0, 498.0, 'id=597'), (384.0, 498.0, 'id=598'), (0.0, 499.0, 'id=599'), (7.6666665, 499.33334, 'id=600'), (44.0, 499.0, 'id=601'), (273.0, 499.0, 'id=602'), (280.875, 500.125, 'id=603'), (494.0, 499.0, 'id=604'), (498.0, 499.0, 'id=605'), (165.66667, 500.66666, 'id=606'), (441.42856, 501.0, 'id=607'), (458.0, 500.5, 'id=608'), (59.384617, 503.80768, 'id=609'), (232.0, 501.0, 'id=610'), (244.0, 501.0, 'id=611'), (262.0, 501.0, 'id=612'), (299.7, 502.2, 'id=613'), (308.0, 501.0, 'id=614'), (330.0, 501.0, 'id=615'), (426.0, 501.0, 'id=616'), (470.0, 501.0, 'id=617'), (349.66666, 503.16666, 'id=618'), (38.0, 504.0, 'id=619'), (117.5, 503.0, 'id=620'), (222.0, 503.0, 'id=621'), (236.5, 503.0, 'id=622'), (242.92308, 504.46155, 'id=623'), (260.0, 503.0, 'id=624'), (311.2, 503.2, 'id=625'), (320.33334, 504.66666, 'id=626'), (343.5, 503.0, 'id=627'), (420.0, 503.0, 'id=628'), (431.6, 503.2, 'id=629'), (450.0, 503.0, 'id=630'), (175.375, 505.5, 'id=631'), (254.33333, 504.66666, 'id=632'), (395.75, 505.5, 'id=633'), (16.0, 505.0, 'id=634'), (26.0, 505.0, 'id=635'), (189.0, 505.0, 'id=636'), (234.0, 505.0, 'id=637'), (305.07693, 506.46155, 'id=638'), (316.0, 505.0, 'id=639'), (340.33334, 506.0, 'id=640'), (371.66666, 505.33334, 'id=641'), (468.0, 505.0, 'id=642'), (47.54286, 509.22858, 'id=643'), (274.0, 506.5, 'id=644'), (11.294118, 509.29413, 'id=645'), (17.95, 509.4, 'id=646'), (62.0, 507.0, 'id=647'), (117.0, 507.25, 'id=648'), (180.0, 507.5, 'id=649'), (236.0, 507.0, 'id=650'), (270.0, 507.0, 'id=651'), (282.0, 507.5, 'id=652'), (354.0, 507.0, 'id=653'), (362.0, 507.0, 'id=654'), (484.0, 508.0, 'id=655'), (503.5, 507.0, 'id=656'), (27.8, 509.6, 'id=657'), (59.57143, 509.7143, 'id=658'), (263.57144, 509.7143, 'id=659'), (332.0909, 509.63635, 'id=660'), (5.5, 510.0, 'id=661'), (237.5, 510.0, 'id=662'), (324.0, 510.0, 'id=663'), (356.5, 510.0, 'id=664'), (390.0, 510.0, 'id=665'), (397.0, 510.0, 'id=666'), (414.0, 510.0, 'id=667'), (479.7143, 510.0, 'id=668'), (368.0, 510.0, 'id=669'), (476.5, 510.0, 'id=670'), (484.0, 511.0, 'id=671')]}, {'type': 'ellipses', 'body': [(276.2707214355469, 133.7836456298828, 328.0933837890625, 179.8610076904297, 3.018669843673706), (245.4584197998047, 128.1914825439453, 16.651796340942383, 14.02171802520752, 0.690736711025238), (215.51852416992188, 120.03704071044922, 4.608210563659668, 2.1209144592285156, 1.512969732284546), (270.0, 121.0, 1.2649110555648804, 1.2649110555648804, 2.356194496154785), (223.38888549804688, 123.27777862548828, 2.8466944694519043, 1.9676482677459717, 1.357241153717041), (262.0, 121.5, 1.0, 0.0, 1.5707963705062866), (286.8193664550781, 130.94837951660156, 11.525166511535645, 7.204635143280029, 0.19030477106571198), (218.11111450195312, 129.5, 3.530224084854126, 1.6463161706924438, 2.209132194519043), (267.0, 127.0, 1.632993221282959, 0.0, 3.1415927410125732), (251.0, 139.0, 2.2563042640686035, 1.4770978689193726, 3.1415927410125732), (296.26666259765625, 140.46665954589844, 3.669114828109741, 2.2019782066345215, 2.0455899238586426), (272.0, 139.0, 0.0, 0.0, 2.356194496154785), (204.0, 151.03509521484375, 11.96079158782959, 3.9852099418640137, 2.3145058155059814), (265.6104736328125, 181.41685485839844, 36.509002685546875, 24.22110366821289, 2.4562292098999023), (270.0, 142.0, 1.632993221282959, 0.0, 1.5707963705062866), (274.5, 141.0, 1.0, 0.0, 3.1415927410125732), (310.5454406738281, 142.81817626953125, 3.917346239089966, 1.4970556497573853, 1.1298604011535645), (509.6363525390625, 143.90908813476562, 2.173064947128296, 1.5336781740188599, 2.8785593509674072), (296.8823547363281, 148.64706420898438, 3.017346143722534, 2.2204110622406006, 1.3552871942520142), (248.0, 147.0, 1.632993221282959, 0.0, 3.1415927410125732), (492.0, 157.0, 0.0, 0.0, 2.356194496154785), (304.3999938964844, 158.8000030517578, 1.5491933822631836, 0.8944271802902222, 1.249045729637146), (204.14285278320312, 161.0, 3.4621500968933105, 1.2830312252044678, 2.407599925994873), (288.20001220703125, 161.60000610351562, 2.0466690063476562, 0.7817580103874207, 1.660723090171814), (206.7142791748047, 171.14285278320312, 3.1949262619018555, 1.589698076248169, 1.5920600891113281), (500.0, 169.0, 0.0, 0.0, 2.356194496154785), (508.0, 171.0, 0.0, 0.0, 2.356194496154785), (207.0, 179.0, 2.6422383785247803, 1.0092452764511108, 1.0172219276428223), (510.28570556640625, 179.0, 1.5118578672409058, 1.3997083902359009, 1.5707963705062866), (252.0, 183.5, 1.0, 0.0, 1.5707963705062866), (504.25, 197.75, 1.7320507764816284, 0.7071067690849304, 1.249045729637146), (497.79998779296875, 204.60000610351562, 2.0466690063476562, 0.7817580103874207, 1.4808695316314697), (202.1219482421875, 215.56097412109375, 14.900699615478516, 0.9197909235954285, 0.8312706351280212), (502.0, 205.0, 0.0, 0.0, 2.356194496154785), (504.3333435058594, 211.0, 1.8856180906295776, 1.154700517654419, 3.1415927410125732), (511.0, 211.0, 0.0, 0.0, 2.356194496154785), (256.9333190917969, 216.8000030517578, 4.393863201141357, 1.1472523212432861, 1.26802396774292), (502.0, 217.0, 0.0, 0.0, 2.356194496154785), (509.9375, 224.1875, 4.256257057189941, 2.0562164783477783, 1.5921796560287476), (261.25, 225.875, 4.26151704788208, 2.0067317485809326, 1.251846432685852), (270.5, 223.0, 1.0, 0.0, 3.1415927410125732), (274.83673095703125, 226.65306091308594, 6.297449588775635, 3.5912249088287354, 0.4991496503353119), (502.0, 227.0, 0.0, 0.0, 2.356194496154785), (505.5, 229.0, 1.0, 0.0, 3.1415927410125732), (256.9090881347656, 240.5454559326172, 4.349363803863525, 0.7662689685821533, 1.2407492399215698), (280.0, 237.0, 0.0, 0.0, 2.356194496154785), (508.0, 237.0, 1.632993221282959, 0.0, 3.1415927410125732), (268.33489990234375, 291.6682434082031, 50.4422721862793, 4.219693660736084, 1.5738857984542847), (287.31097412109375, 293.6456298828125, 43.98822784423828, 15.397847175598145, 1.7467771768569946), (254.0, 255.0, 1.632993221282959, 0.0, 1.5707963705062866), (225.41493225097656, 294.470703125, 30.942045211791992, 11.906290054321289, 1.2717796564102173), (252.0, 265.0, 1.632993221282959, 0.0, 1.5707963705062866), (250.0, 275.0, 2.8284270763397217, 0.0, 1.5707963705062866), (254.68069458007812, 300.7894592285156, 23.173208236694336, 4.579484462738037, 1.4632377624511719), (248.0, 285.5, 2.2360680103302, 0.0, 1.5707963705062866), (301.5, 288.5, 1.0, 1.0, 2.356194496154785), (246.0, 295.5, 3.4156503677368164, 0.0, 1.5707963705062866), (307.9230651855469, 305.01922607421875, 10.396947860717773, 1.9497177600860596, 1.963199496269226), (244.0, 306.0, 4.0, 0.0, 1.5707963705062866), (25.0108699798584, 314.9239196777344, 11.416547775268555, 3.321161985397339, 0.05657835304737091), (500.8333435058594, 314.1041564941406, 10.488977432250977, 2.7221622467041016, 0.07842276245355606), (242.0, 317.0, 4.0, 0.0, 1.5707963705062866), (3.1666667461395264, 315.8333435058594, 2.4274654388427734, 1.597452998161316, 3.1083085536956787), (51.5, 318.5, 2.2360680103302, 1.0, 3.1415927410125732), (218.28915405273438, 323.06024169921875, 14.48153018951416, 2.037936210632324, 0.04470852017402649), (252.63414001464844, 322.3658447265625, 6.495011329650879, 2.0865750312805176, 0.04781278222799301), (315.77777099609375, 326.77777099609375, 7.65121603012085, 1.228119969367981, 1.606326699256897), (341.5, 332.5, 12.152097702026367, 0.9965852499008179, 1.5776140689849854), (240.0, 328.0, 4.0, 0.0, 1.5707963705062866), (316.23785400390625, 373.0072937011719, 72.41310119628906, 2.6567816734313965, 2.3347272872924805), (246.6411895751953, 372.9534912109375, 45.860260009765625, 4.149299144744873, 1.1657755374908447), (247.3333282470703, 335.4666748046875, 3.895936965942383, 1.2804112434387207, 1.7241970300674438), (321.5555419921875, 335.3333435058594, 3.02614164352417, 0.8479448556900024, 1.7501816749572754), (207.867919921875, 336.7547302246094, 7.077769756317139, 2.428297996520996, 3.062228202819824), (238.0, 338.5, 4.582575798034668, 0.0, 1.5707963705062866), (212.5416717529297, 345.3446960449219, 16.940698623657227, 6.012630939483643, 0.13033059239387512), (54.181819915771484, 338.68182373046875, 5.486020565032959, 1.5084699392318726, 3.1005523204803467), (244.5, 338.5, 2.2360680103302, 1.0, 1.5707963705062866), (40.875, 339.0, 3.2307119369506836, 1.0, 3.1415927410125732), (4.5, 339.0, 1.0, 0.0, 3.1415927410125732), (9.800000190734863, 339.9333190917969, 2.8676531314849854, 1.825410008430481, 2.986464262008667), (16.5, 339.0, 1.0, 0.0, 3.1415927410125732), (20.200000762939453, 339.3999938964844, 1.5491933822631836, 0.8944271802902222, 2.8198421001434326), (31.0, 339.29998779296875, 3.464101552963257, 0.9165151119232178, 0.0), (46.5, 339.0, 1.0, 0.0, 3.1415927410125732), (218.0, 339.0, 0.0, 0.0, 2.356194496154785), (294.5, 339.5, 3.0204479694366455, 0.9364264011383057, 0.122489333152771), (360.76470947265625, 344.058837890625, 3.5653164386749268, 1.6786692142486572, 0.2687702178955078), (268.9476623535156, 371.4903564453125, 30.55106544494629, 3.991187334060669, 1.56861412525177), (15.342857360839844, 350.1142883300781, 7.075631141662598, 4.737252712249756, 0.2264050394296646), (311.5, 344.5, 1.0, 1.0, 2.356194496154785), (32.0, 345.5, 1.0, 0.0, 1.5707963705062866), (46.40860366821289, 349.0107421875, 9.882936477661133, 4.109147548675537, 0.06696691364049911), (236.0, 348.5, 4.582575798034668, 0.0, 1.5707963705062866), (243.43478393554688, 351.8043518066406, 7.805454730987549, 2.3201303482055664, 1.6924065351486206), (353.5, 345.0, 1.0, 0.0, 3.1415927410125732), (4.0, 346.5, 1.0, 0.0, 1.5707963705062866), (210.033203125, 362.870849609375, 16.036827087402344, 11.903205871582031, 3.1290462017059326), (329.3999938964844, 354.20001220703125, 1.5491933822631836, 0.8944271802902222, 1.8925468921661377), (27.66666603088379, 354.8333435058594, 3.0057432651519775, 0.6402745246887207, 3.01129150390625), (347.0, 354.71429443359375, 1.5118578672409058, 1.3997083902359009, 3.1415927410125732), (48.0, 355.0, 0.0, 0.0, 2.356194496154785), (234.0, 358.5, 4.582575798034668, 0.0, 1.5707963705062866), (396.0135192871094, 358.92791748046875, 26.802236557006836, 3.429692029953003, 0.0005554801900871098), (5.833333492279053, 357.1666564941406, 1.5634719133377075, 1.154700517654419, 2.356194496154785), (420.0, 356.5, 1.0, 0.0, 1.5707963705062866), (429.28570556640625, 356.8571472167969, 3.3395602703094482, 0.6627020239830017, 3.0727992057800293), (257.0, 357.0, 0.0, 0.0, 2.356194496154785), (318.0, 357.5, 1.0, 0.0, 1.5707963705062866), (331.03125, 361.21875, 3.761357307434082, 3.2644875049591064, 2.275275707244873), (465.0, 357.0, 1.632993221282959, 0.0, 3.1415927410125732), (472.0, 357.0, 0.0, 0.0, 2.356194496154785), (490.5, 357.0, 2.2360680103302, 0.0, 3.1415927410125732), (49.52941131591797, 360.6470642089844, 3.883671283721924, 2.0588581562042236, 0.716254711151123), (350.34783935546875, 361.4347839355469, 4.9199748039245605, 2.420355796813965, 2.0395541191101074), (421.5, 360.5, 3.0, 1.5275251865386963, 1.1071487665176392), (358.0, 359.0, 0.0, 0.0, 2.356194496154785), (240.78787231445312, 364.69696044921875, 7.721569061279297, 1.7955548763275146, 1.2201000452041626), (296.0, 361.5, 1.7320507764816284, 1.4142135381698608, 1.5707963705062866), (478.0, 360.75, 1.4142135381698608, 0.8660253882408142, 3.1415927410125732), (282.52630615234375, 362.52630615234375, 9.875061988830566, 2.8752524852752686, 0.20441024005413055), (317.0714416503906, 362.5714416503906, 2.71681809425354, 1.7339707612991333, 1.1632500886917114), (433.8333435058594, 362.0, 1.9148541688919067, 0.9428090453147888, 0.9272952079772949), (452.0, 361.5, 1.0, 0.0, 1.5707963705062866), (361.6000061035156, 362.79998779296875, 2.0466690063476562, 0.7817580103874207, 0.08992674946784973), (294.0, 365.0, 0.0, 0.0, 2.356194496154785), (301.5, 365.0, 1.0, 0.0, 3.1415927410125732), (13.208333015441895, 370.27777099609375, 8.673247337341309, 4.257447242736816, 2.975245237350464), (232.0, 369.5, 4.582575798034668, 0.0, 1.5707963705062866), (2.5, 367.0, 1.0, 0.0, 3.1415927410125732), (36.79411697387695, 370.20587158203125, 13.951617240905762, 2.103579044342041, 0.03469857573509216), (191.5833282470703, 374.0833435058594, 4.460892200469971, 1.1739752292633057, 1.273051381111145), (45.5, 375.0, 1.6180340051651, 0.6180340051651001, 1.0172219276428223), (210.0, 375.0, 0.0, 0.0, 2.356194496154785), (229.2413787841797, 383.4827575683594, 11.773774147033691, 1.0200730562210083, 1.3749644756317139), (214.2857208251953, 385.0, 2.5555062294006348, 1.0690449476242065, 3.1415927410125732), (208.0, 385.0, 0.0, 0.0, 2.356194496154785), (199.80723571777344, 396.240966796875, 18.581249237060547, 4.572801113128662, 0.10806237906217575), (52.0, 390.0, 0.0, 0.0, 2.356194496154785), (34.0, 391.25, 1.4142135381698608, 0.8660253882408142, 3.1415927410125732), (40.625, 393.25, 2.0602023601531982, 1.2012770175933838, 2.8042221069335938), (251.85714721679688, 394.28570556640625, 3.3395602703094482, 0.6627020239830017, 1.6395896673202515), (257.5, 394.0, 2.8284270763397217, 1.0, 1.5707963705062866), (363.8333435058594, 394.8333435058594, 4.31289005279541, 1.0847845077514648, 2.026791572570801), (246.85714721679688, 394.28570556640625, 1.7910350561141968, 1.2356728315353394, 2.60974383354187), (280.0, 393.0, 0.0, 0.0, 2.356194496154785), (326.4285583496094, 393.5714416503906, 1.842853307723999, 1.411257266998291, 0.3101247549057007), (295.20001220703125, 394.79998779296875, 2.371682643890381, 0.6746264696121216, 2.9513394832611084), (307.6666564941406, 396.0, 1.838544249534607, 0.41870102286338806, 1.0793994665145874), (321.5, 395.0, 1.0, 0.0, 3.1415927410125732), (347.0, 395.5, 1.0, 0.0, 1.5707963705062866), (31.515625, 399.578125, 6.429301738739014, 3.3931822776794434, 0.0843939408659935), (226.0, 399.5, 4.582575798034668, 0.0, 1.5707963705062866), (241.1999969482422, 397.0, 1.9595917463302612, 1.5491933822631836, 3.1415927410125732), (378.0, 396.0, 0.0, 0.0, 2.356194496154785), (9.166666984558105, 398.0, 1.9148541688919067, 0.9428090453147888, 0.9272952079772949), (43.13333511352539, 398.73333740234375, 3.728624105453491, 1.319775104522705, 0.31590285897254944), (325.6666564941406, 398.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (65.28813934326172, 405.0, 7.849324703216553, 6.4693603515625, 1.442439317703247), (289.79998779296875, 401.20001220703125, 3.8199872970581055, 1.0429270267486572, 2.4758102893829346), (389.1000061035156, 400.45001220703125, 5.017458915710449, 1.6047134399414062, 0.02567523717880249), (377.0, 401.625, 3.016545295715332, 0.9153981804847717, 2.482353448867798), (14.0, 402.0, 1.632993221282959, 0.0, 1.5707963705062866), (22.5, 401.0, 1.0, 0.0, 3.1415927410125732), (280.5, 401.0, 2.2360680103302, 0.0, 3.1415927410125732), (259.20001220703125, 404.20001220703125, 2.0, 0.6928203105926514, 2.356194496154785), (315.6666564941406, 404.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (372.75, 404.75, 1.7320507764816284, 0.7071067690849304, 2.8198421001434326), (183.25286865234375, 412.7701110839844, 12.662545204162598, 4.396671772003174, 1.150771975517273), (198.27999877929688, 407.8599853515625, 7.715367794036865, 3.249784231185913, 3.138390302658081), (209.375, 406.625, 2.6609344482421875, 0.8913065195083618, 1.4193538427352905), (211.8575439453125, 471.1382141113281, 56.3292350769043, 6.48135232925415, 1.3676340579986572), (248.05555725097656, 405.4444580078125, 5.356261253356934, 0.9759311079978943, 0.03563787788152695), (264.5, 405.0, 1.0, 0.0, 3.1415927410125732), (286.8719482421875, 410.5975646972656, 21.919391632080078, 3.4390146732330322, 3.110524892807007), (6.0, 407.5, 1.0, 0.0, 1.5707963705062866), (15.125, 407.7916564941406, 7.512304782867432, 1.4127393960952759, 0.07800251245498657), (34.0, 407.0, 0.0, 0.0, 2.356194496154785), (364.3143005371094, 409.6571350097656, 3.8871781826019287, 2.8926451206207275, 0.48186013102531433), (254.3333282470703, 408.6666564941406, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (241.60000610351562, 411.6666564941406, 4.794924736022949, 1.5245505571365356, 0.18482209742069244), (334.3636474609375, 411.5454406738281, 2.583918809890747, 1.5853852033615112, 0.4095466434955597), (370.0, 410.0, 0.0, 0.0, 2.356194496154785), (208.0, 411.0, 0.0, 0.0, 2.356194496154785), (320.125, 411.875, 1.7320507764816284, 1.3693064451217651, 0.7853981852531433), (324.0, 411.5, 1.0, 0.0, 1.5707963705062866), (380.0, 411.0, 0.0, 0.0, 2.356194496154785), (205.0, 412.5, 1.632993221282959, 1.0, 3.1415927410125732), (257.0625, 412.75, 5.0270233154296875, 1.1015492677688599, 0.03900860995054245), (316.0, 412.5, 1.0, 0.0, 1.5707963705062866), (0.0, 414.5, 2.2360680103302, 0.0, 1.5707963705062866), (4.0, 413.0, 0.0, 0.0, 2.356194496154785), (8.25, 413.875, 2.2539470195770264, 1.0522466897964478, 0.6170607805252075), (13.800000190734863, 413.3999938964844, 1.5491933822631836, 0.8944271802902222, 0.32175055146217346), (330.3999938964844, 414.20001220703125, 1.5491933822631836, 0.8944271802902222, 1.8925468921661377), (373.0, 414.20001220703125, 1.9595917463302612, 1.5491933822631836, 1.5707963705062866), (382.0, 413.0, 0.0, 0.0, 2.356194496154785), (398.0, 413.0, 0.0, 0.0, 2.356194496154785), (201.10000610351562, 419.07501220703125, 9.548724174499512, 3.80254864692688, 0.06443893164396286), (266.0, 414.5, 1.0, 0.0, 1.5707963705062866), (38.0, 415.0, 0.0, 0.0, 2.356194496154785), (53.5, 415.0, 2.2360680103302, 0.0, 3.1415927410125732), (247.18182373046875, 417.6363525390625, 3.8367364406585693, 2.31204891204834, 2.435870409011841), (42.0, 416.0, 0.0, 0.0, 2.356194496154785), (230.5, 417.0, 1.632993221282959, 1.0, 1.5707963705062866), (382.0, 416.75, 1.4142135381698608, 0.8660253882408142, 3.1415927410125732), (501.5714416503906, 416.71429443359375, 2.836226224899292, 0.7803092002868652, 2.97377347946167), (34.0, 417.0, 0.0, 0.0, 2.356194496154785), (46.0, 417.0, 0.0, 0.0, 2.356194496154785), (62.658226013183594, 421.417724609375, 7.935923099517822, 5.156559467315674, 2.957583427429199), (254.3333282470703, 417.3333435058594, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (342.5, 417.0, 1.0, 0.0, 3.1415927410125732), (481.7272644042969, 418.0909118652344, 2.5983684062957764, 1.282644271850586, 2.716827630996704), (508.5, 417.0, 1.0, 0.0, 3.1415927410125732), (26.68000030517578, 421.1199951171875, 4.543673992156982, 2.3680851459503174, 2.7220959663391113), (44.0, 419.79998779296875, 1.9595917463302612, 1.5491933822631836, 1.5707963705062866), (15.714285850524902, 420.1428527832031, 1.7910350561141968, 1.2356728315353394, 2.1026451587677), (48.5, 419.0, 1.0, 0.0, 3.1415927410125732), (276.3999938964844, 420.20001220703125, 1.5491933822631836, 0.8944271802902222, 1.8925468921661377), (374.0, 419.0, 0.0, 0.0, 2.356194496154785), (418.0, 419.0, 0.0, 0.0, 2.356194496154785), (234.0, 420.5, 1.0, 0.0, 1.5707963705062866), (493.6666564941406, 420.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (266.0, 421.0, 0.0, 0.0, 2.356194496154785), (312.0, 422.0, 1.632993221282959, 0.0, 1.5707963705062866), (320.0, 421.0, 0.0, 0.0, 2.356194496154785), (502.0, 421.0, 0.0, 0.0, 2.356194496154785), (244.0, 422.5, 1.0, 0.0, 1.5707963705062866), (302.4705810546875, 423.73529052734375, 5.682929515838623, 2.18483829498291, 0.09358145296573639), (341.6666564941406, 422.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (46.0, 423.0, 0.0, 0.0, 2.356194496154785), (282.0, 424.0, 1.632993221282959, 0.0, 1.5707963705062866), (294.0, 423.5, 1.0, 0.0, 1.5707963705062866), (330.71429443359375, 423.1428527832031, 3.3395602703094482, 0.6627020239830017, 3.0727992057800293), (472.0, 423.0, 0.0, 0.0, 2.356194496154785), (15.666666984558105, 424.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (233.0, 425.9200134277344, 7.000832557678223, 1.3574758768081665, 2.935690402984619), (287.5, 424.5, 1.0, 1.0, 2.356194496154785), (323.75, 425.0, 1.4142135381698608, 0.8660253882408142, 1.5707963705062866), (9.0, 425.0, 1.632993221282959, 0.0, 3.1415927410125732), (204.60000610351562, 425.20001220703125, 2.0466690063476562, 0.7817580103874207, 3.051666021347046), (28.0, 427.0, 0.0, 0.0, 2.356194496154785), (55.5, 427.0, 1.0, 0.0, 3.1415927410125732), (320.3333435058594, 427.3333435058594, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (392.0, 427.5, 1.0, 0.0, 1.5707963705062866), (362.0, 431.875, 4.239621639251709, 2.4075798988342285, 1.7823402881622314), (14.0, 429.0, 0.0, 0.0, 2.356194496154785), (58.0, 429.0, 0.0, 0.0, 2.356194496154785), (63.5, 429.0, 1.0, 0.0, 3.1415927410125732), (295.7708435058594, 433.8958435058594, 11.167255401611328, 5.215898513793945, 2.835841655731201), (314.3333435058594, 430.9166564941406, 2.1820225715637207, 1.7606500387191772, 2.5355799198150635), (320.0, 431.5, 1.0, 0.0, 1.5707963705062866), (329.79998779296875, 431.79998779296875, 2.0, 0.6928203105926514, 2.356194496154785), (348.0, 431.0, 0.0, 0.0, 2.356194496154785), (430.0, 431.0, 0.0, 0.0, 2.356194496154785), (452.0, 431.0, 0.0, 0.0, 2.356194496154785), (464.0, 431.0, 0.0, 0.0, 2.356194496154785), (268.71429443359375, 432.8571472167969, 2.138089895248413, 1.1428571939468384, 2.8198421001434326), (306.6666564941406, 433.4444580078125, 2.138831377029419, 1.3215097188949585, 1.4652496576309204), (35.3636360168457, 434.3636474609375, 2.906628131866455, 1.4594606161117554, 0.9015426635742188), (56.5, 433.875, 1.8760998249053955, 1.3848283290863037, 2.5355799198150635), (63.64444351196289, 436.8444519042969, 4.856428623199463, 3.8974876403808594, 1.4479074478149414), (279.5, 433.5, 1.0, 1.0, 2.356194496154785), (324.0, 433.0, 0.0, 0.0, 2.356194496154785), (352.73077392578125, 434.9615478515625, 5.746734619140625, 1.815093994140625, 0.323661208152771), (390.6470642089844, 436.29412841796875, 3.5611495971679688, 1.910600185394287, 1.3197776079177856), (185.0, 434.6666564941406, 1.838544249534607, 0.41870102286338806, 2.650195837020874), (339.125, 436.2083435058594, 4.302449703216553, 2.5663490295410156, 2.431013822555542), (382.0, 435.71429443359375, 2.3983514308929443, 0.8823395371437073, 2.1537485122680664), (404.0, 435.79998779296875, 1.9595917463302612, 1.5491933822631836, 1.5707963705062866), (26.0, 435.0, 0.0, 0.0, 2.356194496154785), (40.0, 435.0, 0.0, 0.0, 2.356194496154785), (253.5, 435.3333435058594, 1.9148541688919067, 0.9428090453147888, 3.1415927410125732), (313.625, 437.5, 3.316624879837036, 1.5612494945526123, 1.919567346572876), (28.5, 436.5, 1.0, 1.0, 2.356194496154785), (45.75, 437.5833435058594, 3.9764370918273926, 1.3820888996124268, 2.7135417461395264), (318.0, 436.5, 1.0, 0.0, 1.5707963705062866), (386.0, 438.0, 2.0, 1.4142135381698608, 1.5707963705062866), (6.0, 437.0, 0.0, 0.0, 2.356194496154785), (54.58333206176758, 438.25, 2.209036111831665, 1.6859365701675415, 0.5596716403961182), (204.0, 437.0, 0.0, 0.0, 2.356194496154785), (307.6666564941406, 438.8333435058594, 2.181668996810913, 0.8275048732757568, 1.3450697660446167), (410.0, 437.0, 0.0, 0.0, 2.356194496154785), (445.6666564941406, 437.3333435058594, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (460.28570556640625, 438.28570556640625, 2.227902889251709, 1.11379075050354, 0.454876571893692), (472.5, 437.0, 1.0, 0.0, 3.1415927410125732), (481.6666564941406, 437.3333435058594, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (234.0, 438.0, 0.0, 0.0, 2.356194496154785), (322.5833435058594, 439.0833435058594, 2.522012710571289, 1.5001877546310425, 3.025660276412964), (327.0, 444.15216064453125, 6.355008602142334, 3.0856504440307617, 1.1840088367462158), (357.28570556640625, 439.5, 4.642357349395752, 2.824735641479492, 0.18299315869808197), (366.77777099609375, 440.4444580078125, 2.764883279800415, 1.070300817489624, 2.0390031337738037), (0.5, 439.0, 1.0, 0.0, 3.1415927410125732), (31.399999618530273, 439.79998779296875, 1.5491933822631836, 0.8944271802902222, 1.249045729637146), (335.6666564941406, 441.1666564941406, 2.8306636810302734, 2.0244638919830322, 1.2689216136932373), (343.79998779296875, 442.32000732421875, 4.2982001304626465, 3.393505096435547, 0.4271944463253021), (397.6666564941406, 439.3333435058594, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (281.77777099609375, 441.1111145019531, 4.31389045715332, 1.8345733880996704, 0.09615650773048401), (408.0, 440.0, 0.0, 0.0, 2.356194496154785), (193.25, 441.25, 1.7320507764816284, 0.7071067690849304, 2.8198421001434326), (299.6000061035156, 442.20001220703125, 1.5491933822631836, 0.8944271802902222, 1.249045729637146), (402.5, 441.0, 1.0, 0.0, 3.1415927410125732), (486.0, 441.0, 0.0, 0.0, 2.356194496154785), (502.0, 441.0, 0.0, 0.0, 2.356194496154785), (316.9333190917969, 442.6000061035156, 3.897742509841919, 1.2178502082824707, 0.02724572829902172), (294.0, 443.5, 1.0, 0.0, 1.5707963705062866), (351.5, 443.0, 1.0, 0.0, 3.1415927410125732), (247.6666717529297, 444.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (310.6000061035156, 445.79998779296875, 2.3015334606170654, 1.379472255706787, 2.285245895385742), (65.07691955566406, 446.6153869628906, 3.035376787185669, 1.5744192600250244, 1.94112229347229), (267.5, 445.0, 1.0, 0.0, 3.1415927410125732), (306.0, 445.0, 1.632993221282959, 0.0, 3.1415927410125732), (363.375, 446.375, 1.9848051071166992, 1.3912400007247925, 0.03120940551161766), (369.4166564941406, 447.1666564941406, 3.2609434127807617, 1.492434024810791, 1.8730461597442627), (424.5, 445.0, 1.0, 0.0, 3.1415927410125732), (244.0, 446.5, 1.0, 0.0, 1.5707963705062866), (254.0, 446.0, 0.0, 0.0, 2.356194496154785), (31.5, 447.0, 1.0, 0.0, 3.1415927410125732), (52.33333206176758, 447.3333435058594, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (304.0, 447.0, 0.0, 0.0, 2.356194496154785), (394.0, 447.5, 1.0, 0.0, 1.5707963705062866), (260.25, 449.5, 2.363572597503662, 1.0786677598953247, 1.1980866193771362), (343.6666564941406, 448.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (390.0, 448.5, 1.0, 0.0, 1.5707963705062866), (14.5, 449.0, 1.0, 0.0, 3.1415927410125732), (60.68421173095703, 453.4210510253906, 4.030118942260742, 1.9242504835128784, 1.6641217470169067), (300.0, 449.0, 0.0, 0.0, 2.356194496154785), (316.3333435058594, 451.7916564941406, 8.5687255859375, 2.731095314025879, 3.016052007675171), (340.45001220703125, 453.70001220703125, 5.850510597229004, 1.673776388168335, 1.4335259199142456), (348.75, 450.0, 2.050309181213379, 1.2434756755828857, 2.715609550476074), (356.0, 449.0, 0.0, 0.0, 2.356194496154785), (456.75, 450.125, 2.2539470195770264, 1.0522466897964478, 0.6170607805252075), (20.0, 450.5, 1.0, 0.0, 1.5707963705062866), (394.3224792480469, 477.5230407714844, 40.061378479003906, 4.337221622467041, 1.9766758680343628), (64.0, 451.0, 0.0, 0.0, 2.356194496154785), (190.0, 451.25, 1.4142135381698608, 0.8660253882408142, 3.1415927410125732), (208.0, 451.0, 0.0, 0.0, 2.356194496154785), (224.6666717529297, 452.0833435058594, 2.8841052055358887, 1.695989966392517, 0.3836748003959656), (248.0, 451.5, 1.0, 0.0, 1.5707963705062866), (418.0, 451.5, 1.0, 0.0, 1.5707963705062866), (440.5, 451.0, 1.0, 0.0, 3.1415927410125732), (16.299999237060547, 453.0, 3.3526108264923096, 1.2649110555648804, 3.1415927410125732), (31.125, 453.125, 1.7320507764816284, 1.3693064451217651, 2.356194496154785), (39.57143020629883, 452.71429443359375, 2.836226224899292, 0.7803092002868652, 2.97377347946167), (46.42856979370117, 452.8571472167969, 1.8243328332901, 1.2529041767120361, 0.19025318324565887), (53.33333206176758, 453.4166564941406, 3.8327527046203613, 1.082181692123413, 0.4414491355419159), (265.9130554199219, 454.34783935546875, 3.5588207244873047, 2.6447174549102783, 1.1431825160980225), (286.4166564941406, 452.9166564941406, 2.522012710571289, 1.5001877546310425, 3.025660276412964), (346.0, 452.5, 1.0, 0.0, 1.5707963705062866), (466.1666564941406, 452.8333435058594, 1.5634719133377075, 1.154700517654419, 0.7853981852531433), (26.0, 453.25, 1.4142135381698608, 0.8660253882408142, 3.1415927410125732), (65.5, 453.5, 1.0, 1.0, 2.356194496154785), (116.75, 454.25, 1.7320507764816284, 0.7071067690849304, 1.249045729637146), (172.6666717529297, 453.6666564941406, 2.3543941974639893, 1.252173900604248, 2.90976881980896), (178.5, 453.0, 1.0, 0.0, 3.1415927410125732), (244.39999389648438, 453.79998779296875, 1.5491933822631836, 0.8944271802902222, 1.249045729637146), (272.6363525390625, 454.5454406738281, 2.46323561668396, 1.5476692914962769, 3.023469924926758), (330.7272644042969, 454.0, 3.6742780208587646, 1.0541592836380005, 2.7507803440093994), (402.0, 453.5, 1.0, 0.0, 1.5707963705062866), (35.66666793823242, 454.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (371.8333435058594, 454.6666564941406, 2.181668996810913, 0.8275048732757568, 0.22572654485702515), (497.3636474609375, 454.9090881347656, 3.238178014755249, 1.2959524393081665, 0.10974163562059402), (230.0, 455.0, 0.0, 0.0, 2.356194496154785), (252.25, 455.75, 1.7320507764816284, 0.7071067690849304, 1.249045729637146), (302.5, 455.0, 1.0, 0.0, 3.1415927410125732), (405.0, 456.4285583496094, 2.335300922393799, 1.113663673400879, 0.4752734303474426), (430.3333435058594, 455.3333435058594, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (462.0, 455.0, 0.0, 0.0, 2.356194496154785), (504.0, 455.0, 0.0, 0.0, 2.356194496154785), (240.72727966308594, 458.0909118652344, 2.8973710536956787, 1.477752685546875, 0.5654768943786621), (291.375, 456.75, 2.0602023601531982, 1.2012770175933838, 2.8042221069335938), (39.20000076293945, 457.79998779296875, 2.0, 0.6928203105926514, 0.7853981852531433), (54.0, 457.0, 0.0, 0.0, 2.356194496154785), (180.8235321044922, 457.8823547363281, 3.8237884044647217, 1.3800703287124634, 0.17671020328998566), (246.39999389648438, 458.20001220703125, 1.5491933822631836, 0.8944271802902222, 1.8925468921661377), (274.71429443359375, 458.28570556640625, 2.3173680305480957, 0.9131739735603333, 0.5213609337806702), (284.0, 457.0, 1.632993221282959, 0.0, 3.1415927410125732), (345.0, 458.0, 1.851640224456787, 1.0690449476242065, 0.7853981852531433), (352.71429443359375, 458.28570556640625, 2.8284270763397217, 0.6998541951179504, 0.7853981852531433), (362.0, 457.0, 0.0, 0.0, 2.356194496154785), (450.25, 457.75, 1.7320507764816284, 0.7071067690849304, 1.249045729637146), (502.0, 457.0, 0.0, 0.0, 2.356194496154785), (22.799999237060547, 458.6000061035156, 1.5491933822631836, 0.8944271802902222, 2.8198421001434326), (117.66666412353516, 458.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (172.5, 458.5, 2.2360680103302, 1.0, 3.1415927410125732), (188.5, 458.5, 1.0, 1.0, 2.356194496154785), (359.0, 458.0, 0.0, 0.0, 2.356194496154785), (435.20001220703125, 458.6000061035156, 1.5491933822631836, 0.8944271802902222, 0.32175055146217346), (442.0, 458.0, 0.0, 0.0, 2.356194496154785), (506.5, 458.5, 1.0, 1.0, 2.356194496154785), (2.0, 459.0, 0.0, 0.0, 2.356194496154785), (6.0, 459.0, 0.0, 0.0, 2.356194496154785), (229.0, 459.0, 1.632993221282959, 0.0, 3.1415927410125732), (260.0, 459.0, 0.0, 0.0, 2.356194496154785), (288.0, 459.0, 0.0, 0.0, 2.356194496154785), (304.0, 459.0, 0.0, 0.0, 2.356194496154785), (336.0, 459.0, 0.0, 0.0, 2.356194496154785), (423.0, 459.0, 1.632993221282959, 0.0, 3.1415927410125732), (358.1000061035156, 465.5249938964844, 7.150650501251221, 2.534895896911621, 2.602750539779663), (65.5, 463.0, 1.0, 0.0, 3.1415927410125732), (115.95918273925781, 469.8571472167969, 7.767367362976074, 3.462350606918335, 1.694848656654358), (170.5, 463.0, 3.4156503677368164, 0.0, 3.1415927410125732), (178.63333129882812, 466.76666259765625, 4.681983947753906, 3.3847899436950684, 0.426662415266037), (188.0, 463.0, 0.0, 0.0, 2.356194496154785), (192.5, 464.0, 1.6180340051651, 0.6180340051651001, 2.124370574951172), (223.6666717529297, 463.3333435058594, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (257.0, 464.1666564941406, 1.9148541688919067, 0.9428090453147888, 0.6435011029243469), (265.6000061035156, 463.79998779296875, 1.5491933822631836, 0.8944271802902222, 1.8925468921661377), (272.5, 463.0, 2.2360680103302, 0.0, 3.1415927410125732), (298.0, 463.0, 0.0, 0.0, 2.356194496154785), (314.0, 463.0, 1.632993221282959, 0.0, 3.1415927410125732), (320.0, 463.5, 1.0, 0.0, 1.5707963705062866), (325.5, 463.0, 1.0, 0.0, 3.1415927410125732), (338.1666564941406, 463.3333435058594, 2.181668996810913, 0.8275048732757568, 0.22572654485702515), (362.0, 463.0, 0.0, 0.0, 2.356194496154785), (398.5, 464.0, 2.424830675125122, 0.6734462976455688, 0.6927241683006287), (404.0, 463.0, 0.0, 0.0, 2.356194496154785), (424.5454406738281, 464.0, 3.5694267749786377, 1.438440203666687, 3.038642168045044), (500.3333435058594, 463.3333435058594, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (329.5, 465.0, 2.2360680103302, 1.632993221282959, 3.1415927410125732), (280.5, 465.0, 1.0, 0.0, 3.1415927410125732), (364.5, 465.0, 1.0, 0.0, 3.1415927410125732), (412.0, 465.0, 0.0, 0.0, 2.356194496154785), (41.66666793823242, 466.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (64.66666412353516, 467.77777099609375, 2.37044620513916, 1.477651596069336, 0.624522864818573), (170.5, 467.9375, 3.1655523777008057, 1.7926665544509888, 3.086390733718872), (38.0, 467.0, 0.0, 0.0, 2.356194496154785), (232.0, 467.5, 1.0, 0.0, 1.5707963705062866), (236.0, 467.5, 1.0, 0.0, 1.5707963705062866), (248.0, 467.0, 0.0, 0.0, 2.356194496154785), (269.0, 467.5, 1.632993221282959, 1.0, 3.1415927410125732), (295.20001220703125, 468.20001220703125, 2.0, 0.6928203105926514, 2.356194496154785), (306.5, 467.0, 1.0, 0.0, 3.1415927410125732), (341.0, 468.6000061035156, 2.629790782928467, 0.6664835214614868, 2.2817494869232178), (490.0, 467.0, 0.0, 0.0, 2.356194496154785), (56.0, 468.5, 1.0, 0.0, 1.5707963705062866), (376.3333435058594, 468.6666564941406, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (18.75, 472.45001220703125, 5.080057621002197, 2.516547918319702, 1.960953712463379), (24.5, 469.0, 1.0, 0.0, 3.1415927410125732), (46.22222137451172, 470.3333435058594, 2.7329318523406982, 1.1056408882141113, 0.9462734460830688), (196.5, 469.0, 1.0, 0.0, 3.1415927410125732), (243.92308044433594, 474.5769348144531, 5.853890419006348, 1.5417311191558838, 1.3942232131958008), (256.29412841796875, 470.5882263183594, 3.40588116645813, 1.7044541835784912, 2.6652090549468994), (262.0, 469.0, 0.0, 0.0, 2.356194496154785), (336.0, 469.0, 0.0, 0.0, 2.356194496154785), (33.0, 470.8333435058594, 1.9148541688919067, 0.9428090453147888, 0.6435011029243469), (228.6666717529297, 470.8333435058594, 1.5275251865386963, 1.3333333730697632, 0.46364760398864746), (498.0, 470.0, 0.0, 0.0, 2.356194496154785), (40.0, 471.0, 0.0, 0.0, 2.356194496154785), (287.5, 472.0, 1.632993221282959, 1.0, 1.5707963705062866), (349.5, 471.0, 1.0, 0.0, 3.1415927410125732), (501.71429443359375, 472.8571472167969, 2.4969637393951416, 0.8863298296928406, 1.645982027053833), (507.75, 472.25, 1.7320507764816284, 0.7071067690849304, 1.249045729637146), (170.0, 472.75, 1.4142135381698608, 0.8660253882408142, 3.1415927410125732), (284.28570556640625, 473.0, 1.5118578672409058, 1.3997083902359009, 1.5707963705062866), (495.6666564941406, 472.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (8.207547187805176, 476.5660400390625, 8.482768058776855, 3.5639808177948, 2.9865691661834717), (50.0, 474.1666564941406, 3.04949951171875, 0.7677510380744934, 0.5821147561073303), (60.0, 473.0, 0.0, 0.0, 2.356194496154785), (266.0, 473.5, 1.0, 0.0, 1.5707963705062866), (273.8461608886719, 474.30767822265625, 3.624140501022339, 2.058734178543091, 0.5916865468025208), (492.3333435058594, 473.3333435058594, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (65.33333587646484, 475.4444580078125, 2.138831377029419, 1.3215097188949585, 1.6763429641723633), (180.0, 474.0, 0.0, 0.0, 2.356194496154785), (308.0, 474.0, 0.0, 0.0, 2.356194496154785), (333.75, 475.0, 1.4142135381698608, 0.8660253882408142, 1.5707963705062866), (194.1666717529297, 475.3333435058594, 2.181668996810913, 0.8275048732757568, 0.22572654485702515), (255.1666717529297, 476.0, 1.9148541688919067, 0.9428090453147888, 0.9272952079772949), (298.0, 475.0, 0.0, 0.0, 2.356194496154785), (317.1111145019531, 477.22222900390625, 2.6297597885131836, 1.4733576774597168, 1.549975037574768), (329.5, 475.0, 1.0, 0.0, 3.1415927410125732), (398.0, 475.0, 0.0, 0.0, 2.356194496154785), (402.0, 475.5, 1.0, 0.0, 1.5707963705062866), (434.0, 475.0, 0.0, 0.0, 2.356194496154785), (62.0, 477.25, 1.4142135381698608, 0.8660253882408142, 3.1415927410125732), (164.5, 477.0, 1.0, 0.0, 3.1415927410125732), (218.75, 478.6666564941406, 2.0838100910186768, 1.815660834312439, 1.9164648056030273), (250.0, 477.0, 0.0, 0.0, 2.356194496154785), (440.5, 477.0, 1.0, 0.0, 3.1415927410125732), (347.8333435058594, 479.1666564941406, 1.5634719133377075, 1.154700517654419, 2.356194496154785), (431.75, 479.0, 1.4142135381698608, 0.8660253882408142, 1.5707963705062866), (493.20001220703125, 478.6000061035156, 1.5491933822631836, 0.8944271802902222, 0.32175055146217346), (21.0, 480.0, 3.045031785964966, 1.435658574104309, 0.2940013110637665), (42.0, 479.0, 0.0, 0.0, 2.356194496154785), (193.1764678955078, 480.23529052734375, 3.009546995162964, 2.002091646194458, 0.03291938453912735), (224.0, 479.0, 0.0, 0.0, 2.356194496154785), (354.3333435058594, 479.3333435058594, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (447.5, 479.0, 1.0, 0.0, 3.1415927410125732), (64.63636016845703, 482.4545593261719, 2.933816432952881, 2.0540614128112793, 1.0543159246444702), (164.0, 480.5, 1.0, 0.0, 1.5707963705062866), (188.0, 480.5, 1.0, 0.0, 1.5707963705062866), (234.5, 481.70001220703125, 2.362816333770752, 1.362754225730896, 2.4864957332611084), (247.39999389648438, 481.8999938964844, 2.5690464973449707, 1.3856406211853027, 2.158798933029175), (256.4210510253906, 482.1052551269531, 4.539236068725586, 1.9761154651641846, 0.12538598477840424), (334.96295166015625, 481.2962951660156, 7.561940670013428, 1.7472813129425049, 3.088681936264038), (376.0, 481.0, 1.2649110555648804, 1.2649110555648804, 2.356194496154785), (111.0, 481.0, 0.0, 0.0, 2.356194496154785), (116.0, 481.0, 0.0, 0.0, 2.356194496154785), (263.0, 481.0, 0.0, 0.0, 2.356194496154785), (284.0, 481.0, 0.0, 0.0, 2.356194496154785), (310.0, 481.5, 1.632993221282959, 1.0, 3.1415927410125732), (324.0, 481.0, 0.0, 0.0, 2.356194496154785), (358.0, 481.0, 0.0, 0.0, 2.356194496154785), (118.75, 483.0, 1.4142135381698608, 0.8660253882408142, 1.5707963705062866), (184.7142791748047, 482.5714416503906, 2.0776872634887695, 0.9527355432510376, 0.14572839438915253), (321.0, 483.0, 2.0466690063476562, 0.7817580103874207, 0.5535743832588196), (434.0, 482.0, 0.0, 0.0, 2.356194496154785), (14.0, 483.0, 0.0, 0.0, 2.356194496154785), (170.3333282470703, 483.3333435058594, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (288.0, 483.0, 0.0, 0.0, 2.356194496154785), (303.5, 483.0, 1.0, 0.0, 3.1415927410125732), (345.6000061035156, 484.20001220703125, 1.5491933822631836, 0.8944271802902222, 1.249045729637146), (386.0, 484.0, 1.632993221282959, 0.0, 1.5707963705062866), (453.5, 483.0, 1.0, 0.0, 3.1415927410125732), (492.0, 483.0, 0.0, 0.0, 2.356194496154785), (37.25, 486.8333435058594, 3.5929245948791504, 1.9312641620635986, 2.257200002670288), (52.0, 485.0, 1.2649110555648804, 1.2649110555648804, 2.356194496154785), (312.3333435058594, 484.6666564941406, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (114.0, 485.0, 0.0, 0.0, 2.356194496154785), (278.0, 485.0, 0.0, 0.0, 2.356194496154785), (332.0, 485.0, 0.0, 0.0, 2.356194496154785), (354.0, 485.0, 0.0, 0.0, 2.356194496154785), (358.0, 485.0, 0.0, 0.0, 2.356194496154785), (376.0, 486.0, 1.632993221282959, 0.0, 1.5707963705062866), (484.1666564941406, 485.3333435058594, 2.7725658416748047, 0.651144802570343, 2.885814666748047), (2.0, 486.71429443359375, 1.5118578672409058, 1.3997083902359009, 3.1415927410125732), (22.0, 489.5, 5.122788429260254, 1.547061800956726, 1.9594523906707764), (350.0, 486.5, 1.0, 0.0, 1.5707963705062866), (64.0, 487.0, 0.0, 0.0, 2.356194496154785), (111.5, 487.3333435058594, 1.9148541688919067, 0.9428090453147888, 3.1415927410125732), (118.46154022216797, 488.6153869628906, 3.1322600841522217, 1.7907254695892334, 0.1756102442741394), (176.0, 488.0, 1.632993221282959, 0.0, 1.5707963705062866), (260.0, 487.0, 0.0, 0.0, 2.356194496154785), (301.0, 487.0, 1.632993221282959, 0.0, 3.1415927410125732), (372.0, 487.0, 0.0, 0.0, 2.356194496154785), (439.0, 487.0, 1.632993221282959, 0.0, 3.1415927410125732), (286.6363525390625, 489.2727355957031, 3.8838398456573486, 1.0863935947418213, 0.15711595118045807), (310.3333435058594, 488.6666564941406, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (32.0, 489.0, 0.0, 0.0, 2.356194496154785), (262.1111145019531, 491.8888854980469, 3.0470075607299805, 1.1329809427261353, 1.5769689083099365), (315.5, 490.0, 1.632993221282959, 1.0, 1.5707963705062866), (374.0, 489.0, 0.0, 0.0, 2.356194496154785), (17.0, 490.6666564941406, 1.838544249534607, 0.41870102286338806, 2.650195837020874), (303.6842041015625, 491.7894592285156, 4.148355484008789, 1.920586347579956, 2.7018847465515137), (323.0, 490.875, 2.4918975830078125, 1.108127474784851, 0.20656441152095795), (349.5, 491.75, 2.8160905838012695, 0.9053365588188171, 2.4445488452911377), (353.75, 491.0, 1.4142135381698608, 0.8660253882408142, 1.5707963705062866), (167.0, 492.0, 1.851640224456787, 1.0690449476242065, 0.7853981852531433), (232.5, 494.125, 3.9687576293945312, 0.828530490398407, 1.8705511093139648), (293.5, 491.0, 1.0, 0.0, 3.1415927410125732), (312.79998779296875, 492.20001220703125, 2.0, 0.6928203105926514, 0.7853981852531433), (384.9230651855469, 493.0, 3.169369697570801, 1.3897579908370972, 0.5718429684638977), (422.6363525390625, 492.18182373046875, 2.486326217651367, 1.372697114944458, 2.4805495738983154), (184.0, 492.0, 0.0, 0.0, 2.356194496154785), (221.8000030517578, 493.20001220703125, 2.371682643890381, 0.6746264696121216, 1.761049509048462), (30.0, 493.0, 0.0, 0.0, 2.356194496154785), (40.0, 493.5, 1.0, 0.0, 1.5707963705062866), (112.53333282470703, 495.6000061035156, 3.3109238147735596, 1.6512638330459595, 1.227020263671875), (239.6666717529297, 493.3333435058594, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (256.25, 493.9166564941406, 2.3472275733947754, 1.8831032514572144, 2.92242431640625), (295.5, 493.0, 1.0, 0.0, 3.1415927410125732), (345.3125, 495.0625, 3.428466796875, 1.685041904449463, 1.6181890964508057), (358.4210510253906, 494.1052551269531, 4.050511360168457, 1.8631893396377563, 0.1459745466709137), (436.0, 493.5, 1.0, 0.0, 1.5707963705062866), (462.0, 493.0, 0.0, 0.0, 2.356194496154785), (325.0666809082031, 496.0666809082031, 3.1383025646209717, 1.9955371618270874, 0.0030302659142762423), (337.9166564941406, 495.5833435058594, 2.7688746452331543, 1.5092308521270752, 0.36717382073402405), (485.77777099609375, 495.8888854980469, 2.4079763889312744, 1.228939414024353, 1.442720651626587), (3.5, 495.0, 1.0, 0.0, 3.1415927410125732), (44.0, 495.0, 0.0, 0.0, 2.356194496154785), (50.0, 496.0, 1.632993221282959, 0.0, 1.5707963705062866), (64.0, 495.0, 0.0, 0.0, 2.356194496154785), (106.0, 495.0, 0.0, 0.0, 2.356194496154785), (182.0, 495.0, 0.0, 0.0, 2.356194496154785), (331.20001220703125, 495.3999938964844, 1.5491933822631836, 0.8944271802902222, 2.8198421001434326), (412.0, 495.0, 0.0, 0.0, 2.356194496154785), (456.5, 495.0, 1.0, 0.0, 3.1415927410125732), (460.0, 495.0, 0.0, 0.0, 2.356194496154785), (496.3333435058594, 495.3333435058594, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (504.0, 495.0, 0.0, 0.0, 2.356194496154785), (40.33333206176758, 496.6666564941406, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (186.73333740234375, 498.73333740234375, 4.207228660583496, 1.209823727607727, 1.3306480646133423), (248.0, 497.0, 1.2649110555648804, 1.2649110555648804, 2.356194496154785), (266.0, 496.0, 0.0, 0.0, 2.356194496154785), (22.0, 497.0, 0.0, 0.0, 2.356194496154785), (217.38888549804688, 498.8333435058594, 4.064578056335449, 1.911032795906067, 0.5153529644012451), (227.0, 497.0, 0.0, 0.0, 2.356194496154785), (352.0, 497.0, 0.0, 0.0, 2.356194496154785), (367.0, 498.1666564941406, 2.5377321243286133, 0.884385883808136, 2.1860034465789795), (418.0, 497.5, 1.0, 0.0, 1.5707963705062866), (422.0, 497.0, 0.0, 0.0, 2.356194496154785), (435.625, 498.375, 2.226987838745117, 1.3840250968933105, 0.9527665972709656), (441.28570556640625, 497.1428527832031, 3.3395602703094482, 0.6627020239830017, 0.06879337131977081), (502.0, 497.0, 0.0, 0.0, 2.356194496154785), (31.375, 499.0, 3.169861316680908, 0.9431217312812805, 0.11007466167211533), (63.0, 498.5, 1.0, 0.0, 1.5707963705062866), (105.19999694824219, 499.70001220703125, 2.2706925868988037, 1.387067198753357, 1.2527586221694946), (116.0, 498.0, 0.0, 0.0, 2.356194496154785), (384.0, 498.0, 0.0, 0.0, 2.356194496154785), (0.0, 499.0, 0.0, 0.0, 2.356194496154785), (7.666666507720947, 499.3333435058594, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (44.0, 499.0, 0.0, 0.0, 2.356194496154785), (273.0, 499.0, 0.0, 0.0, 2.356194496154785), (280.875, 500.125, 1.7320507764816284, 1.3693064451217651, 0.7853981852531433), (494.0, 499.0, 0.0, 0.0, 2.356194496154785), (498.0, 499.0, 0.0, 0.0, 2.356194496154785), (165.6666717529297, 500.6666564941406, 1.154700517654419, 0.6666666865348816, 2.356194496154785), (441.4285583496094, 501.0, 2.9965968132019043, 1.0690449476242065, 3.1415927410125732), (458.0, 500.5, 1.0, 0.0, 1.5707963705062866), (59.38461685180664, 503.80767822265625, 5.830495357513428, 2.260936737060547, 0.5940226912498474), (232.0, 501.0, 0.0, 0.0, 2.356194496154785), (244.0, 501.0, 0.0, 0.0, 2.356194496154785), (262.0, 501.0, 0.0, 0.0, 2.356194496154785), (299.70001220703125, 502.20001220703125, 5.720977783203125, 1.5331051349639893, 0.22328518331050873), (308.0, 501.0, 0.0, 0.0, 2.356194496154785), (330.0, 501.0, 0.0, 0.0, 2.356194496154785), (426.0, 501.0, 1.632993221282959, 0.0, 3.1415927410125732), (470.0, 501.0, 0.0, 0.0, 2.356194496154785), (349.6666564941406, 503.1666564941406, 1.5275251865386963, 1.3333333730697632, 2.677945137023926), (38.0, 504.0, 1.632993221282959, 0.0, 1.5707963705062866), (117.5, 503.0, 1.0, 0.0, 3.1415927410125732), (222.0, 503.0, 0.0, 0.0, 2.356194496154785), (236.5, 503.0, 1.0, 0.0, 3.1415927410125732), (242.92308044433594, 504.4615478515625, 3.489720106124878, 1.6243219375610352, 2.99562668800354), (260.0, 503.0, 0.0, 0.0, 2.356194496154785), (311.20001220703125, 503.20001220703125, 2.371682643890381, 0.6746264696121216, 0.19025318324565887), (320.3333435058594, 504.6666564941406, 3.354182004928589, 1.092660665512085, 1.1780972480773926), (343.5, 503.0, 1.0, 0.0, 3.1415927410125732), (420.0, 503.0, 0.0, 0.0, 2.356194496154785), (431.6000061035156, 503.20001220703125, 2.0466690063476562, 0.7817580103874207, 3.051666021347046), (450.0, 503.0, 0.0, 0.0, 2.356194496154785), (175.375, 505.5, 2.401470184326172, 1.0818690061569214, 0.6827004551887512), (254.3333282470703, 504.6666564941406, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (395.75, 505.5, 2.3401029109954834, 0.523372232913971, 1.2634648084640503), (16.0, 505.0, 0.0, 0.0, 2.356194496154785), (26.0, 505.0, 0.0, 0.0, 2.356194496154785), (189.0, 505.0, 0.0, 0.0, 2.356194496154785), (234.0, 505.0, 0.0, 0.0, 2.356194496154785), (305.0769348144531, 506.4615478515625, 2.20717191696167, 1.9470783472061157, 2.6076161861419678), (316.0, 505.0, 0.0, 0.0, 2.356194496154785), (340.3333435058594, 506.0, 1.838544249534607, 0.41870102286338806, 1.0793994665145874), (371.6666564941406, 505.3333435058594, 1.154700517654419, 0.6666666865348816, 0.7853981852531433), (468.0, 505.0, 0.0, 0.0, 2.356194496154785), (47.5428581237793, 509.22857666015625, 6.4331955909729, 2.719865560531616, 3.0719077587127686), (274.0, 506.5, 1.0, 0.0, 1.5707963705062866), (11.29411792755127, 509.29412841796875, 2.7760891914367676, 1.958719253540039, 2.0294370651245117), (17.950000762939453, 509.3999938964844, 2.6561691761016846, 2.4687578678131104, 2.371814489364624), (62.0, 507.0, 0.0, 0.0, 2.356194496154785), (117.0, 507.25, 1.4142135381698608, 0.8660253882408142, 3.1415927410125732), (180.0, 507.5, 1.0, 0.0, 1.5707963705062866), (236.0, 507.0, 0.0, 0.0, 2.356194496154785), (270.0, 507.0, 0.0, 0.0, 2.356194496154785), (282.0, 507.5, 1.0, 0.0, 1.5707963705062866), (354.0, 507.0, 0.0, 0.0, 2.356194496154785), (362.0, 507.0, 0.0, 0.0, 2.356194496154785), (484.0, 508.0, 1.632993221282959, 0.0, 1.5707963705062866), (503.5, 507.0, 1.0, 0.0, 3.1415927410125732), (27.799999237060547, 509.6000061035156, 2.0466690063476562, 0.7817580103874207, 1.4808695316314697), (59.57143020629883, 509.71429443359375, 2.0776872634887695, 0.9527355432510376, 1.4250679016113281), (263.5714416503906, 509.71429443359375, 2.0776872634887695, 0.9527355432510376, 1.4250679016113281), (332.0909118652344, 509.6363525390625, 2.173064947128296, 1.5336781740188599, 1.3077630996704102), (5.5, 510.0, 1.632993221282959, 1.0, 1.5707963705062866), (237.5, 510.0, 1.632993221282959, 1.0, 1.5707963705062866), (324.0, 510.0, 1.632993221282959, 0.0, 1.5707963705062866), (356.5, 510.0, 1.632993221282959, 1.0, 1.5707963705062866), (390.0, 510.0, 1.632993221282959, 0.0, 1.5707963705062866), (397.0, 510.0, 1.632993221282959, 0.0, 1.5707963705062866), (414.0, 510.0, 1.632993221282959, 1.632993221282959, 2.356194496154785), (479.71429443359375, 510.0, 1.5118578672409058, 1.3997083902359009, 1.5707963705062866), (368.0, 510.0, 0.0, 0.0, 2.356194496154785), (476.5, 510.0, 1.0, 0.0, 3.1415927410125732), (484.0, 511.0, 0.0, 0.0, 2.356194496154785)]}]} \ No newline at end of file diff --git a/sciwx/canvas/mcanvas.py b/sciwx/canvas/mcanvas.py index 663bf37c..28cde2ac 100644 --- a/sciwx/canvas/mcanvas.py +++ b/sciwx/canvas/mcanvas.py @@ -9,7 +9,7 @@ class ICanvas(Canvas): def __init__(self, parent, autofit=False): Canvas.__init__(self, parent, autofit) self.images.append(Image()) - self.images[0].back = Image() + self.images[0].back = None self.Bind(wx.EVT_IDLE, self.on_idle) def get_obj_tol(self): @@ -191,6 +191,9 @@ def set_cn(self, cn, b=False): @property def image(self): return self.canvas.image + @property + def back(self): return self.canvas.back + @property def name(self): return self.canvas.image.name diff --git a/sciwx/plugins/channels.py b/sciwx/plugins/channels.py index d2e64922..b432b110 100644 --- a/sciwx/plugins/channels.py +++ b/sciwx/plugins/channels.py @@ -170,8 +170,8 @@ def on_back(self, event): def on_setback(self, event): name = self.com_back.GetValue() if name is None: return - ImageManager.get().back = ImageManager.get(name) - curwin.ips.update() + self.app.get_img().back = self.app.get_img(name) + self.app.get_img().update() def on_mode(self, event): ips = self.app.get_img() From c6285ac3979118e612d6ef48081684c1d779ff1f Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 7 Jun 2020 20:30:15 +0800 Subject: [PATCH 222/343] Why frame rate drop down!!! --- imagepy/core/app/imagepy.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imagepy/core/app/imagepy.py b/imagepy/core/app/imagepy.py index 8f2ebbd4..a226c407 100644 --- a/imagepy/core/app/imagepy.py +++ b/imagepy/core/app/imagepy.py @@ -11,6 +11,7 @@ from skimage.data import camera from sciapp import App, Source from sciapp.object import Image +from imagepy import root_dir from .source import * class ImagePy(wx.Frame, App): @@ -64,7 +65,7 @@ def load_menu(self, data): def load_tool(self, data, default=None): for i, (name, tols) in enumerate(data[1]): self.toolbar.add_tools(name, tols, i==0) - if not default is None: self.toolbar.add_pop('./tools/drop.gif', default) + if not default is None: self.toolbar.add_pop(os.path.join(root_dir, 'tools/drop.gif'), default) self.toolbar.Layout() def load_widget(self, data): From 4bbe4d11901c1153643fedfd12ef8e2032e1ec15 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 7 Jun 2020 20:51:13 +0800 Subject: [PATCH 223/343] frame rate ok --- sciwx/canvas/canvas.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sciwx/canvas/canvas.py b/sciwx/canvas/canvas.py index 6ccc95cd..f29d65bf 100644 --- a/sciwx/canvas/canvas.py +++ b/sciwx/canvas/canvas.py @@ -161,8 +161,6 @@ def update(self, counter = [0,0]): drawmark(dc, self.to_panel_coor, i, k=self.scale, cur=0, winbox=self.winbox, oribox=self.oribox, conbox=self.conbox) dc.UnMask() - - self.dc = dc counter[1] += time()-start if counter[0] == 50: @@ -263,7 +261,7 @@ def to_panel_coor(self, x, y): return x, y def save_buffer(self, path): - dcSource = self.dc + dcSource = wx.BufferedDC(wx.ClientDC(self), self.buffer) # based largely on code posted to wxpython-users by Andrea Gavana 2006-11-08 size = dcSource.Size From 9c70626b05d699c37e1aa2946d2e314b7e7532de Mon Sep 17 00:00:00 2001 From: Prevalenter <434625142@qq.com> Date: Sun, 7 Jun 2020 21:40:26 +0800 Subject: [PATCH 224/343] fix the networkx Signed-off-by: Prevalenter <434625142@qq.com> --- imagepy/menus/Analysis/Skeleton Network/graph_plgs.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py b/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py index 4339631c..ab4b55c8 100644 --- a/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py +++ b/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py @@ -41,7 +41,9 @@ def run(self, ips, imgs, para = None): etitles = ['PartID', 'StartID', 'EndID', 'Length'] k, unit = ips.unit comid = 0 - for g in nx.connected_components(ips.data): + # for g in nx.connected_components(ips.data): + # for idx in g.nodes(): + for g in [ips.data.subgraph(c).copy() for c in nx.connected_components(ips.data)]: for idx in g.nodes(): o = g.nodes[idx]['o'] print(idx, g.degree(idx)) @@ -72,7 +74,7 @@ def run(self, ips, imgs, para = None): titles = ['PartID', 'Noeds', 'Edges', 'TotalLength', 'Density', 'AveConnect'] k, unit = ips.unit - gs = nx.connected_components(ips.data, False) if para['parts'] else [ips.data] + gs = [ips.data.subgraph(c).copy() for c in nx.connected_components(ips.data)] if para['parts'] else [ips.data] comid, datas = 0, [] for g in gs: sl = 0 @@ -81,6 +83,8 @@ def run(self, ips, imgs, para = None): datas.append([comid, g.number_of_nodes(), g.number_of_edges(), round(sl*k, 2), round(nx.density(g), 2), round(nx.average_node_connectivity(g),2)][1-para['parts']:]) comid += 1 + # print('======datas=========', datas) + # print('======columns=========', titles[1-para['parts']:]) self.app.show_table(pd.DataFrame(datas, columns=titles[1-para['parts']:]), ips.title+'-graph') class CutBranch(Filter): From 5facf11857d6aef213a6ed3724d84043f970d4a9 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 7 Jun 2020 23:13:33 +0800 Subject: [PATCH 225/343] 3d tools ok --- imagepy/core/app/imagepy.py | 2 +- imagepy/menus/Image/Type/convert_plg.py | 11 +-- .../menus/Kit3D/Network 3D/toolkit3d_plgs.py | 82 +++++++++---------- imagepy/tools/Draw/flood3d_tol.py | 7 -- imagepy/tools/Toolkit3D/flood3d_tol.py | 15 +--- sciapp/app.py | 14 ++-- sciapp/object/image.py | 9 +- sciapp/object/surface.py | 3 + sciwx/mesh/scene.py | 12 +-- 9 files changed, 69 insertions(+), 86 deletions(-) diff --git a/imagepy/core/app/imagepy.py b/imagepy/core/app/imagepy.py index a226c407..01bfd32a 100644 --- a/imagepy/core/app/imagepy.py +++ b/imagepy/core/app/imagepy.py @@ -239,7 +239,7 @@ def _show_mesh(self, mesh=None, title=None): if mesh is None: canvas = self.meshnb.add_canvas() canvas.mesh.name = 'Surface' - elif hasattr(mesh, 'ns'): + elif hasattr(mesh, 'vts'): canvas = self.get_mesh_win() if canvas is None: canvas = self.meshnb.add_canvas() diff --git a/imagepy/menus/Image/Type/convert_plg.py b/imagepy/menus/Image/Type/convert_plg.py index bb7f0cac..dc51d877 100644 --- a/imagepy/menus/Image/Type/convert_plg.py +++ b/imagepy/menus/Image/Type/convert_plg.py @@ -38,20 +38,21 @@ class ToRGB(Simple): note = ['all'] def run(self, ips, imgs, para = None): - if ips.imgtype == 'rgb': return - n = ips.get_nslices() - if ips.is3d: + if ips.dtype == np.uint8 and ips.channels == 3: return + n = ips.slices + + if ips.isarray: imgrgb = np.zeros(ips.size+(n,), dtype=np.uint8) if ips.dtype == np.uint8: img8 = imgs else: - minv, maxv = ips.get_updown() + minv, maxv = ips.range k = 255.0/(max(1e-8, maxv-minv)) bf = np.clip(imgs, minv, maxv) img8 = ((bf - minv) * k).astype(np.uint8) rgb = ips.lut[img8] else: rgb = [] - minv, maxv = ips.get_updown() + minv, maxv = ips.range for i in range(n): self.progress(i, len(imgs)) if ips.dtype==np.uint8: diff --git a/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py b/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py index 57f867f2..af9959a0 100644 --- a/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py +++ b/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py @@ -5,6 +5,8 @@ import networkx as nx import numpy as np import pandas as pd +from sciapp.object import Surface, MarkText +from sciapp.util import surfutil norm = np.linalg.norm class Skeleton3D(Simple): @@ -28,19 +30,12 @@ class Show3DGraph(Simple): title = 'Show Graph 3D' note = ['8-bit', 'stack3d'] - para = {'r':1, 'ncolor':(255,0,0), 'lcolor':(0,0,255)} + para = {'r':1, 'ncolor':(255,0,0), 'lcolor':(0,0,255), 'pcolor':(0,255,0)} view = [(int, 'r', (1,100), 0, 'radius', 'pix'), ('color', 'ncolor', 'node', 'rgb'), - ('color', 'lcolor', 'line', 'rgb')] + ('color', 'lcolor', 'line', 'rgb'), + ('color', 'pcolor', 'path', 'rgb')] - def load(self, ips): - if not isinstance(ips.data, nx.MultiGraph): - IPy.alert("Please build graph!"); - return False; - self.frame = myvi.Frame3D.figure(IPy.curapp, title='3D Canvas') - return True; - - #process def run(self, ips, imgs, para = None): balls, ids, rs, graph = [], [], [], ips.data for idx in graph.nodes(): @@ -62,45 +57,38 @@ def run(self, ips, imgs, para = None): rs = [para['r']] * len(balls) cs = tuple(np.array(para['ncolor'])/255.0) - vts, fs, ns, cs = myvi.build_balls(balls, rs, cs) - self.frame.viewer.add_surf_asyn('balls', vts, fs, ns, cs) + vts, fs, ns, cs = surfutil.build_balls(balls, rs, cs) + self.app.show_mesh(Surface(vts, fs, ns, cs), 'balls') - vts, fs, pos, h, color = myvi.build_marks(['ID:%s'%i for i in ids], balls, para['r'], para['r'], (1,1,1)) - self.frame.viewer.add_mark_asyn('txt', vts, fs, pos, h, color) + vts, fs, pos, h, color = surfutil.build_marks(['ID:%s'%i for i in ids], balls, para['r'], para['r'], (1,1,1)) + self.app.show_mesh(MarkText(vts, fs, pos, h, color), 'txt') cs = tuple(np.array(para['lcolor'])/255.0) - vts, fs, ns, cs = myvi.build_lines(xs, ys, zs, cs) - self.frame.viewer.add_surf_asyn('paths', vts, fs, ns, cs, mode='grid') - vts, fs, ns, cs = myvi.build_lines(lxs, lys, lzs, (0,1,0)) - self.frame.viewer.add_surf_asyn('lines', vts, fs, ns, cs, mode='grid') - self.frame.Raise() - self.frame = None + vts, fs, ns, cs = surfutil.build_lines(xs, ys, zs, cs) + self.app.show_mesh(Surface(vts, fs, ns, cs, mode='grid'), 'path') + + cs = tuple(np.array(para['pcolor'])/255.0) + vts, fs, ns, cs = surfutil.build_lines(lxs, lys, lzs, cs) + self.app.show_mesh(Surface(vts, fs, ns, cs, mode='grid'), 'lines') + class Show3DGraphR(Simple): title = 'Show Graph R 3D' note = ['8-bit', 'stack3d'] - para = {'dis':None, 'ncolor':(255,0,0), 'lcolor':(0,0,255)} + para = {'dis':None, 'ncolor':(255,0,0), 'lcolor':(0,0,255), 'pcolor':(0,255,0)} view = [('img', 'dis', 'distance', 'map'), ('color', 'ncolor', 'node', 'rgb'), - ('color', 'lcolor', 'line', 'rgb')] - - def load(self, ips): - if not isinstance(ips.data, nx.MultiGraph): - IPy.alert("Please build graph!"); - return False; - self.frame = myvi.Frame3D.figure(IPy.curapp, title='3D Canvas') - return True; - + ('color', 'lcolor', 'line', 'rgb'), + ('color', 'pcolor', 'path', 'rgb')] #process def run(self, ips, imgs, para = None): - dis = ImageManager.get(para['dis']).imgs + dis = self.app.get_img(para['dis']).imgs balls, ids, rs, graph = [], [], [], ips.data for idx in graph.nodes(): ids.append(idx) balls.append(graph.nodes[idx]['o']) - xs, ys, zs = [], [], [] v1s, v2s = [], [] for (s, e) in graph.edges(): @@ -113,27 +101,31 @@ def run(self, ips, imgs, para = None): xs.append(pts[:,0]) ys.append(pts[:,1]) zs.append(pts[:,2]) + rs1 = dis[list(np.array(v1s).astype(np.int16).T)] rs2 = dis[list(np.array(v2s).astype(np.int16).T)] rs1 = list(np.clip(rs1, 2, 1e4)*0.5) rs2 = list(np.clip(rs2, 2, 1e4)*0.5) rs = dis[list(np.array(balls).astype(np.int16).T)] rs = list(np.clip(rs, 2, 1e4)) + + print(balls, rs1, rs2, rs) + cs = tuple(np.array(para['ncolor'])/255.0) - vts, fs, ns, cs = myvi.build_balls(balls, rs, cs) - self.frame.viewer.add_surf_asyn('balls', vts, fs, ns, cs) + vts, fs, ns, cs = surfutil.build_balls(balls, rs, cs) + self.app.show_mesh(Surface(vts, fs, ns, cs), 'balls') + meansize = sum(rs)/len(rs) - vts, fs, pos, h, color = myvi.build_marks(['ID:%s'%i for i in ids], balls, rs, meansize, (1,1,1)) - self.frame.viewer.add_mark_asyn('txt', vts, fs, pos, h, color) - - css = tuple(np.array(para['lcolor'])/255.0) - vts, fs, ns, cs = myvi.build_lines(xs, ys, zs, css) - self.frame.viewer.add_surf_asyn('paths', vts, fs, ns, cs, mode='grid') - #vts, fs, ns, cs = myvi.build_lines(lxs, lys, lzs, (0,1,0)) - vts, fs, ns, cs = myvi.build_arrows(v1s, v2s, rs1, rs2, 0, 0, css) - self.frame.viewer.add_surf_asyn('lines', vts, fs, ns, cs) - self.frame.Raise() - self.frame = None + vts, fs, pos, h, color = surfutil.build_marks(['ID:%s'%i for i in ids], balls, rs, meansize, (1,1,1)) + self.app.show_mesh(MarkText(vts, fs, pos, h, color), 'txt') + + cs = tuple(np.array(para['lcolor'])/255.0) + vts, fs, ns, cs = surfutil.build_lines(xs, ys, zs, cs) + self.app.show_mesh(Surface(vts, fs, ns, cs, mode='grid'), 'path') + + cs = tuple(np.array(para['pcolor'])/255.0) + vts, fs, ns, cs = surfutil.build_arrows(v1s, v2s, rs1, rs2, 0, 0, cs) + self.app.show_mesh(Surface(vts, fs, ns, cs), 'lines') class Statistic(Simple): title = 'Graph Statistic 3D' diff --git a/imagepy/tools/Draw/flood3d_tol.py b/imagepy/tools/Draw/flood3d_tol.py index bd7172e7..715c5b48 100644 --- a/imagepy/tools/Draw/flood3d_tol.py +++ b/imagepy/tools/Draw/flood3d_tol.py @@ -1,10 +1,3 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Oct 19 17:35:09 2016 - -@author: yxl -""" - from sciapp.action import ImageTool import numpy as np from skimage.morphology import flood_fill, flood diff --git a/imagepy/tools/Toolkit3D/flood3d_tol.py b/imagepy/tools/Toolkit3D/flood3d_tol.py index c5326662..715c5b48 100644 --- a/imagepy/tools/Toolkit3D/flood3d_tol.py +++ b/imagepy/tools/Toolkit3D/flood3d_tol.py @@ -1,13 +1,5 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Oct 19 17:35:09 2016 - -@author: yxl -""" - -from imagepy.core.engine import Tool +from sciapp.action import ImageTool import numpy as np -from sciapp import Source from skimage.morphology import flood_fill, flood from imagepy.core.engine import Filter, Simple @@ -18,14 +10,15 @@ class FloodFill3D(Simple): def run(self, ips, imgs, para = None): flood_fill(imgs, para['seed'], para['color'], connectivity=para['conn'], tolerance=para['tor'], inplace=True) -class Plugin(Tool): +class Plugin(ImageTool): title = 'Flood Fill 3D' para = {'tor':10, 'con':'8-connect'} view = [(int, 'tor', (0,1000), 0, 'torlorance', 'value'), (list, 'con', ['4-connect', '8-connect'], str, 'fill', 'pix')] def mouse_down(self, ips, x, y, btn, **key): - FloodFill3D().start({'seed':(ips.cur, int(y), int(x)), 'color':np.mean(Source.manager('color').get('front')), + FloodFill3D().start(self.app, {'seed':(ips.cur, int(y), int(x)), + 'color':np.mean(self.app.manager('color').get('front')), 'conn':(self.para['con']=='8-connect')+1, 'tor':self.para['tor']}) def mouse_up(self, ips, x, y, btn, **key): diff --git a/sciapp/app.py b/sciapp/app.py index 16051eea..b2a69d99 100644 --- a/sciapp/app.py +++ b/sciapp/app.py @@ -126,24 +126,24 @@ def get_tab_win(self, name=None): return self.wtab_manager.get(name) def add_mesh(self, mesh): - mesh.name = self.mesh_manager.name('name', obj=tab, name=mesh.name) - self.mesh_manager.add(obj=mesh, name=mesh.name) + if not self.mesh_manager.has(mesh.name, obj=mesh): + mesh.name = self.mesh_manager.name(mesh.name) + self.mesh_manager.add(mesh.name, mesh) def remove_mesh(self, mesh): - print('remove', mesh.name) self.mesh_manager.remove(obj=mesh) def add_mesh_win(self, win): - self.wmesh_manager.add(obj=win, name=win.name, check=False) + self.wmesh_manager.add(win.name, win) def remove_mesh_win(self, win): self.wmesh_manager.remove(obj=win) def get_mesh(self, name=None): - return self.mesh_manager.get(name=name) + return self.mesh_manager.get(name) def get_mesh_name(self): - return self.mesh_manager.gets('name') + return self.mesh_manager.names() def get_mesh_win(self, name=None): - return self.wmesh_manager.get(name=name) + return self.wmesh_manager.get(name) diff --git a/sciapp/object/image.py b/sciapp/object/image.py index a5b2d905..35a88b25 100644 --- a/sciapp/object/image.py +++ b/sciapp/object/image.py @@ -7,7 +7,7 @@ def get_updown(imgs, slices='all', chans='all', step=1): if isinstance(slices, int): imgs = [imgs[slices]] if step<=1: step = int(1/step+0.5) else: step = int(min(imgs[0].shape[:2])/step+0.5) - s = slice(None, None, max(step,1)) + s = slice(None, None, max(step, 1)) s = (s,s,c)[:imgs[0].ndim] mins = [i[s].min(axis=(0,1)) for i in imgs] maxs = [i[s].max(axis=(0,1)) for i in imgs] @@ -144,11 +144,12 @@ def reset(self): self.rg = [(0, 255)] * self.channels else: self.rg = self.get_updown('all', 'all', step=512) - print(self.cn, self.rg, '==========') def snapshot(self): - if self.snap is None: - self.snap = self.img.copy() + dif = self.snap is None + dif = dif or self.snap.shape != self.img.shape + dif = dif or self.snap.dtype != self.img.dtype + if dif: self.snap = self.img.copy() else: self.snap[:] = self.img def swap(self): diff --git a/sciapp/object/surface.py b/sciapp/object/surface.py index 2d9e4881..121e6a32 100644 --- a/sciapp/object/surface.py +++ b/sciapp/object/surface.py @@ -17,6 +17,7 @@ def __init__(self, vts, ids, ns, cs=(0,0,1), **key): self.visible = True self.width = 1 self.update = False + self.color = (0,0,0) self.set_style(**key) def set_style(self, **key): @@ -35,10 +36,12 @@ def __init__(self, vts, ids, os, h, color): self.os = os self.h = h self.box = None + self.alpha = 1 self.visible = True self.width = 1 self.mode = 'grid' self.update = False + self.color = (0,0,0) class MeshSet: def __init__(self, name='meshset', objs=None): diff --git a/sciwx/mesh/scene.py b/sciwx/mesh/scene.py index 2e949996..13aae06e 100644 --- a/sciwx/mesh/scene.py +++ b/sciwx/mesh/scene.py @@ -81,10 +81,10 @@ def on_ctx(self, ctx): } ''') for i in self.objs: - if isinstance(self.objs[i], Surface): - self.ctx_obj(self.ctx, self.prog_suf, self.objs[i], i) if isinstance(self.objs[i], MarkText): self.ctx_txt(self.ctx, self.prog_txt, self.objs[i], i) + elif isinstance(self.objs[i], Surface): + self.ctx_obj(self.ctx, self.prog_suf, self.objs[i], i) @property def objs(self): return self.meshset.objs @@ -111,10 +111,10 @@ def ctx_txt(self, ctx, prog, obj, name): def add_surf(self, name, obj): if not self.ctx is None: - if isinstance(obj, Surface): - self.ctx_obj(self.ctx, self.prog_suf, obj, name) if isinstance(obj, MarkText): self.ctx_txt(self.ctx, self.prog_txt, obj, name) + elif isinstance(obj, Surface): + self.ctx_obj(self.ctx, self.prog_suf, obj, name) self.objs[name] = obj self.count_box() @@ -151,8 +151,8 @@ def draw(self): #self.ctx.enable(ModernGL.CULL_FACE) self.ctx.enable(moderngl.BLEND) for i in self.objs: - prog = self.prog_suf if isinstance(self.objs[i], Surface) else self.prog_txt - draw = self.draw_obj if isinstance(self.objs[i], Surface) else self.draw_txt + prog = self.prog_txt if isinstance(self.objs[i], MarkText) else self.prog_suf + draw = self.draw_txt if isinstance(self.objs[i], MarkText) else self.draw_obj draw(i, self.objs[i], self.ctx, prog, self.mvp, self.light, self.bright, self.scatter) def count_box(self): From cec764dd29b348994056618ebd205e1944096f3d Mon Sep 17 00:00:00 2001 From: qixinbo Date: Mon, 8 Jun 2020 10:03:43 +0800 Subject: [PATCH 226/343] aibrush fixed --- imagepy/tools/Draw/aibrush_tol.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/tools/Draw/aibrush_tol.py b/imagepy/tools/Draw/aibrush_tol.py index 5209d2cf..3718a839 100644 --- a/imagepy/tools/Draw/aibrush_tol.py +++ b/imagepy/tools/Draw/aibrush_tol.py @@ -245,7 +245,7 @@ def mouse_move(self, ips, x, y, btn, **key): sc = (max(0,c-w), min(img.shape[1], c+w)) r, c = min(r, w), min(c, w) backclip = imgclip = img[slice(*sr), slice(*sc)] - if not ips.back.img is None: + if not ips.back is None: backclip = ips.back.img[slice(*sr), slice(*sc)] if self.status == 'local_pen': From e4fe6c5eb566150a99d2ac4aac25501622c26a5f Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 8 Jun 2020 11:57:21 +0800 Subject: [PATCH 227/343] transform tools ok --- imagepy/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imagepy/__main__.py b/imagepy/__main__.py index 17d715dd..91ab3c76 100644 --- a/imagepy/__main__.py +++ b/imagepy/__main__.py @@ -47,7 +47,7 @@ def extend_wgts(wgt): # ('Channels', Channels)]), # ('Navigator', [('Viewport', ViewPort)])]) - frame.show_img([camera()], 'camera') - frame.show_img([astronaut()], 'astronaut') + #frame.show_img([camera()], 'camera') + #frame.show_img([astronaut()], 'astronaut') frame.Show() app.MainLoop() \ No newline at end of file From bebc8bb897c1a4cdfad35071b962021542bb8f9e Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 8 Jun 2020 11:57:30 +0800 Subject: [PATCH 228/343] transform tools ok --- imagepy/__main__.py | 4 +- imagepy/tools/Draw/aibrush_tol.py | 2 +- imagepy/tools/Transform/rotate_tol.py | 49 ++++++++++++----- imagepy/tools/Transform/scale_tol.py | 78 +++++++++++++++------------ sciapp/object/shape.py | 9 ++-- sciwx/widgets/paradialog.py | 5 +- 6 files changed, 91 insertions(+), 56 deletions(-) diff --git a/imagepy/__main__.py b/imagepy/__main__.py index 91ab3c76..17d715dd 100644 --- a/imagepy/__main__.py +++ b/imagepy/__main__.py @@ -47,7 +47,7 @@ def extend_wgts(wgt): # ('Channels', Channels)]), # ('Navigator', [('Viewport', ViewPort)])]) - #frame.show_img([camera()], 'camera') - #frame.show_img([astronaut()], 'astronaut') + frame.show_img([camera()], 'camera') + frame.show_img([astronaut()], 'astronaut') frame.Show() app.MainLoop() \ No newline at end of file diff --git a/imagepy/tools/Draw/aibrush_tol.py b/imagepy/tools/Draw/aibrush_tol.py index 5209d2cf..3718a839 100644 --- a/imagepy/tools/Draw/aibrush_tol.py +++ b/imagepy/tools/Draw/aibrush_tol.py @@ -245,7 +245,7 @@ def mouse_move(self, ips, x, y, btn, **key): sc = (max(0,c-w), min(img.shape[1], c+w)) r, c = min(r, w), min(c, w) backclip = imgclip = img[slice(*sr), slice(*sc)] - if not ips.back.img is None: + if not ips.back is None: backclip = ips.back.img[slice(*sr), slice(*sc)] if self.status == 'local_pen': diff --git a/imagepy/tools/Transform/rotate_tol.py b/imagepy/tools/Transform/rotate_tol.py index f1a3941b..6e57673a 100644 --- a/imagepy/tools/Transform/rotate_tol.py +++ b/imagepy/tools/Transform/rotate_tol.py @@ -1,9 +1,13 @@ import wx import numpy as np -from imagepy.core.engine import Tool, Filter +from sciapp.action import ImageTool +from sciapp.util import mark2shp, geom2shp +from sciapp.object import ROI +from imagepy.core.engine import Filter +from shapely.affinity import affine_transform import scipy.ndimage as nimg -class RotateTool(Tool): +class RotateTool(ImageTool): """RotateTool class derived from imagepy.core.engine.Tool""" def __init__(self, plg): self.plg = plg @@ -22,12 +26,13 @@ def mouse_up(self, ips, x, y, btn, **key): def mouse_move(self, ips, x, y, btn, **key): lim = 5.0/key['canvas'].scale if btn==None: - self.cursor = wx.CURSOR_CROSS + self.cursor = 'cross' if abs(x-self.para['ox']) Date: Mon, 8 Jun 2020 23:12:40 +0800 Subject: [PATCH 229/343] measure tools ok --- imagepy/core/app/source.py | 15 +- imagepy/data/config.json | 2 +- imagepy/menus/Image/Mark/mark_plgs.py | 6 +- imagepy/menus/Selection/select_plg.py | 39 +-- imagepy/tools/Measure/angle2_tol.py | 132 +------- imagepy/tools/Measure/angle_tol.py | 136 +------- imagepy/tools/Measure/area_tol.py | 146 +-------- imagepy/tools/Measure/coordinate_tol.py | 100 +----- imagepy/tools/Measure/distance_tol.py | 131 +------- imagepy/tools/Measure/profile_tol.py | 1 - imagepy/tools/Measure/setting.py | 1 - imagepy/tools/Measure/setting_tol.py | 26 +- sciapp/action/__init__.py | 1 + sciapp/action/meaact.py | 292 ++++++++++++++++++ sciapp/action/shpbase.py | 1 - sciapp/object/roi.py | 3 + sciapp/object/shape.py | 9 +- sciwx/canvas/mark.py | 14 +- sciwx/demo/canvas5_frame_demo.py | 2 +- sciwx/demo/shape_measure_demo.py | 26 ++ .../{roi_edit_demo.py => shape_roi_demo.py} | 0 sciwx/widgets/toolbar.py | 2 +- 22 files changed, 379 insertions(+), 706 deletions(-) delete mode 100644 imagepy/tools/Measure/setting.py create mode 100644 sciapp/action/meaact.py create mode 100644 sciwx/demo/shape_measure_demo.py rename sciwx/demo/{roi_edit_demo.py => shape_roi_demo.py} (100%) diff --git a/imagepy/core/app/source.py b/imagepy/core/app/source.py index dc0d5002..efd6400e 100644 --- a/imagepy/core/app/source.py +++ b/imagepy/core/app/source.py @@ -11,10 +11,21 @@ Source.manager('config') Source.manager('config').read(osp.join(root_dir, 'data/config.json')) -from sciapp.object.shape import default_style + +from sciapp.object import Shape mark_style = Source.manager('config').get('mark_style') if not mark_style is None: - for i in mark_style: default_style[i] = mark_style[i] + for i in mark_style: Shape.default[i] = mark_style[i] + +from sciapp.object import ROI +mark_style = Source.manager('config').get('roi_style') +if not mark_style is None: + for i in mark_style: ROI.default[i] = mark_style[i] + +from sciapp.action.meaact import Measure +mark_style = Source.manager('config').get('mea_style') +if not mark_style is None: + for i in mark_style: Measure.default[i] = mark_style[i] Source.manager('colormap').remove() diff --git a/imagepy/data/config.json b/imagepy/data/config.json index 40f51520..dab632db 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null]] \ No newline at end of file +[["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [128, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null]] \ No newline at end of file diff --git a/imagepy/menus/Image/Mark/mark_plgs.py b/imagepy/menus/Image/Mark/mark_plgs.py index 97d5b6d9..231c080a 100644 --- a/imagepy/menus/Image/Mark/mark_plgs.py +++ b/imagepy/menus/Image/Mark/mark_plgs.py @@ -1,6 +1,6 @@ from imagepy.core.engine import Simple, Free from sciapp import Source -from sciapp.object.shape import default_style +from sciapp.object import Shape from sciapp.util import mark2shp import json @@ -44,7 +44,7 @@ def run(self, ips, imgs, para = None): class Setting(Free): title = 'Mark Setting' - para = default_style.copy() + para = Shape.default.copy() view = [('color', 'color', 'line', 'color'), ('color', 'fcolor', 'face', 'color'), ('color', 'tcolor', 'text', 'color'), @@ -53,7 +53,7 @@ class Setting(Free): (bool, 'fill', 'solid fill')] def run(self, para=None): - for i in para: default_style[i] = para[i] + for i in para: Shape.default[i] = para[i] Source.manager('config').add('mark_style', para) plgs = [Open, Save, Clear, '-', Setting] \ No newline at end of file diff --git a/imagepy/menus/Selection/select_plg.py b/imagepy/menus/Selection/select_plg.py index 5b7f28af..e5751a4a 100644 --- a/imagepy/menus/Selection/select_plg.py +++ b/imagepy/menus/Selection/select_plg.py @@ -1,15 +1,9 @@ -# -*- coding: utf-8 -*- -""" -Created on Sun Dec 18 22:31:12 2016 - -@author: yxl -""" from imagepy.core.engine import Simple, Free +from sciapp import Source from sciapp.object import ROI, geom2shp, geom_flatten, Rectangle, geom_union, mark2shp import json, time class SelectAll(Simple): - """SelectAll: derived from imagepy.core.engine.Simple """ title = 'Select All' note = ['all'] @@ -17,7 +11,6 @@ def run(self, ips, imgs, para = None): ips.roi = ROI(Rectangle([0, 0, ips.shape[1], ips.shape[0]])) class SelectNone(Simple): - """SelectNone: derived from imagepy.core.engine.Simple """ title = 'Select None' note = ['all'] @@ -26,7 +19,6 @@ def run(self, ips, imgs, para = None): time.sleep(0.1) class Add2Manager(Simple): - """Add2Manager: derived from imagepy.core.engine.Simple """ title = 'ROI Add' note = ['all', 'req_roi'] para = {'name':''} @@ -70,7 +62,6 @@ def run(self, ips, imgs, para = None): ips.roi = mark2shp(self.app.manager('roi').get(name=para['name'])) class Inflate(Simple): - """Inflate: derived from imagepy.core.engine.Simple """ title = 'ROI Inflate' note = ['all', 'req_roi'] para = {'r':5} @@ -81,7 +72,6 @@ def run(self, ips, imgs, para = None): ips.roi = ROI(geom2shp(geom_flatten(geom))) class Shrink(Simple): - """Shrink: derived from imagepy.core.engine.Simple """ title = 'ROI Shrink' note = ['all', 'req_roi'] para = {'r':5} @@ -92,7 +82,6 @@ def run(self, ips, imgs, para = None): ips.roi = ROI(geom2shp(geom_flatten(geom))) class Convex(Simple): - """Convex: derived from imagepy.core.engine.Simple """ title = 'ROI Convex Hull' note = ['all', 'req_roi'] @@ -101,7 +90,6 @@ def run(self, ips, imgs, para = None): ips.roi = ROI(geom2shp(geom_flatten(geom))) class Box(Simple): - """Box: derived from imagepy.core.engine.Simple """ title = 'ROI Bound Box' note = ['all', 'req_roi'] @@ -110,7 +98,6 @@ def run(self, ips, imgs, para = None): ips.roi = ROI(Rectangle([a,b,c-a,d-b])) class Clip(Simple): - """Clip: derived from imagepy.core.engine.Simple """ title = 'ROI Clip' note = ['all', 'req_roi'] @@ -120,7 +107,6 @@ def run(self, ips, imgs, para = None): ips.roi = ROI(geom2shp(geom_flatten(geom))) class Invert(Simple): - """Invert: derived from imagepy.core.engine.Simple """ title = 'ROI Invert' note = ['all', 'req_roi'] @@ -130,7 +116,6 @@ def run(self, ips, imgs, para = None): ips.roi = ROI(geom2shp(geom_flatten(geom))) class Save(Simple): - """Save: save roi as a wkt file """ title = 'ROI Save' note = ['all', 'req_roi'] para={'path':''} @@ -144,7 +129,6 @@ def run(self, ips, imgs, para = None): f.write(json.dumps(ips.roi.to_mark())) class Open(Simple): - """Save: save roi as a wkt file """ title = 'ROI Open' note = ['all'] para={'path':''} @@ -158,7 +142,6 @@ def run(self, ips, imgs, para = None): ips.roi = ROI(mark2shp(json.loads(f.read()))) class Intersect(Simple): - """Union: derived from imagepy.core.engine.Simple """ title = 'ROI Intersect' note = ['all', 'req_roi'] para = {'name':''} @@ -178,7 +161,6 @@ def run(self, ips, imgs, para = None): ips.roi = ROI(geom2shp(geom_flatten(roi.intersection(obj)))) class Union(Simple): - """Union: derived from imagepy.core.engine.Simple """ title = 'ROI Union' note = ['all', 'req_roi'] para = {'name':''} @@ -198,7 +180,6 @@ def run(self, ips, imgs, para = None): ips.roi = ROI(geom2shp(geom_flatten(roi.union(obj)))) class Diff(Simple): - """Diff: derived from imagepy.core.engine.Simple """ title = 'ROI Difference' note = ['all', 'req_roi'] para = {'name':''} @@ -239,17 +220,17 @@ def run(self, ips, imgs, para = None): class Setting(Free): title = 'ROI Setting' - - def load(self): - Setting.para = {'color':RoiManager.get_color(), - 'lw':RoiManager.get_lw()} - Setting.view = [('color', 'color', 'roi', 'color'), - (int, 'lw', (1,5), 0, 'line width', 'pix')] - return True + para = ROI.default.copy() + view = [('color', 'color', 'line', 'color'), + ('color', 'fcolor', 'face', 'color'), + ('color', 'tcolor', 'text', 'color'), + (int, 'lw', (1,10), 0, 'width', 'pix'), + (int, 'size', (1,30), 0, 'text', 'size'), + (bool, 'fill', 'solid fill')] def run(self, para=None): - RoiManager.set_color(para['color']) - RoiManager.set_lw(para['lw']) + for i in para: ROI.default[i] = para[i] + Source.manager('config').add('roi_style', para) plgs = [SelectAll, SelectNone, '-', Inflate, Shrink, Convex, Box, Clip, Invert, diff --git a/imagepy/tools/Measure/angle2_tol.py b/imagepy/tools/Measure/angle2_tol.py index 3048dee4..8448ea97 100644 --- a/imagepy/tools/Measure/angle2_tol.py +++ b/imagepy/tools/Measure/angle2_tol.py @@ -1,131 +1 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Feb 3 22:21:32 2017 - -@author: yxl -""" - -import wx -from imagepy.core.engine import Tool -import numpy as np -import pandas as pd -from numpy.linalg import norm -from .setting import Setting -#from imagepy import IPy - -class Angle: - """Define the class with line drawing fucntions """ - dtype = 'angle' - def __init__(self, body=None): - self.body = body if body!=None else [] - self.buf = [] - - def addline(self): - line = self.buf - if len(line)!=2 or line[0] !=line[-1]: - self.body.append(line) - self.buf = [] - - def snap(self, x, y, lim): - minl, idx = 1000, None - for i in self.body: - for j in i: - d = (j[0]-x)**2+(j[1]-y)**2 - if d < minl:minl,idx = d,(i, i.index(j)) - return idx if minl**0.52: - self.body.append(line) - self.buf = [] - - def snap(self, x, y, lim): - minl, idx = 1000, None - for i in self.body: - for j in i: - d = (j[0]-x)**2+(j[1]-y)**2 - if d < minl:minl,idx = d,(i, i.index(j)) - return idx if minl**0.5lim:return None - return cur, cur.index(cid) - - def pick(self, x, y, lim): - rst = self.snap(x, y, lim) - if rst!=None:return rst - for i in self.body: - if Polygon(i).contains(Point(x,y)): - return True, i - return None - - def draged(self, ox, oy, nx, ny, i): - self.update, self.infoupdate(), True - if i[0]==True: - for j in range(len(i[1])): - i[1][j] = (i[1][j][0]+(nx-ox), i[1][j][1]+(ny-oy)) - else: - i[0][i[1]] = (nx, ny) - #print('drag,',i,self.body[0][0][i]hasattr(ips.roi, 'topolygon')) - if i[1]==0:i[0][-1] = (nx,ny) - if i[1]==len(i[0])-1: - i[0][0] = (nx, ny) - - def report(self, title): - rst = [] - titles = ['Area', 'OX', 'OY'] - for pg in self.body: - plg = Polygon(pg) - area, xy = plg.area, plg.centroid - unit = 1 if self.unit==None else self.unit[0] - axy = [area*unit**2, xy.x*unit, xy.y*unit] - rst.append([round(i,1) for i in axy]) - IPy.show_table(pd.DataFrame(rst, columns=titles), title) - - def draw(self, dc, f, **key): - dc.SetPen(wx.Pen(Setting['color'], width=1, style=wx.SOLID)) - dc.SetTextForeground(Setting['tcolor']) - font = wx.Font(10, wx.FONTFAMILY_DEFAULT, - wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) - - dc.SetFont(font) - dc.DrawLines([f(*i) for i in self.buf]) - for i in self.buf:dc.DrawCircle(f(*i),2) - - for pg in self.body: - plg = Polygon(pg) - dc.DrawLines([f(*i) for i in pg]) - for i in pg: dc.DrawCircle(f(*i),2) - area, xy = plg.area, plg.centroid - if self.unit!=None: - area *= self.unit[0]**2 - dc.DrawText('%.1f'%area, f(xy.x, xy.y)) - -class Plugin(Tool): - """Define the area class plugin with some events callback fucntions """ - title = 'Area' - def __init__(self): - self.curobj = None - self.doing = False - - def mouse_down(self, ips, x, y, btn, **key): - if key['ctrl'] and key['alt']: - if isinstance(ips.mark, Area): - ips.mark.report(ips.title) - return - - lim = 5.0/key['canvas'].scale - if btn==1: - if not self.doing: - if isinstance(ips.mark, Area): - self.curobj = ips.mark.pick(x, y, lim) - if not self.curobj in (None,True):return - if not isinstance(ips.mark, Area): - ips.mark = Area(unit=ips.unit) - self.doing = True - elif isinstance(ips.mark, Area): - if key['shift']: self.oper,self.doing = '+',True - elif self.curobj: return - else: ips.mark=None - if self.doing: - ips.mark.addpoint((x,y)) - self.curobj = (ips.mark.buf, -1) - self.odx, self.ody = x, y - - elif btn==3: - if self.doing: - ips.mark.addpoint((x,y)) - self.doing = False - ips.mark.commit() - ips.update() - - def mouse_move(self, ips, x, y, btn, **key): - if not isinstance(ips.mark, Area):return - lim = 5.0/key['canvas'].scale - if btn==None: - self.cursor = wx.CURSOR_CROSS - if ips.mark.snap(x, y, lim)!=None: - self.cursor = wx.CURSOR_HAND - elif btn==1: - ips.mark.draged(self.odx, self.ody, x, y, self.curobj) - ips.update() - self.odx, self.ody = x, y - - def mouse_up(self, ips, x, y, btn, **key): - self.curobj = None - - def on_switch(self): - print('AreaTool_Plugin') \ No newline at end of file +from sciapp.action import AreaTool as Plugin \ No newline at end of file diff --git a/imagepy/tools/Measure/coordinate_tol.py b/imagepy/tools/Measure/coordinate_tol.py index 3eb58151..f48bb672 100644 --- a/imagepy/tools/Measure/coordinate_tol.py +++ b/imagepy/tools/Measure/coordinate_tol.py @@ -1,99 +1 @@ -# -*- coding: utf-8 -*- -""" -Created on Thu Feb 2 23:04:46 2017 - -@author: yxl -""" - -import wx -from imagepy.core.engine import Tool -from .setting import Setting -#from imagepy import IPy -import pandas as pd - -class Coordinate: - """Define the coordinate class""" - dtype = 'coordinate' - def __init__(self, body=None, unit=None): - self.body = body if body!=None else [] - self.unit = unit - - def add(self, p): - self.body.append(p) - - def snap(self, x, y, lim): - cur, minl = None, 1000 - for i in self.body: - d = (i[0]-x)**2+(i[1]-y)**2 - if d < minl:cur,minl = i,d - if minl**0.5>lim:return None - return self.body.index(cur) - - def pick(self, x, y, lim): - return self.snap(x, y, lim) - - def draged(self, ox, oy, nx, ny, i): - self.body[i] = (nx, ny) - - def draw(self, dc, f, **key): - dc.SetPen(wx.Pen(Setting['color'], width=1, style=wx.SOLID)) - dc.SetTextForeground(Setting['tcolor']) - font = wx.Font(10, wx.FONTFAMILY_DEFAULT, - wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) - - dc.SetFont(font) - for i in self.body: - x,y = f(*i) - unit = 1 if self.unit is None else self.unit[0] - dc.DrawCircle(x, y, 2) - dc.DrawText('(%.1f,%.1f)'%(i[0]*unit, i[1]*unit), x, y) - - def report(self, title): - unit = 1 if self.unit is None else self.unit[0] - rst = [(x*unit, y*unit) for x,y in self.body] - titles = ['OX', 'OY'] - IPy.show_table(pd.DataFrame(rst, columns=titles), title) - -class Plugin(Tool): - """Define the coordinate class plugin with the event callback functions""" - title = 'Coordinate' - def __init__(self): - self.curobj = None - self.odx, self.ody = 0, 0 - - def mouse_down(self, ips, x, y, btn, **key): - if key['ctrl'] and key['alt']: - if isinstance(ips.mark, Coordinate): - ips.mark.report(ips.title) - return - lim = 5.0/key['canvas'].scale - if btn==1: - if isinstance(ips.mark, Coordinate): - self.curobj = ips.mark.pick(x, y, lim) - if self.curobj!=None:return - if not isinstance(ips.mark, Coordinate): - ips.mark = Coordinate(unit=ips.unit) - elif not key['shift']: - ips.mark = Coordinate(unit=ips.unit) - ips.mark.add((x,y)) - self.curobj = ips.mark.pick(x,y, lim) - ips.update() - self.odx, self.ody = x, y - - def mouse_up(self, ips, x, y, btn, **key): - self.curobj = None - - def mouse_move(self, ips, x, y, btn, **key): - if not isinstance(ips.mark, Coordinate):return - lim = 5.0/key['canvas'].scale - if btn==None: - self.cursor = wx.CURSOR_CROSS - if ips.mark.snap(x, y, lim)!=None: - self.cursor = wx.CURSOR_HAND - elif btn==1: - ips.mark.draged(self.odx, self.ody, x, y, self.curobj) - ips.update() - self.odx, self.ody = x, y - - def mouse_wheel(self, ips, x, y, d, **key): - pass \ No newline at end of file +from sciapp.action import CoordinateTool as Plugin \ No newline at end of file diff --git a/imagepy/tools/Measure/distance_tol.py b/imagepy/tools/Measure/distance_tol.py index 3901add6..6e646ede 100644 --- a/imagepy/tools/Measure/distance_tol.py +++ b/imagepy/tools/Measure/distance_tol.py @@ -1,130 +1 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Feb 3 22:21:32 2017 - -@author: yxl -""" - -import wx -from imagepy.core.engine import Tool -import numpy as np -import pandas as pd -from numpy.linalg import norm -from .setting import Setting -#from imagepy import IPy - -class Distance: - """Define the distance class""" - dtype = 'distance' - def __init__(self, body=None, unit=None): - self.body = body if body!=None else [] - self.buf, self.unit = [], unit - - def addline(self): - line = self.buf - if len(line)!=2 or line[0] !=line[-1]: - self.body.append(line) - self.buf = [] - - def snap(self, x, y, lim): - minl, idx = 1000, None - for i in self.body: - for j in i: - d = (j[0]-x)**2+(j[1]-y)**2 - if d < minl:minl,idx = d,(i, i.index(j)) - return idx if minl**0.50: + rms = [i for i in shp.body if isinstance(i, Texts)] + for i in rms: shp.body.remove(i) + shp.body.append(Texts(txt)) + shp.dirty = True + if not (key['shift'] or key['alt'] or key['ctrl']): + key['canvas'].fit() + + def mouse_up(self, shp, x, y, btn, **key): + self.status = '' + if btn==1: + self.pick_m = self.pick_obj = None + if not (key['alt'] and key['ctrl']): return + pts = mark(shp) + if len(pts)>0: + pts = Points(np.vstack(pts), color=(255,0,0)) + key['canvas'].marks['anchor'] = pts + shp.dirty = True + + def mouse_move(self, shp, x, y, btn, **key): + self.cursor = 'arrow' + if self.status == 'move': + ox, oy = self.oldxy + up = (1,-1)[key['canvas'].up] + key['canvas'].move(key['px']-ox, (key['py']-oy)*up) + self.oldxy = key['px'], key['py'] + if key['alt'] and key['ctrl']: + self.status = 'pick' + if not 'anchor' in key['canvas'].marks: + pts = mark(shp) + if len(pts)>0: + pts = Points(np.vstack(pts), color=(255,0,0)) + key['canvas'].marks['anchor'] = pts + if 'anchor' in key['canvas'].marks: + m, obj, l = pick_point(key['canvas'].marks['anchor'], x, y, 5) + if not m is None: self.cursor = 'hand' + elif 'anchor' in key['canvas'].marks: + self.status = '' + del key['canvas'].marks['anchor'] + shp.dirty = True + if not self.pick_obj is None and not self.pick_m is None: + drag(self.pick_m, self.pick_obj, x, y) + txt = measure(shp) + if len(txt)>0: + rms = [i for i in shp.body if isinstance(i, Texts)] + for i in rms: shp.body.remove(i) + shp.body.append(Texts(txt)) + pts = mark(self.pick_m) + if len(pts)>0: + pts = np.vstack(pts) + key['canvas'].marks['anchor'] = Points(pts, color=(255,0,0)) + self.pick_m.dirty = True + shp.dirty = True + if self.pick_obj is None and not self.pick_m is None: + offset(self.pick_m, x-self.p[0], y-self.p[1]) + pts = mark(self.pick_m) + if len(pts)>0: + pts = np.vstack(pts) + key['canvas'].marks['anchor'] = Points(pts, color=(255,0,0)) + txt = measure(shp) + if len(txt)>0: + rms = [i for i in shp.body if isinstance(i, Texts)] + for i in rms: shp.body.remove(i) + shp.body.append(Texts(txt)) + self.p = x, y + self.pick_m.dirty = shp.dirty = True + + def mouse_wheel(self, shp, x, y, d, **key): + if d>0: key['canvas'].zoomout(x, y, coord='data') + if d<0: key['canvas'].zoomin(x, y, coord='data') + +class Coordinate(Point): mtype = 'coordinate' + +class Distance(Line): mtype = 'distance' + +class Area(Polygon): mtype = 'area' + +class Angle(Line): mtype = 'angle' + +class Slope(Line): mtype = 'slope' + +class BaseMeasure(ImageTool): + def __init__(self, base): + base.__init__(self) + self.base = base + + def mouse_down(self, img, x, y, btn, **key): + if img.mark is None: img.mark = Measure() + self.base.mouse_down(self, img.mark, x, y, btn, **key) + + def mouse_up(self, img, x, y, btn, **key): + self.base.mouse_up(self, img.mark, x, y, btn, **key) + if not img.mark is None: + if len(img.mark.body)==0: img.mark = None + + def mouse_move(self, img, x, y, btn, **key): + self.base.mouse_move(self, img.mark, x, y, btn, **key) + + def mouse_wheel(self, img, x, y, d, **key): + self.base.mouse_wheel(self, img.mark, x, y, d, **key) + +def inbase(key, btn): + status = key['ctrl'], key['alt'], key['shift'] + return status == (1,1,0) or btn in {2,3} + +class PointEditor(BaseEditor): + title = 'Point Tool' + def __init__(self): + BaseEditor.__init__(self) + + def mouse_down(self, shp, x, y, btn, **key): + if inbase(key, btn): + BaseEditor.mouse_down(self, shp, x, y, btn, **key) + if btn==1 and not key['alt'] and not key['ctrl']: + shp.body.append(Coordinate([x,y])) + txt = measure(shp) + if isinstance(shp.body[0], Texts): shp.body.pop(0) + shp.body.insert(0, Texts(txt)) + shp.dirty = True + +class LineEditor(BaseEditor): + title = 'Line Tool' + def __init__(self, tp): + BaseEditor.__init__(self) + self.cur, self.n, self.obj, self.tp = 0, 0, None, tp + + def mouse_down(self, shp, x, y, btn, **key): + if inbase(key, btn) and self.obj is None: + BaseEditor.mouse_down(self, shp, x, y, btn, **key) + if key['alt'] and key['ctrl']: return + if btn==1: + if self.obj is None: + self.obj = self.tp([(x,y)]) + shp.body.append(self.obj) + else: + self.obj.body = np.vstack((self.obj.body, [(x,y)])) + anchor = Points(mark(self.obj)[0], color=(255,0,0)) + key['canvas'].marks['buffer'] = anchor + if btn==3 and not self.obj is None : + self.obj.body = np.vstack((self.obj.body, [(x,y)])) + self.obj.dirty, shp.dirty, self.obj = True, True, None + del key['canvas'].marks['buffer'] + txt = measure(shp) + if len(txt)>0: + if isinstance(shp.body[0], Texts): shp.body.pop(0) + shp.body.insert(0, Texts(txt)) + shp.dirty = True + +class AreaEditor(BaseEditor): + title = 'Polygon Tool' + def __init__(self): + BaseEditor.__init__(self) + self.cur, self.n, self.obj = 0, 0, None + + def mouse_down(self, shp, x, y, btn, **key): + if inbase(key, btn) and self.obj is None: + BaseEditor.mouse_down(self, shp, x, y, btn, **key) + if key['alt'] and key['ctrl']: return + if btn==1: + if self.obj is None: + self.obj = Line([(x,y)]) + shp.body.append(self.obj) + else: + self.obj.body = np.vstack((self.obj.body, [(x,y)])) + anchor = Points(mark(self.obj)[0], color=(255,0,0)) + key['canvas'].marks['buffer'] = anchor + if btn==3 and not self.obj is None : + body = np.vstack((self.obj.body, [(x,y)])) + shp.body[-1] = Area(body) + txt = measure(shp) + if len(txt)>0: + if isinstance(shp.body[0], Texts): shp.body.pop(0) + shp.body.insert(0, Texts(txt)) + self.obj, shp.dirty = None, True + del key['canvas'].marks['buffer'] + shp.dirty = True + +class DistanceEditor(LineEditor): + def __init__(self): LineEditor.__init__(self, Distance) + +class AngleEditor(LineEditor): + def __init__(self): LineEditor.__init__(self, Angle) + +class SlopEditor(LineEditor): + def __init__(self): LineEditor.__init__(self, Slope) + +class CoordinateTool(BaseMeasure): + title = 'Coordinate Tool' + def __init__(self): + BaseMeasure.__init__(self, PointEditor) + +class DistanceTool(BaseMeasure): + title = 'Distance Tool' + def __init__(self): + BaseMeasure.__init__(self, DistanceEditor) + +class AngleTool(BaseMeasure): + title = 'Angle Tool' + def __init__(self): + BaseMeasure.__init__(self, AngleEditor) + +class SlopeTool(BaseMeasure): + title = 'Slop Tool' + def __init__(self): + BaseMeasure.__init__(self, SlopEditor) + +class AreaTool(BaseMeasure): + title = 'Area Tool' + def __init__(self): + BaseMeasure.__init__(self, AreaEditor) \ No newline at end of file diff --git a/sciapp/action/shpbase.py b/sciapp/action/shpbase.py index e8ab631b..f1840ec2 100644 --- a/sciapp/action/shpbase.py +++ b/sciapp/action/shpbase.py @@ -66,7 +66,6 @@ def pick_obj(shp, x, y, lim, types='all'): if d Date: Tue, 9 Jun 2020 13:02:18 +0800 Subject: [PATCH 230/343] all ok --- imagepy/__main__.py | 29 +++--- imagepy/core/app/imagepy.py | 61 ++++++++----- imagepy/core/engine/filter.py | 16 ++-- imagepy/core/engine/free.py | 9 +- imagepy/core/engine/report.py | 4 +- imagepy/core/engine/simple.py | 9 +- imagepy/core/engine/table.py | 9 +- imagepy/data/config.json | 2 +- imagepy/data/logo.ico | Bin 0 -> 16958 bytes imagepy/data/logolong.png | Bin 0 -> 11801 bytes imagepy/data/watermark.png | Bin 0 -> 4867 bytes .../menus/Plugins/Install/installplg_plgs.py | 11 +-- sciapp/action/free.py | 5 +- sciapp/action/meaact.py | 85 ++---------------- sciapp/action/simple.py | 5 +- sciapp/app.py | 7 ++ sciapp/object/__init__.py | 2 +- sciapp/object/roi.py | 60 +++++++++++++ sciwx/canvas/canvas.py | 14 +-- sciwx/canvas/mcanvas.py | 32 ++++--- sciwx/widgets/__init__.py | 1 + sciwx/widgets/progressbar.py | 54 +++++++++++ sciwx/widgets/toolbar.py | 1 - 23 files changed, 239 insertions(+), 177 deletions(-) create mode 100644 imagepy/data/logo.ico create mode 100644 imagepy/data/logolong.png create mode 100644 imagepy/data/watermark.png create mode 100644 sciwx/widgets/progressbar.py diff --git a/imagepy/__main__.py b/imagepy/__main__.py index 17d715dd..7987bd46 100644 --- a/imagepy/__main__.py +++ b/imagepy/__main__.py @@ -25,29 +25,30 @@ def extend_wgts(wgt): if __name__ == '__main__': from skimage.data import camera, astronaut + import wx.lib.agw.advancedsplash as AS app = wx.App(False) + + bitmap = wx.Bitmap('data/logolong.png', wx.BITMAP_TYPE_PNG) + shadow = wx.Colour(255,255,255) + # SplashScreen(bitmap, wx.adv.SPLASH_CENTRE_ON_SCREEN | wx.adv.SPLASH_TIMEOUT, 3000, None, -1) + + asp = AS.AdvancedSplash(None, bitmap=bitmap, timeout=1000, + agwStyle=AS.AS_TIMEOUT | + AS.AS_CENTER_ON_PARENT | + AS.AS_SHADOW_BITMAP, + shadowcolour=shadow) + + frame = ImagePy(None) frame.load_menu(extend_plgs(loader.build_plugins('menus'))) - #frame.load_menu(('menu',[('File',[('Open', Open), - # ('Save', Save)]), - # ('Filters', [('Gaussian', Gaussian), - # ('Undo', Undo)])])) frame.load_tool(extend_tols(loader.build_tools('tools')), 'Transform') - #frame.load_tool(('tools',[('standard', [('P', Pencil), - # ('D', DefaultTool)]), - # ('draw', [('X', Pencil), - # ('X', Pencil)])]), 'draw') frame.load_widget(extend_wgts(loader.build_widgets('widgets'))) - # ('widgets', [('Histogram', [('Histogram', Histogram), - # ('Curve', Curve), - # ('Channels', Channels)]), - # ('Navigator', [('Viewport', ViewPort)])]) - frame.show_img([camera()], 'camera') - frame.show_img([astronaut()], 'astronaut') + #frame.show_img([camera()], 'camera') + #frame.show_img([astronaut()], 'astronaut') frame.Show() app.MainLoop() \ No newline at end of file diff --git a/imagepy/core/app/imagepy.py b/imagepy/core/app/imagepy.py index 01bfd32a..8bfd85bd 100644 --- a/imagepy/core/app/imagepy.py +++ b/imagepy/core/app/imagepy.py @@ -4,6 +4,7 @@ import wx.lib.agw.aui as aui from sciwx.widgets import MenuBar, ToolBar, ChoiceBook, ParaDialog from sciwx.canvas import CanvasNoteBook +from sciwx.widgets import ProgressBar from sciwx.grid import GridNoteBook from sciwx.mesh import Canvas3DNoteBook from sciwx.text import MDNoteFrame, TextNoteFrame @@ -24,6 +25,9 @@ def __init__( self, parent ): self.auimgr.SetManagedWindow( self ) self.SetSizeHints( wx.Size(1024,768) ) + logopath = os.path.join(root_dir, 'data/logo.ico') + self.SetIcon(wx.Icon(logopath, wx.BITMAP_TYPE_ICO)) + self.init_menu() self.init_tool() self.init_canvas() @@ -52,8 +56,9 @@ def init_status(self): self.txt_info = wx.StaticText( stapanel, wx.ID_ANY, "ImagePy v0.2", wx.DefaultPosition, wx.DefaultSize, 0 ) self.txt_info.Wrap( -1 ) sizersta.Add( self.txt_info, 1, wx.ALIGN_BOTTOM|wx.BOTTOM|wx.LEFT|wx.RIGHT, 2 ) - self.pro_bar = wx.Gauge( stapanel, wx.ID_ANY, 100, wx.DefaultPosition, wx.Size( 100,15 ), wx.GA_HORIZONTAL ) - sizersta.Add( self.pro_bar, 0, wx.ALIGN_BOTTOM|wx.BOTTOM|wx.LEFT|wx.RIGHT, 2 ) + #self.pro_bar = wx.Gauge( stapanel, wx.ID_ANY, 100, wx.DefaultPosition, wx.Size( 100,15 ), wx.GA_HORIZONTAL ) + self.pro_bar = ProgressBar(stapanel) + sizersta.Add( self.pro_bar, 0, wx.ALL, 2 ) stapanel.SetSizer(sizersta) self.auimgr.AddPane( stapanel, aui.AuiPaneInfo() .Bottom() .CaptionVisible( False ).PinButton( True ) .PaneBorder( False ).Dock().Resizable().FloatingSize( wx.DefaultSize ).DockFixed( True ) @@ -82,11 +87,28 @@ def init_tool(self): self.auimgr.AddPane(self.toolbar, aui.AuiPaneInfo() .Left() .PinButton( True ) .CaptionVisible( True ).Dock().Resizable().FloatingSize( wx.DefaultSize ).MaxSize(wx.Size( 32,-1 )) . BottomDockable( True ).TopDockable( False ).Layer( 10 ) ) - + + def set_background(self, img): + class ImgArtProvider(aui.AuiDefaultDockArt): + def __init__(self, img): + aui.AuiDefaultDockArt.__init__(self) + self.bitmap = wx.Bitmap(img, wx.BITMAP_TYPE_PNG) + + def DrawBackground(self, dc, window, orient, rect): + aui.AuiDefaultDockArt.DrawBackground(self, dc, window, orient, rect) + + memDC = wx.MemoryDC() + memDC.SelectObject(self.bitmap) + w, h = self.bitmap.GetWidth(), self.bitmap.GetHeight() + dc.Blit((rect[2]-w)//2, (rect[3]-h)//2, w, h, memDC, 0, 0, wx.COPY, True) + self.canvasnb.GetAuiManager().SetArtProvider(ImgArtProvider(img)) + def init_canvas(self): self.canvasnbwrap = wx.Panel(self) sizer = wx.BoxSizer( wx.VERTICAL ) - self.canvasnb = CanvasNoteBook( self.canvasnbwrap) + self.canvasnb = CanvasNoteBook(self.canvasnbwrap) + self.set_background(root_dir+'/data/watermark.png') + sizer.Add( self.canvasnb, 1, wx.EXPAND |wx.ALL, 0 ) self.canvasnbwrap.SetSizer( sizer ) self.canvasnbwrap.Layout() @@ -124,6 +146,18 @@ def init_mesh(self): self.meshnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_new_mesh) self.meshnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close_mesh) + def add_task(self, task): + self.task_manager.add(task.title, task) + tasks = self.task_manager.gets() + tasks = [(p.title, lambda t=p:p.prgs) for n,p,t in tasks] + self.pro_bar.SetValue(tasks) + + def remove_task(self, task): + self.task_manager.remove(obj=task) + tasks = self.task_manager.gets() + tasks = [(p.title, lambda t=p:p.prgs) for n,p,t in tasks] + self.pro_bar.SetValue(tasks) + def init_widgets(self): self.widgets = ChoiceBook(self) self.auimgr.AddPane( self.widgets, aui.AuiPaneInfo() .Right().Caption('Widgets') .PinButton( True ) @@ -359,25 +393,6 @@ def show_para(self, title, view, para, on_handle=None, on_ok=None, on_cancel=Non dialog.Bind('commit', on_ok) return dialog.show() -class P: - def __init__(self, name): - self.name = name - - def start(self): - print(self.name) - - def __call__(self): - return self - -data = ('menu', [ - ('File', [('Open', P('O')), - '-', - ('Close', P('C'))]), - ('Edit', [('Copy', P('C')), - ('A', [('B', P('B')), - ('C', P('C'))]), - ('Paste', P('P'))])]) - if __name__ == '__main__': import numpy as np import pandas as pd diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index 3665f73d..0a4d6a29 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -9,7 +9,7 @@ import numpy as np from sciapp import Source -from time import time +from time import time, sleep def process_channels(plg, ips, src, des, para): if ips.channels>1 and not 'not_channel' in plg.note: @@ -24,7 +24,7 @@ def process_channels(plg, ips, src, des, para): return des def process_one(plg, ips, src, img, para, callafter=None): - Source.manager('task').add(plg.title, plg) + plg.app.add_task(plg) start = time() transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16) transfloat = '2float' in plg.note and not ips.dtype in (np.complex128, np.float32, np.float64) @@ -43,13 +43,12 @@ def process_one(plg, ips, src, img, para, callafter=None): img[msk] = src[msk] plg.app.info('%s: cost %.3fs'%(ips.title, time()-start)) ips.update() - Source.manager('task').remove(plg) + plg.app.remove_task(plg) if not callafter is None:callafter() def process_stack(plg, ips, src, imgs, para, callafter=None): - Source.manager('task').add(plg.title, plg) + plg.app.add_task(plg) start = time() - transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16) transfloat = '2float' in plg.note and not ips.dtype in (np.complex128, np.float32, np.float64) if transint: @@ -74,7 +73,7 @@ def process_stack(plg, ips, src, imgs, para, callafter=None): i[msk] = src[msk] plg.app.info('%s: cost %.3fs'%(ips.title, time()-start)) ips.update() - Source.manager('task').remove(plg) + plg.app.remove_task(plg) if not callafter is None:callafter() @@ -85,12 +84,11 @@ class Filter: 'all, 8-bit, 16-bit, int, rgb, float, not_channel, not_slice, req_roi, auto_snap, auto_msk, preview, 2int, 2float' para = None view = None - prgs = (None, 1) + prgs = None def __init__(self, ips=None): pass - def progress(self, i, n): - self.prgs = (i, n) + def progress(self, i, n): self.prgs = int(i*100/n) def show(self): preview = lambda para, ips=self.ips: self.preview(ips, para) or ips.update() diff --git a/imagepy/core/engine/free.py b/imagepy/core/engine/free.py index 150bea41..c502db18 100644 --- a/imagepy/core/engine/free.py +++ b/imagepy/core/engine/free.py @@ -10,21 +10,20 @@ class Free: title = 'Free' view = None para = None - prgs = (None, 1) + prgs = None asyn = True - def progress(self, i, n): - self.prgs = (i, n) + def progress(self, i, n): self.prgs = int(i*100/n) def run(self, para=None): print('this is a plugin') def runasyn(self, para, callback=None): - self.app.manager('task').add(self.title, self) + self.app.add_task(self) start = time() self.run(para) self.app.info('%s: cost %.3fs'%(self.title, time()-start)) - self.app.manager('task').remove(self) + self.app.remove_task(self) if callback!=None:callback() def load(self):return True diff --git a/imagepy/core/engine/report.py b/imagepy/core/engine/report.py index 0df31300..2e7240ae 100644 --- a/imagepy/core/engine/report.py +++ b/imagepy/core/engine/report.py @@ -19,7 +19,7 @@ def __init__(self, title, cont): def __call__(self): return self def runasyn(self, wb, info, key, para = None, callback = None): - Source.manager('task').add(self) + self.app.add_task(self) for i in para: if i in key and key[i][0] == 'img': ips = ImageManager.get(para[i]) @@ -33,7 +33,7 @@ def runasyn(self, wb, info, key, para = None, callback = None): xlreport.fill_value(wb, info, para) wb.save(para['path']) self.app.set_info('%s: cost %.3fs'%(self.title, time()-start)) - Source.manager('task').remove(self) + self.app.remove_task(self) if callback!=None:callback() def start(self, para=None, callafter=None): diff --git a/imagepy/core/engine/simple.py b/imagepy/core/engine/simple.py index 82abe296..3f9f79b5 100644 --- a/imagepy/core/engine/simple.py +++ b/imagepy/core/engine/simple.py @@ -17,13 +17,12 @@ class Simple: para = None 'all, 8-bit, 16-bit, rgb, float, req_roi, stack, stack2d, stack3d, preview' view = None - prgs = (None, 1) + prgs = None modal = True def __init__(self): pass - def progress(self, i, n): - self.prgs = (i, n) + def progress(self, i, n): self.prgs = int(i*100/n) def load(self, ips):return True @@ -48,12 +47,12 @@ def ok(self, ips, para=None, callafter=None): self.app.record_macros('{}>{}'.format(self.title, para)) def runasyn(self, ips, imgs, para = None, callback = None): - self.app.manager('task').add(self.title, self) + self.app.add_task(self) start = time() self.run(ips, imgs, para) self.app.info('%s: cost %.3fs'%(ips.title, time()-start)) ips.update() - self.app.manager('task').remove(self) + self.app.remove_task(self) if callback!=None:callback() def check(self, ips): diff --git a/imagepy/core/engine/table.py b/imagepy/core/engine/table.py index 7dfe461f..46f259a3 100644 --- a/imagepy/core/engine/table.py +++ b/imagepy/core/engine/table.py @@ -13,7 +13,7 @@ class Table: para = None 'req_sel, req_row, req_col, auto_snap, auto_msk, msk_not, num_only, preview' view = None - prgs = (None, 1) + prgs = None modal = True asyn = True @@ -21,8 +21,7 @@ def __init__(self, tps=None): self.dialog = None - def progress(self, i, n): - self.prgs = (i, n) + def progress(self, i, n): self.prgs = int(i*100/n) def load(self, tps):return True @@ -53,12 +52,12 @@ def ok(self, tps, para=None, callafter=None): if win!=None: win.write('{}>{}'.format(self.title, para)) def runasyn(self, tps, snap, data, para = None, callback = None): - self.app.manager('task').add(self,title, self) + self.app.add_task(self) start = time() self.run(tps, data, snap, para) self.app.set_info('%s: cost %.3fs'%(tps.title, time()-start)) tps.update() - self.app.manager('task').remove(self) + self.app.remove_task(self) if callback!=None:callback() def check(self, tps): diff --git a/imagepy/data/config.json b/imagepy/data/config.json index dab632db..062246ba 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [128, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null]] \ No newline at end of file +[["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null]] \ No newline at end of file diff --git a/imagepy/data/logo.ico b/imagepy/data/logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..616ad78e2db4a40456a62b417dbfad1004655591 GIT binary patch literal 16958 zcmeHP33yf2wGPtOzP3->_xh@RwNM`lNzS?VCU?k81_&WQ2w^Z`2=hFrj9~}~LqUPwV~vzW2mKk{bg8eZKF-@37C=`wZ(} zYwb1def9C_hX10Yeemy3e0=-)`24`f$Hx~3x_q*5u;1ICi1_^far1c4v#0fYefn7c ztB)QS;Hw8jpbhG++x>g#dXL_{d*AwhNf!S80loeG?bCFN^H6|2Ffvy z3~saq1Yg2Ef5CH4XqtV#rUeAwefQnp{3`Iq&(H4EEY7XKaE>^GgCpzjOCH-id(M1W zwQ7xQ-MU@&?%gK`4jho;;$kT%ER?*wJlVZ_kE~yxBTJSnmnl585SjukiHTU)nEF?#>=3@5i()I6j``%k>uoTl7fN)#gmB> zr;0PUuRtF}w+D*`UT6WX!@j-&cmFf_0Nl4wzagKq0f8ZnQ>M&Nm`h4ZWbN8)Ng16g zPFI9mh9Ti%9hE5hyz!!E&K5mqf#};8i@s~A=$q0+UpH6uB~!$bI#O)0{ejUbR(psH z22ZFH`}gmctgLl1Xy|a_7fW!s;)iB+ZU5m91HOH;`9Rx+xq6E8b@uG}lAnJ7dc0p| z%$y_6pon_P%3^hiHg34+Piz#+Yk609Jiklyed(glA1{`W2(bhN$;jlfvSrIw*^DD0 zX_Q+Z93i3yxX%0e1xDWt9(2?7Z&)m@Ex^|@di40_efx5OJzpkGp4I|AXvA?ww>q0N z=&4?madnuz@#y7!qUWy^Yf6&RCF;bME!!j$`Vbb`U#!khw@tM;GNF^*ZWJFl-(c6u zoUZUj>LTa%;>F9v?g*>5*<9}_TW@n!YtVK5@oSK|H~gl4{gL%z88<@BkJJg1W#7Ji z!j|(zhmL4A*JC%rkJ7GNZNV1?4Ib98FE>|mbMs{A&=C#L@j8Fqp3ZM-7FVU_gg$!F z_kh>)BiW=)m^I6}n&n}Lwxwd-jUFt_4}bM%<;E%aF%Hf$89Gos#V4LU&|Pkn<;dYyKu ztM(|}dGtBSkqdEG+=7_#wflLR+vlVU~r|xD#p7tAvxh$2CoV+5P45jN~6ZE5#*4V*s>w1+e@er0SP91_WR zrm*e(y?R;i&}`27MO)IO+Jk)~Z1?rVb1mz!AWPh@w{7cteW9}Q7AQ#Jvm7U9izF!NyH{uyReVPQi!jwIXMs8tY zE~VM0wv}PqYg<6jQ*i@_HgL|bSh*T8th3SA*XrB$K3<0fM?CS)i@Ox|JlOLaf#aIr z`AV)>hr~89TdHVBL z%L>J2iqWo#X2iIb7j}!~_YWvPVtF<9OY@VqKtpW6ST$wr1h-GL*mJz$L2Uc%u%uB< zls$7A+W(I9p3hgEF0fX?#(#RUKn8Xar**~)J#VFGqlXD&M&=7zP?%`JI9!P51CTp~ zA$MFcUG$%?y=GqM^HL=W`cHqrI0;8XM>>GHaW8ZCO`9nD0>t#84d|QgjaQKS5z2no z`ngw4o3=U4ov+y4^$mWv(<3TMG}syaVCGeSOMiNs$_p|OAE%5N@74j$zN9TKi^Z7} z9X(LZd)6Od2R`S9}f(I%oC;j1htN`~u*n z?ngz()neXnAdi641>g3k>WZQ`gJx$b#;xrQ2L@QOk1jI3FMcWC**k-`K|Cje81u!=Rg=@W6}&i7i!>9 zH9Pb%muG#B<9>d&K5qQQZxSnHK9Sc|CkyVtbpwv-_yI9XRP2WuPN(!hUW}3WUf2|w zGG&&WIrBj~ydCkt=&P)J5B0OziVu8`7LM5NsjZ!V*Tf@h+yI#|V~$LpFQ}%P_E)dA)U1F^+&;KE@<2av{m8J5Cn$yJ#RbNGM0dqg(81>qmah=kk z^^H{6smnWe?Y%1827aS`FKM4bxYWR zCgcl=3V(UgT;j+4-)-P;+d>zAPkq!M%x*`A{_GB=r>Ko8-8JaH9_@(}C&2^coXEYj zX`|bH+l*bEvxWw{!M>)@Yx{_XPBHj_tABcy_t7Mj~;XT57rttE^PPxw)9vB4V1a_7RngJLKfIk z%-;mq!4P8RIt1&9i2v&84~P%gvx&c|ykwDqx1*NNGlsFewD)rwO2NyZP{Eo2-d%Ft zu^W7N_>seIn;01-`r!?qf8S*L3_j>nlVvoplLt1`h`43}+cy|~Ma6uJ7A=MC3wh7L zSW{8FmiTG&jJ}Rq$an+&y8giDen2l=t!#csXtdCe8T8(WcJbnL(k)&BqsSS#2{k}FeMbWf%>&_dB zo&03{5E6nIku^BPdxpH*w)&b`5z~*YwQidu1b3Wj`(%js9Q}FXU2}4J+_AT@~ zZDr4Eef@k1?-wVlGmu9h7V-6USW(X#$~7#?f8P8w%D>FO2){R8$-km_kI~mztLe_i zD0BUxY~^3aPnddBup2gou`+RKQ<6KzpD{~f;QN*>UqL@Fy?XV!o%L<%0_*x*%YhyF zz`$5tQ9RJC|0RcwzRp^#(+8r)P@@eWg1p-$>FF6a6+ijF9EvjNQ2tn7ck%umJ9o*tb@!8hP4ItT;}~N+;>U5KGu-AeO}9TuJD{Nkxd{161V1_F6YG z_mJ1IVeqQ2sB!{HkPU=cwF_`v+F8%9LL}{Tpd$ zX#B!wz3J4%iKrPNCcI(}aIFk;o;uEWRJXcXV>%t{*#qORIld-N82s&p^PL|CXGh|!l zOj(3{DF}72Aw!bn`0j8f{lAXU!jM_Y@^R;KhKehcNu*~i+5g_ zGdHar{oA(flKwIM|y8?GxKJ3{rSMlI#^`N%4R>e0O_S6`2z#0`TjBn!xqK;^F z?lWjKTI7+35{8aYc>wOIgMDcA`IpNI{6JeXj*FL%6<@LU>OxuZkI~P1P*vM8&${^) zx!=bhe*&Ekkt4;cJH?LosqvL*6J)@E!B?F_poS;f0_1s&drC4@?U(EJZrR$M25zH8 zF3?xUzAE|v<_A~4)oO;Jhqj64J z5&NFzUbCX2BF3(D;K@p3yyir~UBFm{qxtgbq6nj(ttU>X$5`EGOfd{4DCRxt#HRfCBGw$mrnY(emqBD@W8+3Id*2FmP`3>WE>R<+P{KzihfB*e6>U~zkJB)Wn zjT+O8e5VZZQgOYRu8z*Jm#r`&qF+60V%Y!D1iH`MeRtq@jeBcO6$gW^S~qRQSFDl2 zt}XG<{7H3Lk6d&tZKE`4{D6906GSn?EhN6+7Q|>^ED~ z_~px&zmNy4w}EEvTWDskhxa#|{kfT@j?Re{^WekelyNF1>KhqbXSKSX2LD?35<%C8 zfv!dz^ow)M?}07IC(SrMt|>1GF^_To4D~AjvOe+Kt&+cafl%%Sc60mYGn-p^aO@d3 z{;H~~PVrY)SBopCugZg0tXRdpiH(@U>&-ND<=o%jW{0dA7QzlPH{|?5%)Hn9Y*j_c zSYRX%T5x0=_P~=j6`_yx*J2%EhWR-^J#cIQbTbMy%l*JlUYK$7oIKgFc2+C?2lpzpLli*Eo@~TD2-e?fK*U#U95E=I1?YB-O+U zoA}=s-zrh_guFoAYz_LUAH?w-_+>Gkg)QnIh`LL7Xas6b?eWtN7=GeI#EAoAV`ahs>t^5q_Wn6@%{&D* zu92Sc;C!#Tyl{x|t*TQ+u^3Z10>;UUC#y~uB~wT4wOD?Pc;xTIN|~!QEsq_r0tfy6 z(}yxttelcEPQ1@cTn=R-!{4I;|&daCszt43s4-Y8sBh{x0137;7_||W`bwSk^1x>D zfV{9HCrnC+S3aMyGUd*g-t6xh8yn@NmtH|kH>Z*IlLzCwA&@&{bfHflho3>)^=KjU z+4x2?*HCxt*ooXewjSR(%edM6{;G~D3TJ@iw=QQ{Jf%Abj0dud8`4abmo8x8HM|iaTKJ6-H4BaWBooqYq?Hae< zg4$FP=HAJzPF0|1pCJhYT*8J)>N4@Ax@~q zT-nCow{`X{ysuLp+;dOB4-hxZ1Ma^<=1ts_J89B1<>QGRx{}h_w{FxJ zYsaV?JWLu`KVuD*zt!p2FTRf0AftDIGl%OeTpuyhLChG-Q#{Zd#pbzyq3&u=3ic^s ze*cbk#}yP&mzp|R)o!uw-GDw-UjwhNh`r3;xLN14wV?AL>_AgkSadz@F!#yNoH!sp5g|I14PlWe!fgVqaKzbPa!Vz~6)oAD+xO zoPO;eSZ8^a@GJwd>W+ID>tnt?1bcawH1nX{3=NB}jgE<{iybhej&p_i1#Jg(MBT&ta9)F0;5aZp zjy07B(f2pcJI-^>@}G(ATE5NSqHw(t^`POf`%~!OaID6$41IIZj)(5V`dR|RF)!BQ zp4Wl*vIq00;6W+I7x?-4{p+=S|Eu~8?c`UL_OA%tAXEKaWT8)_#mA>7+lxLvj9erO z|LXuW==Sl65@}-FQw{t0AcU>tHEt|r3sGf7^8^uX7Te60?(VkfR$lr<)!nYzo^?G` zd;3MBja1jSR<7~1t5w^*RDI8)wR#MD0h2HYn!Dyz7f^7 zE%o#pZKda0wcT!s0)7CxrKh{i?ir%4vwK9i+uQD9+kNd01)%#Ma&A-CrLS#gdu!=S zIjXIk{8ARLD<`S;_KN}UmE!vLx;xV<(e|wSPDmQssHWS#$qz)e-1@cepv{zS*=WGW ztkShv7^9`Fedg?G4=w1D0**yIG;Rv3pgunS- zrf`y2tNY5ztHcVG&C&bSY@Y-we(+4QFa_SBI5GH+ELZs&DvD(rO3XSSmu#X>P>5h3 zv_HGJDcxE2`s7n_-Ups@x+hKZ z%Wd10)QJ0*1%Ve44&~t1Szn`DA@+|NDs&3NjVOTU>X%2oBH94=AZ(i&xP}LiItM#f z39ezn2Z2`Cq+xG1SwWysoVRqan^KC2K>(2cJK8k=Ka^nef6Nfz&8k&1<@KpXa3?(< z5e^9CU2~~bu1d_b<#Ol?%y!xD%x=vMQ%?8-^d#PF!i)sI57beP+!E!TuVKrW?C};v zPy&E#r&prth3C@n<G$AOo=;O)C!!|=wL2PcJa67LdfDvzVCNtWqOnQY&N4fzZ zv3!!gfk~6a^cgYMIaOpbSZMtAdn|tBuVb{dpI!@eYk7LDBf$f@t8+|j@_8JP->P1B+xL1 zh9GkSjzTtrO-dNRlLIn=(mkOI-;YM(6mu|zyk&@KkO#(%w#~$+s?Fc8G2Ent%LA;W zKSWiRhVvfVl#qxNGQmi3K&wXXbUmf`RP9frL#}^@zy+_5Ev{_>bkMEu8S1Abqg)%F zJYu<52Pl$aO%qaA$Y%Ba)89}2sA|8#*M|%R=EK6L&Qbt#h2J<)<&?do-MR$Crr_45 z>CTXwhW&W=!sof@!pzTrW%j+|DK$$i^>K;i2{HqVqA>Wihi29)HqEEZ6k+@`2>^fl zo)MH^q4d0AHi1RhQQkZgMhb;)C+MviU42Qdnn?a~B$O<#Pty(+FpWFr)*39iga8gH zTdNO-)7mny{??T%!oTj7xpE0O@C!LI+$LC?gXN|43iGEB-fxr;!~=!iamc2mD6@Qn z-%#mSY8hUiU@5sf1%nN(h0@iIVeiS`sw4^6FoHjnXbWP>=8{!)xdN1!ifNibh$`$zY5p9apMU+ZE%zie6d6a#^RL|o89`c|;g)yWIjmj@WVhVx@jaA+T)+>{82c~FOE zxa4Ge>ior1j90sD8l~vh<}2nBfkGW>Y!!`4S-swojDMfjk#RQL3UgJl|NMC> zah%$D!hoOrh!+Gpx1@8A8`d4)c~4i^aIvl1Eo@!?F{botCRsPBkO^Zlhy>rdyFY}_ zrOZJOv1UQ>L_Yx@!sCQxYm_6HA^c!M1I04_D#R4=^RwpwJ0Jl}2dgX%6j!{08bQPQ z5}p*1KsnwQO~W`dU#w38d+OX4;~F>a8eM5hO4YDhqBSBKpL02$q&)f8DaY_Rpeqdp z5-VdZ!N-cNb#9@{ids~ifgJlG*Ht&~%!?Hva_*1lW(oQEZEs`&dq03*Mpu8M@v$yj zokx0#Z63PNmn@W{Io9NR33))Py~NZ#7n?cRVH%T#Wz^ ziF~$X14IL<#8BE?FAMJ_8xgaA{x&B5RSERJxFhx6s$`-_a6z&|vlTk_{Wc=kP>+~M zn7LPL0wS9^)qun0V$h+pa$AhWf*Kd}3>Aplr|Q&wJL4iRHJvDEQXbwO9}6YR6n)l$ z4}{`#`~h_J4rxyqm&I1C_r{r+J+$5eVqeD|{p+{mMLa1+`P!8R2!*RBqm^KfoBk+O zvTmVgCVk_b5A_OLgwQ~gUmnXf&1bPDeKf-I;3DFLY&Yg`;z4p?DPh4d-zX5fyJB8e zQ>yv?nr(NzCZPRqtzF8VFhQ`OoSA(d9eRfj3xsQt2$<2Y&ug7(A$)u?SPM$Vd!sIC zlmD5~?`W_>r*aUxjw6)4T#(O;vc?Xf_D+d8C&GBWzSNVAAKTCyh+2 zWYClAAdLZ3L+T$-`HY|>+~&y!s<1L(BStCR1LrnNFu=}dUrpW%z2$UUDp--b@ZfT3 z(rrWzNY0uptf(quih*Cpdm)=lFbaTdH_ZQ^(!xbdPv3h4errA`RQV{aLPuVb1p=-5 zM$?~13EIc~fb#79{jJOSdzYfs8`Z-z0xi5-7QBc}?_zG)zi4WjzvHIug954Gt+h7z z?6NhYH-0GNBR830cVNgWcUUDJC~JK74;XvBq@$mB=Ns#*$MehtAgL682!i0nl1#hz zvmJ3okDDc85Xhh0)DUHJe)7Fo(W;yz>#{&Srwb3%6>N{z7SI^z==ggib94W}#q>Rh zsavBX#HYob-w2l(7Zl1cfcBg?5@}BOGS%xq1}jRwG9`SCaELe7}p!F6Rss`jd(kb!0*PTCZY+9TBrp z{T10H&eS0Y2zUqBcyU$4aU5pN+$rcYq8E~n?xpeZDXlhvO8ePo?pMWg7(aF(F!^46&Trn!s#== zOp%8xPYHm?#%73`iECX`T+>Z@9p^MO+8%7=^VWo-p)O(KX5{94VjHNMrO+#R*S3yS zQu_7Dk9>V{gX8yau2ya^7O&{l!0{#&n07eia_8ncWJ?iYj&mCsJ0Fr+&|F8}9Csd- zX~9ZXom6yEEF}R~XS0GeZM}!dW_)C_Epc%If~(Zq^CG1h?u?E7P0JEAr6Q;+KZ*us zPNT-Z;k;ak4mo=ql4|klK+?phbaih;j__$VDAbUujg9OZ>y;6|)C1Q9Aebmz&N^BL zN7QYcnE3xdR+su30d8R~-$wb`HQ|HObY6mg4IN7>kMmi~e_Ny6Ql zNwaT2p^>!;;Ifz=tKpTBkZb@JsIf!ms86wWYW|y_`ZHJm{@#9EpYSM$rgQFcGW|$D zf&ZBA-;@OgGFwOod}h>oy40Bv5s9nl(N-+(8 zZs{(bNm{W0&Vvdx3$2u`9>O_SnF7d}Jbd36H4u+gh<~#1tO%UsK!f8KdHD4cB;526 z-QrxanYy=PX7wN;?DlolP?UlT<&u!NG=v_Sl^ETg1)E&%7ZeVNAdPL)E7 zx}TC(D!P==u>P}uEzK>!&MbGYE|)$8QZKhK;1^fYf4RIJ+5{Ayj&U*f*GQ)rUP@;! zaOSyciM^Ci=Lv^fB88H1lU8w`Qh-Cqw_{K;h6z*zw#qZ4;Aq^6 z<`3)R0zj%L4P1C%cErBJw|gP;USDYP{fs4n-IfROCu5ijcc=Hznz&E%w9L94m5PL3 zVPSY*gRKj#f$y`reWqc{z4V8aJ@=i)wC%mL-%|JVg(2smnZd;D&$J1mGDqZW!g+q) zH+eJML{3|kNsSNlC&f~6FVMY{M+>bb%J)#RSo=FOpp&+y?AM#ARL+wNXnHb7x_>bp z1+t{R5alXn4fFKKFAV27_8qb_=-Y9Gs7q`nRMQHhaD;;s@G(*IsVqe$tD*Ru+6;!G z)a5VDsefZCg?^G%I6I4Nu}G3Vn8QN}>HMA4?o)Up@U`8)#UDkfq{{jtoP+{yxz3Zk z9(7K-&c;#^uK!1}`9(qD0`_iaO7HxdloFVFMyd&+pa^fj+#`eU>g3I<)i0+c$noeW z%qQ4Py%yloy2b~!6pydCC9hA`RJOQ>X3D!SNHM)&=$;RgQU|AFVgeN>wFdv{B4fC} z$~cg-N09Y>(cjPYT9Yew3e3l+(jxXNn-$(wV&Tjbw*Q+IW;m7=-^66NDw_pG#b$Dd zwrm-8Y{)weSzK@4>Adm@-g8hi6Z(NCk1Nht>l5-&(n1|Ekm`Xq z`6Oh%DcS9)AzS=IN)6-g5VtVFS7liQQL%}Sd9{O++mlJu2x(s6>9$bml2Xrnu;dEcFjp4KM8-(Fa7+1IRE=l$cAkP* zUyTWJdy(OWOlv@Z)4@IF9!c&n7b9z?@gw!$yaoisbUk6jD<%}2Tq-wYBWrt9Dr8!1 zL(K(Lu?g+idi!O`85UE;J&aM^RPr%9RezmCf}{ zpz7Vc8?`CrWY4EN2v4&5yYE(G6H9D7!VGdvr_GC9_rK&wR(*K&(w=j(^z6f_o^q+6 zbyJlmC%X*+FSNsB0uN5RMZ#+8>mF`%`Pd94ExAVLJW99u6ZLCqkGBl6 zogC?+Guh{ddL^Yokbd7Gm{0V2TXBZZ+U(D?;lh#>@7LX)@4{NS*$xgr>a^Fu-V|oy zs{@CXv}m>y5z8z&SC^0r#rScd-`eY%C5j7#N=4EvI{{lTdP}LBz{T%CF0!exH3RZv0zsJ za5KAALJ=I2it;!t5jAp{n9`Wk>XJDUbjVjw3NqXD`H|`s%dh!fBUwG*yKl?XHdgjk z;)L1YT`U@lFG^x+@(vcV8x!hPm7Z9i;q0Ag3BY^aNjlRx#zP>9=*Kjj+Xm!-` z16TMD(L|1|pu&Pq)xxOVh|M=v)D(fLjddq~GIy1#0MWQF6rw&$QD(DhIkRQmZs@ag z?OL*)t%#&)ILH!*XZr`av**5OZF6C*4f@xRph`qF-bDYyN6bI!_-AmoM7x~#!jEk5 zXxjs+2;KH#ZMBvj=z9*_?@fU&)BA%YgCk~d?dQiat2J3m=+US(E5-tG{f`@%vAg9| z=7SqKrCIDAWvW{RK8D+i3cno71?>+{UzW<-5RQJle=M{D6D)HWw*8k3b$r;)ydH_v z?V}5;fXyEcjxO6EwO?+;cN#q+BCPZ5|LV6*W|*a3p0&m+^kHmjZMUkkky^x-A%*L& zCtt~Bzgd0fC&e|QOGNPWKef3HI-EK#Rs*NWcYzz zMmhS3Sbk86kTW+rQPej1qqOa6nNysO{d6>$3=xT*N5QoB!<*d~lh--i6PHw7VpM5H z0;s77q^sHT^G7vYF-=L4?2*`Lf03yP{wrQ0iR}T!dC7cEUqiV>>RMjWOf%1az08?f zWyP84@b=iayvEOdsh=HkYuo2U@pglqQcAT9G-nNArH5bz@N8*E+MJAbsm^9YrK$@-MVD&N%0Y zXN^4t?>|>&tPht_69i9izv<0yV_=wd%L2|>MId0^7+oP(V&jdw&d_h2PTr@U86Veu zOYR05NA_BVZ8aQ==P+Oj;q zvUM88r)NhH7+@SJ%3s!-GHIN-+#99#`0pRHC(&-a4txt}2x&5h@$O^oWX%tflxd*q1G0>D(8OqB3c%K#ajp4*vy9 z3AblDlaz$XShGHcEtpFmehMFx?DSPE{#c+*@Wkfc0{L%=wqaNM!^qGvbj~|bY;j#m z$`_y>^R&+srg6PW?_o0LF_(KhpqEL;has9+xJ3-#!gPbyD(az z?e2o0AjFy@kKbsZLuUx})1RY1R}Y+{o77dlSJtZgQ`t*2m9}oSYI}M2IIZ zZ5mo2G9|wR(@bfK=q?z)lg^;!n4!dP@K}xNkRxHb!p3`d5NL#T8Gr8o@CUqCJy{gE z-ja@)(yE;{#1k*O%*v2jBU}9FrTOHaiL^#($Gbblqm(uUGn1(^qU-`@5hjUK*wTbz zU+HYZGOzbaSi*TmP6q8Hnbrt2tT<26qwK(iK)D(;m$(CH}Bh4kr3bz?jzFvJ#p57j*IMS_{66Pp}v;HvqFWWwGz9Qm? zzeaLL+|N%{(gALfIyW!c9$g-qwOE)BD=m_lpdKDvUi?%XV6QdDf61`eayS<+w6dYc zRpYGK#O>OvDfQ)_)|&3dGyxt5QtTSFtCkujWtiIYC9{q5xx$F$LC!3#MSK_iL-O=- zU(8ey%fqOtafVL)3X^+aJg1>F92!o}>?f%jD|b(+cA6?@tqsM)7)Foy+i0MrF2+ds zjA+GCOfe=(wLs?a#QY+ug7J?2Kme7$$dGiShc6=Cz9BJLPsB(0M*m8;2*`llBH?Il z0wh7Z-a5vFx>Zo^i);P)-O7Mt{=jW?imccMdHQOf00-XNIL5DG&@>BvrPH0^7Hws- zQ=@@KANXqDv0qEJ09^dNMsQh@jIY;Lv{3Y-%NRRE)Gg<(>0seC$7u-USBA%_-7x?C zTDxcOBBzx@fMZ18aoR(F_^ECod4g%&Y6wmwe?pY2t1dJV?U| z@nwvXJfuthwmGBd?>%0HzGHruF?9#4xm*#RJXo&(KEdqRUPcR6)CSbK*WtAgQ*KHb z>i~;jU7!7XZ{V*ppSRbj&%u?+2Vb|L&2oc~@I$iLepL@qG&Ob67Ek2UYJ zp+7I!v8IIUAAXY0&|U~ZjCv5W_ZGO_7co64=9bYnZjyPxzL}D+JIB75<(48ROcryJ zwEJ~CPC6ITf)pF(cP4rI3g%aGKk#6_oJnNCh80V!?va_5(suc@$5f~<|6WNrM@+3= zVIo!AaYG%~l2}5x4ZTfg265xWufmfh`hV3V(92rkU1#K^xM0cJ{Dn>YVAJG}L#1Dj znJ>lLx9mpC%)T@F_HbPH?bNSnLyqrAL|;iatkP7KvJw~#%xB%Xbn-AsnQJ_b*XDGy zDW@xL4cO`N%!)NOotk-TRl!`ZD!5Shmz;Qu-Ir4kIl}+tQ&c8bV9q1WjWY}OUXz9s z`~DX&|CleSG4gL1-{{Jnggc^xYGt+WVdeLqe|&pU*;r!9XOoyZ;MY99KP3w`X;lBP zLzsd-f~r)iCPjFU9lv(J-)=aWZ>PDZ=lp*0OMU{QXAjVogxYY&N9{N~Usdiey{lFN>exZF9w zy-5-GYW+Gr0#$41?=R)6GQ1xw!uKrzZwou%W^|ZIE9dfFb(B;ajN6&z zPAeN92`;`19+qg92F!9~mnD1n{HwC9wa@(FRV1zSN{4^`njuZY`C4S!!*K2l*03k6 z^>@QIax|Grp84V*;uhpZqD)l+2Aqq?G_ecsei~nppUatW zw-%eECHOWd)kTWPhAk)#!gd*QY8~d&()+HZb4WQ&tR-gKueVP+zPeN0$ z1q$P|*o}Kp=^X~5#!ED{^W>B+vB}QnWBCIjvoS4YBQhC4zf)$DC71rT z&GMFGu{kVZ(OhA4%WZoO{X9-zF29{o$tok$?kCwqJpn4L8u&4wM-M)W)2+*MSs1L& zN_B4=;s2vK$1n&7c136U*}NXwlSc<8BF@$9XWjtqh9k6=2j?v_74}|{3pG<|l=Hex z=*so$&|0*h$rha!r<#v&lR`?(qoWZ8lxh*~cicuoD|@W|yseaXf+TKevqaw$BqAzM zQIfIEBJ=zQoKcmxj7TcD&PCE>UsU!!MB1}FfqYNCn$f}k`^$~DM;AgfUdLTn90M1< zOu)g4$1*UeY|kjt`hFZ;X`)){%A>De&1(p&e4AqRZz;*b?ayB$U4;S%_KiY?;0N90 zu)&COmJ7BA`pwpNp{78l&st;AU(i4n<5x*tbXfB<(3|bL9z6UWj3pNrHQ38mX3^aD z@`(O@ypjHLFo72+SDpD76*sE1D4p-YKPVawUS0H!4J)Qm#X`3OI%>b#)W47^IS4!C zKltZXn~&NZU$Attveb>M7%ET|9q{~P)}c>fEbNnN+N8WVuG&ae6_2%llQ%b*U>Ubb zEKGOzhTehgnN+d)@UeFJ2py-tqEUU?z)ML)e;2>*rGO8-#4;vW9#xNFm+UavU0 zL*MPAUqhcDWdlN*Tq(c53Psw*7vh8|#Mwn?QMFgj+Z(~&H8$@E%-W@7R&FFK1a~H_ z(n8isp1#)Rd2u*>_TYlur1IDE6WsiJ?w7jH%L)YS5zfr*7iP?g3o4dM7a@oG3dWnl zU7p{kBzNzf+cwvE%?tgWen7FAUHDA(xl#pVEko~VYyZN-GZCQ{_+ww4lb1uUkBg~p zm~(}xrjoqri%F$^C(9+R7q#f|8`{;P?)#Zr^rz!L6;{`MYrRG1GdJ~O=;gd(t#j}% zDv|Ur`&TSR(uG%hSo@X!ERmu-;5x`FoHvC$@Y@vpjXuwr)U%@B?#I0!BJE7-Q>PVK z)K9GO3emMZc{71*1=bC#!CGm->Q3s&L~q1P&h9e<LC&5_y{aP;OjzF0WGI$id$9(&CWLwOLdxQpC^Zb)Q4S9xFn)h7&VF?{L~lbNHGu& zDs1aAo>c3^UQw00Xnp^NtnY?5eVQp;h&WCPk+72}HD*cE#9P#ee(*3;X!N6a`zuaE z9{%h)J@I+57jMP{T4KLOKi8no()nmVPpHNEBXgqt{HJ9-pU``+u89w{xl=4B20ahR zRWup+ohzi+$3aJSzkIo*=$^vG_ifyd{f*4k;}AqhSBw_ydu^oeZddYV(q(r5B58N` zNzF&)1+0ZAruNGP&n$GEN`y6WCHqq;Ekx&9L8-s4P+q3z)XeK*efgVnzr64_S1gKs zqwCK*s6H25E!_~yAVY-WO8V(w9`PW8ytYE!X?$mDPj>ToxHSPVGSy;~GEOV#;v7O(!Z(6Y8f9$BQdBHham0i?8l zi8Ka})|wCzx|!bDt051~4A1-C!q(3&o_)naS4*mXdD6tkoV&psC-NvE&%@NiaroJP zGwN)c4j6PRw73*eQe`ce=W+h}{$pc}KDlN1=XqypWg&9(pMp}Z*wf(bL+YiA-m5Ly zDq2|HuTMLJnNSo;eMgm_y&e%@xFa4ch@)Xj3Aaa%BvmNzKnmwKofPn-|`M`+rl-iZypIreRvk| ziAI_{-f;V!Lhy~1Hp_>Hoj07Hxv+cmUkb3!78ddsiorLYzEWZ)J{T@dA^{@mWRqg| zlJRu;YU$1ZM#Q2YXRphLt*`_Sw?iMG*PGbV>}^Ez0@iOz^q;hAwIH zd7*q>pLu)56Lw0c*kA9OcCRqaZhlbmj`m|vHT6#{L6_x)UOop_CbwW}wEyqE5bsz* zOJ1L#4oQ4o8}Da48T{{yNu`<}q ztCM=c6P8pefo`%kBGpRMETk+ovK)`l$~mFV#fNSEWVyZg*=K^ZP0OfacNyx~o#**Y zB%?b64L38IZHvf%NS))eqH4bJpooi@IK^YEv4{FmN%B1jW#)~?Jb$e|4yw?r`|m^c z`I-=NxJTl=TgPP-1JqpmisGwhDwHnXLVtEa@*+d^kz8(XG;t^lEZzW6WfH_JY zaUGV#ZM^LqatN&cW38Am*Ps4jYU|A<3(zza^#!e+RCeB*t zCwk3ZpxLrtN)=37@`fmB(QF{gT#6lji?y%58`+h8e!j*pWdwephRJ!V!0ktNg$J~% zqV~mWx%n}L-#*>DpaFV1=f5y$eLlv8v}G6Qsy}3fD7tKD;a`_ib-=;6wu)Dj1hc$oleVl4GX0N8ju3d|8-s|2Pw~!qJSp8 zEc{@NXH2&cZPZdqzwvLI7vu47sH|uxfu=5hviW;vIJa=iK#!4`EO2SO$}qUe8vKHd z@)jpFp70joPJRnhU)3E7yj$XrL&v79ZYl4Bc7G}!5ZT$^k_DYR{`Zbupi%aqmAd`E zrKDvEi2mQ?|FN1uaoxA2dkgLP2n&5iMq&4AB6_&V?~-{!wXw52S_ ztu4G+{*w{}G@#+#vQ+e4M{YOfe>tM=?${FlkGe`uxXyn_5-lur9MytP@NQ)rhq*ME<)ViuVI4kaH@XYxPwx3MJ`!3oqW`FXXxj*G z2{VQ1b|WolpiSWaMnnitu3P;zJ!ocXyX==Mw&eS7m_AZP$f4cs`JZt2*~3_95&!a7 z=`LA-CBDh?gk`N{Do;`n@!q2103CKY;k;eZ0EfQ$h|dYK%}ff-=J>Z9H>@MN4+G^= zb`&jLfGhITH49PzcIQ9XS>0gfG|%5}sJN%$VYdc{noz085=`Kj<^#RM_{FPw|ADRZ z=zl))VMXlI-7-Ttl!i~G-QC(X%MAjdgv-N8idQxNn}|3l%ptEJ*hEr7n;2ZV3}96k@iTUNn@&~v_OHhfU1@domq-oe^kVAXr-ucRf4kOBgzf` z^q+ffa?`SJCU1OP<-Da(jRAUac|8el6$H-zc7r0xx^pWdTjDSgZ4mzX3D5xqUOk)` z&Y5Q+HMr$9w8BffJNZ^NSu+0m2ojdD2iN;IICiB?y~nklp@7eWR24N9Dq)u4{{uMk BvWx%# literal 0 HcmV?d00001 diff --git a/imagepy/data/watermark.png b/imagepy/data/watermark.png new file mode 100644 index 0000000000000000000000000000000000000000..ad8fb25c3a109114bc24bd0632c583cdd343d8b6 GIT binary patch literal 4867 zcmZu!X*3jm*!|5|$5OI}Y}v9t?0uVXk z?&g*|wJ<~O79$YQXlqNdAo)D+Io@IdCKs22dx`rGBA07x2$zKkN&jgD9fju*nwa_y zLKlHR2yz(y(N27hQzm3Pp{>9EHLy5bikR#0xMUr()9tr?)1uS2vZ043BzH;C?QG~Kiy%U7S@7O#|L;g) zLVo*HCz@FCa2ism?9Mr>)q%f$|2pgStQS{a@V{KIBvpKK_w?%ZYV4kC`|`j!F;*f8 z7)eUXcq{QJ+3~BY?q3s>&Mj`}Xh=GUr{r$hF*CwSxx+YO#C}PYO2ycVIcL~~y|;g* zhCi@LF+l$Lykz^?FkFY#A*DL| zs7k^YzhuA6wAeJD`9^0lzvEJO*MR-NK{ZkPXRDXYw<9^YEXRkKQaOL?0D~3#m8KD$ zh?>>Nmc&wf%m$eZ9Q)rk-P4O8Hq5UybU5{RSn?v7qm3)3r+m zOoq}1{GB!JhV6gatHr*VkG%?0p+v#0vYqP846VIvhDuko)%UkvYz;EUFg<$Y#Umsi zqu*E(`!#n~a9EXC3rb|4&1wc0hS)KOGyfTba1y0)#XoK;k! zZ}cCYHf-IXjD#?=gD7#ZWdg|0L6;09%?>JDPK$7Xfyv>c^^>eN_us@+rLRpc<-_wr zbLAs(S$1d`dVY<3Qfx!eJtb3%QwUXptmXc~c6YPk1O z19%979*&1?NI;taszquQ8LunGA2vX>JalZ$65Z&r!^{a$+JOO#YV-)=jRiY8R9SO^n>J$&ElAn1d-Ix7OZ$9XVA#es5vvIZZGk1w z#~lz7*CmE&}{i_mdmj+4cS4n8gjdq}F6rQHa|$%)>_vwyagv z%MTpo89?$eU)y>@3k2FA17~M^tQcQJoE$ocWOEY%zb~KmaY(4+D&@1dg=U5l8}F^2 zZ{1di$w{YoXt5>ZO$EDjtyA^CQkQt4qu0ru!vfFsTEwpeUr?UDyEKIr&3ObtBphcn zcj!%BZy3;a=k8>OBPMn<5sBqcf?C=dHgQ7WH21s%{ir`VE0S8r8HwpTI%{comDzoN zkccux{hZ5djtD^}uQ zu*eAtQ0b;mT~QTZ+7GhGc4hm~V%BI#NZwfJ#xqBu(`F&Y?`#ACT}gl#x1Gz758zd5 z3K^HP-adm!lyVWSKt6LdIi}`5VJ2kSt@T|zK6mcwj_!Ne6&U$+N2W$NUZ`1<>K{CZ z6Pco8&!MM|dJM;=TspN$4TRIMiPFU&+@6W!yaS=J%eQE&M}-(bnle=_Pc_@vWq#)9 z=W@%;q8N5;F>f}?ee}s?_$b#m!C!=bTd9~{;*SU%Yk&Icw8F)j_L(^OO`X#VE}Dch zLyoei`x0Eo2K%oLAzri8C=VxvooqFF^(Q16OPl^2|PL1;ShgLkeQ&?l`1WrFhMpxljB0S00&yM zVvK0+c{|$;NB*j2sPpI5BQN@l?|%ja7_U;lHySqU{8)w_C}rRwpsbmpVbf`gv3%h0 z=**J7Th>JmJRPFsU{+lQT{N)!-blY=gpEW|y@vUDYY5Cc`Y3<=k%$0X{i3F_H_RVD zTbFE67Gdi;JEgwxm4|cS4pV^AR$QrSu7qe;2doHb?VsLh@1bW$?;os+5p)~J6~Q20 z!1xtpg@}@*6IZ$-s`YG}+_&@_%a3-TS9O66 z`wcI)bt08Lv+K96R;H2IDkoDW8@th(+cMqqIrAuDwKz3&oBB!Q@#MBySJj5GOs34e zpzxk8MPX~bWG>2|^e&&FHbr)~lv~N&f3%$Zg!^E z5_Lv}2@ieiOK4{4pNboIChcsD>VX{jMpB;_%;&C(*sldI)D~$dE^Kt} z#A2f6P^5cZ)PeXTB|^pzN&lAv|*OpP%DjU-%YW!z@+}KMk|916Vq5 z^}-GwHIs{Wwl)HAt2=M7>9hoWLiHlvLCa-X?OS!6FB~vKT>BS9b%satwz)?Z%hpc7 zW)Zl5Tyb&s=$+pd6Ln4c0*&7xr@7#6ztq~PSK8+OPOwSCcA#o5AlqBis#JtMx$he$ z>q&F}c(^%}bp&(z+7mJ#DZe|@FG<@?{vvn|BG9p7`8^j)j^s&Lni`18E;nc;-KVPX zKqW5BoNjqLya^W6t$nZXJOEp{rf|~nMYm{|CCI*KQou^|Jud{PP^VU_m(P+U52J{# z?{Ez!1E*ZQ0G$Me=Qr6VD``1hqL{!GLijACYiq*{V-C(&C*u-Vf1j1VD|U|s^|IsK zE3&sU(H5HV2iS2ijBJ_|a=O1d?pyG%=R$L|hLELrBQVM31{hV)57AvkR&L$TIgVNL zHUe*PZbd>yN9L|=<>lXdsB55qBstiK|%0L<3a8SaIu^GT#8eJUOc zH!B_QTNPu10(p)NWLJoj;gx*X3E?ly`G!x(f;rUA+>4q5E1k<|n`!k24SxzV!i`svnA-ZD+{91R zfU;Gt+Ow%&()T4S z3pzSGIjKz|6`WwFE45_N6iCTnpL*{EHTx%Y76XD2M!@pg681~qHIMR{$xkdKJK=`n z0~8D^(G$ssr?q%Xj%$*h8(#&V?h0d$wmUTf7nVJi9^PrRxX!L;Sm>-wr9@6e+@m_W z!X^tC62_k#Hz%KOEbOo+s$Jnt__^&$t$)95&p=x*x1YMSv-i{OV)tW@3%#TDP{;pF z%17-hrhi0EwADXS)JDoC?_PYVOluv0bvG@@izXXrIttE+Ucg(brxn;zQBcd^MVm*oQui)kd`YjbS*{Lk zMs%zGvwm5yr}(C2picQ?>tu*mZ;gq?9m}oRodsbLI(A}Q-^z-N37(zTPyU6NR1@G8n~({Z2}`Ju4a zrlDUCzD0ky7X2)f1ZYN5)%XS6-7nCd$65|wT|xtT0gd%E{xRf zaJjz8(|*pr+kx|=`H-k{#bexR;_6&M7{G1{x_F*e#JY~HST)&5ZqRH5q&Dv7bGEFD zyE5xRQgK_hm*)gLu+Zw;^Y-PLEs@&FA+e(&Cy)cc94wc_r&gMHw`@QuTZ0RzdEPHF zomc;i=Uj_lBM@u7v$HcsLZRS@#<|bY$wn6j&B(VU(p4Gb(SnWi_=ll`zsF?w@)_r1 zD`e=L>Xe=dK(La#A2X~kX1T`glUk&gVh2Ex>DTK{hxr zh1y`Lfb(_oWWndg2&OP`$YF9=^$Szqd>dBsV3Bhhz0^yK$iyGt#pQ1;obT)t2SE~| z0Z%TidHi^X957um0r`mj2@>C=K6y^=qnf^4S$9&^X4U!;B&psRrv_| z;obS%P-s?(<^$TdwFXtpsy^I_vJ|`y9cgs-AQzL?z!o^a#lr|H=WpL&tr*jc;rN1~ z>cXYPu}r9Hf4Yg=uftR0GxBbh&i#1GipZnI2oUxzbawC?|JzHGu$)u4(aZ#%$bGcck2ebX6A6j{K)uebx>XPXrC=RZ5E|!AEE650s zJH|&Yj?&nar#`*s&_3L(JSv2g++co`P*1-%Y$H(Z*vVe}v7p>I$Pc$(!Xc=8B)zFx z4NO_h<9{waq@Q`F932(AQ>ySIg~kYzA`R&VHP1)`$cK_Emj?`&QhFS|Y0S7zbN1iy zBO!%Mu*O>{mpSfBzTm+oV4v{QqikQBJIh&`t|d9-Aw~sH)EKqK`v&w&3%ERf-~2?{ zTec;Mp{j&~J}7C}Q9~l2B@w4DeSNG0TBk!yUq_Vu{!<*s&wCA~-u!{ynD6=-xwv=E z_}eC=#6o`cj_Fe6r3p+}DP)OyLoqqiQ!V}Y=4JTz3m}}0#AUm4EYISVkCojf8Gu%|Ub64zisq$oGxO9k1D^z-Q(|9! zg9N`iLk~0%3oxY~mV0}Mm@JS6zP=4d#j!VewZ!B#Lftr-E|@t6a9Tj!qQl80KC+N0 zdtPzyFAg`U_k$KI%z5z_gCro~*!;o*pl+Ko42EL=PZh>C{9i}DaOV literal 0 HcmV?d00001 diff --git a/imagepy/menus/Plugins/Install/installplg_plgs.py b/imagepy/menus/Plugins/Install/installplg_plgs.py index 5851ce33..43c72534 100644 --- a/imagepy/menus/Plugins/Install/installplg_plgs.py +++ b/imagepy/menus/Plugins/Install/installplg_plgs.py @@ -5,12 +5,8 @@ import zipfile, sys, urllib path = 'https://github.com/Image-Py/imagepy/archive/master.zip' -if sys.version_info[0]==2: - from urllib import urlretrieve - from cStringIO import StringIO -else: - from urllib.request import urlretrieve - from io import BytesIO as StringIO +from urllib.request import urlretrieve +from io import BytesIO as StringIO ''' path_plgs = os.path.join(root_dir, 'plugins') path_cache = os.path.join(path_plgs, 'cache') @@ -28,7 +24,6 @@ def Schedule(a,b,c, plg): class Install(Free): title = 'Install Plugins' para = {'repo':'https://github.com/Image-Py/IBook'} - prgs = (0, 100) view = [('lab', None, 'input a zipfile url or github url as http://github.com/username/project'), (str, 'repo', 'package', '')] @@ -55,7 +50,7 @@ def run(self, para=None): os.rename(os.path.join(path_cache, folder), destpath) zipf.close() IPy.set_info('installing requirement liberies') - self.prgs = (None, 1) + self.prgs = None cmds = [sys.executable, '-m', 'pip', 'install', '-r', '%s/requirements.txt'%destpath] subprocess.call(cmds) IPy.reload_plgs(True, True, True, True) diff --git a/sciapp/action/free.py b/sciapp/action/free.py index c8ae5020..897e2c4c 100644 --- a/sciapp/action/free.py +++ b/sciapp/action/free.py @@ -10,11 +10,10 @@ class Free: title = 'Free' view = None para = None - prgs = (None, 1) + prgs = None asyn = True - def progress(self, i, n): - self.prgs = (i, n) + def progress(self, i, n): self.prgs = int(i*100/n) def run(self, para=None): print('this is a plugin') diff --git a/sciapp/action/meaact.py b/sciapp/action/meaact.py index 35e0f01b..24a80bae 100644 --- a/sciapp/action/meaact.py +++ b/sciapp/action/meaact.py @@ -1,13 +1,10 @@ from .shpbase import pick_obj, pick_point, drag, offset from . import ImageTool, ShapeTool from ..object import Point, Line, Polygon, Layer, Points, Texts +from ..object.roi import * import numpy as np from numpy.linalg import norm -class Measure(Layer): - default = {'color':(255,255,0), 'fcolor':(255,255,255), - 'fill':False, 'lw':1, 'tcolor':(255,0,0), 'size':8} - def mark(shp, types = 'all'): pts = [] if not (types=='all' or shp.dtype in types): return pts @@ -20,44 +17,6 @@ def mark(shp, types = 'all'): pts.extend(mark(i, types)) return pts -def measure(shp): - txt = [] - if shp.dtype == 'layer': - for i in shp.body: - txt.extend(measure(i)) - if not hasattr(shp, 'mtype'): return txt - if shp.mtype=='coordinate': - txt.append(tuple(shp.body)+('%.2f,%.2f'%tuple(shp.body),)) - if shp.mtype=='distance': - for s,e in zip(shp.body[:-1], shp.body[1:]): - p = ((s[0]+e[0])/2, (s[1]+e[1])/2) - l = ((s[0]-e[0])**2+(s[1]-e[1])**2)**0.5 - txt.append(p+('%.2f'%l,)) - if shp.mtype=='angle': - pts = np.array(shp.body) - v1 = pts[:-2]-pts[1:-1] - v2 = pts[2:]-pts[1:-1] - a = np.sum(v1*v2, axis=1)*1.0 - a/=norm(v1,axis=1)*norm(v2,axis=1) - ang = np.arccos(a)/np.pi*180 - for v, p in zip(ang, shp.body[1:-1]): - txt.append(tuple(p[:2])+('%.2f'%v,)) - if shp.mtype=='slope': - pts = np.array(shp.body) - mid = (pts[:-1]+pts[1:])/2 - - dxy = (pts[:-1]-pts[1:]) - dxy[:,1][dxy[:,1]==0] = 1 - l = norm(dxy, axis=1)*-np.sign(dxy[:,1]) - ang = np.arccos(dxy[:,0]/l)/np.pi*180 - for v, p in zip(ang, mid): - txt.append(tuple(p[:2])+('%.2f'%v,)) - if shp.mtype=='area': - geom = shp.to_geom() - o = geom.centroid - txt.append((o.x, o.y)+('%.2f'%geom.area,)) - return txt - class BaseEditor(ShapeTool): def __init__(self, dtype='all'): self.status, self.oldxy, self.p = '', None, None @@ -79,11 +38,7 @@ def mouse_down(self, shp, x, y, btn, **key): if key['alt'] and not key['ctrl']: if obj is None: del shp.body[:] else: shp.body.remove(obj) - txt = measure(shp) - if len(txt)>0: - rms = [i for i in shp.body if isinstance(i, Texts)] - for i in rms: shp.body.remove(i) - shp.body.append(Texts(txt)) + shp.measure_mark() shp.dirty = True if not (key['shift'] or key['alt'] or key['ctrl']): key['canvas'].fit() @@ -122,11 +77,7 @@ def mouse_move(self, shp, x, y, btn, **key): shp.dirty = True if not self.pick_obj is None and not self.pick_m is None: drag(self.pick_m, self.pick_obj, x, y) - txt = measure(shp) - if len(txt)>0: - rms = [i for i in shp.body if isinstance(i, Texts)] - for i in rms: shp.body.remove(i) - shp.body.append(Texts(txt)) + shp.measure_mark() pts = mark(self.pick_m) if len(pts)>0: pts = np.vstack(pts) @@ -139,11 +90,7 @@ def mouse_move(self, shp, x, y, btn, **key): if len(pts)>0: pts = np.vstack(pts) key['canvas'].marks['anchor'] = Points(pts, color=(255,0,0)) - txt = measure(shp) - if len(txt)>0: - rms = [i for i in shp.body if isinstance(i, Texts)] - for i in rms: shp.body.remove(i) - shp.body.append(Texts(txt)) + shp.measure_mark() self.p = x, y self.pick_m.dirty = shp.dirty = True @@ -151,16 +98,6 @@ def mouse_wheel(self, shp, x, y, d, **key): if d>0: key['canvas'].zoomout(x, y, coord='data') if d<0: key['canvas'].zoomin(x, y, coord='data') -class Coordinate(Point): mtype = 'coordinate' - -class Distance(Line): mtype = 'distance' - -class Area(Polygon): mtype = 'area' - -class Angle(Line): mtype = 'angle' - -class Slope(Line): mtype = 'slope' - class BaseMeasure(ImageTool): def __init__(self, base): base.__init__(self) @@ -195,9 +132,7 @@ def mouse_down(self, shp, x, y, btn, **key): BaseEditor.mouse_down(self, shp, x, y, btn, **key) if btn==1 and not key['alt'] and not key['ctrl']: shp.body.append(Coordinate([x,y])) - txt = measure(shp) - if isinstance(shp.body[0], Texts): shp.body.pop(0) - shp.body.insert(0, Texts(txt)) + shp.measure_mark() shp.dirty = True class LineEditor(BaseEditor): @@ -222,10 +157,7 @@ def mouse_down(self, shp, x, y, btn, **key): self.obj.body = np.vstack((self.obj.body, [(x,y)])) self.obj.dirty, shp.dirty, self.obj = True, True, None del key['canvas'].marks['buffer'] - txt = measure(shp) - if len(txt)>0: - if isinstance(shp.body[0], Texts): shp.body.pop(0) - shp.body.insert(0, Texts(txt)) + shp.measure_mark() shp.dirty = True class AreaEditor(BaseEditor): @@ -249,10 +181,7 @@ def mouse_down(self, shp, x, y, btn, **key): if btn==3 and not self.obj is None : body = np.vstack((self.obj.body, [(x,y)])) shp.body[-1] = Area(body) - txt = measure(shp) - if len(txt)>0: - if isinstance(shp.body[0], Texts): shp.body.pop(0) - shp.body.insert(0, Texts(txt)) + shp.measure_mark() self.obj, shp.dirty = None, True del key['canvas'].marks['buffer'] shp.dirty = True diff --git a/sciapp/action/simple.py b/sciapp/action/simple.py index 1a207eb5..01dd9b3f 100644 --- a/sciapp/action/simple.py +++ b/sciapp/action/simple.py @@ -15,13 +15,12 @@ class Simple: para = None 'all, 8-bit, 16-bit, rgb, float, req_roi, stack, stack2d, stack3d, preview' view = None - prgs = (None, 1) + prgs = None modal = True def __init__(self): pass - def progress(self, i, n): - self.prgs = (i, n) + def progress(self, i, n): self.prgs = int(i*100/n) def load(self, ips):return True diff --git a/sciapp/app.py b/sciapp/app.py index b2a69d99..bbefa69b 100644 --- a/sciapp/app.py +++ b/sciapp/app.py @@ -64,6 +64,7 @@ def __init__(self): self.wtab_manager = Manager() self.mesh_manager = Manager() self.wmesh_manager = Manager() + self.task_manager = Manager() self.managers = {} def manager(self, name, value=None): @@ -147,3 +148,9 @@ def get_mesh_name(self): def get_mesh_win(self, name=None): return self.wmesh_manager.get(name) + + def add_task(self, task): + self.task_manager.add(task.title, task) + + def remove_task(self, task): + self.task_manager.remove(obj=task) diff --git a/sciapp/object/__init__.py b/sciapp/object/__init__.py index 0f118bae..f3766183 100644 --- a/sciapp/object/__init__.py +++ b/sciapp/object/__init__.py @@ -1,5 +1,5 @@ from .shape import * from .image import Image from .table import Table -from .roi import ROI +from .roi import * from .surface import Surface, MarkText, MeshSet \ No newline at end of file diff --git a/sciapp/object/roi.py b/sciapp/object/roi.py index 5d641c08..34555ebd 100644 --- a/sciapp/object/roi.py +++ b/sciapp/object/roi.py @@ -1,6 +1,7 @@ from .shape import * from ..util import draw_shp import numpy as np +from numpy.linalg import norm import shapely.geometry as geom class ROI(Layer): @@ -41,6 +42,65 @@ def to_mask(self, msk, mode): msk.dtype, self.msk = np.bool, mode return msk +class Measure(Layer): + default = {'color':(255,255,0), 'fcolor':(255,255,255), + 'fill':False, 'lw':1, 'tcolor':(255,0,0), 'size':8} + + def measure(self, shp): + txt = [] + if shp.dtype == 'layer': + for i in shp.body: + txt.extend(self.measure(i)) + if not hasattr(shp, 'mtype'): return txt + if shp.mtype=='coordinate': + txt.append(tuple(shp.body)+('%.2f,%.2f'%tuple(shp.body),)) + if shp.mtype=='distance': + for s,e in zip(shp.body[:-1], shp.body[1:]): + p = ((s[0]+e[0])/2, (s[1]+e[1])/2) + l = ((s[0]-e[0])**2+(s[1]-e[1])**2)**0.5 + txt.append(p+('%.2f'%l,)) + if shp.mtype=='angle': + pts = np.array(shp.body) + v1 = pts[:-2]-pts[1:-1] + v2 = pts[2:]-pts[1:-1] + a = np.sum(v1*v2, axis=1)*1.0 + a/=norm(v1,axis=1)*norm(v2,axis=1) + ang = np.arccos(a)/np.pi*180 + for v, p in zip(ang, shp.body[1:-1]): + txt.append(tuple(p[:2])+('%.2f'%v,)) + if shp.mtype=='slope': + pts = np.array(shp.body) + mid = (pts[:-1]+pts[1:])/2 + + dxy = (pts[:-1]-pts[1:]) + dxy[:,1][dxy[:,1]==0] = 1 + l = norm(dxy, axis=1)*-np.sign(dxy[:,1]) + ang = np.arccos(dxy[:,0]/l)/np.pi*180 + for v, p in zip(ang, mid): + txt.append(tuple(p[:2])+('%.2f'%v,)) + if shp.mtype=='area': + geom = shp.to_geom() + o = geom.centroid + txt.append((o.x, o.y)+('%.2f'%geom.area,)) + return txt + + def measure_mark(self): + txt = self.measure(self) + if len(txt)>0: + rms = [i for i in self.body if isinstance(i, Texts)] + for i in rms: self.body.remove(i) + self.body.append(Texts(txt)) + +class Coordinate(Point): mtype = 'coordinate' + +class Distance(Line): mtype = 'distance' + +class Area(Polygon): mtype = 'area' + +class Angle(Line): mtype = 'angle' + +class Slope(Line): mtype = 'slope' + if __name__ == '__main__': pts = Points([(10,10),(15,20)]) line = Line([(10,10),(15,20),(20,20)]) diff --git a/sciwx/canvas/canvas.py b/sciwx/canvas/canvas.py index f29d65bf..133b4446 100644 --- a/sciwx/canvas/canvas.py +++ b/sciwx/canvas/canvas.py @@ -2,7 +2,7 @@ from .boxutil import cross, multiply, merge, lay, mat, like from .imutil import mix_img from .mark import drawmark -from sciapp.object import Image, mark2shp, Layer, json2shp +from sciapp.object import Image, Shape, mark2shp, Layer, json2shp from sciapp.action import Tool, DefaultTool from time import time @@ -101,7 +101,8 @@ def fit(self): def update_box(self): box = [1e10, 1e10, -1e10, -1e10] for i in self.images: box = merge(box, i.box) - for i in self.marks.values(): box = merge(box, i.box) + shapes = [i for i in self.marks.values() if isinstance(i, Shape)] + for i in shapes: box = merge(box, i.box) if box[2]<=box[0]: box[0], box[2] = box[0]-1e-3, box[2]+1e-3 if box[1]<=box[3]: box[1], box[3] = box[1]-1e-3, box[3]+1e-3 if self.winbox and self.oribox == box: return @@ -156,7 +157,8 @@ def update(self, counter = [0,0]): for i in self.marks.values(): if i is None: continue if callable(i): - i(dc, self.to_panel_coor, k = self.scale) + i(dc, self.to_panel_coor, k=self.scale, cur=0, + winbox=self.winbox, oribox=self.oribox, conbox=self.conbox) else: drawmark(dc, self.to_panel_coor, i, k=self.scale, cur=0, winbox=self.winbox, oribox=self.oribox, conbox=self.conbox) @@ -195,12 +197,12 @@ def on_size(self, event): def on_idle(self, event): need = sum([i.dirty for i in self.images]) - need += sum([i.dirty for i in self.marks.values()]) - + shapes = [i for i in self.marks.values() if isinstance(i, Shape)] + need += sum([i.dirty for i in shapes]) if need==0: return else: for i in self.images: i.dirty = False - for i in self.marks.values(): i.dirty = False + for i in shapes: i.dirty = False return self.update() def on_paint(self, event): diff --git a/sciwx/canvas/mcanvas.py b/sciwx/canvas/mcanvas.py index 28cde2ac..bc7edfde 100644 --- a/sciwx/canvas/mcanvas.py +++ b/sciwx/canvas/mcanvas.py @@ -1,4 +1,4 @@ -import wx +import wx, numpy as np from numpy import ndarray import wx.lib.agw.aui as aui from .canvas import Canvas @@ -52,18 +52,25 @@ def image(self): return self.images[0] @property def back(self): return self.images[0].back + def draw_ruler(self, dc, f, **key): + dc.SetPen(wx.Pen((255,255,255), width=2, style=wx.SOLID)) + conbox, winbox, oribox = key['conbox'], key['winbox'], key['oribox'] + x1 = max(conbox[0], winbox[0])+5 + x2 = min(conbox[2], winbox[2])-5 + pixs = (x2-x1+10)*(oribox[2]-oribox[0])/10.0/(conbox[2]-conbox[0]) + h = min(conbox[3], winbox[3])-5 + dc.DrawLineList([(x1,h,x2,h)]) + dc.DrawLineList([(i,h,i,h-5) for i in np.linspace(x1, x2, 11)]) + dc.SetTextForeground((255,255,255)) + k, unit = self.image.unit + text = 'Unit = %.1f %s'%(k*pixs, unit) + dw, dh = dc.GetTextExtent(text) + dc.DrawText(text, (x2-dw, h-10-dh)) + def on_idle(self, event): - ''' - roi = None if not 'roi' in self.marks else self.marks['roi'] - mark = None if not 'mark' in self.marks else self.marks['mark'] - imark, cur = self.image.mark, self.image.cur - if not imark is None and imark.dtype=='layers': - imark = imark.body[cur] if cur in imark.body else None - - if not (roi is self.image.roi and mark is imark): - print(roi is self.image.roi, mark is imark) - self.image.dirty = True - ''' + if self.image.unit == (1, 'pix'): + if 'unit' in self.marks: del self.marks['unit'] + else: self.marks['unit'] = self.draw_ruler if self.image.roi is None: if 'roi' in self.marks: del self.marks['roi'] else: self.marks['roi'] = self.image.roi @@ -74,7 +81,6 @@ def on_idle(self, event): self.marks['mark'] = self.image.mark.body[self.image.cur] elif 'mark' in self.marks: del self.marks['mark'] else: self.marks['mark'] = self.image.mark - Canvas.on_idle(self, event) class VCanvas(Canvas): diff --git a/sciwx/widgets/__init__.py b/sciwx/widgets/__init__.py index d39955d8..4b471dab 100644 --- a/sciwx/widgets/__init__.py +++ b/sciwx/widgets/__init__.py @@ -3,6 +3,7 @@ from .colormap import CMapSelPanel, CMapSelCtrl from .curvepanel import CurvePanel from .histpanel import HistPanel +from .progressbar import ProgressBar from .normal import * from .toolbar import ToolBar from .menubar import MenuBar diff --git a/sciwx/widgets/progressbar.py b/sciwx/widgets/progressbar.py new file mode 100644 index 00000000..248fbf11 --- /dev/null +++ b/sciwx/widgets/progressbar.py @@ -0,0 +1,54 @@ +import threading, time, wx + +class ProgressBar ( wx.Panel ): + def __init__( self, parent ): + wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( -1,-1 ), style = wx.TAB_TRAVERSAL ) + + sizer = wx.BoxSizer( wx.HORIZONTAL ) + + self.lab_name = wx.StaticText( self, wx.ID_ANY, u"Process", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.lab_name.Wrap( -1 ) + sizer.Add( self.lab_name, 0, wx.RIGHT, 5 ) + + self.gau_bar = wx.Gauge( self, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL ) + self.gau_bar.SetValue( 0 ) + sizer.Add( self.gau_bar, 0, wx.ALL, 0 ) + + self.progress = [] + self.cur = 0 + self.SetSizer( sizer ) + self.Layout() + self.Fit() + + thread = threading.Thread(None, self.hold, ()) + thread.setDaemon(True) + thread.start() + + def SetValue(self, values=[]): + self.progress = values + + def hold(self): + span, c, t = 30, 0, 0 + while True: + time.sleep(0.1) + if len(self.progress)==0 and self.IsShown(): self.Hide() + if len(self.progress)>0 and not self.IsShown(): self.Show() + if len(self.progress)==0: continue + t = (t + 1)%span + if t==0: self.cur = (self.cur + 1)%len(self.progress) + name, f = self.progress[self.cur] + wx.CallAfter(self.lab_name.SetLabel, name) + if f() is None: + c = (c + 5)%200 + wx.CallAfter(self.gau_bar.SetValue, 100-abs(c-100)) + else: wx.CallAfter(self.gau_bar.SetValue, f()) + self.Layout() + self.GetParent().Layout() + +if __name__ == '__main__': + app = wx.App() + frame = wx.Frame(None) + pb = ProgressBar(frame) + pb.SetValue([('third', lambda : -1)]) + frame.Show() + app.MainLoop() diff --git a/sciwx/widgets/toolbar.py b/sciwx/widgets/toolbar.py index 0c57859e..10ed2da1 100644 --- a/sciwx/widgets/toolbar.py +++ b/sciwx/widgets/toolbar.py @@ -87,7 +87,6 @@ def add_tools(self, name, tools, fixed=True): def active_set(self, name): for n, tools in self.toolset: - print('select', name, n) for btn in tools: if n==name: btn.Show() if n!=name: btn.Hide() From bac0468457dd88fb69050dd6f7eac3f56b6c73ad Mon Sep 17 00:00:00 2001 From: qixinbo Date: Tue, 9 Jun 2020 14:10:41 +0800 Subject: [PATCH 231/343] logo directory is fixed using root_dir --- imagepy/__main__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/imagepy/__main__.py b/imagepy/__main__.py index 7987bd46..71eca94d 100644 --- a/imagepy/__main__.py +++ b/imagepy/__main__.py @@ -1,6 +1,7 @@ -import wx, sys +import wx, sys, os sys.path.append('../') from imagepy.core.app import loader, ImagePy +from imagepy import root_dir def extend_plgs(plg): if isinstance(plg, tuple): @@ -29,7 +30,7 @@ def extend_wgts(wgt): app = wx.App(False) - bitmap = wx.Bitmap('data/logolong.png', wx.BITMAP_TYPE_PNG) + bitmap = wx.Bitmap(os.path.join(root_dir, 'data/logolong.png'), wx.BITMAP_TYPE_PNG) shadow = wx.Colour(255,255,255) # SplashScreen(bitmap, wx.adv.SPLASH_CENTRE_ON_SCREEN | wx.adv.SPLASH_TIMEOUT, 3000, None, -1) From 593981866143ae57641cd2ffbd6b9f4e5b98bf4f Mon Sep 17 00:00:00 2001 From: qixinbo Date: Tue, 9 Jun 2020 14:46:28 +0800 Subject: [PATCH 232/343] get roi in 'ROI Remove' --- imagepy/menus/Selection/select_plg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/menus/Selection/select_plg.py b/imagepy/menus/Selection/select_plg.py index e5751a4a..6af28020 100644 --- a/imagepy/menus/Selection/select_plg.py +++ b/imagepy/menus/Selection/select_plg.py @@ -33,7 +33,7 @@ class RemoveFManager(Simple): para = {'name':''} def load(self, ips): - titles = self.app.manager('roi').gets('name') + titles = self.app.manager('roi').names() if len(titles)==0: self.app.alert('No roi in manager!') return False From f1c3f566d504ce4e47992c2b042b597d7eb7b6c2 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Tue, 9 Jun 2020 14:51:01 +0800 Subject: [PATCH 233/343] get roi in 'ROI Load' --- imagepy/menus/Selection/select_plg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/menus/Selection/select_plg.py b/imagepy/menus/Selection/select_plg.py index 6af28020..0c993122 100644 --- a/imagepy/menus/Selection/select_plg.py +++ b/imagepy/menus/Selection/select_plg.py @@ -50,7 +50,7 @@ class LoadRoi(Simple): para = {'name':''} def load(self, ips): - titles = self.app.manager('roi').gets('name') + titles = self.app.manager('roi').names() if len(titles)==0: self.app.alert('No roi in manager!') return False From 962f6db21f6b3a00fb61b7a2b2a1c319dd240a84 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Tue, 9 Jun 2020 14:57:01 +0800 Subject: [PATCH 234/343] get roi in 'ROI Union, Intersect, etc.' --- imagepy/menus/Selection/select_plg.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/imagepy/menus/Selection/select_plg.py b/imagepy/menus/Selection/select_plg.py index 0c993122..19911007 100644 --- a/imagepy/menus/Selection/select_plg.py +++ b/imagepy/menus/Selection/select_plg.py @@ -147,7 +147,7 @@ class Intersect(Simple): para = {'name':''} def load(self, ips): - titles = self.app.manager('roi').gets('name') + titles = self.app.manager('roi').names() if len(titles)==0: self.app.alert('No roi in manager!') return False @@ -166,7 +166,7 @@ class Union(Simple): para = {'name':''} def load(self, ips): - titles = self.app.manager('roi').gets('name') + titles = self.app.manager('roi').names() if len(titles)==0: self.app.alert('No roi in manager!') return False @@ -185,7 +185,7 @@ class Diff(Simple): para = {'name':''} def load(self, ips): - titles = self.app.manager('roi').gets('name') + titles = self.app.manager('roi').names() if len(titles)==0: self.app.alert('No roi in manager!') return False @@ -205,7 +205,7 @@ class SymDiff(Simple): para = {'name':''} def load(self, ips): - titles = self.app.manager('roi').gets('name') + titles = self.app.manager('roi').names() if len(titles)==0: self.app.alert('No roi in manager!') return False From 192eb847f162b24f24a11d761d6665b477a294a0 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Wed, 10 Jun 2020 21:42:29 +0800 Subject: [PATCH 235/343] raw plugin fixed --- imagepy/menus/File/Import/raw_plg.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/imagepy/menus/File/Import/raw_plg.py b/imagepy/menus/File/Import/raw_plg.py index 7847a523..53541350 100644 --- a/imagepy/menus/File/Import/raw_plg.py +++ b/imagepy/menus/File/Import/raw_plg.py @@ -15,9 +15,9 @@ class Plugin(Free): (list, 'c', [1,3], int, 'channel', '')] def load(self): - filt = 'RAW files (*.raw)|*.raw' - rst = IPy.getpath('Open..', filt, 'open', self.para) - if rst!=None:return True + filt = ['raw'] + self.para['path'] = self.app.getpath('Open..', filt, 'open', '') + return not self.para['path'] is None #process def run(self, para = None): @@ -27,10 +27,10 @@ def run(self, para = None): img = np.fromfile(para['path'], dtype=para['type']) sp = (para['h'], para['w'], para['c'])[:2 if para['c']==1 else 3] if img.size != para['h']*para['w']*para['c']: - IPy.alert('raw data error!') + self.app.alert('raw data error!') return img.shape = sp - IPy.show_img([img], fn) + self.app.show_img([img], fn) if __name__ == '__main__': print(Plugin.title) From 126d24be17382a6f40c92bcfff8ecda07724bbf6 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Wed, 10 Jun 2020 22:38:07 +0800 Subject: [PATCH 236/343] roi plugin fixed --- imagepy/menus/File/Import/roi_plg.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/imagepy/menus/File/Import/roi_plg.py b/imagepy/menus/File/Import/roi_plg.py index 61cd0565..826d3ada 100644 --- a/imagepy/menus/File/Import/roi_plg.py +++ b/imagepy/menus/File/Import/roi_plg.py @@ -19,8 +19,9 @@ class Plugin(Free): (int, 'height', (1, 3000), 0, 'height', 'pix')] def load(self): - filt = '|'.join(['%s files (*.%s)|*.%s' % (i.upper(), i, i) for i in ["zip"]]) - return IPy.getpath(self.title, filt, 'open', self.para) + filt = ['zip'] + self.para['path'] = self.app.getpath(self.title, filt, 'open', self.para['name']) + return not self.para['path'] is None def run(self, para=None): ls = read_roi.read_roi_zip(para['path']) @@ -40,4 +41,4 @@ def run(self, para=None): except Exception: ind = int(i.split("-")[-1]) img[rs, cs] = ind - IPy.show_img([img], para['name']) + self.app.show_img([img], para['name']) From 87433ce39e5231945758b5865749aa3e1017df29 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Thu, 11 Jun 2020 11:52:04 +0800 Subject: [PATCH 237/343] exit plugin fixed --- imagepy/menus/File/exit_plg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/menus/File/exit_plg.py b/imagepy/menus/File/exit_plg.py index 9f3ebcff..045b422d 100644 --- a/imagepy/menus/File/exit_plg.py +++ b/imagepy/menus/File/exit_plg.py @@ -10,4 +10,4 @@ class Plugin(Free): asyn = False def run(self, para = None): - IPy.curapp.Close() \ No newline at end of file + self.app.Close() \ No newline at end of file From 7edde3dca44c264d9edccba08cb4da1d91329c3d Mon Sep 17 00:00:00 2001 From: qixinbo Date: Fri, 12 Jun 2020 14:29:31 +0800 Subject: [PATCH 238/343] typo in shapeTool --- sciapp/action/tolact.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sciapp/action/tolact.py b/sciapp/action/tolact.py index c09c9fc7..02336864 100644 --- a/sciapp/action/tolact.py +++ b/sciapp/action/tolact.py @@ -55,7 +55,7 @@ def start(self, app): class ShapeTool(DefaultTool): default = None - title = 'Image Tool' + title = 'Shape Tool' def start(self, app): self.app = app From 98e74284df9f5be359cb2eb34121f8e970267a16 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 13 Jun 2020 00:07:50 +0800 Subject: [PATCH 239/343] imagej style --- imagepy/__main__.py | 57 +-- imagepy/core/app/__init__.py | 1 + imagepy/core/app/imagej.py | 368 ++++++++++++++++++ imagepy/core/app/imagepy.py | 1 - imagepy/core/app/startup.py | 52 +++ imagepy/data/config.json | 2 +- .../Region Analysis/regionprops_plgs.py | 1 - imagepy/menus/File/DICOM/dicom_plgs.py | 2 +- .../menus/Window/Windows Style/style_plgs.py | 4 +- sciapp/object/table.py | 4 + sciwx/canvas/mcanvas.py | 6 +- sciwx/demo/canvas2_m_demo.py | 8 +- sciwx/grid/widget.py | 12 +- sciwx/mesh/widget.py | 6 +- sciwx/widgets/toolbar.py | 3 +- 15 files changed, 451 insertions(+), 76 deletions(-) create mode 100644 imagepy/core/app/imagej.py create mode 100644 imagepy/core/app/startup.py diff --git a/imagepy/__main__.py b/imagepy/__main__.py index 71eca94d..c26a2c29 100644 --- a/imagepy/__main__.py +++ b/imagepy/__main__.py @@ -1,55 +1,4 @@ -import wx, sys, os +import sys sys.path.append('../') -from imagepy.core.app import loader, ImagePy -from imagepy import root_dir - -def extend_plgs(plg): - if isinstance(plg, tuple): - return (plg[0].title, extend_plgs(plg[1])) - elif isinstance(plg, list): - return [extend_plgs(i) for i in plg] - elif isinstance(plg, str): return plg - else: return (plg.title, plg) - -def extend_tols(tol): - if isinstance(tol, tuple) and isinstance(tol[1], list): - return (tol[0].title, extend_tols(tol[1])) - elif isinstance(tol, tuple) and isinstance(tol[1], str): - return (tol[1], tol[0]) - elif isinstance(tol, list): return [extend_tols(i) for i in tol] - -def extend_wgts(wgt): - if isinstance(wgt, tuple) and isinstance(wgt[1], list): - return (wgt[0].title, extend_wgts(wgt[1])) - elif isinstance(wgt, list): return [extend_wgts(i) for i in wgt] - else: return (wgt.title, wgt) - -if __name__ == '__main__': - from skimage.data import camera, astronaut - import wx.lib.agw.advancedsplash as AS - - app = wx.App(False) - - bitmap = wx.Bitmap(os.path.join(root_dir, 'data/logolong.png'), wx.BITMAP_TYPE_PNG) - shadow = wx.Colour(255,255,255) - # SplashScreen(bitmap, wx.adv.SPLASH_CENTRE_ON_SCREEN | wx.adv.SPLASH_TIMEOUT, 3000, None, -1) - - asp = AS.AdvancedSplash(None, bitmap=bitmap, timeout=1000, - agwStyle=AS.AS_TIMEOUT | - AS.AS_CENTER_ON_PARENT | - AS.AS_SHADOW_BITMAP, - shadowcolour=shadow) - - - frame = ImagePy(None) - - frame.load_menu(extend_plgs(loader.build_plugins('menus'))) - - frame.load_tool(extend_tols(loader.build_tools('tools')), 'Transform') - - frame.load_widget(extend_wgts(loader.build_widgets('widgets'))) - - #frame.show_img([camera()], 'camera') - #frame.show_img([astronaut()], 'astronaut') - frame.Show() - app.MainLoop() \ No newline at end of file +from core.app import startup +startup.start() \ No newline at end of file diff --git a/imagepy/core/app/__init__.py b/imagepy/core/app/__init__.py index c887d111..b6143f36 100644 --- a/imagepy/core/app/__init__.py +++ b/imagepy/core/app/__init__.py @@ -1 +1,2 @@ from .imagepy import ImagePy +from .imagej import ImageJ \ No newline at end of file diff --git a/imagepy/core/app/imagej.py b/imagepy/core/app/imagej.py new file mode 100644 index 00000000..0fadc92e --- /dev/null +++ b/imagepy/core/app/imagej.py @@ -0,0 +1,368 @@ +import wx, os, sys +import time, threading +sys.path.append('../../../') +import wx.lib.agw.aui as aui +from sciwx.widgets import MenuBar, ToolBar, ChoiceBook, ParaDialog +from sciwx.canvas import CanvasFrame +from sciwx.widgets import ProgressBar +from sciwx.grid import GridFrame +from sciwx.mesh import Canvas3DFrame +from sciwx.text import MDNoteFrame, TextNoteFrame +from sciwx.plot import PlotFrame +from skimage.data import camera +from sciapp import App, Source +from sciapp.object import Image +from imagepy import root_dir +from .source import * + +class ImageJ(wx.Frame, App): + def __init__( self, parent ): + wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = 'ImagePy', + size = wx.Size(-1,-1), pos = wx.DefaultPosition, + style = wx.RESIZE_BORDER|wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + App.__init__(self) + self.auimgr = aui.AuiManager() + self.auimgr.SetManagedWindow( self ) + self.SetSizeHints( wx.Size(600,-1) ) + + logopath = os.path.join(root_dir, 'data/logo.ico') + self.SetIcon(wx.Icon(logopath, wx.BITMAP_TYPE_ICO)) + + self.init_menu() + self.init_tool() + self.init_widgets() + self.init_text() + self.init_status() + self.Fit() + + self.Layout() + self.auimgr.Update() + self.Fit() + self.Centre( wx.BOTH ) + + self.Bind(wx.EVT_CLOSE, self.on_close) + self.Bind(aui.EVT_AUI_PANE_CLOSE, self.on_pan_close) + self.source() + + def source(self): + self.manager('color').add('front', (255, 255, 255)) + self.manager('color').add('back', (0, 0, 0)) + + def init_status(self): + self.stapanel = stapanel = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + sizersta = wx.BoxSizer( wx.HORIZONTAL ) + self.txt_info = wx.StaticText( stapanel, wx.ID_ANY, "ImagePy v0.2", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.txt_info.Wrap( -1 ) + sizersta.Add( self.txt_info, 1, wx.ALIGN_BOTTOM|wx.BOTTOM|wx.LEFT|wx.RIGHT, 2 ) + #self.pro_bar = wx.Gauge( stapanel, wx.ID_ANY, 100, wx.DefaultPosition, wx.Size( 100,15 ), wx.GA_HORIZONTAL ) + self.pro_bar = ProgressBar(stapanel) + sizersta.Add( self.pro_bar, 0, wx.ALL, 2 ) + stapanel.SetSizer(sizersta) + self.auimgr.AddPane( stapanel, aui.AuiPaneInfo() .Bottom() .CaptionVisible( False ).PinButton( True ) + .PaneBorder( False ).Dock().Resizable().FloatingSize( wx.DefaultSize ).DockFixed( True ) + . MinSize(wx.Size(-1, 20)). MaxSize(wx.Size(-1, 20)).Layer( 10 ) ) + + def load_menu(self, data): + self.menubar.load(data) + + def load_tool(self, data, default=None): + for i, (name, tols) in enumerate(data[1]): + self.toolbar.add_tools(name, tols, i==0) + if not default is None: self.toolbar.add_pop(os.path.join(root_dir, 'tools/drop.gif'), default) + self.toolbar.Layout() + + def load_widget(self, data): + self.widgets.load(data) + + def init_menu(self): + self.menubar = MenuBar(self) + + def init_tool(self): + sizer = wx.BoxSizer(wx.VERTICAL) + self.toolbar = ToolBar(self, False) + self.toolbar.Fit() + + self.auimgr.AddPane(self.toolbar, aui.AuiPaneInfo() .Top() .PinButton( True ).PaneBorder( False ) + .CaptionVisible( False ).Dock().FloatingSize( wx.DefaultSize ).MinSize(wx.Size( -1,34 )).DockFixed( True ) + . BottomDockable( False ).TopDockable( False ).Layer( 10 ) ) + + def set_background(self, img): + class ImgArtProvider(aui.AuiDefaultDockArt): + def __init__(self, img): + aui.AuiDefaultDockArt.__init__(self) + self.bitmap = wx.Bitmap(img, wx.BITMAP_TYPE_PNG) + + def DrawBackground(self, dc, window, orient, rect): + aui.AuiDefaultDockArt.DrawBackground(self, dc, window, orient, rect) + + memDC = wx.MemoryDC() + memDC.SelectObject(self.bitmap) + w, h = self.bitmap.GetWidth(), self.bitmap.GetHeight() + dc.Blit((rect[2]-w)//2, (rect[3]-h)//2, w, h, memDC, 0, 0, wx.COPY, True) + self.canvasnb.GetAuiManager().SetArtProvider(ImgArtProvider(img)) + + def add_task(self, task): + self.task_manager.add(task.title, task) + tasks = self.task_manager.gets() + tasks = [(p.title, lambda t=p:p.prgs) for n,p,t in tasks] + self.pro_bar.SetValue(tasks) + + def remove_task(self, task): + self.task_manager.remove(obj=task) + tasks = self.task_manager.gets() + tasks = [(p.title, lambda t=p:p.prgs) for n,p,t in tasks] + self.pro_bar.SetValue(tasks) + + def init_widgets(self): + self.widgets = ChoiceBook(self) + self.auimgr.AddPane( self.widgets, aui.AuiPaneInfo() .Right().Caption('Widgets') .PinButton( True ).Hide() + .Float().Resizable().FloatingSize( wx.DefaultSize ).MinSize( wx.Size( 266,300 ) ).Layer( 10 ) ) + + def init_text(self): + self.mdframe = MDNoteFrame(self, 'Sci Document') + self.txtframe = TextNoteFrame(self, 'Sci Text') + + def on_pan_close(self, event): + if event.GetPane().window in [self.toolbar, self.widgets]: + event.Veto() + if hasattr(event.GetPane().window, 'close'): + event.GetPane().window.close() + + def on_new_img(self, event): + self.add_img(event.GetEventObject().canvas.image) + self.add_img_win(event.GetEventObject().canvas) + + def on_close_img(self, event): + self.remove_img_win(event.GetEventObject().canvas) + self.remove_img(event.GetEventObject().canvas.image) + event.Skip() + + def on_new_tab(self, event): + self.add_tab(event.GetEventObject().grid.table) + self.add_tab_win(event.GetEventObject().grid) + + def on_close_tab(self, event): + self.remove_tab_win(event.GetEventObject().grid) + self.remove_tab(event.GetEventObject().grid.table) + event.Skip() + + def on_new_mesh(self, event): + self.add_mesh(event.GetEventObject().canvas.mesh) + self.add_mesh_win(event.GetEventObject().canvas) + + def on_close_mesh(self, event): + self.remove_mesh(event.GetEventObject().canvas.mesh) + self.remove_mesh_win(event.GetEventObject().canvas) + event.Skip() + + def set_info(self, value): + self.txt_info.SetLabel(value) + + def set_progress(self, value): + v = max(min(value, 100), 0) + self.pro_bar.SetValue(v) + if value==-1: + self.pro_bar.Hide() + elif not self.pro_bar.IsShown(): + self.pro_bar.Show() + self.stapanel.GetSizer().Layout() + self.pro_bar.Update() + + def on_close(self, event): + print('close') + #ConfigManager.write() + self.auimgr.UnInit() + del self.auimgr + self.Destroy() + Source.manager('config').write() + sys.exit() + + def _show_img(self, img, title=None): + cframe = CanvasFrame(self, True) + canvas = cframe.canvas + if not title is None: + canvas.set_imgs(img) + canvas.image.name = title + else: canvas.set_img(img) + cframe.Bind(wx.EVT_ACTIVATE, self.on_new_img) + cframe.Bind(wx.EVT_CLOSE, self.on_close_img) + cframe.Show() + + def show_img(self, img, title=None): + wx.CallAfter(self._show_img, img, title) + + def _show_table(self, tab, title): + cframe = GridFrame(self) + grid = cframe.grid + grid.set_data(tab) + if not title is None: + grid.table.name = title + cframe.Bind(wx.EVT_ACTIVATE, self.on_new_tab) + cframe.Bind(wx.EVT_CLOSE, self.on_close_tab) + cframe.Show() + + def show_table(self, tab, title=None): + wx.CallAfter(self._show_table, tab, title) + + def show_plot(self, title): + fig = PlotFrame(self) + fig.figure.title = title + return fig + + def show_md(self, cont, title='Document'): + page = self.mdframe.add_page() + page.set_cont(cont) + self.mdframe.Show() + + def _show_txt(self, cont, title='ImagePy'): + page = self.txtframe.add_notepad() + page.append(cont) + self.txtframe.Show() + + def show_txt(self, cont, title='ImagePy'): + wx.CallAfter(self._show_txt, cont, title) + + def _show_mesh(self, mesh=None, title=None): + if mesh is None: + cframe = Canvas3DFrame(self) + canvas = cframe.canvas + canvas.mesh.name = 'Surface' + + elif hasattr(mesh, 'vts'): + canvas = self.get_mesh_win() + if canvas is None: + cframe = Canvas3DFrame(self) + canvas = cframe.canvas + canvas.mesh.name = 'Surface' + canvas.add_surf(title, mesh) + else: + cframe = Canvas3DFrame(self) + canvas = cframe.canvas + canvas.set_mesh(mesh) + canvas.GetParent().Show() + canvas.GetParent().Bind(wx.EVT_ACTIVATE, self.on_new_mesh) + canvas.GetParent().Bind(wx.EVT_CLOSE, self.on_close_mesh) + self.add_mesh(canvas.mesh) + self.add_mesh_win(canvas) + + def show_mesh(self, mesh=None, title=None): + wx.CallAfter(self._show_mesh, mesh, title) + + def show_widget(self, panel, title='Widgets'): + obj = self.manager('widget').get(panel.title) + if obj is None: + pan = panel(self) + self.manager('widget').add(obj=pan, name=panel.title) + self.auimgr.AddPane(pan, aui.AuiPaneInfo().Caption(panel.title).Left().Layer( 15 ).PinButton( True ) + .Float().Resizable().FloatingSize( wx.DefaultSize ).Dockable(True)) #.DestroyOnClose()) + else: + info = self.auimgr.GetPane(obj) + info.Show(True) + self.Layout() + self.auimgr.Update() + + def switch_widget(self, visible=None): + info = self.auimgr.GetPane(self.widgets) + info.Show(not info.IsShown() if visible is None else visible) + self.auimgr.Update() + + def switch_toolbar(self, visible=None): + info = self.auimgr.GetPane(self.toolbar) + info.Show(not info.IsShown() if visible is None else visible) + self.auimgr.Update() + + def switch_table(self, visible=None): + info = self.auimgr.GetPane(self.tablenbwrap) + info.Show(not info.IsShown() if visible is None else visible) + self.auimgr.Update() + + def close_img(self, name=None): + names = self.get_img_name() if name is None else [name] + for name in names: + idx = self.canvasnb.GetPageIndex(self.get_img_win(name)) + self.remove_img(self.get_img_win(name).image) + self.remove_img_win(self.get_img_win(name)) + self.canvasnb.DeletePage(idx) + + def close_table(self, name=None): + names = self.get_tab_name() if name is None else [name] + for name in names: + idx = self.tablenb.GetPageIndex(self.get_tab_win(name)) + self.remove_tab(self.get_tab_win(name).table) + self.remove_tab_win(self.get_tab_win(name)) + self.tablenb.DeletePage(idx) + + def record_macros(self, cmd): + obj = self.manager('widget').get(name='Macros Recorder') + if obj is None or not obj.IsShown(): return + wx.CallAfter(obj.write, cmd) + + def run_macros(self, cmd, callafter=None): + cmds = [i for i in cmd] + def one(cmds, after): + cmd = cmds.pop(0) + title, para = cmd.split('>') + plg = Source.manager('plugin').get(name=title)() + after = lambda cmds=cmds: one(cmds, one) + if len(cmds)==0: after = callafter + wx.CallAfter(plg.start, self, eval(para), after) + one(cmds, None) + + def show(self, tag, cont, title): + tag = tag or 'img' + if tag=='img': + self.show_img([cont], title) + if tag=='imgs': + self.show_img(cont, title) + if tag=='macros': + self.run_macros(cont) + + def info(self, cont): + wx.CallAfter(self.txt_info.SetLabel, cont) + + def alert(self, info, title='ImagePy'): + dialog=wx.MessageDialog(self, info, title, wx.OK) + dialog.ShowModal() == wx.ID_OK + dialog.Destroy() + + def yes_no(self, info, title='ImagePy'): + dialog = wx.MessageDialog(self, info, title, wx.YES_NO | wx.CANCEL) + rst = dialog.ShowModal() + dialog.Destroy() + dic = {wx.ID_YES:'yes', wx.ID_NO:'no', wx.ID_CANCEL:'cancel'} + return dic[rst] + + def getpath(self, title, filt, io, name=''): + filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in filt]) + dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} + dialog = wx.FileDialog(self, title, '', name, filt, dic[io]) + rst = dialog.ShowModal() + path = dialog.GetPath() if rst == wx.ID_OK else None + dialog.Destroy() + return path + + def show_para(self, title, view, para, on_handle=None, on_ok=None, on_cancel=None, preview=False, modal=True): + dialog = ParaDialog(self, title) + dialog.init_view(view, para, preview, modal=modal, app=self) + dialog.Bind('cancel', on_cancel) + dialog.Bind('parameter', on_handle) + dialog.Bind('commit', on_ok) + return dialog.show() + +if __name__ == '__main__': + import numpy as np + import pandas as pd + + app = wx.App(False) + frame = ImageJ(None) + frame.Show() + frame.show_img([np.zeros((512, 512), dtype=np.uint8)], 'zeros') + #frame.show_img(None) + frame.show_table(pd.DataFrame(np.arange(100).reshape((10,10))), 'title') + ''' + frame.show_md('abcdefg', 'md') + frame.show_md('ddddddd', 'md') + frame.show_txt('abcdefg', 'txt') + frame.show_txt('ddddddd', 'txt') + ''' + app.MainLoop() \ No newline at end of file diff --git a/imagepy/core/app/imagepy.py b/imagepy/core/app/imagepy.py index 8bfd85bd..7798fb18 100644 --- a/imagepy/core/app/imagepy.py +++ b/imagepy/core/app/imagepy.py @@ -13,7 +13,6 @@ from sciapp import App, Source from sciapp.object import Image from imagepy import root_dir -from .source import * class ImagePy(wx.Frame, App): def __init__( self, parent ): diff --git a/imagepy/core/app/startup.py b/imagepy/core/app/startup.py new file mode 100644 index 00000000..da3ac84a --- /dev/null +++ b/imagepy/core/app/startup.py @@ -0,0 +1,52 @@ +import wx, sys +from .source import * +from sciapp import Source +from imagepy.core.app import loader, ImagePy, ImageJ + +def extend_plgs(plg): + if isinstance(plg, tuple): + return (plg[0].title, extend_plgs(plg[1])) + elif isinstance(plg, list): + return [extend_plgs(i) for i in plg] + elif isinstance(plg, str): return plg + else: return (plg.title, plg) + +def extend_tols(tol): + if isinstance(tol, tuple) and isinstance(tol[1], list): + return (tol[0].title, extend_tols(tol[1])) + elif isinstance(tol, tuple) and isinstance(tol[1], str): + return (tol[1], tol[0]) + elif isinstance(tol, list): return [extend_tols(i) for i in tol] + +def extend_wgts(wgt): + if isinstance(wgt, tuple) and isinstance(wgt[1], list): + return (wgt[0].title, extend_wgts(wgt[1])) + elif isinstance(wgt, list): return [extend_wgts(i) for i in wgt] + else: return (wgt.title, wgt) + +def start(): + from skimage.data import camera, astronaut + import wx.lib.agw.advancedsplash as AS + + app = wx.App(False) + + bitmap = wx.Bitmap('data/logolong.png', wx.BITMAP_TYPE_PNG) + shadow = wx.Colour(255,255,255) + + asp = AS.AdvancedSplash(None, bitmap=bitmap, timeout=1000, + agwStyle=AS.AS_TIMEOUT | + AS.AS_CENTER_ON_PARENT | + AS.AS_SHADOW_BITMAP, + shadowcolour=shadow) + asp.Update() + + uistyle = Source.manager('config').get('uistyle') or 'imagepy' + frame = ImageJ(None) if uistyle == 'imagej' else ImagePy(None) + frame.load_menu(extend_plgs(loader.build_plugins('menus'))) + frame.load_tool(extend_tols(loader.build_tools('tools')), 'Transform') + frame.load_widget(extend_wgts(loader.build_widgets('widgets'))) + frame.Fit() + #frame.show_img([camera()], 'camera') + #frame.show_img([astronaut()], 'astronaut') + frame.Show() + app.MainLoop() \ No newline at end of file diff --git a/imagepy/data/config.json b/imagepy/data/config.json index 062246ba..1c5d42fe 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null]] \ No newline at end of file +[["recent", ["C:\\Users\\54631\\Desktop\\\u6848\u4f8b\\\u80f8\u90e8CT\\0000.png"], null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["uistyle", "imagepy", null]] \ No newline at end of file diff --git a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py index 01c1ab89..b8993466 100644 --- a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py @@ -110,7 +110,6 @@ class RegionFilter(Filter): (float, 'solid', (-1, 1,), 1, 'solidity', 'ratio'), (float, 'e', (-100,100), 1, 'eccentricity', 'ratio')] - #process def run(self, ips, snap, img, para = None): k, unit = ips.unit strc = generate_binary_structure(2, 1 if para['con']=='4-connect' else 2) diff --git a/imagepy/menus/File/DICOM/dicom_plgs.py b/imagepy/menus/File/DICOM/dicom_plgs.py index af7dcb48..c4e04456 100644 --- a/imagepy/menus/File/DICOM/dicom_plgs.py +++ b/imagepy/menus/File/DICOM/dicom_plgs.py @@ -7,7 +7,7 @@ def imread(path): return pydicom.read_file(path, force=True).pixel_array -Source.manager('reader').add(name='dcm', obj=imread) +Source.manager('reader').add('dcm', imread) class OpenFile(fileio.Reader): title = 'DCM Open' diff --git a/imagepy/menus/Window/Windows Style/style_plgs.py b/imagepy/menus/Window/Windows Style/style_plgs.py index bddce12b..9c068985 100644 --- a/imagepy/menus/Window/Windows Style/style_plgs.py +++ b/imagepy/menus/Window/Windows Style/style_plgs.py @@ -5,14 +5,14 @@ class ImageJStyle(Free): title = 'Pay Tribute To ImageJ' asyn = False def run(self, para = None): - Source.manager('config').set('uistyle', 'ij') + Source.manager('config').add('uistyle', 'imagej') self.app.alert('Shown in ImageJ style when next setup!') class ImagePyStyle(Free): title = 'Elegant ImagePy Style' asyn = False def run(self, para = None): - Source.manager('config').set('uistyle', 'ipy') + Source.manager('config').add('uistyle', 'imagepy') self.app.alert('Shown in ImagePy style when next setup!') plgs = [ImageJStyle, ImagePyStyle] \ No newline at end of file diff --git a/sciapp/object/table.py b/sciapp/object/table.py index d98fdeee..cc69c7e0 100644 --- a/sciapp/object/table.py +++ b/sciapp/object/table.py @@ -70,6 +70,10 @@ def count_range(self): def select(self, rs=[], cs=[], byidx=False): if byidx: rs, cs = self.df.index[rs], self.df.columns[cs] self.rowmsk, self.colmsk = rs, cs + + @property + def info(self): + return '%sx%s %.2fM'%(*self.shape, self.nbytes/1024/1024) if __name__ == '__main__': import numpy as np diff --git a/sciwx/canvas/mcanvas.py b/sciwx/canvas/mcanvas.py index bc7edfde..9caa39b1 100644 --- a/sciwx/canvas/mcanvas.py +++ b/sciwx/canvas/mcanvas.py @@ -31,7 +31,7 @@ def set_log(self, log, b=False): def set_rg(self, rg, b=False): if b: self.back.rg = rg else: self.image.rg = rg - + def set_lut(self, lut, b=False): if b: self.back.lut = lut else: self.image.lut = lut @@ -188,6 +188,7 @@ def __init__(self, parent=None, autofit=False): def set_img(self, img, b=False): self.canvas.set_img(img, b) + self.canvas.update_box() self.update() def set_cn(self, cn, b=False): @@ -253,8 +254,7 @@ def on_idle(self, event): image, info = self.image, self.lab_info.GetLabel() imgs = image.slices, image.channels, image.cn, image.cur selfs = self.pages ,self.chans, self.cn, self.cur - if imgs != selfs or info!=self.image.info: - self.update() + if imgs != selfs or info!=self.image.info: self.update() def __del__(self): print('canvas panel del') diff --git a/sciwx/demo/canvas2_m_demo.py b/sciwx/demo/canvas2_m_demo.py index efa4ef91..4a7dd548 100644 --- a/sciwx/demo/canvas2_m_demo.py +++ b/sciwx/demo/canvas2_m_demo.py @@ -5,21 +5,21 @@ import wx def mcanvas_test(): - frame = wx.Frame(None, title='gray test') + frame = wx.Frame(None, title='gray test1') canvas = MCanvas(frame, autofit=True) canvas.set_img(astronaut()) canvas.set_cn((0,1,2)) frame.Show() def channels_test(): - frame = wx.Frame(None, title='gray test') + frame = wx.Frame(None, title='gray test2') canvas = MCanvas(frame, autofit=True) canvas.set_img(astronaut()) canvas.set_cn(0) frame.Show() def sequence_test(): - frame = wx.Frame(None, title='gray test') + frame = wx.Frame(None, title='gray test3') canvas = MCanvas(frame, autofit=True) canvas.set_imgs([astronaut(), 255-astronaut()]) canvas.set_cn(0) @@ -28,6 +28,6 @@ def sequence_test(): if __name__ == '__main__': app = wx.App() mcanvas_test() - channels_test() sequence_test() + channels_test() app.MainLoop() diff --git a/sciwx/grid/widget.py b/sciwx/grid/widget.py index 9acf595e..a1e33e67 100644 --- a/sciwx/grid/widget.py +++ b/sciwx/grid/widget.py @@ -11,7 +11,7 @@ def __init__(self, parent=None, autofit=False): self.lab_info = wx.StaticText( self, wx.ID_ANY, 'information', wx.DefaultPosition, wx.DefaultSize, 0 ) self.lab_info.Wrap( -1 ) - # self.lab_info.Hide() + self.Bind(wx.EVT_IDLE, self.on_idle) sizer.Add( self.lab_info, 0, wx.EXPAND, 0 ) self.grid = Grid(self) @@ -26,6 +26,10 @@ def table(self): return self.grid.table @property def name(self): return self.grid.table.name + def on_idle(self, event): + if self.lab_info.GetLabel() != self.table.info: + self.lab_info.SetLabel(self.table.info) + class GridFrame(wx.Frame): def __init__(self, parent=None): wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, @@ -41,10 +45,10 @@ def __init__(self, parent=None): self.Bind(wx.EVT_IDLE, self.on_idle) def on_idle(self, event): - if self.GetTitle()!=self.grid.table.title: - self.SetTitle(self.grid.table.title) + if self.GetTitle()!=self.grid.table.name: + self.SetTitle(self.grid.table.name) - def set_title(self, tab): self.SetTitle(tab.title) + def set_title(self, tab): self.SetTitle(tab.name) def on_valid(self, event): event.Skip() diff --git a/sciwx/mesh/widget.py b/sciwx/mesh/widget.py index eabd1f9c..5cfbc2a8 100644 --- a/sciwx/mesh/widget.py +++ b/sciwx/mesh/widget.py @@ -23,13 +23,11 @@ def __init__(self, parent=None): self.set_bright = self.canvas.set_bright self.add_surf_asyn = self.canvas.add_surf_asyn self.add_surf = self.canvas.add_surf - self.add_mark_asyn = self.canvas.add_mark_asyn - self.add_mark = self.canvas.add_mark self.set_mesh = self.canvas.set_mesh def on_idle(self, event): - if self.GetTitle()!=self.canvas.meshset.title: - self.SetTitle(self.canvas.meshset.title) + if self.GetTitle()!=self.canvas.mesh.name: + self.SetTitle(self.canvas.mesh.name) def set_title(self, tab): self.SetTitle(tab.title) diff --git a/sciwx/widgets/toolbar.py b/sciwx/widgets/toolbar.py index 10ed2da1..2461fa06 100644 --- a/sciwx/widgets/toolbar.py +++ b/sciwx/widgets/toolbar.py @@ -36,7 +36,7 @@ def __init__(self, parent, vertical=False): def on_tool(self, evt, tol): tol.start(self.app) - # evt.Skip() + evt.Skip() btn = evt.GetEventObject() #print(self.GetBackgroundColour()) #print(btn.GetClassDefaultAttributes().colFg) @@ -97,6 +97,7 @@ def add_pop(self, logo, default): btn = wx.BitmapButton(self, wx.ID_ANY, make_logo(logo), wx.DefaultPosition, (32,32), wx.BU_AUTODRAW|wx.RAISED_BORDER ) btn.Bind(wx.EVT_LEFT_DOWN, self.menu_drop) + btn.SetBackgroundColour(self.GetBackgroundColour()) self.GetSizer().Add(btn, 0, wx.ALL, 1) self.active_set(default) From c35edab00435b6cf702d6c78139e196ceb469395 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 15 Jun 2020 10:19:04 +0800 Subject: [PATCH 240/343] wx 4.1 --- imagepy/__init__.py | 1 - imagepy/core/app/imagej.py | 12 +++-- imagepy/core/app/imagepy.py | 12 +++-- imagepy/core/engine/table.py | 3 +- imagepy/core/util/fileio.py | 55 ++++++++++++-------- imagepy/core/util/tableio.py | 4 +- imagepy/data/config.json | 2 +- imagepy/menus/File/BMP/bmp_plgs.py | 8 +-- imagepy/menus/File/DAT/dat_plgs.py | 8 +-- imagepy/menus/File/DICOM/dicom_plgs.py | 3 +- imagepy/menus/File/GIF/__init__.py | 1 - imagepy/menus/File/GIF/animate_plgs.py | 34 ------------ imagepy/menus/File/GIF/gif_plgs.py | 33 ++++++++++-- imagepy/menus/File/JPG/jpg_plgs.py | 12 +++-- imagepy/menus/File/MAT/mat_plgs.py | 27 +++++----- imagepy/menus/File/Numpy/ndarray_plgs.py | 23 ++++---- imagepy/menus/File/PNG/png_plgs.py | 8 +-- imagepy/menus/File/TIF/__init__.py | 1 - imagepy/menus/File/TIF/tif3d_plgs.py | 25 --------- imagepy/menus/File/TIF/tif_plgs.py | 29 ++++++++--- imagepy/menus/File/save_plg.py | 4 +- imagepy/menus/Table/Table IO/tableio_plgs.py | 26 +++++---- sciwx/widgets/cmappanel.py | 1 + sciwx/widgets/curvepanel.py | 1 + sciwx/widgets/histpanel.py | 1 + sciwx/widgets/normal.py | 2 +- sciwx/widgets/paradialog.py | 6 +-- sciwx/widgets/progressbar.py | 28 +++++----- sciwx/widgets/viewport.py | 1 + 29 files changed, 194 insertions(+), 177 deletions(-) delete mode 100644 imagepy/menus/File/GIF/animate_plgs.py delete mode 100644 imagepy/menus/File/TIF/tif3d_plgs.py diff --git a/imagepy/__init__.py b/imagepy/__init__.py index 62699fa7..9fc44552 100644 --- a/imagepy/__init__.py +++ b/imagepy/__init__.py @@ -1,3 +1,2 @@ import os.path as osp - root_dir = osp.abspath(osp.dirname(__file__)) diff --git a/imagepy/core/app/imagej.py b/imagepy/core/app/imagej.py index 0fadc92e..fdc85df4 100644 --- a/imagepy/core/app/imagej.py +++ b/imagepy/core/app/imagej.py @@ -312,19 +312,25 @@ def show(self, tag, cont, title): tag = tag or 'img' if tag=='img': self.show_img([cont], title) - if tag=='imgs': + elif tag=='imgs': self.show_img(cont, title) - if tag=='macros': + elif tag=='tab': + self.show_table(cont, title) + elif tag=='macros': self.run_macros(cont) + else: self.alert('no view for %s!'%tag) def info(self, cont): wx.CallAfter(self.txt_info.SetLabel, cont) - def alert(self, info, title='ImagePy'): + def _alert(self, info, title='ImagePy'): dialog=wx.MessageDialog(self, info, title, wx.OK) dialog.ShowModal() == wx.ID_OK dialog.Destroy() + def alert(self, info, title='ImagePy'): + wx.CallAfter(self._alert, info, title) + def yes_no(self, info, title='ImagePy'): dialog = wx.MessageDialog(self, info, title, wx.YES_NO | wx.CANCEL) rst = dialog.ShowModal() diff --git a/imagepy/core/app/imagepy.py b/imagepy/core/app/imagepy.py index 7798fb18..758dd4e6 100644 --- a/imagepy/core/app/imagepy.py +++ b/imagepy/core/app/imagepy.py @@ -355,19 +355,25 @@ def show(self, tag, cont, title): tag = tag or 'img' if tag=='img': self.show_img([cont], title) - if tag=='imgs': + elif tag=='imgs': self.show_img(cont, title) - if tag=='macros': + elif tag=='tab': + self.show_table(cont, title) + elif tag=='macros': self.run_macros(cont) + else: self.alert('no view for %s!'%tag) def info(self, cont): wx.CallAfter(self.txt_info.SetLabel, cont) - def alert(self, info, title='ImagePy'): + def _alert(self, info, title='ImagePy'): dialog=wx.MessageDialog(self, info, title, wx.OK) dialog.ShowModal() == wx.ID_OK dialog.Destroy() + def alert(self, info, title='ImagePy'): + wx.CallAfter(self._alert, info, title) + def yes_no(self, info, title='ImagePy'): dialog = wx.MessageDialog(self, info, title, wx.YES_NO | wx.CANCEL) rst = dialog.ShowModal() diff --git a/imagepy/core/engine/table.py b/imagepy/core/engine/table.py index 46f259a3..850d118e 100644 --- a/imagepy/core/engine/table.py +++ b/imagepy/core/engine/table.py @@ -48,8 +48,7 @@ def ok(self, tps, para=None, callafter=None): threading.Thread(target = self.runasyn, args = (tps, tps.data, tps.snap, para, callafter)).start() else: self.runasyn(tps, tps.data, tps.snap, para, callafter) - win = Source.get('widget').get('obj', name='Macros Recorder') - if win!=None: win.write('{}>{}'.format(self.title, para)) + self.app.record_macros('{}>{}'.format(self.title, para)) def runasyn(self, tps, snap, data, para = None, callback = None): self.app.add_task(self) diff --git a/imagepy/core/util/fileio.py b/imagepy/core/util/fileio.py index ee39cd1d..6b88370b 100644 --- a/imagepy/core/util/fileio.py +++ b/imagepy/core/util/fileio.py @@ -1,7 +1,7 @@ import os from sciapp import Source from ... import root_dir -from ..engine import Free, Simple, Macros +from ..engine import Free, Simple, Table, Macros import numpy as np @@ -32,10 +32,11 @@ def add_recent(path): class Reader(Free): para = {'path':''} - tag, note = None, None + tag = None def show(self): - self.para['path'] = self.app.getpath('Open..', self.filt, 'open', '') + filt = [i.lower() for i in self.filt] + self.para['path'] = self.app.getpath('Open..', filt, 'open', '') return not self.para['path'] is None #process @@ -44,32 +45,42 @@ def run(self, para = None): fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) - reader = Source.manager('reader').gets(name=fe[1:], tag=self.tag) - print(fe, self.tag, self.note, reader) - ''' - if len(reader) == 0: - a, b = os.path.splitext(fn) - fn, fe = a, b+fe - reader = ReaderManager.gets(name=fe[1:], tag=self.tag, note=self.note) - if len(reader) is None: - return self.app.alert('No reader found for %s'%fe[1:]) - # ext, read, tag, note = reader - ''' - self.app.show(self.tag, reader[0][1](para['path']), fn) - -class Writer(Simple): + readers = Source.manager('reader').gets(name=fe[1:], tag=self.tag) + if len(readers)==0: + return self.app.alert('no reader found for %s file'%fe[1:]) + self.app.show(self.tag, readers[0][1](para['path']), fn) + +class ImageWriter(Simple): + tag = 'img' note = ['all'] para={'path':''} def show(self): - self.para['path'] = self.app.getpath('Save..', self.filt, 'save', '') + filt = [i.lower() for i in self.filt] + self.para['path'] = self.app.getpath('Save..', filt, 'save', '') return not self.para['path'] is None #process def run(self, ips, imgs, para = None): fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) - writer = Source.manager('writer').gets(name=fe[1:]) - if len(writer)==1: return writer[0][1](para['path'], ips.img) - writer = Source.manager('writer').get(fe[1:], 'imgs') - if len(writer)==1: return writer[0][1](para['path'], imgs) \ No newline at end of file + writer = Source.manager('writer').gets(name=fe[1:].lower(), tag=self.tag) + if len(writer)==1: writer[0][1](para['path'], ips.img if self.tag=='img' else imgs) + +class TableWriter(Table): + tag = 'tab' + note = ['all'] + para={'path':''} + + def show(self): + filt = [i.lower() for i in self.filt] + self.para['path'] = self.app.getpath('Save..', filt, 'save', '') + return not self.para['path'] is None + + #process + def run(self, tps, snap, data, para = None): + fp, fn = os.path.split(para['path']) + fn, fe = os.path.splitext(fn) + + writer = Source.manager('writer').gets(name=fe[1:], tag=self.tag) + if len(writer)==1: return writer[0][1](para['path'], data) \ No newline at end of file diff --git a/imagepy/core/util/tableio.py b/imagepy/core/util/tableio.py index 0595095c..5ec17a3a 100644 --- a/imagepy/core/util/tableio.py +++ b/imagepy/core/util/tableio.py @@ -1,3 +1,4 @@ +''' import os from ... import root_dir from ..engine import Free, Table, Macros @@ -34,4 +35,5 @@ def run(self, tps, snap, data, para = None): fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) write = Source.manager('writer').get(fe[1:], tag='tab') - write(para['path'], data) \ No newline at end of file + write(para['path'], data) +''' \ No newline at end of file diff --git a/imagepy/data/config.json b/imagepy/data/config.json index 1c5d42fe..f5ca91a4 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["recent", ["C:\\Users\\54631\\Desktop\\\u6848\u4f8b\\\u80f8\u90e8CT\\0000.png"], null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["uistyle", "imagepy", null]] \ No newline at end of file +[["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["recent", ["C:\\Users\\54631\\Desktop\\CaptureImage.bmp", "C:\\Users\\54631\\Desktop\\aaa\\a.xls", "C:\\Users\\54631\\Desktop\\aaa\\a.csv", "C:\\Users\\54631\\Desktop\\aaa\\as.gif"], null], ["uistyle", "imagepy", null]] \ No newline at end of file diff --git a/imagepy/menus/File/BMP/bmp_plgs.py b/imagepy/menus/File/BMP/bmp_plgs.py index 5c556e0b..fbfa0446 100644 --- a/imagepy/menus/File/BMP/bmp_plgs.py +++ b/imagepy/menus/File/BMP/bmp_plgs.py @@ -2,15 +2,17 @@ from skimage.io import imread, imsave from sciapp import Source -Source.manager('reader').add(name='bmp', obj=imread) -Source.manager('writer').add(name='bmp', obj=imsave) +Source.manager('reader').add('bmp', imread, 'img') +Source.manager('writer').add('bmp', imsave, 'img') class OpenFile(fileio.Reader): title = 'BMP Open' + tag = 'img' filt = ['BMP'] -class SaveFile(fileio.Writer): +class SaveFile(fileio.ImageWriter): title = 'BMP Save' + tag = 'img' filt = ['BMP'] plgs = [OpenFile, SaveFile] \ No newline at end of file diff --git a/imagepy/menus/File/DAT/dat_plgs.py b/imagepy/menus/File/DAT/dat_plgs.py index 0b57fb25..78839f4c 100644 --- a/imagepy/menus/File/DAT/dat_plgs.py +++ b/imagepy/menus/File/DAT/dat_plgs.py @@ -8,15 +8,17 @@ def imread(path): def imsave(path,img): np.savetxt(path,img) -Source.manager('reader').add(name='dat', obj=imread) -Source.manager('writer').add(name='dat', obj=imsave) +Source.manager('reader').add('dat', imread, 'img') +Source.manager('writer').add('dat', imsave, 'img') class OpenFile(fileio.Reader): title = 'DAT Open' + tag = 'img' filt = ['DAT'] -class SaveFile(fileio.Writer): +class SaveFile(fileio.ImageWriter): title = 'DAT Save' + tag = 'img' filt = ['DAT'] plgs = [OpenFile,SaveFile] \ No newline at end of file diff --git a/imagepy/menus/File/DICOM/dicom_plgs.py b/imagepy/menus/File/DICOM/dicom_plgs.py index c4e04456..1d5649e8 100644 --- a/imagepy/menus/File/DICOM/dicom_plgs.py +++ b/imagepy/menus/File/DICOM/dicom_plgs.py @@ -7,10 +7,11 @@ def imread(path): return pydicom.read_file(path, force=True).pixel_array -Source.manager('reader').add('dcm', imread) +Source.manager('reader').add('dcm', imread, 'img') class OpenFile(fileio.Reader): title = 'DCM Open' filt = ['DCM'] + tag = 'img' plgs = [OpenFile] \ No newline at end of file diff --git a/imagepy/menus/File/GIF/__init__.py b/imagepy/menus/File/GIF/__init__.py index ee081cf4..e69de29b 100644 --- a/imagepy/menus/File/GIF/__init__.py +++ b/imagepy/menus/File/GIF/__init__.py @@ -1 +0,0 @@ -catlog = ['gif_plgs', '-', 'animate_plgs'] \ No newline at end of file diff --git a/imagepy/menus/File/GIF/animate_plgs.py b/imagepy/menus/File/GIF/animate_plgs.py deleted file mode 100644 index 17bbe06a..00000000 --- a/imagepy/menus/File/GIF/animate_plgs.py +++ /dev/null @@ -1,34 +0,0 @@ -from imagepy.core.util import fileio -from imagepy.core.engine import Simple -import os, imageio -import numpy as np - -class SaveAnimate(Simple): - title = 'GIF Animate Save' - note = ['all'] - para={'path':'', 'dur':0.2} - view = [(int, 'dur', (0.01, 10), 2, 'duration', 's')] - - def load(self, ips): - filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in ['GIF']]) - return IPy.getpath('Save..', filt, 'save', self.para) - - #process - def run(self, ips, imgs, para = None): - imageio.mimsave(para['path'], imgs, 'GIF', duration = para['dur']) - -class OpenAnimate(fileio.Reader): - title = 'GIF Animate Open' - filt = ['GIF'] - note = ['8-bit', 'rgb', 'stack'] - - #process - def run(self, para = None): - #imgs = readGif(para['path']) - - imgs = imageio.mimread(para['path']) - fp, fn = os.path.split(para['path']) - fn, fe = os.path.splitext(fn) - IPy.show_img(imgs, fn) - -plgs = [OpenAnimate, SaveAnimate] \ No newline at end of file diff --git a/imagepy/menus/File/GIF/gif_plgs.py b/imagepy/menus/File/GIF/gif_plgs.py index 260ab267..648f9670 100644 --- a/imagepy/menus/File/GIF/gif_plgs.py +++ b/imagepy/menus/File/GIF/gif_plgs.py @@ -1,16 +1,41 @@ from imagepy.core.util import fileio +from imagepy.core.engine import Simple from skimage.io import imread, imsave from sciapp import Source +import imageio -Source.manager('reader').add(name='gif', obj=imread) -Source.manager('writer').add(name='gif', obj=imsave) +Source.manager('reader').add('gif', imread, 'img') +Source.manager('writer').add('gif', imsave, 'img') +Source.manager('reader').add('gif', imageio.mimread, 'imgs') class OpenFile(fileio.Reader): title = 'GIF Open' + tag = 'img' filt = ['GIF'] -class SaveFile(fileio.Writer): +class SaveFile(fileio.ImageWriter): title = 'GIF Save' + tag = 'img' filt = ['GIF'] -plgs = [OpenFile, SaveFile] \ No newline at end of file +class SaveAnimate(Simple): + title = 'GIF Animate Save' + note = ['all'] + filt = ['gif'] + para={'path':'', 'dur':0.2} + view = [(int, 'dur', (0.01, 10), 2, 'duration', 's')] + + def load(self, ips): + self.para['path'] = self.app.getpath('Save..', self.filt, 'save', '') + return not self.para['path'] is None + + def run(self, ips, imgs, para = None): + imageio.mimsave(para['path'], imgs, 'gif', duration = para['dur']) + +class OpenAnimate(fileio.Reader): + title = 'GIF Animate Open' + filt = ['GIF'] + tag = 'imgs' + note = ['8-bit', 'rgb', 'stack'] + +plgs = [OpenFile, SaveFile, '-', OpenAnimate, SaveAnimate] \ No newline at end of file diff --git a/imagepy/menus/File/JPG/jpg_plgs.py b/imagepy/menus/File/JPG/jpg_plgs.py index 34de84e3..b8ed873c 100644 --- a/imagepy/menus/File/JPG/jpg_plgs.py +++ b/imagepy/menus/File/JPG/jpg_plgs.py @@ -2,17 +2,19 @@ from imageio import imread, imsave from sciapp import Source -Source.manager('reader').add(name='jpg', obj=imread) -Source.manager('writer').add(name='jpg', obj=imsave) -Source.manager('reader').add(name='jpeg', obj=imread) -Source.manager('writer').add(name='jpeg', obj=imsave) +Source.manager('reader').add('jpg', imread, 'img') +Source.manager('writer').add('jpg', imsave, 'img') +Source.manager('reader').add('jpeg', imread, 'img') +Source.manager('writer').add('jpeg', imsave, 'img') class OpenFile(fileio.Reader): title = 'JPG Open' + tag = 'img' filt = ['JPG','JPEG'] -class SaveFile(fileio.Writer): +class SaveFile(fileio.ImageWriter): title = 'JPG Save' + tag = 'img' filt = ['JPG','JPEG'] plgs = [OpenFile, SaveFile] \ No newline at end of file diff --git a/imagepy/menus/File/MAT/mat_plgs.py b/imagepy/menus/File/MAT/mat_plgs.py index 7b8a1096..d6ddd6bd 100644 --- a/imagepy/menus/File/MAT/mat_plgs.py +++ b/imagepy/menus/File/MAT/mat_plgs.py @@ -3,33 +3,30 @@ from sciapp import Source import os -Source.manager('reader').add(name='mat', obj=lambda path: loadmat(path)['img']) -Source.manager('writer').add(name='mat', obj=lambda path, img: savemat(path, {'img':img})) +Source.manager('reader').add('mat', lambda path: loadmat(path)['img'], 'img') +Source.manager('writer').add('mat', lambda path, img: savemat(path, {'img':img}), 'img') +Source.manager('reader').add('mat', lambda path: loadmat(path)['img'], 'imgs') +Source.manager('writer').add('mat', lambda path, img: savemat(path, {'img':img}), 'imgs') class OpenFile(fileio.Reader): title = 'Mat Open' + tag = 'img' filt = ['Mat'] -class SaveFile(fileio.Writer): +class SaveFile(fileio.ImageWriter): title = 'Mat Save' - filt = ['mat'] + tag = 'img' + filt = ['Mat'] class Open3D(fileio.Reader): title = 'Mat 3D Open' + tag = 'imgs' filt = ['Mat'] - def run(self, para = None): - imgs = loadmat(para['path'])['img'] - fp, fn = os.path.split(para['path']) - fn, fe = os.path.splitext(fn) - IPy.show_img(imgs, fn) - -class Save3D(fileio.Writer): +class Save3D(fileio.ImageWriter): title = 'Mat 3D Save' - filt = ['mat'] + tag = 'imgs' + filt = ['Mat'] note = ['8-bit', 'rgb', 'stack'] - def run(self, ips, imgs, para = None): - savemat(para['path'], {'img':imgs}) - plgs = [OpenFile, SaveFile, '-', Open3D, Save3D] \ No newline at end of file diff --git a/imagepy/menus/File/Numpy/ndarray_plgs.py b/imagepy/menus/File/Numpy/ndarray_plgs.py index aa1e3d70..23b8eaeb 100644 --- a/imagepy/menus/File/Numpy/ndarray_plgs.py +++ b/imagepy/menus/File/Numpy/ndarray_plgs.py @@ -3,33 +3,30 @@ from sciapp import Source import os -Source.manager('reader').add(name='npy', obj=np.load) -Source.manager('writer').add(name='npy', obj=np.save) +Source.manager('reader').add('npy', np.load, 'img') +Source.manager('writer').add('npy', np.save, 'img') +Source.manager('reader').add('npy', np.load, 'imgs') +Source.manager('writer').add('npy', np.save, 'imgs') class OpenFile(fileio.Reader): title = 'Numpy Open' + tag = 'img' filt = ['npy'] -class SaveFile(fileio.Writer): +class SaveFile(fileio.ImageWriter): title = 'Numpy Save' + tag = 'img' filt = ['npy'] class Open3D(fileio.Reader): title = 'Numpy 3D Open' + tag = 'imgs' filt = ['npy'] - def run(self, para = None): - imgs = np.load(para['path']) - fp, fn = os.path.split(para['path']) - fn, fe = os.path.splitext(fn) - IPy.show_img(imgs, fn) - -class Save3D(fileio.Writer): +class Save3D(fileio.ImageWriter): title = 'Numpy 3D Save' + tag = 'imgs' filt = ['npy'] note = ['all', 'stack'] - def run(self, ips, imgs, para = None): - np.save(para['path'], imgs) - plgs = [OpenFile, SaveFile, '-', Open3D, Save3D] \ No newline at end of file diff --git a/imagepy/menus/File/PNG/png_plgs.py b/imagepy/menus/File/PNG/png_plgs.py index b13cba78..ad6d426b 100644 --- a/imagepy/menus/File/PNG/png_plgs.py +++ b/imagepy/menus/File/PNG/png_plgs.py @@ -2,15 +2,17 @@ from skimage.io import imread, imsave from sciapp import Source -Source.manager('reader').add(name='png', obj=imread) -Source.manager('writer').add(name='png', obj=imsave) +Source.manager('reader').add('png', imread, 'img') +Source.manager('writer').add('png', imsave, 'img') class OpenFile(fileio.Reader): title = 'PNG Open' + tag = 'img' filt = ['PNG'] -class SaveFile(fileio.Writer): +class SaveFile(fileio.ImageWriter): title = 'PNG Save' + tag = 'img' filt = ['PNG'] plgs = [OpenFile, SaveFile] \ No newline at end of file diff --git a/imagepy/menus/File/TIF/__init__.py b/imagepy/menus/File/TIF/__init__.py index e77a53b9..e69de29b 100644 --- a/imagepy/menus/File/TIF/__init__.py +++ b/imagepy/menus/File/TIF/__init__.py @@ -1 +0,0 @@ -catlog = ['tif_plgs', '-', 'tif3d_plgs'] \ No newline at end of file diff --git a/imagepy/menus/File/TIF/tif3d_plgs.py b/imagepy/menus/File/TIF/tif3d_plgs.py deleted file mode 100644 index 49db21af..00000000 --- a/imagepy/menus/File/TIF/tif3d_plgs.py +++ /dev/null @@ -1,25 +0,0 @@ -from skimage.io import imread, imsave -from imagepy.core.util import fileio -import os - -class Save(fileio.Writer): - title = 'TIF 3D Save' - filt = ['TIF'] - note = ['all', 'stack3d'] - - #process - def run(self, ips, imgs, para = None): - imsave(para['path'], imgs) - -class Open(fileio.Reader): - title = 'TIF 3D Open' - filt = ['TIF'] - - #process - def run(self, para = None): - imgs = imread(para['path']).transpose(2,0,1) - fp, fn = os.path.split(para['path']) - fn, fe = os.path.splitext(fn) - self.app.show_img(imgs, fn) - -plgs = [Open, Save] \ No newline at end of file diff --git a/imagepy/menus/File/TIF/tif_plgs.py b/imagepy/menus/File/TIF/tif_plgs.py index 7cd14de0..5f9b6749 100644 --- a/imagepy/menus/File/TIF/tif_plgs.py +++ b/imagepy/menus/File/TIF/tif_plgs.py @@ -2,16 +2,33 @@ from skimage.io import imread, imsave from sciapp import Source -Source.manager('reader').add(name='tif', obj=imread) -Source.manager('reader').add(name='tiff', obj=imread) -Source.manager('writer').add(name='tif', obj=imsave) +Source.manager('reader').add('tif', imread, 'img') +Source.manager('reader').add('tiff', imread, 'img') +Source.manager('writer').add('tif', imsave, 'img') -class OpenFile(fileio.Reader): +Source.manager('reader').add('tif', imread, 'imgs') +Source.manager('reader').add('tiff', imread, 'imgs') +Source.manager('writer').add('tif', imsave, 'imgs') + +class OpenTIF(fileio.Reader): title = 'TIF Open' + tag = 'img' filt = ['TIF', 'TIFF'] -class SaveFile(fileio.Writer): +class SaveTIF(fileio.ImageWriter): title = 'TIF Save' + tag = 'img' + filt = ['TIF'] + +class OpenTIFS(fileio.Reader): + title = 'TIF 3D Open' + tag = 'imgs' + filt = ['TIF', 'TIFF'] + +class SaveTIFS(fileio.ImageWriter): + title = 'TIF 3D Save' + tag = 'imgs' filt = ['TIF'] + note = ['all', 'stack3d'] -plgs = [OpenFile, SaveFile] \ No newline at end of file +plgs = [OpenTIF, SaveTIF, '-', OpenTIFS, SaveTIFS] \ No newline at end of file diff --git a/imagepy/menus/File/save_plg.py b/imagepy/menus/File/save_plg.py index cf6d0399..a1cd2744 100644 --- a/imagepy/menus/File/save_plg.py +++ b/imagepy/menus/File/save_plg.py @@ -7,14 +7,14 @@ from sciapp import Source from imagepy.core.engine import Simple -class SaveImage(fileio.Writer): +class SaveImage(fileio.ImageWriter): title = 'Save' def load(self, ips): self.filt = [i for i in sorted(Source.manager('writer').names())] return True -class WindowCapture(fileio.Writer): +class WindowCapture(fileio.ImageWriter): title = 'Save With Mark' filt = ['PNG'] diff --git a/imagepy/menus/Table/Table IO/tableio_plgs.py b/imagepy/menus/Table/Table IO/tableio_plgs.py index 9ec82ea7..a6d70aef 100644 --- a/imagepy/menus/Table/Table IO/tableio_plgs.py +++ b/imagepy/menus/Table/Table IO/tableio_plgs.py @@ -1,4 +1,4 @@ -from imagepy.core.util import tableio +from imagepy.core.util import fileio from pandas import read_csv, read_excel, read_hdf from sciapp import Source @@ -8,29 +8,33 @@ def show(data, title): # ViewerManager.add('tab', show) save_csv = lambda path, data:data.to_csv(path) -Source.manager('reader').add(name='csv', obj=read_csv, tag='tab') -Source.manager('writer').add(name='csv', obj=save_csv, tag='tab') +Source.manager('reader').add('csv', read_csv, 'tab') +Source.manager('writer').add('csv', save_csv, 'tab') -class OpenCSV(tableio.Reader): +class OpenCSV(fileio.Reader): title = 'CSV Open' + tag = 'tab' filt = ['csv'] -class SaveCSV(tableio.Writer): +class SaveCSV(fileio.TableWriter): title = 'CSV Save' + tag = 'tab' filt = ['csv'] save_excel = lambda path, data:data.to_excel(path) -Source.manager('reader').add(name='xls', obj=read_excel, tag='tab') -Source.manager('writer').add(name='xlsx', obj=read_excel, tag='tab') -Source.manager('reader').add(name='xls', obj=save_excel, tag='tab') -Source.manager('writer').add(name='xlsx', obj=save_excel, tag='tab') +Source.manager('reader').add('xls', read_excel, 'tab') +Source.manager('writer').add('xls', save_excel, 'tab') +Source.manager('reader').add('xlsx', read_excel, 'tab') +Source.manager('writer').add('xlsx', save_excel, 'tab') -class OpenExcel(tableio.Reader): +class OpenExcel(fileio.Reader): title = 'Excel Open' + tag = 'tab' filt = ['xls','xlsx'] -class SaveExcel(tableio.Writer): +class SaveExcel(fileio.TableWriter): title = 'Excel Save' + tag = 'tab' filt = ['xls', 'xlsx'] plgs = [OpenCSV, SaveCSV, '-', OpenExcel, SaveExcel] \ No newline at end of file diff --git a/sciwx/widgets/cmappanel.py b/sciwx/widgets/cmappanel.py index 8c79f033..d0aa8ff3 100644 --- a/sciwx/widgets/cmappanel.py +++ b/sciwx/widgets/cmappanel.py @@ -30,6 +30,7 @@ def update(self): self.dirty = True def init_buf(self): box = self.GetClientSize() + if min(box)==0: return self.buffer = wx.Bitmap(box.width, box.height) @classmethod diff --git a/sciwx/widgets/curvepanel.py b/sciwx/widgets/curvepanel.py index 7208e6c1..58443ccc 100644 --- a/sciwx/widgets/curvepanel.py +++ b/sciwx/widgets/curvepanel.py @@ -35,6 +35,7 @@ def lookup(cls, pts): def init_buf(self): box = self.GetClientSize() + if min(box)==0: return self.buffer = wx.Bitmap(box.width, box.height) def on_size(self, event): diff --git a/sciwx/widgets/histpanel.py b/sciwx/widgets/histpanel.py index 4f3bd48d..217a2242 100644 --- a/sciwx/widgets/histpanel.py +++ b/sciwx/widgets/histpanel.py @@ -22,6 +22,7 @@ def update(self): self.dirty = True def init_buf(self): box = self.GetClientSize() + if min(box)==0: return self.buffer = wx.Bitmap(box.width, box.height) def on_size(self, event): diff --git a/sciwx/widgets/normal.py b/sciwx/widgets/normal.py index 9b854944..7d7430b5 100644 --- a/sciwx/widgets/normal.py +++ b/sciwx/widgets/normal.py @@ -327,7 +327,7 @@ def __init__( self, parent, rang, accury, title, unit='', app=None): self.spin = wx.SpinButton( self, wx.ID_ANY, wx.DefaultPosition, wx.Size([20,-1][self.linux],-1), [0, wx.SP_HORIZONTAL][self.linux]) self.spin.SetRange(0, 255) self.spin.SetValue(128) - subsizer.Add( self.spin, 0, wx.ALIGN_CENTER|wx.BOTTOM|wx.EXPAND, 5 ) + subsizer.Add( self.spin, 0, wx.BOTTOM|wx.EXPAND, 5 ) self.lab_unit = wx.StaticText( self, wx.ID_ANY, unit, wx.DefaultPosition, wx.DefaultSize, 0 ) self.lab_unit.Wrap( -1 ) subsizer.Add( self.lab_unit, 0, wx.ALIGN_CENTER|wx.BOTTOM|wx.LEFT, 5 ) diff --git a/sciwx/widgets/paradialog.py b/sciwx/widgets/paradialog.py index ce43264e..10f6a4e1 100644 --- a/sciwx/widgets/paradialog.py +++ b/sciwx/widgets/paradialog.py @@ -33,13 +33,13 @@ def commit(self, state): def add_confirm(self, modal): sizer = wx.BoxSizer( wx.HORIZONTAL ) self.btn_ok = wx.Button( self, wx.ID_OK, 'OK', wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) - sizer.Add( self.btn_ok, 0, wx.ALIGN_RIGHT|wx.ALL, 5 ) + sizer.Add( self.btn_ok, 0, wx.ALL, 5 ) self.btn_cancel = wx.Button( self, wx.ID_CANCEL, 'Cancel', wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) - sizer.Add( self.btn_cancel, 0, wx.ALIGN_RIGHT|wx.ALL, 5 ) + sizer.Add( self.btn_cancel, 0, wx.ALL, 5 ) self.btn_help = wx.Button( self, wx.ID_HELP, 'Help', wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) - sizer.Add( self.btn_help, 0, wx.ALIGN_RIGHT|wx.ALL, 5 ) + sizer.Add( self.btn_help, 0, wx.ALL, 5 ) self.lst.Add(sizer, 0, wx.ALIGN_RIGHT, 5 ) self.btn_help.Bind(wx.EVT_BUTTON, lambda e: self.on_help and self.on_help()) if not modal: diff --git a/sciwx/widgets/progressbar.py b/sciwx/widgets/progressbar.py index 248fbf11..23503b43 100644 --- a/sciwx/widgets/progressbar.py +++ b/sciwx/widgets/progressbar.py @@ -31,19 +31,21 @@ def hold(self): span, c, t = 30, 0, 0 while True: time.sleep(0.1) - if len(self.progress)==0 and self.IsShown(): self.Hide() - if len(self.progress)>0 and not self.IsShown(): self.Show() - if len(self.progress)==0: continue - t = (t + 1)%span - if t==0: self.cur = (self.cur + 1)%len(self.progress) - name, f = self.progress[self.cur] - wx.CallAfter(self.lab_name.SetLabel, name) - if f() is None: - c = (c + 5)%200 - wx.CallAfter(self.gau_bar.SetValue, 100-abs(c-100)) - else: wx.CallAfter(self.gau_bar.SetValue, f()) - self.Layout() - self.GetParent().Layout() + try: + if len(self.progress)==0 and self.IsShown(): self.Hide() + if len(self.progress)>0 and not self.IsShown(): self.Show() + if len(self.progress)==0: continue + t = (t + 1)%span + if t==0: self.cur = (self.cur + 1)%len(self.progress) + name, f = self.progress[self.cur] + wx.CallAfter(self.lab_name.SetLabel, name) + if f() is None: + c = (c + 5)%200 + wx.CallAfter(self.gau_bar.SetValue, 100-abs(c-100)) + else: wx.CallAfter(self.gau_bar.SetValue, f()) + self.Layout() + self.GetParent().Layout() + except: pass if __name__ == '__main__': app = wx.App() diff --git a/sciwx/widgets/viewport.py b/sciwx/widgets/viewport.py index 60811fcc..c901db58 100644 --- a/sciwx/widgets/viewport.py +++ b/sciwx/widgets/viewport.py @@ -29,6 +29,7 @@ def update(self): self.dirty = True def init_buf(self): self.box = box = self.GetClientSize() + if min(box)==0: return self.buffer = wx.Bitmap(box.width, box.height) self.update() From 7e03b65aaca1320c998ee60122f9b3de6dbaa311 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 16 Jun 2020 18:04:54 +0800 Subject: [PATCH 241/343] workflow ok --- imagepy/core/app/imagej.py | 49 +++++-- imagepy/core/app/imagepy.py | 57 ++++++-- imagepy/core/app/loader.py | 23 +-- imagepy/core/util/fileio.py | 4 +- imagepy/data/config.json | 2 +- imagepy/menus/File/MarkDown/md_plg.py | 12 ++ imagepy/menus/File/__init__.py | 2 +- imagepy/menus/Plugins/Macros/recorder_plg.py | 21 ++- sciapp/app.py | 2 + sciwx/text/index1.htm | 0 sciwx/text/index2.htm | 0 sciwx/text/index3.htm | 133 ------------------ sciwx/text/mdpad.py | 2 +- sciwx/widgets/__init__.py | 1 + sciwx/widgets/choicebook.py | 5 +- sciwx/widgets/toolbar.py | 6 +- sciwx/widgets/workflow.py | 140 +++++++++++++++++++ 17 files changed, 273 insertions(+), 186 deletions(-) create mode 100644 imagepy/menus/File/MarkDown/md_plg.py delete mode 100644 sciwx/text/index1.htm delete mode 100644 sciwx/text/index2.htm delete mode 100644 sciwx/text/index3.htm create mode 100644 sciwx/widgets/workflow.py diff --git a/imagepy/core/app/imagej.py b/imagepy/core/app/imagej.py index fdc85df4..b92061a9 100644 --- a/imagepy/core/app/imagej.py +++ b/imagepy/core/app/imagej.py @@ -2,12 +2,12 @@ import time, threading sys.path.append('../../../') import wx.lib.agw.aui as aui -from sciwx.widgets import MenuBar, ToolBar, ChoiceBook, ParaDialog +from sciwx.widgets import MenuBar, ToolBar, ChoiceBook, ParaDialog, WorkFlowPanel from sciwx.canvas import CanvasFrame from sciwx.widgets import ProgressBar from sciwx.grid import GridFrame from sciwx.mesh import Canvas3DFrame -from sciwx.text import MDNoteFrame, TextNoteFrame +from sciwx.text import MDFrame, TextNoteFrame from sciwx.plot import PlotFrame from skimage.data import camera from sciapp import App, Source @@ -58,6 +58,13 @@ def init_status(self): self.pro_bar = ProgressBar(stapanel) sizersta.Add( self.pro_bar, 0, wx.ALL, 2 ) stapanel.SetSizer(sizersta) + class OpenDrop(wx.FileDropTarget): + def __init__(self, app): + wx.FileDropTarget.__init__(self) + self.app = app + def OnDropFiles(self, x, y, path): + self.app.run_macros(["Open>{'path':'%s'}"%i.replace('\\', '/') for i in path]) + stapanel.SetDropTarget(OpenDrop(self)) self.auimgr.AddPane( stapanel, aui.AuiPaneInfo() .Bottom() .CaptionVisible( False ).PinButton( True ) .PaneBorder( False ).Dock().Resizable().FloatingSize( wx.DefaultSize ).DockFixed( True ) . MinSize(wx.Size(-1, 20)). MaxSize(wx.Size(-1, 20)).Layer( 10 ) ) @@ -118,9 +125,9 @@ def init_widgets(self): self.auimgr.AddPane( self.widgets, aui.AuiPaneInfo() .Right().Caption('Widgets') .PinButton( True ).Hide() .Float().Resizable().FloatingSize( wx.DefaultSize ).MinSize( wx.Size( 266,300 ) ).Layer( 10 ) ) - def init_text(self): - self.mdframe = MDNoteFrame(self, 'Sci Document') - self.txtframe = TextNoteFrame(self, 'Sci Text') + def init_text(self): return + #self.mdframe = MDNoteFrame(self, 'Sci Document') + #self.txtframe = TextNoteFrame(self, 'Sci Text') def on_pan_close(self, event): if event.GetPane().window in [self.toolbar, self.widgets]: @@ -133,6 +140,7 @@ def on_new_img(self, event): self.add_img_win(event.GetEventObject().canvas) def on_close_img(self, event): + event.GetEventObject().Bind(wx.EVT_ACTIVATE, None) self.remove_img_win(event.GetEventObject().canvas) self.remove_img(event.GetEventObject().canvas.image) event.Skip() @@ -209,11 +217,27 @@ def show_plot(self, title): fig.figure.title = title return fig - def show_md(self, cont, title='Document'): - page = self.mdframe.add_page() - page.set_cont(cont) - self.mdframe.Show() + def _show_md(self, cont, title='ImagePy'): + mdframe = MDFrame(self) + mdframe.set_cont(cont) + mdframe.mdpad.title = title + mdframe.Show(True) + + def show_md(self, cont, title='ImagePy'): + wx.CallAfter(self._show_md, cont, title) + def _show_workflow(self, cont, title='ImagePy'): + pan = WorkFlowPanel(self) + pan.SetValue(cont) + info = aui.AuiPaneInfo(). DestroyOnClose(True). Left(). Caption(title) .PinButton( True ) \ + .Resizable().FloatingSize( wx.DefaultSize ).Dockable(False).Float().Top().Layer( 5 ) + pan.Bind(None, lambda x:self.run_macros(['%s>None'%x])) + self.auimgr.AddPane(pan, info) + self.auimgr.Update() + + def show_workflow(self, cont, title='ImagePy'): + wx.CallAfter(self._show_workflow, cont, title) + def _show_txt(self, cont, title='ImagePy'): page = self.txtframe.add_notepad() page.append(cont) @@ -302,6 +326,7 @@ def run_macros(self, cmd, callafter=None): def one(cmds, after): cmd = cmds.pop(0) title, para = cmd.split('>') + print(title, para) plg = Source.manager('plugin').get(name=title)() after = lambda cmds=cmds: one(cmds, one) if len(cmds)==0: after = callafter @@ -316,8 +341,12 @@ def show(self, tag, cont, title): self.show_img(cont, title) elif tag=='tab': self.show_table(cont, title) - elif tag=='macros': + elif tag=='mc': self.run_macros(cont) + elif tag=='md': + self.show_md(cont, title) + elif tag=='wf': + self.show_workflow(cont, title) else: self.alert('no view for %s!'%tag) def info(self, cont): diff --git a/imagepy/core/app/imagepy.py b/imagepy/core/app/imagepy.py index 758dd4e6..08e77935 100644 --- a/imagepy/core/app/imagepy.py +++ b/imagepy/core/app/imagepy.py @@ -2,12 +2,11 @@ import time, threading sys.path.append('../../../') import wx.lib.agw.aui as aui -from sciwx.widgets import MenuBar, ToolBar, ChoiceBook, ParaDialog +from sciwx.widgets import MenuBar, ToolBar, ChoiceBook, ParaDialog, WorkFlowPanel, ProgressBar from sciwx.canvas import CanvasNoteBook -from sciwx.widgets import ProgressBar from sciwx.grid import GridNoteBook from sciwx.mesh import Canvas3DNoteBook -from sciwx.text import MDNoteFrame, TextNoteFrame +from sciwx.text import MDNoteBook, TextNoteFrame from sciwx.plot import PlotFrame from skimage.data import camera from sciapp import App, Source @@ -59,6 +58,13 @@ def init_status(self): self.pro_bar = ProgressBar(stapanel) sizersta.Add( self.pro_bar, 0, wx.ALL, 2 ) stapanel.SetSizer(sizersta) + class OpenDrop(wx.FileDropTarget): + def __init__(self, app): + wx.FileDropTarget.__init__(self) + self.app = app + def OnDropFiles(self, x, y, path): + self.app.run_macros(["Open>{'path':'%s'}"%i.replace('\\', '/') for i in path]) + stapanel.SetDropTarget(OpenDrop(self)) self.auimgr.AddPane( stapanel, aui.AuiPaneInfo() .Bottom() .CaptionVisible( False ).PinButton( True ) .PaneBorder( False ).Dock().Resizable().FloatingSize( wx.DefaultSize ).DockFixed( True ) . MinSize(wx.Size(-1, 20)). MaxSize(wx.Size(-1, 20)).Layer( 10 ) ) @@ -73,6 +79,7 @@ def load_tool(self, data, default=None): self.toolbar.Layout() def load_widget(self, data): + print(self.widgets, '============') self.widgets.load(data) def init_menu(self): @@ -163,7 +170,17 @@ def init_widgets(self): .Dock().Resizable().FloatingSize( wx.DefaultSize ).MinSize( wx.Size( 266,-1 ) ).Layer( 10 ) ) def init_text(self): - self.mdframe = MDNoteFrame(self, 'Sci Document') + self.mdwarp = wx.Panel(self) + sizer = wx.BoxSizer( wx.VERTICAL ) + self.mdnb = MDNoteBook( self.mdwarp) + sizer.Add( self.mdnb, 1, wx.EXPAND |wx.ALL, 0 ) + self.mdwarp.SetSizer( sizer ) + self.mdwarp.Layout() + + self.auimgr.AddPane( self.mdwarp, aui.AuiPaneInfo() .Bottom() .CaptionVisible( True ).PinButton( True ).Float().Hide() + .MaximizeButton( True ).Resizable().FloatingSize((400, 400)).BestSize(( 120,120 )). Caption('MarkDown') . + BottomDockable( True ).TopDockable( False ).LeftDockable( True ).RightDockable( True ) ) + self.txtframe = TextNoteFrame(self, 'Sci Text') def on_pan_close(self, event): @@ -255,11 +272,29 @@ def show_plot(self, title): fig.figure.title = title return fig - def show_md(self, cont, title='Document'): - page = self.mdframe.add_page() + def _show_md(self, cont, title='ImagePy'): + page = self.mdnb.add_page() page.set_cont(cont) - self.mdframe.Show() + page.title = title + info = self.auimgr.GetPane(self.mdwarp) + info.Show(True) + self.auimgr.Update() + + def show_md(self, cont, title='ImagePy'): + wx.CallAfter(self._show_md, cont, title) + def _show_workflow(self, cont, title='ImagePy'): + pan = WorkFlowPanel(self) + pan.SetValue(cont) + info = aui.AuiPaneInfo(). DestroyOnClose(True). Left(). Caption(title) .PinButton( True ) \ + .Resizable().FloatingSize( wx.DefaultSize ).Dockable(True).Dock().Top().Layer( 5 ) + pan.Bind(None, pan.Bind(None, lambda x:self.run_macros(['%s>None'%x]))) + self.auimgr.AddPane(pan, info) + self.auimgr.Update() + + def show_workflow(self, cont, title='ImagePy'): + wx.CallAfter(self._show_workflow, cont, title) + def _show_txt(self, cont, title='ImagePy'): page = self.txtframe.add_notepad() page.append(cont) @@ -294,7 +329,7 @@ def show_mesh(self, mesh=None, title=None): def show_widget(self, panel, title='Widgets'): obj = self.manager('widget').get(panel.title) if obj is None: - pan = panel(self) + pan = panel(self, self) self.manager('widget').add(obj=pan, name=panel.title) self.auimgr.AddPane(pan, aui.AuiPaneInfo().Caption(panel.title).Left().Layer( 15 ).PinButton( True ) .Float().Resizable().FloatingSize( wx.DefaultSize ).Dockable(True)) #.DestroyOnClose()) @@ -359,8 +394,12 @@ def show(self, tag, cont, title): self.show_img(cont, title) elif tag=='tab': self.show_table(cont, title) - elif tag=='macros': + elif tag=='mc': self.run_macros(cont) + elif tag=='md': + self.show_md(cont, title) + elif tag=='wf': + self.show_workflow(cont, title) else: self.alert('no view for %s!'%tag) def info(self, cont): diff --git a/imagepy/core/app/loader.py b/imagepy/core/app/loader.py index 669baace..ee28fda2 100644 --- a/imagepy/core/app/loader.py +++ b/imagepy/core/app/loader.py @@ -27,25 +27,10 @@ def extend_plugins(path, lst, err): pt = os.path.join(root_dir,path) rst.append(Report(i[:-4], pt+'/'+i)) Source.manager('plugin').add(obj=rst[-1], name=rst[-1].title) - elif i[-3:] == '.mc': - pt = os.path.join(root_dir, path) - f = open(pt+'/'+i, 'r', 'utf-8') - cmds = f.readlines() - f.close() - rst.append(Macros(i[:-3], [getpath(pt, i) for i in cmds])) - Source.manager('plugin').add(obj=rst[-1], name=rst[-1].title) - elif i[-3:] == '.wf': - pt = os.path.join(root_dir,path) - f = open(pt+'/'+i, 'r', 'utf-8') - cmds = f.read() - f.close() - rst.append(WorkFlow(i[:-3], cmds)) - Source.manager('plugin').add(obj=rst[-1], name=rst[-1].title) - elif i[-3:] == '.md': - f = open(os.path.join(root_dir,path)+'/'+i, 'r', 'utf-8') - cont = f.read() - f.close() - rst.append(MkDown(i[:-3], cont)) + elif i[-3:] in {'.md', '.mc', '.wf'}: + p = os.path.join(os.path.join(root_dir,path), i).replace('\\','/') + opener = Macros(i[:-3], ['Open>{"path":"%s"}'%p]) + rst.append(opener) Source.manager('plugin').add(obj=rst[-1], name=rst[-1].title) elif i[-6:] in ['wgt.py', 'gts.py']: #try: diff --git a/imagepy/core/util/fileio.py b/imagepy/core/util/fileio.py index 6b88370b..44507f37 100644 --- a/imagepy/core/util/fileio.py +++ b/imagepy/core/util/fileio.py @@ -48,7 +48,9 @@ def run(self, para = None): readers = Source.manager('reader').gets(name=fe[1:], tag=self.tag) if len(readers)==0: return self.app.alert('no reader found for %s file'%fe[1:]) - self.app.show(self.tag, readers[0][1](para['path']), fn) + if not self.tag is None: + self.app.show(self.tag, readers[0][1](para['path']), fn) + else: self.app.show(readers[0][2], readers[0][1](para['path']), fn) class ImageWriter(Simple): tag = 'img' diff --git a/imagepy/data/config.json b/imagepy/data/config.json index f5ca91a4..eb8c58d3 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["recent", ["C:\\Users\\54631\\Desktop\\CaptureImage.bmp", "C:\\Users\\54631\\Desktop\\aaa\\a.xls", "C:\\Users\\54631\\Desktop\\aaa\\a.csv", "C:\\Users\\54631\\Desktop\\aaa\\as.gif"], null], ["uistyle", "imagepy", null]] \ No newline at end of file +[["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["recent", ["C:/Users/54631/Desktop/aaa/a.csv", "C:/Users/54631/Desktop/aaa/a.png", "C:\\Users\\54631\\Desktop\\aaa\\test.mc", "C:\\Users\\54631\\Desktop\\aaa\\Contribute Document.md"], null], ["uistyle", "imagepy", null]] \ No newline at end of file diff --git a/imagepy/menus/File/MarkDown/md_plg.py b/imagepy/menus/File/MarkDown/md_plg.py new file mode 100644 index 00000000..b28a6f50 --- /dev/null +++ b/imagepy/menus/File/MarkDown/md_plg.py @@ -0,0 +1,12 @@ +from imagepy.core.util import fileio +from sciapp import Source + +def read(path): + with open(path) as f: return f.read() + +Source.manager('reader').add('md', read, 'md') + +class Plugin(fileio.Reader): + title = 'MarkDown Open' + tag = 'md' + filt = ['MD'] \ No newline at end of file diff --git a/imagepy/menus/File/__init__.py b/imagepy/menus/File/__init__.py index 0577219f..1c4d8c47 100644 --- a/imagepy/menus/File/__init__.py +++ b/imagepy/menus/File/__init__.py @@ -1,3 +1,3 @@ ### TODO: Fixme! In this directory, many path should be corrected?! catlog = ['new_plg', '-', 'open_plg', 'save_plg', '-', 'Open Recent', 'Samples Local', 'Samples Online', - 'Samples ImageJ', '-', 'Import', 'Export', '-', 'BMP', 'JPG', 'PNG', 'TIF', 'GIF', 'DICOM', 'DAT', 'Numpy', 'MAT', '-', 'exit_plg'] \ No newline at end of file + 'Samples ImageJ', '-', 'Import', 'Export', '-', 'BMP', 'JPG', 'PNG', 'TIF', 'GIF', 'DICOM', 'DAT', 'Numpy', 'MAT', '-', 'MarkDown', '-', 'exit_plg'] \ No newline at end of file diff --git a/imagepy/menus/Plugins/Macros/recorder_plg.py b/imagepy/menus/Plugins/Macros/recorder_plg.py index 394e5962..20d3d091 100644 --- a/imagepy/menus/Plugins/Macros/recorder_plg.py +++ b/imagepy/menus/Plugins/Macros/recorder_plg.py @@ -2,12 +2,23 @@ from sciapp import Source def readmc(path): - with open(path) as f: - return f.readlines() + with open(path) as f: return f.readlines() -class Plugin(fileio.Reader): +Source.manager('reader').add('mc', readmc, 'mc') + +class Macros(fileio.Reader): title = 'Run Macros' - tag = 'macros' + tag = 'mc' filt = ['MC'] -Source.manager('reader').add(name='mc', tag='macros', obj=readmc) \ No newline at end of file +def readwf(path): + with open(path) as f: return f.read() + +Source.manager('reader').add('wf', readwf, 'wf') + +class WorkFlow(fileio.Reader): + title = 'Run WorkFlow' + tag = 'wf' + filt = ['wf'] + +plgs = [Macros, WorkFlow] \ No newline at end of file diff --git a/sciapp/app.py b/sciapp/app.py index bbefa69b..f9fa1e12 100644 --- a/sciapp/app.py +++ b/sciapp/app.py @@ -83,10 +83,12 @@ def add_img(self, img): if not self.img_manager.has(img.name, obj=img): img.name = self.img_manager.name(img.name) self.img_manager.add(img.name, img) + print(self.img_manager.objs, 'open') def remove_img(self, img): print('remove', img.name) self.img_manager.remove(obj=img) + print(self.img_manager.objs, 'close') def add_img_win(self, win): self.wimg_manager.add(win.name, win) diff --git a/sciwx/text/index1.htm b/sciwx/text/index1.htm deleted file mode 100644 index e69de29b..00000000 diff --git a/sciwx/text/index2.htm b/sciwx/text/index2.htm deleted file mode 100644 index e69de29b..00000000 diff --git a/sciwx/text/index3.htm b/sciwx/text/index3.htm deleted file mode 100644 index 59668291..00000000 --- a/sciwx/text/index3.htm +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - - - - - - - - -

Demo Plugin

-

Path: https://github.com/Image-Py/demoplugin

-

Version: 0.1

-

Author: YXDragon

-

Email: yxdragon@imagepy.org

-

Keyword: demo, tutorial

-

Description: a friendly development tutorial.

-

English Document | ĵ

-

This is a demo project to show How to write ImagePy plugin. Including the usage of all kinds of plugin, with document wrote in detail. Developers can take this project as example.

-

Install

-

ImagePy MenuPlugins > Manager > Plugins Manager input demo, and select the Demo Plugin, then click Install/Update. When complete the installing, the user interface would be changed. New plugins' menu, tool, and widget would be loaded in place.

-

06 -

Install DemoPlugin

-

Basic

-

Start here

-
    -
  1. What is plugin
  2. -
  3. Hello Worldmy first plugin
  4. -
  5. Who Are Youinteractive
  6. -
  7. Questionnaireparameter dialog in detail
  8. -
  9. Multi plugin in one file
  10. -
-

Plugin development

-

Markdown: document

-
    -
  1. Markdown Demo
  2. -
-

Macros: serialise existing function

-
    -
  1. Gaussian blur - Invert
  2. -
  3. Coins Segmentation Macros
  4. -
-

Workflow: interactive macros

-
    -
  1. Coins Segment Workflow
  2. -
-

Report: generate report

-
    -
  1. Personal Information
  2. -
  3. Coins Report: report for coins segment
  4. -
  5. Rule of Report design
  6. -
-

Filter: image filter in 2d

-
    -
  1. Invert Demo: without parameter
  2. -
  3. Gaussian Demo: with parameter
  4. -
  5. Filter operating mechanism
  6. -
-

Simple: treat sequence and other attributes

-
    -
  1. Gaussian 3D Demo: filter in 3d
  2. -
  3. Red Lut Demo: operate color lookup table
  4. -
  5. ROI Inflate Demo: operate ROI
  6. -
  7. Unit Demo: set unit and scale
  8. -
  9. Draw Mark Demo: Set Mark
  10. -
  11. Simple operating mechanism
  12. -
-

Table: treat dataframe

-
    -
  1. Generate Table Demo: generate table
  2. -
  3. Sort By Key Demo: sort
  4. -
  5. Table Plot Demo: plot
  6. -
  7. Table operation mechanism
  8. -
-

Free: depend on nothing

-
    -
  1. New Image Demo: creat image
  2. -
  3. About Demo: the about dialog
  4. -
  5. Close Demo: quit program
  6. -
  7. Free operating mechanism
  8. -
-

Tool: mouse interaction

-
    -
  1. Painter Demo: draw with mouse
  2. -
  3. Tool operating mechanism
  4. -
-

Widget: customed panel

-
    -
  1. Widget Demo
  2. -
  3. Widget opterating mechanism
  4. -
-

Plugin Release

-

Function Organization

-
    -
  1. Functional partitioning
  2. -
  3. Set Order
  4. -
-

Plugin project creation

-
    -
  1. Create a plugin project repository
  2. -
  3. Write requirements
  4. -
  5. Write readme
  6. -
  7. Install Plugin
  8. -
-

Release to ImagePy

-
    -
  1. Send Pull Request to ImagePy
  2. -
  3. About the top-level menu
  4. -
-

Write Document

-

Write The Opteration Manual

-

View The Operation Manual

-

Attention

-

User Friendliness

-

Developer Friendliness

-

Communicate Timely

-

This document introduces how to write ImagePy plugin. More questions not exhaustive hereplease post in forum.Image.sc

- - - \ No newline at end of file diff --git a/sciwx/text/mdpad.py b/sciwx/text/mdpad.py index fac0361f..83d9ecf1 100644 --- a/sciwx/text/mdpad.py +++ b/sciwx/text/mdpad.py @@ -18,7 +18,7 @@ def __init__(self, parent, cont='', url='', title='markdown pad'): self.title = title def set_cont(self, value, url=''): - # self.wv.SetPage(md2html(value), url) + return self.wv.SetPage(md2html(value), url) # I do not know why use SetPage the js would not run, So I write a file here here = osp.split(osp.abspath(__file__)) for n in range(1,10): diff --git a/sciwx/widgets/__init__.py b/sciwx/widgets/__init__.py index 4b471dab..643ef710 100644 --- a/sciwx/widgets/__init__.py +++ b/sciwx/widgets/__init__.py @@ -9,4 +9,5 @@ from .menubar import MenuBar from .viewport import ViewPort from .choicebook import ChoiceBook +from .workflow import WorkFlowPanel from . import util \ No newline at end of file diff --git a/sciwx/widgets/choicebook.py b/sciwx/widgets/choicebook.py index d607c122..f51cfc1c 100644 --- a/sciwx/widgets/choicebook.py +++ b/sciwx/widgets/choicebook.py @@ -1,14 +1,15 @@ import wx class ChoiceBook(wx.ScrolledWindow): - def __init__(self, parent): + def __init__(self, parent, app=None): wx.ScrolledWindow.__init__(self, parent, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.HSCROLL|wx.VSCROLL ) + self.app = app or parent self.SetSizer(wx.BoxSizer( wx.VERTICAL )) def add_wgts(self, name, wgts): book = wx.Choicebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.CHB_DEFAULT ) for name, wgt in wgts: - book.AddPage(wgt(book, self.GetParent()), name, False ) + book.AddPage(wgt(book, self.app), name, False ) self.GetSizer().Add( book, 0, wx.EXPAND |wx.ALL, 0 ) self.Layout() self.GetSizer().Fit(self) diff --git a/sciwx/widgets/toolbar.py b/sciwx/widgets/toolbar.py index 2461fa06..19689bb2 100644 --- a/sciwx/widgets/toolbar.py +++ b/sciwx/widgets/toolbar.py @@ -50,11 +50,9 @@ def on_config(self, evt, tol): self.app.show_para(tol.title, tol.view, tol.para) tol.config() - def on_help(self, evt, tol): - pass + def on_help(self, evt, tol): pass - def on_info(self, event, tol): - pass + def on_info(self, event, tol): pass def bind(self, btn, tol): obj = tol() diff --git a/sciwx/widgets/workflow.py b/sciwx/widgets/workflow.py new file mode 100644 index 00000000..600304bc --- /dev/null +++ b/sciwx/widgets/workflow.py @@ -0,0 +1,140 @@ +import wx + +def parse(cont): + ls = cont.split('\n') + workflow = {'title':ls[0], 'chapter':[]} + for line in ls[2:]: + line = line.strip() + if line == '':continue + if line.startswith('## '): + chapter = {'title':line[3:], 'section':[]} + workflow['chapter'].append(chapter) + elif line[1:3] == '. ': + section = {'title':line[3:]} + else: + section['hint'] = line + chapter['section'].append(section) + return workflow + +class WorkFlowPanel ( wx.Panel ): + def __init__( self, parent ): + wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.TAB_TRAVERSAL ) + self.app, self.f = parent, print + + def SetValue(self, cont): + self.workflow, self.cont = parse(cont), cont + sizer_scroll = wx.BoxSizer( wx.HORIZONTAL ) + + self.scr_workflow = wx.ScrolledCanvas( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize) + self.scr_workflow.SetScrollRate( 30, 0 ) + self.scr_workflow.ShowScrollbars(wx.SHOW_SB_NEVER, wx.SHOW_SB_NEVER) + self.scr_workflow.SetMinSize((600,-1)) + + sizer_chapter = wx.BoxSizer( wx.HORIZONTAL ) + self.spn_scroll = wx.SpinButton( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.SP_HORIZONTAL ) + sizer_scroll.Add( self.spn_scroll, 0, wx.ALL|wx.EXPAND, 3 ) + + for chapter in self.workflow['chapter']: + self.pan_chapter = wx.Panel( self.scr_workflow, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER|wx.TAB_TRAVERSAL ) + sizer_frame = wx.BoxSizer( wx.VERTICAL ) + + self.lab_chapter = wx.StaticText( self.pan_chapter, wx.ID_ANY, chapter['title'], wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE ) + self.lab_chapter.Wrap( -1 ) + self.lab_chapter.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_INACTIVECAPTION ) ) + + sizer_frame.Add( self.lab_chapter, 0, wx.ALL|wx.EXPAND, 0 ) + + sizer_section = wx.BoxSizer( wx.HORIZONTAL ) + for section in chapter['section']: + btn = wx.Button( self.pan_chapter, wx.ID_ANY, section['title'], wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) + sizer_section.Add( btn, 0, wx.ALL, 3 ) + btn.Bind(wx.EVT_BUTTON, lambda e, x=section['title']: self.f(x)) + btn.Bind( wx.EVT_ENTER_WINDOW, lambda e, info=section['hint']: self.set_info(info)) + #self.m_button1.Bind( wx.EVT_LEAVE_WINDOW, self.on_out ) + + sizer_frame.Add( sizer_section, 0, wx.EXPAND, 3 ) + sizer_btn = wx.BoxSizer( wx.HORIZONTAL ) + sizer_btn.AddStretchSpacer(1) + + self.btn_snap = wx.StaticText( self.pan_chapter, wx.ID_ANY, u" Snap ", wx.DefaultPosition, wx.DefaultSize, 0|wx.SIMPLE_BORDER ) + self.btn_snap.Wrap( -1 ) + sizer_btn.Add( self.btn_snap, 0, wx.ALL, 3 ) + + self.btn_load = wx.StaticText( self.pan_chapter, wx.ID_ANY, u" Load ", wx.DefaultPosition, wx.DefaultSize, 0|wx.SIMPLE_BORDER ) + self.btn_load.Wrap( -1 ) + sizer_btn.Add( self.btn_load, 0, wx.ALL, 3 ) + + self.btn_step = wx.StaticText( self.pan_chapter, wx.ID_ANY, u" >> ", wx.DefaultPosition, wx.DefaultSize, 0|wx.SIMPLE_BORDER ) + self.btn_step.Wrap( -1 ) + sizer_btn.Add( self.btn_step, 0, wx.ALL, 3 ) + sizer_frame.Add( sizer_btn, 0, wx.EXPAND, 3 ) + + + self.pan_chapter.SetSizer( sizer_frame ) + self.pan_chapter.Layout() + sizer_frame.Fit( self.pan_chapter ) + sizer_chapter.Add( self.pan_chapter, 0, wx.EXPAND |wx.ALL, 3 ) + + sizer_scroll.Add( self.scr_workflow, 1, wx.EXPAND |wx.ALL, 0) + sizer_info = wx.BoxSizer( wx.VERTICAL ) + sizer_info.SetMinSize( wx.Size( 260,-1 ) ) + self.btn_help = wx.StaticText( self, wx.ID_ANY, u" Click For Detail Document ", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE|wx.SIMPLE_BORDER ) + self.btn_help.Wrap( -1 ) + self.btn_help.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_INACTIVECAPTION ) ) + + sizer_info.Add( self.btn_help, 0, wx.ALL|wx.EXPAND, 0 ) + + self.txt_info = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_AUTO_URL|wx.TE_MULTILINE|wx.TE_READONLY ) + sizer_info.Add( self.txt_info, 1, wx.TOP|wx.EXPAND, 3 ) + + sizer_scroll.Add( sizer_info, 0, wx.EXPAND |wx.ALL, 3) + + self.scr_workflow.SetSizer( sizer_chapter ) + self.scr_workflow.Layout() + + self.SetSizer( sizer_scroll ) + + #self.Fit() + self.Layout() + + self.spn_scroll.Bind( wx.EVT_SPIN, self.on_spn ) + self.btn_help.Bind( wx.EVT_LEFT_DOWN, self.on_help ) + + def set_info(self, info): + self.txt_info.SetValue(info) + + def on_spn(self, event): + v = self.spn_scroll.GetValue() + self.scr_workflow.Scroll(v, 0) + self.spn_scroll.SetValue(self.scr_workflow.GetViewStart()[0]) + + def Bind(self, event, f=print): self.f = f + + def on_help(self, event): + self.app.show_md(self.cont, self.workflow['title']) + +if __name__ == '__main__': + + cont = '''Title +===== +## Chapter1 +1. Section1 +some coment for section1 ... +2. Section2 +some coment for section2 ... +## Chapter2 +1. Section1 +some coment for section1 ... +2. Section2 +some coment for section2 ... +''' + + app = wx.App() + frame = wx.Frame(None) + sizer = wx.BoxSizer(wx.VERTICAL) + wf = WorkFlowPanel(frame) + wf.SetValue(cont) + sizer.Add(wf, 0, wx.EXPAND, 0 ) + frame.SetSizer(sizer) + frame.Show() + app.MainLoop() \ No newline at end of file From b6219375f95fb150d877a12a7b9f6595e45a0a77 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Wed, 17 Jun 2020 10:12:50 +0800 Subject: [PATCH 242/343] statistic plg corrected --- imagepy/menus/Analysis/statistic_plg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/menus/Analysis/statistic_plg.py b/imagepy/menus/Analysis/statistic_plg.py index 9c242836..90540e41 100644 --- a/imagepy/menus/Analysis/statistic_plg.py +++ b/imagepy/menus/Analysis/statistic_plg.py @@ -77,7 +77,7 @@ def run(self, ips, imgs, para = None): else: img = ips.lookup() if msk is None else ips.lookup()[msk] hist = np.histogram(img, np.arange(257))[0] - show_hist(IPy.curapp, ips.title+'-Histogram', hist) + show_hist(self.app, ips.title+'-Histogram', hist) class Frequence(Simple): From e7abe91c666ad631e4c150aa86c8d271af4eb8ff Mon Sep 17 00:00:00 2001 From: qixinbo Date: Wed, 17 Jun 2020 10:17:16 +0800 Subject: [PATCH 243/343] use ips channels instead of imgtype --- imagepy/menus/Analysis/statistic_plg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/menus/Analysis/statistic_plg.py b/imagepy/menus/Analysis/statistic_plg.py index 90540e41..3d7d4ea3 100644 --- a/imagepy/menus/Analysis/statistic_plg.py +++ b/imagepy/menus/Analysis/statistic_plg.py @@ -71,7 +71,7 @@ class Histogram(Simple): def run(self, ips, imgs, para = None): msk = ips.mask('in') - if ips.imgtype == 'rgb': + if ips.channels == 3: img = ips.img if msk is None else ips.img[msk] hist = [np.histogram(img.ravel()[i::3], np.arange(257))[0] for i in (0,1,2)] else: From 4b91611f25a2e37358d81e6784a6bcb7e6251fd3 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Wed, 17 Jun 2020 10:19:06 +0800 Subject: [PATCH 244/343] HistCanvas -> HistPanel --- imagepy/menus/Analysis/statistic_plg.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/imagepy/menus/Analysis/statistic_plg.py b/imagepy/menus/Analysis/statistic_plg.py index 3d7d4ea3..c63e8c37 100644 --- a/imagepy/menus/Analysis/statistic_plg.py +++ b/imagepy/menus/Analysis/statistic_plg.py @@ -11,6 +11,7 @@ from skimage.graph import route_through_array from sciapp.object import mark2shp, Circles +from sciwx.widgets.histpanel import HistPanel class HistogramFrame(wx.Frame): def __init__(self, parent, title, hist): @@ -27,7 +28,7 @@ def rgb(self, hist): sizer = wx.BoxSizer( wx.VERTICAL ) rgb = ['Red', 'Green', 'Blue'] for i in (0,1,2): - histc = HistCanvas(panel) + histc = HistPanel(panel) histc.SetValue(hist[i]) txt = wx.StaticText( panel, wx.ID_ANY, 'Channel:'+ rgb[i], wx.DefaultPosition, wx.DefaultSize, 0 ) sizer.Add( txt, 0, wx.LEFT|wx.RIGHT, 8 ) @@ -45,7 +46,7 @@ def gray(self, hist): back = wx.BoxSizer( wx.VERTICAL ) back.Add(panel, 1, wx.EXPAND) sizer = wx.BoxSizer( wx.VERTICAL ) - histc = HistCanvas(panel) + histc = HistPanel(panel) histc.SetValue(hist) txt = wx.StaticText( panel, wx.ID_ANY, 'Channel:'+'Gray', wx.DefaultPosition, wx.DefaultSize, 0 ) sizer.Add( txt, 0, wx.LEFT|wx.RIGHT, 8 ) From 5de6d118f99ab5010943f8d40cd7bb3552dc7714 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Wed, 17 Jun 2020 16:34:04 +0800 Subject: [PATCH 245/343] colormap corrected in plot plg --- imagepy/menus/Table/Chart/plot_plgs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imagepy/menus/Table/Chart/plot_plgs.py b/imagepy/menus/Table/Chart/plot_plgs.py index f2e37928..4eeb4880 100644 --- a/imagepy/menus/Table/Chart/plot_plgs.py +++ b/imagepy/menus/Table/Chart/plot_plgs.py @@ -2,6 +2,7 @@ import matplotlib.pyplot as plt #from imagepy.core.manager import ColorManager from matplotlib import colors +from sciapp import Source class Plot(Table): title = 'Plot Chart' @@ -112,7 +113,7 @@ class Scatter(Table): def run(self, tps, snap, data, para = None): rs = data[para['rs']] * para['s'] if para['rs'] != 'None' else para['s'] cs = data[para['cs']] if para['cs'] != 'None' else '#%.2x%.2x%.2x'%para['c'] - cm = ColorManager.get_lut(para['cm'])/255.0 + cm = Source.manager('colormap').get(para['cm'])/255.0 cm = None if para['cs'] == 'None' else colors.ListedColormap(cm, N=256) plt = self.app.show_plot(para['title']) data.plot.scatter(x=para['x'], y=para['y'], s=rs, c=cs, alpha=para['alpha'], From 78b8801312d174f8d029f897a0042a1e67811e60 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Wed, 17 Jun 2020 16:38:05 +0800 Subject: [PATCH 246/343] colormap corrected in regionprops plg --- imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py index b8993466..7f3a1a1d 100644 --- a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py @@ -9,6 +9,7 @@ from skimage.measure import regionprops from sciapp.object import mark2shp import pandas as pd +from sciapp import Source # center, area, l, extent, cov class RegionCounter(Simple): @@ -194,6 +195,6 @@ def run(self, ips, snap, img, para = None): else: ps = ps / ps.max() idx[1:] = ps * 245 + 10 img[:] = idx[lab] - ips.lut = ColorManager.get_lut(para['cm']) + ips.lut = Source.manager('colormap').get(para['cm']) plgs = [RegionCounter, RegionFilter, PropertyMarker] \ No newline at end of file From 63371bf9eed9b5b14168181c3b2227faf5351cb5 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Wed, 17 Jun 2020 16:40:23 +0800 Subject: [PATCH 247/343] colormap corrected in tablepoints plg --- imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py b/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py index 65556ef8..424b7674 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py +++ b/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py @@ -27,7 +27,7 @@ def load(self, para): def run(self, tps, snap, data, para = None): pts = np.array(data[[para['x'], para['y'], para['z']]]) rs = data[para['rs']]*para['r'] if para['rs'] != 'None' else [para['r']]*len(pts) - cm = ColorManager.get_lut(para['cm'])/255.0 + cm = Source.manager('colormap').get(para['cm'])/255.0 clip = lambda x : (x-x.min())/(x.max()-x.min())*255 if para['cs'] == 'None': cs = [np.array(para['c'])/255.0]*len(pts) else: cs = cm[clip(data[para['cs']]).astype(np.uint8)] From 4f67357e236d506df83b65f8f1d624ff74e04e8e Mon Sep 17 00:00:00 2001 From: qixinbo Date: Thu, 18 Jun 2020 15:13:32 +0800 Subject: [PATCH 248/343] provide the freedom to modify the title of Canvas frame --- sciwx/canvas/widget.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sciwx/canvas/widget.py b/sciwx/canvas/widget.py index e5c80047..3f4346dc 100644 --- a/sciwx/canvas/widget.py +++ b/sciwx/canvas/widget.py @@ -85,10 +85,10 @@ def on_valid(self, event): pass def on_close(self, event): pass class CanvasNoteFrame(wx.Frame, App): - def __init__(self, parent): + def __init__(self, parent, title = 'CanvasNoteFrame'): App.__init__(self) wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, - title = 'CanvasNoteFrame', + title = title, pos = wx.DefaultPosition, size = wx.Size( 800, 600 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) From 169c6a056c93cf00a1d796e8401e4859e9f40df1 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Fri, 19 Jun 2020 09:19:04 +0800 Subject: [PATCH 249/343] menubar needs one parameter --- sciwx/canvas/widget.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sciwx/canvas/widget.py b/sciwx/canvas/widget.py index 3f4346dc..1b295c14 100644 --- a/sciwx/canvas/widget.py +++ b/sciwx/canvas/widget.py @@ -46,7 +46,7 @@ def add_toolbar(self): return toolbar def add_menubar(self): - menubar = MenuBar() + menubar = MenuBar(self) self.SetMenuBar(menubar) return menubar @@ -107,7 +107,7 @@ def add_toolbar(self): return toolbar def add_menubar(self): - menubar = MenuBar() + menubar = MenuBar(self) self.SetMenuBar(menubar) return menubar From bae9606c4784cef416dd5933b334095e60c4ce7d Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 20 Jun 2020 16:33:23 +0800 Subject: [PATCH 250/343] plugin install ok --- imagepy/core/app/imagej.py | 18 +++++++- imagepy/core/app/imagepy.py | 17 +++++++- imagepy/core/app/loader.py | 1 - imagepy/core/app/startup.py | 42 +++++++++++++++---- imagepy/data/config.json | 2 +- .../Region Analysis/statistic_plgs.py | 25 ----------- imagepy/menus/File/Export/sequence_plg.py | 1 - imagepy/menus/File/Import/raw_plg.py | 9 +--- imagepy/menus/File/Import/sequence_plg.py | 8 ++-- .../menus/Plugins/Install/installplg_plgs.py | 13 +++--- imagepy/menus/Plugins/New/demo_tool.py | 3 +- sciwx/__init__.py | 1 + sciwx/canvas/mcanvas.py | 2 +- sciwx/canvas/widget.py | 12 +++++- sciwx/demo/grid_demo.py | 10 ++--- sciwx/demo/mesh2_mesh_demo.py | 27 +++++++----- sciwx/demo/mesh3_geoutil.py | 14 ++++--- sciwx/demo/plt_demo.py | 7 ++-- sciwx/mesh/mcanvas.py | 2 +- sciwx/plt.py | 2 +- sciwx/widgets/choicebook.py | 3 ++ sciwx/widgets/menubar.py | 3 ++ sciwx/widgets/toolbar.py | 7 ++++ 23 files changed, 141 insertions(+), 88 deletions(-) diff --git a/imagepy/core/app/imagej.py b/imagepy/core/app/imagej.py index b92061a9..9418a073 100644 --- a/imagepy/core/app/imagej.py +++ b/imagepy/core/app/imagej.py @@ -13,7 +13,8 @@ from sciapp import App, Source from sciapp.object import Image from imagepy import root_dir -from .source import * +from .startup import load_plugins, load_tools, load_widgets +#from .source import * class ImageJ(wx.Frame, App): def __init__( self, parent ): @@ -33,6 +34,7 @@ def __init__( self, parent ): self.init_widgets() self.init_text() self.init_status() + self._load_all() self.Fit() self.Layout() @@ -69,16 +71,28 @@ def OnDropFiles(self, x, y, path): .PaneBorder( False ).Dock().Resizable().FloatingSize( wx.DefaultSize ).DockFixed( True ) . MinSize(wx.Size(-1, 20)). MaxSize(wx.Size(-1, 20)).Layer( 10 ) ) + def _load_all(self): + self.load_menu(load_plugins()) + dtool = Source.manager('tools').get('default') + self.load_tool(load_tools(), dtool or 'Transform') + self.load_widget(load_widgets()) + + def load_all(self): + wx.CallAfter(self._load_all) + def load_menu(self, data): + self.menubar.clear() self.menubar.load(data) def load_tool(self, data, default=None): + self.toolbar.clear() for i, (name, tols) in enumerate(data[1]): self.toolbar.add_tools(name, tols, i==0) if not default is None: self.toolbar.add_pop(os.path.join(root_dir, 'tools/drop.gif'), default) self.toolbar.Layout() def load_widget(self, data): + self.widgets.clear() self.widgets.load(data) def init_menu(self): @@ -164,7 +178,7 @@ def on_close_mesh(self, event): event.Skip() def set_info(self, value): - self.txt_info.SetLabel(value) + wx.CallAfter(self.txt_info.SetLabel, value) def set_progress(self, value): v = max(min(value, 100), 0) diff --git a/imagepy/core/app/imagepy.py b/imagepy/core/app/imagepy.py index 08e77935..e84ebb09 100644 --- a/imagepy/core/app/imagepy.py +++ b/imagepy/core/app/imagepy.py @@ -12,6 +12,7 @@ from sciapp import App, Source from sciapp.object import Image from imagepy import root_dir +from .startup import load_plugins, load_tools, load_widgets class ImagePy(wx.Frame, App): def __init__( self, parent ): @@ -34,6 +35,7 @@ def __init__( self, parent ): self.init_widgets() self.init_text() self.init_status() + self._load_all() self.Layout() self.auimgr.Update() @@ -69,17 +71,28 @@ def OnDropFiles(self, x, y, path): .PaneBorder( False ).Dock().Resizable().FloatingSize( wx.DefaultSize ).DockFixed( True ) . MinSize(wx.Size(-1, 20)). MaxSize(wx.Size(-1, 20)).Layer( 10 ) ) + def _load_all(self): + self.load_menu(load_plugins()) + dtool = Source.manager('tools').get('default') + self.load_tool(load_tools(), dtool or 'Transform') + self.load_widget(load_widgets()) + + def load_all(self): + wx.CallAfter(self._load_all) + def load_menu(self, data): + self.menubar.clear() self.menubar.load(data) def load_tool(self, data, default=None): + self.toolbar.clear() for i, (name, tols) in enumerate(data[1]): self.toolbar.add_tools(name, tols, i==0) if not default is None: self.toolbar.add_pop(os.path.join(root_dir, 'tools/drop.gif'), default) self.toolbar.Layout() def load_widget(self, data): - print(self.widgets, '============') + self.widgets.clear() self.widgets.load(data) def init_menu(self): @@ -217,7 +230,7 @@ def on_close_mesh(self, event): self.remove_mesh_win(canvas3d) def set_info(self, value): - self.txt_info.SetLabel(value) + wx.CallAfter(self.txt_info.SetLabel, value) def set_progress(self, value): v = max(min(value, 100), 0) diff --git a/imagepy/core/app/loader.py b/imagepy/core/app/loader.py index ee28fda2..c4fe794e 100644 --- a/imagepy/core/app/loader.py +++ b/imagepy/core/app/loader.py @@ -173,7 +173,6 @@ def extend_widgets(path, lst, err): for i in lst: try: rpath = path.replace('/', '.').replace('\\','.') - #rpath = rpath[rpath.index('imagepy.'):] plg = __import__('imagepy.'+rpath+'.'+i,'','',['']) rst.append(plg.Plugin) except Exception as e: diff --git a/imagepy/core/app/startup.py b/imagepy/core/app/startup.py index da3ac84a..372f716b 100644 --- a/imagepy/core/app/startup.py +++ b/imagepy/core/app/startup.py @@ -1,7 +1,7 @@ import wx, sys from .source import * from sciapp import Source -from imagepy.core.app import loader, ImagePy, ImageJ +from imagepy.core.app import loader def extend_plgs(plg): if isinstance(plg, tuple): @@ -24,12 +24,44 @@ def extend_wgts(wgt): elif isinstance(wgt, list): return [extend_wgts(i) for i in wgt] else: return (wgt.title, wgt) +def load_plugins(): + data = loader.build_plugins('menus') + extends = glob('plugins/*/menus') + keydata = {} + for i in data[1]: + if isinstance(i, tuple): keydata[i[0].title] = i[1] + for i in extends: + plgs = loader.build_plugins(i) + for j in plgs[1]: + if not isinstance(j, tuple): continue + name = j[0].title + if name in keydata: keydata[name].extend(j[1]) + else: data[1].append(j) + return extend_plgs(data) + +def load_tools(): + data = loader.build_tools('tools') + extends = glob('plugins/*/tools') + default = 'Transform' + for i in extends: + tols = loader.build_tools(i) + if len(tols)!=0: data[1].extend(tols[1]) + return extend_tols(data) + +def load_widgets(): + data = loader.build_widgets('widgets') + extends = glob('plugins/*/widgets') + for i in extends: + tols = loader.build_widgets(i) + if len(tols)!=0: data[1].extend(tols[1]) + return extend_wgts(data) + def start(): - from skimage.data import camera, astronaut + from imagepy.core.app import ImagePy, ImageJ + #from skimage.data import camera, astronaut import wx.lib.agw.advancedsplash as AS app = wx.App(False) - bitmap = wx.Bitmap('data/logolong.png', wx.BITMAP_TYPE_PNG) shadow = wx.Colour(255,255,255) @@ -42,10 +74,6 @@ def start(): uistyle = Source.manager('config').get('uistyle') or 'imagepy' frame = ImageJ(None) if uistyle == 'imagej' else ImagePy(None) - frame.load_menu(extend_plgs(loader.build_plugins('menus'))) - frame.load_tool(extend_tols(loader.build_tools('tools')), 'Transform') - frame.load_widget(extend_wgts(loader.build_widgets('widgets'))) - frame.Fit() #frame.show_img([camera()], 'camera') #frame.show_img([astronaut()], 'astronaut') frame.Show() diff --git a/imagepy/data/config.json b/imagepy/data/config.json index eb8c58d3..3de9517d 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["recent", ["C:/Users/54631/Desktop/aaa/a.csv", "C:/Users/54631/Desktop/aaa/a.png", "C:\\Users\\54631\\Desktop\\aaa\\test.mc", "C:\\Users\\54631\\Desktop\\aaa\\Contribute Document.md"], null], ["uistyle", "imagepy", null]] \ No newline at end of file +[["recent", ["C:/Users/54631/Documents/projects/imagepy/imagepy/plugins/demoplugin/menus/Demos/Workflow Demo/Workflow Coins Demo.wf", "C:/Users/54631/Documents/projects/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Coins Segment.mc", "C:/Users/54631/Documents/projects/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "C:/Users/54631/Documents/projects/imagepy/imagepy/plugins/demoplugin/menus/Demos/Markdown Demo/MarkDown Demo.md"], null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["uistyle", "imagej", null]] \ No newline at end of file diff --git a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py index 0770401a..e5d83933 100644 --- a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py @@ -5,8 +5,6 @@ """ import numpy as np from scipy import ndimage -import wx - from imagepy.core.engine import Simple, Filter from sciapp.object import mark2shp import pandas as pd @@ -94,29 +92,6 @@ def run(self, ips, imgs, para = None): inten.mark = mark2shp(mark) inten.update() -class RGMark: - def __init__(self, data): - self.xy, self.msk = data - - def draw(self, dc, f, **key): - dc.SetTextForeground((255,255,0)) - font = wx.Font(8, wx.FONTFAMILY_DEFAULT, - wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) - dc.SetFont(font) - - dc.SetPen(wx.Pen((0,255,0), width=1, style=wx.SOLID)) - dc.SetBrush(wx.Brush((0,255,0))) - pos = [f(*(i[1], i[0])) for i in self.xy[self.msk]] - for i in pos:dc.DrawCircle(int(i[0]), int(i[1]), 2) - - - dc.SetPen(wx.Pen((255,0,0), width=1, style=wx.SOLID)) - dc.SetBrush(wx.Brush((255,0,0))) - pos = [f(*(i[1], i[0])) for i in self.xy[~self.msk]] - for i in pos:dc.DrawCircle(int(i[0]), int(i[1]), 2) - - - class IntensityFilter(Filter): title = 'Intensity Filter' note = ['8-bit', '16-bit', 'int', 'auto_msk', 'auto_snap', 'not_slice', 'preview'] diff --git a/imagepy/menus/File/Export/sequence_plg.py b/imagepy/menus/File/Export/sequence_plg.py index 0f852fa9..6bef883e 100644 --- a/imagepy/menus/File/Export/sequence_plg.py +++ b/imagepy/menus/File/Export/sequence_plg.py @@ -4,7 +4,6 @@ @author: yxl """ -import wx from skimage.io import imsave from imagepy.core.engine import Simple from sciapp import Source diff --git a/imagepy/menus/File/Import/raw_plg.py b/imagepy/menus/File/Import/raw_plg.py index 53541350..366e4c86 100644 --- a/imagepy/menus/File/Import/raw_plg.py +++ b/imagepy/menus/File/Import/raw_plg.py @@ -1,4 +1,4 @@ -import wx,os,sys +import os,sys import numpy as np import io# urllib2 urllib.request, urllib.error, urllib.parse from skimage.io import imread @@ -30,9 +30,4 @@ def run(self, para = None): self.app.alert('raw data error!') return img.shape = sp - self.app.show_img([img], fn) - -if __name__ == '__main__': - print(Plugin.title) - app = wx.App(False) - Plugin().run() \ No newline at end of file + self.app.show_img([img], fn) \ No newline at end of file diff --git a/imagepy/menus/File/Import/sequence_plg.py b/imagepy/menus/File/Import/sequence_plg.py index d3532acf..227cf9d0 100644 --- a/imagepy/menus/File/Import/sequence_plg.py +++ b/imagepy/menus/File/Import/sequence_plg.py @@ -9,7 +9,7 @@ from sciapp import Source from imagepy.core.engine import Free from glob import glob -import wx, os +import os.path as osp class Plugin(Free): title = 'Import Sequence' @@ -34,7 +34,7 @@ def show(self): return self.app.show_para('Import sequence', self.view, self.para) def getfiles(self, name): - p,f = os.path.split(name) + p,f = osp.split(name) s = p+'/*.'+name.split('.')[-1] return glob(s) @@ -51,8 +51,8 @@ def readimgs(self, names, read, shape, dtype): #process def run(self, para = None): - fp, fn = os.path.split(para['path']) - fn, fe = os.path.splitext(fn) + fp, fn = osp.split(para['path']) + fn, fe = osp.splitext(fn) read = Source.manager('reader').get(name=fe[1:]) try: img = read(para['path']) except: return self.app.alert('unknown img format!') diff --git a/imagepy/menus/Plugins/Install/installplg_plgs.py b/imagepy/menus/Plugins/Install/installplg_plgs.py index 43c72534..f6bf2a0c 100644 --- a/imagepy/menus/Plugins/Install/installplg_plgs.py +++ b/imagepy/menus/Plugins/Install/installplg_plgs.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from imagepy import root_dir from imagepy.core.engine import Free import os, subprocess, zipfile, shutil @@ -7,19 +8,19 @@ from urllib.request import urlretrieve from io import BytesIO as StringIO -''' + path_plgs = os.path.join(root_dir, 'plugins') path_cache = os.path.join(path_plgs, 'cache') if not os.path.exists(path_plgs): os.mkdir(path_plgs) if not os.path.exists(path_cache): os.mkdir(path_cache) -''' + def Schedule(a,b,c, plg): per = 100.0 * a * b / c if per > 100 : per = 100 - print('%-3d%%'%per) plg.progress(int(per), 100) + if c==-1: plg.prgs = None class Install(Free): title = 'Install Plugins' @@ -39,7 +40,7 @@ def run(self, para=None): domain, name = (url[:-4].replace('.','-')).split('/')[-2:] domain, name = domain.replace('_', '-'), name.replace('_', '-') - IPy.set_info('downloading plugin from %s'%para['repo']) + self.app.set_info('downloading plugin from %s'%para['repo']) urlretrieve(url, os.path.join(path_cache, domain+'_'+name+'.zip'), lambda a,b,c, p=self: Schedule(a,b,c,p)) zipf = zipfile.ZipFile(os.path.join(path_cache, domain+'_'+name+'.zip')) @@ -49,11 +50,11 @@ def run(self, para=None): if os.path.exists(destpath): shutil.rmtree(destpath) os.rename(os.path.join(path_cache, folder), destpath) zipf.close() - IPy.set_info('installing requirement liberies') + self.app.set_info('installing requirement liberies') self.prgs = None cmds = [sys.executable, '-m', 'pip', 'install', '-r', '%s/requirements.txt'%destpath] subprocess.call(cmds) - IPy.reload_plgs(True, True, True, True) + self.app.load_all() class List(Free): title = 'List Plugins' diff --git a/imagepy/menus/Plugins/New/demo_tool.py b/imagepy/menus/Plugins/New/demo_tool.py index 5b9e7d41..a8e224fd 100644 --- a/imagepy/menus/Plugins/New/demo_tool.py +++ b/imagepy/menus/Plugins/New/demo_tool.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- from imagepy.core.draw import paint from imagepy.core.engine import Tool -import wx # this is a simple tool implements a pencial class Plugin(Tool): @@ -16,7 +15,7 @@ def __init__(self): self.sta = 0 self.paint = paint.Paint() self.paint.color = 255 - self.cursor = wx.CURSOR_CROSS + self.cursor = 'cross' # do it when mouse_down def mouse_down(self, ips, x, y, btn, **key): diff --git a/sciwx/__init__.py b/sciwx/__init__.py index 3322da8c..dff7b6fe 100644 --- a/sciwx/__init__.py +++ b/sciwx/__init__.py @@ -8,6 +8,7 @@ vs = np.linspace(0, cm.N, 256, endpoint=False) lut = cm(vs.astype(np.int), bytes=True)[:,:3] Source.manager('colormap').add(i, lut) +del plt graylut = Source.manager('colormap').get('gray') Source.manager('colormap').add('Grays', graylut) Source.manager('colormap').remove('gray') \ No newline at end of file diff --git a/sciwx/canvas/mcanvas.py b/sciwx/canvas/mcanvas.py index 9caa39b1..464b1f6f 100644 --- a/sciwx/canvas/mcanvas.py +++ b/sciwx/canvas/mcanvas.py @@ -9,7 +9,7 @@ class ICanvas(Canvas): def __init__(self, parent, autofit=False): Canvas.__init__(self, parent, autofit) self.images.append(Image()) - self.images[0].back = None + self.images[0].back = Image() self.Bind(wx.EVT_IDLE, self.on_idle) def get_obj_tol(self): diff --git a/sciwx/canvas/widget.py b/sciwx/canvas/widget.py index e5c80047..a5a8252d 100644 --- a/sciwx/canvas/widget.py +++ b/sciwx/canvas/widget.py @@ -1,6 +1,6 @@ import wx, wx.lib.agw.aui as aui from .mcanvas import MCanvas -from ..widgets import ToolBar, MenuBar +from ..widgets import ToolBar, MenuBar, ParaDialog from sciapp import App class CanvasFrame(wx.Frame, App): @@ -46,10 +46,18 @@ def add_toolbar(self): return toolbar def add_menubar(self): - menubar = MenuBar() + menubar = MenuBar(self) self.SetMenuBar(menubar) return menubar + def show_para(self, title, view, para, on_handle=None, on_ok=None, on_cancel=None, preview=False, modal=True): + dialog = ParaDialog(self, title) + dialog.init_view(view, para, preview, modal=modal, app=self) + dialog.Bind('cancel', on_cancel) + dialog.Bind('parameter', on_handle) + dialog.Bind('commit', on_ok) + return dialog.show() + class CanvasNoteBook(wx.lib.agw.aui.AuiNotebook): def __init__(self, parent): wx.lib.agw.aui.AuiNotebook.__init__( self, parent, wx.ID_ANY, diff --git a/sciwx/demo/grid_demo.py b/sciwx/demo/grid_demo.py index 0605cc63..4f13c6bc 100644 --- a/sciwx/demo/grid_demo.py +++ b/sciwx/demo/grid_demo.py @@ -62,9 +62,9 @@ def table_obj_test(): if __name__=='__main__': app = wx.App() grid_test() - #grid_style_test() - #grid_frame_test() - #grid_note_book() - #grid_note_frame() - #table_obj_test() + grid_style_test() + grid_frame_test() + grid_note_book() + grid_note_frame() + table_obj_test() app.MainLoop() diff --git a/sciwx/demo/mesh2_mesh_demo.py b/sciwx/demo/mesh2_mesh_demo.py index 564f2e7c..076d254d 100644 --- a/sciwx/demo/mesh2_mesh_demo.py +++ b/sciwx/demo/mesh2_mesh_demo.py @@ -1,27 +1,32 @@ import sys, wx sys.path.append('../../') -from sciwx.mesh import Canvas3D, MCanvas3D, MeshSet, geoutil +from sciwx.mesh import Canvas3D, MCanvas3D, MeshSet +from sciapp.util import surfutil +from sciapp.object import Surface from sciwx.mesh import Canvas3DFrame, Canvas3DNoteBook, Canvas3DNoteFrame -vts, fs, ns, cs = geoutil.build_ball((100,100,100),50, (1,0,0)) +vts, fs, ns, cs = surfutil.build_ball((100,100,100),50, (1,0,0)) def add_with_para(): cnf = Canvas3DFrame(None) - cnf.add_surf('gridball', vts, fs, ns, cs, mode='grid') + surf = Surface(vts, fs, ns, cs, mode='grid') + cnf.add_surf('gridball', surf) cnf.Show() def mesh_obj_test(): cnf = Canvas3DFrame(None) meshes = MeshSet() - vts, fs, ns, cs = geoutil.build_ball((100,100,100),50, (1,0,0)) - redball = meshes.add_surf('redball', vts, fs, ns, cs) - vts, fs, ns, cs = geoutil.build_ball((300,100,100),50, (1,1,0)) - yellowball = meshes.add_surf('yellowball', vts, fs, ns, cs) - yellowball.mode = 'grid' - vts, fs, ns, cs = geoutil.build_ball((300,-300,100),50, (0,1,0)) - hideball = meshes.add_surf('hideball', vts, fs, ns, cs) - hideball.visible = False + vts, fs, ns, cs = surfutil.build_ball((100,100,100),50, (1,0,0)) + redball = Surface(vts, fs, ns, cs) + meshes.add_surf('redball', redball) + vts, fs, ns, cs = surfutil.build_ball((300,100,100),50, (1,1,0)) + yellowball = Surface(vts, fs, ns, cs, mode='grid') + meshes.add_surf('yellowball', yellowball) + hideball = Surface(vts, fs, ns, cs) + vts, fs, ns, cs = surfutil.build_ball((300,-300,100),50, (0,1,0)) + hideball = Surface(vts, fs, ns, cs, visible=False) + hideball = meshes.add_surf('hideball', hideball) meshes.background = (0, 0, 0.3) cnf.set_mesh(meshes) cnf.Show() diff --git a/sciwx/demo/mesh3_geoutil.py b/sciwx/demo/mesh3_geoutil.py index 16d3163a..ff939994 100644 --- a/sciwx/demo/mesh3_geoutil.py +++ b/sciwx/demo/mesh3_geoutil.py @@ -13,8 +13,8 @@ def dem_test(): cnf = Canvas3DFrame(None) - vts, fs, ns, cs = geoutil.build_surf2d(moon(), ds=1, k=0.3, sigma=2) - cnf.add_surf('dem', vts, fs, ns, cs) + vts, fs, ns, cs = surfutil.build_surf2d(moon(), ds=1, k=0.3, sigma=2) + cnf.add_surf('dem', Surface(vts, fs, ns, cs)) cnf.Show() def ball_test(): @@ -101,9 +101,10 @@ def surface2d_test(): cnf = Canvas3DFrame(None) x, y = np.ogrid[-2:2:20j, -2:2:20j] z = x * np.exp( - x**2 - y**2) - vts, fs, ns, cs = geoutil.build_surf2d(z, ds=1, k=20, sigma=2) - cs[:] = geoutil.auto_lookup(vts[:,2], geoutil.linear_color('jet'))/255 - cnf.add_surf('dem', vts, fs, ns, cs) + vts, fs, ns, cs = surfutil.build_surf2d(z, ds=1, k=20, sigma=2) + cs[:] = surfutil.auto_lookup(vts[:,2], surfutil.linear_color('jet'))/255 + dem = Surface(vts, fs, ns, cs) + cnf.add_surf('dem', dem) cnf.Show() def arrow_test(): @@ -142,8 +143,8 @@ def volume_test(): if __name__ == '__main__': app = wx.App() - balls_mark_rest() ''' + balls_mark_rest() dem_test() ball_test() random_ball_test() @@ -157,4 +158,5 @@ def volume_test(): cube_surf_test() volume_test() ''' + dem_test() app.MainLoop() diff --git a/sciwx/demo/plt_demo.py b/sciwx/demo/plt_demo.py index 7c6adbe4..ebbb5039 100644 --- a/sciwx/demo/plt_demo.py +++ b/sciwx/demo/plt_demo.py @@ -3,6 +3,7 @@ from skimage.data import astronaut, camera from sciwx import plt +print(plt, '=========') import numpy as np import pandas as pd @@ -22,9 +23,9 @@ ax = fg.add_subplot() ax.plot(np.random.rand(100)) - mesh = plt.meshshow() - vts, fs, ns, cs = plt.build_ball((100,100,100),50, (1,0,0)) - mesh.add_surf('ball',vts, fs, ns, cs) + #mesh = plt.meshshow() + #vts, fs, ns, cs = plt.build_ball((100,100,100),50, (1,0,0)) + #mesh.add_surf('ball',vts, fs, ns, cs) plt.parashow({'c':(255,0,0)}, [('color', 'c', 'select', 'color')], False) plt.show() diff --git a/sciwx/mesh/mcanvas.py b/sciwx/mesh/mcanvas.py index 802fd783..fc790d89 100644 --- a/sciwx/mesh/mcanvas.py +++ b/sciwx/mesh/mcanvas.py @@ -43,7 +43,7 @@ def __init__( self, parent, scene=None): tsizer.Add( self.btn_stl, 0, wx.ALIGN_CENTER|wx.ALL, 0 ) #pan = wx.Panel(self.toolbar, size=(50, 50)) self.btn_color = wx.ColourPickerCtrl( self.toolbar, wx.ID_ANY, wx.Colour( 128, 128, 128 ), wx.DefaultPosition, [(33, 38), (-1, -1)][platform.system() in ['Windows', 'Linux']], wx.CLRP_DEFAULT_STYLE ) - tsizer.Add( self.btn_color, 0, wx.ALIGN_CENTER|wx.ALL|(0, wx.EXPAND)[platform.system() in ['Windows', 'Linux']], 0 ) + tsizer.Add( self.btn_color, 0, wx.ALL|(0, wx.EXPAND)[platform.system() in ['Windows', 'Linux']], 0 ) tsizer.Add(wx.StaticLine( self.toolbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL), 0, wx.ALL|wx.EXPAND, 2 ) self.cho_light = wx.Choice( self.toolbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, ['force light', 'normal light', 'weak light', 'off light'], 0 ) self.cho_light.SetSelection( 1 ) diff --git a/sciwx/plt.py b/sciwx/plt.py index dc8f8266..627ec0d2 100644 --- a/sciwx/plt.py +++ b/sciwx/plt.py @@ -5,7 +5,7 @@ from .plot import PlotFrame from .mesh import Canvas3DFrame from .widgets import ParaDialog -from .mesh.geoutil import * +from sciapp.util.surfutil import * app = None diff --git a/sciwx/widgets/choicebook.py b/sciwx/widgets/choicebook.py index f51cfc1c..85db3d0e 100644 --- a/sciwx/widgets/choicebook.py +++ b/sciwx/widgets/choicebook.py @@ -18,6 +18,9 @@ def load(self, data): for name, wgts in data[1]: self.add_wgts(name, wgts) + def clear(self): + self.DestroyChildren() + if __name__ == '__main__': app = wx.App() frame = wx.Frame(None) diff --git a/sciwx/widgets/menubar.py b/sciwx/widgets/menubar.py index 04f1c32f..037a234b 100644 --- a/sciwx/widgets/menubar.py +++ b/sciwx/widgets/menubar.py @@ -27,6 +27,9 @@ def load(self, data): def on_menu(self, event): print('here') + + def clear(self): + while self.GetMenuCount()>0: self.Remove(0) if __name__ == '__main__': diff --git a/sciwx/widgets/toolbar.py b/sciwx/widgets/toolbar.py index 19689bb2..7c0444d8 100644 --- a/sciwx/widgets/toolbar.py +++ b/sciwx/widgets/toolbar.py @@ -2,6 +2,7 @@ from .paradialog import ParaDialog def make_logo(obj): + bmp = None if isinstance(obj, str) and len(obj)>1: bmp = wx.Bitmap(obj) if isinstance(obj, str) and len(obj)==1: @@ -17,6 +18,7 @@ def make_logo(obj): w, h = dc.GetTextExtent(obj) dc.DrawText(obj, 8-w//2, 8-h//2) rgb = bytes(768) + dc.SelectObject(wx.NullBitmap) bmp.CopyToBuffer(rgb) a = memoryview(rgb[::3]).tolist() a = bytes([255-i for i in a]) @@ -65,6 +67,11 @@ def bind(self, btn, tol): #if not isinstance(data[0], Macros) and issubclass(data[0], Tool): btn.Bind(wx.EVT_LEFT_DCLICK, lambda e, obj=obj: self.on_config(e, obj)) + def clear(self): + del self.toolset[:] + self.GetSizer().Clear() + self.DestroyChildren() + def add_tool(self, logo, tool): btn = wx.BitmapButton(self, wx.ID_ANY, make_logo(logo), wx.DefaultPosition, (32,32), wx.BU_AUTODRAW|wx.RAISED_BORDER ) From 560f6e5f8e012ff9d1dc6a8fdd264fb764190e68 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 20 Jun 2020 21:06:44 +0800 Subject: [PATCH 251/343] try when loading --- imagepy/core/app/imagej.py | 20 ++- imagepy/core/app/imagepy.py | 36 +++-- imagepy/core/app/loader.py | 134 ++++++++---------- imagepy/core/app/startup.py | 20 +-- imagepy/data/config.json | 2 +- .../menus/Plugins/Contribute/pmanager_wgt.py | 9 +- imagepy/menus/Plugins/Macros/recorder_wgt.py | 2 +- sciwx/text/textpad.py | 5 +- 8 files changed, 119 insertions(+), 109 deletions(-) diff --git a/imagepy/core/app/imagej.py b/imagepy/core/app/imagej.py index 9418a073..9b573100 100644 --- a/imagepy/core/app/imagej.py +++ b/imagepy/core/app/imagej.py @@ -7,7 +7,7 @@ from sciwx.widgets import ProgressBar from sciwx.grid import GridFrame from sciwx.mesh import Canvas3DFrame -from sciwx.text import MDFrame, TextNoteFrame +from sciwx.text import MDFrame, TextFrame from sciwx.plot import PlotFrame from skimage.data import camera from sciapp import App, Source @@ -72,10 +72,18 @@ def OnDropFiles(self, x, y, path): . MinSize(wx.Size(-1, 20)). MaxSize(wx.Size(-1, 20)).Layer( 10 ) ) def _load_all(self): - self.load_menu(load_plugins()) + plgs, errplg = load_plugins() + self.load_menu(plgs) dtool = Source.manager('tools').get('default') - self.load_tool(load_tools(), dtool or 'Transform') - self.load_widget(load_widgets()) + tols, errtol = load_tools() + self.load_tool(tols, dtool or 'Transform') + wgts, errwgt = load_widgets() + self.load_widget(wgts) + err = errplg + errtol + errwgt + if len(err)>0: + err = [('File', 'Name', 'Error')] + err + cont = '\n'.join(['%-30s\t%-20s\t%s'%i for i in err]) + self.show_txt(cont, 'loading error log') def load_all(self): wx.CallAfter(self._load_all) @@ -253,9 +261,7 @@ def show_workflow(self, cont, title='ImagePy'): wx.CallAfter(self._show_workflow, cont, title) def _show_txt(self, cont, title='ImagePy'): - page = self.txtframe.add_notepad() - page.append(cont) - self.txtframe.Show() + TextFrame(self, title, cont).Show() def show_txt(self, cont, title='ImagePy'): wx.CallAfter(self._show_txt, cont, title) diff --git a/imagepy/core/app/imagepy.py b/imagepy/core/app/imagepy.py index e84ebb09..b90c5188 100644 --- a/imagepy/core/app/imagepy.py +++ b/imagepy/core/app/imagepy.py @@ -6,7 +6,7 @@ from sciwx.canvas import CanvasNoteBook from sciwx.grid import GridNoteBook from sciwx.mesh import Canvas3DNoteBook -from sciwx.text import MDNoteBook, TextNoteFrame +from sciwx.text import MDNoteBook, TextNoteBook from sciwx.plot import PlotFrame from skimage.data import camera from sciapp import App, Source @@ -72,10 +72,18 @@ def OnDropFiles(self, x, y, path): . MinSize(wx.Size(-1, 20)). MaxSize(wx.Size(-1, 20)).Layer( 10 ) ) def _load_all(self): - self.load_menu(load_plugins()) + plgs, errplg = load_plugins() + self.load_menu(plgs) dtool = Source.manager('tools').get('default') - self.load_tool(load_tools(), dtool or 'Transform') - self.load_widget(load_widgets()) + tols, errtol = load_tools() + self.load_tool(tols, dtool or 'Transform') + wgts, errwgt = load_widgets() + self.load_widget(wgts) + err = errplg + errtol + errwgt + if len(err)>0: + err = [('File', 'Name', 'Error')] + err + cont = '\n'.join(['%-30s\t%-20s\t%s'%i for i in err]) + self.show_txt(cont, 'loading error log') def load_all(self): wx.CallAfter(self._load_all) @@ -194,7 +202,16 @@ def init_text(self): .MaximizeButton( True ).Resizable().FloatingSize((400, 400)).BestSize(( 120,120 )). Caption('MarkDown') . BottomDockable( True ).TopDockable( False ).LeftDockable( True ).RightDockable( True ) ) - self.txtframe = TextNoteFrame(self, 'Sci Text') + self.txtwarp = wx.Panel(self) + sizer = wx.BoxSizer( wx.VERTICAL ) + self.txtnb = TextNoteBook( self.txtwarp) + sizer.Add( self.txtnb, 1, wx.EXPAND |wx.ALL, 0 ) + self.txtwarp.SetSizer( sizer ) + self.txtwarp.Layout() + + self.auimgr.AddPane( self.txtwarp, aui.AuiPaneInfo() .Bottom() .CaptionVisible( True ).PinButton( True ).Float().Hide() + .MaximizeButton( True ).Resizable().FloatingSize((400, 400)).BestSize(( 120,120 )). Caption('TextPanel') . + BottomDockable( True ).TopDockable( False ).LeftDockable( True ).RightDockable( True ) ) def on_pan_close(self, event): if event.GetPane().window in [self.toolbar, self.widgets]: @@ -309,9 +326,12 @@ def show_workflow(self, cont, title='ImagePy'): wx.CallAfter(self._show_workflow, cont, title) def _show_txt(self, cont, title='ImagePy'): - page = self.txtframe.add_notepad() - page.append(cont) - self.txtframe.Show() + page = self.txtnb.add_page() + page.set_cont(cont) + page.title = title + info = self.auimgr.GetPane(self.txtwarp) + info.Show(True) + self.auimgr.Update() def show_txt(self, cont, title='ImagePy'): wx.CallAfter(self._show_txt, cont, title) diff --git a/imagepy/core/app/loader.py b/imagepy/core/app/loader.py index c4fe794e..c9a9d415 100644 --- a/imagepy/core/app/loader.py +++ b/imagepy/core/app/loader.py @@ -21,45 +21,41 @@ def getpath(root, path): def extend_plugins(path, lst, err): rst = [] for i in lst: - if isinstance(i, tuple) or i=='-': - rst.append(i) + if isinstance(i, tuple) or i=='-': rst.append(i) elif i[-3:] == 'rpt': pt = os.path.join(root_dir,path) rst.append(Report(i[:-4], pt+'/'+i)) Source.manager('plugin').add(obj=rst[-1], name=rst[-1].title) elif i[-3:] in {'.md', '.mc', '.wf'}: - p = os.path.join(os.path.join(root_dir,path), i).replace('\\','/') - opener = Macros(i[:-3], ['Open>{"path":"%s"}'%p]) - rst.append(opener) + p = os.path.join(os.path.join(root_dir, path), i).replace('\\','/') + rst.append(Macros(i[:-3], ['Open>{"path":"%s"}'%p])) Source.manager('plugin').add(obj=rst[-1], name=rst[-1].title) elif i[-6:] in ['wgt.py', 'gts.py']: - #try: - rpath = path.replace('/', '.').replace('\\','.') - #rpath = rpath[rpath.index('imagepy.'):] - plg = __import__('imagepy.'+ rpath+'.'+i[:-3],'','',['']) - if hasattr(plg, 'wgts'): - rst.extend([j if j=='-' else Widget(j) for j in plg.wgts]) - for p in plg.wgts: - if not isinstance(p, str):Source.manager('widget').add(obj=p, name=p.title) - else: - rst.append(Widget(plg.Plugin)) - Source.manager('widget').add(obj=plg.Plugin, name=plg.Plugin.title) - #except Exception as e: - # err.append((path, i, sys.exc_info()[1])) + try: + rpath = path.replace('/', '.').replace('\\','.') + plg = __import__('imagepy.'+ rpath+'.'+i[:-3],'','',['']) + if hasattr(plg, 'wgts'): + rst.extend([j if j=='-' else Widget(j) for j in plg.wgts]) + for p in plg.wgts: + if not isinstance(p, str):Source.manager('widget').add(obj=p, name=p.title) + else: + rst.append(Widget(plg.Plugin)) + Source.manager('widget').add(obj=plg.Plugin, name=plg.Plugin.title) + except Exception as e: + err.append((path, i, sys.exc_info()[1])) else: - #try: - rpath = path.replace('/', '.').replace('\\','.') - #rpath = rpath[rpath.index('imagepy.'):] - plg = __import__('imagepy.'+ rpath+'.'+i[:-3],'','',['']) - if hasattr(plg, 'plgs'): - rst.extend([j for j in plg.plgs]) - for p in plg.plgs: - if not isinstance(p, str): Source.manager('plugin').add(obj=p, name=p.title) - else: - rst.append(plg.Plugin) - Source.manager('plugin').add(obj=plg.Plugin, name=plg.Plugin.title) - #except Exception as e: - # err.append((path, i, sys.exc_info()[1])) + try: + rpath = path.replace('/', '.').replace('\\','.') + plg = __import__('imagepy.'+ rpath+'.'+i[:-3],'','',['']) + if hasattr(plg, 'plgs'): + rst.extend([j for j in plg.plgs]) + for p in plg.plgs: + if not isinstance(p, str): Source.manager('plugin').add(obj=p, name=p.title) + else: + rst.append(plg.Plugin) + Source.manager('plugin').add(obj=plg.Plugin, name=plg.Plugin.title) + except Exception as e: + err.append((path, i, sys.exc_info()[1])) return rst def sort_plugins(catlog, lst): @@ -73,16 +69,16 @@ def sort_plugins(catlog, lst): rst.extend(lst) return rst -def build_plugins(path, err=False): - root = err in (True, False) - if root: sta, err = err, [] +def build_plugins(path, err='root'): + root = err=='root' + if root: err=[] subtree = [] cont = os.listdir(os.path.join(root_dir, path)) for i in cont: subp = os.path.join(path,i) if os.path.isdir(os.path.join(root_dir, subp)): sub = build_plugins(subp, err) - if len(sub)!=0:subtree.append(sub) + if len(sub)!=0:subtree.append(sub[:2]) elif i[-6:] in ('plg.py', 'lgs.py', 'wgt.py', 'gts.py'): subtree.append(i) elif i[-3:] in ('.mc', '.md', '.wf', 'rpt'): @@ -96,35 +92,28 @@ def build_plugins(path, err=False): if hasattr(pg, 'catlog'): subtree = sort_plugins(pg.catlog, subtree) subtree = extend_plugins(path, subtree, err) - - if root and sta and len(err)>0: - IPy.write('Some plugin may be not loaded, but not affect others!') - for i in err: IPy.write('>>> %-50s%-20s%s'%i) - return (pg, subtree) + return pg, subtree, err def extend_tools(path, lst, err): rst = [] for i in lst: if i[-3:] in ('.mc', '.md', '.wf', 'rpt'): - pt = os.path.join(root_dir, path) - # if i[-3:] == '.md':print(pt) - f = open(pt+'/'+i) - cmds = f.readlines() - f.close() - rst.append((Macros(i[:-3], [getpath(pt, i) for i in cmds]), + p = os.path.join(os.path.join(root_dir,path), i).replace('\\','/') + rst.append((Macros(i[:-3], ['Open>{"path":"%s"}'%p]), os.path.join(root_dir, path)+'/'+i[:-3]+'.gif')) + + #rst.append((Macros(i[:-3], [getpath(pt, i) for i in cmds]), + # os.path.join(root_dir, path)+'/'+i[:-3]+'.gif')) else: - #try: - rpath = path.replace('/', '.').replace('\\','.') - #rpath = rpath[rpath.index('imagepy.'):] - - plg = __import__('imagepy.'+rpath+'.'+i,'','',['']) - if hasattr(plg, 'plgs'): - for i,j in plg.plgs: rst.append((i, path+'/'+j)) - else: rst.append((plg.Plugin, - os.path.join(root_dir, path)+'/'+i.split('_')[0]+'.gif')) - #except Exception as e: - # err.append((path, i, sys.exc_info()[1])) + try: + rpath = path.replace('/', '.').replace('\\','.') + plg = __import__('imagepy.'+rpath+'.'+i,'','',['']) + if hasattr(plg, 'plgs'): + for i,j in plg.plgs: rst.append((i, path+'/'+j)) + else: rst.append((plg.Plugin, + os.path.join(root_dir, path)+'/'+i.split('_')[0]+'.gif')) + except Exception as e: + err.append((path, i, sys.exc_info()[1])) for i in rst:Source.manager('tool').add(obj=i[0], name=i[0].title) return rst @@ -138,10 +127,10 @@ def sort_tools(catlog, lst): rst.append(j) rst.extend(lst) return rst - -def build_tools(path, err=False): - root = err in (True, False) - if root: sta, err = err, [] + +def build_tools(path, err='root'): + root = err=='root' + if root: err=[] subtree = [] cont = os.listdir(os.path.join(root_dir, path)) @@ -149,7 +138,7 @@ def build_tools(path, err=False): subp = os.path.join(path,i) if root and os.path.isdir(os.path.join(root_dir, subp)): sub = build_tools(subp, err) - if len(sub)!=0:subtree.append(sub) + if len(sub)!=0:subtree.append(sub[:2]) elif not root: if i[len(i)-7:] in ('_tol.py', 'tols.py'): subtree.append(i[:-3]) @@ -163,10 +152,7 @@ def build_tools(path, err=False): if hasattr(pg, 'catlog'): subtree = sort_tools(pg.catlog, subtree) if not root:subtree = extend_tools(path, subtree, err) - elif sta and len(err)>0: - IPy.write('tools not loaded:') - for i in err: IPy.write('>>> %-50s%-20s%s'%i) - return (pg, subtree) + return pg, subtree, err def extend_widgets(path, lst, err): rst = [] @@ -191,16 +177,16 @@ def sort_widgets(catlog, lst): rst.extend(lst) return rst -def build_widgets(path, err=False): - root = err in (True, False) - if root: sta, err = err, [] +def build_widgets(path, err='root'): + root = err=='root' + if root: err=[] subtree = [] cont = os.listdir(os.path.join(root_dir, path)) for i in cont: subp = os.path.join(path,i) if root and os.path.isdir(os.path.join(root_dir, subp)): sub = build_widgets(subp, err) - if len(sub)!=0:subtree.append(sub) + if len(sub)!=0:subtree.append(sub[:2]) elif not root: if i[len(i)-7:] in ('_wgt.py', 'wgts.py'): subtree.append(i[:-3]) @@ -212,12 +198,8 @@ def build_widgets(path, err=False): pg.title = os.path.basename(path) if hasattr(pg, 'catlog'): subtree = sort_widgets(pg.catlog, subtree) - if not root: - subtree = extend_widgets(path, subtree, err) - elif sta and len(err)>0: - IPy.write('widgets not loaded:') - for i in err: IPy.write('>>> %-50s%-20s%s'%i) - return (pg, subtree) + if not root: subtree = extend_widgets(path, subtree, err) + return pg, subtree, err def build_document(path): docs = [] diff --git a/imagepy/core/app/startup.py b/imagepy/core/app/startup.py index 372f716b..2b5b0626 100644 --- a/imagepy/core/app/startup.py +++ b/imagepy/core/app/startup.py @@ -32,12 +32,13 @@ def load_plugins(): if isinstance(i, tuple): keydata[i[0].title] = i[1] for i in extends: plgs = loader.build_plugins(i) + data[2].extend(plgs[2]) for j in plgs[1]: if not isinstance(j, tuple): continue name = j[0].title if name in keydata: keydata[name].extend(j[1]) else: data[1].append(j) - return extend_plgs(data) + return extend_plgs(data[:2]), data[2] def load_tools(): data = loader.build_tools('tools') @@ -45,20 +46,23 @@ def load_tools(): default = 'Transform' for i in extends: tols = loader.build_tools(i) - if len(tols)!=0: data[1].extend(tols[1]) - return extend_tols(data) + #if len(tols)!=0: + data[1].extend(tols[1]) + data[2].extend(tols[2]) + return extend_tols(data[:2]), data[2] def load_widgets(): data = loader.build_widgets('widgets') extends = glob('plugins/*/widgets') for i in extends: - tols = loader.build_widgets(i) - if len(tols)!=0: data[1].extend(tols[1]) - return extend_wgts(data) + wgts = loader.build_widgets(i) + #if len(wgts)!=0: + data[1].extend(wgts[1]) + data[2].extend(wgts[2]) + return extend_wgts(data[:2]), data[2] def start(): from imagepy.core.app import ImagePy, ImageJ - #from skimage.data import camera, astronaut import wx.lib.agw.advancedsplash as AS app = wx.App(False) @@ -74,7 +78,5 @@ def start(): uistyle = Source.manager('config').get('uistyle') or 'imagepy' frame = ImageJ(None) if uistyle == 'imagej' else ImagePy(None) - #frame.show_img([camera()], 'camera') - #frame.show_img([astronaut()], 'astronaut') frame.Show() app.MainLoop() \ No newline at end of file diff --git a/imagepy/data/config.json b/imagepy/data/config.json index 3de9517d..2c4dc319 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["recent", ["C:/Users/54631/Documents/projects/imagepy/imagepy/plugins/demoplugin/menus/Demos/Workflow Demo/Workflow Coins Demo.wf", "C:/Users/54631/Documents/projects/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Coins Segment.mc", "C:/Users/54631/Documents/projects/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "C:/Users/54631/Documents/projects/imagepy/imagepy/plugins/demoplugin/menus/Demos/Markdown Demo/MarkDown Demo.md"], null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["uistyle", "imagej", null]] \ No newline at end of file +[["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["recent", ["C:/Users/54631/Documents/projects/imagepy/imagepy/tools/Stack/Add Slice.mc", "C:/Users/54631/Documents/projects/imagepy/imagepy/menus/Plugins/Contribute/Contributions/CellPose.md", "C:/Users/54631/Documents/projects/imagepy/imagepy/plugins/demoplugin/menus/Demos/Workflow Demo/Workflow Coins Demo.wf", "C:/Users/54631/Documents/projects/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Coins Segment.mc"], null], ["uistyle", "imagepy", null]] \ No newline at end of file diff --git a/imagepy/menus/Plugins/Contribute/pmanager_wgt.py b/imagepy/menus/Plugins/Contribute/pmanager_wgt.py index c0a2ee6b..92f00423 100644 --- a/imagepy/menus/Plugins/Contribute/pmanager_wgt.py +++ b/imagepy/menus/Plugins/Contribute/pmanager_wgt.py @@ -54,7 +54,7 @@ def parse(path): class Plugin( wx.Panel ): title = 'Plugins Manager' single = None - def __init__( self, parent,): + def __init__( self, parent, app=None): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 600,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) @@ -64,7 +64,7 @@ def __init__( self, parent,): self.m_staticText1 = wx.StaticText( self, wx.ID_ANY, "Search:", wx.DefaultPosition, wx.DefaultSize, 0 ) self.m_staticText1.Wrap( -1 ) - bSizer2.Add( self.m_staticText1, 0, wx.ALIGN_CENTER|wx.ALL|wx.EXPAND, 5 ) + bSizer2.Add( self.m_staticText1, 0, wx.ALL|wx.EXPAND, 5 ) self.txt_search = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) bSizer2.Add( self.txt_search, 1, wx.ALL, 5 ) @@ -99,6 +99,7 @@ def __init__( self, parent,): self.btn_install.Bind(wx.EVT_BUTTON, self.on_install) self.btn_uninstall.Bind(wx.EVT_BUTTON, self.on_remove) self.chk_has.Bind( wx.EVT_CHECKBOX, self.on_check) + self.app = app self.load() #def list_plg(self, lst, items @@ -143,13 +144,13 @@ def on_install(self, event): if i==-1: return path = self.buf[i][-1]['path'] Source.manager('plugin').get('Install Plugins')().start( - {'repo':self.buf[i][-1]['path']}, self.load) + self.app, {'repo':self.buf[i][-1]['path']}, self.load) def on_remove(self, event): i = self.lst_plgs.GetFirstSelected() if i==-1: return shutil.rmtree(self.buf[i][-1]['folder']) - IPy.reload_plgs(True, True, True, True) + self.app.load_all() self.load() def on_check(self, event): self.load() diff --git a/imagepy/menus/Plugins/Macros/recorder_wgt.py b/imagepy/menus/Plugins/Macros/recorder_wgt.py index 49a90460..ec00a701 100644 --- a/imagepy/menus/Plugins/Macros/recorder_wgt.py +++ b/imagepy/menus/Plugins/Macros/recorder_wgt.py @@ -5,7 +5,7 @@ class Plugin ( wx.Panel ): title = 'Macros Recorder' - def __init__( self, parent ): + def __init__( self, parent, app=None): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size(300, 200), style = wx.TAB_TRAVERSAL ) bSizer1 = wx.BoxSizer( wx.VERTICAL ) diff --git a/sciwx/text/textpad.py b/sciwx/text/textpad.py index 5daddab0..25a89b7c 100644 --- a/sciwx/text/textpad.py +++ b/sciwx/text/textpad.py @@ -12,8 +12,8 @@ def __init__(self, parent, cont='', title='no name'): self.text.SetValue(cont) sizer.Add( self.text, 1, wx.ALL|wx.EXPAND, 1 ) self.SetSizer( sizer ) - self.Bind(wx.EVT_RIGHT_DOWN,self.OnRClick) + self.set_cont = self.text.SetValue def OnOpen(self,event): dialog=wx.FileDialog(self,'wxpython Notebook(o)',style=wx.FD_OPEN) @@ -69,7 +69,6 @@ def __init__(self, parent, title='no name', cont=''): self.title = title self.textpad = TextPad(self, cont, title) self.append = self.textpad.append - self.append(cont) ### Create menus (name:event) k-v pairs menus = [ ## File @@ -135,7 +134,7 @@ def textpad(self, i=None): def set_background(self, img): self.GetAuiManager().SetArtProvider(ImgArtProvider(img)) - def add_notepad(self, textpad=None): + def add_page(self, textpad=None): if textpad is None: textpad = TextPad(self) self.AddPage(textpad, 'Text', True, wx.NullBitmap ) return textpad From 607e81fe7fbaecf92c476327e0c24098c9a82649 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Sat, 20 Jun 2020 22:55:56 +0800 Subject: [PATCH 252/343] painter bug fix: check the color value --- imagepy/tools/Standard/painter_tol.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/tools/Standard/painter_tol.py b/imagepy/tools/Standard/painter_tol.py index ad820717..5642b348 100644 --- a/imagepy/tools/Standard/painter_tol.py +++ b/imagepy/tools/Standard/painter_tol.py @@ -2,7 +2,7 @@ from skimage.draw import line, circle def drawline(img, oldp, newp, w, value): - if img.ndim == 2: value = sum(value)/3 + if img.ndim == 2 and hasattr(value, '__iter__'): value = sum(value)/3 oy, ox = line(*[int(round(i)) for i in oldp+newp]) cy, cx = circle(0, 0, w/2+1e-6) ys = (oy.reshape((-1,1))+cy).clip(0, img.shape[0]-1) From 12202c7317f115762918daa06de320c16d329010 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 20 Jun 2020 23:24:53 +0800 Subject: [PATCH 253/343] status bar --- imagepy/data/config.json | 2 +- sciapp/action/tolact.py | 13 ++++++++++++- sciwx/canvas/canvas.py | 6 ++++-- sciwx/canvas/mcanvas.py | 2 +- sciwx/widgets/toolbar.py | 4 +--- 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/imagepy/data/config.json b/imagepy/data/config.json index 2c4dc319..55a088ec 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["recent", ["C:/Users/54631/Documents/projects/imagepy/imagepy/tools/Stack/Add Slice.mc", "C:/Users/54631/Documents/projects/imagepy/imagepy/menus/Plugins/Contribute/Contributions/CellPose.md", "C:/Users/54631/Documents/projects/imagepy/imagepy/plugins/demoplugin/menus/Demos/Workflow Demo/Workflow Coins Demo.wf", "C:/Users/54631/Documents/projects/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Coins Segment.mc"], null], ["uistyle", "imagepy", null]] \ No newline at end of file +[["uistyle", "imagepy", null], ["recent", ["C:/Users/54631/Documents/projects/imagepy/imagepy/tools/Stack/Add Slice.mc", "C:/Users/54631/Documents/projects/imagepy/imagepy/menus/Plugins/Contribute/Contributions/CellPose.md", "C:/Users/54631/Documents/projects/imagepy/imagepy/plugins/demoplugin/menus/Demos/Workflow Demo/Workflow Coins Demo.wf", "C:/Users/54631/Documents/projects/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Coins Segment.mc"], null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null]] \ No newline at end of file diff --git a/sciapp/action/tolact.py b/sciapp/action/tolact.py index 02336864..47b478ec 100644 --- a/sciapp/action/tolact.py +++ b/sciapp/action/tolact.py @@ -8,6 +8,7 @@ class Tool(SciAction): para = None def config(self): pass + def mouse_hover(self, canvas, x, y, btn, **key): pass def mouse_down(self, canvas, x, y, btn, **key): pass def mouse_up(self, canvas, x, y, btn, **key): pass def mouse_move(self, canvas, x, y, btn, **key): pass @@ -20,7 +21,7 @@ class DefaultTool(Tool): title = 'Move And Scale' def __init__(self): self.oldxy = None - + def mouse_down(self, obj, x, y, btn, **key): if btn==1: self.oldxy = key['px'], key['py'] if btn==3: key['canvas'].fit() @@ -48,6 +49,13 @@ class ImageTool(DefaultTool): default = None title = 'Image Tool' + def mouse_move(self, img, x, y, btn, **key): + if self.app is None: return + r, c = int(y), int(x) + if (r>0) & (c>0) & (r Date: Sun, 21 Jun 2020 16:57:11 +0800 Subject: [PATCH 254/343] table statistic note corrected --- imagepy/menus/Table/Statistic/statistic_plgs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imagepy/menus/Table/Statistic/statistic_plgs.py b/imagepy/menus/Table/Statistic/statistic_plgs.py index 8bce5ead..d62a76f5 100644 --- a/imagepy/menus/Table/Statistic/statistic_plgs.py +++ b/imagepy/menus/Table/Statistic/statistic_plgs.py @@ -3,7 +3,7 @@ class Statistic(Table): title = 'Table Statistic' - note = ['snap', 'only_num', 'auto_msk'] + note = ['auto_snap', 'num_only', 'auto_msk'] para = {'axis':'Column', 'sum':True, 'mean':True,'max':False, 'min':False,'var':False,'std':False,'skew':False,'kurt':False} @@ -20,6 +20,7 @@ class Statistic(Table): def run(self, tps, snap, data, para=None): rst, axis = {}, (0,1)[para['axis']=='Row'] + print("snap = ", snap) if para['sum']:rst['sum'] = snap.sum(axis=axis) if para['mean']:rst['mean'] = snap.mean(axis=axis) if para['max']:rst['max'] = snap.max(axis=axis) From f4573305ac160a70a39a23971ed36ec439c74775 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 22 Jun 2020 08:27:00 +0800 Subject: [PATCH 255/343] chinese --- imagepy/core/app/imagepy.py | 22 +- imagepy/core/app/loader.py | 39 +- imagepy/core/app/source.py | 2 + imagepy/core/app/startup.py | 13 +- imagepy/core/engine/__init__.py | 2 - imagepy/core/engine/filter.py | 9 +- imagepy/core/engine/free.py | 8 +- imagepy/core/engine/simple.py | 8 +- imagepy/core/engine/table.py | 8 +- imagepy/core/engine/tool.py | 28 -- imagepy/core/engine/workflow.py | 52 --- imagepy/data/config.json | 2 +- imagepy/doc/English/plugins/File/New.md | 19 + imagepy/lang/Chinese/plugins/file.dic | 13 + imagepy/lang/Chinese/plugins/menus.dic | 21 ++ imagepy/lang/Chinese/tools/standard.dic | 4 + .../Analysis/Pixel Cluster/kmeans_plgs.py | 8 + imagepy/menus/Edit/edit_plg.py | 5 +- imagepy/menus/File/new_plg.py | 2 +- imagepy/menus/File/open_plg.py | 2 +- imagepy/menus/Help/Language/language_plgs.py | 40 +-- imagepy/menus/Image/Stack/orthogonal_plg.py | 5 +- imagepy/menus/Plugins/Games/lifegame_plg.py | 5 +- imagepy/menus/Process/Filters/classic_plgs.py | 10 +- imagepy/tools/Draw/Route_tol.py | 3 +- imagepy/tools/Measure/profile_tol.py | 4 +- imagepy/tools/Standard/polygon_tol.py | 10 +- imagepy/tools/Toolkit3D/cursor3d_tol.py | 4 +- sciapp/action/tolact.py | 1 - sciwx/app/__init__.py | 3 +- sciwx/app/imgapp.py | 187 ++++++++++ sciwx/app/miniapp.py | 332 ++++++++++++++++++ sciwx/app/sciapp.py | 222 ------------ sciwx/demo/dialog_demo.py | 3 +- sciwx/widgets/menubar.py | 12 +- sciwx/widgets/paradialog.py | 15 +- sciwx/widgets/propdialog.py | 100 ++++++ sciwx/widgets/toolbar.py | 11 +- 38 files changed, 840 insertions(+), 394 deletions(-) delete mode 100644 imagepy/core/engine/tool.py delete mode 100644 imagepy/core/engine/workflow.py create mode 100644 imagepy/doc/English/plugins/File/New.md create mode 100644 imagepy/lang/Chinese/plugins/file.dic create mode 100644 imagepy/lang/Chinese/plugins/menus.dic create mode 100644 imagepy/lang/Chinese/tools/standard.dic create mode 100644 sciwx/app/imgapp.py create mode 100644 sciwx/app/miniapp.py delete mode 100644 sciwx/app/sciapp.py create mode 100644 sciwx/widgets/propdialog.py diff --git a/imagepy/core/app/imagepy.py b/imagepy/core/app/imagepy.py index b90c5188..eef26986 100644 --- a/imagepy/core/app/imagepy.py +++ b/imagepy/core/app/imagepy.py @@ -12,7 +12,7 @@ from sciapp import App, Source from sciapp.object import Image from imagepy import root_dir -from .startup import load_plugins, load_tools, load_widgets +from .startup import load_plugins, load_tools, load_widgets, load_document, load_dictionary class ImagePy(wx.Frame, App): def __init__( self, parent ): @@ -72,6 +72,8 @@ def OnDropFiles(self, x, y, path): . MinSize(wx.Size(-1, 20)). MaxSize(wx.Size(-1, 20)).Layer( 10 ) ) def _load_all(self): + load_document() + load_dictionary() plgs, errplg = load_plugins() self.load_menu(plgs) dtool = Source.manager('tools').get('default') @@ -90,6 +92,9 @@ def load_all(self): def load_menu(self, data): self.menubar.clear() + lang = Source.manager('config').get('language') + ls = Source.manager('dictionary').gets(tag=lang) + self.menubar.dic = dict([(i,j[i]) for i,j,_ in ls]) self.menubar.load(data) def load_tool(self, data, default=None): @@ -109,6 +114,11 @@ def init_menu(self): def init_tool(self): sizer = wx.BoxSizer(wx.VERTICAL) self.toolbar = ToolBar(self, True) + def on_help(evt, tol): + lang = Source.manager('config').get('language') + doc = Source.manager('document').get(tol.title, tag=lang) + self.show_md(doc or 'No Document!', tol.title) + self.toolbar.on_help = on_help self.toolbar.Fit() self.auimgr.AddPane(self.toolbar, aui.AuiPaneInfo() .Left() .PinButton( True ) @@ -318,7 +328,7 @@ def _show_workflow(self, cont, title='ImagePy'): pan.SetValue(cont) info = aui.AuiPaneInfo(). DestroyOnClose(True). Left(). Caption(title) .PinButton( True ) \ .Resizable().FloatingSize( wx.DefaultSize ).Dockable(True).Dock().Top().Layer( 5 ) - pan.Bind(None, pan.Bind(None, lambda x:self.run_macros(['%s>None'%x]))) + pan.Bind(None, lambda x:self.run_macros(['%s>None'%x])) self.auimgr.AddPane(pan, info) self.auimgr.Update() @@ -462,12 +472,16 @@ def getpath(self, title, filt, io, name=''): dialog.Destroy() return path - def show_para(self, title, view, para, on_handle=None, on_ok=None, on_cancel=None, preview=False, modal=True): - dialog = ParaDialog(self, title) + def show_para(self, title, view, para, on_handle=None, on_ok=None, + on_cancel=None, on_help=None, preview=False, modal=True): + lang = Source.manager('config').get('language') + dic = Source.manager('dictionary').get(name=title, tag=lang) + dialog = ParaDialog(self, title, dic or {}) dialog.init_view(view, para, preview, modal=modal, app=self) dialog.Bind('cancel', on_cancel) dialog.Bind('parameter', on_handle) dialog.Bind('commit', on_ok) + dialog.Bind('help', on_help) return dialog.show() if __name__ == '__main__': diff --git a/imagepy/core/app/loader.py b/imagepy/core/app/loader.py index c9a9d415..c7f94134 100644 --- a/imagepy/core/app/loader.py +++ b/imagepy/core/app/loader.py @@ -5,7 +5,7 @@ @author: yxl """ import os, sys -from ..engine import Macros, MkDown, Widget, WorkFlow, Report +from ..engine import Macros, MkDown, Widget, Report from sciapp import Source from ... import root_dir from codecs import open @@ -203,16 +203,37 @@ def build_widgets(path, err='root'): def build_document(path): docs = [] - for dirpath,dirnames,filenames in os.walk(path): - for filename in filenames: - if filename[-3:] != '.md': continue - docs.append(os.path.join(dirpath, filename)) - f = open(docs[-1], encoding='utf-8') - cont = f.read() - f.close() - DocumentManager.add(filename[:-3], cont) + for lang in Source.manager('document').get('language'): + for dirpath, dirnames, filenames in os.walk(path+'/'+lang): + for filename in filenames: + if filename[-3:] != '.md': continue + docs.append(os.path.join(dirpath, filename)) + f = open(docs[-1], encoding='utf-8') + cont = f.read() + f.close() + Source.manager('document').add(filename[:-3], cont, lang) return docs +def build_dictionary(path): + for lang in Source.manager('dictionary').get('language'): + for dirpath, dirnames, filenames in os.walk(path+'/'+lang): + for filename in filenames: + if filename[-3:] != 'dic': continue + with open(os.path.join(dirpath, filename), encoding='utf-8') as f: + lines = f.read().replace('\r','').split('\n') + dic = [] + for line in lines: + if line == '': + dic[-1] = (dic[-1][0][0], dict(dic[-1])) + elif line[0] == '\t': + dic[-1].append(line[1:].split('::')) + else: + dic.append([line.split('::')]) + if isinstance(dic[-1], list): + dic[-1] = (dic[-1][0][0], dict(dic[-1])) + dic = dict(dic) + for i in dic: Source.manager('dictionary').add(i, dic[i], lang) + if __name__ == "__main__": print (os.getcwd()) os.chdir('../../') diff --git a/imagepy/core/app/source.py b/imagepy/core/app/source.py index efd6400e..b248be44 100644 --- a/imagepy/core/app/source.py +++ b/imagepy/core/app/source.py @@ -11,6 +11,8 @@ Source.manager('config') Source.manager('config').read(osp.join(root_dir, 'data/config.json')) +if Source.manager('config').get('language') is None: + Source.manager('config').add('language', 'english') from sciapp.object import Shape mark_style = Source.manager('config').get('mark_style') diff --git a/imagepy/core/app/startup.py b/imagepy/core/app/startup.py index 2b5b0626..a238b3d6 100644 --- a/imagepy/core/app/startup.py +++ b/imagepy/core/app/startup.py @@ -1,4 +1,4 @@ -import wx, sys +import wx, sys, os from .source import * from sciapp import Source from imagepy.core.app import loader @@ -61,6 +61,14 @@ def load_widgets(): data[2].extend(wgts[2]) return extend_wgts(data[:2]), data[2] +def load_document(): + Source.manager('document').add('language', os.listdir('doc')) + loader.build_document('doc/') + +def load_dictionary(): + Source.manager('dictionary').add('language', os.listdir('lang')) + loader.build_dictionary('lang/') + def start(): from imagepy.core.app import ImagePy, ImageJ import wx.lib.agw.advancedsplash as AS @@ -75,7 +83,8 @@ def start(): AS.AS_SHADOW_BITMAP, shadowcolour=shadow) asp.Update() - + load_document() + load_dictionary() uistyle = Source.manager('config').get('uistyle') or 'imagepy' frame = ImageJ(None) if uistyle == 'imagej' else ImagePy(None) frame.Show() diff --git a/imagepy/core/engine/__init__.py b/imagepy/core/engine/__init__.py index c3b02487..84798dcb 100644 --- a/imagepy/core/engine/__init__.py +++ b/imagepy/core/engine/__init__.py @@ -1,10 +1,8 @@ from .filter import Filter from .simple import Simple from .free import Free -from .tool import Tool from .macros import Macros from .mkdown import MkDown from .widget import Widget from .table import Table -from .workflow import WorkFlow from .report import Report \ No newline at end of file diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index 0a4d6a29..4c17b5ae 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -4,7 +4,6 @@ @author: yxl """ -import wx import threading import numpy as np @@ -93,7 +92,8 @@ def progress(self, i, n): self.prgs = int(i*100/n) def show(self): preview = lambda para, ips=self.ips: self.preview(ips, para) or ips.update() return self.app.show_para(self.title, self.view, self.para, preview, - on_ok=lambda : self.ok(self.ips), on_cancel=lambda : self.cancel(self.ips) or self.ips.update(), + on_ok=lambda : self.ok(self.ips), on_help=self.on_help, + on_cancel=lambda : self.cancel(self.ips) or self.ips.update(), preview='preview' in self.note, modal=self.modal) def run(self, ips, snap, img, para = None): @@ -157,6 +157,11 @@ def ok(self, ips, para=None, callafter=None): elif rst == 'cancel': pass #ips.update() + def on_help(self): + lang = Source.manager('config').get('language') + doc = Source.manager('document').get(self.title, tag=lang) + self.app.show_md(doc or 'No Document!', self.title) + def cancel(self, ips): if 'auto_snap' in self.note: ips.img[:] = ips.snap diff --git a/imagepy/core/engine/free.py b/imagepy/core/engine/free.py index c502db18..f9fa3d53 100644 --- a/imagepy/core/engine/free.py +++ b/imagepy/core/engine/free.py @@ -4,6 +4,7 @@ @author: yxl """ import threading +from sciapp import Source from time import time class Free: @@ -27,10 +28,15 @@ def runasyn(self, para, callback=None): if callback!=None:callback() def load(self):return True + + def on_help(self): + lang = Source.manager('config').get('language') + doc = Source.manager('document').get(self.title, tag=lang) + self.app.show_md(doc or 'No Document!', self.title) def show(self): if self.view==None:return True - return self.app.show_para(self.title, self.view, self.para, None) + return self.app.show_para(self.title, self.view, self.para, on_help=self.on_help) def start(self, app, para=None, callback=None): self.app = app diff --git a/imagepy/core/engine/simple.py b/imagepy/core/engine/simple.py index 3f9f79b5..706344ca 100644 --- a/imagepy/core/engine/simple.py +++ b/imagepy/core/engine/simple.py @@ -31,13 +31,19 @@ def preview(self, ips, para):pass def show(self): preview = lambda para, ips=self.ips: self.preview(ips, para) or ips.update() return self.app.show_para(self.title, self.view, self.para, preview, - on_ok=lambda : self.ok(self.ips), on_cancel=lambda : self.cancel(self.ips) or self.ips.update(), + on_ok=lambda : self.ok(self.ips), on_help=self.on_help, + on_cancel=lambda : self.cancel(self.ips) or self.ips.update(), preview='preview' in self.note, modal=self.modal) def run(self, ips, imgs, para = None):pass def cancel(self, ips):pass + def on_help(self): + lang = Source.manager('config').get('language') + doc = Source.manager('document').get(self.title, tag=lang) + self.app.show_md(doc or 'No Document!', self.title) + def ok(self, ips, para=None, callafter=None): if para == None: para = self.para if self.asyn : diff --git a/imagepy/core/engine/table.py b/imagepy/core/engine/table.py index 850d118e..20be2323 100644 --- a/imagepy/core/engine/table.py +++ b/imagepy/core/engine/table.py @@ -32,7 +32,8 @@ def preview(self, tps, para): def show(self): preview = lambda para, tps=self.tps: self.preview(tps, para) or tps.update() return self.app.show_para(self.title, self.view, self.para, preview, - on_ok=lambda : self.ok(self.tps), on_cancel=lambda : self.cancel(self.tps) or self.tps.update(), + on_ok=lambda : self.ok(self.tps), on_help=self.on_help, + on_cancel=lambda : self.cancel(self.tps) or self.tps.update(), preview='preview' in self.note, modal=self.modal) def run(self, tps, snap, data, para = None):pass @@ -42,6 +43,11 @@ def cancel(self, tps): tps.data[tps.snap.columns] = tps.snap tps.update() + def on_help(self): + lang = Source.manager('config').get('language') + doc = Source.manager('document').get(self.title, tag=lang) + self.app.show_md(doc or 'No Document!', self.title) + def ok(self, tps, para=None, callafter=None): if para == None: para = self.para if self.asyn: diff --git a/imagepy/core/engine/tool.py b/imagepy/core/engine/tool.py deleted file mode 100644 index 81738fbf..00000000 --- a/imagepy/core/engine/tool.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Sat Dec 3 03:55:51 2016 -@author: yxl -""" - -class Tool: - title = 'Tool' - view, para = None, None - - def show(self): - if self.view == None:return - rst = IPy.get_para(self.title, self.view, self.para) - if rst!=None : self.config() - - def config(self):pass - def load(self):pass - def switch(self):pass - - def start(self): - ips = IPy.get_ips() - if not ips is None and not ips.tool is None: - ips.tool = None - - def mouse_down(self, ips, x, y, btn, **key): pass - def mouse_up(self, ips, x, y, btn, **key): pass - def mouse_move(self, ips, x, y, btn, **key): pass - def mouse_wheel(self, ips, x, y, d, **key): pass \ No newline at end of file diff --git a/imagepy/core/engine/workflow.py b/imagepy/core/engine/workflow.py deleted file mode 100644 index d31a6cf1..00000000 --- a/imagepy/core/engine/workflow.py +++ /dev/null @@ -1,52 +0,0 @@ -import threading, wx, os, wx.lib.agw.aui as aui -from sciapp import Source - -def parse(cont): - ls = cont.split('\n') - workflow = {'title':ls[0], 'chapter':[]} - for line in ls[2:]: - line = line.strip() - if line.startswith('## '): - chapter = {'title':line[3:], 'section':[]} - workflow['chapter'].append(chapter) - elif line[1:3] == '. ': - section = {'title':line[3:]} - else: - section['hint'] = line - chapter['section'].append(section) - return workflow - -class WorkFlow: - def __init__(self, title, cont): - self.title = title - self.workflow = parse(cont) - self.cont = cont - - def __call__(self): - return self - - def start(self, para=None, callafter=None): - pan = WorkFlowPanel(IPy.curapp) - pan.load(self.cont, self.workflow) - info = aui.AuiPaneInfo(). DestroyOnClose(True). Left(). Caption(self.title) .PinButton( True ) \ - .Resizable().FloatingSize( wx.DefaultSize ).Dockable(IPy.uimode()=='ipy').Layer( 5 ) - - if IPy.uimode()=='ipy': info.Dock().Top() - if IPy.uimode()=='ij': info.Float() - IPy.curapp.auimgr.AddPane(pan, info) - IPy.curapp.Layout() - IPy.curapp.auimgr.Update() - -def show_wf(data, title): - wx.CallAfter(WorkFlow(title, data).start) - -# ViewerManager.add('wf', show_wf) - -def read_wf(path): - f = open(path, encoding='utf-8') - cont = f.read() - f.close() - print(cont) - return cont - -Source.manager('reader').add(name='wf', obj=read_wf, tag='wf') \ No newline at end of file diff --git a/imagepy/data/config.json b/imagepy/data/config.json index 55a088ec..88c3b2f4 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["uistyle", "imagepy", null], ["recent", ["C:/Users/54631/Documents/projects/imagepy/imagepy/tools/Stack/Add Slice.mc", "C:/Users/54631/Documents/projects/imagepy/imagepy/menus/Plugins/Contribute/Contributions/CellPose.md", "C:/Users/54631/Documents/projects/imagepy/imagepy/plugins/demoplugin/menus/Demos/Workflow Demo/Workflow Coins Demo.wf", "C:/Users/54631/Documents/projects/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Coins Segment.mc"], null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null]] \ No newline at end of file +[["language", "Chinese", null], ["uistyle", "imagepy", null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["recent", ["C:/Users/54631/Desktop/Workflow Coins Demo.wf", "C:/Users/54631/Documents/projects/imagepy/imagepy/tools/Stack/Add Slice.mc", "C:/Users/54631/Documents/projects/imagepy/imagepy/menus/Plugins/Contribute/Contributions/CellPose.md", "C:/Users/54631/Documents/projects/imagepy/imagepy/plugins/demoplugin/menus/Demos/Workflow Demo/Workflow Coins Demo.wf"], null]] \ No newline at end of file diff --git a/imagepy/doc/English/plugins/File/New.md b/imagepy/doc/English/plugins/File/New.md new file mode 100644 index 00000000..2464a4a2 --- /dev/null +++ b/imagepy/doc/English/plugins/File/New.md @@ -0,0 +1,19 @@ +# New + +**Description:** create a new Image + + +## Parameter + +**name:** the new image's title + +**width:** image's width + +**height:** image's height + +**type:** image's type + +1. 8-bit: create a 8-bit one channel image +2. rgb: create a 24-bit three channel image + +**slice:** the z thickness \ No newline at end of file diff --git a/imagepy/lang/Chinese/plugins/file.dic b/imagepy/lang/Chinese/plugins/file.dic new file mode 100644 index 00000000..52e6ffec --- /dev/null +++ b/imagepy/lang/Chinese/plugins/file.dic @@ -0,0 +1,13 @@ +New::新建图像 + name::名称 + width::宽度 + height::高度 + pix::像素 + type::类型 + slice::层数 + +Open::打开图像 + +Open Url::打开链接 + Input the URL, eg. http://data.imagepy.org/testdata/yxdragon.jpg::输入链接, 例如 http://data.imagepy.org/testdata/yxdragon.jpg + Url::链接 \ No newline at end of file diff --git a/imagepy/lang/Chinese/plugins/menus.dic b/imagepy/lang/Chinese/plugins/menus.dic new file mode 100644 index 00000000..c868de56 --- /dev/null +++ b/imagepy/lang/Chinese/plugins/menus.dic @@ -0,0 +1,21 @@ +File::文件 + +Edit::编辑 + +Image::图像 + +Process::处理 + +Selection::选择 + +Analysis::分析 + +Table::表格 + +Kit3D::3D工具 + +Plugins::插件 + +Window::窗口 + +Help::帮助 \ No newline at end of file diff --git a/imagepy/lang/Chinese/tools/standard.dic b/imagepy/lang/Chinese/tools/standard.dic new file mode 100644 index 00000000..e569d867 --- /dev/null +++ b/imagepy/lang/Chinese/tools/standard.dic @@ -0,0 +1,4 @@ +Color Picker::取色器 + front::前景色 + back::背景色 + color::颜色 \ No newline at end of file diff --git a/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py b/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py index e9e52485..86863a62 100644 --- a/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py +++ b/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py @@ -20,3 +20,11 @@ def run(self, ips, snap, img, para = None): img[:] = ms[vq(pts, ms)[0]].reshape(img.shape) plgs = [K_mean] + +if __name__ == '__main__': + from skimage.data import camera, astronaut + from sciwx.app import ImageApp + + ImageApp.start( + imgs = [('astronaut', astronaut())], + plgs=[('K', K_mean)]) \ No newline at end of file diff --git a/imagepy/menus/Edit/edit_plg.py b/imagepy/menus/Edit/edit_plg.py index 35f0e925..635081cf 100644 --- a/imagepy/menus/Edit/edit_plg.py +++ b/imagepy/menus/Edit/edit_plg.py @@ -4,10 +4,11 @@ @author: yxl """ import numpy as np -from imagepy.core.engine import Simple, Tool, Filter +from imagepy.core.engine import Simple, Filter +from sciapp.action import ImageTool from sciapp import Source -class PasteMove(Tool): +class PasteMove(ImageTool): def __init__(self): self.moving = True self.cx, self.cy = 0, 0 diff --git a/imagepy/menus/File/new_plg.py b/imagepy/menus/File/new_plg.py index e08c1f68..2cfe8764 100644 --- a/imagepy/menus/File/new_plg.py +++ b/imagepy/menus/File/new_plg.py @@ -13,7 +13,7 @@ class Plugin(Free): view = [(str, 'name', 'name', ''), (int, 'width', (1,10240), 0, 'width', 'pix'), (int, 'height', (1,10240), 0, 'height', 'pix'), - (list, 'type', ['8-bit','RGB'], str, 'Type', ''), + (list, 'type', ['8-bit','RGB'], str, 'type', ''), (int, 'slice', (1,2048), 0, 'slice', '')] #process diff --git a/imagepy/menus/File/open_plg.py b/imagepy/menus/File/open_plg.py index 0f96544b..bec6356d 100644 --- a/imagepy/menus/File/open_plg.py +++ b/imagepy/menus/File/open_plg.py @@ -19,7 +19,7 @@ class OpenUrl(Free): title = 'Open Url' para = {'url':'http://data.imagepy.org/testdata/yxdragon.jpg'} view = [('lab', None, 'Input the URL, eg. http://data.imagepy.org/testdata/yxdragon.jpg'), - (str, 'url', 'Url:', '')] + (str, 'url', 'Url', '')] def run(self, para = None): try: diff --git a/imagepy/menus/Help/Language/language_plgs.py b/imagepy/menus/Help/Language/language_plgs.py index a39b9922..8ab03960 100644 --- a/imagepy/menus/Help/Language/language_plgs.py +++ b/imagepy/menus/Help/Language/language_plgs.py @@ -6,44 +6,12 @@ def __init__(self, key): self.title = key asyn = False - #process def run(self, para = None): - Manager.set(self.title) - self.app.reload_plugins() + Source.manager('config').remove('language') + Source.manager('config').add('language', self.title) + self.app.load_all() def __call__(self): return self -plgs = [Language(i) for i in list(Source.manager('language').names())] -plgs.insert(0, Language('English')) -plgs.append('-') - - -class NewLanguage(Free): - title = 'New Language' - para = {'name':'your language'} - view = [(str, 'name', 'name', '')] - - def run(self, para = None): - LanguageManager.newdic(para['name']) - LanguageManager.write() - -class UpdateDictionary(Free): - title = 'Update Dictionary' - - def run(self, para = None): - LanguageManager.add() - LanguageManager.write() - -class CleanDictionary(Free): - title = 'Clean Dictionary' - - def run(self, para = None): - LanguageManager.rm() - LanguageManager.write() - -plgs.extend([NewLanguage, UpdateDictionary, CleanDictionary]) - -if __name__ == '__main__': - print(list(ColorManager.luts.keys())) - \ No newline at end of file +plgs = [Language(i) for i in Source.manager('dictionary').get('language')] \ No newline at end of file diff --git a/imagepy/menus/Image/Stack/orthogonal_plg.py b/imagepy/menus/Image/Stack/orthogonal_plg.py index 74fd62eb..4bd88505 100644 --- a/imagepy/menus/Image/Stack/orthogonal_plg.py +++ b/imagepy/menus/Image/Stack/orthogonal_plg.py @@ -6,7 +6,8 @@ import wx # from imagepy.core import ImagePlus -from imagepy.core.engine import Tool, Simple +from sciapp.action import ImageTool +from imagepy.core.engine import Simple class Cross: def __init__(self, w, h): @@ -23,7 +24,7 @@ def draw(self, dc, f): dc.DrawLines([f(0,self.y),f(self.w,self.y)]) dc.DrawLines([f(self.x,0),f(self.x,self.h)]) -class Orthogonal(Tool): +class Orthogonal(ImageTool): title = 'Orthogonal View' def __init__(self): self.view1, self.view2 = None, None diff --git a/imagepy/menus/Plugins/Games/lifegame_plg.py b/imagepy/menus/Plugins/Games/lifegame_plg.py index db59ef74..bdbad486 100644 --- a/imagepy/menus/Plugins/Games/lifegame_plg.py +++ b/imagepy/menus/Plugins/Games/lifegame_plg.py @@ -1,5 +1,6 @@ import numpy as np -from imagepy.core.engine import Free, Tool +from imagepy.core.engine import Free +from sciapp.action import ImageTool from scipy.ndimage import label from scipy.signal import convolve2d from sciapp.object import Image @@ -21,7 +22,7 @@ def run(scr): lut = [0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0] return np.array(lut, dtype=np.uint8)[cov.astype(np.uint8)] -class Painter(Tool): +class Painter(ImageTool): def __init__(self, size): self.size = size title = 'Life Painter' diff --git a/imagepy/menus/Process/Filters/classic_plgs.py b/imagepy/menus/Process/Filters/classic_plgs.py index c6617183..c4bd890f 100644 --- a/imagepy/menus/Process/Filters/classic_plgs.py +++ b/imagepy/menus/Process/Filters/classic_plgs.py @@ -190,4 +190,12 @@ def run(self, ips, snap, img, para = None): img += snap plgs = [Uniform, Gaussian, '-', Maximum, Minimum, Median, Percent, '-', - Prewitt, Sobel, Laplace, GaussianLaplace, DOG, '-', Variance, LaplaceSharp, USM] \ No newline at end of file + Prewitt, Sobel, Laplace, GaussianLaplace, DOG, '-', Variance, LaplaceSharp, USM] + +if __name__ == '__main__': + from skimage.data import camera, astronaut + from sciwx.app import ImageApp + + ImageApp.start( + imgs = [('astronaut', astronaut())], + plgs=[('U', USM), ('G', Gaussian)]) \ No newline at end of file diff --git a/imagepy/tools/Draw/Route_tol.py b/imagepy/tools/Draw/Route_tol.py index f1772eae..a76eceaa 100644 --- a/imagepy/tools/Draw/Route_tol.py +++ b/imagepy/tools/Draw/Route_tol.py @@ -4,7 +4,8 @@ @author: yxl """ -from imagepy.core.engine import Tool,Filter +from imagepy.core.engine import Filter +from sciapp.action import ImageTool import numpy as np from sciapp.util import mark2shp from sciapp.action import ImageTool diff --git a/imagepy/tools/Measure/profile_tol.py b/imagepy/tools/Measure/profile_tol.py index 5dbbb0f0..3511c283 100644 --- a/imagepy/tools/Measure/profile_tol.py +++ b/imagepy/tools/Measure/profile_tol.py @@ -7,7 +7,7 @@ import wx #from imagepy import IPy -from imagepy.core.engine import Tool +from sciapp.action import ImageTool import numpy as np import pandas as pd from numpy.linalg import norm @@ -73,7 +73,7 @@ def report(self, title): rst.append(np.round(np.arccos(dxy[:,0]/l)/np.pi*180,1)) IPy.show_table(pd.DataFrame(rst, columns=titles), title) -class Plugin(Tool): +class Plugin(ImageTool): """Define the profile class plugin with the event callback functions""" title = 'Profile' def __init__(self): diff --git a/imagepy/tools/Standard/polygon_tol.py b/imagepy/tools/Standard/polygon_tol.py index 86fabfb6..f29674f9 100644 --- a/imagepy/tools/Standard/polygon_tol.py +++ b/imagepy/tools/Standard/polygon_tol.py @@ -1 +1,9 @@ -from sciapp.action import PolygonROI as Plugin \ No newline at end of file +from sciapp.action import PolygonROI as Plugin + +if __name__ == '__main__': + from skimage.data import camera, astronaut + from sciwx.app import ImageApp + + ImageApp.start( + imgs = [('astronaut', astronaut())], + plgs=[('P', Plugin)]) \ No newline at end of file diff --git a/imagepy/tools/Toolkit3D/cursor3d_tol.py b/imagepy/tools/Toolkit3D/cursor3d_tol.py index 4d94e9e2..366ff90e 100644 --- a/imagepy/tools/Toolkit3D/cursor3d_tol.py +++ b/imagepy/tools/Toolkit3D/cursor3d_tol.py @@ -1,10 +1,10 @@ import wx -from imagepy.core.engine import Tool +from sciapp.action import ImageTool #from imagepy.core import myvi #from imagepy import IPy import numpy as np -class Plugin(Tool): +class Plugin(ImageTool): title = 'Cursor 3D' para = {'r':1, 'color':(255,0,0)} view = [(int, 'r', (0,100), 0, 'radius', 'pix'), diff --git a/sciapp/action/tolact.py b/sciapp/action/tolact.py index 47b478ec..8255e822 100644 --- a/sciapp/action/tolact.py +++ b/sciapp/action/tolact.py @@ -8,7 +8,6 @@ class Tool(SciAction): para = None def config(self): pass - def mouse_hover(self, canvas, x, y, btn, **key): pass def mouse_down(self, canvas, x, y, btn, **key): pass def mouse_up(self, canvas, x, y, btn, **key): pass def mouse_move(self, canvas, x, y, btn, **key): pass diff --git a/sciwx/app/__init__.py b/sciwx/app/__init__.py index b27ca44f..d9a89d55 100644 --- a/sciwx/app/__init__.py +++ b/sciwx/app/__init__.py @@ -1,2 +1,3 @@ from sciapp import App, Source -from .sciapp import SciApp \ No newline at end of file +# from .sciapp import SciApp +from .imgapp import ImageApp \ No newline at end of file diff --git a/sciwx/app/imgapp.py b/sciwx/app/imgapp.py new file mode 100644 index 00000000..cac703e4 --- /dev/null +++ b/sciwx/app/imgapp.py @@ -0,0 +1,187 @@ +import wx, os, sys +import time, threading +sys.path.append('../../') +import wx.lib.agw.aui as aui +from sciwx.widgets import MenuBar, ToolBar, ParaDialog +from sciwx.canvas import CanvasNoteBook +from sciwx.widgets import ProgressBar +from sciwx.grid import GridFrame +from sciwx.mesh import Canvas3DFrame +from sciwx.text import MDFrame, TextFrame +from sciwx.plot import PlotFrame +from sciapp import App, Source + + +class ImageApp(wx.Frame, App): + def __init__( self, parent ): + wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = 'ImageApp', + size = wx.Size(800,600), pos = wx.DefaultPosition, + style = wx.RESIZE_BORDER|wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + App.__init__(self) + self.SetSizeHints( wx.Size(600,-1) ) + + sizer = wx.BoxSizer(wx.VERTICAL) + self.toolbar = ToolBar(self) + self.toolbar.Fit() + sizer.Add(self.toolbar, 0, wx.EXPAND |wx.ALL, 0) + + self.canvasnb = CanvasNoteBook(self) + self.canvasnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_new_img) + self.canvasnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close_img) + sizer.Add(self.canvasnb, 1, wx.EXPAND |wx.ALL, 0) + + self.stapanel = stapanel = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + sizersta = wx.BoxSizer( wx.HORIZONTAL ) + self.txt_info = wx.StaticText( stapanel, wx.ID_ANY, "ImageApp", wx.DefaultPosition, wx.DefaultSize, 0 ) + sizersta.Add( self.txt_info, 1, wx.ALIGN_BOTTOM|wx.BOTTOM|wx.LEFT|wx.RIGHT, 2 ) + self.pro_bar = ProgressBar(stapanel) + sizersta.Add( self.pro_bar, 0, wx.ALL, 2 ) + stapanel.SetSizer(sizersta) + sizer.Add(self.stapanel, 0, wx.EXPAND | wx.ALL, 0) + self.SetSizer(sizer) + self.Layout() + self.Centre( wx.BOTH ) + self.Bind(wx.EVT_CLOSE, self.on_close) + + def record_macros(self, x): print(x) + + def add_task(self, task): + self.task_manager.add(task.title, task) + tasks = self.task_manager.gets() + tasks = [(p.title, lambda t=p:p.prgs) for n,p,t in tasks] + self.pro_bar.SetValue(tasks) + + def remove_task(self, task): + self.task_manager.remove(obj=task) + tasks = self.task_manager.gets() + tasks = [(p.title, lambda t=p:p.prgs) for n,p,t in tasks] + self.pro_bar.SetValue(tasks) + + def on_new_img(self, event): + self.add_img(self.canvasnb.canvas().image) + self.add_img_win(self.canvasnb.canvas()) + + def on_close_img(self, event): + canvas = event.GetEventObject().GetPage(event.GetSelection()) + self.remove_img_win(canvas) + self.remove_img(canvas.image) + + def on_new_tab(self, event): + self.add_tab(event.GetEventObject().grid.table) + self.add_tab_win(event.GetEventObject().grid) + + def on_close_tab(self, event): + self.remove_tab_win(event.GetEventObject().grid) + self.remove_tab(event.GetEventObject().grid.table) + event.Skip() + + def set_info(self, value): + wx.CallAfter(self.txt_info.SetLabel, value) + + def set_progress(self, value): + v = max(min(value, 100), 0) + self.pro_bar.SetValue(v) + if value==-1: + self.pro_bar.Hide() + elif not self.pro_bar.IsShown(): + self.pro_bar.Show() + self.stapanel.GetSizer().Layout() + self.pro_bar.Update() + + def on_close(self, event): + self.Destroy() + sys.exit() + + def _show_img(self, img, title=None): + canvas = self.canvasnb.add_canvas() + self.remove_img(canvas.image) + self.remove_img_win(canvas) + if not title is None: + canvas.set_imgs(img) + canvas.image.name = title + else: canvas.set_img(img) + self.add_img(canvas.image) + self.add_img_win(canvas) + + def show_img(self, img, title=None): + wx.CallAfter(self._show_img, img, title) + + def _show_table(self, tab, title): + cframe = GridFrame(self) + grid = cframe.grid + grid.set_data(tab) + if not title is None: + grid.table.name = title + cframe.Bind(wx.EVT_ACTIVATE, self.on_new_tab) + cframe.Bind(wx.EVT_CLOSE, self.on_close_tab) + cframe.Show() + + def show_table(self, tab, title=None): + wx.CallAfter(self._show_table, tab, title) + + def show(self, tag, cont, title): + tag = tag or 'img' + if tag=='img': + self.show_img([cont], title) + elif tag=='imgs': + self.show_img(cont, title) + elif tag=='tab': + self.show_table(cont, title) + elif tag=='mc': + self.run_macros(cont) + elif tag=='md': + self.show_md(cont, title) + elif tag=='wf': + self.show_workflow(cont, title) + else: self.alert('no view for %s!'%tag) + + def info(self, cont): + wx.CallAfter(self.txt_info.SetLabel, cont) + + def _alert(self, info, title='ImagePy'): + dialog=wx.MessageDialog(self, info, title, wx.OK) + dialog.ShowModal() == wx.ID_OK + dialog.Destroy() + + def alert(self, info, title='ImagePy'): + wx.CallAfter(self._alert, info, title) + + def yes_no(self, info, title='ImagePy'): + dialog = wx.MessageDialog(self, info, title, wx.YES_NO | wx.CANCEL) + rst = dialog.ShowModal() + dialog.Destroy() + dic = {wx.ID_YES:'yes', wx.ID_NO:'no', wx.ID_CANCEL:'cancel'} + return dic[rst] + + def getpath(self, title, filt, io, name=''): + filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in filt]) + dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} + dialog = wx.FileDialog(self, title, '', name, filt, dic[io]) + rst = dialog.ShowModal() + path = dialog.GetPath() if rst == wx.ID_OK else None + dialog.Destroy() + return path + + def show_para(self, title, view, para, on_handle=None, on_ok=None, on_cancel=None, preview=False, modal=True): + dialog = ParaDialog(self, title) + dialog.init_view(view, para, preview, modal=modal, app=self) + dialog.Bind('cancel', on_cancel) + dialog.Bind('parameter', on_handle) + dialog.Bind('commit', on_ok) + return dialog.show() + + def start(imgs=[], plgs=[]): + app = wx.App(False) + frame = ImageApp(None) + for name, i in imgs: frame.show_img([i], name) + for name, i in plgs: frame.toolbar.add_tool(name, i) + frame.Show() + app.MainLoop() + +if __name__ == '__main__': + from skimage.data import camera + from sciwx.plugins.filters import Gaussian + + ImageApp.start( + imgs = [('camera', camera())], + plgs=[('G', Gaussian)]) \ No newline at end of file diff --git a/sciwx/app/miniapp.py b/sciwx/app/miniapp.py new file mode 100644 index 00000000..94e4826b --- /dev/null +++ b/sciwx/app/miniapp.py @@ -0,0 +1,332 @@ +import wx, os, sys +import time, threading +sys.path.append('../../') +import wx.lib.agw.aui as aui +from sciwx.widgets import MenuBar, ToolBar +from sciwx.canvas import CanvasNoteBook +from sciwx.widgets import ProgressBar +from sciwx.grid import GridFrame +from sciwx.mesh import Canvas3DFrame +from sciwx.text import MDFrame, TextFrame +from sciwx.plot import PlotFrame +from sciapp import App, Source + + +class MiniApp(wx.Frame, App): + def __init__( self, parent ): + wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = 'ImagePy', + size = wx.Size(800,600), pos = wx.DefaultPosition, + style = wx.RESIZE_BORDER|wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + App.__init__(self) + self.SetSizeHints( wx.Size(600,-1) ) + + self.init_menu() + sizer = wx.BoxSizer() + self.canvasnb = CanvasNoteBook(self) + self.canvasnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_new_img) + self.canvasnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close_img) + sizer.Add(self.canvasnb, 1, wx.EXPAND |wx.ALL, 0) + + self.toolbar = ToolBar(self, False) + self.toolbar.Fit() + sizer.Add(self.toolbar, 0, wx.EXPAND |wx.ALL, 0) + + self.SetSizer(sizer) + self.Layout() + self.Centre( wx.BOTH ) + + self.Bind(wx.EVT_CLOSE, self.on_close) + + def init_status(self): + self.stapanel = stapanel = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + sizersta = wx.BoxSizer( wx.HORIZONTAL ) + self.txt_info = wx.StaticText( stapanel, wx.ID_ANY, "ImagePy v0.2", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.txt_info.Wrap( -1 ) + sizersta.Add( self.txt_info, 1, wx.ALIGN_BOTTOM|wx.BOTTOM|wx.LEFT|wx.RIGHT, 2 ) + #self.pro_bar = wx.Gauge( stapanel, wx.ID_ANY, 100, wx.DefaultPosition, wx.Size( 100,15 ), wx.GA_HORIZONTAL ) + self.pro_bar = ProgressBar(stapanel) + sizersta.Add( self.pro_bar, 0, wx.ALL, 2 ) + stapanel.SetSizer(sizersta) + class OpenDrop(wx.FileDropTarget): + def __init__(self, app): + wx.FileDropTarget.__init__(self) + self.app = app + def OnDropFiles(self, x, y, path): + self.app.run_macros(["Open>{'path':'%s'}"%i.replace('\\', '/') for i in path]) + stapanel.SetDropTarget(OpenDrop(self)) + self.auimgr.AddPane( stapanel, aui.AuiPaneInfo() .Bottom() .CaptionVisible( False ).PinButton( True ) + .PaneBorder( False ).Dock().Resizable().FloatingSize( wx.DefaultSize ).DockFixed( True ) + . MinSize(wx.Size(-1, 20)). MaxSize(wx.Size(-1, 20)).Layer( 10 ) ) + + def init_menu(self): + self.menubar = MenuBar(self) + + def init_tool(self): + sizer = wx.BoxSizer(wx.VERTICAL) + self.toolbar = ToolBar(self, False) + self.toolbar.Fit() + + self.auimgr.AddPane(self.toolbar, aui.AuiPaneInfo() .Top() .PinButton( True ).PaneBorder( False ) + .CaptionVisible( False ).Dock().FloatingSize( wx.DefaultSize ).MinSize(wx.Size( -1,34 )).DockFixed( True ) + . BottomDockable( False ).TopDockable( False ).Layer( 10 ) ) + + def add_task(self, task): + self.task_manager.add(task.title, task) + tasks = self.task_manager.gets() + tasks = [(p.title, lambda t=p:p.prgs) for n,p,t in tasks] + self.pro_bar.SetValue(tasks) + + def remove_task(self, task): + self.task_manager.remove(obj=task) + tasks = self.task_manager.gets() + tasks = [(p.title, lambda t=p:p.prgs) for n,p,t in tasks] + self.pro_bar.SetValue(tasks) + + def init_text(self): return + #self.mdframe = MDNoteFrame(self, 'Sci Document') + #self.txtframe = TextNoteFrame(self, 'Sci Text') + + def on_pan_close(self, event): + if event.GetPane().window in [self.toolbar, self.widgets]: + event.Veto() + if hasattr(event.GetPane().window, 'close'): + event.GetPane().window.close() + + def on_new_img(self, event): + self.add_img(self.canvasnb.canvas().image) + self.add_img_win(self.canvasnb.canvas()) + + def on_close_img(self, event): + canvas = event.GetEventObject().GetPage(event.GetSelection()) + self.remove_img_win(canvas) + self.remove_img(canvas.image) + + def on_new_tab(self, event): + self.add_tab(event.GetEventObject().grid.table) + self.add_tab_win(event.GetEventObject().grid) + + def on_close_tab(self, event): + self.remove_tab_win(event.GetEventObject().grid) + self.remove_tab(event.GetEventObject().grid.table) + event.Skip() + + def on_new_mesh(self, event): + self.add_mesh(event.GetEventObject().canvas.mesh) + self.add_mesh_win(event.GetEventObject().canvas) + + def on_close_mesh(self, event): + self.remove_mesh(event.GetEventObject().canvas.mesh) + self.remove_mesh_win(event.GetEventObject().canvas) + event.Skip() + + def set_info(self, value): + wx.CallAfter(self.txt_info.SetLabel, value) + + def set_progress(self, value): + v = max(min(value, 100), 0) + self.pro_bar.SetValue(v) + if value==-1: + self.pro_bar.Hide() + elif not self.pro_bar.IsShown(): + self.pro_bar.Show() + self.stapanel.GetSizer().Layout() + self.pro_bar.Update() + + def on_close(self, event): + self.Destroy() + sys.exit() + + def _show_img(self, img, title=None): + canvas = self.canvasnb.add_canvas() + self.remove_img(canvas.image) + self.remove_img_win(canvas) + if not title is None: + canvas.set_imgs(img) + canvas.image.name = title + else: canvas.set_img(img) + self.add_img(canvas.image) + self.add_img_win(canvas) + + def show_img(self, img, title=None): + wx.CallAfter(self._show_img, img, title) + + def _show_table(self, tab, title): + cframe = GridFrame(self) + grid = cframe.grid + grid.set_data(tab) + if not title is None: + grid.table.name = title + cframe.Bind(wx.EVT_ACTIVATE, self.on_new_tab) + cframe.Bind(wx.EVT_CLOSE, self.on_close_tab) + cframe.Show() + + def show_table(self, tab, title=None): + wx.CallAfter(self._show_table, tab, title) + + def show_plot(self, title): + fig = PlotFrame(self) + fig.figure.title = title + return fig + + def _show_md(self, cont, title='ImagePy'): + mdframe = MDFrame(self) + mdframe.set_cont(cont) + mdframe.mdpad.title = title + mdframe.Show(True) + + def show_md(self, cont, title='ImagePy'): + wx.CallAfter(self._show_md, cont, title) + + def _show_workflow(self, cont, title='ImagePy'): + pan = WorkFlowPanel(self) + pan.SetValue(cont) + info = aui.AuiPaneInfo(). DestroyOnClose(True). Left(). Caption(title) .PinButton( True ) \ + .Resizable().FloatingSize( wx.DefaultSize ).Dockable(False).Float().Top().Layer( 5 ) + pan.Bind(None, lambda x:self.run_macros(['%s>None'%x])) + self.auimgr.AddPane(pan, info) + self.auimgr.Update() + + def show_workflow(self, cont, title='ImagePy'): + wx.CallAfter(self._show_workflow, cont, title) + + def _show_txt(self, cont, title='ImagePy'): + TextFrame(self, title, cont).Show() + + def show_txt(self, cont, title='ImagePy'): + wx.CallAfter(self._show_txt, cont, title) + + def _show_mesh(self, mesh=None, title=None): + if mesh is None: + cframe = Canvas3DFrame(self) + canvas = cframe.canvas + canvas.mesh.name = 'Surface' + + elif hasattr(mesh, 'vts'): + canvas = self.get_mesh_win() + if canvas is None: + cframe = Canvas3DFrame(self) + canvas = cframe.canvas + canvas.mesh.name = 'Surface' + canvas.add_surf(title, mesh) + else: + cframe = Canvas3DFrame(self) + canvas = cframe.canvas + canvas.set_mesh(mesh) + canvas.GetParent().Show() + canvas.GetParent().Bind(wx.EVT_ACTIVATE, self.on_new_mesh) + canvas.GetParent().Bind(wx.EVT_CLOSE, self.on_close_mesh) + self.add_mesh(canvas.mesh) + self.add_mesh_win(canvas) + + def show_mesh(self, mesh=None, title=None): + wx.CallAfter(self._show_mesh, mesh, title) + + def show_widget(self, panel, title='Widgets'): + obj = self.manager('widget').get(panel.title) + if obj is None: + pan = panel(self) + self.manager('widget').add(obj=pan, name=panel.title) + self.auimgr.AddPane(pan, aui.AuiPaneInfo().Caption(panel.title).Left().Layer( 15 ).PinButton( True ) + .Float().Resizable().FloatingSize( wx.DefaultSize ).Dockable(True)) #.DestroyOnClose()) + else: + info = self.auimgr.GetPane(obj) + info.Show(True) + self.Layout() + self.auimgr.Update() + + def close_img(self, name=None): + names = self.get_img_name() if name is None else [name] + for name in names: + idx = self.canvasnb.GetPageIndex(self.get_img_win(name)) + self.remove_img(self.get_img_win(name).image) + self.remove_img_win(self.get_img_win(name)) + self.canvasnb.DeletePage(idx) + + def close_table(self, name=None): + names = self.get_tab_name() if name is None else [name] + for name in names: + idx = self.tablenb.GetPageIndex(self.get_tab_win(name)) + self.remove_tab(self.get_tab_win(name).table) + self.remove_tab_win(self.get_tab_win(name)) + self.tablenb.DeletePage(idx) + + def run_macros(self, cmd, callafter=None): + cmds = [i for i in cmd] + def one(cmds, after): + cmd = cmds.pop(0) + title, para = cmd.split('>') + print(title, para) + plg = Source.manager('plugin').get(name=title)() + after = lambda cmds=cmds: one(cmds, one) + if len(cmds)==0: after = callafter + wx.CallAfter(plg.start, self, eval(para), after) + one(cmds, None) + + def show(self, tag, cont, title): + tag = tag or 'img' + if tag=='img': + self.show_img([cont], title) + elif tag=='imgs': + self.show_img(cont, title) + elif tag=='tab': + self.show_table(cont, title) + elif tag=='mc': + self.run_macros(cont) + elif tag=='md': + self.show_md(cont, title) + elif tag=='wf': + self.show_workflow(cont, title) + else: self.alert('no view for %s!'%tag) + + def info(self, cont): + wx.CallAfter(self.txt_info.SetLabel, cont) + + def _alert(self, info, title='ImagePy'): + dialog=wx.MessageDialog(self, info, title, wx.OK) + dialog.ShowModal() == wx.ID_OK + dialog.Destroy() + + def alert(self, info, title='ImagePy'): + wx.CallAfter(self._alert, info, title) + + def yes_no(self, info, title='ImagePy'): + dialog = wx.MessageDialog(self, info, title, wx.YES_NO | wx.CANCEL) + rst = dialog.ShowModal() + dialog.Destroy() + dic = {wx.ID_YES:'yes', wx.ID_NO:'no', wx.ID_CANCEL:'cancel'} + return dic[rst] + + def getpath(self, title, filt, io, name=''): + filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in filt]) + dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} + dialog = wx.FileDialog(self, title, '', name, filt, dic[io]) + rst = dialog.ShowModal() + path = dialog.GetPath() if rst == wx.ID_OK else None + dialog.Destroy() + return path + + def show_para(self, title, view, para, on_handle=None, on_ok=None, on_cancel=None, preview=False, modal=True): + dialog = ParaDialog(self, title) + dialog.init_view(view, para, preview, modal=modal, app=self) + dialog.Bind('cancel', on_cancel) + dialog.Bind('parameter', on_handle) + dialog.Bind('commit', on_ok) + return dialog.show() + +if __name__ == '__main__': + import numpy as np + import pandas as pd + + app = wx.App(False) + frame = MiniApp(None) + frame.Show() + frame.show_img([np.zeros((512, 512), dtype=np.uint8)], 'zeros') + #frame.show_img(None) + frame.show_table(pd.DataFrame(np.arange(100).reshape((10,10))), 'title') + ''' + frame.show_md('abcdefg', 'md') + frame.show_md('ddddddd', 'md') + frame.show_txt('abcdefg', 'txt') + frame.show_txt('ddddddd', 'txt') + ''' + app.MainLoop() \ No newline at end of file diff --git a/sciwx/app/sciapp.py b/sciwx/app/sciapp.py deleted file mode 100644 index 92178f3d..00000000 --- a/sciwx/app/sciapp.py +++ /dev/null @@ -1,222 +0,0 @@ -import wx, os, sys -import time, threading - -import wx.lib.agw.aui as aui -from sciwx.widgets import MenuBar, ToolBar, ChoiceBook, ParaDialog -from sciwx.canvas import CanvasNoteBook -from sciwx.grid import GridNoteBook -from sciwx.text import MDNoteFrame, TextNoteFrame -from skimage.data import camera -from sciapp import App - -class SciApp(wx.Frame, App): - def __init__( self, parent ): - wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = 'SciApp', - size = wx.Size(-1,-1), pos = wx.DefaultPosition, - style = wx.RESIZE_BORDER|wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) - App.__init__(self) - self.auimgr = aui.AuiManager() - self.auimgr.SetManagedWindow( self ) - self.SetSizeHints( wx.Size(1024,768) ) - - self.init_menu() - self.init_tool() - self.init_canvas() - self.init_table() - self.init_widgets() - self.init_text() - self.init_status() - - self.Layout() - self.auimgr.Update() - self.Fit() - self.Centre( wx.BOTH ) - - self.Bind(wx.EVT_CLOSE, self.on_close) - self.Bind(aui.EVT_AUI_PANE_CLOSE, self.on_pan_close) - - def init_status(self): - self.stapanel = stapanel = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) - sizersta = wx.BoxSizer( wx.HORIZONTAL ) - self.txt_info = wx.StaticText( stapanel, wx.ID_ANY, "ImagePy v0.2", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.txt_info.Wrap( -1 ) - sizersta.Add( self.txt_info, 1, wx.ALIGN_BOTTOM|wx.BOTTOM|wx.LEFT|wx.RIGHT, 2 ) - self.pro_bar = wx.Gauge( stapanel, wx.ID_ANY, 100, wx.DefaultPosition, wx.Size( 100,15 ), wx.GA_HORIZONTAL ) - sizersta.Add( self.pro_bar, 0, wx.ALIGN_BOTTOM|wx.BOTTOM|wx.LEFT|wx.RIGHT, 2 ) - stapanel.SetSizer(sizersta) - self.auimgr.AddPane( stapanel, aui.AuiPaneInfo() .Bottom() .CaptionVisible( False ).PinButton( True ) - .PaneBorder( False ).Dock().Resizable().FloatingSize( wx.DefaultSize ).DockFixed( True ) - . MinSize(wx.Size(-1, 20)). MaxSize(wx.Size(-1, 20)).Layer( 10 ) ) - - def load_menu(self, data): - self.menubar.load(data) - - def load_tool(self, data, default=None): - for i, (name, tols) in enumerate(data[1]): - self.toolbar.add_tools(name, tols, i==0) - if not default is None: self.toolbar.add_pop('P', default) - self.toolbar.Layout() - - def load_widget(self, data): - self.widgets.load(data) - - def init_menu(self): - self.menubar = MenuBar(self) - - def init_tool(self): - sizer = wx.BoxSizer(wx.VERTICAL) - self.toolbar = ToolBar(self, True) - self.toolbar.Fit() - - self.auimgr.AddPane(self.toolbar, aui.AuiPaneInfo() .Left() .PinButton( True ) - .CaptionVisible( True ).Dock().Resizable().FloatingSize( wx.DefaultSize ).MaxSize(wx.Size( 32,-1 )) - . BottomDockable( True ).TopDockable( False ).Layer( 10 ) ) - - def init_canvas(self): - self.canvasnbwrap = wx.Panel(self) - sizer = wx.BoxSizer( wx.VERTICAL ) - self.canvasnb = CanvasNoteBook( self.canvasnbwrap) - sizer.Add( self.canvasnb, 1, wx.EXPAND |wx.ALL, 0 ) - self.canvasnbwrap.SetSizer( sizer ) - self.canvasnbwrap.Layout() - self.auimgr.AddPane( self.canvasnbwrap, aui.AuiPaneInfo() .Center() .CaptionVisible( False ).PinButton( True ).Dock() - .PaneBorder( False ).Resizable().FloatingSize( wx.DefaultSize ). BottomDockable( True ).TopDockable( False ) - .LeftDockable( True ).RightDockable( True ) ) - self.canvasnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_new_img) - self.canvasnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close_img) - - def init_table(self): - self.tablenbwrap = wx.Panel(self) - sizer = wx.BoxSizer( wx.VERTICAL ) - self.tablenb = GridNoteBook( self.tablenbwrap) - sizer.Add( self.tablenb, 1, wx.EXPAND |wx.ALL, 0 ) - self.tablenbwrap.SetSizer( sizer ) - self.tablenbwrap.Layout() - - self.auimgr.AddPane( self.tablenbwrap, aui.AuiPaneInfo() .Bottom() .CaptionVisible( True ).PinButton( True ).Dock().Hide() - .MaximizeButton( True ).Resizable().FloatingSize((800, 600)).BestSize(( 120,120 )). Caption('Tables') . - BottomDockable( True ).TopDockable( False ).LeftDockable( True ).RightDockable( True ) ) - - def init_widgets(self): - self.widgets = ChoiceBook(self) - self.auimgr.AddPane( self.widgets, aui.AuiPaneInfo() .Right().Caption('Widgets') .PinButton( True ) - .Dock().Resizable().FloatingSize( wx.DefaultSize ).MinSize( wx.Size( 266,-1 ) ).Layer( 10 ) ) - - def init_text(self): - self.mdframe = MDNoteFrame(self, 'Sci Document') - self.txtframe = TextNoteFrame(self, 'Sci Text') - - def on_pan_close(self, event): - if event.GetPane().window in [self.toolbar, self.widgets]: - event.Veto() - if hasattr(event.GetPane().window, 'close'): - event.GetPane().window.close() - - def on_new_img(self, event): - self.add_img_win(self.canvasnb.canvas()) - self.add_img(self.canvasnb.canvas().image) - - def on_close_img(self, event): - canvas = event.GetEventObject().GetPage(event.GetSelection()) - self.remove_img_win(canvas) - self.remove_img(canvas.image) - - def info(self, value): - wx.CallAfter(self.txt_info.SetLabel, value) - - def set_progress(self, value): - v = max(min(value, 100), 0) - self.pro_bar.SetValue(v) - if value==-1: - self.pro_bar.Hide() - elif not self.pro_bar.IsShown(): - self.pro_bar.Show() - self.stapanel.GetSizer().Layout() - self.pro_bar.Update() - - def on_close(self, event): - print('close') - #ConfigManager.write() - self.auimgr.UnInit() - del self.auimgr - self.Destroy() - sys.exit() - - def show_img(self, img): - canvas = self.canvasnb.add_canvas() - canvas.set_img(img) - self.add_img_win(canvas) - self.add_img(canvas.image) - - def show_tab(self, tab): - import pandas as pd - grid = self.tablenb.add_grid() - grid.set_data(pd.DataFrame([[1,1],[2,2]])) - info = self.auimgr.GetPane(self.tablenbwrap) - info.Show(True) - self.auimgr.Update() - - def show_md(self, cont, title='Document'): - page = self.mdframe.add_page() - page.set_cont(cont) - self.mdframe.Show() - - def show_txt(self, cont, title='Text'): - page = self.txtframe.add_notepad() - page.append(cont) - self.txtframe.Show() - - def alert(self, info, title='SciWx'): - dialog=wx.MessageDialog(self, info, title, wx.OK) - dialog.ShowModal() == wx.ID_OK - dialog.Destroy() - - def getpath(self, title, filt, io, name=''): - filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in filt]) - dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} - dialog = wx.FileDialog(self, title, '', name, filt, dic[io]) - rst = dialog.ShowModal() - path = dialog.GetPath() if rst == wx.ID_OK else None - dialog.Destroy() - return path - - def show_para(self, title, view, para, on_handle, on_ok=None, on_cancel=None, preview=False, modal=True): - dialog = ParaDialog(self, title) - dialog.init_view(view, para, preview, modal=True) - dialog.Bind('cancel', on_cancel) - dialog.Bind('parameter', on_handle) - return dialog.show() - -class P: - def __init__(self, name): - self.name = name - - def start(self): - print(self.name) - - def __call__(self): - return self - -data = ('menu', [ - ('File', [('Open', P('O')), - '-', - ('Close', P('C'))]), - ('Edit', [('Copy', P('C')), - ('A', [('B', P('B')), - ('C', P('C'))]), - ('Paste', P('P'))])]) - -if __name__ == '__main__': - app = wx.App(False) - frame = SciApp(None) - frame.Show() - frame.show_img(None) - frame.show_img(None) - frame.show_tab(None) - ''' - frame.show_md('abcdefg', 'md') - frame.show_md('ddddddd', 'md') - frame.show_txt('abcdefg', 'txt') - frame.show_txt('ddddddd', 'txt') - ''' - app.MainLoop() diff --git a/sciwx/demo/dialog_demo.py b/sciwx/demo/dialog_demo.py index bb5ce9b4..d241bfdb 100644 --- a/sciwx/demo/dialog_demo.py +++ b/sciwx/demo/dialog_demo.py @@ -16,7 +16,8 @@ ('color', 'c', 'which', 'you like')] app = wx.App() - pd = ParaDialog(None, 'Test') + dic = {'height':'高度', 'm':'米'} + pd = ParaDialog(None, 'Test', dic) pd.init_view(view, para, preview=True, modal=False) pd.pack() pd.ShowModal() diff --git a/sciwx/widgets/menubar.py b/sciwx/widgets/menubar.py index 037a234b..8585132e 100644 --- a/sciwx/widgets/menubar.py +++ b/sciwx/widgets/menubar.py @@ -1,9 +1,12 @@ import wx +def translate(v, dic): return dic[v] if v in dic else v + class MenuBar(wx.MenuBar): - def __init__(self, app): + def __init__(self, app, dic={}): wx.MenuBar.__init__(self) self.app = app + self.dic = dic app.SetMenuBar(self) def parse(self, ks, vs, pt): @@ -12,9 +15,9 @@ def parse(self, ks, vs, pt): for kv in vs: if kv == '-': menu.AppendSeparator() else: self.parse(*kv, menu) - pt.Append(1, ks, menu) + pt.Append(1, translate(ks, self.dic), menu) else: - item = wx.MenuItem(pt, -1, ks) + item = wx.MenuItem(pt, -1, translate(ks, self.dic)) f = lambda e, p=vs: p().start(self.app) self.Bind(wx.EVT_MENU, f, item) pt.Append(item) @@ -25,8 +28,7 @@ def Append(self, id, item, menu): def load(self, data): for k,v in data[1]: self.parse(k, v, self) - def on_menu(self, event): - print('here') + def on_menu(self, event): print('here') def clear(self): while self.GetMenuCount()>0: self.Remove(0) diff --git a/sciwx/widgets/paradialog.py b/sciwx/widgets/paradialog.py index 10f6a4e1..fdd842f5 100644 --- a/sciwx/widgets/paradialog.py +++ b/sciwx/widgets/paradialog.py @@ -12,11 +12,15 @@ def add_widget(key, value): widgets[key] = value +def translate(v, dic): + if isinstance(v, str): return dic[v] if v in dic else v + return [dic[i] if isinstance(i, str) and i in dic else i for i in v] + class ParaDialog (wx.Dialog): - def __init__( self, parent, title): - wx.Dialog.__init__ (self, parent, -1, title, style = wx.DEFAULT_DIALOG_STYLE) + def __init__( self, parent, title, dic={}): + wx.Dialog.__init__ (self, parent, -1, translate(title, dic), style = wx.DEFAULT_DIALOG_STYLE) self.lst = wx.BoxSizer( wx.VERTICAL ) - self.tus = [] + self.tus, self.dic = [], dic self.on_ok = self.on_cancel = self.on_help = None self.handle = print self.ctrl_dic = {} @@ -47,10 +51,10 @@ def add_confirm(self, modal): self.btn_cancel.Bind( wx.EVT_BUTTON, lambda e:self.commit('cancel')) #self.lst.Add() - def init_view(self, items, para, preview=False, modal=True, app=None): + def init_view(self, items, para, preview=False, modal=True, app=None, dic={}): self.para, self.modal = para, modal for item in items: - self.add_ctrl_(widgets[item[0]], item[1], item[2:], app=app) + self.add_ctrl_(widgets[item[0]], item[1], translate(item[2:], self.dic), app=app) if preview:self.add_ctrl_(Check, 'preview', ('preview',), app=app) self.reset(para) self.add_confirm(modal) @@ -123,6 +127,7 @@ def Bind(self, tag, f): if tag == 'parameter': self.handle = f if not f is None else print if tag == 'commit': self.on_ok = f if tag == 'cancel': self.on_cancel = f + if tag == 'help': self.on_help = f def show(self): if self.modal: diff --git a/sciwx/widgets/propdialog.py b/sciwx/widgets/propdialog.py new file mode 100644 index 00000000..471e5a9f --- /dev/null +++ b/sciwx/widgets/propdialog.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python + +import sys, time, math, os.path + +import wx, wx.adv +import wx.propgrid as wxpg + +from six import exec_ +_ = wx.GetTranslation + +class GridDialog( wx.Dialog ): + + def __init__( self, parent, title): + wx.Dialog.__init__ (self, parent, -1, title, style = wx.DEFAULT_DIALOG_STYLE, size = wx.Size((300, 480))) + # wx.Panel.__init__(self, parent, wx.ID_ANY) + + def commit(self, state): + self.Destroy() + if state=='ok' and self.on_ok:self.on_ok() + if state=='cancel' and self.on_cancel:self.on_cancel() + + def add_confirm(self, modal): + rowsizer = wx.BoxSizer(wx.HORIZONTAL) + but = wx.Button(self, wx.ID_OK, "OK") + rowsizer.Add(but,1) + #but.Bind( wx.EVT_BUTTON, self.OnGetPropertyValues ) + but = wx.Button(self, wx.ID_CANCEL,"Cancel") + rowsizer.Add(but,1) + if not modal: + self.btn_ok.Bind( wx.EVT_BUTTON, lambda e:self.commit('ok')) + self.btn_cancel.Bind( wx.EVT_BUTTON, lambda e:self.commit('cancel')) + self.GetSizer().Add(rowsizer,0,wx.EXPAND) + + def init_view(self, items, para, preview=False, modal=True, app=None): + self.para, self.modal = para, modal + self.pg = pg = wxpg.PropertyGridManager(self, style=wxpg.PG_SPLITTER_AUTO_CENTER) + + sizer = wx.BoxSizer(wx.VERTICAL) + # Show help as tooltips + pg.SetExtraStyle(wxpg.PG_EX_HELP_AS_TOOLTIPS) + pg.Bind( wxpg.EVT_PG_CHANGED, self.OnPropGridChange ) + pg.Bind( wxpg.EVT_PG_SELECTED, self.OnPropGridSelect ) + + pg.AddPage('') + for i in items: + if i[0] == 'lab': pg.Append(wxpg.PropertyCategory(i[2])) + if i[0] == int: pg.Append( wxpg.IntProperty(i[1], value=int(para[i[1]]) or 0)) + if i[0] == float: pg.Append( wxpg.FloatProperty(i[1], value=float(para[i[1]]) or 0)) + if i[0] == str: pg.Append( wxpg.StringProperty(i[1], value=para[i[1]] or '')) + if i[0] == 'txt': pg.Append( wxpg.LongStringProperty(i[1], value=para[i[1]] or '')) + if i[0] == bool: pg.Append( wxpg.BoolProperty(i[1])) + if i[0] == 'date': pg.Append( wxpg.DateProperty(i[1], value=wx.DateTime.Now())) + #if i[0] == 'list': pg.Append( wxpg.EnumProperty(i[1], i[1], [i.strip() for i in i[2][1:-1].split(',')])) + #if i[0] == 'img': pg.Append( wxpg.EnumProperty(v[1], v[1], ['a','b','c'])) + #if i[0] == 'tab': pg.Append( wxpg.EnumProperty(v[1], v[1], ['a','b','c'])) + + #if preview:self.add_ctrl_(Check, 'preview', ('preview',), app=app) + #self.reset(para) + sizer.Add(pg, 1, wx.EXPAND) + self.SetSizer(sizer) + self.add_confirm(modal) + self.Layout() + self.Fit() + wx.Dialog.Bind(self, wx.EVT_WINDOW_DESTROY, self.OnDestroy) + #wx.Dialog.Bind(self, wx.EVT_IDLE, lambda e: self.reset()) + print('bind close') + + def OnDestroy( self, event ): + self.handle = print + self.on_cancel = self.on_ok = self.on_help = None + del self.ctrl_dic + + def GetValue(self): + return self.pg.GetPropertyValues(as_strings=True) + + def OnGetPropertyValues(self,event): + para = self.pg.GetPropertyValues(inc_attributes=True) + + def OnPropGridChange(self, event): + p = event.GetProperty() + if p: print('%s changed to "%s"\n' % (p.GetName(),p.GetValueAsString())) + + def OnPropGridSelect(self, event): + p = event.GetProperty() + if p: self.txt_info.SetValue('%s: %s'%(p.GetName(), self.key[p.GetName()][3])) + +if __name__ == '__main__': + app = wx.App(False) + + para = {'sid':'001', 'name':'yxl', 'Date':None, 'age':5} + view = [('lab', None, 'Label1'), + (str, 'sid', 'Sample_ID'), + (str, 'name', 'Operator_Name'), + ('lab', None, 'Label2'), + (int, 'age', 'Your Age')] + + frame = GridDialog(None, 'Property Grid') + frame.init_view(view, para) + rst = frame.ShowModal() == wx.ID_OK + app.MainLoop() \ No newline at end of file diff --git a/sciwx/widgets/toolbar.py b/sciwx/widgets/toolbar.py index 0fe9280f..776e3f49 100644 --- a/sciwx/widgets/toolbar.py +++ b/sciwx/widgets/toolbar.py @@ -38,7 +38,7 @@ def __init__(self, parent, vertical=False): def on_tool(self, evt, tol): tol.start(self.app) - evt.Skip() + # evt.Skip() btn = evt.GetEventObject() #print(self.GetBackgroundColour()) #print(btn.GetClassDefaultAttributes().colFg) @@ -54,17 +54,18 @@ def on_config(self, evt, tol): def on_help(self, evt, tol): pass - def on_info(self, event, tol): pass + def on_info(self, event, tol): + self.app.set_info('%s Tool'%tol.title) def bind(self, btn, tol): obj = tol() btn.SetBackgroundColour(self.GetBackgroundColour()) btn.Bind( wx.EVT_LEFT_DOWN, lambda e, obj=obj: self.on_tool(e, obj)) - #btn.Bind( wx.EVT_RIGHT_DOWN, lambda x, p=data[0]: IPy.show_md(p.title, DocumentManager.get(p.title))) - btn.Bind( wx.EVT_ENTER_WINDOW, lambda x, p='%s Tool'%obj.title: self.app.set_info(p)) + btn.Bind( wx.EVT_RIGHT_DOWN, lambda e, obj=obj: self.on_help(e, obj)) + btn.Bind( wx.EVT_ENTER_WINDOW, lambda e, obj=obj: self.on_info(e, obj)) #if not isinstance(data[0], Macros) and issubclass(data[0], Tool): btn.Bind(wx.EVT_LEFT_DCLICK, lambda e, obj=obj: self.on_config(e, obj)) - + def clear(self): del self.toolset[:] self.GetSizer().Clear() From 445b693aef80e17d09f0a010227d5659ed915b75 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Mon, 22 Jun 2020 09:54:49 +0800 Subject: [PATCH 256/343] file menu translate to chinese --- imagepy/lang/Chinese/plugins/file.dic | 100 +++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/imagepy/lang/Chinese/plugins/file.dic b/imagepy/lang/Chinese/plugins/file.dic index 52e6ffec..f8688699 100644 --- a/imagepy/lang/Chinese/plugins/file.dic +++ b/imagepy/lang/Chinese/plugins/file.dic @@ -10,4 +10,102 @@ Open::打开图像 Open Url::打开链接 Input the URL, eg. http://data.imagepy.org/testdata/yxdragon.jpg::输入链接, 例如 http://data.imagepy.org/testdata/yxdragon.jpg - Url::链接 \ No newline at end of file + Url::链接 + +Open Recent::打开最近 + +Samples Local::本地图像样例 + +Samples Online::在线图像样例 + +Samples ImageJ::ImageJ图像样例 + +Import::导入 + +Open Raw::打开Raw文件 + +Import Rois from IJ::导入ImageJ感兴趣区域 + +Import Sequence::导入图像序列 + +Export::导出 + +Save Sequence::导出图像序列 + +BMP::BMP图像 + +BMP Open::打开BMP图像 + +BMP Save::保存BMP图像 + +JPG::JPG图像 + +JPG Open::打开JPG图像 + +JPG Save::保存JPG图像 + +PNG::PNG图像 + +PNG Open::打开PNG图像 + +PNG Save::保存PNG图像 + +TIF::TIF图像 + +TIF Open::打开TIF图像 + +TIF Save::保存TIF图像 + +TIF 3D Open::打开3D TIF图像 + +TIF 3D Save::保存3D TIF图像 + +GIF::GIF图像 + +GIF Open::打开GIF图像 + +GIF Save::保存GIF图像 + +GIF Animate Open::打开GIF动画 + +GIF Animate Save::保存GIF动画 + +DICOM::DICOM图像 + +DCM Open::打开DICOM图像 + +DAT::DAT图像 + +DAT Open::打开DAT图像 + +DAT Save::保存DAT图像 + +Numpy::Numpy文件 + +Numpy Open::打开Numpy文件 + +Numpy Save::保存Numpy文件 + +Numpy 3D Open::打开3D Numpy文件 + +Numpy 3D Save::保存3D Numpy文件 + +MAT::MAT图像 + +Mat Open::打开Mat图像 + +Mat Save::保存Mat图像 + +Mat 3D Open::打开3D Mat图像 + +Mat 3D Save::保存3D Mat图像 + +MarkDown::MarkDown文件 + +MarkDown Open::打开MarkDown文件 + +Save::保存图像 + +Save With Mark::保存图像及标记 + +Exit::退出 \ No newline at end of file From d8711761487ebadeb22ea23e22d60ccec65868dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BE=AF=E5=B1=95=E6=84=8F?= <1295752786@qq.com> Date: Mon, 22 Jun 2020 12:28:35 +0800 Subject: [PATCH 257/343] added translation for menu \'Window\' and 'Help' --- .gitignore | 1 + ImagePy.bat | 0 imagepy/__main__.py | 2 +- imagepy/data/config.json | 2 +- imagepy/lang/Chinese/plugins/help.dic | 9 + imagepy/lang/Chinese/plugins/window.dic | 35 +++ imagepy/tools/Draw/Route_tol.py | 366 ++++++++++++------------ requirements.txt | 2 +- trans.nov | 197 +++++++++++++ 9 files changed, 428 insertions(+), 186 deletions(-) mode change 100755 => 100644 ImagePy.bat create mode 100644 imagepy/lang/Chinese/plugins/help.dic create mode 100644 imagepy/lang/Chinese/plugins/window.dic create mode 100644 trans.nov diff --git a/.gitignore b/.gitignore index 0cf2d407..5e37d11a 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ imagepy/plugins/ # Distribution / packaging .Python env/ +.metadata/ build/ develop-eggs/ dist/ diff --git a/ImagePy.bat b/ImagePy.bat old mode 100755 new mode 100644 diff --git a/imagepy/__main__.py b/imagepy/__main__.py index c26a2c29..01158801 100644 --- a/imagepy/__main__.py +++ b/imagepy/__main__.py @@ -1,4 +1,4 @@ import sys -sys.path.append('../') +sys.path.append(r'C:\Users\12957\Desktop\imagepy\totrans\imagepy') from core.app import startup startup.start() \ No newline at end of file diff --git a/imagepy/data/config.json b/imagepy/data/config.json index 88c3b2f4..55d50fb4 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["language", "Chinese", null], ["uistyle", "imagepy", null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["recent", ["C:/Users/54631/Desktop/Workflow Coins Demo.wf", "C:/Users/54631/Documents/projects/imagepy/imagepy/tools/Stack/Add Slice.mc", "C:/Users/54631/Documents/projects/imagepy/imagepy/menus/Plugins/Contribute/Contributions/CellPose.md", "C:/Users/54631/Documents/projects/imagepy/imagepy/plugins/demoplugin/menus/Demos/Workflow Demo/Workflow Coins Demo.wf"], null]] \ No newline at end of file +[["recent", ["C:\\Users\\12957\\Pictures\\computer-museum[1].png", "C:/Users/54631/Desktop/Workflow Coins Demo.wf", "C:/Users/54631/Documents/projects/imagepy/imagepy/tools/Stack/Add Slice.mc", "C:/Users/54631/Documents/projects/imagepy/imagepy/menus/Plugins/Contribute/Contributions/CellPose.md"], null], ["language", "Chinese", null], ["uistyle", "imagepy", null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null]] \ No newline at end of file diff --git a/imagepy/lang/Chinese/plugins/help.dic b/imagepy/lang/Chinese/plugins/help.dic new file mode 100644 index 00000000..1495f53b --- /dev/null +++ b/imagepy/lang/Chinese/plugins/help.dic @@ -0,0 +1,9 @@ +Topic::主题 + +About::关于 + +Home Page::主页 + +Language::语言 + +Chinese::中文 \ No newline at end of file diff --git a/imagepy/lang/Chinese/plugins/window.dic b/imagepy/lang/Chinese/plugins/window.dic new file mode 100644 index 00000000..6f9a610d --- /dev/null +++ b/imagepy/lang/Chinese/plugins/window.dic @@ -0,0 +1,35 @@ +Windows Style::窗口样式 + Shown in ImagePy style when next setup!::ImagePy布局将在下次启动时展示! + +Pay Tribute To ImageJ::ImageJ布局风格 + +Elegant ImagePy Style::ImagePy布局风格 + +Kill Image::关闭图像 + name::图像名称 + close all images::关闭所有图像 + OK::确认 + cancel::取消 + Help::帮助 + +Kill Table::关闭表格 + name::表格名称 + close all tables::关闭所有表格 + +Widgets::控件列表 + +Toolbar::工具栏 + +Tables Window::表格窗口 + +Macros Recorder::宏记录器 + +Command Line::命令行 + +Plugin List View::插件列表 + +Plugin Tree View::插件树形图 + +Tool Tree View::工具树形图 + +Develop Tool Sute::开发工具集 \ No newline at end of file diff --git a/imagepy/tools/Draw/Route_tol.py b/imagepy/tools/Draw/Route_tol.py index a76eceaa..0a9ca455 100644 --- a/imagepy/tools/Draw/Route_tol.py +++ b/imagepy/tools/Draw/Route_tol.py @@ -1,184 +1,184 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Oct 19 17:35:09 2016 - -@author: yxl -""" -from imagepy.core.engine import Filter -from sciapp.action import ImageTool -import numpy as np -from sciapp.util import mark2shp -from sciapp.action import ImageTool -#from imagepy.core.manager import ColorManager -#from imagepy.core.roi import lineroi -#from imagepy.core.mark import GeometryMark -from skimage.graph import route_through_array -import scipy.ndimage as ndimg - -def route_through(ips, snap, poins,para): - para['max']=True - para['lcost']=2 - img = snap.astype('float32') - if para['max']: img *= -1 - np.add(img, para['lcost']-img.min(), casting='unsafe', out=img) - minv, maxv = ips.range - routes = [] - for line in poins: - pts = np.array(list(zip(line[:-1], line[1:]))) - for p0, p1 in pts[:,:,::-1].astype(int): - indices, weight = route_through_array(img, p0, p1) - routes.append(indices) - return routes - -class Plugin(ImageTool): - title = 'Route Toolk' - note = ['auto_snap','8-bit', '16-bit','int', 'float','2int', 'preview'] - para = {'fully connected':True, 'lcost':0, 'max':False, 'geometric':True, 'type':'ROI'} - view = [(float, 'lcost', (0, 1e5), 3, 'step', 'cost'), - (bool, 'max', 'max cost'), - (bool, 'fully connected', 'fully connected'), - (bool, 'geometric', 'geometric'), - (list, 'type', ['white line', 'ROI'], str, 'output', '')] - - def __init__(self): - self.curobj = None - self.doing = 'Nothing' - #初始化线条缓存 - # self.helper = AnchorLine() - self.odx,self.ody = 0, 0 - self.cursor = 'cross' - self.buf=[] - - def mouse_down(self, ips, x, y, btn, **key): - # print(key['canvas'].scale) - lim = 5.0/key['canvas'].scale - if btn==2: - if key['ctrl']: - if len(self.buf)>1: - lines=route_through(ips, ips.img,[self.buf],self.para) - rs, cs = np.vstack(lines).T - minv, maxv = ips.range - if self.para['type']=='white line': - # ips.img[:,:] = minv - ips.img[rs,cs] = maxv - elif self.para['type']=='ROI': - ips.img[:,:] = minv - ips.img[rs,cs] = maxv - ndimg.binary_fill_holes(ips.img.copy(), output=ips.img) - ips.img[:,:] *= 255 - self.buf=[] - self.draw(ips) - return - else: - self.oldp = key['canvas'].to_panel_coor(x,y) - self.do_old=self.doing - self.doing = 'all_move' - return - #左键按下 - elif btn==1: - if self.doing=='Nothing': - if len(self.buf) == 0: - self.doing ='draw' - elif self.doing=='draw' and self.cursor=='hand': - a=self.in_points(x,y,lim) - if a!=(-1,-1): - # print('doing to move') - self.doing='move' - #记录第几个点被改变 - self.p_index=self.buf.index(a) - elif self.doing=='down': - #判断是否在点内,如果在,说明在移动 - a=self.in_points(x,y,lim) - if a!=(-1,-1): - self.doing='move' - self.p_index=self.buf.index(a) - return - self.buf=[] - self.doing='Nothing' - self.draw(ips) - return - if self.doing=='draw' and self.cursor=='cross': - self.addpoint((x,y)) - self.draw(ips) - elif btn == 3: - - if (self.doing=='draw' or self.doing=='down')and key['ctrl']: - a=self.in_points(x,y,lim) - if a!=(-1,-1):self.delete(self.buf.index(a)) - self.draw(ips) - elif self.doing=='draw': - self.addpoint((x,y)) - self.addpoint(self.buf[0]) - self.draw(ips) - self.doing='down' - - def mouse_up(self, ips, x, y, btn, **key): - if self.doing == 'move' and btn == 1: - self.doing = 'draw' - elif self.doing == 'all_move' and btn ==2: - self.doing = self.do_old - - def mouse_move(self, ips, x, y, btn, **key): - if len(self.buf)==0:return - lim = 5.0/key['canvas'].scale - #如果鼠标移动的同时没有按下 - if btn==1: - #如果在鼠标是手的时候左键按下,而且在移动,说明在修改点的位置 - if self.doing=='move': - self.change(self.p_index,(x,y)) - self.draw(ips) - elif btn==None: - #鼠标变成一个十字架 - self.cursor = 'cross' - a=self.in_points(x,y,lim) - if a!=(-1,-1): - self.cursor = 'hand' - if self.doing == 'all_move': - x,y = key['canvas'].to_panel_coor(x,y) - key['canvas'].move(x-self.oldp[0], y-self.oldp[1]) - self.oldp = x, y - ips.update() - # print('move') - self.odx, self.ody = x, y - - def in_points(self,x,y,range): - for i in self.buf: - if ((i[0]>x-range) and (i[0]y-range) and (i[1]1: - lines=route_through(ips, ips.img,[self.buf],self.para) - lst=[] - for line in lines:lst.append([(j,i) for i,j in line]) - layer['body'].append({'type':'lines', 'body':lst}) - mark['body'][0] = layer - ips.mark = mark2shp(mark) - ips.update() - - def mouse_wheel(self, ips, x, y, d, **key): - if d>0:key['canvas'].zoomout(x, y, 'data') - if d<0:key['canvas'].zoomin(x, y, 'data') +# -*- coding: utf-8 -*- +""" +Created on Wed Oct 19 17:35:09 2016 + +@author: yxl +""" +from imagepy.core.engine import Filter +from sciapp.action import ImageTool +import numpy as np +from sciapp.util import mark2shp +from sciapp.action import ImageTool +#from imagepy.core.manager import ColorManager +#from imagepy.core.roi import lineroi +#from imagepy.core.mark import GeometryMark +from skimage.graph import route_through_array +import scipy.ndimage as ndimg + +def route_through(ips, snap, poins,para): + para['max']=True + para['lcost']=2 + img = snap.astype('float32') + if para['max']: img *= -1 + np.add(img, para['lcost']-img.min(), casting='unsafe', out=img) + minv, maxv = ips.range + routes = [] + for line in poins: + pts = np.array(list(zip(line[:-1], line[1:]))) + for p0, p1 in pts[:,:,::-1].astype(int): + indices, weight = route_through_array(img, p0, p1) + routes.append(indices) + return routes + +class Plugin(ImageTool): + title = 'Route Toolk' + note = ['auto_snap','8-bit', '16-bit','int', 'float','2int', 'preview'] + para = {'fully connected':True, 'lcost':0, 'max':False, 'geometric':True, 'type':'ROI'} + view = [(float, 'lcost', (0, 1e5), 3, 'step', 'cost'), + (bool, 'max', 'max cost'), + (bool, 'fully connected', 'fully connected'), + (bool, 'geometric', 'geometric'), + (list, 'type', ['white line', 'ROI'], str, 'output', '')] + + def __init__(self): + self.curobj = None + self.doing = 'Nothing' + #初始化线条缓存 + # self.helper = AnchorLine() + self.odx,self.ody = 0, 0 + self.cursor = 'cross' + self.buf=[] + + def mouse_down(self, ips, x, y, btn, **key): + # print(key['canvas'].scale) + lim = 5.0/key['canvas'].scale + if btn==2: + if key['ctrl']: + if len(self.buf)>1: + lines=route_through(ips, ips.img,[self.buf],self.para) + rs, cs = np.vstack(lines).T + minv, maxv = ips.range + if self.para['type']=='white line': + # ips.img[:,:] = minv + ips.img[rs,cs] = maxv + elif self.para['type']=='ROI': + ips.img[:,:] = minv + ips.img[rs,cs] = maxv + ndimg.binary_fill_holes(ips.img.copy(), output=ips.img) + ips.img[:,:] *= 255 + self.buf=[] + self.draw(ips) + return + else: + self.oldp = key['canvas'].to_panel_coor(x,y) + self.do_old=self.doing + self.doing = 'all_move' + return + #左键按下 + elif btn==1: + if self.doing=='Nothing': + if len(self.buf) == 0: + self.doing ='draw' + elif self.doing=='draw' and self.cursor=='hand': + a=self.in_points(x,y,lim) + if a!=(-1,-1): + # print('doing to move') + self.doing='move' + #记录第几个点被改变 + self.p_index=self.buf.index(a) + elif self.doing=='down': + #判断是否在点内,如果在,说明在移动 + a=self.in_points(x,y,lim) + if a!=(-1,-1): + self.doing='move' + self.p_index=self.buf.index(a) + return + self.buf=[] + self.doing='Nothing' + self.draw(ips) + return + if self.doing=='draw' and self.cursor=='cross': + self.addpoint((x,y)) + self.draw(ips) + elif btn == 3: + + if (self.doing=='draw' or self.doing=='down')and key['ctrl']: + a=self.in_points(x,y,lim) + if a!=(-1,-1):self.delete(self.buf.index(a)) + self.draw(ips) + elif self.doing=='draw': + self.addpoint((x,y)) + self.addpoint(self.buf[0]) + self.draw(ips) + self.doing='down' + + def mouse_up(self, ips, x, y, btn, **key): + if self.doing == 'move' and btn == 1: + self.doing = 'draw' + elif self.doing == 'all_move' and btn ==2: + self.doing = self.do_old + + def mouse_move(self, ips, x, y, btn, **key): + if len(self.buf)==0:return + lim = 5.0/key['canvas'].scale + #如果鼠标移动的同时没有按下 + if btn==1: + #如果在鼠标是手的时候左键按下,而且在移动,说明在修改点的位置 + if self.doing=='move': + self.change(self.p_index,(x,y)) + self.draw(ips) + elif btn==None: + #鼠标变成一个十字架 + self.cursor = 'cross' + a=self.in_points(x,y,lim) + if a!=(-1,-1): + self.cursor = 'hand' + if self.doing == 'all_move': + x,y = key['canvas'].to_panel_coor(x,y) + key['canvas'].move(x-self.oldp[0], y-self.oldp[1]) + self.oldp = x, y + ips.update() + # print('move') + self.odx, self.ody = x, y + + def in_points(self,x,y,range): + for i in self.buf: + if ((i[0]>x-range) and (i[0]y-range) and (i[1]1: + lines=route_through(ips, ips.img,[self.buf],self.para) + lst=[] + for line in lines:lst.append([(j,i) for i,j in line]) + layer['body'].append({'type':'lines', 'body':lst}) + mark['body'][0] = layer + ips.mark = mark2shp(mark) + ips.update() + + def mouse_wheel(self, ips, x, y, d, **key): + if d>0:key['canvas'].zoomout(x, y, 'data') + if d<0:key['canvas'].zoomin(x, y, 'data') ips.update() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 88143f67..b385485d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ numpy-stl openpyxl pandas pydicom -pystackreg + pypubsub read-roi scikit-image diff --git a/trans.nov b/trans.nov new file mode 100644 index 00000000..cf2fb244 --- /dev/null +++ b/trans.nov @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + noval.python.project.viewer.PythonProjectTemplate + + + From f8890c04b1e971fa98b10c7e06f5da7e7dbe1150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BE=AF=E5=B1=95=E6=84=8F?= <1295752786@qq.com> Date: Mon, 22 Jun 2020 12:35:17 +0800 Subject: [PATCH 258/343] added translation for menu 'Window' and 'Help' --- .gitignore | 1 + imagepy/__main__.py | 2 +- trans.nov | 197 -------------------------------------------- 3 files changed, 2 insertions(+), 198 deletions(-) delete mode 100644 trans.nov diff --git a/.gitignore b/.gitignore index 5e37d11a..ad1fb101 100644 --- a/.gitignore +++ b/.gitignore @@ -95,3 +95,4 @@ ENV/ # Pycharm project .idea/ +*.nov diff --git a/imagepy/__main__.py b/imagepy/__main__.py index 01158801..0189c45e 100644 --- a/imagepy/__main__.py +++ b/imagepy/__main__.py @@ -1,4 +1,4 @@ import sys -sys.path.append(r'C:\Users\12957\Desktop\imagepy\totrans\imagepy') +sys.path.append(r'../') from core.app import startup startup.start() \ No newline at end of file diff --git a/trans.nov b/trans.nov deleted file mode 100644 index cf2fb244..00000000 --- a/trans.nov +++ /dev/null @@ -1,197 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - noval.python.project.viewer.PythonProjectTemplate - - - From 8d6172ab4cbd5887ed5512bc5e80189e777d42a6 Mon Sep 17 00:00:00 2001 From: FengZhiheng <592440477@qq.com> Date: Mon, 22 Jun 2020 13:57:49 +0800 Subject: [PATCH 259/343] Create Edit.dic --- imagepy/lang/Chinese/plugins/Edit.dic | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 imagepy/lang/Chinese/plugins/Edit.dic diff --git a/imagepy/lang/Chinese/plugins/Edit.dic b/imagepy/lang/Chinese/plugins/Edit.dic new file mode 100644 index 00000000..64dccb2b --- /dev/null +++ b/imagepy/lang/Chinese/plugins/Edit.dic @@ -0,0 +1,15 @@ +Undo::撤回 + +Copy::拷贝 + +Paste::粘贴 + +Sketch::草图 + +Fill::填充 + +Invert::反转 + +Clear::清空(ROI内) + +Clear Out::清空(ROI外) \ No newline at end of file From e7093c809bbc5ffdb09648896dab0360fd309a0d Mon Sep 17 00:00:00 2001 From: FengZhiheng <592440477@qq.com> Date: Mon, 22 Jun 2020 14:35:10 +0800 Subject: [PATCH 260/343] Update Edit.dic --- imagepy/lang/Chinese/plugins/Edit.dic | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/imagepy/lang/Chinese/plugins/Edit.dic b/imagepy/lang/Chinese/plugins/Edit.dic index 64dccb2b..0288ab16 100644 --- a/imagepy/lang/Chinese/plugins/Edit.dic +++ b/imagepy/lang/Chinese/plugins/Edit.dic @@ -4,7 +4,9 @@ Copy::拷贝 Paste::粘贴 -Sketch::草图 +Sketch::描边 + width::宽度 + pix::像素 Fill::填充 From e8c0341e1a6d0334f2f6028839ff42dd0f2b7bde Mon Sep 17 00:00:00 2001 From: Mingda Lv Date: Mon, 22 Jun 2020 16:05:08 +0800 Subject: [PATCH 261/343] Menu-Plugins chinese --- imagepy/lang/Chinese/plugins/Plugins.dic | 82 ++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 imagepy/lang/Chinese/plugins/Plugins.dic diff --git a/imagepy/lang/Chinese/plugins/Plugins.dic b/imagepy/lang/Chinese/plugins/Plugins.dic new file mode 100644 index 00000000..a25d5f7e --- /dev/null +++ b/imagepy/lang/Chinese/plugins/Plugins.dic @@ -0,0 +1,82 @@ +New::新建 + +New Filter::新建Filter + +New Simple::新建Simple + +New Free::新建Free + +New Tool::新建Tool + +Macros::宏 + +Macros Recorder::录制宏 + +Run Macros::运行宏 + +Manager::管理 + +Plugins Manager::插件管理 + Search::搜索 + Name::名称 + Author::作者 + Version::版本 + Status::状态 + installed::已安装 + only installed::只显示已安装 + Install::安装 + Update::更新 + Remove::移除 + +Plugin Tree View::插件树浏览 + +Tool Tree View::工具树浏览 + +Plugin List View::插件列表 + +Shortcut Editor::编辑快捷键 + +Command Line::打开命令行 + +Install::安装 + +Install Packages::安装包 + +List Packages::包列表 + +Install Plugins::安装插件 + +List Plugins::插件列表 + +Contribute::参与改进 + +Contribute Document::提交文档 + +Contribute Plugin::提交插件 + +Plugins Manager::插件管理 + +Contributions:: + +Update Software::软件升级 + +Reload Plugins::重新载入插件 + +StackReg::StackReg(图像栈对齐) + +Stack Register::生成图像栈 + +Register By Mats::矩阵生成图像栈 + +StackReg License::许可文件 + +Games::游戏 + +Cross Stick::十字绣 + +Stroke Step::笔迹 + +Game Of Life::生命游戏 + +Screen Capture::截屏 + From a22d8de0f7c9f3ed77d33dd5c7b57b4941a8a682 Mon Sep 17 00:00:00 2001 From: Mingda Lv Date: Mon, 22 Jun 2020 16:07:44 +0800 Subject: [PATCH 262/343] Update Plugins.dic --- imagepy/lang/Chinese/plugins/Plugins.dic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/lang/Chinese/plugins/Plugins.dic b/imagepy/lang/Chinese/plugins/Plugins.dic index a25d5f7e..beb6468c 100644 --- a/imagepy/lang/Chinese/plugins/Plugins.dic +++ b/imagepy/lang/Chinese/plugins/Plugins.dic @@ -56,7 +56,7 @@ Contribute Plugin::提交插件 Plugins Manager::插件管理 -Contributions:: +Contributions::支持信息 Update Software::软件升级 From 4323a3c8c842dbe6209db47897ff4ac11feec92e Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 24 Jun 2020 20:58:41 +0800 Subject: [PATCH 263/343] chinese --- imagepy/core/app/imagepy.py | 82 +++++++++++++++++--- imagepy/core/app/loader.py | 12 ++- imagepy/core/app/source.py | 1 + imagepy/core/app/startup.py | 2 - imagepy/data/config.json | 2 +- imagepy/data/shortcut.json | 1 + imagepy/data/shotcut.cfg | 1 - imagepy/doc/Chinese/plugins/File/New.md | 19 +++++ imagepy/lang/Chinese/plugins/Plugins.dic | 29 ++++--- imagepy/lang/Chinese/plugins/common.dic | 7 ++ imagepy/lang/Chinese/plugins/file.dic | 2 +- imagepy/lang/Chinese/plugins/help.dic | 6 +- imagepy/lang/Chinese/plugins/menus.dic | 2 +- imagepy/lang/Chinese/plugins/selection.dic | 46 +++++++++++ imagepy/lang/Chinese/plugins/window.dic | 5 +- imagepy/lang/Chinese/tools/tools.dic | 11 +++ imagepy/lang/Chinese/widgets/widgets.dic | 18 +++++ imagepy/menus/File/new_plg.py | 2 +- imagepy/menus/Plugins/Manager/console_wgt.py | 4 +- imagepy/menus/Plugins/Manager/plglist_wgt.py | 4 +- imagepy/menus/Plugins/Manager/plgtree_wgt.py | 10 ++- imagepy/menus/Plugins/Manager/shotcut_wgt.py | 17 ++-- imagepy/menus/Plugins/Manager/toltree_wgt.py | 4 +- sciwx/widgets/menubar.py | 33 +++++--- sciwx/widgets/paradialog.py | 16 ++-- 25 files changed, 260 insertions(+), 76 deletions(-) create mode 100644 imagepy/data/shortcut.json delete mode 100644 imagepy/data/shotcut.cfg create mode 100644 imagepy/doc/Chinese/plugins/File/New.md create mode 100644 imagepy/lang/Chinese/plugins/common.dic create mode 100644 imagepy/lang/Chinese/plugins/selection.dic create mode 100644 imagepy/lang/Chinese/tools/tools.dic create mode 100644 imagepy/lang/Chinese/widgets/widgets.dic diff --git a/imagepy/core/app/imagepy.py b/imagepy/core/app/imagepy.py index eef26986..d9c4b06c 100644 --- a/imagepy/core/app/imagepy.py +++ b/imagepy/core/app/imagepy.py @@ -87,26 +87,41 @@ def _load_all(self): cont = '\n'.join(['%-30s\t%-20s\t%s'%i for i in err]) self.show_txt(cont, 'loading error log') - def load_all(self): - wx.CallAfter(self._load_all) + def load_all(self): wx.CallAfter(self._load_all) def load_menu(self, data): self.menubar.clear() lang = Source.manager('config').get('language') ls = Source.manager('dictionary').gets(tag=lang) - self.menubar.dic = dict([(i,j[i]) for i,j,_ in ls]) - self.menubar.load(data) + short = Source.manager('shortcut').gets() + acc = self.menubar.load(data, dict([i[:2] for i in short])) + self.translate(dict([(i,j[i]) for i,j,_ in ls]))(self.menubar) + self.SetAcceleratorTable(acc) def load_tool(self, data, default=None): self.toolbar.clear() + lang = Source.manager('config').get('language') + ls = Source.manager('dictionary').gets(tag=lang) + dic = dict([(i,j[i]) for i,j,_ in ls]) for i, (name, tols) in enumerate(data[1]): + name = dic[name] if name in dic else name self.toolbar.add_tools(name, tols, i==0) + default = dic[default] if default in dic else default if not default is None: self.toolbar.add_pop(os.path.join(root_dir, 'tools/drop.gif'), default) self.toolbar.Layout() def load_widget(self, data): self.widgets.clear() + lang = Source.manager('config').get('language') self.widgets.load(data) + for cbk in self.widgets.GetChildren(): + for i in range(cbk.GetPageCount()): + dic = Source.manager('dictionary').get(cbk.GetPageText(i), tag=lang) or {} + translate = self.translate(dic) + title = cbk.GetPageText(i) + cbk.SetPageText(i, dic[title] if title in dic else title) + self.translate(dic)(cbk.GetPage(i)) + # self.translate(self.widgets) def init_menu(self): self.menubar = MenuBar(self) @@ -162,9 +177,11 @@ def init_table(self): sizer.Add( self.tablenb, 1, wx.EXPAND |wx.ALL, 0 ) self.tablenbwrap.SetSizer( sizer ) self.tablenbwrap.Layout() - + lang = Source.manager('config').get('language') + dic = Source.manager('dictionary').get('common') or {} + title = dic['Table'] if 'Table' in dic else 'Table' self.auimgr.AddPane( self.tablenbwrap, aui.AuiPaneInfo() .Bottom() .CaptionVisible( True ).PinButton( True ).Dock().Hide() - .MaximizeButton( True ).Resizable().FloatingSize((800, 600)).BestSize(( 120,120 )). Caption('Tables') . + .MaximizeButton( True ).Resizable().FloatingSize((800, 600)).BestSize(( 120,120 )). Caption(title) . BottomDockable( True ).TopDockable( False ).LeftDockable( True ).RightDockable( True ) ) self.tablenb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_new_tab) self.tablenb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close_tab) @@ -196,8 +213,11 @@ def remove_task(self, task): self.pro_bar.SetValue(tasks) def init_widgets(self): + lang = Source.manager('config').get('language') + dic = Source.manager('dictionary').get('common') or {} + title = dic['Widgets'] if 'Widgets' in dic else 'Widgets' self.widgets = ChoiceBook(self) - self.auimgr.AddPane( self.widgets, aui.AuiPaneInfo() .Right().Caption('Widgets') .PinButton( True ) + self.auimgr.AddPane( self.widgets, aui.AuiPaneInfo() .Right().Caption(title) .PinButton( True ) .Dock().Resizable().FloatingSize( wx.DefaultSize ).MinSize( wx.Size( 266,-1 ) ).Layer( 10 ) ) def init_text(self): @@ -373,8 +393,12 @@ def show_widget(self, panel, title='Widgets'): obj = self.manager('widget').get(panel.title) if obj is None: pan = panel(self, self) + lang = Source.manager('config').get('language') + dic = Source.manager('dictionary').get(panel.title, tag=lang) + self.translate(dic)(pan) self.manager('widget').add(obj=pan, name=panel.title) - self.auimgr.AddPane(pan, aui.AuiPaneInfo().Caption(panel.title).Left().Layer( 15 ).PinButton( True ) + title = dic[panel.title] if panel.title in dic else panel.title + self.auimgr.AddPane(pan, aui.AuiPaneInfo().Caption(title).Left().Layer( 15 ).PinButton( True ) .Float().Resizable().FloatingSize( wx.DefaultSize ).Dockable(True)) #.DestroyOnClose()) else: info = self.auimgr.GetPane(obj) @@ -449,7 +473,10 @@ def info(self, cont): wx.CallAfter(self.txt_info.SetLabel, cont) def _alert(self, info, title='ImagePy'): - dialog=wx.MessageDialog(self, info, title, wx.OK) + lang = Source.manager('config').get('language') + dics = Source.manager('dictionary').gets(tag=lang) + dialog = wx.MessageDialog(self, info, title, wx.OK) + self.translate([i[1] for i in dics])(dialog) dialog.ShowModal() == wx.ID_OK dialog.Destroy() @@ -476,14 +503,47 @@ def show_para(self, title, view, para, on_handle=None, on_ok=None, on_cancel=None, on_help=None, preview=False, modal=True): lang = Source.manager('config').get('language') dic = Source.manager('dictionary').get(name=title, tag=lang) - dialog = ParaDialog(self, title, dic or {}) - dialog.init_view(view, para, preview, modal=modal, app=self) + dialog = ParaDialog(self, title) + dialog.init_view(view, para, preview, modal=modal, + app=self, translate=self.translate(dic)) dialog.Bind('cancel', on_cancel) dialog.Bind('parameter', on_handle) dialog.Bind('commit', on_ok) dialog.Bind('help', on_help) return dialog.show() + def translate(self, dic): + dic = dic or {} + if isinstance(dic, list): + dic = dict(j for i in dic for j in i.items()) + def lang(x): return dic[x] if x in dic else x + def trans(frame): + if hasattr(frame, 'GetChildren'): + for i in frame.GetChildren(): trans(i) + if isinstance(frame, wx.MenuBar): + for i in frame.GetMenus(): trans(i[0]) + for i in range(frame.GetMenuCount()): + frame.SetMenuLabel(i, lang(frame.GetMenuLabel(i))) + return 'not set title' + if isinstance(frame, wx.Menu): + for i in frame.GetMenuItems(): trans(i) + return 'not set title' + if isinstance(frame, wx.MenuItem): + frame.SetItemLabel(lang(frame.GetItemLabel())) + trans(frame.GetSubMenu()) + if isinstance(frame, wx.Button): + frame.SetLabel(lang(frame.GetLabel())) + if isinstance(frame, wx.CheckBox): + frame.SetLabel(lang(frame.GetLabel())) + if isinstance(frame, wx.StaticText): + frame.SetLabel(lang(frame.GetLabel())) + if hasattr(frame, 'SetTitle'): + frame.SetTitle(lang(frame.GetTitle())) + if isinstance(frame, wx.MessageDialog): + frame.SetMessage(lang(frame.GetMessage())) + if hasattr(frame, 'Layout'): frame.Layout() + return trans + if __name__ == '__main__': import numpy as np import pandas as pd diff --git a/imagepy/core/app/loader.py b/imagepy/core/app/loader.py index c7f94134..252f5798 100644 --- a/imagepy/core/app/loader.py +++ b/imagepy/core/app/loader.py @@ -232,9 +232,17 @@ def build_dictionary(path): if isinstance(dic[-1], list): dic[-1] = (dic[-1][0][0], dict(dic[-1])) dic = dict(dic) - for i in dic: Source.manager('dictionary').add(i, dic[i], lang) + for i in dic: + obj = Source.manager('dictionary').get(i, tag=lang) + if not obj is None: obj.update(dic[i]) + else: Source.manager('dictionary').add(i, dic[i], lang) + common = Source.manager('dictionary').get('common', tag=lang) + if common is None: return + objs = Source.manager('dictionary').gets(tag=lang) + for i in objs: + for j in common: i[1][j] = common[j] if __name__ == "__main__": print (os.getcwd()) os.chdir('../../') - data = build_tools('tools') + data = build_tools('tools') \ No newline at end of file diff --git a/imagepy/core/app/source.py b/imagepy/core/app/source.py index b248be44..b484fd87 100644 --- a/imagepy/core/app/source.py +++ b/imagepy/core/app/source.py @@ -11,6 +11,7 @@ Source.manager('config') Source.manager('config').read(osp.join(root_dir, 'data/config.json')) +Source.manager('shortcut').read(osp.join(root_dir, 'data/shortcut.json')) if Source.manager('config').get('language') is None: Source.manager('config').add('language', 'english') diff --git a/imagepy/core/app/startup.py b/imagepy/core/app/startup.py index a238b3d6..8556e155 100644 --- a/imagepy/core/app/startup.py +++ b/imagepy/core/app/startup.py @@ -83,8 +83,6 @@ def start(): AS.AS_SHADOW_BITMAP, shadowcolour=shadow) asp.Update() - load_document() - load_dictionary() uistyle = Source.manager('config').get('uistyle') or 'imagepy' frame = ImageJ(None) if uistyle == 'imagej' else ImagePy(None) frame.Show() diff --git a/imagepy/data/config.json b/imagepy/data/config.json index 55d50fb4..5b8ab92d 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["recent", ["C:\\Users\\12957\\Pictures\\computer-museum[1].png", "C:/Users/54631/Desktop/Workflow Coins Demo.wf", "C:/Users/54631/Documents/projects/imagepy/imagepy/tools/Stack/Add Slice.mc", "C:/Users/54631/Documents/projects/imagepy/imagepy/menus/Plugins/Contribute/Contributions/CellPose.md"], null], ["language", "Chinese", null], ["uistyle", "imagepy", null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null]] \ No newline at end of file +[["language", "Chinese", null], ["uistyle", "imagepy", null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["recent", ["C:/Users/54631/AppData/Roaming/Tencent/QQ/Temp/QQ5LIOM0M5D%2)@GY8256`T.jpg", "c:/users/54631/documents/projects/imagepy/imagepy/menus/Plugins/Contribute/Contributions/CellPose.md", "C:\\Users\\12957\\Pictures\\computer-museum[1].png", "C:/Users/54631/Desktop/Workflow Coins Demo.wf"], null]] \ No newline at end of file diff --git a/imagepy/data/shortcut.json b/imagepy/data/shortcut.json new file mode 100644 index 00000000..0ba8a061 --- /dev/null +++ b/imagepy/data/shortcut.json @@ -0,0 +1 @@ +[["Invert", "Ctrl-I", null], ["Open", "Ctrl-O", null], ["New", "Ctrl-N", null], ["8-bit", "Ctrl-8", null], ["Undo", "Ctrl-Z", null], ["Save", "Ctrl-S", null]] \ No newline at end of file diff --git a/imagepy/data/shotcut.cfg b/imagepy/data/shotcut.cfg deleted file mode 100644 index 72e0e02e..00000000 --- a/imagepy/data/shotcut.cfg +++ /dev/null @@ -1 +0,0 @@ -[{"name": "3D Surface", "shotcut": "Ctrl-F"}, {"name": "2DSurface Demo", "shotcut": "Ctrl-D"}] \ No newline at end of file diff --git a/imagepy/doc/Chinese/plugins/File/New.md b/imagepy/doc/Chinese/plugins/File/New.md new file mode 100644 index 00000000..41bf6985 --- /dev/null +++ b/imagepy/doc/Chinese/plugins/File/New.md @@ -0,0 +1,19 @@ +# 新建图像 + +**描述:** 创建一个空白图像 + + +## 参数 + +**名称:** 新图像的标题 + +**宽度:** 图像宽度 + +**高度:** 图像高度 + +**类型:** 图像类型 + +1. 8-bit: 创建8位灰度图像 +2. rgb: 创建24位RGB图像 + +**slice:** 序列层数 \ No newline at end of file diff --git a/imagepy/lang/Chinese/plugins/Plugins.dic b/imagepy/lang/Chinese/plugins/Plugins.dic index beb6468c..7b0ad8cb 100644 --- a/imagepy/lang/Chinese/plugins/Plugins.dic +++ b/imagepy/lang/Chinese/plugins/Plugins.dic @@ -1,5 +1,3 @@ -New::新建 - New Filter::新建Filter New Simple::新建Simple @@ -17,18 +15,20 @@ Run Macros::运行宏 Manager::管理 Plugins Manager::插件管理 - Search::搜索 - Name::名称 - Author::作者 - Version::版本 - Status::状态 - installed::已安装 - only installed::只显示已安装 - Install::安装 - Update::更新 - Remove::移除 - + Search::搜索 + Name::名称 + Author::作者 + Version::版本 + Status::状态 + installed::已安装 + only installed::只显示已安装 + Install::安装 + Update::更新 + Remove::移除 + Plugin Tree View::插件树浏览 + Plugin Information::插件信息 + [SourceCode]::[查看源码] Tool Tree View::工具树浏览 @@ -78,5 +78,4 @@ Stroke Step::笔迹 Game Of Life::生命游戏 -Screen Capture::截屏 - +Screen Capture::截屏 \ No newline at end of file diff --git a/imagepy/lang/Chinese/plugins/common.dic b/imagepy/lang/Chinese/plugins/common.dic new file mode 100644 index 00000000..1de06239 --- /dev/null +++ b/imagepy/lang/Chinese/plugins/common.dic @@ -0,0 +1,7 @@ +common::普通 + OK::确定 + Cancel::取消 + Help::帮助 + preview::预览 + Widgets::控件 + Table::表格 \ No newline at end of file diff --git a/imagepy/lang/Chinese/plugins/file.dic b/imagepy/lang/Chinese/plugins/file.dic index f8688699..538133cd 100644 --- a/imagepy/lang/Chinese/plugins/file.dic +++ b/imagepy/lang/Chinese/plugins/file.dic @@ -1,4 +1,4 @@ -New::新建图像 +New Image::新建图像 name::名称 width::宽度 height::高度 diff --git a/imagepy/lang/Chinese/plugins/help.dic b/imagepy/lang/Chinese/plugins/help.dic index 1495f53b..36bbe4a5 100644 --- a/imagepy/lang/Chinese/plugins/help.dic +++ b/imagepy/lang/Chinese/plugins/help.dic @@ -4,6 +4,8 @@ About::关于 Home Page::主页 -Language::语言 +Language::语言支持 -Chinese::中文 \ No newline at end of file +Chinese::中文 + +English::英文 \ No newline at end of file diff --git a/imagepy/lang/Chinese/plugins/menus.dic b/imagepy/lang/Chinese/plugins/menus.dic index c868de56..48f1740b 100644 --- a/imagepy/lang/Chinese/plugins/menus.dic +++ b/imagepy/lang/Chinese/plugins/menus.dic @@ -12,7 +12,7 @@ Analysis::分析 Table::表格 -Kit3D::3D工具 +Kit3D::三维 Plugins::插件 diff --git a/imagepy/lang/Chinese/plugins/selection.dic b/imagepy/lang/Chinese/plugins/selection.dic new file mode 100644 index 00000000..3b8b16f8 --- /dev/null +++ b/imagepy/lang/Chinese/plugins/selection.dic @@ -0,0 +1,46 @@ +Select All::整图全选 + +Select None::取消选区 + +ROI Inflate::选区外扩 + +ROI Shrink::选区内缩 + +ROI Convex Hull::选区凸包变换 + +ROI Bound Box::选区包围盒变换 + +ROI Clip::选区变形 + +ROI Invert::选区补集 + +ROI Open::打开选区文件 + +ROI Save::保存选区文件 + +ROI Add::逐个添加选区 + Name::新增选区名 + +ROI Load::加载任意选区 + Name::加载选区名 + +ROI Remove::移除任意选区 + Name::移除选区名 + +ROI Intersect::与任意选区交集 + Name::求交集选区名 + +ROI Union::与任意选区并集 + Name::求并集选区名 + +ROI Difference::与任意选区差集 + Name::求差集选区名 + +ROI Symmetric Diff::与任意选区联合 + Name::求联合选区名 + +ROI Setting::选区设置 + roi::选区颜色 + line width::线宽 + +ROI Ctrl Panel::选区控制面板 \ No newline at end of file diff --git a/imagepy/lang/Chinese/plugins/window.dic b/imagepy/lang/Chinese/plugins/window.dic index 6f9a610d..0c0a6191 100644 --- a/imagepy/lang/Chinese/plugins/window.dic +++ b/imagepy/lang/Chinese/plugins/window.dic @@ -1,5 +1,6 @@ Windows Style::窗口样式 - Shown in ImagePy style when next setup!::ImagePy布局将在下次启动时展示! + Shown in ImageJ style when next setup!::下次启动将会以ImageJ风格展示 + Shown in ImagePy style when next setup!::下次启动将会以ImagePy风格展示 Pay Tribute To ImageJ::ImageJ布局风格 @@ -28,7 +29,7 @@ Command Line::命令行 Plugin List View::插件列表 -Plugin Tree View::插件树形图 +Plugin Tree View::插件树浏览 Tool Tree View::工具树形图 diff --git a/imagepy/lang/Chinese/tools/tools.dic b/imagepy/lang/Chinese/tools/tools.dic new file mode 100644 index 00000000..5133a177 --- /dev/null +++ b/imagepy/lang/Chinese/tools/tools.dic @@ -0,0 +1,11 @@ +Draw::绘图 + +Transform::变换 + +Stack::序列 + +Measure::测量 + +Network::拓扑 + +Toolkit3D::三维 \ No newline at end of file diff --git a/imagepy/lang/Chinese/widgets/widgets.dic b/imagepy/lang/Chinese/widgets/widgets.dic new file mode 100644 index 00000000..a62632c0 --- /dev/null +++ b/imagepy/lang/Chinese/widgets/widgets.dic @@ -0,0 +1,18 @@ +Curve Adjust::曲线调整 + apply::刷新 + clear::清除 + reset::重置 + invert::反向 + +Histogram::直方图 + min-max::极限 + slice::单张 + stack::序列 + +Channels RGB::通道 + stack::序列 + +Navigator::导航栏 + Apply::刷新 + Fit::适应屏幕 + Normal::原始大小 \ No newline at end of file diff --git a/imagepy/menus/File/new_plg.py b/imagepy/menus/File/new_plg.py index 2cfe8764..960ccbd7 100644 --- a/imagepy/menus/File/new_plg.py +++ b/imagepy/menus/File/new_plg.py @@ -8,7 +8,7 @@ import numpy as np class Plugin(Free): - title = 'New' + title = 'New Image' para = {'name':'Undefined','width':300, 'height':300, 'type':'8-bit','slice':1} view = [(str, 'name', 'name', ''), (int, 'width', (1,10240), 0, 'width', 'pix'), diff --git a/imagepy/menus/Plugins/Manager/console_wgt.py b/imagepy/menus/Plugins/Manager/console_wgt.py index 76611209..5e08cae7 100644 --- a/imagepy/menus/Plugins/Manager/console_wgt.py +++ b/imagepy/menus/Plugins/Manager/console_wgt.py @@ -22,11 +22,11 @@ def __init__(self): class Plugin(wx.Panel): title = 'Command Line' single = None - def __init__(self, parent): + def __init__(self, parent, app=None): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) - cmds['app'] = parent + cmds['app'] = app cmds['get_img'] = lambda name=None, app=self: self.app.get_img() cmds['update'] = lambda app=self: self.app.get_img().update() shell = Shell(self, locals=cmds) diff --git a/imagepy/menus/Plugins/Manager/plglist_wgt.py b/imagepy/menus/Plugins/Manager/plglist_wgt.py index a5e88c2e..f8102a76 100644 --- a/imagepy/menus/Plugins/Manager/plglist_wgt.py +++ b/imagepy/menus/Plugins/Manager/plglist_wgt.py @@ -36,11 +36,11 @@ class Plugin( wx.Panel ): title = 'Plugin List View' single = None - def __init__( self, parent,): + def __init__( self, parent, app=None): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) - self.app = parent + self.app = app bSizer1 = wx.BoxSizer( wx.VERTICAL ) bSizer2 = wx.BoxSizer( wx.HORIZONTAL ) self.m_staticText1 = wx.StaticText( self, wx.ID_ANY, "Search:", diff --git a/imagepy/menus/Plugins/Manager/plgtree_wgt.py b/imagepy/menus/Plugins/Manager/plgtree_wgt.py index f297f7e5..24070fb8 100644 --- a/imagepy/menus/Plugins/Manager/plgtree_wgt.py +++ b/imagepy/menus/Plugins/Manager/plgtree_wgt.py @@ -17,11 +17,11 @@ class Plugin ( wx.Panel ): title = 'Plugin Tree View' single = None - def __init__( self, parent ): + def __init__( self, parent, app=None): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) - self.app = parent + self.app = app bSizer1 = wx.BoxSizer( wx.HORIZONTAL ) self.tre_plugins = wx.TreeCtrl( self, wx.ID_ANY, wx.DefaultPosition, @@ -32,7 +32,7 @@ def __init__( self, parent ): bSizer3 = wx.BoxSizer( wx.VERTICAL ) bSizer4 = wx.BoxSizer( wx.HORIZONTAL ) - self.m_staticText2 = wx.StaticText( self, wx.ID_ANY, "Plugin Infomation:", + self.m_staticText2 = wx.StaticText( self, wx.ID_ANY, "Plugin Information", wx.DefaultPosition, wx.DefaultSize, 0 ) self.m_staticText2.Wrap( -1 ) bSizer4.Add( self.m_staticText2, 0, wx.ALL, 5 ) @@ -102,7 +102,9 @@ def on_select( self, event ): if plg!=None: self.plg = plg name = self.tre_plugins.GetItemText(event.GetItem()) - self.txt_info.set_cont(Source.manager('document').get(name)) + lang = Source.manager('config').get('language') + cont = Source.manager('document').get(name, tag=lang) + self.txt_info.set_cont(cont or 'No Document!') def on_source(self, event): ## TODO: should it be absolute path ? diff --git a/imagepy/menus/Plugins/Manager/shotcut_wgt.py b/imagepy/menus/Plugins/Manager/shotcut_wgt.py index 7925e7fd..f259b7fd 100644 --- a/imagepy/menus/Plugins/Manager/shotcut_wgt.py +++ b/imagepy/menus/Plugins/Manager/shotcut_wgt.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import wx, os from imagepy.core.engine import Free from sciapp import Source @@ -28,14 +27,14 @@ def refresh(self): self.SetItemCount(len(self.data)) class Plugin( wx.Panel ): - title = 'Shotcut Editor' + title = 'Shortcut Editor' single = None - def __init__( self, parent): + def __init__( self, parent, app=None): wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) - self.app = parent + self.app = app bSizer1 = wx.BoxSizer( wx.VERTICAL ) bSizer2 = wx.BoxSizer( wx.HORIZONTAL ) self.m_staticText1 = wx.StaticText( self, wx.ID_ANY, "Search:", @@ -46,7 +45,7 @@ def __init__( self, parent): wx.DefaultPosition, wx.DefaultSize, 0 ) bSizer2.Add( self.txt_search, 1, wx.ALL, 5 ) bSizer1.Add( bSizer2, 0, wx.EXPAND, 5 ) - self.lst_plgs = VirtualListCtrl( self, ['Name', 'Shotcut']) + self.lst_plgs = VirtualListCtrl( self, ['Name', 'Shortcut']) self.lst_plgs.SetColumnWidth(0,200) self.lst_plgs.SetColumnWidth(1,200) bSizer1.Add( self.lst_plgs, 1, wx.ALL|wx.EXPAND, 5 ) @@ -65,7 +64,7 @@ def __init__( self, parent): #def list_plg(self, lst, items def load(self): lst = Source.manager('plugin').names() - self.plgs = [[i, Source.manager('shotcut').get(item='shotcut', name=i)] for i in lst] + self.plgs = [[i, Source.manager('shortcut').get(i)] for i in lst] for i in self.plgs: if i[1]==None:i[1]='' self.plgs.sort() @@ -116,9 +115,9 @@ def on_run(self, event): if len(txt)>0 and txt[-1]=='-':txt=txt[:-1] self.buf[event.GetIndex()][1] = txt self.lst_plgs.RefreshItem(event.GetIndex()) - Source.manager('shotcut').remove(name=title) - if txt!='': Source.manager('shotcut').add(title, txt) + Source.manager('shortcut').remove(name=title) + if txt!='': Source.manager('shortcut').add(title, txt) #PluginsManager.plgs[self.buf[event.GetIndex()][0]]().start() def close(self): - Source.manager('shotcut').write(os.path.join(root_dir,'data/shotcut.cfg')) + Source.manager('shortcut').write(os.path.join(root_dir,'data/shortcut.json')) diff --git a/imagepy/menus/Plugins/Manager/toltree_wgt.py b/imagepy/menus/Plugins/Manager/toltree_wgt.py index b43c452b..d98cd1ae 100644 --- a/imagepy/menus/Plugins/Manager/toltree_wgt.py +++ b/imagepy/menus/Plugins/Manager/toltree_wgt.py @@ -18,11 +18,11 @@ class Plugin ( wx.Panel ): title = 'Tool Tree View' single = None - def __init__( self, parent ): + def __init__( self, parent, app=None): wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) - self.app = parent + self.app = app bSizer1 = wx.BoxSizer( wx.HORIZONTAL ) self.tre_plugins = wx.TreeCtrl( self, wx.ID_ANY, wx.DefaultPosition, diff --git a/sciwx/widgets/menubar.py b/sciwx/widgets/menubar.py index 8585132e..6e8f1397 100644 --- a/sciwx/widgets/menubar.py +++ b/sciwx/widgets/menubar.py @@ -1,23 +1,34 @@ import wx -def translate(v, dic): return dic[v] if v in dic else v +def hot_key(txt): + sep = txt.split('-') + acc, code = wx.ACCEL_NORMAL, -1 + if 'Ctrl' in sep: acc|= wx.ACCEL_CTRL + if 'Alt' in sep: acc|= wx.ACCEL_ALT + if 'Shift' in sep: acc|= wx.ACCEL_SHIFT + fs = ['F%d'%i for i in range(1,13)] + if sep[-1] in fs: + code = 340+fs.index(sep[-1]) + elif len(sep[-1])==1: code = ord(sep[-1]) + return acc, code class MenuBar(wx.MenuBar): - def __init__(self, app, dic={}): + def __init__(self, app): wx.MenuBar.__init__(self) self.app = app - self.dic = dic app.SetMenuBar(self) - def parse(self, ks, vs, pt): + def parse(self, ks, vs, pt, short, rst): if isinstance(vs, list): menu = wx.Menu() for kv in vs: if kv == '-': menu.AppendSeparator() - else: self.parse(*kv, menu) - pt.Append(1, translate(ks, self.dic), menu) + else: self.parse(*kv, menu, short, rst) + pt.Append(1, ks, menu) else: - item = wx.MenuItem(pt, -1, translate(ks, self.dic)) + item = wx.MenuItem(pt, -1, ks) + if ks in short: + rst.append((short[ks], item.GetId())) f = lambda e, p=vs: p().start(self.app) self.Bind(wx.EVT_MENU, f, item) pt.Append(item) @@ -25,8 +36,12 @@ def parse(self, ks, vs, pt): def Append(self, id, item, menu): wx.MenuBar.Append(self, menu, item) - def load(self, data): - for k,v in data[1]: self.parse(k, v, self) + def load(self, data, shortcut={}): + rst = [] + for k,v in data[1]: + self.parse(k, v, self, shortcut, rst) + rst = [(*hot_key(i[0]), i[1]) for i in rst] + return wx.AcceleratorTable(rst) def on_menu(self, event): print('here') diff --git a/sciwx/widgets/paradialog.py b/sciwx/widgets/paradialog.py index fdd842f5..0d1ae696 100644 --- a/sciwx/widgets/paradialog.py +++ b/sciwx/widgets/paradialog.py @@ -12,15 +12,11 @@ def add_widget(key, value): widgets[key] = value -def translate(v, dic): - if isinstance(v, str): return dic[v] if v in dic else v - return [dic[i] if isinstance(i, str) and i in dic else i for i in v] - class ParaDialog (wx.Dialog): - def __init__( self, parent, title, dic={}): - wx.Dialog.__init__ (self, parent, -1, translate(title, dic), style = wx.DEFAULT_DIALOG_STYLE) + def __init__( self, parent, title): + wx.Dialog.__init__ (self, parent, -1, title, style = wx.DEFAULT_DIALOG_STYLE) self.lst = wx.BoxSizer( wx.VERTICAL ) - self.tus, self.dic = [], dic + self.tus = [] self.on_ok = self.on_cancel = self.on_help = None self.handle = print self.ctrl_dic = {} @@ -37,6 +33,7 @@ def commit(self, state): def add_confirm(self, modal): sizer = wx.BoxSizer( wx.HORIZONTAL ) self.btn_ok = wx.Button( self, wx.ID_OK, 'OK', wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) + self.btn_ok.SetFocus() sizer.Add( self.btn_ok, 0, wx.ALL, 5 ) self.btn_cancel = wx.Button( self, wx.ID_CANCEL, 'Cancel', wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) @@ -51,13 +48,14 @@ def add_confirm(self, modal): self.btn_cancel.Bind( wx.EVT_BUTTON, lambda e:self.commit('cancel')) #self.lst.Add() - def init_view(self, items, para, preview=False, modal=True, app=None, dic={}): + def init_view(self, items, para, preview=False, modal=True, app=None, translate=lambda x:x): self.para, self.modal = para, modal for item in items: - self.add_ctrl_(widgets[item[0]], item[1], translate(item[2:], self.dic), app=app) + self.add_ctrl_(widgets[item[0]], item[1], item[2:], app=app) if preview:self.add_ctrl_(Check, 'preview', ('preview',), app=app) self.reset(para) self.add_confirm(modal) + translate(self) self.pack() wx.Dialog.Bind(self, wx.EVT_WINDOW_DESTROY, self.OnDestroy) #wx.Dialog.Bind(self, wx.EVT_IDLE, lambda e: self.reset()) From 729ec4b12b8685a9861d300ebd41f7dcccbbe6c2 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 24 Jun 2020 23:18:55 +0800 Subject: [PATCH 264/343] ij chinese --- imagepy/core/app/imagej.py | 96 ++++++++++++++++++++-- imagepy/core/app/imagepy.py | 47 ++++++----- imagepy/core/app/loader.py | 3 +- imagepy/core/app/startup.py | 3 + imagepy/core/util/tableio.py | 39 --------- imagepy/data/config.json | 2 +- imagepy/lang/Chinese/plugins/selection.dic | 25 +++++- imagepy/menus/Selection/roiwindow_wgt.py | 4 +- sciwx/plugins/viewport.py | 2 +- sciwx/widgets/toolbar.py | 2 +- 10 files changed, 148 insertions(+), 75 deletions(-) delete mode 100644 imagepy/core/util/tableio.py diff --git a/imagepy/core/app/imagej.py b/imagepy/core/app/imagej.py index 9b573100..904a163b 100644 --- a/imagepy/core/app/imagej.py +++ b/imagepy/core/app/imagej.py @@ -72,6 +72,13 @@ def OnDropFiles(self, x, y, path): . MinSize(wx.Size(-1, 20)). MaxSize(wx.Size(-1, 20)).Layer( 10 ) ) def _load_all(self): + lang = Source.manager('config').get('language') + dic = Source.manager('dictionary').get('common', tag=lang) or {} + self.auimgr.GetPane(self.widgets).Caption('Widgets') + for i in self.auimgr.GetAllPanes(): + i.Caption(dic[i.caption] if i.caption in dic else i.caption) + self.auimgr.Update() + plgs, errplg = load_plugins() self.load_menu(plgs) dtool = Source.manager('tools').get('default') @@ -90,18 +97,37 @@ def load_all(self): def load_menu(self, data): self.menubar.clear() - self.menubar.load(data) + lang = Source.manager('config').get('language') + ls = Source.manager('dictionary').gets(tag=lang) + short = Source.manager('shortcut').gets() + acc = self.menubar.load(data, dict([i[:2] for i in short])) + self.translate(dict([(i,j[i]) for i,j,_ in ls]))(self.menubar) + self.SetAcceleratorTable(acc) def load_tool(self, data, default=None): self.toolbar.clear() + lang = Source.manager('config').get('language') + ls = Source.manager('dictionary').gets(tag=lang) + dic = dict([(i,j[i]) for i,j,_ in ls]) for i, (name, tols) in enumerate(data[1]): + name = dic[name] if name in dic else name self.toolbar.add_tools(name, tols, i==0) + default = dic[default] if default in dic else default if not default is None: self.toolbar.add_pop(os.path.join(root_dir, 'tools/drop.gif'), default) self.toolbar.Layout() def load_widget(self, data): self.widgets.clear() + lang = Source.manager('config').get('language') self.widgets.load(data) + for cbk in self.widgets.GetChildren(): + for i in range(cbk.GetPageCount()): + dic = Source.manager('dictionary').get(cbk.GetPageText(i), tag=lang) or {} + translate = self.translate(dic) + title = cbk.GetPageText(i) + cbk.SetPageText(i, dic[title] if title in dic else title) + self.translate(dic)(cbk.GetPage(i)) + # self.translate(self.widgets) def init_menu(self): self.menubar = MenuBar(self) @@ -109,6 +135,11 @@ def init_menu(self): def init_tool(self): sizer = wx.BoxSizer(wx.VERTICAL) self.toolbar = ToolBar(self, False) + def on_help(evt, tol): + lang = Source.manager('config').get('language') + doc = Source.manager('document').get(tol.title, tag=lang) + self.show_md(doc or 'No Document!', tol.title) + self.toolbar.on_help = on_help self.toolbar.Fit() self.auimgr.AddPane(self.toolbar, aui.AuiPaneInfo() .Top() .PinButton( True ).PaneBorder( False ) @@ -186,6 +217,10 @@ def on_close_mesh(self, event): event.Skip() def set_info(self, value): + lang = Source.manager('config').get('language') + dics = Source.manager('dictionary').gets(tag=lang) + dic = dict(j for i in dics for j in i[1].items()) + value = dic[value] if value in dic else value wx.CallAfter(self.txt_info.SetLabel, value) def set_progress(self, value): @@ -241,6 +276,7 @@ def show_plot(self, title): def _show_md(self, cont, title='ImagePy'): mdframe = MDFrame(self) + mdframe.SetIcon(self.GetIcon()) mdframe.set_cont(cont) mdframe.mdpad.title = title mdframe.Show(True) @@ -295,13 +331,16 @@ def show_mesh(self, mesh=None, title=None): def show_widget(self, panel, title='Widgets'): obj = self.manager('widget').get(panel.title) if obj is None: - pan = panel(self) - self.manager('widget').add(obj=pan, name=panel.title) - self.auimgr.AddPane(pan, aui.AuiPaneInfo().Caption(panel.title).Left().Layer( 15 ).PinButton( True ) + obj = panel(self, self) + self.manager('widget').add(panel.title, obj) + self.auimgr.AddPane(obj, aui.AuiPaneInfo().Caption(title).Left().Layer( 15 ).PinButton( True ) .Float().Resizable().FloatingSize( wx.DefaultSize ).Dockable(True)) #.DestroyOnClose()) - else: - info = self.auimgr.GetPane(obj) - info.Show(True) + lang = Source.manager('config').get('language') + dic = Source.manager('dictionary').get(obj.title, tag=lang) + info = self.auimgr.GetPane(obj) + info.Show(True).Caption(dic[obj.title] if obj.title in dic else obj.title) + self.translate(dic)(obj) + self.Layout() self.auimgr.Update() @@ -396,14 +435,53 @@ def getpath(self, title, filt, io, name=''): dialog.Destroy() return path - def show_para(self, title, view, para, on_handle=None, on_ok=None, on_cancel=None, preview=False, modal=True): + def show_para(self, title, view, para, on_handle=None, on_ok=None, + on_cancel=None, on_help=None, preview=False, modal=True): + lang = Source.manager('config').get('language') + dic = Source.manager('dictionary').get(name=title, tag=lang) dialog = ParaDialog(self, title) - dialog.init_view(view, para, preview, modal=modal, app=self) + dialog.init_view(view, para, preview, modal=modal, + app=self, translate=self.translate(dic)) dialog.Bind('cancel', on_cancel) dialog.Bind('parameter', on_handle) dialog.Bind('commit', on_ok) + dialog.Bind('help', on_help) return dialog.show() + def translate(self, dic): + dic = dic or {} + if isinstance(dic, list): + dic = dict(j for i in dic for j in i.items()) + def lang(x): return dic[x] if x in dic else x + def trans(frame): + if hasattr(frame, 'GetChildren'): + for i in frame.GetChildren(): trans(i) + if isinstance(frame, wx.MenuBar): + for i in frame.GetMenus(): trans(i[0]) + for i in range(frame.GetMenuCount()): + frame.SetMenuLabel(i, lang(frame.GetMenuLabel(i))) + return 'not set title' + if isinstance(frame, wx.Menu): + for i in frame.GetMenuItems(): trans(i) + return 'not set title' + if isinstance(frame, wx.MenuItem): + frame.SetItemLabel(lang(frame.GetItemLabel())) + trans(frame.GetSubMenu()) + if isinstance(frame, wx.Button): + frame.SetLabel(lang(frame.GetLabel())) + if isinstance(frame, wx.CheckBox): + frame.SetLabel(lang(frame.GetLabel())) + if isinstance(frame, wx.StaticText): + frame.SetLabel(lang(frame.GetLabel())) + if hasattr(frame, 'SetTitle'): + frame.SetTitle(lang(frame.GetTitle())) + if isinstance(frame, wx.MessageDialog): + frame.SetMessage(lang(frame.GetMessage())) + if isinstance(frame, wx.Notebook): + for i in range(frame.GetPageCount()): + frame.SetPageText(i, lang(frame.GetPageText(i))) + if hasattr(frame, 'Layout'): frame.Layout() + return trans if __name__ == '__main__': import numpy as np import pandas as pd diff --git a/imagepy/core/app/imagepy.py b/imagepy/core/app/imagepy.py index d9c4b06c..fa2d1633 100644 --- a/imagepy/core/app/imagepy.py +++ b/imagepy/core/app/imagepy.py @@ -72,8 +72,14 @@ def OnDropFiles(self, x, y, path): . MinSize(wx.Size(-1, 20)). MaxSize(wx.Size(-1, 20)).Layer( 10 ) ) def _load_all(self): - load_document() - load_dictionary() + lang = Source.manager('config').get('language') + dic = Source.manager('dictionary').get('common', tag=lang) or {} + self.auimgr.GetPane(self.widgets).Caption('Widgets') + self.auimgr.GetPane(self.tablenbwrap).Caption('Table') + for i in self.auimgr.GetAllPanes(): + i.Caption(dic[i.caption] if i.caption in dic else i.caption) + self.auimgr.Update() + plgs, errplg = load_plugins() self.load_menu(plgs) dtool = Source.manager('tools').get('default') @@ -177,11 +183,9 @@ def init_table(self): sizer.Add( self.tablenb, 1, wx.EXPAND |wx.ALL, 0 ) self.tablenbwrap.SetSizer( sizer ) self.tablenbwrap.Layout() - lang = Source.manager('config').get('language') - dic = Source.manager('dictionary').get('common') or {} - title = dic['Table'] if 'Table' in dic else 'Table' + self.auimgr.AddPane( self.tablenbwrap, aui.AuiPaneInfo() .Bottom() .CaptionVisible( True ).PinButton( True ).Dock().Hide() - .MaximizeButton( True ).Resizable().FloatingSize((800, 600)).BestSize(( 120,120 )). Caption(title) . + .MaximizeButton( True ).Resizable().FloatingSize((800, 600)).BestSize(( 120,120 )). Caption('Table') . BottomDockable( True ).TopDockable( False ).LeftDockable( True ).RightDockable( True ) ) self.tablenb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_new_tab) self.tablenb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close_tab) @@ -214,10 +218,9 @@ def remove_task(self, task): def init_widgets(self): lang = Source.manager('config').get('language') - dic = Source.manager('dictionary').get('common') or {} - title = dic['Widgets'] if 'Widgets' in dic else 'Widgets' + dic = Source.manager('dictionary').get('common', tag=lang) self.widgets = ChoiceBook(self) - self.auimgr.AddPane( self.widgets, aui.AuiPaneInfo() .Right().Caption(title) .PinButton( True ) + self.auimgr.AddPane( self.widgets, aui.AuiPaneInfo() .Right().Caption('Widgets') .PinButton( True ) .Dock().Resizable().FloatingSize( wx.DefaultSize ).MinSize( wx.Size( 266,-1 ) ).Layer( 10 ) ) def init_text(self): @@ -277,6 +280,10 @@ def on_close_mesh(self, event): self.remove_mesh_win(canvas3d) def set_info(self, value): + lang = Source.manager('config').get('language') + dics = Source.manager('dictionary').gets(tag=lang) + dic = dict(j for i in dics for j in i[1].items()) + value = dic[value] if value in dic else value wx.CallAfter(self.txt_info.SetLabel, value) def set_progress(self, value): @@ -392,17 +399,16 @@ def show_mesh(self, mesh=None, title=None): def show_widget(self, panel, title='Widgets'): obj = self.manager('widget').get(panel.title) if obj is None: - pan = panel(self, self) - lang = Source.manager('config').get('language') - dic = Source.manager('dictionary').get(panel.title, tag=lang) - self.translate(dic)(pan) - self.manager('widget').add(obj=pan, name=panel.title) - title = dic[panel.title] if panel.title in dic else panel.title - self.auimgr.AddPane(pan, aui.AuiPaneInfo().Caption(title).Left().Layer( 15 ).PinButton( True ) + obj = panel(self, self) + self.manager('widget').add(panel.title, obj) + self.auimgr.AddPane(obj, aui.AuiPaneInfo().Caption(title).Left().Layer( 15 ).PinButton( True ) .Float().Resizable().FloatingSize( wx.DefaultSize ).Dockable(True)) #.DestroyOnClose()) - else: - info = self.auimgr.GetPane(obj) - info.Show(True) + lang = Source.manager('config').get('language') + dic = Source.manager('dictionary').get(obj.title, tag=lang) + info = self.auimgr.GetPane(obj) + info.Show(True).Caption(dic[obj.title] if obj.title in dic else obj.title) + self.translate(dic)(obj) + self.Layout() self.auimgr.Update() @@ -541,6 +547,9 @@ def trans(frame): frame.SetTitle(lang(frame.GetTitle())) if isinstance(frame, wx.MessageDialog): frame.SetMessage(lang(frame.GetMessage())) + if isinstance(frame, wx.Notebook): + for i in range(frame.GetPageCount()): + frame.SetPageText(i, lang(frame.GetPageText(i))) if hasattr(frame, 'Layout'): frame.Layout() return trans diff --git a/imagepy/core/app/loader.py b/imagepy/core/app/loader.py index 252f5798..dd0be051 100644 --- a/imagepy/core/app/loader.py +++ b/imagepy/core/app/loader.py @@ -239,8 +239,7 @@ def build_dictionary(path): common = Source.manager('dictionary').get('common', tag=lang) if common is None: return objs = Source.manager('dictionary').gets(tag=lang) - for i in objs: - for j in common: i[1][j] = common[j] + for i in objs: i[1].update(common) if __name__ == "__main__": print (os.getcwd()) diff --git a/imagepy/core/app/startup.py b/imagepy/core/app/startup.py index 8556e155..b7e7f84c 100644 --- a/imagepy/core/app/startup.py +++ b/imagepy/core/app/startup.py @@ -83,6 +83,9 @@ def start(): AS.AS_SHADOW_BITMAP, shadowcolour=shadow) asp.Update() + + load_document() + load_dictionary() uistyle = Source.manager('config').get('uistyle') or 'imagepy' frame = ImageJ(None) if uistyle == 'imagej' else ImagePy(None) frame.Show() diff --git a/imagepy/core/util/tableio.py b/imagepy/core/util/tableio.py deleted file mode 100644 index 5ec17a3a..00000000 --- a/imagepy/core/util/tableio.py +++ /dev/null @@ -1,39 +0,0 @@ -''' -import os -from ... import root_dir -from ..engine import Free, Table, Macros -from sciapp import Source -import numpy as np - -class Reader(Free): - para = {'path':''} - - def show(self): - filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in self.filt]) - return IPy.getpath('Open..', filt, 'open', self.para) - - #process - def run(self, para = None): - - fp, fn = os.path.split(para['path']) - fn, fe = os.path.splitext(fn) - read = Source.manager('reader').get(fe[1:], tag = 'tab') - - table = read(para['path']) - ViewerManager.get(fe[1:])(table, fn) - -class Writer(Table): - note = ['all'] - para={'path':root_dir} - - def show(self): - filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in self.filt]) - return IPy.getpath('Save..', filt, 'save', self.para) - - #process - def run(self, tps, snap, data, para = None): - fp, fn = os.path.split(para['path']) - fn, fe = os.path.splitext(fn) - write = Source.manager('writer').get(fe[1:], tag='tab') - write(para['path'], data) -''' \ No newline at end of file diff --git a/imagepy/data/config.json b/imagepy/data/config.json index 5b8ab92d..df094e95 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["language", "Chinese", null], ["uistyle", "imagepy", null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["recent", ["C:/Users/54631/AppData/Roaming/Tencent/QQ/Temp/QQ5LIOM0M5D%2)@GY8256`T.jpg", "c:/users/54631/documents/projects/imagepy/imagepy/menus/Plugins/Contribute/Contributions/CellPose.md", "C:\\Users\\12957\\Pictures\\computer-museum[1].png", "C:/Users/54631/Desktop/Workflow Coins Demo.wf"], null]] \ No newline at end of file +[["language", "English", null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["recent", ["C:/Users/54631/AppData/Roaming/Tencent/QQ/Temp/QQ5LIOM0M5D%2)@GY8256`T.jpg", "c:/users/54631/documents/projects/imagepy/imagepy/menus/Plugins/Contribute/Contributions/CellPose.md", "C:\\Users\\12957\\Pictures\\computer-museum[1].png", "C:/Users/54631/Desktop/Workflow Coins Demo.wf"], null], ["uistyle", "imagej", null]] \ No newline at end of file diff --git a/imagepy/lang/Chinese/plugins/selection.dic b/imagepy/lang/Chinese/plugins/selection.dic index 3b8b16f8..021c8925 100644 --- a/imagepy/lang/Chinese/plugins/selection.dic +++ b/imagepy/lang/Chinese/plugins/selection.dic @@ -43,4 +43,27 @@ ROI Setting::选区设置 roi::选区颜色 line width::线宽 -ROI Ctrl Panel::选区控制面板 \ No newline at end of file +ROI Ctrl Panel::选区控制面板 + Manage::管理 + Add::添加 + Load::加载 + Update::更新 + Remove::移除 + Open::打开 + Save::保存 + Operate::几何运算 + Inflate::扩张 + Shrink::收缩 + Convex::凸包 + Bound::外接矩 + Clip::裁剪 + Invert::反向 + Relationship::关系运算 + Intersect::香蕉 + Union::合并 + Difference::减去 + Sym Diff::抑或 + Draw::绘图 + Sketch::描边 + Clear::清除内部 + Clear Out::清除外部 \ No newline at end of file diff --git a/imagepy/menus/Selection/roiwindow_wgt.py b/imagepy/menus/Selection/roiwindow_wgt.py index b90dd274..732704f2 100644 --- a/imagepy/menus/Selection/roiwindow_wgt.py +++ b/imagepy/menus/Selection/roiwindow_wgt.py @@ -32,9 +32,9 @@ class Plugin(wx.Panel): title = 'ROI Ctrl Panel' single = None - def __init__( self, parent ): + def __init__( self, parent, app=None): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size(-1,-1), style = wx.TAB_TRAVERSAL ) - self.app = parent + self.app = app sizer = wx.BoxSizer( wx.HORIZONTAL ) self.note_book = wx.Notebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.NB_LEFT|wx.NB_TOP ) diff --git a/sciwx/plugins/viewport.py b/sciwx/plugins/viewport.py index ea02019c..1b351981 100644 --- a/sciwx/plugins/viewport.py +++ b/sciwx/plugins/viewport.py @@ -16,7 +16,7 @@ def __init__( self, parent , app): bSizer3.Add( self.viewport, 1, wx.EXPAND |wx.ALL, 5 ) self.slider = wx.Slider( self, wx.ID_ANY, 6, 0, len(self.scales), wx.DefaultPosition, wx.DefaultSize, wx.SL_LEFT|wx.SL_VERTICAL|wx.SL_SELRANGE|wx.SL_INVERSE ) - bSizer3.Add( self.slider, 0, wx.ALL|wx.EXPAND, 0 ) + bSizer3.Add( self.slider, 0, wx.RIGHT|wx.EXPAND, 5 ) bSizer1.Add( bSizer3, 1, wx.EXPAND, 5 ) diff --git a/sciwx/widgets/toolbar.py b/sciwx/widgets/toolbar.py index 776e3f49..19f15c3f 100644 --- a/sciwx/widgets/toolbar.py +++ b/sciwx/widgets/toolbar.py @@ -55,7 +55,7 @@ def on_config(self, evt, tol): def on_help(self, evt, tol): pass def on_info(self, event, tol): - self.app.set_info('%s Tool'%tol.title) + self.app.set_info(tol.title) def bind(self, btn, tol): obj = tol() From 72e6936eb6ba05b491fc224a2268fb659bc3fb99 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 25 Jun 2020 08:49:59 +0800 Subject: [PATCH 265/343] language ok --- imagepy/core/app/imagej.py | 15 +++++++++------ imagepy/core/app/imagepy.py | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/imagepy/core/app/imagej.py b/imagepy/core/app/imagej.py index 904a163b..39cc75c0 100644 --- a/imagepy/core/app/imagej.py +++ b/imagepy/core/app/imagej.py @@ -58,7 +58,7 @@ def init_status(self): sizersta.Add( self.txt_info, 1, wx.ALIGN_BOTTOM|wx.BOTTOM|wx.LEFT|wx.RIGHT, 2 ) #self.pro_bar = wx.Gauge( stapanel, wx.ID_ANY, 100, wx.DefaultPosition, wx.Size( 100,15 ), wx.GA_HORIZONTAL ) self.pro_bar = ProgressBar(stapanel) - sizersta.Add( self.pro_bar, 0, wx.ALL, 2 ) + sizersta.Add( self.pro_bar, 0, wx.ALL|wx.ALIGN_CENTER, 0 ) stapanel.SetSizer(sizersta) class OpenDrop(wx.FileDropTarget): def __init__(self, app): @@ -87,20 +87,19 @@ def _load_all(self): wgts, errwgt = load_widgets() self.load_widget(wgts) err = errplg + errtol + errwgt - if len(err)>0: + if len(err)>0: err = [('File', 'Name', 'Error')] + err cont = '\n'.join(['%-30s\t%-20s\t%s'%i for i in err]) self.show_txt(cont, 'loading error log') - def load_all(self): - wx.CallAfter(self._load_all) + def load_all(self): wx.CallAfter(self._load_all) def load_menu(self, data): self.menubar.clear() lang = Source.manager('config').get('language') ls = Source.manager('dictionary').gets(tag=lang) short = Source.manager('shortcut').gets() - acc = self.menubar.load(data, dict([i[:2] for i in short])) + acc = self.menubar.load(data) self.translate(dict([(i,j[i]) for i,j,_ in ls]))(self.menubar) self.SetAcceleratorTable(acc) @@ -329,6 +328,7 @@ def show_mesh(self, mesh=None, title=None): wx.CallAfter(self._show_mesh, mesh, title) def show_widget(self, panel, title='Widgets'): + print(self.stapanel.GetSize(), '===========') obj = self.manager('widget').get(panel.title) if obj is None: obj = panel(self, self) @@ -336,13 +336,14 @@ def show_widget(self, panel, title='Widgets'): self.auimgr.AddPane(obj, aui.AuiPaneInfo().Caption(title).Left().Layer( 15 ).PinButton( True ) .Float().Resizable().FloatingSize( wx.DefaultSize ).Dockable(True)) #.DestroyOnClose()) lang = Source.manager('config').get('language') - dic = Source.manager('dictionary').get(obj.title, tag=lang) + dic = Source.manager('dictionary').get(obj.title, tag=lang) or {} info = self.auimgr.GetPane(obj) info.Show(True).Caption(dic[obj.title] if obj.title in dic else obj.title) self.translate(dic)(obj) self.Layout() self.auimgr.Update() + print(self.stapanel.GetSize(), '===========') def switch_widget(self, visible=None): info = self.auimgr.GetPane(self.widgets) @@ -419,6 +420,7 @@ def _alert(self, info, title='ImagePy'): def alert(self, info, title='ImagePy'): wx.CallAfter(self._alert, info, title) + def yes_no(self, info, title='ImagePy'): dialog = wx.MessageDialog(self, info, title, wx.YES_NO | wx.CANCEL) rst = dialog.ShowModal() @@ -482,6 +484,7 @@ def trans(frame): frame.SetPageText(i, lang(frame.GetPageText(i))) if hasattr(frame, 'Layout'): frame.Layout() return trans + if __name__ == '__main__': import numpy as np import pandas as pd diff --git a/imagepy/core/app/imagepy.py b/imagepy/core/app/imagepy.py index fa2d1633..85fea6c8 100644 --- a/imagepy/core/app/imagepy.py +++ b/imagepy/core/app/imagepy.py @@ -100,7 +100,7 @@ def load_menu(self, data): lang = Source.manager('config').get('language') ls = Source.manager('dictionary').gets(tag=lang) short = Source.manager('shortcut').gets() - acc = self.menubar.load(data, dict([i[:2] for i in short])) + acc = self.menubar.load(data) self.translate(dict([(i,j[i]) for i,j,_ in ls]))(self.menubar) self.SetAcceleratorTable(acc) From a3a30205ba99eb27884cea0ae4d54b60d4efa829 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 25 Jun 2020 09:35:55 +0800 Subject: [PATCH 266/343] mac --- imagepy/__main__.py | 4 ++-- imagepy/core/app/__init__.py | 3 ++- imagepy/core/app/startup.py | 15 +++++++-------- imagepy/data/config.json | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/imagepy/__main__.py b/imagepy/__main__.py index 0189c45e..5ae21ab4 100644 --- a/imagepy/__main__.py +++ b/imagepy/__main__.py @@ -1,4 +1,4 @@ import sys sys.path.append(r'../') -from core.app import startup -startup.start() \ No newline at end of file +from imagepy.core import app +app.startup.start() \ No newline at end of file diff --git a/imagepy/core/app/__init__.py b/imagepy/core/app/__init__.py index b6143f36..bff58554 100644 --- a/imagepy/core/app/__init__.py +++ b/imagepy/core/app/__init__.py @@ -1,2 +1,3 @@ from .imagepy import ImagePy -from .imagej import ImageJ \ No newline at end of file +from .imagej import ImageJ +from .import startup \ No newline at end of file diff --git a/imagepy/core/app/startup.py b/imagepy/core/app/startup.py index b7e7f84c..123f3b0b 100644 --- a/imagepy/core/app/startup.py +++ b/imagepy/core/app/startup.py @@ -2,6 +2,7 @@ from .source import * from sciapp import Source from imagepy.core.app import loader +from imagepy import root_dir def extend_plgs(plg): if isinstance(plg, tuple): @@ -62,28 +63,26 @@ def load_widgets(): return extend_wgts(data[:2]), data[2] def load_document(): - Source.manager('document').add('language', os.listdir('doc')) - loader.build_document('doc/') + Source.manager('document').add('language', os.listdir(root_dir+'/doc')) + loader.build_document(root_dir+'/doc/') def load_dictionary(): - Source.manager('dictionary').add('language', os.listdir('lang')) - loader.build_dictionary('lang/') + Source.manager('dictionary').add('language', os.listdir(root_dir+'/lang')) + loader.build_dictionary(root_dir+'/lang/') def start(): from imagepy.core.app import ImagePy, ImageJ import wx.lib.agw.advancedsplash as AS - app = wx.App(False) - bitmap = wx.Bitmap('data/logolong.png', wx.BITMAP_TYPE_PNG) + bitmap = wx.Bitmap(root_dir+'/data/logolong.png', wx.BITMAP_TYPE_PNG) shadow = wx.Colour(255,255,255) - asp = AS.AdvancedSplash(None, bitmap=bitmap, timeout=1000, agwStyle=AS.AS_TIMEOUT | AS.AS_CENTER_ON_PARENT | AS.AS_SHADOW_BITMAP, shadowcolour=shadow) asp.Update() - + print('d') load_document() load_dictionary() uistyle = Source.manager('config').get('uistyle') or 'imagepy' diff --git a/imagepy/data/config.json b/imagepy/data/config.json index df094e95..4326ffe4 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["language", "English", null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["recent", ["C:/Users/54631/AppData/Roaming/Tencent/QQ/Temp/QQ5LIOM0M5D%2)@GY8256`T.jpg", "c:/users/54631/documents/projects/imagepy/imagepy/menus/Plugins/Contribute/Contributions/CellPose.md", "C:\\Users\\12957\\Pictures\\computer-museum[1].png", "C:/Users/54631/Desktop/Workflow Coins Demo.wf"], null], ["uistyle", "imagej", null]] \ No newline at end of file +[["uistyle", "imagepy", null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["recent", ["C:/Users/54631/AppData/Roaming/Tencent/QQ/Temp/QQ5LIOM0M5D%2)@GY8256`T.jpg", "c:/users/54631/documents/projects/imagepy/imagepy/menus/Plugins/Contribute/Contributions/CellPose.md", "C:\\Users\\12957\\Pictures\\computer-museum[1].png", "C:/Users/54631/Desktop/Workflow Coins Demo.wf"], null], ["language", "Chinese", null]] \ No newline at end of file From 37f77e786ccaa1c2a97b7391d18bcade3a081521 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 25 Jun 2020 18:12:11 +0800 Subject: [PATCH 267/343] dictionary ok --- imagepy/__main__.py | 2 +- imagepy/core/app/imagej.py | 2 + imagepy/core/app/imagepy.py | 7 +- imagepy/core/app/loader.py | 17 +-- imagepy/core/app/startup.py | 16 ++- imagepy/core/engine/__init__.py | 3 +- .../core/{util/fileio.py => engine/dataio.py} | 8 +- imagepy/core/engine/filter.py | 1 + imagepy/core/engine/free.py | 1 + imagepy/core/engine/report.py | 3 +- imagepy/core/engine/simple.py | 2 +- imagepy/core/engine/table.py | 2 +- imagepy/core/util/__init__.py | 1 - imagepy/core/util/xlreport.py | 116 ------------------ imagepy/data/config.json | 2 +- .../plugins/File/{New.md => New Image.md} | 0 .../plugins/File/{New.md => New Image.md} | 0 .../English/tools/standard/Rectangle ROI.md | 1 + imagepy/lang/Chinese/plugins/Plugins.dic | 9 +- imagepy/lang/Chinese/plugins/window.dic | 23 +++- imagepy/lang/English/english.dic | 1 + imagepy/menus/File/BMP/bmp_plgs.py | 6 +- imagepy/menus/File/DAT/dat_plgs.py | 6 +- imagepy/menus/File/DICOM/dicom_plgs.py | 4 +- imagepy/menus/File/GIF/gif_plgs.py | 8 +- imagepy/menus/File/Import/sequence_plg.py | 2 +- imagepy/menus/File/JPG/jpg_plgs.py | 6 +- imagepy/menus/File/MAT/mat_plgs.py | 10 +- imagepy/menus/File/MarkDown/md_plg.py | 4 +- imagepy/menus/File/Numpy/ndarray_plgs.py | 10 +- imagepy/menus/File/Open Recent/recent_plgs.py | 4 +- imagepy/menus/File/PNG/png_plgs.py | 6 +- imagepy/menus/File/TIF/tif_plgs.py | 10 +- imagepy/menus/File/open_plg.py | 4 +- imagepy/menus/File/save_plg.py | 6 +- .../Contribute/Contributions/SimpleITK.md | 18 +-- imagepy/menus/Plugins/Macros/recorder_plg.py | 6 +- imagepy/menus/Plugins/Manager/plglist_wgt.py | 2 +- imagepy/menus/Plugins/Manager/plgtree_wgt.py | 5 +- imagepy/menus/Plugins/Manager/toltree_wgt.py | 7 +- imagepy/menus/Plugins/update_plg.py | 2 +- imagepy/menus/Table/Table IO/tableio_plgs.py | 10 +- imagepy/menus/Window/develop_wgts.py | 2 +- 43 files changed, 141 insertions(+), 214 deletions(-) rename imagepy/core/{util/fileio.py => engine/dataio.py} (95%) delete mode 100644 imagepy/core/util/__init__.py delete mode 100644 imagepy/core/util/xlreport.py rename imagepy/doc/Chinese/plugins/File/{New.md => New Image.md} (100%) rename imagepy/doc/English/plugins/File/{New.md => New Image.md} (100%) create mode 100644 imagepy/doc/English/tools/standard/Rectangle ROI.md create mode 100644 imagepy/lang/English/english.dic diff --git a/imagepy/__main__.py b/imagepy/__main__.py index 5ae21ab4..0eeed204 100644 --- a/imagepy/__main__.py +++ b/imagepy/__main__.py @@ -1,4 +1,4 @@ import sys -sys.path.append(r'../') +sys.path.append('../') from imagepy.core import app app.startup.start() \ No newline at end of file diff --git a/imagepy/core/app/imagej.py b/imagepy/core/app/imagej.py index 39cc75c0..cc942e93 100644 --- a/imagepy/core/app/imagej.py +++ b/imagepy/core/app/imagej.py @@ -137,6 +137,7 @@ def init_tool(self): def on_help(evt, tol): lang = Source.manager('config').get('language') doc = Source.manager('document').get(tol.title, tag=lang) + doc = doc or Source.manager('document').get(tol.title, tag='English') self.show_md(doc or 'No Document!', tol.title) self.toolbar.on_help = on_help self.toolbar.Fit() @@ -270,6 +271,7 @@ def show_table(self, tab, title=None): def show_plot(self, title): fig = PlotFrame(self) + fig.SetIcon(self.GetIcon()) fig.figure.title = title return fig diff --git a/imagepy/core/app/imagepy.py b/imagepy/core/app/imagepy.py index 85fea6c8..ef126e7f 100644 --- a/imagepy/core/app/imagepy.py +++ b/imagepy/core/app/imagepy.py @@ -39,7 +39,6 @@ def __init__( self, parent ): self.Layout() self.auimgr.Update() - self.Fit() self.Centre( wx.BOTH ) self.Bind(wx.EVT_CLOSE, self.on_close) @@ -138,6 +137,7 @@ def init_tool(self): def on_help(evt, tol): lang = Source.manager('config').get('language') doc = Source.manager('document').get(tol.title, tag=lang) + doc = doc or Source.manager('document').get(tol.title, tag='English') self.show_md(doc or 'No Document!', tol.title) self.toolbar.on_help = on_help self.toolbar.Fit() @@ -336,6 +336,7 @@ def show_table(self, tab, title=None): def show_plot(self, title): fig = PlotFrame(self) + fig.SetIcon(self.GetIcon()) fig.figure.title = title return fig @@ -404,7 +405,7 @@ def show_widget(self, panel, title='Widgets'): self.auimgr.AddPane(obj, aui.AuiPaneInfo().Caption(title).Left().Layer( 15 ).PinButton( True ) .Float().Resizable().FloatingSize( wx.DefaultSize ).Dockable(True)) #.DestroyOnClose()) lang = Source.manager('config').get('language') - dic = Source.manager('dictionary').get(obj.title, tag=lang) + dic = Source.manager('dictionary').get(obj.title, tag=lang) or {} info = self.auimgr.GetPane(obj) info.Show(True).Caption(dic[obj.title] if obj.title in dic else obj.title) self.translate(dic)(obj) @@ -547,7 +548,7 @@ def trans(frame): frame.SetTitle(lang(frame.GetTitle())) if isinstance(frame, wx.MessageDialog): frame.SetMessage(lang(frame.GetMessage())) - if isinstance(frame, wx.Notebook): + if hasattr(frame, 'SetPageText'): for i in range(frame.GetPageCount()): frame.SetPageText(i, lang(frame.GetPageText(i))) if hasattr(frame, 'Layout'): frame.Layout() diff --git a/imagepy/core/app/loader.py b/imagepy/core/app/loader.py index dd0be051..180fa86e 100644 --- a/imagepy/core/app/loader.py +++ b/imagepy/core/app/loader.py @@ -4,7 +4,8 @@ @author: yxl """ -import os, sys +import os, sys, os.path as osp +from glob import glob from ..engine import Macros, MkDown, Widget, Report from sciapp import Source from ... import root_dir @@ -29,7 +30,7 @@ def extend_plugins(path, lst, err): elif i[-3:] in {'.md', '.mc', '.wf'}: p = os.path.join(os.path.join(root_dir, path), i).replace('\\','/') rst.append(Macros(i[:-3], ['Open>{"path":"%s"}'%p])) - Source.manager('plugin').add(obj=rst[-1], name=rst[-1].title) + Source.manager('plugin').add(rst[-1].title, rst[-1]) elif i[-6:] in ['wgt.py', 'gts.py']: try: rpath = path.replace('/', '.').replace('\\','.') @@ -37,10 +38,9 @@ def extend_plugins(path, lst, err): if hasattr(plg, 'wgts'): rst.extend([j if j=='-' else Widget(j) for j in plg.wgts]) for p in plg.wgts: - if not isinstance(p, str):Source.manager('widget').add(obj=p, name=p.title) + if not isinstance(p, str):Source.manager('widget').add(p.title, p) else: rst.append(Widget(plg.Plugin)) - Source.manager('widget').add(obj=plg.Plugin, name=plg.Plugin.title) except Exception as e: err.append((path, i, sys.exc_info()[1])) else: @@ -50,10 +50,11 @@ def extend_plugins(path, lst, err): if hasattr(plg, 'plgs'): rst.extend([j for j in plg.plgs]) for p in plg.plgs: - if not isinstance(p, str): Source.manager('plugin').add(obj=p, name=p.title) + if not isinstance(p, str): + Source.manager('plugin').add(p.title, p) else: rst.append(plg.Plugin) - Source.manager('plugin').add(obj=plg.Plugin, name=plg.Plugin.title) + Source.manager('plugin').add(plg.Plugin.title, plg.Plugin) except Exception as e: err.append((path, i, sys.exc_info()[1])) return rst @@ -203,7 +204,7 @@ def build_widgets(path, err='root'): def build_document(path): docs = [] - for lang in Source.manager('document').get('language'): + for lang in [osp.split(i)[1] for i in glob(path+'/*') if osp.isdir(i)]: for dirpath, dirnames, filenames in os.walk(path+'/'+lang): for filename in filenames: if filename[-3:] != '.md': continue @@ -215,7 +216,7 @@ def build_document(path): return docs def build_dictionary(path): - for lang in Source.manager('dictionary').get('language'): + for lang in [osp.split(i)[1] for i in glob(path+'/*') if osp.isdir(i)]: for dirpath, dirnames, filenames in os.walk(path+'/'+lang): for filename in filenames: if filename[-3:] != 'dic': continue diff --git a/imagepy/core/app/startup.py b/imagepy/core/app/startup.py index 123f3b0b..ea5b70f3 100644 --- a/imagepy/core/app/startup.py +++ b/imagepy/core/app/startup.py @@ -63,12 +63,19 @@ def load_widgets(): return extend_wgts(data[:2]), data[2] def load_document(): - Source.manager('document').add('language', os.listdir(root_dir+'/doc')) - loader.build_document(root_dir+'/doc/') + docs = [root_dir+'/doc'] + docs += glob(root_dir+'/plugins/*/doc') + for i in docs:loader.build_document(i) def load_dictionary(): - Source.manager('dictionary').add('language', os.listdir(root_dir+'/lang')) - loader.build_dictionary(root_dir+'/lang/') + lans = glob(root_dir+'/lang/*') + lans += glob(root_dir+'/plugins/*/lang/*') + lans = [i for i in lans if os.path.isdir(i)] + lans = [os.path.split(i) for i in lans] + lan = sorted(set([i[1] for i in lans])) + Source.manager('dictionary').add('language', lan) + lans = sorted(set([i[0] for i in lans])) + for i in lans: loader.build_dictionary(i) def start(): from imagepy.core.app import ImagePy, ImageJ @@ -82,7 +89,6 @@ def start(): AS.AS_SHADOW_BITMAP, shadowcolour=shadow) asp.Update() - print('d') load_document() load_dictionary() uistyle = Source.manager('config').get('uistyle') or 'imagepy' diff --git a/imagepy/core/engine/__init__.py b/imagepy/core/engine/__init__.py index 84798dcb..3036a2c5 100644 --- a/imagepy/core/engine/__init__.py +++ b/imagepy/core/engine/__init__.py @@ -5,4 +5,5 @@ from .mkdown import MkDown from .widget import Widget from .table import Table -from .report import Report \ No newline at end of file +from .report import Report +from .dataio import * \ No newline at end of file diff --git a/imagepy/core/util/fileio.py b/imagepy/core/engine/dataio.py similarity index 95% rename from imagepy/core/util/fileio.py rename to imagepy/core/engine/dataio.py index 44507f37..b71f8aa2 100644 --- a/imagepy/core/util/fileio.py +++ b/imagepy/core/engine/dataio.py @@ -1,10 +1,12 @@ import os from sciapp import Source -from ... import root_dir -from ..engine import Free, Simple, Table, Macros +from imagepy import root_dir +from .free import Free +from .simple import Simple +from .table import Table +from .macros import Macros import numpy as np - # ViewerManager.add('imgs', IPy.show_img) recent = Source.manager('config').get('recent') if recent==None : recent = [] diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index 4c17b5ae..96936954 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -160,6 +160,7 @@ def ok(self, ips, para=None, callafter=None): def on_help(self): lang = Source.manager('config').get('language') doc = Source.manager('document').get(self.title, tag=lang) + doc = doc or Source.manager('document').get(tol.title, tag='English') self.app.show_md(doc or 'No Document!', self.title) def cancel(self, ips): diff --git a/imagepy/core/engine/free.py b/imagepy/core/engine/free.py index f9fa3d53..e4bc7e2a 100644 --- a/imagepy/core/engine/free.py +++ b/imagepy/core/engine/free.py @@ -32,6 +32,7 @@ def load(self):return True def on_help(self): lang = Source.manager('config').get('language') doc = Source.manager('document').get(self.title, tag=lang) + doc = doc or Source.manager('document').get(tol.title, tag='English') self.app.show_md(doc or 'No Document!', self.title) def show(self): diff --git a/imagepy/core/engine/report.py b/imagepy/core/engine/report.py index 2e7240ae..e7461b8b 100644 --- a/imagepy/core/engine/report.py +++ b/imagepy/core/engine/report.py @@ -3,10 +3,9 @@ Created on Thu Dec 29 01:48:23 2016 @author: yxl """ -import wx from sciapp import Source #from imagepy.ui.propertygrid import GridDialog -from imagepy.core.util import xlreport +#from imagepy.core.util import xlreport from time import time import openpyxl as pyxl from sciapp import Source diff --git a/imagepy/core/engine/simple.py b/imagepy/core/engine/simple.py index 706344ca..f073cca3 100644 --- a/imagepy/core/engine/simple.py +++ b/imagepy/core/engine/simple.py @@ -3,7 +3,6 @@ Created on Sat Dec 3 03:32:05 2016 @author: yxl """ -import wx import threading from sciapp import Source @@ -42,6 +41,7 @@ def cancel(self, ips):pass def on_help(self): lang = Source.manager('config').get('language') doc = Source.manager('document').get(self.title, tag=lang) + doc = doc or Source.manager('document').get(tol.title, tag='English') self.app.show_md(doc or 'No Document!', self.title) def ok(self, ips, para=None, callafter=None): diff --git a/imagepy/core/engine/table.py b/imagepy/core/engine/table.py index 20be2323..0c898b22 100644 --- a/imagepy/core/engine/table.py +++ b/imagepy/core/engine/table.py @@ -3,7 +3,6 @@ Created on Sat Dec 3 03:32:05 2016 @author: yxl """ -import wx import threading from time import time @@ -46,6 +45,7 @@ def cancel(self, tps): def on_help(self): lang = Source.manager('config').get('language') doc = Source.manager('document').get(self.title, tag=lang) + doc = doc or Source.manager('document').get(tol.title, tag='English') self.app.show_md(doc or 'No Document!', self.title) def ok(self, tps, para=None, callafter=None): diff --git a/imagepy/core/util/__init__.py b/imagepy/core/util/__init__.py deleted file mode 100644 index f8491474..00000000 --- a/imagepy/core/util/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .fileio import * \ No newline at end of file diff --git a/imagepy/core/util/xlreport.py b/imagepy/core/util/xlreport.py deleted file mode 100644 index 18b3931c..00000000 --- a/imagepy/core/util/xlreport.py +++ /dev/null @@ -1,116 +0,0 @@ -import openpyxl as pyxl -from openpyxl.utils.units import cm_to_EMU, EMU_to_pixels -from io import BytesIO -from openpyxl.drawing.image import Image -from PIL import Image as PImage -import numpy as np -import pandas as pd -from copy import copy - -if not '.rpt' in pyxl.reader.excel.SUPPORTED_FORMATS: - pyxl.reader.excel.SUPPORTED_FORMATS += ('.rpt',) - -def parse(wb): - rst, key = [], {} - for ws in wb.worksheets: - rst.append((ws.title, [])) - for row in ws.rows: - for cell in row: - if not isinstance(cell.value, str):continue - if cell.value[0]+cell.value[-1] != '{}': continue - cont = cell.value[1:-1].strip() - tp = cont.split(' ')[0] - cont = cont[len(tp):].strip() - note, value = 'no description', None - if '#' in cont: - note = cont.split('#')[-1].strip() - cont = cont[:cont.index('#')].strip() - if '=' in cont: - value = cont.split('=')[1].strip() - name = cont[:cont.index('=')].strip() - else: name = cont - - rst[-1][-1].append(((cell.row, cell.col_idx), - [tp, name, value, note])) - key[name] = [tp, name, value, note] - return rst, key - -def trans(img, W, H, margin, scale): - h, w = img.shape[:2] - h2, w2 = int(h/margin), int(w/margin) - if scale: - if W/H > w/h: w2 = int(W/H*h2) - if H/W > h/w: h2 = int(H/W*w2) - newshp = (h2, w2) if img.ndim==2 else (h2, w2, 3) - blank = np.ones(newshp, dtype=np.uint8) * 255 - blank[(h2-h)//2:(h2-h)//2+h, (w2-w)//2:(w2-w)//2+w] = img - return blank - -def add_image(wb, ws, pos, key, img): - if img is None: return - w, h, margin, scale = eval(key[2]) - img = trans(img, w, h, margin, scale==0) - img = PImage.fromarray(img) - image_file = BytesIO() - img.save(image_file, 'png') - ref = BytesIO(image_file.getvalue()) - image = Image(img) - image.ref = ref - image.height = EMU_to_pixels(cm_to_EMU(h)) - image.width = EMU_to_pixels(cm_to_EMU(w)) - wb[ws].add_image(image, wb[ws].cell(*pos).coordinate) - -def add_table(wb, ws, pos, key, data): - if data is None: return - vs = data.values - idx, cols = data.index, data.columns - dr, dc, ir, ic = 1, 1, 0, 0 - if key[2] != None: dr, dc, ir, ic = eval(key[2]) - for r in range(vs.shape[0]): - if ir!=0: wb[ws].cell(pos[0]+r*dr, pos[1]+ir, idx[r]) - for c in range(vs.shape[1]): - if ic!=0: wb[ws].cell(pos[0]+ic, pos[1]+c*dc, cols[c]) - for r in range(vs.shape[0]): - for c in range(vs.shape[1]): - wb[ws].cell(pos[0]+r*dr, pos[1]+c*dc, vs[r,c]) - -def fill_value(wb, infos, para): - for worksheet in infos: - ws, info = worksheet - for pos, key in info: - if not key[1] in para: continue - if key[0] in ('str', 'int', 'float', 'bool', 'txt', 'list', 'date'): - wb[ws].cell(pos[0], pos[1], para[key[1]]) - if key[0] == 'img': - add_image(wb, ws, pos, key, para[key[1]]) - if key[0] == 'tab': - add_table(wb, ws, pos, key, para[key[1]]) - -def repair(wb): - for ws in wb.worksheets: - for cr in ws.merged_cells: - ltc = ws.cell(cr.min_row, cr.min_col) - vb, hb = ltc.border.left, ltc.border.top - for r in range(cr.min_row, cr.max_row+1): - for c in range(cr.min_col, cr.max_col+1): - cur = copy(ws.cell(r, c).border) - cur.left, cur.right = copy(vb), copy(vb) - cur.top, cur.bottom = copy(hb), copy(hb) - ws.cell(r, c).border = cur - -if __name__ == '__main__': - rst = pd.read_csv('rst.csv') - img = np.arange(10000, dtype=np.uint8).reshape((100,100)) - data = {'Sample_ID':'Coins-0001', 'Operator_Name':'YX Dragon', 'Date':'2019-02-05', - 'Record':rst, 'Original_Image':img, 'Mask_Image':img} - - wb = pyxl.load_workbook('Coins Report.xlsx',) - repair(wb) - ws = wb.active - - - infos = parse(wb) - print(infos) - fill_value(wb, infos, data) - wb.save('new.xlsx') - diff --git a/imagepy/data/config.json b/imagepy/data/config.json index 4326ffe4..81cb8a5f 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["uistyle", "imagepy", null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["recent", ["C:/Users/54631/AppData/Roaming/Tencent/QQ/Temp/QQ5LIOM0M5D%2)@GY8256`T.jpg", "c:/users/54631/documents/projects/imagepy/imagepy/menus/Plugins/Contribute/Contributions/CellPose.md", "C:\\Users\\12957\\Pictures\\computer-museum[1].png", "C:/Users/54631/Desktop/Workflow Coins Demo.wf"], null], ["language", "Chinese", null]] \ No newline at end of file +[["uistyle", "imagepy", null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["language", "English", null], ["recent", ["C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc", "C:/Users/Administrator/Desktop/imagepy/imagepy/menus/File/Samples Online/DEM.mc"], null]] \ No newline at end of file diff --git a/imagepy/doc/Chinese/plugins/File/New.md b/imagepy/doc/Chinese/plugins/File/New Image.md similarity index 100% rename from imagepy/doc/Chinese/plugins/File/New.md rename to imagepy/doc/Chinese/plugins/File/New Image.md diff --git a/imagepy/doc/English/plugins/File/New.md b/imagepy/doc/English/plugins/File/New Image.md similarity index 100% rename from imagepy/doc/English/plugins/File/New.md rename to imagepy/doc/English/plugins/File/New Image.md diff --git a/imagepy/doc/English/tools/standard/Rectangle ROI.md b/imagepy/doc/English/tools/standard/Rectangle ROI.md new file mode 100644 index 00000000..9499f508 --- /dev/null +++ b/imagepy/doc/English/tools/standard/Rectangle ROI.md @@ -0,0 +1 @@ +# Rectangel ROI Tool \ No newline at end of file diff --git a/imagepy/lang/Chinese/plugins/Plugins.dic b/imagepy/lang/Chinese/plugins/Plugins.dic index 7b0ad8cb..2b2bbe98 100644 --- a/imagepy/lang/Chinese/plugins/Plugins.dic +++ b/imagepy/lang/Chinese/plugins/Plugins.dic @@ -8,7 +8,7 @@ New Tool::新建Tool Macros::宏 -Macros Recorder::录制宏 +Macros Recorder::宏录制器 Run Macros::运行宏 @@ -31,12 +31,15 @@ Plugin Tree View::插件树浏览 [SourceCode]::[查看源码] Tool Tree View::工具树浏览 + Tool Information::插件信息 + [SourceCode]::[查看源码] Plugin List View::插件列表 + Search::搜索 -Shortcut Editor::编辑快捷键 +Shortcut Editor::快捷键编辑器 -Command Line::打开命令行 +Command Line::命令行 Install::安装 diff --git a/imagepy/lang/Chinese/plugins/window.dic b/imagepy/lang/Chinese/plugins/window.dic index 0c0a6191..efde03c5 100644 --- a/imagepy/lang/Chinese/plugins/window.dic +++ b/imagepy/lang/Chinese/plugins/window.dic @@ -33,4 +33,25 @@ Plugin Tree View::插件树浏览 Tool Tree View::工具树形图 -Develop Tool Sute::开发工具集 \ No newline at end of file +Develop Tool Sute::开发工具集 + Plugin Tree View::插件树浏览 + Tool Tree View::工具树浏览 + Plugin List View::插件列表 + Shortcut Editor::快捷键编辑器 + Macros Recorder::宏录制器 + Command Line::命令行 + Search::搜索 + Name::名称 + Author::作者 + Version::版本 + Status::状态 + installed::已安装 + only installed::只显示已安装 + Install::安装 + Update::更新 + Remove::移除 + Plugin Information::插件信息 + [SourceCode]::[查看源码] + Tool Information::插件信息 + [SourceCode]::[查看源码] + Search::搜索 \ No newline at end of file diff --git a/imagepy/lang/English/english.dic b/imagepy/lang/English/english.dic new file mode 100644 index 00000000..3bdfed2f --- /dev/null +++ b/imagepy/lang/English/english.dic @@ -0,0 +1 @@ +Nothing::English need nothing to be translated! \ No newline at end of file diff --git a/imagepy/menus/File/BMP/bmp_plgs.py b/imagepy/menus/File/BMP/bmp_plgs.py index fbfa0446..6d801e0a 100644 --- a/imagepy/menus/File/BMP/bmp_plgs.py +++ b/imagepy/menus/File/BMP/bmp_plgs.py @@ -1,16 +1,16 @@ -from imagepy.core.util import fileio +from imagepy.core.engine import dataio from skimage.io import imread, imsave from sciapp import Source Source.manager('reader').add('bmp', imread, 'img') Source.manager('writer').add('bmp', imsave, 'img') -class OpenFile(fileio.Reader): +class OpenFile(dataio.Reader): title = 'BMP Open' tag = 'img' filt = ['BMP'] -class SaveFile(fileio.ImageWriter): +class SaveFile(dataio.ImageWriter): title = 'BMP Save' tag = 'img' filt = ['BMP'] diff --git a/imagepy/menus/File/DAT/dat_plgs.py b/imagepy/menus/File/DAT/dat_plgs.py index 78839f4c..0765da67 100644 --- a/imagepy/menus/File/DAT/dat_plgs.py +++ b/imagepy/menus/File/DAT/dat_plgs.py @@ -1,4 +1,4 @@ -from imagepy.core.util import fileio +from imagepy.core.engine import dataio import numpy as np from sciapp import Source @@ -11,12 +11,12 @@ def imsave(path,img): Source.manager('reader').add('dat', imread, 'img') Source.manager('writer').add('dat', imsave, 'img') -class OpenFile(fileio.Reader): +class OpenFile(dataio.Reader): title = 'DAT Open' tag = 'img' filt = ['DAT'] -class SaveFile(fileio.ImageWriter): +class SaveFile(dataio.ImageWriter): title = 'DAT Save' tag = 'img' filt = ['DAT'] diff --git a/imagepy/menus/File/DICOM/dicom_plgs.py b/imagepy/menus/File/DICOM/dicom_plgs.py index 1d5649e8..0f01b9e9 100644 --- a/imagepy/menus/File/DICOM/dicom_plgs.py +++ b/imagepy/menus/File/DICOM/dicom_plgs.py @@ -1,4 +1,4 @@ -from imagepy.core.util import fileio +from imagepy.core.engine import dataio import pydicom from sciapp import Source @@ -9,7 +9,7 @@ def imread(path): Source.manager('reader').add('dcm', imread, 'img') -class OpenFile(fileio.Reader): +class OpenFile(dataio.Reader): title = 'DCM Open' filt = ['DCM'] tag = 'img' diff --git a/imagepy/menus/File/GIF/gif_plgs.py b/imagepy/menus/File/GIF/gif_plgs.py index 648f9670..93ae0f35 100644 --- a/imagepy/menus/File/GIF/gif_plgs.py +++ b/imagepy/menus/File/GIF/gif_plgs.py @@ -1,4 +1,4 @@ -from imagepy.core.util import fileio +from imagepy.core.engine import dataio from imagepy.core.engine import Simple from skimage.io import imread, imsave from sciapp import Source @@ -8,12 +8,12 @@ Source.manager('writer').add('gif', imsave, 'img') Source.manager('reader').add('gif', imageio.mimread, 'imgs') -class OpenFile(fileio.Reader): +class OpenFile(dataio.Reader): title = 'GIF Open' tag = 'img' filt = ['GIF'] -class SaveFile(fileio.ImageWriter): +class SaveFile(dataio.ImageWriter): title = 'GIF Save' tag = 'img' filt = ['GIF'] @@ -32,7 +32,7 @@ def load(self, ips): def run(self, ips, imgs, para = None): imageio.mimsave(para['path'], imgs, 'gif', duration = para['dur']) -class OpenAnimate(fileio.Reader): +class OpenAnimate(dataio.Reader): title = 'GIF Animate Open' filt = ['GIF'] tag = 'imgs' diff --git a/imagepy/menus/File/Import/sequence_plg.py b/imagepy/menus/File/Import/sequence_plg.py index 227cf9d0..b94666cb 100644 --- a/imagepy/menus/File/Import/sequence_plg.py +++ b/imagepy/menus/File/Import/sequence_plg.py @@ -4,7 +4,7 @@ @author: yxl """ -from imagepy.core.util import fileio +from imagepy.core.engine import dataio from skimage.io import imread from sciapp import Source from imagepy.core.engine import Free diff --git a/imagepy/menus/File/JPG/jpg_plgs.py b/imagepy/menus/File/JPG/jpg_plgs.py index b8ed873c..5805fb81 100644 --- a/imagepy/menus/File/JPG/jpg_plgs.py +++ b/imagepy/menus/File/JPG/jpg_plgs.py @@ -1,4 +1,4 @@ -from imagepy.core.util import fileio +from imagepy.core.engine import dataio from imageio import imread, imsave from sciapp import Source @@ -7,12 +7,12 @@ Source.manager('reader').add('jpeg', imread, 'img') Source.manager('writer').add('jpeg', imsave, 'img') -class OpenFile(fileio.Reader): +class OpenFile(dataio.Reader): title = 'JPG Open' tag = 'img' filt = ['JPG','JPEG'] -class SaveFile(fileio.ImageWriter): +class SaveFile(dataio.ImageWriter): title = 'JPG Save' tag = 'img' filt = ['JPG','JPEG'] diff --git a/imagepy/menus/File/MAT/mat_plgs.py b/imagepy/menus/File/MAT/mat_plgs.py index d6ddd6bd..8ab71ed7 100644 --- a/imagepy/menus/File/MAT/mat_plgs.py +++ b/imagepy/menus/File/MAT/mat_plgs.py @@ -1,4 +1,4 @@ -from imagepy.core.util import fileio +from imagepy.core.engine import dataio from scipy.io import savemat, loadmat from sciapp import Source import os @@ -8,22 +8,22 @@ Source.manager('reader').add('mat', lambda path: loadmat(path)['img'], 'imgs') Source.manager('writer').add('mat', lambda path, img: savemat(path, {'img':img}), 'imgs') -class OpenFile(fileio.Reader): +class OpenFile(dataio.Reader): title = 'Mat Open' tag = 'img' filt = ['Mat'] -class SaveFile(fileio.ImageWriter): +class SaveFile(dataio.ImageWriter): title = 'Mat Save' tag = 'img' filt = ['Mat'] -class Open3D(fileio.Reader): +class Open3D(dataio.Reader): title = 'Mat 3D Open' tag = 'imgs' filt = ['Mat'] -class Save3D(fileio.ImageWriter): +class Save3D(dataio.ImageWriter): title = 'Mat 3D Save' tag = 'imgs' filt = ['Mat'] diff --git a/imagepy/menus/File/MarkDown/md_plg.py b/imagepy/menus/File/MarkDown/md_plg.py index b28a6f50..79eb5ae9 100644 --- a/imagepy/menus/File/MarkDown/md_plg.py +++ b/imagepy/menus/File/MarkDown/md_plg.py @@ -1,4 +1,4 @@ -from imagepy.core.util import fileio +from imagepy.core.engine import dataio from sciapp import Source def read(path): @@ -6,7 +6,7 @@ def read(path): Source.manager('reader').add('md', read, 'md') -class Plugin(fileio.Reader): +class Plugin(dataio.Reader): title = 'MarkDown Open' tag = 'md' filt = ['MD'] \ No newline at end of file diff --git a/imagepy/menus/File/Numpy/ndarray_plgs.py b/imagepy/menus/File/Numpy/ndarray_plgs.py index 23b8eaeb..bea37aba 100644 --- a/imagepy/menus/File/Numpy/ndarray_plgs.py +++ b/imagepy/menus/File/Numpy/ndarray_plgs.py @@ -1,4 +1,4 @@ -from imagepy.core.util import fileio +from imagepy.core.engine import dataio import numpy as np from sciapp import Source import os @@ -8,22 +8,22 @@ Source.manager('reader').add('npy', np.load, 'imgs') Source.manager('writer').add('npy', np.save, 'imgs') -class OpenFile(fileio.Reader): +class OpenFile(dataio.Reader): title = 'Numpy Open' tag = 'img' filt = ['npy'] -class SaveFile(fileio.ImageWriter): +class SaveFile(dataio.ImageWriter): title = 'Numpy Save' tag = 'img' filt = ['npy'] -class Open3D(fileio.Reader): +class Open3D(dataio.Reader): title = 'Numpy 3D Open' tag = 'imgs' filt = ['npy'] -class Save3D(fileio.ImageWriter): +class Save3D(dataio.ImageWriter): title = 'Numpy 3D Save' tag = 'imgs' filt = ['npy'] diff --git a/imagepy/menus/File/Open Recent/recent_plgs.py b/imagepy/menus/File/Open Recent/recent_plgs.py index 41973ade..8bd91f3a 100644 --- a/imagepy/menus/File/Open Recent/recent_plgs.py +++ b/imagepy/menus/File/Open Recent/recent_plgs.py @@ -1,3 +1,3 @@ -from imagepy.core.util import fileio +from imagepy.core.engine import dataio -plgs = fileio.rlist \ No newline at end of file +plgs = dataio.rlist \ No newline at end of file diff --git a/imagepy/menus/File/PNG/png_plgs.py b/imagepy/menus/File/PNG/png_plgs.py index ad6d426b..029b4cd5 100644 --- a/imagepy/menus/File/PNG/png_plgs.py +++ b/imagepy/menus/File/PNG/png_plgs.py @@ -1,16 +1,16 @@ -from imagepy.core.util import fileio +from imagepy.core.engine import dataio from skimage.io import imread, imsave from sciapp import Source Source.manager('reader').add('png', imread, 'img') Source.manager('writer').add('png', imsave, 'img') -class OpenFile(fileio.Reader): +class OpenFile(dataio.Reader): title = 'PNG Open' tag = 'img' filt = ['PNG'] -class SaveFile(fileio.ImageWriter): +class SaveFile(dataio.ImageWriter): title = 'PNG Save' tag = 'img' filt = ['PNG'] diff --git a/imagepy/menus/File/TIF/tif_plgs.py b/imagepy/menus/File/TIF/tif_plgs.py index 5f9b6749..0e358a34 100644 --- a/imagepy/menus/File/TIF/tif_plgs.py +++ b/imagepy/menus/File/TIF/tif_plgs.py @@ -1,4 +1,4 @@ -from imagepy.core.util import fileio +from imagepy.core.engine import dataio from skimage.io import imread, imsave from sciapp import Source @@ -10,22 +10,22 @@ Source.manager('reader').add('tiff', imread, 'imgs') Source.manager('writer').add('tif', imsave, 'imgs') -class OpenTIF(fileio.Reader): +class OpenTIF(dataio.Reader): title = 'TIF Open' tag = 'img' filt = ['TIF', 'TIFF'] -class SaveTIF(fileio.ImageWriter): +class SaveTIF(dataio.ImageWriter): title = 'TIF Save' tag = 'img' filt = ['TIF'] -class OpenTIFS(fileio.Reader): +class OpenTIFS(dataio.Reader): title = 'TIF 3D Open' tag = 'imgs' filt = ['TIF', 'TIFF'] -class SaveTIFS(fileio.ImageWriter): +class SaveTIFS(dataio.ImageWriter): title = 'TIF 3D Save' tag = 'imgs' filt = ['TIF'] diff --git a/imagepy/menus/File/open_plg.py b/imagepy/menus/File/open_plg.py index bec6356d..a8ad97f7 100644 --- a/imagepy/menus/File/open_plg.py +++ b/imagepy/menus/File/open_plg.py @@ -5,10 +5,10 @@ from io import BytesIO as StringIO from imagepy.core.engine import Free -from imagepy.core.util import fileio +from imagepy.core.engine import dataio from sciapp import Source -class OpenFile(fileio.Reader): +class OpenFile(dataio.Reader): title = 'Open' def load(self): diff --git a/imagepy/menus/File/save_plg.py b/imagepy/menus/File/save_plg.py index a1cd2744..9d41f598 100644 --- a/imagepy/menus/File/save_plg.py +++ b/imagepy/menus/File/save_plg.py @@ -3,18 +3,18 @@ Created on Mon Dec 5 03:19:13 2016 @author: yxl """ -from imagepy.core.util import fileio +from imagepy.core.engine import dataio from sciapp import Source from imagepy.core.engine import Simple -class SaveImage(fileio.ImageWriter): +class SaveImage(dataio.ImageWriter): title = 'Save' def load(self, ips): self.filt = [i for i in sorted(Source.manager('writer').names())] return True -class WindowCapture(fileio.ImageWriter): +class WindowCapture(dataio.ImageWriter): title = 'Save With Mark' filt = ['PNG'] diff --git a/imagepy/menus/Plugins/Contribute/Contributions/SimpleITK.md b/imagepy/menus/Plugins/Contribute/Contributions/SimpleITK.md index 1a73c23c..b1d49e00 100644 --- a/imagepy/menus/Plugins/Contribute/Contributions/SimpleITK.md +++ b/imagepy/menus/Plugins/Contribute/Contributions/SimpleITK.md @@ -53,29 +53,29 @@ def write(path, img): ``` ### register reader and writer to the io manager ```python -from imagepy.core.util import fileio +from imagepy.core.engine import dataio # add dicom reader and writer -fileio.add_reader(['dcm'], read) -fileio.add_writer(['dcm'], write) +dataio.add_reader(['dcm'], read) +dataio.add_writer(['dcm'], write) -class OpenDCM(fileio.Reader): +class OpenDCM(dataio.Reader): title = 'DICOM Open' filt = ['DCM'] -class SaveDCM(fileio.Writer): +class SaveDCM(dataio.Writer): title = 'DICOM Save' filt = ['DCM'] # add nii reader and writer, because nii is a sequence, so ruse read all, and give as a tuple. -fileio.add_reader(['nii'], (readall,)) -fileio.add_writer(['nii'], (write,)) +dataio.add_reader(['nii'], (readall,)) +dataio.add_writer(['nii'], (write,)) -class OpenNII(fileio.Reader): +class OpenNII(dataio.Reader): title = 'NII Open' filt = ['NII'] -class SaveNII(fileio.Reader): +class SaveNII(dataio.Reader): title = 'NII Save' filt = ['NII'] diff --git a/imagepy/menus/Plugins/Macros/recorder_plg.py b/imagepy/menus/Plugins/Macros/recorder_plg.py index 20d3d091..3638aaea 100644 --- a/imagepy/menus/Plugins/Macros/recorder_plg.py +++ b/imagepy/menus/Plugins/Macros/recorder_plg.py @@ -1,4 +1,4 @@ -from imagepy.core.util import fileio +from imagepy.core.engine import dataio from sciapp import Source def readmc(path): @@ -6,7 +6,7 @@ def readmc(path): Source.manager('reader').add('mc', readmc, 'mc') -class Macros(fileio.Reader): +class Macros(dataio.Reader): title = 'Run Macros' tag = 'mc' filt = ['MC'] @@ -16,7 +16,7 @@ def readwf(path): Source.manager('reader').add('wf', readwf, 'wf') -class WorkFlow(fileio.Reader): +class WorkFlow(dataio.Reader): title = 'Run WorkFlow' tag = 'wf' filt = ['wf'] diff --git a/imagepy/menus/Plugins/Manager/plglist_wgt.py b/imagepy/menus/Plugins/Manager/plglist_wgt.py index f8102a76..0082c7d3 100644 --- a/imagepy/menus/Plugins/Manager/plglist_wgt.py +++ b/imagepy/menus/Plugins/Manager/plglist_wgt.py @@ -43,7 +43,7 @@ def __init__( self, parent, app=None): self.app = app bSizer1 = wx.BoxSizer( wx.VERTICAL ) bSizer2 = wx.BoxSizer( wx.HORIZONTAL ) - self.m_staticText1 = wx.StaticText( self, wx.ID_ANY, "Search:", + self.m_staticText1 = wx.StaticText( self, wx.ID_ANY, "Search", wx.DefaultPosition, wx.DefaultSize, 0 ) self.m_staticText1.Wrap( -1 ) bSizer2.Add( self.m_staticText1, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) diff --git a/imagepy/menus/Plugins/Manager/plgtree_wgt.py b/imagepy/menus/Plugins/Manager/plgtree_wgt.py index 24070fb8..87716507 100644 --- a/imagepy/menus/Plugins/Manager/plgtree_wgt.py +++ b/imagepy/menus/Plugins/Manager/plgtree_wgt.py @@ -103,8 +103,9 @@ def on_select( self, event ): self.plg = plg name = self.tre_plugins.GetItemText(event.GetItem()) lang = Source.manager('config').get('language') - cont = Source.manager('document').get(name, tag=lang) - self.txt_info.set_cont(cont or 'No Document!') + doc = Source.manager('document').get(name, tag=lang) + doc = doc or Source.manager('document').get(name, tag='English') + self.txt_info.set_cont(doc or 'No Document!') def on_source(self, event): ## TODO: should it be absolute path ? diff --git a/imagepy/menus/Plugins/Manager/toltree_wgt.py b/imagepy/menus/Plugins/Manager/toltree_wgt.py index d98cd1ae..a3c45c2f 100644 --- a/imagepy/menus/Plugins/Manager/toltree_wgt.py +++ b/imagepy/menus/Plugins/Manager/toltree_wgt.py @@ -33,7 +33,7 @@ def __init__( self, parent, app=None): bSizer3 = wx.BoxSizer( wx.VERTICAL ) bSizer4 = wx.BoxSizer( wx.HORIZONTAL ) - self.m_staticText2 = wx.StaticText( self, wx.ID_ANY, "Tool Infomation:", + self.m_staticText2 = wx.StaticText( self, wx.ID_ANY, "Tool Information", wx.DefaultPosition, wx.DefaultSize, 0 ) self.m_staticText2.Wrap( -1 ) bSizer4.Add( self.m_staticText2, 0, wx.ALL, 5 ) @@ -100,7 +100,10 @@ def on_select( self, event ): if plg!=None: self.plg = plg name = self.tre_plugins.GetItemText(event.GetItem()) - self.txt_info.set_cont(Source.manager('document').get(name)) + lang = Source.manager('config').get('language') + doc = Source.manager('document').get(name, tag=lang) + doc = doc or Source.manager('document').get(name, tag='English') + self.txt_info.set_cont(doc or 'No Document!') def on_source(self, event): ## TODO: should it be absolute path ? diff --git a/imagepy/menus/Plugins/update_plg.py b/imagepy/menus/Plugins/update_plg.py index a9395d20..c1c93e26 100644 --- a/imagepy/menus/Plugins/update_plg.py +++ b/imagepy/menus/Plugins/update_plg.py @@ -53,6 +53,6 @@ class Refresh(Free): title = 'Reload Plugins' def run(self, para=None): - IPy.reload_plgs(True, True, True, True) + self.app.load_all() plgs = [Update, Refresh] diff --git a/imagepy/menus/Table/Table IO/tableio_plgs.py b/imagepy/menus/Table/Table IO/tableio_plgs.py index a6d70aef..6645a2ff 100644 --- a/imagepy/menus/Table/Table IO/tableio_plgs.py +++ b/imagepy/menus/Table/Table IO/tableio_plgs.py @@ -1,4 +1,4 @@ -from imagepy.core.util import fileio +from imagepy.core.engine import dataio from pandas import read_csv, read_excel, read_hdf from sciapp import Source @@ -11,12 +11,12 @@ def show(data, title): Source.manager('reader').add('csv', read_csv, 'tab') Source.manager('writer').add('csv', save_csv, 'tab') -class OpenCSV(fileio.Reader): +class OpenCSV(dataio.Reader): title = 'CSV Open' tag = 'tab' filt = ['csv'] -class SaveCSV(fileio.TableWriter): +class SaveCSV(dataio.TableWriter): title = 'CSV Save' tag = 'tab' filt = ['csv'] @@ -27,12 +27,12 @@ class SaveCSV(fileio.TableWriter): Source.manager('reader').add('xlsx', read_excel, 'tab') Source.manager('writer').add('xlsx', save_excel, 'tab') -class OpenExcel(fileio.Reader): +class OpenExcel(dataio.Reader): title = 'Excel Open' tag = 'tab' filt = ['xls','xlsx'] -class SaveExcel(fileio.TableWriter): +class SaveExcel(dataio.TableWriter): title = 'Excel Save' tag = 'tab' filt = ['xls', 'xlsx'] diff --git a/imagepy/menus/Window/develop_wgts.py b/imagepy/menus/Window/develop_wgts.py index 4e0938f6..d650d8f8 100644 --- a/imagepy/menus/Window/develop_wgts.py +++ b/imagepy/menus/Window/develop_wgts.py @@ -9,7 +9,7 @@ class DevelopToolSute ( wx.Panel ): title = 'Develop Tool Sute' single = True - def __init__( self, parent ): + def __init__( self, parent, app=None): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 300,200), style = wx.TAB_TRAVERSAL ) sizer = wx.BoxSizer( wx.VERTICAL ) From e35ad8ce3f73ccc146676c5c48e31b3dc466a53c Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 25 Jun 2020 22:14:14 +0800 Subject: [PATCH 268/343] refactor --- imagepy/data/config.json | 2 +- imagepy/menus/Selection/select_plg.py | 3 +- sciapp/app.py | 23 +- sciapp/object/roi.py | 2 +- sciapp/object/shape.py | 12 - sciapp/util/__init__.py | 3 +- .../imutil.py => sciapp/util/imgutil.py | 66 ++++- sciapp/util/shputil.py | 2 +- sciwx/canvas/boxutil.py | 63 ----- sciwx/canvas/canvas.py | 3 +- sciwx/canvas/test.py | 258 ------------------ sciwx/demo/canvas1_demo.py | 14 - 12 files changed, 82 insertions(+), 369 deletions(-) rename sciwx/canvas/imutil.py => sciapp/util/imgutil.py (82%) delete mode 100644 sciwx/canvas/boxutil.py delete mode 100644 sciwx/canvas/test.py diff --git a/imagepy/data/config.json b/imagepy/data/config.json index 81cb8a5f..c6b5e5b7 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["uistyle", "imagepy", null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["language", "English", null], ["recent", ["C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc", "C:/Users/Administrator/Desktop/imagepy/imagepy/menus/File/Samples Online/DEM.mc"], null]] \ No newline at end of file +[["uistyle", "imagepy", null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["recent", ["C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc", "C:/Users/Administrator/Desktop/imagepy/imagepy/menus/File/Samples Online/DEM.mc"], null], ["language", "Chinese", null]] \ No newline at end of file diff --git a/imagepy/menus/Selection/select_plg.py b/imagepy/menus/Selection/select_plg.py index 19911007..78925906 100644 --- a/imagepy/menus/Selection/select_plg.py +++ b/imagepy/menus/Selection/select_plg.py @@ -1,6 +1,7 @@ from imagepy.core.engine import Simple, Free from sciapp import Source -from sciapp.object import ROI, geom2shp, geom_flatten, Rectangle, geom_union, mark2shp +from sciapp.object import ROI, Rectangle +from sciapp.util.shputil import geom2shp, geom_flatten, geom_union, mark2shp import json, time class SelectAll(Simple): diff --git a/sciapp/app.py b/sciapp/app.py index f9fa1e12..3020ad41 100644 --- a/sciapp/app.py +++ b/sciapp/app.py @@ -9,6 +9,10 @@ def add(self, name, obj, tag=None): if self.unique: self.remove(name, tag) self.objs.insert(0, (name, obj, tag)) + def set(self, name, obj, tag=None): + self.remove(name, tag) + self.objs.insert(0, (name, obj, tag)) + def adds(self, objs): for i in objs: self.add(*i) @@ -51,21 +55,21 @@ class Source: managers = {} @classmethod - def manager(cls, name, value=None): + def manager(cls, name): if not name in cls.managers: cls.managers[name] = Manager() return cls.managers[name] class App(): def __init__(self): - self.img_manager = Manager() - self.wimg_manager = Manager() - self.tab_manager = Manager() - self.wtab_manager = Manager() - self.mesh_manager = Manager() - self.wmesh_manager = Manager() - self.task_manager = Manager() self.managers = {} + self.img_manager = self.manager('img') + self.wimg_manager = self.manager('wimg') + self.tab_manager = self.manager('tab') + self.wtab_manager = self.manager('wtab') + self.mesh_manager = self.manager('mesh') + self.wmesh_manager = self.manager('wmesh') + self.task_manager = self.manager('task') def manager(self, name, value=None): if not name in self.managers: @@ -83,7 +87,6 @@ def add_img(self, img): if not self.img_manager.has(img.name, obj=img): img.name = self.img_manager.name(img.name) self.img_manager.add(img.name, img) - print(self.img_manager.objs, 'open') def remove_img(self, img): print('remove', img.name) @@ -155,4 +158,4 @@ def add_task(self, task): self.task_manager.add(task.title, task) def remove_task(self, task): - self.task_manager.remove(obj=task) + self.task_manager.remove(obj=task) \ No newline at end of file diff --git a/sciapp/object/roi.py b/sciapp/object/roi.py index 34555ebd..f961659c 100644 --- a/sciapp/object/roi.py +++ b/sciapp/object/roi.py @@ -1,5 +1,4 @@ from .shape import * -from ..util import draw_shp import numpy as np from numpy.linalg import norm import shapely.geometry as geom @@ -25,6 +24,7 @@ def roitype(self): return roitype def to_mask(self, msk, mode): + from ..util import draw_shp if isinstance(msk, tuple): msk = np.zeros(msk, dtype=np.int8) else: msk[:] = 0 diff --git a/sciapp/object/shape.py b/sciapp/object/shape.py index 0ab3ecb6..927e7d1d 100644 --- a/sciapp/object/shape.py +++ b/sciapp/object/shape.py @@ -353,18 +353,6 @@ def json2shp(obj): def geom2shp(obj): return json2shp(geom.mapping(obj)) -def geom_flatten(obj, geoms=None): - geoms, root = ([], True) if geoms is None else (geoms, False) - if isinstance(obj, geom.GeometryCollection): - for i in obj: geom_flatten(i, geoms) - elif type(obj) in {geom.MultiPolygon, geom.MultiPoint, geom.MultiLineString}: - geoms.extend(list(obj)) - else: geoms.append(obj) - if root: return geom.GeometryCollection(geoms) - -def geom_union(obj): - return geom_flatten(unary_union(geom_flatten(obj))) - if __name__ == '__main__': import json diff --git a/sciapp/util/__init__.py b/sciapp/util/__init__.py index cc56ca62..d1b21603 100644 --- a/sciapp/util/__init__.py +++ b/sciapp/util/__init__.py @@ -1,2 +1,3 @@ from .surfutil import * -from .shputil import * \ No newline at end of file +from .shputil import * +from .imgutil import * \ No newline at end of file diff --git a/sciwx/canvas/imutil.py b/sciapp/util/imgutil.py similarity index 82% rename from sciwx/canvas/imutil.py rename to sciapp/util/imgutil.py index 51750f88..518cdb52 100644 --- a/sciwx/canvas/imutil.py +++ b/sciapp/util/imgutil.py @@ -214,8 +214,64 @@ def mix_img(img, m, o, shp, buf, rgb, byt, rg=(0,255), lut=None, log=True, cns=0 stretch(buf, byt, rg[v], log) blend(byt, rgb[:,:,i], byt, mode) -''' -rgb = np.array([[[0,0,0]]], dtype=np.uint8) -lut = np.array([[0,0,0]], dtype=np.uint8) -mix_img(img, (1,1), (0,0), (1,1), img, rgb, img, (0,255), lut, 0, 'set') -''' +def cross(winbox, conbox): + two = np.array([winbox, conbox]) + x1, y1 = two[:,:2].max(axis=0) + x2, y2 = two[:,2:].min(axis=0) + return [x1, y1, x2, y2] + +def merge(winbox, conbox): + two = np.array([winbox, conbox]) + x1, y1 = two[:,:2].min(axis=0) + x2, y2 = two[:,2:].max(axis=0) + return [x1, y1, x2, y2] + +def multiply(rect, kx, ky): + return rect * [kx, ky, kx, ky] + +def layx(winbox, conbox): + conw = conbox[2]-conbox[0] + winw = winbox[2]-winbox[0] + if conw winbox[0]: + conbox[0] = winbox[0] + conbox[2] = conbox[0] + conw + elif conbox[2] < winbox[2]: + conbox[2] = winbox[2] + conbox[0] = conbox[2] - conw + +def layy(winbox, conbox): + winh = winbox[3]-winbox[1] + conh = conbox[3]-conbox[1] + if conh winbox[1]: + conbox[1] = winbox[1] + conbox[3] = conbox[1] + conh + elif conbox[3] < winbox[3]: + conbox[3] = winbox[3] + conbox[1] = conbox[3] - conh + +def lay(winbox, conbox): + layx(winbox, conbox) + layy(winbox, conbox) + +def like(ori, cont, cell): + kx = (cont[2]-cont[0])/(ori[2]-ori[0]) + ky = (cont[3]-cont[1])/(ori[3]-ori[1]) + ox = cont[0] - ori[0]*kx + oy = cont[1] - ori[1]*kx + return [cell[0]*kx+ox, cell[1]*ky+oy, + cell[2]*kx+ox, cell[3]*kx+oy] + +def mat(ori, con, cell, cros): + kx = (ori[2]-ori[0])/(con[2]-con[0]) + ky = (ori[3]-ori[1])/(con[3]-con[1]) + ox = (cros[1]-cell[1])*ky + oy = (cros[0]-cell[0])*kx + return (ox, oy), (kx, ky) \ No newline at end of file diff --git a/sciapp/util/shputil.py b/sciapp/util/shputil.py index d30f60a5..603305c7 100644 --- a/sciapp/util/shputil.py +++ b/sciapp/util/shputil.py @@ -1,6 +1,6 @@ import numpy as np from skimage import draw -from ..object.shape import * +# from ..object.shape import * def offset(shp, dx, dy): if shp.dtype in {'rectangle', 'ellipse', 'circle'}: diff --git a/sciwx/canvas/boxutil.py b/sciwx/canvas/boxutil.py deleted file mode 100644 index 0bc0f93f..00000000 --- a/sciwx/canvas/boxutil.py +++ /dev/null @@ -1,63 +0,0 @@ -import numpy as np - -def cross(winbox, conbox): - two = np.array([winbox, conbox]) - x1, y1 = two[:,:2].max(axis=0) - x2, y2 = two[:,2:].min(axis=0) - return [x1, y1, x2, y2] - -def merge(winbox, conbox): - two = np.array([winbox, conbox]) - x1, y1 = two[:,:2].min(axis=0) - x2, y2 = two[:,2:].max(axis=0) - return [x1, y1, x2, y2] - -def multiply(rect, kx, ky): - return rect * [kx, ky, kx, ky] - -def layx(winbox, conbox): - conw = conbox[2]-conbox[0] - winw = winbox[2]-winbox[0] - if conw winbox[0]: - conbox[0] = winbox[0] - conbox[2] = conbox[0] + conw - elif conbox[2] < winbox[2]: - conbox[2] = winbox[2] - conbox[0] = conbox[2] - conw - -def layy(winbox, conbox): - winh = winbox[3]-winbox[1] - conh = conbox[3]-conbox[1] - if conh winbox[1]: - conbox[1] = winbox[1] - conbox[3] = conbox[1] + conh - elif conbox[3] < winbox[3]: - conbox[3] = winbox[3] - conbox[1] = conbox[3] - conh - -def lay(winbox, conbox): - layx(winbox, conbox) - layy(winbox, conbox) - -def like(ori, cont, cell): - kx = (cont[2]-cont[0])/(ori[2]-ori[0]) - ky = (cont[3]-cont[1])/(ori[3]-ori[1]) - ox = cont[0] - ori[0]*kx - oy = cont[1] - ori[1]*kx - return [cell[0]*kx+ox, cell[1]*ky+oy, - cell[2]*kx+ox, cell[3]*kx+oy] - -def mat(ori, con, cell, cros): - kx = (ori[2]-ori[0])/(con[2]-con[0]) - ky = (ori[3]-ori[1])/(con[3]-con[1]) - ox = (cros[1]-cell[1])*ky - oy = (cros[0]-cell[0])*kx - return (ox, oy), (kx, ky) \ No newline at end of file diff --git a/sciwx/canvas/canvas.py b/sciwx/canvas/canvas.py index 54d9b127..a703caa2 100644 --- a/sciwx/canvas/canvas.py +++ b/sciwx/canvas/canvas.py @@ -1,6 +1,5 @@ import wx, numpy as np -from .boxutil import cross, multiply, merge, lay, mat, like -from .imutil import mix_img +from sciapp.util.imgutil import mix_img, cross, multiply, merge, lay, mat, like from .mark import drawmark from sciapp.object import Image, Shape, mark2shp, Layer, json2shp from sciapp.action import ImageTool, ShapeTool diff --git a/sciwx/canvas/test.py b/sciwx/canvas/test.py deleted file mode 100644 index d29f05c3..00000000 --- a/sciwx/canvas/test.py +++ /dev/null @@ -1,258 +0,0 @@ -import wx, numpy as np -from boxutil import cross, multiply, lay, mat -from imutil import mix_img -from mark import drawmark -from time import time - -class Canvas (wx.Panel): - scales = [0.03125, 0.0625, 0.125, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4, 5, 8, 10, 15, 20, 30, 50] - - def __init__(self, parent, autofit=False): - wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.TAB_TRAVERSAL ) - self.img = None - self.back = None - self.mode = 'set' - - self.winbox = None - self.conbox = None - self.oribox = None - - self.outbak = None - self.outimg = None - self.outrgb = None - self.outbmp = None - self.outint = None - - self.buffer = None - - lut = np.arange(256*3)//3 - lut.shape = (256,3) - lut = lut.astype(np.uint8) - - self.lut = lut - self.rg = (0, 255) - self.cn = 0 - self.log = False - - self._lut = lut - self._rg = (0, 255) - self._cn = 0 - self._log = False - - self.marks = {} - - self.scaidx = 6 - self.autofit = autofit - self.scrbox = wx.DisplaySize() - self.bindEvents() - - def bindEvents(self): - for event, handler in [ \ - (wx.EVT_SIZE, self.on_size), - (wx.EVT_MOUSE_EVENTS, self.on_mouseevent), - (wx.EVT_IDLE, self.on_idle), - (wx.EVT_PAINT, self.on_paint)]: - self.Bind(event, handler) - - def on_mouseevent(self, me): - if me.ButtonDown(): - if me.GetButton()==1: - self.oldxy = me.GetX(), me.GetY() - if me.GetButton()==3: - self.fit() - wheel = np.sign(me.GetWheelRotation()) - if wheel!=0: - if wheel == 1: - self.zoomout(me.GetX(), me.GetY()) - if wheel == -1: - self.zoomin(me.GetX(), me.GetY()) - if me.Dragging(): - x, y = self.oldxy - self.move(me.GetX()-x, me.GetY()-y) - self.oldxy = me.GetX(), me.GetY() - - def initBuffer(self): - box = self.GetClientSize() - self.buffer = wx.Bitmap(*box) - self.winbox = [0, 0, *box] - - def fit(self): - oriw = self.oribox[2]-self.oribox[0] - orih = self.oribox[3]-self.oribox[1] - if not self.autofit: a,b,c,d = self.winbox - else: - (a,b),(c,d) = (0,0), self.scrbox - c, d = c*0.9, d*0.9 - for i in self.scales[6::-1]: - if oriw*i Date: Thu, 25 Jun 2020 23:11:52 +0800 Subject: [PATCH 269/343] nothing --- imagepy/data/config.json | 2 +- sciwx/app/canvasapp.py | 63 +++++++++++++++++++++++++++++ sciwx/app/imgapp.py | 1 - sciwx/canvas/canvas.py | 3 +- sciwx/canvas/mcanvas.py | 3 +- sciwx/demo/canvas1_demo.py | 8 ++-- sciwx/demo/canvas6_frame_toolbar.py | 4 +- 7 files changed, 73 insertions(+), 11 deletions(-) create mode 100644 sciwx/app/canvasapp.py diff --git a/imagepy/data/config.json b/imagepy/data/config.json index c6b5e5b7..c1b1e46c 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["uistyle", "imagepy", null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["recent", ["C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc", "C:/Users/Administrator/Desktop/imagepy/imagepy/menus/File/Samples Online/DEM.mc"], null], ["language", "Chinese", null]] \ No newline at end of file +[["language", "Chinese", null], ["recent", ["C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc", "C:/Users/Administrator/Desktop/imagepy/imagepy/menus/File/Samples Online/DEM.mc"], null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["uistyle", "imagepy", null]] \ No newline at end of file diff --git a/sciwx/app/canvasapp.py b/sciwx/app/canvasapp.py new file mode 100644 index 00000000..cfbcdc55 --- /dev/null +++ b/sciwx/app/canvasapp.py @@ -0,0 +1,63 @@ +import wx, wx.lib.agw.aui as aui +from ..canvas.mcanvas import MCanvas +from ..widgets import ToolBar, MenuBar, ParaDialog +from sciapp import App + +class CanvasApp(wx.Frame, App): + def __init__(self, parent=None, autofit=False): + App.__init__(self) + wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, + title = 'CanvasFrame', + pos = wx.DefaultPosition, + size = wx.Size( 800, 600 ), + style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) + sizer = wx.BoxSizer(wx.VERTICAL) + self.canvas = MCanvas(self, autofit=autofit) + sizer.Add(self.canvas, 1, wx.EXPAND|wx.ALL, 0) + self.SetSizer(sizer) + + self.set_rg = self.canvas.set_rg + self.set_lut = self.canvas.set_rg + self.set_log = self.canvas.set_log + self.set_mode = self.canvas.set_mode + self.set_tool = self.canvas.set_tool + self.set_imgs = self.canvas.set_imgs + self.set_img = self.canvas.set_img + self.set_cn = self.canvas.set_cn + + self.Bind(wx.EVT_IDLE, self.on_idle) + self.Bind(wx.EVT_ACTIVATE, self.on_valid) + self.Bind(wx.EVT_CLOSE, self.on_close) + + self.status = self.CreateStatusBar( 1 ) + + def on_idle(self, event): + if self.GetTitle()!=self.canvas.image.title: + self.SetTitle(self.canvas.image.title) + + def set_title(self, ips): self.SetTitle(ips.title) + + def set_info(self, info): self.status.SetStatusText(info) + + def on_valid(self, event): pass + + def on_close(self, event): + event.Skip() + + def add_toolbar(self): + toolbar = ToolBar(self) + self.GetSizer().Insert(0, toolbar, 0, wx.EXPAND | wx.ALL, 0) + return toolbar + + def add_menubar(self): + menubar = MenuBar(self) + self.SetMenuBar(menubar) + return menubar + + def show_para(self, title, view, para, on_handle=None, on_ok=None, on_cancel=None, preview=False, modal=True): + dialog = ParaDialog(self, title) + dialog.init_view(view, para, preview, modal=modal, app=self) + dialog.Bind('cancel', on_cancel) + dialog.Bind('parameter', on_handle) + dialog.Bind('commit', on_ok) + return dialog.show() \ No newline at end of file diff --git a/sciwx/app/imgapp.py b/sciwx/app/imgapp.py index cac703e4..aa25224c 100644 --- a/sciwx/app/imgapp.py +++ b/sciwx/app/imgapp.py @@ -11,7 +11,6 @@ from sciwx.plot import PlotFrame from sciapp import App, Source - class ImageApp(wx.Frame, App): def __init__( self, parent ): wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = 'ImageApp', diff --git a/sciwx/canvas/canvas.py b/sciwx/canvas/canvas.py index a703caa2..ead97f1b 100644 --- a/sciwx/canvas/canvas.py +++ b/sciwx/canvas/canvas.py @@ -170,8 +170,7 @@ def update(self, counter = [0,0]): print('frame rate:',int(50/max(0.001,counter[1]))) counter[0] = counter[1] = 0 - def set_tool(self, tool): - self.tool = tool + def set_tool(self, tool): self.tool = tool @property def scale(self): diff --git a/sciwx/canvas/mcanvas.py b/sciwx/canvas/mcanvas.py index eb45ec77..6845356f 100644 --- a/sciwx/canvas/mcanvas.py +++ b/sciwx/canvas/mcanvas.py @@ -19,9 +19,10 @@ def set_img(self, img, b=False): isarr = isinstance(img, ndarray) if b and not isarr: self.images[0].back = img if not b and not isarr: self.images[0] = img - if b and isarr: self.images[0].back.img = img + if b and isarr: self.images[0].back = Image([img]) if not b and isarr: self.images[0].img = img [self.images[0], self.images[0].back][b].reset() + self.update_box() self.update() def set_log(self, log, b=False): diff --git a/sciwx/demo/canvas1_demo.py b/sciwx/demo/canvas1_demo.py index f3eef3a8..79d8a9e3 100644 --- a/sciwx/demo/canvas1_demo.py +++ b/sciwx/demo/canvas1_demo.py @@ -39,9 +39,9 @@ def complex_test(): if __name__ == '__main__': app = wx.App() - #gray_test() - #rgb_test() - #rgb_gray_blend() - #complex_test() + gray_test() + rgb_test() + rgb_gray_blend() + complex_test() app.MainLoop() diff --git a/sciwx/demo/canvas6_frame_toolbar.py b/sciwx/demo/canvas6_frame_toolbar.py index 9ef2dd95..3a30b248 100644 --- a/sciwx/demo/canvas6_frame_toolbar.py +++ b/sciwx/demo/canvas6_frame_toolbar.py @@ -1,7 +1,7 @@ import sys, wx sys.path.append('../../') from skimage.draw import line -from sciwx.canvas import CanvasFrame +from sciwx.app.canvasapp import CanvasApp from sciapp.action import Tool, ImageTool class Pencil(ImageTool): @@ -35,7 +35,7 @@ def mouse_wheel(self, ips, x, y, d, **key):pass from skimage.io import imread app = wx.App() - cf = CanvasFrame(None, autofit=False) + cf = CanvasApp(None, autofit=False) cf.set_imgs([astronaut(), 255-astronaut()]) cf.set_cn((0,1,2)) bar = cf.add_toolbar() From 7efc36791952923985b9d6e43d0362710f181ae6 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 26 Jun 2020 17:32:54 +0800 Subject: [PATCH 270/343] remove some action in advanced --- imagepy/core/app/imagepy.py | 13 +++++++++++-- imagepy/core/app/source.py | 2 +- imagepy/core/engine/filter.py | 7 +------ imagepy/core/engine/free.py | 6 +----- imagepy/core/engine/simple.py | 7 +------ imagepy/core/engine/table.py | 5 +---- imagepy/menus/Plugins/Manager/shotcut_wgt.py | 4 +--- imagepy/tools/Measure/setting_tol.py | 2 +- sciapp/action/__init__.py | 6 +++--- sciapp/action/{ => advanced}/free.py | 0 sciapp/action/{ => advanced}/simple.py | 0 sciapp/action/{ => plugin}/meaact.py | 6 +++--- sciapp/action/{ => plugin}/roiact.py | 4 ++-- sciapp/action/{ => plugin}/shpact.py | 0 sciapp/action/{ => plugin}/shpbase.py | 2 +- sciapp/object/table.py | 2 ++ sciwx/app/canvasapp.py | 12 ++++++++++-- sciwx/canvas/mcanvas.py | 4 +++- sciwx/demo/canvas1_demo.py | 4 ++-- sciwx/demo/canvas6_frame_toolbar.py | 7 ++++--- sciwx/demo/canvas7_frame_menu.py | 17 ++++++++--------- ..._menu_tool.py => canvas8_frame_menu_tool.py} | 15 +++++++-------- sciwx/widgets/paradialog.py | 8 +++----- 23 files changed, 66 insertions(+), 67 deletions(-) rename sciapp/action/{ => advanced}/free.py (100%) rename sciapp/action/{ => advanced}/simple.py (100%) rename sciapp/action/{ => plugin}/meaact.py (98%) rename sciapp/action/{ => plugin}/roiact.py (96%) rename sciapp/action/{ => plugin}/shpact.py (100%) rename sciapp/action/{ => plugin}/shpbase.py (99%) rename sciwx/demo/{canvas7_frame_menu_tool.py => canvas8_frame_menu_tool.py} (85%) diff --git a/imagepy/core/app/imagepy.py b/imagepy/core/app/imagepy.py index ef126e7f..f0ca7b65 100644 --- a/imagepy/core/app/imagepy.py +++ b/imagepy/core/app/imagepy.py @@ -506,13 +506,22 @@ def getpath(self, title, filt, io, name=''): dialog.Destroy() return path + + lang = Source.manager('config').get('language') + doc = Source.manager('document').get(self.title, tag=lang) + doc = doc or Source.manager('document').get(tol.title, tag='English') + self.app.show_md(doc or 'No Document!', self.title) + def show_para(self, title, view, para, on_handle=None, on_ok=None, on_cancel=None, on_help=None, preview=False, modal=True): lang = Source.manager('config').get('language') dic = Source.manager('dictionary').get(name=title, tag=lang) + doc = Source.manager('document').get(title, tag=lang) + doc = doc or Source.manager('document').get(title, tag='English') + on_help = lambda x=doc:self.show_md(x or 'No Document!', title) dialog = ParaDialog(self, title) - dialog.init_view(view, para, preview, modal=modal, - app=self, translate=self.translate(dic)) + dialog.init_view(view, para, preview, modal=modal, app=self) + self.translate(dic)(dialog) dialog.Bind('cancel', on_cancel) dialog.Bind('parameter', on_handle) dialog.Bind('commit', on_ok) diff --git a/imagepy/core/app/source.py b/imagepy/core/app/source.py index b484fd87..e0341ef0 100644 --- a/imagepy/core/app/source.py +++ b/imagepy/core/app/source.py @@ -25,7 +25,7 @@ if not mark_style is None: for i in mark_style: ROI.default[i] = mark_style[i] -from sciapp.action.meaact import Measure +from sciapp.action import Measure mark_style = Source.manager('config').get('mea_style') if not mark_style is None: for i in mark_style: Measure.default[i] = mark_style[i] diff --git a/imagepy/core/engine/filter.py b/imagepy/core/engine/filter.py index 96936954..566ba87d 100644 --- a/imagepy/core/engine/filter.py +++ b/imagepy/core/engine/filter.py @@ -6,8 +6,6 @@ import threading import numpy as np - -from sciapp import Source from time import time, sleep def process_channels(plg, ips, src, des, para): @@ -158,10 +156,7 @@ def ok(self, ips, para=None, callafter=None): #ips.update() def on_help(self): - lang = Source.manager('config').get('language') - doc = Source.manager('document').get(self.title, tag=lang) - doc = doc or Source.manager('document').get(tol.title, tag='English') - self.app.show_md(doc or 'No Document!', self.title) + self.app.show_md(self.__doc__ or 'No Document!', self.title) def cancel(self, ips): if 'auto_snap' in self.note: diff --git a/imagepy/core/engine/free.py b/imagepy/core/engine/free.py index e4bc7e2a..076dfc6a 100644 --- a/imagepy/core/engine/free.py +++ b/imagepy/core/engine/free.py @@ -4,7 +4,6 @@ @author: yxl """ import threading -from sciapp import Source from time import time class Free: @@ -30,10 +29,7 @@ def runasyn(self, para, callback=None): def load(self):return True def on_help(self): - lang = Source.manager('config').get('language') - doc = Source.manager('document').get(self.title, tag=lang) - doc = doc or Source.manager('document').get(tol.title, tag='English') - self.app.show_md(doc or 'No Document!', self.title) + self.app.show_md(self.__doc__ or 'No Document!', self.title) def show(self): if self.view==None:return True diff --git a/imagepy/core/engine/simple.py b/imagepy/core/engine/simple.py index f073cca3..c08b6b03 100644 --- a/imagepy/core/engine/simple.py +++ b/imagepy/core/engine/simple.py @@ -4,8 +4,6 @@ @author: yxl """ import threading - -from sciapp import Source from time import time import numpy as np @@ -39,10 +37,7 @@ def run(self, ips, imgs, para = None):pass def cancel(self, ips):pass def on_help(self): - lang = Source.manager('config').get('language') - doc = Source.manager('document').get(self.title, tag=lang) - doc = doc or Source.manager('document').get(tol.title, tag='English') - self.app.show_md(doc or 'No Document!', self.title) + self.app.show_md(self.__doc__ or 'No Document!', self.title) def ok(self, ips, para=None, callafter=None): if para == None: para = self.para diff --git a/imagepy/core/engine/table.py b/imagepy/core/engine/table.py index 0c898b22..3f1f9787 100644 --- a/imagepy/core/engine/table.py +++ b/imagepy/core/engine/table.py @@ -43,10 +43,7 @@ def cancel(self, tps): tps.update() def on_help(self): - lang = Source.manager('config').get('language') - doc = Source.manager('document').get(self.title, tag=lang) - doc = doc or Source.manager('document').get(tol.title, tag='English') - self.app.show_md(doc or 'No Document!', self.title) + self.app.show_md(self.__doc__ or 'No Document!', self.title) def ok(self, tps, para=None, callafter=None): if para == None: para = self.para diff --git a/imagepy/menus/Plugins/Manager/shotcut_wgt.py b/imagepy/menus/Plugins/Manager/shotcut_wgt.py index f259b7fd..261d99b6 100644 --- a/imagepy/menus/Plugins/Manager/shotcut_wgt.py +++ b/imagepy/menus/Plugins/Manager/shotcut_wgt.py @@ -99,8 +99,7 @@ def on_run(self, event): code = event.GetKeyCode() title = self.buf[event.GetIndex()][0] txt = self.buf[event.GetIndex()][1] - if code == wx.WXK_DELETE: - txt = '' + if code == wx.WXK_DELETE: txt = '' elif code == wx.WXK_CONTROL: txt = self.ist(txt, 'Ctrl') elif code == wx.WXK_ALT: @@ -115,7 +114,6 @@ def on_run(self, event): if len(txt)>0 and txt[-1]=='-':txt=txt[:-1] self.buf[event.GetIndex()][1] = txt self.lst_plgs.RefreshItem(event.GetIndex()) - Source.manager('shortcut').remove(name=title) if txt!='': Source.manager('shortcut').add(title, txt) #PluginsManager.plgs[self.buf[event.GetIndex()][0]]().start() diff --git a/imagepy/tools/Measure/setting_tol.py b/imagepy/tools/Measure/setting_tol.py index 4e0581ad..bef82710 100644 --- a/imagepy/tools/Measure/setting_tol.py +++ b/imagepy/tools/Measure/setting_tol.py @@ -1,5 +1,5 @@ from sciapp.action import ImageTool -from sciapp.action.meaact import Measure +from sciapp.action import Measure from imagepy.core.engine import Free from sciapp import Source diff --git a/sciapp/action/__init__.py b/sciapp/action/__init__.py index ff7459c3..876d36c9 100644 --- a/sciapp/action/__init__.py +++ b/sciapp/action/__init__.py @@ -1,6 +1,6 @@ from .action import SciAction from .imgact import ImgAction from .tolact import Tool, DefaultTool, ImageTool, ShapeTool, TableTool -from .meaact import DistanceTool, AngleTool, SlopeTool, AreaTool, CoordinateTool -from .shpact import * -from .roiact import * \ No newline at end of file +from .plugin.meaact import DistanceTool, AngleTool, SlopeTool, AreaTool, CoordinateTool +from .plugin.shpact import * +from .plugin.roiact import * \ No newline at end of file diff --git a/sciapp/action/free.py b/sciapp/action/advanced/free.py similarity index 100% rename from sciapp/action/free.py rename to sciapp/action/advanced/free.py diff --git a/sciapp/action/simple.py b/sciapp/action/advanced/simple.py similarity index 100% rename from sciapp/action/simple.py rename to sciapp/action/advanced/simple.py diff --git a/sciapp/action/meaact.py b/sciapp/action/plugin/meaact.py similarity index 98% rename from sciapp/action/meaact.py rename to sciapp/action/plugin/meaact.py index 24a80bae..c1652d58 100644 --- a/sciapp/action/meaact.py +++ b/sciapp/action/plugin/meaact.py @@ -1,7 +1,7 @@ from .shpbase import pick_obj, pick_point, drag, offset -from . import ImageTool, ShapeTool -from ..object import Point, Line, Polygon, Layer, Points, Texts -from ..object.roi import * +from .. import ImageTool, ShapeTool +from ...object import Point, Line, Polygon, Layer, Points, Texts +from ...object.roi import * import numpy as np from numpy.linalg import norm diff --git a/sciapp/action/roiact.py b/sciapp/action/plugin/roiact.py similarity index 96% rename from sciapp/action/roiact.py rename to sciapp/action/plugin/roiact.py index 3bbf1c5b..cb8e4b66 100644 --- a/sciapp/action/roiact.py +++ b/sciapp/action/plugin/roiact.py @@ -1,7 +1,7 @@ from .shpact import * from .shpbase import BaseEditor -from .tolact import ImageTool -from ..object import ROI +from ..tolact import ImageTool +from ...object import ROI class BaseROI(ImageTool): def __init__(self, base): diff --git a/sciapp/action/shpact.py b/sciapp/action/plugin/shpact.py similarity index 100% rename from sciapp/action/shpact.py rename to sciapp/action/plugin/shpact.py diff --git a/sciapp/action/shpbase.py b/sciapp/action/plugin/shpbase.py similarity index 99% rename from sciapp/action/shpbase.py rename to sciapp/action/plugin/shpbase.py index f1840ec2..e5bc360c 100644 --- a/sciapp/action/shpbase.py +++ b/sciapp/action/plugin/shpbase.py @@ -1,4 +1,4 @@ -from .tolact import ShapeTool +from ..tolact import ShapeTool from sciapp.object import * from sciapp.util import geom_union from numpy.linalg import norm diff --git a/sciapp/object/table.py b/sciapp/object/table.py index cc69c7e0..725ee186 100644 --- a/sciapp/object/table.py +++ b/sciapp/object/table.py @@ -1,3 +1,5 @@ +import numpy as np + class Table(): def __init__(self, df=None): self.name = 'Table' diff --git a/sciwx/app/canvasapp.py b/sciwx/app/canvasapp.py index cfbcdc55..ad5cf0bc 100644 --- a/sciwx/app/canvasapp.py +++ b/sciwx/app/canvasapp.py @@ -21,8 +21,6 @@ def __init__(self, parent=None, autofit=False): self.set_log = self.canvas.set_log self.set_mode = self.canvas.set_mode self.set_tool = self.canvas.set_tool - self.set_imgs = self.canvas.set_imgs - self.set_img = self.canvas.set_img self.set_cn = self.canvas.set_cn self.Bind(wx.EVT_IDLE, self.on_idle) @@ -31,6 +29,16 @@ def __init__(self, parent=None, autofit=False): self.status = self.CreateStatusBar( 1 ) + def set_imgs(self, imgs): + self.remove_img(self.canvas.image) + self.canvas.set_imgs(imgs) + self.add_img(self.canvas.image) + + def set_img(self, img, b=False): + self.remove_img(self.canvas.image) + self.canvas.set_img(img, b) + self.add_img(self.canvas.image) + def on_idle(self, event): if self.GetTitle()!=self.canvas.image.title: self.SetTitle(self.canvas.image.title) diff --git a/sciwx/canvas/mcanvas.py b/sciwx/canvas/mcanvas.py index 6845356f..2482fdca 100644 --- a/sciwx/canvas/mcanvas.py +++ b/sciwx/canvas/mcanvas.py @@ -21,7 +21,9 @@ def set_img(self, img, b=False): if not b and not isarr: self.images[0] = img if b and isarr: self.images[0].back = Image([img]) if not b and isarr: self.images[0].img = img - [self.images[0], self.images[0].back][b].reset() + if not b: self.images[0].reset() + if b and self.images[0].back: + self.images[0].back.reset() self.update_box() self.update() diff --git a/sciwx/demo/canvas1_demo.py b/sciwx/demo/canvas1_demo.py index 79d8a9e3..379a3fe8 100644 --- a/sciwx/demo/canvas1_demo.py +++ b/sciwx/demo/canvas1_demo.py @@ -8,7 +8,7 @@ def gray_test(): frame = wx.Frame(None, title='gray test') - canvas = Canvas(frame, autofit=False) + canvas = Canvas(frame, autofit=True) canvas.set_img(camera()) frame.Show() @@ -30,7 +30,7 @@ def rgb_gray_blend(): frame.Show() def complex_test(): - frame = wx.Frame(None, title='blend') + frame = wx.Frame(None, title='fft') canvas = Canvas(frame, autofit=True) canvas.set_img(fftshift(fft2(camera()))) canvas.set_rg((0,31015306)) diff --git a/sciwx/demo/canvas6_frame_toolbar.py b/sciwx/demo/canvas6_frame_toolbar.py index 3a30b248..a6b841f0 100644 --- a/sciwx/demo/canvas6_frame_toolbar.py +++ b/sciwx/demo/canvas6_frame_toolbar.py @@ -6,6 +6,8 @@ class Pencil(ImageTool): title = 'Pencil' + para = {'pc':(255,0,0)} + view = [('color', 'pc','pen', 'color')] def __init__(self): self.status = False @@ -24,7 +26,7 @@ def mouse_move(self, ips, x, y, btn, **key): rs,cs = line(*[int(i) for i in se]) rs.clip(0, ips.shape[1], out=rs) cs.clip(0, ips.shape[0], out=cs) - ips.img[rs,cs] = (255, 0, 0) + ips.img[rs,cs] = self.para['pc'] self.oldp = (y, x) key['canvas'].update() @@ -36,8 +38,7 @@ def mouse_wheel(self, ips, x, y, d, **key):pass app = wx.App() cf = CanvasApp(None, autofit=False) - cf.set_imgs([astronaut(), 255-astronaut()]) - cf.set_cn((0,1,2)) + cf.set_img(astronaut()) bar = cf.add_toolbar() bar.add_tool('M', ImageTool) bar.add_tool('P', Pencil) diff --git a/sciwx/demo/canvas7_frame_menu.py b/sciwx/demo/canvas7_frame_menu.py index db860247..d618e989 100644 --- a/sciwx/demo/canvas7_frame_menu.py +++ b/sciwx/demo/canvas7_frame_menu.py @@ -1,10 +1,10 @@ import sys, wx sys.path.append('../../') from scipy.ndimage import gaussian_filter -from sciwx.canvas import CanvasFrame -from sciwx.event import ImgEvent +from sciwx.app.canvasapp import CanvasApp +from sciapp.action import ImgAction -class Gaussian(ImgEvent): +class Gaussian(ImgAction): title = 'Gaussian' note = ['auto_snap', 'preview'] para = {'sigma':2} @@ -13,7 +13,7 @@ class Gaussian(ImgEvent): def run(self, ips, img, snap, para): gaussian_filter(snap, para['sigma'], output=img) -class Undo(ImgEvent): +class Undo(ImgAction): title = 'Undo' def run(self, ips, img, snap, para): print(ips.img.mean(), ips.snap.mean()) @@ -24,12 +24,11 @@ def run(self, ips, img, snap, para): from skimage.io import imread app = wx.App() - cf = CanvasFrame(None, autofit=False) - cf.set_imgs([camera(), 255-camera()]) - cf.set_cn(0) - bar = cf.add_menubar() + ca = CanvasApp(None, autofit=False) + ca.set_img(camera()) + bar = ca.add_menubar() bar.load(('menu',[('Filter',[('Gaussian', Gaussian), ('Unto', Undo)]), ])) - cf.Show() + ca.Show() app.MainLoop() diff --git a/sciwx/demo/canvas7_frame_menu_tool.py b/sciwx/demo/canvas8_frame_menu_tool.py similarity index 85% rename from sciwx/demo/canvas7_frame_menu_tool.py rename to sciwx/demo/canvas8_frame_menu_tool.py index fe6d6dd6..c9d031f9 100644 --- a/sciwx/demo/canvas7_frame_menu_tool.py +++ b/sciwx/demo/canvas8_frame_menu_tool.py @@ -2,10 +2,10 @@ sys.path.append('../../') from scipy.ndimage import gaussian_filter from skimage.draw import line -from sciwx.canvas import CanvasFrame -from sciwx.event import ImgEvent, Tool, DefaultTool +from sciwx.app.canvasapp import CanvasApp +from sciapp.action import ImgAction, ImageTool -class Gaussian(ImgEvent): +class Gaussian(ImgAction): title = 'Gaussian' note = ['auto_snap', 'preview'] para = {'sigma':2} @@ -14,11 +14,11 @@ class Gaussian(ImgEvent): def run(self, ips, img, snap, para): gaussian_filter(snap, para['sigma'], output=img) -class Undo(ImgEvent): +class Undo(ImgAction): title = 'Undo' def run(self, ips, img, snap, para): ips.swap() -class Pencil(Tool): +class Pencil(ImageTool): title = 'Pencil' def __init__(self): self.status = False @@ -49,7 +49,7 @@ def mouse_wheel(self, ips, x, y, d, **key):pass from skimage.io import imread app = wx.App() - cf = CanvasFrame(None, autofit=False) + cf = CanvasApp(None, autofit=False) cf.set_imgs([camera(), 255-camera()]) cf.set_cn(0) bar = cf.add_menubar() @@ -57,7 +57,6 @@ def mouse_wheel(self, ips, x, y, d, **key):pass ('Unto', Undo)]),])) bar = cf.add_toolbar() - bar.add_tool(DefaultTool, 'M') - bar.add_tool(Pencil, 'P') + bar.add_tool('P', Pencil) cf.Show() app.MainLoop() diff --git a/sciwx/widgets/paradialog.py b/sciwx/widgets/paradialog.py index 0d1ae696..095836b7 100644 --- a/sciwx/widgets/paradialog.py +++ b/sciwx/widgets/paradialog.py @@ -48,18 +48,15 @@ def add_confirm(self, modal): self.btn_cancel.Bind( wx.EVT_BUTTON, lambda e:self.commit('cancel')) #self.lst.Add() - def init_view(self, items, para, preview=False, modal=True, app=None, translate=lambda x:x): + def init_view(self, items, para, preview=False, modal=True, app=None): self.para, self.modal = para, modal for item in items: self.add_ctrl_(widgets[item[0]], item[1], item[2:], app=app) if preview:self.add_ctrl_(Check, 'preview', ('preview',), app=app) self.reset(para) self.add_confirm(modal) - translate(self) - self.pack() wx.Dialog.Bind(self, wx.EVT_WINDOW_DESTROY, self.OnDestroy) #wx.Dialog.Bind(self, wx.EVT_IDLE, lambda e: self.reset()) - print('bind close') def OnDestroy( self, event ): self.handle = print @@ -82,7 +79,6 @@ def add_ctrl_(self, Ctrl, key, p, app=None): self.lst.Add( ctrl, 0, wx.EXPAND, 0 ) def pack(self): - self.Layout() mint, minu = [], [] for t,u in self.tus: if not t is None: mint.append(t.GetSize()[0]) @@ -90,6 +86,7 @@ def pack(self): for t,u in self.tus: if not t is None:t.SetInitialSize((max(mint),-1)) if not u is None:u.SetInitialSize((max(minu),-1)) + self.Layout() self.Fit() def para_check(self, para, key):pass @@ -128,6 +125,7 @@ def Bind(self, tag, f): if tag == 'help': self.on_help = f def show(self): + self.pack() if self.modal: status = self.ShowModal() == 5100 self.Destroy() From f62e1263c052018b3ccf6aacbe89515cc9d33e90 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Fri, 26 Jun 2020 19:36:26 +0800 Subject: [PATCH 271/343] add proxy to install plugins --- .../menus/Plugins/Install/installplg_plgs.py | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/imagepy/menus/Plugins/Install/installplg_plgs.py b/imagepy/menus/Plugins/Install/installplg_plgs.py index f6bf2a0c..ad7d1767 100644 --- a/imagepy/menus/Plugins/Install/installplg_plgs.py +++ b/imagepy/menus/Plugins/Install/installplg_plgs.py @@ -7,6 +7,7 @@ path = 'https://github.com/Image-Py/imagepy/archive/master.zip' from urllib.request import urlretrieve +import urllib from io import BytesIO as StringIO path_plgs = os.path.join(root_dir, 'plugins') @@ -24,11 +25,16 @@ def Schedule(a,b,c, plg): class Install(Free): title = 'Install Plugins' - para = {'repo':'https://github.com/Image-Py/IBook'} + para = {'repo':'https://github.com/Image-Py/IBook', 'proxy': False, 'Protocol': 'https', 'IP': '127.0.0.1', 'Port': '1080'} view = [('lab', None, 'input a zipfile url or github url as http://github.com/username/project'), - (str, 'repo', 'package', '')] + (str, 'repo', 'package', ''), + (bool, 'proxy', 'Use proxy'), + (list, 'Protocol', ['socks5', 'http', 'https'], str, 'Protocol', ''), + (str, 'IP', 'IP Address', ''), + (str, 'Port', 'Port', '')] def run(self, para=None): + para = self.para url = para['repo'] if 'github.com' in url: if url[-4:] == '.git': @@ -41,6 +47,17 @@ def run(self, para=None): domain, name = domain.replace('_', '-'), name.replace('_', '-') self.app.set_info('downloading plugin from %s'%para['repo']) + + if True == para['proxy']: + proxy=para['Protocol']+"://"+para['IP']+":"+para['Port'] + print("proxy = ", proxy) + # Build ProxyHandler object by given proxy + proxy_support=urllib.request.ProxyHandler({para['Protocol']:proxy}) + # Build opener with ProxyHandler object + opener = urllib.request.build_opener(proxy_support) + # Install opener to request + urllib.request.install_opener(opener) + urlretrieve(url, os.path.join(path_cache, domain+'_'+name+'.zip'), lambda a,b,c, p=self: Schedule(a,b,c,p)) zipf = zipfile.ZipFile(os.path.join(path_cache, domain+'_'+name+'.zip')) From acf822f4bf04eb09fcd62a9c9fb9eca535dad835 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Fri, 26 Jun 2020 20:34:33 +0800 Subject: [PATCH 272/343] dic for proxy setting --- imagepy/lang/Chinese/plugins/Plugins.dic | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/imagepy/lang/Chinese/plugins/Plugins.dic b/imagepy/lang/Chinese/plugins/Plugins.dic index 2b2bbe98..e50e3e9f 100644 --- a/imagepy/lang/Chinese/plugins/Plugins.dic +++ b/imagepy/lang/Chinese/plugins/Plugins.dic @@ -48,6 +48,12 @@ Install Packages::安装包 List Packages::包列表 Install Plugins::安装插件 + input a zipfile url or github url as http://github.com/username/project::输入一个zip文件链接或github链接,例如http://github.com/username/project + package::链接 + Use proxy::使用代理 + Protocol::协议类型 + IP Address::IP地址 + Port::端口号 List Plugins::插件列表 From 1248dc52228ca77e234d749d02c081754ab1292a Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 26 Jun 2020 21:07:20 +0800 Subject: [PATCH 273/343] remove free, filter, simple to sciapp.action --- imagepy/core/app/imagej.py | 14 ++- imagepy/core/engine/__init__.py | 8 +- imagepy/core/engine/dataio.py | 4 +- imagepy/core/engine/free.py | 47 ------- imagepy/core/engine/simple.py | 115 ------------------ imagepy/data/config.json | 2 +- sciapp/action/__init__.py | 3 +- sciapp/action/advanced/__init__.py | 4 + .../action/advanced}/filter.py | 0 sciapp/action/advanced/simple.py | 77 ++++++------ .../action/advanced}/table.py | 0 sciapp/action/tabact.py | 0 sciwx/doc/document.md | 0 13 files changed, 64 insertions(+), 210 deletions(-) delete mode 100644 imagepy/core/engine/free.py delete mode 100644 imagepy/core/engine/simple.py create mode 100644 sciapp/action/advanced/__init__.py rename {imagepy/core/engine => sciapp/action/advanced}/filter.py (100%) rename {imagepy/core/engine => sciapp/action/advanced}/table.py (100%) create mode 100644 sciapp/action/tabact.py create mode 100644 sciwx/doc/document.md diff --git a/imagepy/core/app/imagej.py b/imagepy/core/app/imagej.py index cc942e93..7e14b9d8 100644 --- a/imagepy/core/app/imagej.py +++ b/imagepy/core/app/imagej.py @@ -251,6 +251,7 @@ def _show_img(self, img, title=None): else: canvas.set_img(img) cframe.Bind(wx.EVT_ACTIVATE, self.on_new_img) cframe.Bind(wx.EVT_CLOSE, self.on_close_img) + cframe.SetIcon(self.GetIcon()) cframe.Show() def show_img(self, img, title=None): @@ -264,6 +265,7 @@ def _show_table(self, tab, title): grid.table.name = title cframe.Bind(wx.EVT_ACTIVATE, self.on_new_tab) cframe.Bind(wx.EVT_CLOSE, self.on_close_tab) + cframe.SetIcon(self.GetIcon()) cframe.Show() def show_table(self, tab, title=None): @@ -415,7 +417,10 @@ def info(self, cont): wx.CallAfter(self.txt_info.SetLabel, cont) def _alert(self, info, title='ImagePy'): - dialog=wx.MessageDialog(self, info, title, wx.OK) + lang = Source.manager('config').get('language') + dics = Source.manager('dictionary').gets(tag=lang) + dialog = wx.MessageDialog(self, info, title, wx.OK) + self.translate([i[1] for i in dics])(dialog) dialog.ShowModal() == wx.ID_OK dialog.Destroy() @@ -443,9 +448,12 @@ def show_para(self, title, view, para, on_handle=None, on_ok=None, on_cancel=None, on_help=None, preview=False, modal=True): lang = Source.manager('config').get('language') dic = Source.manager('dictionary').get(name=title, tag=lang) + doc = Source.manager('document').get(title, tag=lang) + doc = doc or Source.manager('document').get(title, tag='English') + on_help = lambda x=doc:self.show_md(x or 'No Document!', title) dialog = ParaDialog(self, title) - dialog.init_view(view, para, preview, modal=modal, - app=self, translate=self.translate(dic)) + dialog.init_view(view, para, preview, modal=modal, app=self) + self.translate(dic)(dialog) dialog.Bind('cancel', on_cancel) dialog.Bind('parameter', on_handle) dialog.Bind('commit', on_ok) diff --git a/imagepy/core/engine/__init__.py b/imagepy/core/engine/__init__.py index 3036a2c5..958e5d6c 100644 --- a/imagepy/core/engine/__init__.py +++ b/imagepy/core/engine/__init__.py @@ -1,9 +1,9 @@ -from .filter import Filter -from .simple import Simple -from .free import Free from .macros import Macros from .mkdown import MkDown from .widget import Widget -from .table import Table +from sciapp.action import Table +from sciapp.action import Filter +from sciapp.action import Simple +from sciapp.action import Free from .report import Report from .dataio import * \ No newline at end of file diff --git a/imagepy/core/engine/dataio.py b/imagepy/core/engine/dataio.py index b71f8aa2..62fa1304 100644 --- a/imagepy/core/engine/dataio.py +++ b/imagepy/core/engine/dataio.py @@ -1,9 +1,7 @@ import os from sciapp import Source from imagepy import root_dir -from .free import Free -from .simple import Simple -from .table import Table +from . import Free, Simple, Table from .macros import Macros import numpy as np diff --git a/imagepy/core/engine/free.py b/imagepy/core/engine/free.py deleted file mode 100644 index 076dfc6a..00000000 --- a/imagepy/core/engine/free.py +++ /dev/null @@ -1,47 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Sat Dec 3 03:57:53 2016 -@author: yxl -""" -import threading -from time import time - -class Free: - title = 'Free' - view = None - para = None - prgs = None - asyn = True - - def progress(self, i, n): self.prgs = int(i*100/n) - - def run(self, para=None): - print('this is a plugin') - - def runasyn(self, para, callback=None): - self.app.add_task(self) - start = time() - self.run(para) - self.app.info('%s: cost %.3fs'%(self.title, time()-start)) - self.app.remove_task(self) - if callback!=None:callback() - - def load(self):return True - - def on_help(self): - self.app.show_md(self.__doc__ or 'No Document!', self.title) - - def show(self): - if self.view==None:return True - return self.app.show_para(self.title, self.view, self.para, on_help=self.on_help) - - def start(self, app, para=None, callback=None): - self.app = app - if not self.load():return - if para!=None or self.show(): - if para==None:para = self.para - self.app.record_macros('{}>{}'.format(self.title, para)) - if self.asyn: - threading.Thread(target = self.runasyn, - args = (para, callback)).start() - else: self.runasyn(para, callback) diff --git a/imagepy/core/engine/simple.py b/imagepy/core/engine/simple.py deleted file mode 100644 index c08b6b03..00000000 --- a/imagepy/core/engine/simple.py +++ /dev/null @@ -1,115 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Sat Dec 3 03:32:05 2016 -@author: yxl -""" -import threading -from time import time -import numpy as np - -class Simple: - title = 'SimpleFilter' - asyn = True - note = [] - para = None - 'all, 8-bit, 16-bit, rgb, float, req_roi, stack, stack2d, stack3d, preview' - view = None - prgs = None - modal = True - - def __init__(self): pass - - def progress(self, i, n): self.prgs = int(i*100/n) - - def load(self, ips):return True - - def preview(self, ips, para):pass - - def show(self): - preview = lambda para, ips=self.ips: self.preview(ips, para) or ips.update() - return self.app.show_para(self.title, self.view, self.para, preview, - on_ok=lambda : self.ok(self.ips), on_help=self.on_help, - on_cancel=lambda : self.cancel(self.ips) or self.ips.update(), - preview='preview' in self.note, modal=self.modal) - - def run(self, ips, imgs, para = None):pass - - def cancel(self, ips):pass - - def on_help(self): - self.app.show_md(self.__doc__ or 'No Document!', self.title) - - def ok(self, ips, para=None, callafter=None): - if para == None: para = self.para - if self.asyn : - threading.Thread(target = self.runasyn, - args = (ips, ips.imgs, para, callafter)).start() - else: self.runasyn(ips, ips.imgs, para, callafter) - self.app.record_macros('{}>{}'.format(self.title, para)) - - def runasyn(self, ips, imgs, para = None, callback = None): - self.app.add_task(self) - start = time() - self.run(ips, imgs, para) - self.app.info('%s: cost %.3fs'%(ips.title, time()-start)) - ips.update() - self.app.remove_task(self) - if callback!=None:callback() - - def check(self, ips): - note = self.note - if ips == None: - self.app.alert('No image opened!') - return False - if 'req_roi' in note and ips.roi == None: - self.app.alert('No Roi found!') - return False - if not 'all' in note: - if ips.dtype==np.uint8 and ips.channels==3 and not 'rgb' in note: - self.app.alert('Do not surport rgb image') - return False - elif ips.dtype==np.uint8 and ips.channels==1 and not '8-bit' in note: - self.app.alert('Do not surport 8-bit image') - return False - elif ips.dtype==np.uint16 and not '16-bit' in note: - self.app.alert('Do not surport 16-bit uint image') - return False - elif ips.dtype==np.int32 and not 'int' in note: - self.app.alert('Do not surport 32-bit int uint image') - return False - elif ips.dtype in {np.float32, np.float64} and not 'float' in note: - self.app.alert('Do not surport float image') - return False - if sum([i in note for i in ('stack','stack2d','stack3d')])>0: - if ips.slices==1: - self.app.alert('Stack required!') - return False - elif 'stack2d' in note and ips.isarray: - self.app.alert('Stack2d required!') - return False - elif 'stack3d' in note and not ips.isarray: - self.app.alert('Stack3d required!') - return False - return True - - def start(self, app, para=None, callback=None): - #print self.title, para - self.app, self.ips = app, app.get_img() - if not self.check(self.ips):return - if not self.load(self.ips):return - - - if para!=None: - self.ok(self.ips, para, callback) - elif self.view==None: - if not self.__class__.show is Simple.show: - if self.show(): - self.ok(self.ips, para, callback) - else: self.ok(self.ips, para, callback) - elif self.modal: - if self.show(): - self.ok(self.ips, para, callback) - else: - self.cancel(self.ips) - self.ips.update() - else: self.show() \ No newline at end of file diff --git a/imagepy/data/config.json b/imagepy/data/config.json index c1b1e46c..5f243f37 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["language", "Chinese", null], ["recent", ["C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc", "C:/Users/Administrator/Desktop/imagepy/imagepy/menus/File/Samples Online/DEM.mc"], null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["uistyle", "imagepy", null]] \ No newline at end of file +[["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["recent", ["C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc", "C:/Users/Administrator/Desktop/imagepy/imagepy/menus/File/Samples Online/DEM.mc"], null], ["language", "Chinese", null], ["uistyle", "imagepy", null]] \ No newline at end of file diff --git a/sciapp/action/__init__.py b/sciapp/action/__init__.py index 876d36c9..c972b4d7 100644 --- a/sciapp/action/__init__.py +++ b/sciapp/action/__init__.py @@ -3,4 +3,5 @@ from .tolact import Tool, DefaultTool, ImageTool, ShapeTool, TableTool from .plugin.meaact import DistanceTool, AngleTool, SlopeTool, AreaTool, CoordinateTool from .plugin.shpact import * -from .plugin.roiact import * \ No newline at end of file +from .plugin.roiact import * +from .advanced import Filter, Free, Simple, Table \ No newline at end of file diff --git a/sciapp/action/advanced/__init__.py b/sciapp/action/advanced/__init__.py new file mode 100644 index 00000000..97727203 --- /dev/null +++ b/sciapp/action/advanced/__init__.py @@ -0,0 +1,4 @@ +from .filter import Filter +from .simple import Simple +from .table import Table +from .free import Free \ No newline at end of file diff --git a/imagepy/core/engine/filter.py b/sciapp/action/advanced/filter.py similarity index 100% rename from imagepy/core/engine/filter.py rename to sciapp/action/advanced/filter.py diff --git a/sciapp/action/advanced/simple.py b/sciapp/action/advanced/simple.py index 01dd9b3f..8d780343 100644 --- a/sciapp/action/advanced/simple.py +++ b/sciapp/action/advanced/simple.py @@ -3,10 +3,9 @@ Created on Sat Dec 3 03:32:05 2016 @author: yxl """ -import wx import threading -import numpy as np from time import time +import numpy as np class Simple: title = 'SimpleFilter' @@ -19,37 +18,9 @@ class Simple: modal = True def __init__(self): pass - - def progress(self, i, n): self.prgs = int(i*100/n) def load(self, ips):return True - def preview(self, ips, para):pass - - def show(self): - preview = lambda para, ips=self.ips: self.preview(ips, para) or ips.update() - return self.app.show_para(self.title, self.view, self.para, preview, - on_ok=lambda : self.ok(self.ips), on_cancel=lambda : self.cancel(self.ips) or self.ips.update(), - preview='preview' in self.note, modal=self.modal) - - def run(self, ips, imgs, para = None):pass - - def cancel(self, ips):pass - - def ok(self, ips, para=None, callafter=None): - if para == None: para = self.para - if self.asyn : - threading.Thread(target = self.runasyn, - args = (ips, ips.imgs, para, callafter)).start() - else: self.runasyn(ips, ips.imgs, para, callafter) - - def runasyn(self, ips, imgs, para = None, callback = None): - start = time() - self.run(ips, imgs, para) - self.app.info('%s: cost %.3fs'%(ips.title, time()-start)) - ips.update() - if callback!=None:callback() - def check(self, ips): note = self.note if ips == None: @@ -68,26 +39,60 @@ def check(self, ips): elif ips.dtype==np.uint16 and not '16-bit' in note: self.app.alert('Do not surport 16-bit uint image') return False - elif ips.dtype in [np.int32, np.int64] and not 'int' in note: + elif ips.dtype==np.int32 and not 'int' in note: self.app.alert('Do not surport 32-bit int uint image') return False - elif ips.dtype in [np.float32, np.float64] and not 'float' in note: + elif ips.dtype in {np.float32, np.float64} and not 'float' in note: self.app.alert('Do not surport float image') return False if sum([i in note for i in ('stack','stack2d','stack3d')])>0: - if ips.get_nslices()==1: + if ips.slices==1: self.app.alert('Stack required!') return False - elif 'stack2d' in note and ips.is3d: + elif 'stack2d' in note and ips.isarray: self.app.alert('Stack2d required!') return False - elif 'stack3d' in note and not ips.is3d: + elif 'stack3d' in note and not ips.isarray: self.app.alert('Stack3d required!') return False return True + + def preview(self, ips, para):pass + + def progress(self, i, n): self.prgs = int(i*100/n) + + def show(self): + preview = lambda para, ips=self.ips: self.preview(ips, para) or ips.update() + return self.app.show_para(self.title, self.view, self.para, preview, + on_ok=lambda : self.ok(self.ips), on_help=self.on_help, + on_cancel=lambda : self.cancel(self.ips) or self.ips.update(), + preview='preview' in self.note, modal=self.modal) + + def run(self, ips, imgs, para = None):pass + + def cancel(self, ips):pass + + def on_help(self): + self.app.show_md(self.__doc__ or 'No Document!', self.title) + + def ok(self, ips, para=None, callafter=None): + if para == None: para = self.para + if self.asyn : + threading.Thread(target = self.runasyn, + args = (ips, ips.imgs, para, callafter)).start() + else: self.runasyn(ips, ips.imgs, para, callafter) + self.app.record_macros('{}>{}'.format(self.title, para)) + + def runasyn(self, ips, imgs, para = None, callback = None): + self.app.add_task(self) + start = time() + self.run(ips, imgs, para) + self.app.info('%s: cost %.3fs'%(ips.title, time()-start)) + ips.update() + self.app.remove_task(self) + if callback!=None:callback() def start(self, app, para=None, callback=None): - #print self.title, para self.app, self.ips = app, app.get_img() if not self.check(self.ips):return if not self.load(self.ips):return diff --git a/imagepy/core/engine/table.py b/sciapp/action/advanced/table.py similarity index 100% rename from imagepy/core/engine/table.py rename to sciapp/action/advanced/table.py diff --git a/sciapp/action/tabact.py b/sciapp/action/tabact.py new file mode 100644 index 00000000..e69de29b diff --git a/sciwx/doc/document.md b/sciwx/doc/document.md new file mode 100644 index 00000000..e69de29b From 5ffe0cf5053c1f587c82105716e04e7c41967a87 Mon Sep 17 00:00:00 2001 From: Prevalenter <434625142@qq.com> Date: Fri, 26 Jun 2020 23:23:27 +0800 Subject: [PATCH 274/343] translate the image part Signed-off-by: Prevalenter <434625142@qq.com> --- imagepy/lang/Chinese/plugins/image.dic | 122 +++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 imagepy/lang/Chinese/plugins/image.dic diff --git a/imagepy/lang/Chinese/plugins/image.dic b/imagepy/lang/Chinese/plugins/image.dic new file mode 100644 index 00000000..f3c3481d --- /dev/null +++ b/imagepy/lang/Chinese/plugins/image.dic @@ -0,0 +1,122 @@ +Type::类型 + +Adjust::调整 + +Color::颜色 + +Stack::序列 + +Orthogonal view::正交视图 + +Set Slice::设定切片 + Num::范围 + +Next Slice::下一切片 + +Previous Slice::上一切片 + +Add Slice::添加切片 + +Delete Slice::删除切片 + +Sub Stack::子连续序列 + start::开始 + end::结束 + OK::确定 + Cancel::取消 + Help::帮助 + +Rename::重命名 + name::名字 + +Duplicate::复制 + +Crop::裁剪 + +Canvas Size::画布尺寸 + Weight::宽 + Height::高 + Horizontal::水平 + Vertical::垂直 + pix::像素 + left::左 + center::中 + right::右 + +Resize::设定尺寸 + kx::x比例 + ky::y比例 + kz::z比例 + accu::插值次数 + the kz only works on stack!::z比例仅用于连续序列 + +Scale And Unit::比例和单位 + per::比例 + unit::单位 + pix::像素 + kill scale::清除比例 + +Mark::标记 + +Open Mark::打开标记 + +Save Mark::保存标记 + +Clear Mark::清除标记 + +Mark Setting::标记设定 + line::线 + face::填充 + text::文本 + color::颜色 + width::宽 + solid fill::填充 + +Set Background::设定背景 + background::背景 + mode::模式 + kill::清除 + ratial::混合比例 + +Trans to Stack::连续序列 + +Trans to List::离散序列 + +Background Self::原图作为背景 + mode::模式 + ratial::混合比例 + +Lookup tabel::索引色 + +Threshold::阈值 + +Gray Stairs::灰度等级 + +Bright And Constract::亮度与对比度 + +Bleach Correction::白校正 + +Enhance Contrast::对比度增强 + Saturated pixels::饱和像素 + +Normalize::归一化 + 3D stack::三维连续序列 + Subtract background::减去背景 + +Color Balance::色彩平衡 + +Color Stairs::色彩等级 + +Histogram Normalize::直方图归一化 + +Histogram Match::直方图匹配 + temp::目标 + +Lookup table::索引色 + +Rotate::旋转 + degree::度 + angle::角度 + +Scale::缩放 + fact::因子 \ No newline at end of file From 2d61543917ae7687facf6a11202bce2747bd922d Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 26 Jun 2020 23:49:05 +0800 Subject: [PATCH 275/343] pluginmanager from source to app --- imagepy/core/app/imagej.py | 18 +++++++-- imagepy/core/app/imagepy.py | 24 ++++++++--- imagepy/core/app/loader.py | 15 +++---- imagepy/core/engine/__init__.py | 1 - imagepy/core/engine/dataio.py | 1 - imagepy/core/engine/mkdown.py | 16 -------- imagepy/data/config.json | 2 +- imagepy/data/shortcut.json | 2 +- imagepy/menus/Image/resize_plg.py | 2 +- imagepy/menus/Plugins/Manager/shotcut_wgt.py | 2 +- sciapp/action/tabact.py | 42 ++++++++++++++++++++ 11 files changed, 86 insertions(+), 39 deletions(-) delete mode 100644 imagepy/core/engine/mkdown.py diff --git a/imagepy/core/app/imagej.py b/imagepy/core/app/imagej.py index 7e14b9d8..f31667e9 100644 --- a/imagepy/core/app/imagej.py +++ b/imagepy/core/app/imagej.py @@ -71,6 +71,18 @@ def OnDropFiles(self, x, y, path): .PaneBorder( False ).Dock().Resizable().FloatingSize( wx.DefaultSize ).DockFixed( True ) . MinSize(wx.Size(-1, 20)). MaxSize(wx.Size(-1, 20)).Layer( 10 ) ) + def add_plugin(self, name, plg): + self.manager('plugin').add(name, plg) + + def flatten(self, plgs, lst=None): + if lst is None: lst = [] + if isinstance(plgs, tuple): + if callable(plgs[1]): lst.append((plgs)) + else: self.flatten(plgs[1], lst) + if isinstance(plgs, list): + for i in plgs: self.flatten(i, lst) + return lst + def _load_all(self): lang = Source.manager('config').get('language') dic = Source.manager('dictionary').get('common', tag=lang) or {} @@ -78,8 +90,8 @@ def _load_all(self): for i in self.auimgr.GetAllPanes(): i.Caption(dic[i.caption] if i.caption in dic else i.caption) self.auimgr.Update() - plgs, errplg = load_plugins() + for name, plg in self.flatten(plgs): self.add_plugin(name, plg) self.load_menu(plgs) dtool = Source.manager('tools').get('default') tols, errtol = load_tools() @@ -99,7 +111,7 @@ def load_menu(self, data): lang = Source.manager('config').get('language') ls = Source.manager('dictionary').gets(tag=lang) short = Source.manager('shortcut').gets() - acc = self.menubar.load(data) + acc = self.menubar.load(data, dict([i[:2] for i in short])) self.translate(dict([(i,j[i]) for i,j,_ in ls]))(self.menubar) self.SetAcceleratorTable(acc) @@ -391,7 +403,7 @@ def one(cmds, after): cmd = cmds.pop(0) title, para = cmd.split('>') print(title, para) - plg = Source.manager('plugin').get(name=title)() + plg = self.manager('plugin').get(name=title)() after = lambda cmds=cmds: one(cmds, one) if len(cmds)==0: after = callafter wx.CallAfter(plg.start, self, eval(para), after) diff --git a/imagepy/core/app/imagepy.py b/imagepy/core/app/imagepy.py index f0ca7b65..71199bfe 100644 --- a/imagepy/core/app/imagepy.py +++ b/imagepy/core/app/imagepy.py @@ -39,6 +39,7 @@ def __init__( self, parent ): self.Layout() self.auimgr.Update() + self.Fit() self.Centre( wx.BOTH ) self.Bind(wx.EVT_CLOSE, self.on_close) @@ -69,7 +70,19 @@ def OnDropFiles(self, x, y, path): self.auimgr.AddPane( stapanel, aui.AuiPaneInfo() .Bottom() .CaptionVisible( False ).PinButton( True ) .PaneBorder( False ).Dock().Resizable().FloatingSize( wx.DefaultSize ).DockFixed( True ) . MinSize(wx.Size(-1, 20)). MaxSize(wx.Size(-1, 20)).Layer( 10 ) ) - + + def add_plugin(self, name, plg): + self.manager('plugin').add(name, plg) + + def flatten(self, plgs, lst=None): + if lst is None: lst = [] + if isinstance(plgs, tuple): + if callable(plgs[1]): lst.append((plgs)) + else: self.flatten(plgs[1], lst) + if isinstance(plgs, list): + for i in plgs: self.flatten(i, lst) + return lst + def _load_all(self): lang = Source.manager('config').get('language') dic = Source.manager('dictionary').get('common', tag=lang) or {} @@ -78,8 +91,8 @@ def _load_all(self): for i in self.auimgr.GetAllPanes(): i.Caption(dic[i.caption] if i.caption in dic else i.caption) self.auimgr.Update() - - plgs, errplg = load_plugins() + plgs, errplg = load_plugins() + for name, plg in self.flatten(plgs): self.add_plugin(name, plg) self.load_menu(plgs) dtool = Source.manager('tools').get('default') tols, errtol = load_tools() @@ -99,7 +112,8 @@ def load_menu(self, data): lang = Source.manager('config').get('language') ls = Source.manager('dictionary').gets(tag=lang) short = Source.manager('shortcut').gets() - acc = self.menubar.load(data) + + acc = self.menubar.load(data, dict([i[:2] for i in short])) self.translate(dict([(i,j[i]) for i,j,_ in ls]))(self.menubar) self.SetAcceleratorTable(acc) @@ -454,7 +468,7 @@ def run_macros(self, cmd, callafter=None): def one(cmds, after): cmd = cmds.pop(0) title, para = cmd.split('>') - plg = Source.manager('plugin').get(name=title)() + plg = self.manager('plugin').get(name=title)() after = lambda cmds=cmds: one(cmds, one) if len(cmds)==0: after = callafter wx.CallAfter(plg.start, self, eval(para), after) diff --git a/imagepy/core/app/loader.py b/imagepy/core/app/loader.py index 180fa86e..03035261 100644 --- a/imagepy/core/app/loader.py +++ b/imagepy/core/app/loader.py @@ -6,7 +6,7 @@ """ import os, sys, os.path as osp from glob import glob -from ..engine import Macros, MkDown, Widget, Report +from ..engine import Macros, Widget, Report from sciapp import Source from ... import root_dir from codecs import open @@ -26,11 +26,11 @@ def extend_plugins(path, lst, err): elif i[-3:] == 'rpt': pt = os.path.join(root_dir,path) rst.append(Report(i[:-4], pt+'/'+i)) - Source.manager('plugin').add(obj=rst[-1], name=rst[-1].title) + # Source.manager('plugin').add(obj=rst[-1], name=rst[-1].title) elif i[-3:] in {'.md', '.mc', '.wf'}: p = os.path.join(os.path.join(root_dir, path), i).replace('\\','/') rst.append(Macros(i[:-3], ['Open>{"path":"%s"}'%p])) - Source.manager('plugin').add(rst[-1].title, rst[-1]) + # Source.manager('plugin').add(rst[-1].title, rst[-1]) elif i[-6:] in ['wgt.py', 'gts.py']: try: rpath = path.replace('/', '.').replace('\\','.') @@ -50,11 +50,11 @@ def extend_plugins(path, lst, err): if hasattr(plg, 'plgs'): rst.extend([j for j in plg.plgs]) for p in plg.plgs: - if not isinstance(p, str): - Source.manager('plugin').add(p.title, p) + if not isinstance(p, str): pass + # Source.manager('plugin').add(p.title, p) else: rst.append(plg.Plugin) - Source.manager('plugin').add(plg.Plugin.title, plg.Plugin) + # Source.manager('plugin').add(plg.Plugin.title, plg.Plugin) except Exception as e: err.append((path, i, sys.exc_info()[1])) return rst @@ -102,9 +102,6 @@ def extend_tools(path, lst, err): p = os.path.join(os.path.join(root_dir,path), i).replace('\\','/') rst.append((Macros(i[:-3], ['Open>{"path":"%s"}'%p]), os.path.join(root_dir, path)+'/'+i[:-3]+'.gif')) - - #rst.append((Macros(i[:-3], [getpath(pt, i) for i in cmds]), - # os.path.join(root_dir, path)+'/'+i[:-3]+'.gif')) else: try: rpath = path.replace('/', '.').replace('\\','.') diff --git a/imagepy/core/engine/__init__.py b/imagepy/core/engine/__init__.py index 958e5d6c..97159050 100644 --- a/imagepy/core/engine/__init__.py +++ b/imagepy/core/engine/__init__.py @@ -1,5 +1,4 @@ from .macros import Macros -from .mkdown import MkDown from .widget import Widget from sciapp.action import Table from sciapp.action import Filter diff --git a/imagepy/core/engine/dataio.py b/imagepy/core/engine/dataio.py index 62fa1304..9e1dd449 100644 --- a/imagepy/core/engine/dataio.py +++ b/imagepy/core/engine/dataio.py @@ -42,7 +42,6 @@ def show(self): #process def run(self, para = None): add_recent(para['path']) - fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) readers = Source.manager('reader').gets(name=fe[1:], tag=self.tag) diff --git a/imagepy/core/engine/mkdown.py b/imagepy/core/engine/mkdown.py deleted file mode 100644 index 5890c140..00000000 --- a/imagepy/core/engine/mkdown.py +++ /dev/null @@ -1,16 +0,0 @@ -class MkDown: - def __init__(self, title, cont, url=''): - self.title = title - self.cont = cont - self.url = url - - def __call__(self): return self - - def run(self): IPy.show_md(self.title, self.cont, self.url) - - def start(self, para=None, callafter=None): self.run() - -if __name__ == '__main__': - app = wx.App() - show_help('title', 'abc', '') - app.MainLoop() \ No newline at end of file diff --git a/imagepy/data/config.json b/imagepy/data/config.json index 5f243f37..454716d6 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["recent", ["C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc", "C:/Users/Administrator/Desktop/imagepy/imagepy/menus/File/Samples Online/DEM.mc"], null], ["language", "Chinese", null], ["uistyle", "imagepy", null]] \ No newline at end of file +[["recent", ["C:/Users/54631/Desktop/\u6d77\u51b0\u62a5\u4ef7/testmacros.mc", "C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc"], null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["language", "Chinese", null], ["uistyle", "imagej", null]] \ No newline at end of file diff --git a/imagepy/data/shortcut.json b/imagepy/data/shortcut.json index 0ba8a061..27d4824d 100644 --- a/imagepy/data/shortcut.json +++ b/imagepy/data/shortcut.json @@ -1 +1 @@ -[["Invert", "Ctrl-I", null], ["Open", "Ctrl-O", null], ["New", "Ctrl-N", null], ["8-bit", "Ctrl-8", null], ["Undo", "Ctrl-Z", null], ["Save", "Ctrl-S", null]] \ No newline at end of file +[["Save", "Ctrl-S", null], ["Undo", "Ctrl-Z", null], ["8-bit", "Ctrl-8", null], ["New", "Ctrl-N", null], ["Open", "Ctrl-O", null], ["Invert", "Ctrl-I", null]] \ No newline at end of file diff --git a/imagepy/menus/Image/resize_plg.py b/imagepy/menus/Image/resize_plg.py index 5bd0535e..99c9efca 100644 --- a/imagepy/menus/Image/resize_plg.py +++ b/imagepy/menus/Image/resize_plg.py @@ -24,7 +24,7 @@ def run(self, ips, imgs, para = None): size = np.round([ips.slices*kz, ips.shape[1]*kx, ips.shape[0]*ky]) n, w, h = size.astype(np.uint16) - buf = np.zeros((n, w, h, ips.channels), dtype=ips.dtype) + buf = np.zeros((n, h, w, ips.channels), dtype=ips.dtype) if kz==1: for i in range(ips.slices): img = imgs[i].reshape(ips.shape+(-1,)) diff --git a/imagepy/menus/Plugins/Manager/shotcut_wgt.py b/imagepy/menus/Plugins/Manager/shotcut_wgt.py index 261d99b6..a49fb1ce 100644 --- a/imagepy/menus/Plugins/Manager/shotcut_wgt.py +++ b/imagepy/menus/Plugins/Manager/shotcut_wgt.py @@ -63,7 +63,7 @@ def __init__( self, parent, app=None): #def list_plg(self, lst, items def load(self): - lst = Source.manager('plugin').names() + lst = self.app.manager('plugin').names() self.plgs = [[i, Source.manager('shortcut').get(i)] for i in lst] for i in self.plgs: if i[1]==None:i[1]='' diff --git a/sciapp/action/tabact.py b/sciapp/action/tabact.py index e69de29b..ab88e2eb 100644 --- a/sciapp/action/tabact.py +++ b/sciapp/action/tabact.py @@ -0,0 +1,42 @@ +from .action import SciAction + +class TabAction(SciAction): + title = 'Table Action' + note, para, view = [], None, None + + def __init__(self): pass + + def show(self): + tps, data, snap = self.tps, self.tps.data, self.tps.snap + f = lambda p: self.run(tps, data, snap, p) or tps.update() + return self.app.show_para(self.title, self.view, self.para, f, on_ok=None, + on_cancel=lambda x=self.tps:self.cancel(x), + preview='preview' in self.note, modal=True) + + def cancel(self, tps): + tps.data[:] = pts.snap + tps.update() + + def run(self, tps, snap, data, para = None): + print('I am running!!!') + + def start(self, app, para=None, callback=None): + self.app, self.tps = app, app.get_tab() + if 'auto_snap' in self.note: + if 'auto_msk' in self.note: mode = True + elif 'msk_not' in self.note: mode = False + else: mode = None + self.tps.snapshot(mode, 'num_only' in self.note) + if para!=None: + self.ok(self.tps, para, callback) + elif self.view==None: + if not self.__class__.show is Table.show: + if self.show(): + self.run(self.tps, para, callback) + else: self.ok(self.tps, para, callback) + elif self.modal: + if self.show(): + self.ok(self.tps, para, callback) + else:self.cancel(self.tps) + if not self.dialog is None: self.dialog.Destroy() + else: self.show() \ No newline at end of file From 6680cebbe56e91a336d2ae5c77be74c242f384d8 Mon Sep 17 00:00:00 2001 From: PengGuanjun Date: Sat, 27 Jun 2020 23:24:54 +0800 Subject: [PATCH 276/343] translate --- imagepy/lang/Chinese/plugins/analysis.dic | 76 +++++ imagepy/lang/Chinese/plugins/process.dic | 339 ++++++++++++++++++++++ 2 files changed, 415 insertions(+) create mode 100644 imagepy/lang/Chinese/plugins/analysis.dic create mode 100644 imagepy/lang/Chinese/plugins/process.dic diff --git a/imagepy/lang/Chinese/plugins/analysis.dic b/imagepy/lang/Chinese/plugins/analysis.dic new file mode 100644 index 00000000..8e985a6b --- /dev/null +++ b/imagepy/lang/Chinese/plugins/analysis.dic @@ -0,0 +1,76 @@ +label image::标记图 + +mark boundaries::标记边线 + +label render::标记着色 + color::标记颜色个数 + +frequence::灰度值频数 + +pixel statistic::灰度值统计特性 + +histogram::直方图 + +points value::点ROI灰度值 + +shortest route::灰度图最短路径 + step::通路路径成本值 + +pixel cluster::像素聚类 + +gray cluster::灰度图聚类 + torlerance::公差 + +color cluster::彩色图聚类 + torlerance::公差 + +gray cluster 3d::3D灰度图聚类 + +color cluster 3d::3D彩色图聚类 + +gray points statistic::灰度图点ROI统计特性 + +color points statistic::彩色图点ROI统计特性 + +k-means::K均值聚类 + k::质心数 + iterate::迭代次数 + threshold::变化阈值 + resample::重采样 + +region analysis::区域分析 + +geometry analysis::几何分析 + +geometry filter::几何筛选 + front color::前景灰度值 + back color::背景灰度值 + area::面积 + perimeter::周长 + holes::孔洞数 + solidity::紧固度 + eccentricity::偏心率 + +property marker::属性标记 + +connective analysis::连通性分析 + +intensity analysis::灰度值分析 + +intensity filter::灰度值统计特性过滤 + +skeleton network::骨架网络 + +build graph::建图 + +graph summarise::图摘要 + +remove isolate node::移除孤立节点 + +remove 2path node::移除第二路径节点 + +cut branch::分支剪切 + +cut by roi::ROI剪切 + +graph shortest path::图最短路径 \ No newline at end of file diff --git a/imagepy/lang/Chinese/plugins/process.dic b/imagepy/lang/Chinese/plugins/process.dic new file mode 100644 index 00000000..46353555 --- /dev/null +++ b/imagepy/lang/Chinese/plugins/process.dic @@ -0,0 +1,339 @@ +Math::灰度数值运算 + +Add::全局相加 + +Subtract::全局相减 + +Multiply::全局相乘 + +Max::限定最大灰度 + +Min::限定最小灰度 + +Square Root::平方根变换 + +Gamma::伽马指数变换 + +Binary::二值图操作 + +Binary Dilation::二值膨胀 + width::结构元宽 + height::结构元高 + +Binary Erosion::二值腐蚀 + width::结构元宽 + height::结构元高 + +Binary Closing::二值闭合 + width::结构元宽 + height::结构元高 + +Binary Opening::二值开口 + width::结构元宽 + height::结构元高 + +Binary Outline::二值轮廓线 + +Fill Holes::填充孔洞 + +Clear Border::清理边界线 + +Binary ConvexHull::二值凸包 + +Skeleton::骨架线 + +Medial Axis::中值线 + +Distance Transform::距离变换 + +Binary Watershed::二值分水岭 + tolerance::公差值 + +Binary Voronoi::二值韦恩剖分图 + +Filters::滤波 + +Uniform::均匀滤波 + size::核大小 + +Gaussian::高斯滤波 + sigma::标准差大小 + +Maximum::最大值滤波 + size::核大小 + +Minimun::最小值滤波 + size::核大小 + +Median::中值滤波 + size::核大小 + +Percent::百分比滤波 + size::核大小 + percent::百分比 + +Prewitt::prewitt滤波 + +Sobel::Sobel滤波 + +Laplace::拉普拉斯滤波 + +Gaussian Laplace::高斯拉普拉斯q滤波 + sigma::标准差大小 + DOG::高斯差分 + sigma1::标准差参数1 + sigma2::标准差参数2 + Variance::方差 + size::核大小 + +Laplace Sharp::拉普拉斯锐化 + weight::权重大小 + +Unsharp Mask::非锐化蒙板 + sigma::标准差大小 + weight::权重大小 + +FFT::傅里叶变换 + +FFT::傅里叶变换 + +Inverse FFT::傅里叶逆变换 + +Zero Center::零中心 + +Zero Edge::零边 + +Split Real And Image::实数和图分离 + +Threshold::阈值 + +Simple Threshold::简单阈值 + +Auto Threshold::自动阈值 + +Adaptive Threshold::自适应阈值 + blocksize::局部块大小 + offset::偏离值 + +Niblack Threshold::Niblack阈值 + blocksize::局部块大小 + offset::偏离值 + +Sauvola Threshold::Sauvola阈值 + blocksize::局部块大小 + offset::偏离值 + +Hysteresis Threshold::滞止阈值 + +Hydrology::水文法分割 + +Find Maximum::寻找极大值 + tolerance::公差值 + +Find Minimum::寻找极小值 + tolerance::公差值 + +Find Isolin::寻找海岸线 + low::低阈值 + high::高阈值 + step::步长 + +Find Riedge::寻找山脊线 + sigma::标准差大小 + +Active Riedge::主动山脊线分割 + +Find Watershed::寻找分水岭分割 + sigma::标准差大小 + +Up And Down Watershed::向上向下分水岭 + +Watershed With ROI::带选区分水岭 + +Features::特征点选取 + +Canny::Canny边缘检测 + sigma::标准差大小 + +Harris::哈尔特征点 + sigma::标准差大小 + K::灵敏度大小 + +Kitchen Rosenfeld::Kitchen特征 + cval::边界外值 + +Moravec::Moravec特征 + size::窗口大小 + +Tomasi::Tomasi特征 + sigma::标准差大小 + +Frangi::Frangi特征 + sigma start::标准差起始值 + sigma end::标准差终止值 + sigma step::标准差步长 + alpha::blob灵敏度 + beta::纹理灵敏度 + gamma::伽马值 + +Meijering::Meijering特征 + sigma start::标准差起始值 + sigma end::标准差终止值 + sigma step::标准差步长 + +Sato::Sato特征 + sigma start::标准差起始值 + sigma end::标准差终止值 + sigma step::标准差步长 + +Hessian::Hessian变换 + sigma start::标准差起始值 + sigma end::标准差终止值 + sigma step::标准差步长 + alpha::blob灵敏度 + beta::纹理灵敏度 + gamma::伽马值 + +Blob Dog::Blob Dog特征 + min::最小标准偏差 + max::最大标准偏差 + ratio::标准偏差间比率 + threshold::绝对下界 + overlab::重叠分数 + +Blob Doh::Blob Doh特征 + min::最小标准偏差 + max::最大标准偏差 + ratio::标准偏差间比率 + threshold::绝对下界 + overlab::重叠分数 + +Blob Log::Blob Log特征 + min::最小标准偏差 + max::最大标准偏差 + ratio::标准偏差间比率 + threshold::绝对下界 + overlab::重叠分数 + +Segment::分割 + +SLIC Superpixel::超像素分割 + segments::分段标签数量 + campactness::紧凑性 + max_iter::最大迭代次数 + sigma::高斯平滑内核的宽度 + Quick Shift::快速转换聚类分割 + ratio::色彩、图像空间平衡比例 + kernel_size::高斯平滑内核的宽度 + distance::数据的切点距离 + sigma::高斯平滑作为预处理的可选宽度 + +Feizenszwalb::Feizenszwalb分割 + scale::尺度参数 + sigma::高斯平滑内核的宽度 + min_size::最小组件大小 + +SLIC Superpixel Label::超像素分割块标记 + segments::分段标签数量 + campactness::紧凑性 + max_iter::最大迭代次数 + sigma::高斯平滑内核的宽度 + +Quickshift Label::快速Shift分割块标记 + ratio::色彩、图像空间平衡比例 + kernel_size::高斯平滑内核的宽度 + distance::数据的切点距离 + sigma::高斯平滑作为预处理的可选宽度 + +Felzenszwalb Lable::Feizenszwalb分割块标记 + scale::尺度参数 + sigma::高斯平滑内核的宽度 + min_size::最小组件大小 + +RAG Threshold::区域邻接图阈值 + threshold::阈值 + sigma::sigma参数 + +RAG Cut Normalized::RAG剪切正则 + threshold::阈值 + num::N切分 + sigma::sigma参数 + +RAG Merge Hierarchical::RAG分层融合 + threshold::N切分 + sigma::sigma参数 + +Evolving Level Set::演化水平集 + mu::距离正则项的系数R(phi) + lambda1::加权长度项L(phi)的系数 + lambda2::加权面积项A(phi)的系数 + tol::公差 + max_iter::最大迭代数 + dt::dt参数 + +Morphological Snake Fit::形态学蛇行拟合 + iterations::迭代次数 + smoothing::平滑因子 + lambda1::lambda1参数 + lambda2::lambda2参数 + +Bound Snake Fit::蛇行包拟合 + iterations::迭代次数 + smoothing::平滑因子 + threshold::阈值 + balloon::balloon参数 + +ROI Snake Fit::选区蛇行拟合 + iterations::迭代次数 + smoothing::平滑因子 + threshold::阈值 + balloon::balloon参数 + +Random Walker::随机漫步分割 + beta::惩罚系数 + tolerance::公差 + +Classify::分类器 + +Label Tool::特征标记工具 + +Feature Classify Panel::特征分类器面板 + +Random Forest Classify::随机森林分类器 + estimators::估计参数 + depth::树深度 + grade::grade参数 + sigma::sigma参数 + +ExtraTrees Classify::拓展随机数分类器 + estimators::估计参数 + depth::树深度 + grade::grade参数 + sigma::sigma参数 + +Bagging Classify::词袋分类器 + estimators::估计参数 + depth::树深度 + grade::grade参数 + sigma::sigma参数 + +AdaBoost Classify::AdaBoost分类器 + estimators::估计参数 + depth::树深度 + grade::grade参数 + sigma::sigma参数 + +Gradient Boosting Classsify::梯度增强分类器 + estimators::估计参数 + depth::树深度 + learn::学习率 + grade::grade参数 + sigma::sigma参数 + +Feature Predictor::特征推断 + +Build Mark Image::标记图生成器 + +Fragment Repair::小块修复 + +Image Calculator::图像间四则运算 From 46bec4c6d99886e4f7fd57f53821fac2168b6351 Mon Sep 17 00:00:00 2001 From: PengGuanjun Date: Sat, 27 Jun 2020 23:27:04 +0800 Subject: [PATCH 277/343] Update selection.dic --- imagepy/lang/Chinese/plugins/selection.dic | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/imagepy/lang/Chinese/plugins/selection.dic b/imagepy/lang/Chinese/plugins/selection.dic index 021c8925..afead2fc 100644 --- a/imagepy/lang/Chinese/plugins/selection.dic +++ b/imagepy/lang/Chinese/plugins/selection.dic @@ -40,8 +40,11 @@ ROI Symmetric Diff::与任意选区联合 Name::求联合选区名 ROI Setting::选区设置 - roi::选区颜色 - line width::线宽 + line::轮廓线颜色 + face::内部颜色 + text::文本颜色 + width::线宽 + text::文本大小 ROI Ctrl Panel::选区控制面板 Manage::管理 @@ -55,15 +58,15 @@ ROI Ctrl Panel::选区控制面板 Inflate::扩张 Shrink::收缩 Convex::凸包 - Bound::外接矩 + Bound::外接矩形 Clip::裁剪 Invert::反向 Relationship::关系运算 - Intersect::香蕉 + Intersect::相交 Union::合并 Difference::减去 - Sym Diff::抑或 + Sym Diff::异或 Draw::绘图 Sketch::描边 Clear::清除内部 - Clear Out::清除外部 \ No newline at end of file + Clear Out::清除外部 From d3a565429a69e209d42daacdc22f23540bbc70ab Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 28 Jun 2020 08:08:27 +0800 Subject: [PATCH 278/343] nothing --- imagepy/core/app/imagej.py | 5 +- imagepy/core/app/imagepy.py | 18 ++--- imagepy/core/app/loader.py | 4 +- imagepy/core/engine/report.py | 2 +- imagepy/data/config.json | 2 +- imagepy/data/shortcut.json | 2 +- imagepy/lang/Chinese/plugins/selection.dic | 69 ---------------- imagepy/menus/Image/Adjust/normalize_plg.py | 4 +- .../menus/Plugins/Install/installplg_plgs.py | 4 +- imagepy/menus/Plugins/Manager/plglist_wgt.py | 4 +- imagepy/menus/Plugins/Manager/shotcut_wgt.py | 2 +- imagepy/menus/Process/Binary/binary_plgs.py | 2 +- imagepy/menus/Process/Math/math_plgs.py | 2 +- sciapp/action/plugin/shpact.py | 1 + sciapp/app.py | 81 ++++++++++++++++++- sciapp/util/shputil.py | 2 +- 16 files changed, 106 insertions(+), 98 deletions(-) delete mode 100644 imagepy/lang/Chinese/plugins/selection.dic diff --git a/imagepy/core/app/imagej.py b/imagepy/core/app/imagej.py index f31667e9..8c0b1af4 100644 --- a/imagepy/core/app/imagej.py +++ b/imagepy/core/app/imagej.py @@ -228,7 +228,7 @@ def on_close_mesh(self, event): self.remove_mesh_win(event.GetEventObject().canvas) event.Skip() - def set_info(self, value): + def info(self, value): lang = Source.manager('config').get('language') dics = Source.manager('dictionary').gets(tag=lang) dic = dict(j for i in dics for j in i[1].items()) @@ -425,9 +425,6 @@ def show(self, tag, cont, title): self.show_workflow(cont, title) else: self.alert('no view for %s!'%tag) - def info(self, cont): - wx.CallAfter(self.txt_info.SetLabel, cont) - def _alert(self, info, title='ImagePy'): lang = Source.manager('config').get('language') dics = Source.manager('dictionary').gets(tag=lang) diff --git a/imagepy/core/app/imagepy.py b/imagepy/core/app/imagepy.py index 71199bfe..6c11102c 100644 --- a/imagepy/core/app/imagepy.py +++ b/imagepy/core/app/imagepy.py @@ -70,9 +70,6 @@ def OnDropFiles(self, x, y, path): self.auimgr.AddPane( stapanel, aui.AuiPaneInfo() .Bottom() .CaptionVisible( False ).PinButton( True ) .PaneBorder( False ).Dock().Resizable().FloatingSize( wx.DefaultSize ).DockFixed( True ) . MinSize(wx.Size(-1, 20)). MaxSize(wx.Size(-1, 20)).Layer( 10 ) ) - - def add_plugin(self, name, plg): - self.manager('plugin').add(name, plg) def flatten(self, plgs, lst=None): if lst is None: lst = [] @@ -91,13 +88,19 @@ def _load_all(self): for i in self.auimgr.GetAllPanes(): i.Caption(dic[i.caption] if i.caption in dic else i.caption) self.auimgr.Update() - plgs, errplg = load_plugins() - for name, plg in self.flatten(plgs): self.add_plugin(name, plg) + plgs, errplg = load_plugins() + self.plugin_manager.remove() + for name, plg in self.flatten(plgs): + self.add_plugin(name, plg, 'plugin') self.load_menu(plgs) dtool = Source.manager('tools').get('default') tols, errtol = load_tools() + for name, plg in self.flatten(tols): + self.add_plugin(name, plg, 'tool') self.load_tool(tols, dtool or 'Transform') wgts, errwgt = load_widgets() + for name, plg in self.flatten(wgts): + self.add_plugin(name, plg, 'widget') self.load_widget(wgts) err = errplg + errtol + errwgt if len(err)>0: @@ -293,7 +296,7 @@ def on_close_mesh(self, event): self.remove_mesh(canvas3d.mesh) self.remove_mesh_win(canvas3d) - def set_info(self, value): + def info(self, value): lang = Source.manager('config').get('language') dics = Source.manager('dictionary').gets(tag=lang) dic = dict(j for i in dics for j in i[1].items()) @@ -490,9 +493,6 @@ def show(self, tag, cont, title): self.show_workflow(cont, title) else: self.alert('no view for %s!'%tag) - def info(self, cont): - wx.CallAfter(self.txt_info.SetLabel, cont) - def _alert(self, info, title='ImagePy'): lang = Source.manager('config').get('language') dics = Source.manager('dictionary').gets(tag=lang) diff --git a/imagepy/core/app/loader.py b/imagepy/core/app/loader.py index 03035261..b90145e2 100644 --- a/imagepy/core/app/loader.py +++ b/imagepy/core/app/loader.py @@ -112,7 +112,7 @@ def extend_tools(path, lst, err): os.path.join(root_dir, path)+'/'+i.split('_')[0]+'.gif')) except Exception as e: err.append((path, i, sys.exc_info()[1])) - for i in rst:Source.manager('tool').add(obj=i[0], name=i[0].title) + # for i in rst:Source.manager('tool').add(obj=i[0], name=i[0].title) return rst def sort_tools(catlog, lst): @@ -161,7 +161,7 @@ def extend_widgets(path, lst, err): rst.append(plg.Plugin) except Exception as e: err.append((path, i, sys.exc_info()[1])) - for i in rst:Source.manager('widget').add(obj=i, name=i.title) + #for i in rst:Source.manager('widget').add(obj=i, name=i.title) return rst def sort_widgets(catlog, lst): diff --git a/imagepy/core/engine/report.py b/imagepy/core/engine/report.py index e7461b8b..dc35d088 100644 --- a/imagepy/core/engine/report.py +++ b/imagepy/core/engine/report.py @@ -31,7 +31,7 @@ def runasyn(self, wb, info, key, para = None, callback = None): start = time() xlreport.fill_value(wb, info, para) wb.save(para['path']) - self.app.set_info('%s: cost %.3fs'%(self.title, time()-start)) + self.app.info('%s: cost %.3fs'%(self.title, time()-start)) self.app.remove_task(self) if callback!=None:callback() diff --git a/imagepy/data/config.json b/imagepy/data/config.json index 454716d6..5cb2d834 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["recent", ["C:/Users/54631/Desktop/\u6d77\u51b0\u62a5\u4ef7/testmacros.mc", "C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc"], null], ["roi_style", {"color": [0, 255, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [255, 128, 0], "size": 8}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["language", "Chinese", null], ["uistyle", "imagej", null]] \ No newline at end of file +[["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["recent", ["C:/Users/54631/Desktop/\u6d77\u51b0\u62a5\u4ef7/testmacros.mc", "C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc"], null], ["uistyle", "imagepy", null], ["language", "Chinese", null], ["roi_style", {"color": [255, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 255, 0], "size": 8}, null]] \ No newline at end of file diff --git a/imagepy/data/shortcut.json b/imagepy/data/shortcut.json index 27d4824d..0ba8a061 100644 --- a/imagepy/data/shortcut.json +++ b/imagepy/data/shortcut.json @@ -1 +1 @@ -[["Save", "Ctrl-S", null], ["Undo", "Ctrl-Z", null], ["8-bit", "Ctrl-8", null], ["New", "Ctrl-N", null], ["Open", "Ctrl-O", null], ["Invert", "Ctrl-I", null]] \ No newline at end of file +[["Invert", "Ctrl-I", null], ["Open", "Ctrl-O", null], ["New", "Ctrl-N", null], ["8-bit", "Ctrl-8", null], ["Undo", "Ctrl-Z", null], ["Save", "Ctrl-S", null]] \ No newline at end of file diff --git a/imagepy/lang/Chinese/plugins/selection.dic b/imagepy/lang/Chinese/plugins/selection.dic deleted file mode 100644 index 021c8925..00000000 --- a/imagepy/lang/Chinese/plugins/selection.dic +++ /dev/null @@ -1,69 +0,0 @@ -Select All::整图全选 - -Select None::取消选区 - -ROI Inflate::选区外扩 - -ROI Shrink::选区内缩 - -ROI Convex Hull::选区凸包变换 - -ROI Bound Box::选区包围盒变换 - -ROI Clip::选区变形 - -ROI Invert::选区补集 - -ROI Open::打开选区文件 - -ROI Save::保存选区文件 - -ROI Add::逐个添加选区 - Name::新增选区名 - -ROI Load::加载任意选区 - Name::加载选区名 - -ROI Remove::移除任意选区 - Name::移除选区名 - -ROI Intersect::与任意选区交集 - Name::求交集选区名 - -ROI Union::与任意选区并集 - Name::求并集选区名 - -ROI Difference::与任意选区差集 - Name::求差集选区名 - -ROI Symmetric Diff::与任意选区联合 - Name::求联合选区名 - -ROI Setting::选区设置 - roi::选区颜色 - line width::线宽 - -ROI Ctrl Panel::选区控制面板 - Manage::管理 - Add::添加 - Load::加载 - Update::更新 - Remove::移除 - Open::打开 - Save::保存 - Operate::几何运算 - Inflate::扩张 - Shrink::收缩 - Convex::凸包 - Bound::外接矩 - Clip::裁剪 - Invert::反向 - Relationship::关系运算 - Intersect::香蕉 - Union::合并 - Difference::减去 - Sym Diff::抑或 - Draw::绘图 - Sketch::描边 - Clear::清除内部 - Clear Out::清除外部 \ No newline at end of file diff --git a/imagepy/menus/Image/Adjust/normalize_plg.py b/imagepy/menus/Image/Adjust/normalize_plg.py index f6f09854..5a9a3356 100644 --- a/imagepy/menus/Image/Adjust/normalize_plg.py +++ b/imagepy/menus/Image/Adjust/normalize_plg.py @@ -16,7 +16,7 @@ def run(self, ips, imgs, para = None): lim = np.zeros([len(imgs), 2], dtype=imgs[0].dtype) dic = {np.uint8:255, np.uint16:65535, np.float32:1, np.float64:1} - self.app.set_info('count range ...') + self.app.info('count range ...') for i in range(len(imgs)): lim[i] = imgs[i].min(), imgs[i].max() self.progress(i, len(imgs)) @@ -25,7 +25,7 @@ def run(self, ips, imgs, para = None): if not para['sb']: lim[:,0] = 0 rg = lim[:,0].min(), lim[:,1].max() if para['is3d']: lim[:] = rg - self.app.set_info('adjust range ...') + self.app.info('adjust range ...') for i in range(len(imgs)): if para['sb']: imgs[i] -= lim[i,0] np.multiply(imgs[i], maxvalue/(lim[i].ptp()), out=imgs[i], casting='unsafe') diff --git a/imagepy/menus/Plugins/Install/installplg_plgs.py b/imagepy/menus/Plugins/Install/installplg_plgs.py index ad7d1767..c9a49a01 100644 --- a/imagepy/menus/Plugins/Install/installplg_plgs.py +++ b/imagepy/menus/Plugins/Install/installplg_plgs.py @@ -46,7 +46,7 @@ def run(self, para=None): domain, name = (url[:-4].replace('.','-')).split('/')[-2:] domain, name = domain.replace('_', '-'), name.replace('_', '-') - self.app.set_info('downloading plugin from %s'%para['repo']) + self.app.info('downloading plugin from %s'%para['repo']) if True == para['proxy']: proxy=para['Protocol']+"://"+para['IP']+":"+para['Port'] @@ -67,7 +67,7 @@ def run(self, para=None): if os.path.exists(destpath): shutil.rmtree(destpath) os.rename(os.path.join(path_cache, folder), destpath) zipf.close() - self.app.set_info('installing requirement liberies') + self.app.info('installing requirement liberies') self.prgs = None cmds = [sys.executable, '-m', 'pip', 'install', '-r', '%s/requirements.txt'%destpath] subprocess.call(cmds) diff --git a/imagepy/menus/Plugins/Manager/plglist_wgt.py b/imagepy/menus/Plugins/Manager/plglist_wgt.py index 0082c7d3..45015fa9 100644 --- a/imagepy/menus/Plugins/Manager/plglist_wgt.py +++ b/imagepy/menus/Plugins/Manager/plglist_wgt.py @@ -68,7 +68,7 @@ def __del__( self ): #def list_plg(self, lst, items def load(self): - lst = [i[1] for i in list(Source.manager('plugin').gets())] + lst = [i[1] for i in list(self.app.plugin_manager.gets())] self.plgs = [(i.title, i.__module__) for i in lst] self.plgs.sort() self.buf = self.plgs @@ -83,4 +83,4 @@ def on_search( self, event ): def on_run(self, event): name=self.buf[event.GetIndex()][0] - Source.manager('plugin').get(name)().start(self.app) \ No newline at end of file + self.app.manager('plugin').get(name)().start(self.app) \ No newline at end of file diff --git a/imagepy/menus/Plugins/Manager/shotcut_wgt.py b/imagepy/menus/Plugins/Manager/shotcut_wgt.py index a49fb1ce..a7ee1993 100644 --- a/imagepy/menus/Plugins/Manager/shotcut_wgt.py +++ b/imagepy/menus/Plugins/Manager/shotcut_wgt.py @@ -63,7 +63,7 @@ def __init__( self, parent, app=None): #def list_plg(self, lst, items def load(self): - lst = self.app.manager('plugin').names() + lst = self.app.plugin_names() self.plgs = [[i, Source.manager('shortcut').get(i)] for i in lst] for i in self.plgs: if i[1]==None:i[1]='' diff --git a/imagepy/menus/Process/Binary/binary_plgs.py b/imagepy/menus/Process/Binary/binary_plgs.py index 366da8e3..c6fca418 100644 --- a/imagepy/menus/Process/Binary/binary_plgs.py +++ b/imagepy/menus/Process/Binary/binary_plgs.py @@ -14,7 +14,7 @@ class Closing(Filter): """Closing: derived from imagepy.core.engine.Filter """ - title = 'Binary Closeing' + title = 'Binary Closing' note = ['8-bit', 'auto_msk', 'auto_snap','preview'] para = {'w':3, 'h':3} view = [(int, 'w', (1,15), 0, 'width', 'pix'), diff --git a/imagepy/menus/Process/Math/math_plgs.py b/imagepy/menus/Process/Math/math_plgs.py index ab7d5a07..ed838637 100644 --- a/imagepy/menus/Process/Math/math_plgs.py +++ b/imagepy/menus/Process/Math/math_plgs.py @@ -57,7 +57,7 @@ def run(self, ips, snap, img, para = None): class Sqrt(Filter): """Sqrt_plg: derived from imagepy.core.engine.Filter """ - title = 'Squre Root' + title = 'Square Root' note = ['all', 'auto_msk', 'auto_snap', 'preview'] def run(self, ips, snap, img, para = None): diff --git a/sciapp/action/plugin/shpact.py b/sciapp/action/plugin/shpact.py index 947e30c0..2d9b7f80 100644 --- a/sciapp/action/plugin/shpact.py +++ b/sciapp/action/plugin/shpact.py @@ -1,4 +1,5 @@ from .shpbase import * +from ...util import geom_flatten def inbase(key, btn): status = key['ctrl'], key['alt'], key['shift'] diff --git a/sciapp/app.py b/sciapp/app.py index 3020ad41..7eeccb18 100644 --- a/sciapp/app.py +++ b/sciapp/app.py @@ -9,6 +9,11 @@ def add(self, name, obj, tag=None): if self.unique: self.remove(name, tag) self.objs.insert(0, (name, obj, tag)) + def active(self, name=None, tag=None, obj=None): + objs = self.gets(name, tag, obj) + for i in objs: self.objs.remove(i) + for i in objs: self.objs.insert(0, i) + def set(self, name, obj, tag=None): self.remove(name, tag) self.objs.insert(0, (name, obj, tag)) @@ -70,6 +75,16 @@ def __init__(self): self.mesh_manager = self.manager('mesh') self.wmesh_manager = self.manager('wmesh') self.task_manager = self.manager('task') + self.plugin_manager = self.manager('plugin') + + def add_plugin(self, name, plg, tag=None): + self.plugin_manager.add(name, plg, tag) + + def get_plugin(self, name=None): + return self.plugin_manager.get(name) + + def plugin_names(self, tag=None): + return self.plugin_manager.names(tag) def manager(self, name, value=None): if not name in self.managers: @@ -158,4 +173,68 @@ def add_task(self, task): self.task_manager.add(task.title, task) def remove_task(self, task): - self.task_manager.remove(obj=task) \ No newline at end of file + self.task_manager.remove(obj=task) + + def info(self, value): + print('Information:', value) + + def show_md(self, cont, title='ImagePy'): + print(title, '\n', cont) + + def show_txt(self, cont, title='ImagePy'): + print(title, '\n', cont) + + def alert(self, cont, title='ImagePy'): + print(title, '\n', cont, 'enter to continue!') + input() + + def yes_no(self, cont, title='ImagePy'): + print(title, '\n', cont, 'Y/N?') + return input() in 'yY' + + def getpath(self, title, filt, io, name=''): + print('input file path:') + return input() + + def show_para(self, title, view, para, on_handle=None, on_ok=None, + on_cancel=None, on_help=None, preview=False, modal=True): + print(title+':') + for i in view: + if i[0]==str: para[i[1]] = input(i[2]+': ? '+i[3]+' ') + if i[0]==int: para[i[1]] = int(input(i[4]+': ? '+i[5]+' ')) + if i[0]==float: para[i[1]] = float(input(i[4]+': ? '+i[5]+' ')) + if i[0]=='slide': para[i[1]] = float(input(i[4]+': ? '+i[5]+' ')) + if i[0]==bool: para[i[1]] = bool(input(i[2]+': ')) + if i[0]==list: para[i[1]] = i[3](input('%s %s: %s'%(i[4],i[5],i[2])+' ')) + if i[0]=='chos':para[i[1]] = input('%s:%s '%(i[3],i[2])).split(',') + if i[0]=='color': para[i[1]] = eval(input(i[2]+': ? '+i[3]+' ')) + return para + + def run_macros(self, cmd, callafter=None): + cmds = [i for i in cmd] + def one(cmds, after): + cmd = cmds.pop(0) + title, para = cmd.split('>') + plg = self.manager('plugin').get(name=title)() + after = lambda cmds=cmds: one(cmds, one) + if len(cmds)==0: after = callafter + plg.start(self, eval(para), after) + one(cmds, None) + +if __name__ == '__main__': + app = App() + app.alert('Hello, SciApp!') + + para = {'name':'yxdragon', 'age':10, 'h':1.72, 'w':70, 'sport':True, 'sys':'Mac', 'lan':['C/C++', 'Python'], 'c':(255,0,0)} + + view = [('lab', 'lab', 'This is a questionnaire'), + (str, 'name', 'name', 'please'), + (int, 'age', (0,150), 0, 'age', 'years old'), + (float, 'h', (0.3, 2.5), 2, 'height', 'm'), + ('slide', 'w', (1, 150), 0, 'weight','kg'), + (bool, 'sport', 'do you like sport'), + (list, 'sys', ['Windows','Mac','Linux'], str, 'favourite', 'system'), + ('chos', 'lan', ['C/C++','Java','Python'], 'lanuage you like(multi)'), + ('color', 'c', 'which', 'you like')] + + app.show_para('parameter', view, para) diff --git a/sciapp/util/shputil.py b/sciapp/util/shputil.py index 603305c7..d30f60a5 100644 --- a/sciapp/util/shputil.py +++ b/sciapp/util/shputil.py @@ -1,6 +1,6 @@ import numpy as np from skimage import draw -# from ..object.shape import * +from ..object.shape import * def offset(shp, dx, dy): if shp.dtype in {'rectangle', 'ellipse', 'circle'}: From 8d6d8df76cdcc872c4d4e2f251178769de83d68e Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 28 Jun 2020 16:03:31 +0800 Subject: [PATCH 279/343] all to sciapp --- imagepy/__main__.py | 4 +- imagepy/{core => }/app/__init__.py | 0 imagepy/{core => }/app/imagej.py | 0 imagepy/{core => }/app/imagepy.py | 0 imagepy/{core => }/app/loader.py | 4 +- imagepy/{core => }/app/source.py | 0 imagepy/{core => }/app/startup.py | 4 +- imagepy/core/__init__.py | 0 imagepy/core/engine/__init__.py | 8 --- imagepy/core/engine/report.py | 61 ------------------- imagepy/data/config.json | 2 +- .../Analysis/Pixel Cluster/cluster_plgs.py | 2 +- .../Analysis/Pixel Cluster/kmeans_plgs.py | 2 +- .../Analysis/Pixel Cluster/statistic_plgs.py | 2 +- .../Analysis/Region Analysis/connect_plg.py | 2 +- .../Region Analysis/regionprops_plgs.py | 2 +- .../Region Analysis/statistic_plgs.py | 2 +- .../Analysis/Skeleton Network/graph_plgs.py | 2 +- imagepy/menus/Analysis/label_plg.py | 2 +- imagepy/menus/Analysis/statistic_plg.py | 2 +- imagepy/menus/Edit/edit_plg.py | 2 +- imagepy/menus/File/BMP/bmp_plgs.py | 6 +- imagepy/menus/File/DAT/dat_plgs.py | 6 +- imagepy/menus/File/DICOM/dicom_plgs.py | 4 +- imagepy/menus/File/Export/sequence_plg.py | 4 +- imagepy/menus/File/GIF/gif_plgs.py | 10 +-- imagepy/menus/File/Import/raw_plg.py | 2 +- imagepy/menus/File/Import/roi_plg.py | 2 +- imagepy/menus/File/Import/sequence_plg.py | 8 +-- imagepy/menus/File/JPG/jpg_plgs.py | 10 +-- imagepy/menus/File/MAT/mat_plgs.py | 10 +-- imagepy/menus/File/MarkDown/md_plg.py | 4 +- imagepy/menus/File/Numpy/ndarray_plgs.py | 10 +-- imagepy/menus/File/Open Recent/recent_plgs.py | 2 +- imagepy/menus/File/PNG/png_plgs.py | 6 +- .../File/Samples ImageJ/ijsample_plgs.py | 2 +- .../menus/File/Samples Local/samples_plgs.py | 2 +- imagepy/menus/File/TIF/tif_plgs.py | 14 ++--- imagepy/menus/File/exit_plg.py | 2 +- imagepy/menus/File/new_plg.py | 2 +- imagepy/menus/File/open_plg.py | 6 +- imagepy/menus/File/save_plg.py | 6 +- imagepy/menus/Help/Help_plgs.py | 2 +- imagepy/menus/Help/Language/language_plgs.py | 2 +- .../Image/Adjust/bleachCorrection_plg.py | 2 +- imagepy/menus/Image/Adjust/brightcons_plg.py | 2 +- .../menus/Image/Adjust/colorbalance_plg.py | 2 +- imagepy/menus/Image/Adjust/colorstairs_plg.py | 2 +- imagepy/menus/Image/Adjust/curve_plg.py | 2 +- .../menus/Image/Adjust/enhanceContrast_plg.py | 2 +- imagepy/menus/Image/Adjust/graystairs_plg.py | 2 +- imagepy/menus/Image/Adjust/histogram_plgs.py | 4 +- imagepy/menus/Image/Adjust/normalize_plg.py | 2 +- imagepy/menus/Image/Adjust/threshold_plg.py | 2 +- .../menus/Image/Color/splitandmerge_plgs.py | 2 +- .../Image/Lookup table/lookuptables_plg.py | 2 +- imagepy/menus/Image/Mark/mark_plgs.py | 2 +- imagepy/menus/Image/Stack/orthogonal_plg.py | 2 +- imagepy/menus/Image/Stack/stack_plgs.py | 2 +- .../menus/Image/Transform/Transform_plgs.py | 2 +- imagepy/menus/Image/Type/convert_plg.py | 2 +- imagepy/menus/Image/Type/tostack_plg.py | 2 +- imagepy/menus/Image/background_plg.py | 2 +- imagepy/menus/Image/canvassize_plg.py | 2 +- imagepy/menus/Image/duplicate_plg.py | 2 +- imagepy/menus/Image/resize_plg.py | 2 +- imagepy/menus/Image/setscale_plg.py | 2 +- .../Kit3D/Analysis 3D/pixelstatistic_plgs.py | 2 +- .../Kit3D/Analysis 3D/regionprops3d_plgs.py | 2 +- .../Kit3D/Analysis 3D/surfacemeasure_plg.py | 2 +- .../menus/Kit3D/Binary 3D/binary3d_plgs.py | 12 ++-- imagepy/menus/Kit3D/Features 3D/ridge_plgs.py | 2 +- .../menus/Kit3D/Filters 3D/filters3d_plgs.py | 2 +- .../menus/Kit3D/Network 3D/toolkit3d_plgs.py | 2 +- imagepy/menus/Kit3D/Viewer 3D/colorpts_plg.py | 2 +- imagepy/menus/Kit3D/Viewer 3D/demo_plgs.py | 2 +- imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py | 2 +- .../menus/Kit3D/Viewer 3D/tablepoints_plg.py | 2 +- .../Contribute/Contributions/OpenCV.md | 10 +-- .../Contribute/Contributions/SimpleITK.md | 12 ++-- imagepy/menus/Plugins/Games/crossstick_plg.py | 2 +- imagepy/menus/Plugins/Games/drawstep_plg.py | 2 +- imagepy/menus/Plugins/Games/lifegame_plg.py | 2 +- .../menus/Plugins/Install/installpkg_plgs.py | 2 +- .../menus/Plugins/Install/installplg_plgs.py | 2 +- imagepy/menus/Plugins/Macros/recorder_plg.py | 6 +- imagepy/menus/Plugins/Macros/recorder_wgt.py | 2 +- imagepy/menus/Plugins/Manager/console_wgt.py | 2 +- imagepy/menus/Plugins/Manager/plgtree_wgt.py | 4 +- imagepy/menus/Plugins/Manager/shotcut_wgt.py | 2 +- imagepy/menus/Plugins/Manager/toltree_wgt.py | 4 +- imagepy/menus/Plugins/New/demo_filter.py | 2 +- imagepy/menus/Plugins/New/demo_free.py | 2 +- imagepy/menus/Plugins/New/demo_simple.py | 2 +- imagepy/menus/Plugins/New/demo_tool.py | 4 +- imagepy/menus/Plugins/New/new_plg.py | 2 +- .../menus/Plugins/StackReg/stackreg_plgs.py | 8 +-- imagepy/menus/Plugins/screencap_plg.py | 2 +- imagepy/menus/Plugins/temporal_plg.py | 2 +- imagepy/menus/Plugins/update_plg.py | 4 +- imagepy/menus/Process/Binary/binary_plgs.py | 14 ++--- imagepy/menus/Process/Binary/distance_plgs.py | 4 +- .../menus/Process/Classify/classify_plgs.py | 20 +++--- imagepy/menus/Process/Classify/io_plgs.py | 4 +- imagepy/menus/Process/Classify/label_wgt.py | 2 +- imagepy/menus/Process/Classify/predict_plg.py | 2 +- imagepy/menus/Process/FFT/fft_plgs.py | 2 +- imagepy/menus/Process/Features/blob_plgs.py | 2 +- imagepy/menus/Process/Features/corner_plgs.py | 2 +- imagepy/menus/Process/Features/edge_plgs.py | 2 +- imagepy/menus/Process/Features/ridge_plgs.py | 2 +- imagepy/menus/Process/Filters/classic_plgs.py | 2 +- .../menus/Process/Hydrology/hydrology_plgs.py | 2 +- imagepy/menus/Process/Math/math_plgs.py | 16 ++--- imagepy/menus/Process/Segment/active_plgs.py | 2 +- imagepy/menus/Process/Segment/graph_plgs.py | 2 +- imagepy/menus/Process/Segment/shift_plgs.py | 2 +- .../menus/Process/Threshold/threshold_plgs.py | 2 +- imagepy/menus/Process/calculator_plg.py | 2 +- imagepy/menus/Process/repair_plg.py | 2 +- imagepy/menus/Selection/roiwindow_wgt.py | 2 +- imagepy/menus/Selection/select_plg.py | 2 +- .../menus/Table/Basic Operator/basic_plgs.py | 2 +- imagepy/menus/Table/Chart/plot_plgs.py | 2 +- imagepy/menus/Table/Selection/select_plgs.py | 2 +- imagepy/menus/Table/Signal/signal_plgs.py | 2 +- .../menus/Table/Statistic/frequency_plgs.py | 2 +- imagepy/menus/Table/Statistic/sort_plg.py | 2 +- .../menus/Table/Statistic/statistic_plgs.py | 2 +- imagepy/menus/Table/Table IO/tableio_plgs.py | 14 ++--- .../Universal Generator/gnerator_plgs.py | 2 +- .../menus/Window/Windows Style/style_plgs.py | 2 +- imagepy/menus/Window/widgets_plgs.py | 6 +- imagepy/menus/Window/windowskiller_plg.py | 2 +- imagepy/tools/Draw/Route_tol.py | 2 +- imagepy/tools/Draw/flood3d_tol.py | 2 +- imagepy/tools/Measure/setting_tol.py | 2 +- imagepy/tools/Toolkit3D/flood3d_tol.py | 2 +- imagepy/tools/Transform/rotate_tol.py | 6 +- imagepy/tools/Transform/scale_tol.py | 2 +- sciapp/action/__init__.py | 2 +- sciapp/action/advanced/__init__.py | 4 +- .../action/advanced}/dataio.py | 35 +++-------- .../action/advanced}/macros.py | 0 sciapp/action/advanced/table.py | 2 +- .../action/advanced}/widget.py | 0 sciapp/action/tolact.py | 4 +- sciwx/app/canvasapp.py | 2 +- sciwx/app/imgapp.py | 2 +- sciwx/app/miniapp.py | 2 +- sciwx/widgets/toolbar.py | 2 +- sciwx/widgets/workflow.py | 4 +- 152 files changed, 252 insertions(+), 340 deletions(-) rename imagepy/{core => }/app/__init__.py (100%) rename imagepy/{core => }/app/imagej.py (100%) rename imagepy/{core => }/app/imagepy.py (100%) rename imagepy/{core => }/app/loader.py (99%) rename imagepy/{core => }/app/source.py (100%) rename imagepy/{core => }/app/startup.py (97%) delete mode 100644 imagepy/core/__init__.py delete mode 100644 imagepy/core/engine/__init__.py delete mode 100644 imagepy/core/engine/report.py rename {imagepy/core/engine => sciapp/action/advanced}/dataio.py (66%) rename {imagepy/core/engine => sciapp/action/advanced}/macros.py (100%) rename {imagepy/core/engine => sciapp/action/advanced}/widget.py (100%) diff --git a/imagepy/__main__.py b/imagepy/__main__.py index 0eeed204..7a8f2546 100644 --- a/imagepy/__main__.py +++ b/imagepy/__main__.py @@ -1,4 +1,4 @@ import sys sys.path.append('../') -from imagepy.core import app -app.startup.start() \ No newline at end of file +from imagepy.app import startup +startup.start() \ No newline at end of file diff --git a/imagepy/core/app/__init__.py b/imagepy/app/__init__.py similarity index 100% rename from imagepy/core/app/__init__.py rename to imagepy/app/__init__.py diff --git a/imagepy/core/app/imagej.py b/imagepy/app/imagej.py similarity index 100% rename from imagepy/core/app/imagej.py rename to imagepy/app/imagej.py diff --git a/imagepy/core/app/imagepy.py b/imagepy/app/imagepy.py similarity index 100% rename from imagepy/core/app/imagepy.py rename to imagepy/app/imagepy.py diff --git a/imagepy/core/app/loader.py b/imagepy/app/loader.py similarity index 99% rename from imagepy/core/app/loader.py rename to imagepy/app/loader.py index b90145e2..a617f8c1 100644 --- a/imagepy/core/app/loader.py +++ b/imagepy/app/loader.py @@ -6,9 +6,9 @@ """ import os, sys, os.path as osp from glob import glob -from ..engine import Macros, Widget, Report +from sciapp.action import Macros, Widget#, Report from sciapp import Source -from ... import root_dir +from .. import root_dir from codecs import open def getpath(root, path): diff --git a/imagepy/core/app/source.py b/imagepy/app/source.py similarity index 100% rename from imagepy/core/app/source.py rename to imagepy/app/source.py diff --git a/imagepy/core/app/startup.py b/imagepy/app/startup.py similarity index 97% rename from imagepy/core/app/startup.py rename to imagepy/app/startup.py index ea5b70f3..dc3c5e37 100644 --- a/imagepy/core/app/startup.py +++ b/imagepy/app/startup.py @@ -1,7 +1,7 @@ import wx, sys, os from .source import * from sciapp import Source -from imagepy.core.app import loader +from . import loader from imagepy import root_dir def extend_plgs(plg): @@ -78,7 +78,7 @@ def load_dictionary(): for i in lans: loader.build_dictionary(i) def start(): - from imagepy.core.app import ImagePy, ImageJ + from . import ImagePy, ImageJ import wx.lib.agw.advancedsplash as AS app = wx.App(False) bitmap = wx.Bitmap(root_dir+'/data/logolong.png', wx.BITMAP_TYPE_PNG) diff --git a/imagepy/core/__init__.py b/imagepy/core/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/imagepy/core/engine/__init__.py b/imagepy/core/engine/__init__.py deleted file mode 100644 index 97159050..00000000 --- a/imagepy/core/engine/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from .macros import Macros -from .widget import Widget -from sciapp.action import Table -from sciapp.action import Filter -from sciapp.action import Simple -from sciapp.action import Free -from .report import Report -from .dataio import * \ No newline at end of file diff --git a/imagepy/core/engine/report.py b/imagepy/core/engine/report.py deleted file mode 100644 index dc35d088..00000000 --- a/imagepy/core/engine/report.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Thu Dec 29 01:48:23 2016 -@author: yxl -""" -from sciapp import Source -#from imagepy.ui.propertygrid import GridDialog -#from imagepy.core.util import xlreport -from time import time -import openpyxl as pyxl -from sciapp import Source - -class Report: - def __init__(self, title, cont): - self.title = title - self.cont = cont - - def __call__(self): return self - - def runasyn(self, wb, info, key, para = None, callback = None): - self.app.add_task(self) - for i in para: - if i in key and key[i][0] == 'img': - ips = ImageManager.get(para[i]) - para[i] = ips if ips is None else ips.img - - if i in key and key[i][0] == 'tab': - tps = TableManager.get(para[i]) - para[i] = tps if tps is None else tps.data - - start = time() - xlreport.fill_value(wb, info, para) - wb.save(para['path']) - self.app.info('%s: cost %.3fs'%(self.title, time()-start)) - self.app.remove_task(self) - if callback!=None:callback() - - def start(self, para=None, callafter=None): - wb = pyxl.load_workbook(self.cont) - xlreport.repair(wb) - info, key = xlreport.parse(wb) - if para is not None: - return self.runasyn(wb, info, para, callafter) - dialog = GridDialog(IPy.curapp, self.title, info, key) - rst = dialog.ShowModal() - para = dialog.GetValue() - dialog.Destroy() - if rst != 5100: return - filt = '|'.join(['%s files (*.%s)|*.%s'%('XLSX', 'xlsx', 'xlsx')]) - if not self.app.getpath('Save..', filt, 'save', para): return - win = Source.manager('widget').get('obj', name='Macros Recorder') - if win!=None: win.write('{}>{}'.format(self.title, para)) - self.runasyn(wb, info, key, para, callafter) - -def show_rpt(data, title): - wx.CallAfter(Report(title, data).start) - -# ViewerManager.add('rpt', show_rpt) -def read_rpt(path): return path - -Source.manager('reader').add(name='rpt', obj=read_rpt, tag='rpt') \ No newline at end of file diff --git a/imagepy/data/config.json b/imagepy/data/config.json index 92f32548..5cb2d834 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["roi_style", {"color": [255, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 255, 0], "size": 8}, null], ["language", "Chinese", null], ["uistyle", "imagepy", null], ["recent", ["C:/Users/54631/Desktop/\u6d77\u51b0\u62a5\u4ef7/testmacros.mc", "C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc"], null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null]] \ No newline at end of file +[["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["recent", ["C:/Users/54631/Desktop/\u6d77\u51b0\u62a5\u4ef7/testmacros.mc", "C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc"], null], ["uistyle", "imagepy", null], ["language", "Chinese", null], ["roi_style", {"color": [255, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 255, 0], "size": 8}, null]] \ No newline at end of file diff --git a/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py b/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py index 02b3b77b..c85749e7 100644 --- a/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py +++ b/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py @@ -1,6 +1,6 @@ import scipy.ndimage as ndimg import numpy as np -from imagepy.core.engine import Filter, Simple +from sciapp.action import Filter, Simple import pandas as pd def colorselect(img, pts, k, usecov=True): diff --git a/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py b/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py index 86863a62..7ff90bd4 100644 --- a/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py +++ b/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py @@ -1,4 +1,4 @@ -from imagepy.core.engine import Filter +from sciapp.action import Filter import numpy as np from scipy.cluster.vq import kmeans, vq diff --git a/imagepy/menus/Analysis/Pixel Cluster/statistic_plgs.py b/imagepy/menus/Analysis/Pixel Cluster/statistic_plgs.py index 95d74866..40e20de2 100644 --- a/imagepy/menus/Analysis/Pixel Cluster/statistic_plgs.py +++ b/imagepy/menus/Analysis/Pixel Cluster/statistic_plgs.py @@ -1,4 +1,4 @@ -from imagepy.core.engine import Simple +from sciapp.action import Simple import pandas as pd class GrayStatistic(Simple): diff --git a/imagepy/menus/Analysis/Region Analysis/connect_plg.py b/imagepy/menus/Analysis/Region Analysis/connect_plg.py index abd93816..9500defb 100644 --- a/imagepy/menus/Analysis/Region Analysis/connect_plg.py +++ b/imagepy/menus/Analysis/Region Analysis/connect_plg.py @@ -1,5 +1,5 @@ import numpy as np -from imagepy.core.engine import Simple +from sciapp.action import Simple from skimage.measure import regionprops from scipy.ndimage import label, generate_binary_structure from imagepy.ipyalg.graph.connect import connect_graph, mapidx diff --git a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py index 7f3a1a1d..2106bf2e 100644 --- a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py @@ -4,7 +4,7 @@ @author: yxl """ import numpy as np -from imagepy.core.engine import Simple, Filter +from sciapp.action import Simple, Filter from scipy.ndimage import label, generate_binary_structure from skimage.measure import regionprops from sciapp.object import mark2shp diff --git a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py index e5d83933..fa5d6183 100644 --- a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py @@ -5,7 +5,7 @@ """ import numpy as np from scipy import ndimage -from imagepy.core.engine import Simple, Filter +from sciapp.action import Simple, Filter from sciapp.object import mark2shp import pandas as pd diff --git a/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py b/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py index afa9268e..9043adfc 100644 --- a/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py +++ b/imagepy/menus/Analysis/Skeleton Network/graph_plgs.py @@ -1,4 +1,4 @@ -from imagepy.core.engine import Filter, Simple +from sciapp.action import Filter, Simple from imagepy.ipyalg.graph import sknw import numpy as np from numpy.linalg import norm diff --git a/imagepy/menus/Analysis/label_plg.py b/imagepy/menus/Analysis/label_plg.py index d0ff2ccf..ab7dd8d0 100644 --- a/imagepy/menus/Analysis/label_plg.py +++ b/imagepy/menus/Analysis/label_plg.py @@ -6,7 +6,7 @@ import numpy as np from scipy.ndimage import label, generate_binary_structure from skimage.segmentation import find_boundaries -from imagepy.core.engine import Filter, Simple +from sciapp.action import Filter, Simple from imagepy.ipyalg.graph import connect, render from sciapp.object import Image diff --git a/imagepy/menus/Analysis/statistic_plg.py b/imagepy/menus/Analysis/statistic_plg.py index c63e8c37..f46a9b0a 100644 --- a/imagepy/menus/Analysis/statistic_plg.py +++ b/imagepy/menus/Analysis/statistic_plg.py @@ -5,7 +5,7 @@ """ from imagepy import root_dir import wx, numpy as np, os -from imagepy.core.engine import Filter,Simple +from sciapp.action import Filter,Simple from pubsub import pub import pandas as pd diff --git a/imagepy/menus/Edit/edit_plg.py b/imagepy/menus/Edit/edit_plg.py index 635081cf..13b621d2 100644 --- a/imagepy/menus/Edit/edit_plg.py +++ b/imagepy/menus/Edit/edit_plg.py @@ -4,7 +4,7 @@ @author: yxl """ import numpy as np -from imagepy.core.engine import Simple, Filter +from sciapp.action import Simple, Filter from sciapp.action import ImageTool from sciapp import Source diff --git a/imagepy/menus/File/BMP/bmp_plgs.py b/imagepy/menus/File/BMP/bmp_plgs.py index 6d801e0a..104d12cd 100644 --- a/imagepy/menus/File/BMP/bmp_plgs.py +++ b/imagepy/menus/File/BMP/bmp_plgs.py @@ -1,9 +1,9 @@ -from imagepy.core.engine import dataio +from sciapp.action import dataio from skimage.io import imread, imsave from sciapp import Source -Source.manager('reader').add('bmp', imread, 'img') -Source.manager('writer').add('bmp', imsave, 'img') +dataio.ReaderManager.add('bmp', imread, 'img') +dataio.WriterManager.add('bmp', imsave, 'img') class OpenFile(dataio.Reader): title = 'BMP Open' diff --git a/imagepy/menus/File/DAT/dat_plgs.py b/imagepy/menus/File/DAT/dat_plgs.py index 0765da67..b960e9de 100644 --- a/imagepy/menus/File/DAT/dat_plgs.py +++ b/imagepy/menus/File/DAT/dat_plgs.py @@ -1,4 +1,4 @@ -from imagepy.core.engine import dataio +from sciapp.action import dataio import numpy as np from sciapp import Source @@ -8,8 +8,8 @@ def imread(path): def imsave(path,img): np.savetxt(path,img) -Source.manager('reader').add('dat', imread, 'img') -Source.manager('writer').add('dat', imsave, 'img') +dataio.ReaderManager.add('dat', imread, 'img') +dataio.WriterManager.add('dat', imsave, 'img') class OpenFile(dataio.Reader): title = 'DAT Open' diff --git a/imagepy/menus/File/DICOM/dicom_plgs.py b/imagepy/menus/File/DICOM/dicom_plgs.py index 0f01b9e9..8052f200 100644 --- a/imagepy/menus/File/DICOM/dicom_plgs.py +++ b/imagepy/menus/File/DICOM/dicom_plgs.py @@ -1,4 +1,4 @@ -from imagepy.core.engine import dataio +from sciapp.action import dataio import pydicom from sciapp import Source @@ -7,7 +7,7 @@ def imread(path): return pydicom.read_file(path, force=True).pixel_array -Source.manager('reader').add('dcm', imread, 'img') +dataio.ReaderManager.add('dcm', imread, 'img') class OpenFile(dataio.Reader): title = 'DCM Open' diff --git a/imagepy/menus/File/Export/sequence_plg.py b/imagepy/menus/File/Export/sequence_plg.py index 6bef883e..05d43e8f 100644 --- a/imagepy/menus/File/Export/sequence_plg.py +++ b/imagepy/menus/File/Export/sequence_plg.py @@ -5,7 +5,7 @@ @author: yxl """ from skimage.io import imsave -from imagepy.core.engine import Simple +from sciapp.action import Simple from sciapp import Source class Plugin(Simple): @@ -28,7 +28,7 @@ def show(self): #process def run(self, ips, imgs, para = None): path = para['path']+'/'+para['name'] - write = Source.manager('writer').get(para['format']) + write = dataio.WriterManager.get(para['format']) print(path) for i in range(len(imgs)): self.progress(i, len(imgs)) diff --git a/imagepy/menus/File/GIF/gif_plgs.py b/imagepy/menus/File/GIF/gif_plgs.py index 93ae0f35..dd8030ca 100644 --- a/imagepy/menus/File/GIF/gif_plgs.py +++ b/imagepy/menus/File/GIF/gif_plgs.py @@ -1,12 +1,12 @@ -from imagepy.core.engine import dataio -from imagepy.core.engine import Simple +from sciapp.action import dataio +from sciapp.action import Simple from skimage.io import imread, imsave from sciapp import Source import imageio -Source.manager('reader').add('gif', imread, 'img') -Source.manager('writer').add('gif', imsave, 'img') -Source.manager('reader').add('gif', imageio.mimread, 'imgs') +dataio.ReaderManager.add('gif', imread, 'img') +dataio.WriterManager.add('gif', imsave, 'img') +dataio.ReaderManager.add('gif', imageio.mimread, 'imgs') class OpenFile(dataio.Reader): title = 'GIF Open' diff --git a/imagepy/menus/File/Import/raw_plg.py b/imagepy/menus/File/Import/raw_plg.py index 366e4c86..11686cbf 100644 --- a/imagepy/menus/File/Import/raw_plg.py +++ b/imagepy/menus/File/Import/raw_plg.py @@ -2,7 +2,7 @@ import numpy as np import io# urllib2 urllib.request, urllib.error, urllib.parse from skimage.io import imread -from imagepy.core.engine import Free +from sciapp.action import Free class Plugin(Free): title = 'Open Raw' diff --git a/imagepy/menus/File/Import/roi_plg.py b/imagepy/menus/File/Import/roi_plg.py index 826d3ada..afed24ca 100644 --- a/imagepy/menus/File/Import/roi_plg.py +++ b/imagepy/menus/File/Import/roi_plg.py @@ -5,7 +5,7 @@ """ import numpy as np import read_roi -from imagepy.core.engine import Free +from sciapp.action import Free from skimage.draw import polygon, ellipse class Plugin(Free): diff --git a/imagepy/menus/File/Import/sequence_plg.py b/imagepy/menus/File/Import/sequence_plg.py index b94666cb..beb48d67 100644 --- a/imagepy/menus/File/Import/sequence_plg.py +++ b/imagepy/menus/File/Import/sequence_plg.py @@ -4,10 +4,10 @@ @author: yxl """ -from imagepy.core.engine import dataio +from sciapp.action import dataio from skimage.io import imread from sciapp import Source -from imagepy.core.engine import Free +from sciapp.action import Free from glob import glob import os.path as osp @@ -16,7 +16,7 @@ class Plugin(Free): para = {'path':'', 'start':0, 'end':0, 'step':1, 'title':'sequence'} def load(self): - self.filt = Source.manager('reader').names() + self.filt = dataio.ReaderManager.names() return True def show(self): @@ -53,7 +53,7 @@ def readimgs(self, names, read, shape, dtype): def run(self, para = None): fp, fn = osp.split(para['path']) fn, fe = osp.splitext(fn) - read = Source.manager('reader').get(name=fe[1:]) + read = dataio.ReaderManager.get(name=fe[1:]) try: img = read(para['path']) except: return self.app.alert('unknown img format!') files = self.getfiles(para['path']) diff --git a/imagepy/menus/File/JPG/jpg_plgs.py b/imagepy/menus/File/JPG/jpg_plgs.py index 5805fb81..ab3cf782 100644 --- a/imagepy/menus/File/JPG/jpg_plgs.py +++ b/imagepy/menus/File/JPG/jpg_plgs.py @@ -1,11 +1,11 @@ -from imagepy.core.engine import dataio +from sciapp.action import dataio from imageio import imread, imsave from sciapp import Source -Source.manager('reader').add('jpg', imread, 'img') -Source.manager('writer').add('jpg', imsave, 'img') -Source.manager('reader').add('jpeg', imread, 'img') -Source.manager('writer').add('jpeg', imsave, 'img') +dataio.ReaderManager.add('jpg', imread, 'img') +dataio.WriterManager.add('jpg', imsave, 'img') +dataio.ReaderManager.add('jpeg', imread, 'img') +dataio.WriterManager.add('jpeg', imsave, 'img') class OpenFile(dataio.Reader): title = 'JPG Open' diff --git a/imagepy/menus/File/MAT/mat_plgs.py b/imagepy/menus/File/MAT/mat_plgs.py index 8ab71ed7..f71e7fe0 100644 --- a/imagepy/menus/File/MAT/mat_plgs.py +++ b/imagepy/menus/File/MAT/mat_plgs.py @@ -1,12 +1,12 @@ -from imagepy.core.engine import dataio +from sciapp.action import dataio from scipy.io import savemat, loadmat from sciapp import Source import os -Source.manager('reader').add('mat', lambda path: loadmat(path)['img'], 'img') -Source.manager('writer').add('mat', lambda path, img: savemat(path, {'img':img}), 'img') -Source.manager('reader').add('mat', lambda path: loadmat(path)['img'], 'imgs') -Source.manager('writer').add('mat', lambda path, img: savemat(path, {'img':img}), 'imgs') +dataio.ReaderManager.add('mat', lambda path: loadmat(path)['img'], 'img') +dataio.WriterManager.add('mat', lambda path, img: savemat(path, {'img':img}), 'img') +dataio.ReaderManager.add('mat', lambda path: loadmat(path)['img'], 'imgs') +dataio.WriterManager.add('mat', lambda path, img: savemat(path, {'img':img}), 'imgs') class OpenFile(dataio.Reader): title = 'Mat Open' diff --git a/imagepy/menus/File/MarkDown/md_plg.py b/imagepy/menus/File/MarkDown/md_plg.py index 79eb5ae9..0c108589 100644 --- a/imagepy/menus/File/MarkDown/md_plg.py +++ b/imagepy/menus/File/MarkDown/md_plg.py @@ -1,10 +1,10 @@ -from imagepy.core.engine import dataio +from sciapp.action import dataio from sciapp import Source def read(path): with open(path) as f: return f.read() -Source.manager('reader').add('md', read, 'md') +dataio.ReaderManager.add('md', read, 'md') class Plugin(dataio.Reader): title = 'MarkDown Open' diff --git a/imagepy/menus/File/Numpy/ndarray_plgs.py b/imagepy/menus/File/Numpy/ndarray_plgs.py index bea37aba..044f20bf 100644 --- a/imagepy/menus/File/Numpy/ndarray_plgs.py +++ b/imagepy/menus/File/Numpy/ndarray_plgs.py @@ -1,12 +1,12 @@ -from imagepy.core.engine import dataio +from sciapp.action import dataio import numpy as np from sciapp import Source import os -Source.manager('reader').add('npy', np.load, 'img') -Source.manager('writer').add('npy', np.save, 'img') -Source.manager('reader').add('npy', np.load, 'imgs') -Source.manager('writer').add('npy', np.save, 'imgs') +dataio.ReaderManager.add('npy', np.load, 'img') +dataio.WriterManager.add('npy', np.save, 'img') +dataio.ReaderManager.add('npy', np.load, 'imgs') +dataio.WriterManager.add('npy', np.save, 'imgs') class OpenFile(dataio.Reader): title = 'Numpy Open' diff --git a/imagepy/menus/File/Open Recent/recent_plgs.py b/imagepy/menus/File/Open Recent/recent_plgs.py index 8bd91f3a..0ad7bb5a 100644 --- a/imagepy/menus/File/Open Recent/recent_plgs.py +++ b/imagepy/menus/File/Open Recent/recent_plgs.py @@ -1,3 +1,3 @@ -from imagepy.core.engine import dataio +from sciapp.action import dataio plgs = dataio.rlist \ No newline at end of file diff --git a/imagepy/menus/File/PNG/png_plgs.py b/imagepy/menus/File/PNG/png_plgs.py index 029b4cd5..44a90f94 100644 --- a/imagepy/menus/File/PNG/png_plgs.py +++ b/imagepy/menus/File/PNG/png_plgs.py @@ -1,9 +1,9 @@ -from imagepy.core.engine import dataio +from sciapp.action import dataio from skimage.io import imread, imsave from sciapp import Source -Source.manager('reader').add('png', imread, 'img') -Source.manager('writer').add('png', imsave, 'img') +dataio.ReaderManager.add('png', imread, 'img') +dataio.WriterManager.add('png', imsave, 'img') class OpenFile(dataio.Reader): title = 'PNG Open' diff --git a/imagepy/menus/File/Samples ImageJ/ijsample_plgs.py b/imagepy/menus/File/Samples ImageJ/ijsample_plgs.py index b70f793e..72a61e1d 100644 --- a/imagepy/menus/File/Samples ImageJ/ijsample_plgs.py +++ b/imagepy/menus/File/Samples ImageJ/ijsample_plgs.py @@ -2,7 +2,7 @@ from urllib.request import urlopen from io import BytesIO as StringIO -from imagepy.core.engine import Free +from sciapp.action import Free class IJImg(Free): def __init__(self, title, name): diff --git a/imagepy/menus/File/Samples Local/samples_plgs.py b/imagepy/menus/File/Samples Local/samples_plgs.py index 457e9f16..811945c1 100644 --- a/imagepy/menus/File/Samples Local/samples_plgs.py +++ b/imagepy/menus/File/Samples Local/samples_plgs.py @@ -1,4 +1,4 @@ -from imagepy.core.engine import Free +from sciapp.action import Free from skimage import data from scipy import misc import numpy as np diff --git a/imagepy/menus/File/TIF/tif_plgs.py b/imagepy/menus/File/TIF/tif_plgs.py index 0e358a34..bde4bf8e 100644 --- a/imagepy/menus/File/TIF/tif_plgs.py +++ b/imagepy/menus/File/TIF/tif_plgs.py @@ -1,14 +1,14 @@ -from imagepy.core.engine import dataio +from sciapp.action import dataio from skimage.io import imread, imsave from sciapp import Source -Source.manager('reader').add('tif', imread, 'img') -Source.manager('reader').add('tiff', imread, 'img') -Source.manager('writer').add('tif', imsave, 'img') +dataio.ReaderManager.add('tif', imread, 'img') +dataio.ReaderManager.add('tiff', imread, 'img') +dataio.WriterManager.add('tif', imsave, 'img') -Source.manager('reader').add('tif', imread, 'imgs') -Source.manager('reader').add('tiff', imread, 'imgs') -Source.manager('writer').add('tif', imsave, 'imgs') +dataio.ReaderManager.add('tif', imread, 'imgs') +dataio.ReaderManager.add('tiff', imread, 'imgs') +dataio.WriterManager.add('tif', imsave, 'imgs') class OpenTIF(dataio.Reader): title = 'TIF Open' diff --git a/imagepy/menus/File/exit_plg.py b/imagepy/menus/File/exit_plg.py index 045b422d..c9f4a15f 100644 --- a/imagepy/menus/File/exit_plg.py +++ b/imagepy/menus/File/exit_plg.py @@ -3,7 +3,7 @@ Created on Mon Dec 5 05:43:50 2016 @author: yxl """ -from imagepy.core.engine import Free +from sciapp.action import Free class Plugin(Free): title = 'Exit' diff --git a/imagepy/menus/File/new_plg.py b/imagepy/menus/File/new_plg.py index 960ccbd7..50c8b99d 100644 --- a/imagepy/menus/File/new_plg.py +++ b/imagepy/menus/File/new_plg.py @@ -4,7 +4,7 @@ @author: yxl """ -from imagepy.core.engine import Free +from sciapp.action import Free import numpy as np class Plugin(Free): diff --git a/imagepy/menus/File/open_plg.py b/imagepy/menus/File/open_plg.py index a8ad97f7..fe807744 100644 --- a/imagepy/menus/File/open_plg.py +++ b/imagepy/menus/File/open_plg.py @@ -4,15 +4,15 @@ from urllib.request import urlopen from io import BytesIO as StringIO -from imagepy.core.engine import Free -from imagepy.core.engine import dataio +from sciapp.action import Free +from sciapp.action import dataio from sciapp import Source class OpenFile(dataio.Reader): title = 'Open' def load(self): - self.filt = [i for i in sorted(Source.manager('reader').names())] + self.filt = [i for i in sorted(dataio.ReaderManager.names())] return True class OpenUrl(Free): diff --git a/imagepy/menus/File/save_plg.py b/imagepy/menus/File/save_plg.py index 9d41f598..00a857b4 100644 --- a/imagepy/menus/File/save_plg.py +++ b/imagepy/menus/File/save_plg.py @@ -3,15 +3,15 @@ Created on Mon Dec 5 03:19:13 2016 @author: yxl """ -from imagepy.core.engine import dataio +from sciapp.action import dataio from sciapp import Source -from imagepy.core.engine import Simple +from sciapp.action import Simple class SaveImage(dataio.ImageWriter): title = 'Save' def load(self, ips): - self.filt = [i for i in sorted(Source.manager('writer').names())] + self.filt = [i for i in sorted(dataio.WriterManager.names())] return True class WindowCapture(dataio.ImageWriter): diff --git a/imagepy/menus/Help/Help_plgs.py b/imagepy/menus/Help/Help_plgs.py index 77621776..b65eef41 100644 --- a/imagepy/menus/Help/Help_plgs.py +++ b/imagepy/menus/Help/Help_plgs.py @@ -4,7 +4,7 @@ @author: yxl """ import webbrowser -from imagepy.core.engine import Free +from sciapp.action import Free ## TODO:Fixme! class About(Free): diff --git a/imagepy/menus/Help/Language/language_plgs.py b/imagepy/menus/Help/Language/language_plgs.py index 8ab03960..53da6686 100644 --- a/imagepy/menus/Help/Language/language_plgs.py +++ b/imagepy/menus/Help/Language/language_plgs.py @@ -1,5 +1,5 @@ from sciapp import Source -from imagepy.core.engine import Free +from sciapp.action import Free class Language(Free): def __init__(self, key): diff --git a/imagepy/menus/Image/Adjust/bleachCorrection_plg.py b/imagepy/menus/Image/Adjust/bleachCorrection_plg.py index 4dad9999..3259e013 100644 --- a/imagepy/menus/Image/Adjust/bleachCorrection_plg.py +++ b/imagepy/menus/Image/Adjust/bleachCorrection_plg.py @@ -2,7 +2,7 @@ Created on Sun Jan 23 11:53:00 2020 @author: weisong """ -from imagepy.core.engine import Simple +from sciapp.action import Simple import numpy as np from scipy.optimize import curve_fit from skimage.exposure import histogram_matching diff --git a/imagepy/menus/Image/Adjust/brightcons_plg.py b/imagepy/menus/Image/Adjust/brightcons_plg.py index 8763dc6f..0e293098 100644 --- a/imagepy/menus/Image/Adjust/brightcons_plg.py +++ b/imagepy/menus/Image/Adjust/brightcons_plg.py @@ -3,7 +3,7 @@ @author: yxl """ import numpy as np -from imagepy.core.engine import Filter +from sciapp.action import Filter #from imagepy.ui.widgets import HistCanvas class Plugin(Filter): diff --git a/imagepy/menus/Image/Adjust/colorbalance_plg.py b/imagepy/menus/Image/Adjust/colorbalance_plg.py index 94db40c0..3669b0c1 100644 --- a/imagepy/menus/Image/Adjust/colorbalance_plg.py +++ b/imagepy/menus/Image/Adjust/colorbalance_plg.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- import numpy as np -from imagepy.core.engine import Filter +from sciapp.action import Filter class Plugin(Filter): title = 'Color Balance' diff --git a/imagepy/menus/Image/Adjust/colorstairs_plg.py b/imagepy/menus/Image/Adjust/colorstairs_plg.py index adcd2b55..4a0b5589 100644 --- a/imagepy/menus/Image/Adjust/colorstairs_plg.py +++ b/imagepy/menus/Image/Adjust/colorstairs_plg.py @@ -5,7 +5,7 @@ @author: yxl """ import numpy as np -from imagepy.core.engine import Filter +from sciapp.action import Filter #from imagepy.ui.widgets import HistCanvas class Plugin(Filter): diff --git a/imagepy/menus/Image/Adjust/curve_plg.py b/imagepy/menus/Image/Adjust/curve_plg.py index 1d4b4e65..8a791c64 100644 --- a/imagepy/menus/Image/Adjust/curve_plg.py +++ b/imagepy/menus/Image/Adjust/curve_plg.py @@ -1,5 +1,5 @@ import numpy as np -from imagepy.core.engine import Filter +from sciapp.action import Filter #from imagepy.ui.widgets import CurvePanel from scipy import interpolate diff --git a/imagepy/menus/Image/Adjust/enhanceContrast_plg.py b/imagepy/menus/Image/Adjust/enhanceContrast_plg.py index 2389842e..e396403b 100644 --- a/imagepy/menus/Image/Adjust/enhanceContrast_plg.py +++ b/imagepy/menus/Image/Adjust/enhanceContrast_plg.py @@ -2,7 +2,7 @@ Created on Sun Jan 25 17:00:00 2020 @author: weisong """ -from imagepy.core.engine import Filter +from sciapp.action import Filter from skimage import exposure import numpy as np diff --git a/imagepy/menus/Image/Adjust/graystairs_plg.py b/imagepy/menus/Image/Adjust/graystairs_plg.py index 33a6bcd1..d812b4a3 100644 --- a/imagepy/menus/Image/Adjust/graystairs_plg.py +++ b/imagepy/menus/Image/Adjust/graystairs_plg.py @@ -4,7 +4,7 @@ @author: yxl """ import numpy as np -from imagepy.core.engine import Filter +from sciapp.action import Filter class Plugin(Filter): diff --git a/imagepy/menus/Image/Adjust/histogram_plgs.py b/imagepy/menus/Image/Adjust/histogram_plgs.py index b02cfa88..528841a3 100644 --- a/imagepy/menus/Image/Adjust/histogram_plgs.py +++ b/imagepy/menus/Image/Adjust/histogram_plgs.py @@ -4,7 +4,7 @@ @author: yxl """ import numpy as np -from imagepy.core.engine import Filter, Simple +from sciapp.action import Filter, Simple def like(hist1, hist2): hist1 = np.cumsum(hist1)/hist1.sum() @@ -53,7 +53,7 @@ def run(self, ips, snap, img, para = None): img[:] = ahist[img] class Match(Filter): - """Calculator Plugin derived from imagepy.core.engine.Simple """ + """Calculator Plugin derived from sciapp.action.Simple """ title = 'Histogram Match' note = ['all', 'not_channel', 'auto_snap', 'auto_msk'] para = {'img':None} diff --git a/imagepy/menus/Image/Adjust/normalize_plg.py b/imagepy/menus/Image/Adjust/normalize_plg.py index 5a9a3356..9fc7a363 100644 --- a/imagepy/menus/Image/Adjust/normalize_plg.py +++ b/imagepy/menus/Image/Adjust/normalize_plg.py @@ -2,7 +2,7 @@ Created on Sun Jan 25 9:00:00 2020 @author: weisong """ -from imagepy.core.engine import Simple +from sciapp.action import Simple import numpy as np class Plugin(Simple): diff --git a/imagepy/menus/Image/Adjust/threshold_plg.py b/imagepy/menus/Image/Adjust/threshold_plg.py index 0eba462a..24c7efad 100644 --- a/imagepy/menus/Image/Adjust/threshold_plg.py +++ b/imagepy/menus/Image/Adjust/threshold_plg.py @@ -5,7 +5,7 @@ """ import numpy as np -from imagepy.core.engine import Filter +from sciapp.action import Filter class Plugin(Filter): modal = False diff --git a/imagepy/menus/Image/Color/splitandmerge_plgs.py b/imagepy/menus/Image/Color/splitandmerge_plgs.py index d954c1c5..2bfe3f36 100644 --- a/imagepy/menus/Image/Color/splitandmerge_plgs.py +++ b/imagepy/menus/Image/Color/splitandmerge_plgs.py @@ -4,7 +4,7 @@ @author: yxl """ import numpy as np -from imagepy.core.engine import Simple +from sciapp.action import Simple from skimage import color class SplitRGB(Simple): diff --git a/imagepy/menus/Image/Lookup table/lookuptables_plg.py b/imagepy/menus/Image/Lookup table/lookuptables_plg.py index a6595661..614cc7a3 100644 --- a/imagepy/menus/Image/Lookup table/lookuptables_plg.py +++ b/imagepy/menus/Image/Lookup table/lookuptables_plg.py @@ -6,7 +6,7 @@ from imagepy import root_dir import numpy as np #from imagepy.core import ImagePlus -from imagepy.core.engine import Free +from sciapp.action import Free from sciapp import Source from sciapp.object import Image diff --git a/imagepy/menus/Image/Mark/mark_plgs.py b/imagepy/menus/Image/Mark/mark_plgs.py index 231c080a..9a8610d2 100644 --- a/imagepy/menus/Image/Mark/mark_plgs.py +++ b/imagepy/menus/Image/Mark/mark_plgs.py @@ -1,4 +1,4 @@ -from imagepy.core.engine import Simple, Free +from sciapp.action import Simple, Free from sciapp import Source from sciapp.object import Shape from sciapp.util import mark2shp diff --git a/imagepy/menus/Image/Stack/orthogonal_plg.py b/imagepy/menus/Image/Stack/orthogonal_plg.py index 4bd88505..bff6ab3a 100644 --- a/imagepy/menus/Image/Stack/orthogonal_plg.py +++ b/imagepy/menus/Image/Stack/orthogonal_plg.py @@ -7,7 +7,7 @@ import wx # from imagepy.core import ImagePlus from sciapp.action import ImageTool -from imagepy.core.engine import Simple +from sciapp.action import Simple class Cross: def __init__(self, w, h): diff --git a/imagepy/menus/Image/Stack/stack_plgs.py b/imagepy/menus/Image/Stack/stack_plgs.py index c373414f..2694e3cf 100644 --- a/imagepy/menus/Image/Stack/stack_plgs.py +++ b/imagepy/menus/Image/Stack/stack_plgs.py @@ -3,7 +3,7 @@ Created on Sat Nov 19 11:26:12 2016 @author: yxl """ -from imagepy.core.engine import Simple +from sciapp.action import Simple class SetSlice(Simple): title = 'Set Slice' diff --git a/imagepy/menus/Image/Transform/Transform_plgs.py b/imagepy/menus/Image/Transform/Transform_plgs.py index 1d2d054c..309a79dd 100644 --- a/imagepy/menus/Image/Transform/Transform_plgs.py +++ b/imagepy/menus/Image/Transform/Transform_plgs.py @@ -5,7 +5,7 @@ """ import numpy as np import scipy.ndimage as nimg -from imagepy.core.engine import Filter +from sciapp.action import Filter class Rotate(Filter): title = 'Rotate' diff --git a/imagepy/menus/Image/Type/convert_plg.py b/imagepy/menus/Image/Type/convert_plg.py index dc51d877..2241c131 100644 --- a/imagepy/menus/Image/Type/convert_plg.py +++ b/imagepy/menus/Image/Type/convert_plg.py @@ -4,7 +4,7 @@ @author: yxl """ import numpy as np -from imagepy.core.engine import Simple +from sciapp.action import Simple def trans(imgs, shp, cn, sl, rg1, rg2, tp, prog=print): buf = np.zeros(shp, dtype=np.float32) diff --git a/imagepy/menus/Image/Type/tostack_plg.py b/imagepy/menus/Image/Type/tostack_plg.py index 2a03c360..4610b40d 100644 --- a/imagepy/menus/Image/Type/tostack_plg.py +++ b/imagepy/menus/Image/Type/tostack_plg.py @@ -4,7 +4,7 @@ @author: yxl """ import numpy as np -from imagepy.core.engine import Simple +from sciapp.action import Simple class ToStack(Simple): title = 'Trans to Stack' diff --git a/imagepy/menus/Image/background_plg.py b/imagepy/menus/Image/background_plg.py index ef8af0d5..690d79d2 100644 --- a/imagepy/menus/Image/background_plg.py +++ b/imagepy/menus/Image/background_plg.py @@ -1,6 +1,6 @@ import numpy as np from sciapp.object import Image -from imagepy.core.engine import Simple +from sciapp.action import Simple class SetBackground(Simple): title = 'Set Background' diff --git a/imagepy/menus/Image/canvassize_plg.py b/imagepy/menus/Image/canvassize_plg.py index 6e43e762..25e5bcf3 100644 --- a/imagepy/menus/Image/canvassize_plg.py +++ b/imagepy/menus/Image/canvassize_plg.py @@ -3,7 +3,7 @@ Created on Sun Dec 11 23:43:44 2016 @author: yxl """ -from imagepy.core.engine import Simple +from sciapp.action import Simple import numpy as np def make_slice(a, b, mode=1): diff --git a/imagepy/menus/Image/duplicate_plg.py b/imagepy/menus/Image/duplicate_plg.py index dacea799..f154cb4c 100644 --- a/imagepy/menus/Image/duplicate_plg.py +++ b/imagepy/menus/Image/duplicate_plg.py @@ -4,7 +4,7 @@ @author: yxl """ -from imagepy.core.engine import Simple +from sciapp.action import Simple from sciapp.object import Image, ROI from sciapp.util import offset, mark2shp import numpy as np diff --git a/imagepy/menus/Image/resize_plg.py b/imagepy/menus/Image/resize_plg.py index 99c9efca..db7a8666 100644 --- a/imagepy/menus/Image/resize_plg.py +++ b/imagepy/menus/Image/resize_plg.py @@ -4,7 +4,7 @@ @author: yxl """ -from imagepy.core.engine import Simple +from sciapp.action import Simple import scipy.ndimage as ndimg import numpy as np diff --git a/imagepy/menus/Image/setscale_plg.py b/imagepy/menus/Image/setscale_plg.py index e71d71c0..2a139480 100644 --- a/imagepy/menus/Image/setscale_plg.py +++ b/imagepy/menus/Image/setscale_plg.py @@ -1,4 +1,4 @@ -from imagepy.core.engine import Simple +from sciapp.action import Simple import numpy as np class Plugin(Simple): diff --git a/imagepy/menus/Kit3D/Analysis 3D/pixelstatistic_plgs.py b/imagepy/menus/Kit3D/Analysis 3D/pixelstatistic_plgs.py index 1217c944..f1935120 100644 --- a/imagepy/menus/Kit3D/Analysis 3D/pixelstatistic_plgs.py +++ b/imagepy/menus/Kit3D/Analysis 3D/pixelstatistic_plgs.py @@ -1,5 +1,5 @@ import numpy as np -from imagepy.core.engine import Simple, Filter +from sciapp.action import Simple, Filter import pandas as pd class Statistic(Simple): diff --git a/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py b/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py index 9d5c59f9..f9149b3c 100644 --- a/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py +++ b/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py @@ -1,5 +1,5 @@ import numpy as np -from imagepy.core.engine import Simple, Filter +from sciapp.action import Simple, Filter from scipy.ndimage import label, generate_binary_structure from skimage.measure import marching_cubes_lewiner, mesh_surface_area from skimage.segmentation import find_boundaries diff --git a/imagepy/menus/Kit3D/Analysis 3D/surfacemeasure_plg.py b/imagepy/menus/Kit3D/Analysis 3D/surfacemeasure_plg.py index e1f8c89a..2a8f6cd6 100644 --- a/imagepy/menus/Kit3D/Analysis 3D/surfacemeasure_plg.py +++ b/imagepy/menus/Kit3D/Analysis 3D/surfacemeasure_plg.py @@ -1,4 +1,4 @@ -from imagepy.core.engine import Filter +from sciapp.action import Filter from skimage.measure import marching_cubes_lewiner, mesh_surface_area import numpy as np import pandas as pd diff --git a/imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py b/imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py index 7960af32..3f42cd94 100644 --- a/imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py +++ b/imagepy/menus/Kit3D/Binary 3D/binary3d_plgs.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -* import scipy.ndimage as ndimg -from imagepy.core.engine import Simple +from sciapp.action import Simple from skimage.morphology import skeletonize_3d from imagepy.ipyalg import find_maximum, watershed from skimage.filters import apply_hysteresis_threshold @@ -8,7 +8,7 @@ import numpy as np class Dilation(Simple): - """Dilation: derived from imagepy.core.engine.Filter """ + """Dilation: derived from sciapp.action.Filter """ title = 'Dilation 3D' note = ['all', 'stack3d'] para = {'r':3} @@ -20,7 +20,7 @@ def run(self, ips, imgs, para = None): imgs *= 255 class Erosion(Simple): - """Dilation: derived from imagepy.core.engine.Filter """ + """Dilation: derived from sciapp.action.Filter """ title = 'Erosion 3D' note = ['all', 'stack3d'] para = {'r':3} @@ -32,7 +32,7 @@ def run(self, ips, imgs, para = None): imgs *= 255 class Opening(Simple): - """Dilation: derived from imagepy.core.engine.Filter """ + """Dilation: derived from sciapp.action.Filter """ title = 'Opening 3D' note = ['all', 'stack3d'] para = {'r':3} @@ -44,7 +44,7 @@ def run(self, ips, imgs, para = None): imgs *= 255 class Closing(Simple): - """Dilation: derived from imagepy.core.engine.Filter """ + """Dilation: derived from sciapp.action.Filter """ title = 'Closing 3D' note = ['all', 'stack3d'] para = {'r':3} @@ -56,7 +56,7 @@ def run(self, ips, imgs, para = None): imgs *= 255 class FillHole(Simple): - """Dilation: derived from imagepy.core.engine.Filter """ + """Dilation: derived from sciapp.action.Filter """ title = 'Fill Holes 3D' note = ['all', 'stack3d'] diff --git a/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py b/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py index 95252294..22e779b5 100644 --- a/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py +++ b/imagepy/menus/Kit3D/Features 3D/ridge_plgs.py @@ -1,5 +1,5 @@ from skimage.filters import frangi, sato, hessian ,meijering -from imagepy.core.engine import Filter, Simple +from sciapp.action import Filter, Simple class Frangi(Simple): title = 'Frangi 3D' diff --git a/imagepy/menus/Kit3D/Filters 3D/filters3d_plgs.py b/imagepy/menus/Kit3D/Filters 3D/filters3d_plgs.py index 0cf8c3a4..9f46adb1 100644 --- a/imagepy/menus/Kit3D/Filters 3D/filters3d_plgs.py +++ b/imagepy/menus/Kit3D/Filters 3D/filters3d_plgs.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -* import scipy.ndimage as ndimg -from imagepy.core.engine import Filter, Simple +from sciapp.action import Filter, Simple #from skimage.morphology import watershed from imagepy.ipyalg import watershed import numpy as np diff --git a/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py b/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py index af9959a0..1e082127 100644 --- a/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py +++ b/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py @@ -1,4 +1,4 @@ -from imagepy.core.engine import Filter, Simple +from sciapp.action import Filter, Simple from imagepy.ipyalg.graph import sknw from skimage.morphology import skeletonize_3d from itertools import combinations diff --git a/imagepy/menus/Kit3D/Viewer 3D/colorpts_plg.py b/imagepy/menus/Kit3D/Viewer 3D/colorpts_plg.py index dd3be1af..f2cdb69b 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/colorpts_plg.py +++ b/imagepy/menus/Kit3D/Viewer 3D/colorpts_plg.py @@ -1,4 +1,4 @@ -from imagepy.core.engine import Simple +from sciapp.action import Simple from sciapp.object import Surface, MarkText from sciapp.util import surfutil import numpy as np diff --git a/imagepy/menus/Kit3D/Viewer 3D/demo_plgs.py b/imagepy/menus/Kit3D/Viewer 3D/demo_plgs.py index ae79aece..65ffb778 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/demo_plgs.py +++ b/imagepy/menus/Kit3D/Viewer 3D/demo_plgs.py @@ -1,4 +1,4 @@ -from imagepy.core.engine import Free +from sciapp.action import Free from sciapp.object import Surface, MarkText from sciapp.util import surfutil import numpy as np diff --git a/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py b/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py index b2fdbed2..1ee73c63 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py +++ b/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py @@ -4,7 +4,7 @@ @author: yxl """ -from imagepy.core.engine import Simple, Filter, Free +from sciapp.action import Simple, Filter, Free from scipy.ndimage.filters import gaussian_filter from sciapp.object import Surface, MarkText from sciapp.util import surfutil diff --git a/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py b/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py index 424b7674..436bb38a 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py +++ b/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py @@ -1,4 +1,4 @@ -from imagepy.core.engine import Table +from sciapp.action import Table from sciapp import Source import numpy as np diff --git a/imagepy/menus/Plugins/Contribute/Contributions/OpenCV.md b/imagepy/menus/Plugins/Contribute/Contributions/OpenCV.md index c85b7ad8..9e6b6f95 100644 --- a/imagepy/menus/Plugins/Contribute/Contributions/OpenCV.md +++ b/imagepy/menus/Plugins/Contribute/Contributions/OpenCV.md @@ -35,7 +35,7 @@ Do a simple filter ```python # -*- coding: utf-8 -* import cv2 -from imagepy.core.engine import Filter +from sciapp.action import Filter class Plugin(Filter): title = 'Laplacian' @@ -72,7 +72,7 @@ Filter with parameter ```python # -*- coding: utf-8 -* import cv2 -from imagepy.core.engine import Filter +from sciapp.action import Filter class Plugin(Filter): title = 'Canny' @@ -99,7 +99,7 @@ More detail information please see [ImagePy's README](https://github.com/Image-P # -*- coding: utf-8 -*- from imagepy import IPy import numpy as np, cv2 -from imagepy.core.engine import Filter +from sciapp.action import Filter class Plugin(Filter): title = 'Adaptive Threshold' @@ -126,7 +126,7 @@ Watershed with interactive marker ![interactive watershed](http://opencvplgs.imagepy.org/watershed.png) ```python # -*- coding: utf-8 -* -from imagepy.core.engine import Filter +from sciapp.action import Filter import numpy as np, cv2 class Plugin(Filter): @@ -158,7 +158,7 @@ mark is a overlay drawn on a image, It has draw method with parameter: 3. **key** other parameter such as slice number. ```python # -*- coding: utf-8 -* -from imagepy.core.engine import Tool, Filter +from sciapp.action import Tool, Filter import numpy as np, wx, cv2 class Mark(): diff --git a/imagepy/menus/Plugins/Contribute/Contributions/SimpleITK.md b/imagepy/menus/Plugins/Contribute/Contributions/SimpleITK.md index b1d49e00..0f4928f7 100644 --- a/imagepy/menus/Plugins/Contribute/Contributions/SimpleITK.md +++ b/imagepy/menus/Plugins/Contribute/Contributions/SimpleITK.md @@ -53,7 +53,7 @@ def write(path, img): ``` ### register reader and writer to the io manager ```python -from imagepy.core.engine import dataio +from sciapp.action import dataio # add dicom reader and writer dataio.add_reader(['dcm'], read) @@ -87,7 +87,7 @@ Do a simple filter ![gradient](http://idoc.imagepy.org/itk/gradient.png) ```python import SimpleITK as sitk -from imagepy.core.engine import Filter +from sciapp.action import Filter class Plugin(Filter): title = 'ITK Gradient Magnitude' @@ -122,7 +122,7 @@ Filter with parameter ```python # -*- coding: utf-8 -* import SimpleITK as sitk -from imagepy.core.engine import Filter +from sciapp.action import Filter class Plugin(Filter): title = 'ITK Discrete Gaussian' @@ -147,7 +147,7 @@ Write 3D Filter ![gaussian3d](http://idoc.imagepy.org/itk/gaussian3d.png) ```python import SimpleITK as sitk -from imagepy.core.engine import Simple +from sciapp.action import Simple class Plugin(Simple): title = 'ITK Gradient Magnitude 3D' @@ -167,7 +167,7 @@ Treat ROI and ColorImage ![roicolor](http://idoc.imagepy.org/itk/roicolor.png) ```python import SimpleITK as sitk -from imagepy.core.engine import Filter, Simple +from sciapp.action import Filter, Simple import numpy as np class Plugin(Filter): @@ -190,7 +190,7 @@ Watershed With ROI ![roiwatershed](http://idoc.imagepy.org/itk/roiwatershed.png) ```python import SimpleITK as sitk -from imagepy.core.engine import Filter, Simple +from sciapp.action import Filter, Simple import numpy as np class Plugin(Filter): diff --git a/imagepy/menus/Plugins/Games/crossstick_plg.py b/imagepy/menus/Plugins/Games/crossstick_plg.py index 19f7d880..ba45a200 100644 --- a/imagepy/menus/Plugins/Games/crossstick_plg.py +++ b/imagepy/menus/Plugins/Games/crossstick_plg.py @@ -1,4 +1,4 @@ -from imagepy.core.engine import Filter +from sciapp.action import Filter import numpy as np from scipy.cluster.vq import kmeans, vq diff --git a/imagepy/menus/Plugins/Games/drawstep_plg.py b/imagepy/menus/Plugins/Games/drawstep_plg.py index f8c0402a..ceaec72a 100644 --- a/imagepy/menus/Plugins/Games/drawstep_plg.py +++ b/imagepy/menus/Plugins/Games/drawstep_plg.py @@ -4,7 +4,7 @@ from imagepy.ipyalg import distance_transform_edt import numpy as np from imagepy.ipyalg.graph import sknw -from imagepy.core.engine import Simple +from sciapp.action import Simple def draw_pixs(img, xs, ys, color=None): diff --git a/imagepy/menus/Plugins/Games/lifegame_plg.py b/imagepy/menus/Plugins/Games/lifegame_plg.py index bdbad486..d497caa5 100644 --- a/imagepy/menus/Plugins/Games/lifegame_plg.py +++ b/imagepy/menus/Plugins/Games/lifegame_plg.py @@ -1,5 +1,5 @@ import numpy as np -from imagepy.core.engine import Free +from sciapp.action import Free from sciapp.action import ImageTool from scipy.ndimage import label from scipy.signal import convolve2d diff --git a/imagepy/menus/Plugins/Install/installpkg_plgs.py b/imagepy/menus/Plugins/Install/installpkg_plgs.py index 5f5e37e0..87cecc0f 100644 --- a/imagepy/menus/Plugins/Install/installpkg_plgs.py +++ b/imagepy/menus/Plugins/Install/installpkg_plgs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from imagepy.core.engine import Free +from sciapp.action import Free import subprocess, sys import pandas as pd diff --git a/imagepy/menus/Plugins/Install/installplg_plgs.py b/imagepy/menus/Plugins/Install/installplg_plgs.py index c9a49a01..c572090a 100644 --- a/imagepy/menus/Plugins/Install/installplg_plgs.py +++ b/imagepy/menus/Plugins/Install/installplg_plgs.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from imagepy import root_dir -from imagepy.core.engine import Free +from sciapp.action import Free import os, subprocess, zipfile, shutil import zipfile, sys, urllib diff --git a/imagepy/menus/Plugins/Macros/recorder_plg.py b/imagepy/menus/Plugins/Macros/recorder_plg.py index 3638aaea..f8df3096 100644 --- a/imagepy/menus/Plugins/Macros/recorder_plg.py +++ b/imagepy/menus/Plugins/Macros/recorder_plg.py @@ -1,10 +1,10 @@ -from imagepy.core.engine import dataio +from sciapp.action import dataio from sciapp import Source def readmc(path): with open(path) as f: return f.readlines() -Source.manager('reader').add('mc', readmc, 'mc') +dataio.ReaderManager.add('mc', readmc, 'mc') class Macros(dataio.Reader): title = 'Run Macros' @@ -14,7 +14,7 @@ class Macros(dataio.Reader): def readwf(path): with open(path) as f: return f.read() -Source.manager('reader').add('wf', readwf, 'wf') +dataio.ReaderManager.add('wf', readwf, 'wf') class WorkFlow(dataio.Reader): title = 'Run WorkFlow' diff --git a/imagepy/menus/Plugins/Macros/recorder_wgt.py b/imagepy/menus/Plugins/Macros/recorder_wgt.py index ec00a701..a8c32a8f 100644 --- a/imagepy/menus/Plugins/Macros/recorder_wgt.py +++ b/imagepy/menus/Plugins/Macros/recorder_wgt.py @@ -1,5 +1,5 @@ import wx, weakref -from imagepy.core.engine import Macros +from sciapp.action import Macros import os.path as osp class Plugin ( wx.Panel ): diff --git a/imagepy/menus/Plugins/Manager/console_wgt.py b/imagepy/menus/Plugins/Manager/console_wgt.py index 5e08cae7..081a01ba 100644 --- a/imagepy/menus/Plugins/Manager/console_wgt.py +++ b/imagepy/menus/Plugins/Manager/console_wgt.py @@ -5,7 +5,7 @@ import numpy as np # from imagepy import IPy -from imagepy.core.engine import Free +from sciapp.action import Free from sciapp import Source cmds = {'app':'app', 'np':np, 'ndimg':ndimg, 'update':None, 'get_img':None} diff --git a/imagepy/menus/Plugins/Manager/plgtree_wgt.py b/imagepy/menus/Plugins/Manager/plgtree_wgt.py index 87716507..16b606b2 100644 --- a/imagepy/menus/Plugins/Manager/plgtree_wgt.py +++ b/imagepy/menus/Plugins/Manager/plgtree_wgt.py @@ -5,10 +5,10 @@ @author: yxl """ -from imagepy.core.engine import Free +from sciapp.action import Free import wx,os from imagepy import root_dir -from imagepy.core.app import loader +from imagepy.app import loader from wx.py.editor import EditorFrame from sciwx.text import MDPad from sciapp import Source diff --git a/imagepy/menus/Plugins/Manager/shotcut_wgt.py b/imagepy/menus/Plugins/Manager/shotcut_wgt.py index a7ee1993..796f377a 100644 --- a/imagepy/menus/Plugins/Manager/shotcut_wgt.py +++ b/imagepy/menus/Plugins/Manager/shotcut_wgt.py @@ -1,5 +1,5 @@ import wx, os -from imagepy.core.engine import Free +from sciapp.action import Free from sciapp import Source from imagepy import root_dir diff --git a/imagepy/menus/Plugins/Manager/toltree_wgt.py b/imagepy/menus/Plugins/Manager/toltree_wgt.py index a3c45c2f..3425ad48 100644 --- a/imagepy/menus/Plugins/Manager/toltree_wgt.py +++ b/imagepy/menus/Plugins/Manager/toltree_wgt.py @@ -5,10 +5,10 @@ @author: yxl """ -from imagepy.core.engine import Free +from sciapp.action import Free import wx,os from imagepy import root_dir -from imagepy.core.app import loader +from imagepy.app import loader from wx.py.editor import EditorFrame from sciapp import Source #from imagepy.ui.mkdownwindow import HtmlPanel, md2html diff --git a/imagepy/menus/Plugins/New/demo_filter.py b/imagepy/menus/Plugins/New/demo_filter.py index b64e647f..9479e17c 100644 --- a/imagepy/menus/Plugins/New/demo_filter.py +++ b/imagepy/menus/Plugins/New/demo_filter.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- import scipy.ndimage as nimg -from imagepy.core.engine import Filter +from sciapp.action import Filter # this is a Filter Sample, implements the Gaussian Blur class Plugin(Filter): diff --git a/imagepy/menus/Plugins/New/demo_free.py b/imagepy/menus/Plugins/New/demo_free.py index 976b4c4f..9dd86eb8 100644 --- a/imagepy/menus/Plugins/New/demo_free.py +++ b/imagepy/menus/Plugins/New/demo_free.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from imagepy.core.engine import Free +from sciapp.action import Free from imagepy import IPy class Plugin(Free): diff --git a/imagepy/menus/Plugins/New/demo_simple.py b/imagepy/menus/Plugins/New/demo_simple.py index f43cac67..5efc1a41 100644 --- a/imagepy/menus/Plugins/New/demo_simple.py +++ b/imagepy/menus/Plugins/New/demo_simple.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from imagepy.core.engine import Simple +from sciapp.action import Simple # a simple demo implements the next slice class Plugin(Simple): diff --git a/imagepy/menus/Plugins/New/demo_tool.py b/imagepy/menus/Plugins/New/demo_tool.py index a8e224fd..3f1fbc6e 100644 --- a/imagepy/menus/Plugins/New/demo_tool.py +++ b/imagepy/menus/Plugins/New/demo_tool.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from imagepy.core.draw import paint -from imagepy.core.engine import Tool +from imagepy.draw import paint +from sciapp.action import Tool # this is a simple tool implements a pencial class Plugin(Tool): diff --git a/imagepy/menus/Plugins/New/new_plg.py b/imagepy/menus/Plugins/New/new_plg.py index 6f501da0..9acda5dd 100644 --- a/imagepy/menus/Plugins/New/new_plg.py +++ b/imagepy/menus/Plugins/New/new_plg.py @@ -5,7 +5,7 @@ @author: yxl """ import os, wx -from imagepy.core.engine import Free +from sciapp.action import Free from wx.py.editor import EditorFrame from imagepy import root_dir diff --git a/imagepy/menus/Plugins/StackReg/stackreg_plgs.py b/imagepy/menus/Plugins/StackReg/stackreg_plgs.py index ab78271b..03275ea7 100644 --- a/imagepy/menus/Plugins/StackReg/stackreg_plgs.py +++ b/imagepy/menus/Plugins/StackReg/stackreg_plgs.py @@ -1,4 +1,4 @@ -from imagepy.core.engine import Filter, Simple +from sciapp.action import Filter, Simple from pystackreg import StackReg import numpy as np import pandas as pd @@ -20,7 +20,7 @@ class Register(Simple): def run(self, ips, imgs, para = None): k = para['diag']/np.sqrt((np.array(ips.img.shape)**2).sum()) size = tuple((np.array(ips.img.shape)*k).astype(np.int16)) - IPy.set_info('down sample...') + IPy.info('down sample...') news = [] for img in imgs: if k!=0: img = tf.resize(img, size) @@ -28,7 +28,7 @@ def run(self, ips, imgs, para = None): img = ndimg.gaussian_filter(img, para['sigma']) news.append(img) - IPy.set_info('register...') + IPy.info('register...') sr = StackReg(eval('StackReg.%s'%para['trans'])) sr.register_stack(np.array(news), reference=para['ref']) @@ -40,7 +40,7 @@ def run(self, ips, imgs, para = None): mats, columns=['A%d'%(i+1) for i in range(mats.shape[1])]), title='%s-Tmats'%ips.title) if para['new'] == 'None': return - IPy.set_info('transform...') + IPy.info('transform...') for i in range(sr._tmats.shape[0]): tform = tf.ProjectiveTransform(matrix=sr._tmats[i]) img = tf.warp(imgs[i], tform) diff --git a/imagepy/menus/Plugins/screencap_plg.py b/imagepy/menus/Plugins/screencap_plg.py index 06d2409b..751bde32 100644 --- a/imagepy/menus/Plugins/screencap_plg.py +++ b/imagepy/menus/Plugins/screencap_plg.py @@ -1,4 +1,4 @@ -from imagepy.core.engine import Free +from sciapp.action import Free import time, wx, numpy as np class Plugin(Free): diff --git a/imagepy/menus/Plugins/temporal_plg.py b/imagepy/menus/Plugins/temporal_plg.py index 9f4352cb..0c27ae75 100644 --- a/imagepy/menus/Plugins/temporal_plg.py +++ b/imagepy/menus/Plugins/temporal_plg.py @@ -2,7 +2,7 @@ Created on Sun Jan 22 12:56:00 2020 @author: weisong """ -from imagepy.core.engine import Simple +from sciapp.action import Simple from sciapp import Source import numpy as np diff --git a/imagepy/menus/Plugins/update_plg.py b/imagepy/menus/Plugins/update_plg.py index c1c93e26..6f19f2b5 100644 --- a/imagepy/menus/Plugins/update_plg.py +++ b/imagepy/menus/Plugins/update_plg.py @@ -1,4 +1,4 @@ -from imagepy.core.engine import Free +from sciapp.action import Free import os, sys, os.path as osp import zipfile, urllib from io import BytesIO @@ -19,7 +19,7 @@ class Update(Free): title = 'Update Software' def run(self, para=None): - IPy.set_info('update now, waiting...') + IPy.info('update now, waiting...') self.download_zip() self.deal_file() #self.delete_cache() diff --git a/imagepy/menus/Process/Binary/binary_plgs.py b/imagepy/menus/Process/Binary/binary_plgs.py index c6fca418..378e6a53 100644 --- a/imagepy/menus/Process/Binary/binary_plgs.py +++ b/imagepy/menus/Process/Binary/binary_plgs.py @@ -8,12 +8,12 @@ # -*- coding: utf-8 -* import scipy.ndimage as ndimg import numpy as np -from imagepy.core.engine import Filter +from sciapp.action import Filter from skimage.morphology import convex_hull_object from skimage.segmentation import clear_border class Closing(Filter): - """Closing: derived from imagepy.core.engine.Filter """ + """Closing: derived from sciapp.action.Filter """ title = 'Binary Closing' note = ['8-bit', 'auto_msk', 'auto_snap','preview'] para = {'w':3, 'h':3} @@ -26,7 +26,7 @@ def run(self, ips, snap, img, para = None): img *= 255 class Opening(Filter): - """Opening: derived from imagepy.core.engine.Filter """ + """Opening: derived from sciapp.action.Filter """ title = 'Binary Opening' note = ['8-bit', 'auto_msk', 'auto_snap','preview'] para = {'w':3, 'h':3} @@ -39,7 +39,7 @@ def run(self, ips, snap, img, para = None): img *= 255 class Dilation(Filter): - """Dilation: derived from imagepy.core.engine.Filter """ + """Dilation: derived from sciapp.action.Filter """ title = 'Binary Dilation' note = ['8-bit', 'auto_msk', 'auto_snap','preview'] para = {'w':3, 'h':3} @@ -52,7 +52,7 @@ def run(self, ips, snap, img, para = None): img *= 255 class Erosion(Filter): - """Erosion: derived from imagepy.core.engine.Filter """ + """Erosion: derived from sciapp.action.Filter """ title = 'Binary Erosion' note = ['8-bit', 'auto_msk', 'auto_snap','preview'] para = {'w':3, 'h':3} @@ -65,7 +65,7 @@ def run(self, ips, snap, img, para = None): img *= 255 class Outline(Filter): - """Outline: derived from imagepy.core.engine.Filter """ + """Outline: derived from sciapp.action.Filter """ title = 'Binary Outline' note = ['8-bit', 'auto_msk', 'auto_snap','preview'] @@ -75,7 +75,7 @@ def run(self, ips, snap, img, para = None): img -= snap class FillHoles(Filter): - """FillHoles: derived from imagepy.core.engine.Filter """ + """FillHoles: derived from sciapp.action.Filter """ title = 'Fill Holes' note = ['8-bit', 'auto_msk', 'auto_snap','preview'] diff --git a/imagepy/menus/Process/Binary/distance_plgs.py b/imagepy/menus/Process/Binary/distance_plgs.py index cdf9015b..211d579e 100644 --- a/imagepy/menus/Process/Binary/distance_plgs.py +++ b/imagepy/menus/Process/Binary/distance_plgs.py @@ -7,7 +7,7 @@ from skimage.morphology import skeletonize from skimage.morphology import medial_axis from imagepy.ipyalg.graph import skel2d -from imagepy.core.engine import Filter +from sciapp.action import Filter from imagepy.ipyalg import find_maximum, watershed, distance_transform_edt from skimage.filters import apply_hysteresis_threshold import scipy.ndimage as ndimg @@ -21,7 +21,7 @@ def run(self, ips, snap, img, para = None): img *= 255 class EDT(Filter): - """EDT: derived from imagepy.core.engine.Filter """ + """EDT: derived from sciapp.action.Filter """ title = 'Distance Transform' note = ['all', 'auto_msk', 'auto_snap','preview'] diff --git a/imagepy/menus/Process/Classify/classify_plgs.py b/imagepy/menus/Process/Classify/classify_plgs.py index 3a1bf0e5..bbfe1bd1 100644 --- a/imagepy/menus/Process/Classify/classify_plgs.py +++ b/imagepy/menus/Process/Classify/classify_plgs.py @@ -1,4 +1,4 @@ -from imagepy.core.engine import Filter, Simple +from sciapp.action import Filter, Simple # from imagepy.core import ImagePlus import numpy as np @@ -11,7 +11,7 @@ model_para = None class Base(Simple): - """Closing: derived from imagepy.core.engine.Filter """ + """Closing: derived from sciapp.action.Filter """ def load(self, ips): if len(ips.imgs)==1: ips.snapshot() return True @@ -37,15 +37,15 @@ def run(self, ips, imgs, para = None, preview=False): if len(imgs)==1: ori = [ImageManager.get(para['img']).img] oris = [i[slir, slic] for i in ori] - IPy.set_info('extract features...') + IPy.info('extract features...') feat, lab, key = feature.get_feature(oris, labs, key, callback=self.progress) - IPy.set_info('training data...') + IPy.info('training data...') self.progress(None, 1) model = self.classify(para) model.fit(feat, lab) - IPy.set_info('predict data...') + IPy.info('predict data...') if preview: return feature.get_predict(oris, model, key, labs, callback=self.progress) if len(imgs) == 1: ips.swap() @@ -82,7 +82,7 @@ def classify(self, para): max_features = feat_dic[para['max_features']], max_depth=max_depth) class AdaBoost(Base): - """Closing: derived from imagepy.core.engine.Filter """ + """Closing: derived from sciapp.action.Filter """ title = 'AdaBoost Classify' note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] para = {'img':None, 'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, @@ -105,7 +105,7 @@ def classify(self, para): learning_rate = para['learning_rate'], algorithm=para['algorithm']) class Bagging(Base): - """Closing: derived from imagepy.core.engine.Filter """ + """Closing: derived from sciapp.action.Filter """ title = 'Bagging Classify' note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] para = {'img':None, 'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, @@ -127,7 +127,7 @@ def classify(self, para): max_features = para['max_features']) class ExtraTrees(Base): - """Closing: derived from imagepy.core.engine.Filter """ + """Closing: derived from sciapp.action.Filter """ title = 'ExtraTrees Classify' note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] para = {'img':None, 'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, @@ -152,7 +152,7 @@ def classify(self, para): max_features = feat_dic[para['max_features']], max_depth=max_depth) class GradientBoosting(Base): - """Closing: derived from imagepy.core.engine.Filter """ + """Closing: derived from sciapp.action.Filter """ title = 'Gradient Boosting Classify' note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] para = {'img':None, 'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, @@ -180,7 +180,7 @@ def classify(self, para): max_depth=para['max_depth'], learning_rate=para['learning_rate']) class Voting(Base): - """Closing: derived from imagepy.core.engine.Filter """ + """Closing: derived from sciapp.action.Filter """ title = 'Voting Classify' note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] para = {'img':None, 'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, diff --git a/imagepy/menus/Process/Classify/io_plgs.py b/imagepy/menus/Process/Classify/io_plgs.py index 5f537a86..8ce18657 100644 --- a/imagepy/menus/Process/Classify/io_plgs.py +++ b/imagepy/menus/Process/Classify/io_plgs.py @@ -1,10 +1,10 @@ -from imagepy.core.engine import Filter, Simple +from sciapp.action import Filter, Simple from sciapp import Source # from imagepy.core import ImagePlus import numpy as np class BuildMark(Simple): - """Closing: derived from imagepy.core.engine.Filter """ + """Closing: derived from sciapp.action.Filter """ title = 'Build Mark Image' note = ['all'] para = {'mode':'Mask', 'cm':'16_Colors', 'n':2, 'slice':True} diff --git a/imagepy/menus/Process/Classify/label_wgt.py b/imagepy/menus/Process/Classify/label_wgt.py index ba8aa909..796a30e7 100644 --- a/imagepy/menus/Process/Classify/label_wgt.py +++ b/imagepy/menus/Process/Classify/label_wgt.py @@ -1,6 +1,6 @@ # from imagepy.ui.widgets import CMapSelCtrl # from imagepy.core.manager import ColorManager, ImageManager, WindowsManager, ToolsManager -from imagepy.core.engine import Macros +from sciapp.action import Macros import numpy as np import wx, os.path as osp diff --git a/imagepy/menus/Process/Classify/predict_plg.py b/imagepy/menus/Process/Classify/predict_plg.py index de920644..da118ef8 100644 --- a/imagepy/menus/Process/Classify/predict_plg.py +++ b/imagepy/menus/Process/Classify/predict_plg.py @@ -1,4 +1,4 @@ -from imagepy.core.engine import Simple +from sciapp.action import Simple # from imagepy.core import ImagePlus from glob import glob import os.path as osp diff --git a/imagepy/menus/Process/FFT/fft_plgs.py b/imagepy/menus/Process/FFT/fft_plgs.py index 7740d8b9..e4bab2ce 100644 --- a/imagepy/menus/Process/FFT/fft_plgs.py +++ b/imagepy/menus/Process/FFT/fft_plgs.py @@ -1,5 +1,5 @@ import numpy as np -from imagepy.core.engine import Simple, Filter +from sciapp.action import Simple, Filter from numpy.fft import fft2, ifft2, fftshift, ifftshift from sciapp.object import Image #from imagepy.core import ImagePlus diff --git a/imagepy/menus/Process/Features/blob_plgs.py b/imagepy/menus/Process/Features/blob_plgs.py index b2927b13..373de9de 100644 --- a/imagepy/menus/Process/Features/blob_plgs.py +++ b/imagepy/menus/Process/Features/blob_plgs.py @@ -1,5 +1,5 @@ import numpy as np -from imagepy.core.engine import Simple +from sciapp.action import Simple from skimage.feature import blob_dog, blob_doh, blob_log from sciapp.object import mark2shp import pandas as pd diff --git a/imagepy/menus/Process/Features/corner_plgs.py b/imagepy/menus/Process/Features/corner_plgs.py index 30058e54..cc9407ee 100644 --- a/imagepy/menus/Process/Features/corner_plgs.py +++ b/imagepy/menus/Process/Features/corner_plgs.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -* from skimage import feature -from imagepy.core.engine import Filter +from sciapp.action import Filter from sciapp.object import Points, ROI class Harris(Filter): diff --git a/imagepy/menus/Process/Features/edge_plgs.py b/imagepy/menus/Process/Features/edge_plgs.py index bfb55635..bcf9134b 100644 --- a/imagepy/menus/Process/Features/edge_plgs.py +++ b/imagepy/menus/Process/Features/edge_plgs.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -* from skimage import feature -from imagepy.core.engine import Filter +from sciapp.action import Filter class Canny(Filter): title = 'Canny' diff --git a/imagepy/menus/Process/Features/ridge_plgs.py b/imagepy/menus/Process/Features/ridge_plgs.py index 925f364f..cb1b2b00 100644 --- a/imagepy/menus/Process/Features/ridge_plgs.py +++ b/imagepy/menus/Process/Features/ridge_plgs.py @@ -1,5 +1,5 @@ from skimage.filters import frangi, sato, hessian ,meijering -from imagepy.core.engine import Filter, Simple +from sciapp.action import Filter, Simple def scale(img, low, high): img *= (high-low)/(max(img.ptp(), 1e-5)) diff --git a/imagepy/menus/Process/Filters/classic_plgs.py b/imagepy/menus/Process/Filters/classic_plgs.py index c4bd890f..7a5f34a1 100644 --- a/imagepy/menus/Process/Filters/classic_plgs.py +++ b/imagepy/menus/Process/Filters/classic_plgs.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -* import scipy.ndimage as nimg -from imagepy.core.engine import Filter, Simple +from sciapp.action import Filter, Simple import numpy as np class Uniform(Filter): diff --git a/imagepy/menus/Process/Hydrology/hydrology_plgs.py b/imagepy/menus/Process/Hydrology/hydrology_plgs.py index d938b86f..171d61bc 100644 --- a/imagepy/menus/Process/Hydrology/hydrology_plgs.py +++ b/imagepy/menus/Process/Hydrology/hydrology_plgs.py @@ -1,6 +1,6 @@ import scipy.ndimage as ndimg import numpy as np -from imagepy.core.engine import Filter +from sciapp.action import Filter from imagepy.ipyalg import find_maximum, ridge, stair, isoline, watershed # from imagepy.core.roi import PointRoi from sciapp.object import Points, ROI diff --git a/imagepy/menus/Process/Math/math_plgs.py b/imagepy/menus/Process/Math/math_plgs.py index ed838637..67752bd7 100644 --- a/imagepy/menus/Process/Math/math_plgs.py +++ b/imagepy/menus/Process/Math/math_plgs.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -* import numpy as np -from imagepy.core.engine import Filter +from sciapp.action import Filter class Add(Filter): - """Add_plg: derived from imagepy.core.engine.Filter """ + """Add_plg: derived from sciapp.action.Filter """ title = 'Add' note = ['all', 'auto_msk', 'auto_snap', 'preview', '2int'] para = {'num':0} @@ -13,7 +13,7 @@ def run(self, ips, snap, img, para = None): np.add(snap, para['num'], out=img, casting='unsafe') class Subtract(Filter): - """Subtract_plg: derived from imagepy.core.engine.Filter """ + """Subtract_plg: derived from sciapp.action.Filter """ title = 'Subtract' note = ['all', 'auto_msk', 'auto_snap', 'preview', '2int'] para = {'num':0} @@ -23,7 +23,7 @@ def run(self, ips, snap, img, para = None): np.subtract(snap, para['num'], out=img, casting='unsafe') class Multiply(Filter): - """Multiply_plg: derived from imagepy.core.engine.Filter """ + """Multiply_plg: derived from sciapp.action.Filter """ title = 'Multiply' note = ['all', 'auto_msk', 'auto_snap', 'preview', '2int'] para = {'num':0} @@ -33,7 +33,7 @@ def run(self, ips, snap, img, para = None): np.multiply(snap, para['num'], out=img, casting='unsafe') class Max(Filter): - """Max_plg: derived from imagepy.core.engine.Filter """ + """Max_plg: derived from sciapp.action.Filter """ title = 'Max' note = ['all', 'auto_msk', 'auto_snap', 'preview'] para = {'num':0} @@ -44,7 +44,7 @@ def run(self, ips, snap, img, para = None): img[img{'path':%s}"%repr(path)]) - -rlist = [f(i) for i in recent] - -def add_recent(path): - global recent, rlist - if path in recent: - idx = recent.index(path) - recent.insert(0, recent.pop(idx)) - rlist.insert(0, rlist.pop(idx)) - else: - recent.insert(0, path) - rlist.insert(0, f(path)) - if len(recent)>=5: - recent.pop(-1) - rlist.pop(-1) - - Source.manager('config').add('recent', recent) - #IPy.curapp.reload_plugins() +ReaderManager = Manager() +WriterManager = Manager() class Reader(Free): para = {'path':''} @@ -44,7 +23,7 @@ def run(self, para = None): add_recent(para['path']) fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) - readers = Source.manager('reader').gets(name=fe[1:], tag=self.tag) + readers = ReaderManager.gets(fe[1:], tag=self.tag) if len(readers)==0: return self.app.alert('no reader found for %s file'%fe[1:]) if not self.tag is None: @@ -65,7 +44,7 @@ def show(self): def run(self, ips, imgs, para = None): fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) - writer = Source.manager('writer').gets(name=fe[1:].lower(), tag=self.tag) + writer = WriterManager.gets(fe[1:].lower(), tag=self.tag) if len(writer)==1: writer[0][1](para['path'], ips.img if self.tag=='img' else imgs) class TableWriter(Table): @@ -83,5 +62,5 @@ def run(self, tps, snap, data, para = None): fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) - writer = Source.manager('writer').gets(name=fe[1:], tag=self.tag) + writer = WriterManager.gets(fe[1:], tag=self.tag) if len(writer)==1: return writer[0][1](para['path'], data) \ No newline at end of file diff --git a/imagepy/core/engine/macros.py b/sciapp/action/advanced/macros.py similarity index 100% rename from imagepy/core/engine/macros.py rename to sciapp/action/advanced/macros.py diff --git a/sciapp/action/advanced/table.py b/sciapp/action/advanced/table.py index 3f1f9787..706c7a63 100644 --- a/sciapp/action/advanced/table.py +++ b/sciapp/action/advanced/table.py @@ -57,7 +57,7 @@ def runasyn(self, tps, snap, data, para = None, callback = None): self.app.add_task(self) start = time() self.run(tps, data, snap, para) - self.app.set_info('%s: cost %.3fs'%(tps.title, time()-start)) + self.app.info('%s: cost %.3fs'%(tps.title, time()-start)) tps.update() self.app.remove_task(self) if callback!=None:callback() diff --git a/imagepy/core/engine/widget.py b/sciapp/action/advanced/widget.py similarity index 100% rename from imagepy/core/engine/widget.py rename to sciapp/action/advanced/widget.py diff --git a/sciapp/action/tolact.py b/sciapp/action/tolact.py index 8255e822..ecc5bc62 100644 --- a/sciapp/action/tolact.py +++ b/sciapp/action/tolact.py @@ -53,7 +53,7 @@ def mouse_move(self, img, x, y, btn, **key): r, c = int(y), int(x) if (r>0) & (c>0) & (r Date: Sun, 28 Jun 2020 23:34:35 +0800 Subject: [PATCH 280/343] console --- imagepy/app/__init__.py | 1 + imagepy/app/console.py | 24 +++ imagepy/app/imagej.py | 2 +- imagepy/app/imagepy.py | 70 +++---- imagepy/app/source.py | 1 - imagepy/data/config.json | 2 +- sciapp/__init__.py | 12 +- sciapp/action/__init__.py | 6 +- sciapp/action/advanced/dataio.py | 5 +- sciapp/action/advanced/filter.py | 28 ++- sciapp/action/advanced/free.py | 23 ++- sciapp/action/advanced/simple.py | 6 +- sciapp/action/advanced/table.py | 6 +- sciapp/action/plugin/__init__.py | 1 + sciapp/action/plugin/filters.py | 11 + sciapp/action/plugin/generalio.py | 20 ++ .../action/plugin/{meaact.py => mea_tools.py} | 0 .../action/plugin/{roiact.py => roi_tools.py} | 2 +- .../action/plugin/{shpact.py => shp_tools.py} | 0 sciapp/action/tabact.py | 2 +- sciapp/app.py | 195 ++++++------------ sciapp/demo/app_test.py | 27 +++ sciapp/demo/imagepy_console.py | 12 ++ sciapp/manager.py | 57 +++++ sciapp/object/table.py | 4 +- sciwx/app/miniapp.py | 5 +- sciwx/plugins/channels.py | 2 +- sciwx/widgets/advanced.py | 8 +- 28 files changed, 310 insertions(+), 222 deletions(-) create mode 100644 imagepy/app/console.py create mode 100644 sciapp/action/plugin/__init__.py create mode 100644 sciapp/action/plugin/filters.py create mode 100644 sciapp/action/plugin/generalio.py rename sciapp/action/plugin/{meaact.py => mea_tools.py} (100%) rename sciapp/action/plugin/{roiact.py => roi_tools.py} (98%) rename sciapp/action/plugin/{shpact.py => shp_tools.py} (100%) create mode 100644 sciapp/demo/app_test.py create mode 100644 sciapp/demo/imagepy_console.py create mode 100644 sciapp/manager.py diff --git a/imagepy/app/__init__.py b/imagepy/app/__init__.py index bff58554..c6d5ab9d 100644 --- a/imagepy/app/__init__.py +++ b/imagepy/app/__init__.py @@ -1,3 +1,4 @@ from .imagepy import ImagePy from .imagej import ImageJ +from .console import Console from .import startup \ No newline at end of file diff --git a/imagepy/app/console.py b/imagepy/app/console.py new file mode 100644 index 00000000..64aa0d18 --- /dev/null +++ b/imagepy/app/console.py @@ -0,0 +1,24 @@ +from sciapp import App, Source +from imagepy import root_dir +from .startup import load_plugins + +class Console(App): + def __init__( self ): + App.__init__(self, False) + + def load_plugins(self, plgs=None): + if plgs is None: plgs = load_plugins()[0] + if isinstance(plgs, tuple): + if callable(plgs[1]): + name, plg = plgs[:2] + self.add_plugin(name, plg, 'plugin') + for i in ' _.-': + name = name.replace(i,'_') + exec('self._%s_ = plg'%name) + else: self.load_plugins(plgs[1]) + if isinstance(plgs, list): + for i in plgs: self.load_plugins(i) + +if __name__ == '__main__': + import numpy as np + import pandas as pd diff --git a/imagepy/app/imagej.py b/imagepy/app/imagej.py index 8c0b1af4..6ea66d5d 100644 --- a/imagepy/app/imagej.py +++ b/imagepy/app/imagej.py @@ -377,7 +377,7 @@ def switch_table(self, visible=None): self.auimgr.Update() def close_img(self, name=None): - names = self.get_img_name() if name is None else [name] + names = self.img_names() if name is None else [name] for name in names: idx = self.canvasnb.GetPageIndex(self.get_img_win(name)) self.remove_img(self.get_img_win(name).image) diff --git a/imagepy/app/imagepy.py b/imagepy/app/imagepy.py index 6c11102c..d1b0ae4d 100644 --- a/imagepy/app/imagepy.py +++ b/imagepy/app/imagepy.py @@ -10,7 +10,7 @@ from sciwx.plot import PlotFrame from skimage.data import camera from sciapp import App, Source -from sciapp.object import Image +from sciapp.object import Image, Table from imagepy import root_dir from .startup import load_plugins, load_tools, load_widgets, load_document, load_dictionary @@ -190,7 +190,7 @@ def init_canvas(self): self.auimgr.AddPane( self.canvasnbwrap, aui.AuiPaneInfo() .Center() .CaptionVisible( False ).PinButton( True ).Dock() .PaneBorder( False ).Resizable().FloatingSize( wx.DefaultSize ). BottomDockable( True ).TopDockable( False ) .LeftDockable( True ).RightDockable( True ) ) - self.canvasnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_new_img) + self.canvasnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_active_img) self.canvasnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close_img) def init_table(self): @@ -204,8 +204,8 @@ def init_table(self): self.auimgr.AddPane( self.tablenbwrap, aui.AuiPaneInfo() .Bottom() .CaptionVisible( True ).PinButton( True ).Dock().Hide() .MaximizeButton( True ).Resizable().FloatingSize((800, 600)).BestSize(( 120,120 )). Caption('Table') . BottomDockable( True ).TopDockable( False ).LeftDockable( True ).RightDockable( True ) ) - self.tablenb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_new_tab) - self.tablenb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close_tab) + self.tablenb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_active_table) + self.tablenb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close_table) def init_mesh(self): self.meshnbwrap = wx.Panel(self) @@ -269,23 +269,21 @@ def on_pan_close(self, event): if hasattr(event.GetPane().window, 'close'): event.GetPane().window.close() - def on_new_img(self, event): - self.add_img(self.canvasnb.canvas().image) - self.add_img_win(self.canvasnb.canvas()) + def on_active_img(self, event): + self.active_img(self.canvasnb.canvas().image.name) + #self.add_img_win(self.canvasnb.canvas()) def on_close_img(self, event): canvas = event.GetEventObject().GetPage(event.GetSelection()) - self.remove_img_win(canvas) - self.remove_img(canvas.image) + #self.remove_img_win(canvas) + App.close_img(self, canvas.image.title) - def on_new_tab(self, event): - self.add_tab(self.tablenb.grid().table) - self.add_tab_win(self.tablenb.grid()) + def on_active_table(self, event): + self.active_table(self.tablenb.grid().table.title) - def on_close_tab(self, event): + def on_close_table(self, event): grid = event.GetEventObject().GetPage(event.GetSelection()) - self.remove_tab_win(grid) - self.remove_tab(grid.table) + App.close_table(self, grid.table.title) def on_new_mesh(self, event): self.add_mesh(self.meshnb.canvas().mesh) @@ -324,29 +322,23 @@ def on_close(self, event): def _show_img(self, img, title=None): canvas = self.canvasnb.add_canvas() - self.remove_img(canvas.image) - self.remove_img_win(canvas) - if not title is None: - canvas.set_imgs(img) - canvas.image.name = title - else: canvas.set_img(img) - self.add_img(canvas.image) - self.add_img_win(canvas) + if not isinstance(img, Image): + img = Image(img, title) + App.show_img(self, img, img.title) + canvas.set_img(img) def show_img(self, img, title=None): wx.CallAfter(self._show_img, img, title) def _show_table(self, tab, title): grid = self.tablenb.add_grid() - self.remove_tab(grid.table) - self.remove_tab_win(grid) + if not isinstance(tab, Table): + tab = Table(tab, title) + App.show_table(self, tab, tab.title) grid.set_data(tab) - grid.table.name = title info = self.auimgr.GetPane(self.tablenbwrap) info.Show(True) self.auimgr.Update() - self.add_tab(grid.table) - self.add_tab_win(grid) def show_table(self, tab, title=None): wx.CallAfter(self._show_table, tab, title) @@ -445,21 +437,17 @@ def switch_table(self, visible=None): info.Show(not info.IsShown() if visible is None else visible) self.auimgr.Update() - def close_img(self, name=None): - names = self.get_img_name() if name is None else [name] - for name in names: - idx = self.canvasnb.GetPageIndex(self.get_img_win(name)) - self.remove_img(self.get_img_win(name).image) - self.remove_img_win(self.get_img_win(name)) - self.canvasnb.DeletePage(idx) + def close_img(self, name): + App.close_img(self, name) + for i in range(self.canvasnb.GetPageCount()): + if self.canvasnb.GetPageText(i)==name: + return self.canvasnb.DeletePage(i) def close_table(self, name=None): - names = self.get_tab_name() if name is None else [name] - for name in names: - idx = self.tablenb.GetPageIndex(self.get_tab_win(name)) - self.remove_tab(self.get_tab_win(name).table) - self.remove_tab_win(self.get_tab_win(name)) - self.tablenb.DeletePage(idx) + App.close_tab(self, name) + for i in range(self.tablenb.GetPageCount()): + if self.tablenb.GetPageText(i)==name: + return self.tablenb.DeletePage(i) def record_macros(self, cmd): obj = self.manager('widget').get(name='Macros Recorder') diff --git a/imagepy/app/source.py b/imagepy/app/source.py index e0341ef0..517e9292 100644 --- a/imagepy/app/source.py +++ b/imagepy/app/source.py @@ -41,5 +41,4 @@ keys = [osp.split(filename)[-1][:-4] for filename in filenames] values = [np.fromfile(i, dtype=np.uint8).reshape((3,256)).T.copy() for i in filenames] for k,v in zip(keys[::-1], values[::-1]): Source.manager('colormap').add(k, v, 'base') -print(Source.manager('colormap').names) Source.manager('colormap').add('Grays', Source.manager('colormap').get('Grays'), 'base') \ No newline at end of file diff --git a/imagepy/data/config.json b/imagepy/data/config.json index 5cb2d834..92f32548 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["recent", ["C:/Users/54631/Desktop/\u6d77\u51b0\u62a5\u4ef7/testmacros.mc", "C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc"], null], ["uistyle", "imagepy", null], ["language", "Chinese", null], ["roi_style", {"color": [255, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 255, 0], "size": 8}, null]] \ No newline at end of file +[["roi_style", {"color": [255, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 255, 0], "size": 8}, null], ["language", "Chinese", null], ["uistyle", "imagepy", null], ["recent", ["C:/Users/54631/Desktop/\u6d77\u51b0\u62a5\u4ef7/testmacros.mc", "C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc"], null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null]] \ No newline at end of file diff --git a/sciapp/__init__.py b/sciapp/__init__.py index 7e408ad0..11add961 100644 --- a/sciapp/__init__.py +++ b/sciapp/__init__.py @@ -1 +1,11 @@ -from .app import App, Manager, Source \ No newline at end of file +from .app import App +from .manager import Manager + +class Source: + managers = {} + + @classmethod + def manager(cls, name): + if not name in cls.managers: + cls.managers[name] = Manager() + return cls.managers[name] \ No newline at end of file diff --git a/sciapp/action/__init__.py b/sciapp/action/__init__.py index 5baeedcc..3faee3d4 100644 --- a/sciapp/action/__init__.py +++ b/sciapp/action/__init__.py @@ -1,7 +1,7 @@ from .action import SciAction from .imgact import ImgAction from .tolact import Tool, DefaultTool, ImageTool, ShapeTool, TableTool -from .plugin.meaact import DistanceTool, AngleTool, SlopeTool, AreaTool, CoordinateTool -from .plugin.shpact import * -from .plugin.roiact import * +from .plugin.mea_tools import * +from .plugin.shp_tools import * +from .plugin.roi_tools import * from .advanced import Filter, Free, Simple, Table, Macros, Widget, dataio \ No newline at end of file diff --git a/sciapp/action/advanced/dataio.py b/sciapp/action/advanced/dataio.py index 442b1b54..dd2222de 100644 --- a/sciapp/action/advanced/dataio.py +++ b/sciapp/action/advanced/dataio.py @@ -6,8 +6,7 @@ from ... import Manager import numpy as np -ReaderManager = Manager() -WriterManager = Manager() +ReaderManager, WriterManager = Manager(), Manager() class Reader(Free): para = {'path':''} @@ -20,7 +19,7 @@ def show(self): #process def run(self, para = None): - add_recent(para['path']) + #add_recent(para['path']) fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) readers = ReaderManager.gets(fe[1:], tag=self.tag) diff --git a/sciapp/action/advanced/filter.py b/sciapp/action/advanced/filter.py index 566ba87d..4196bc84 100644 --- a/sciapp/action/advanced/filter.py +++ b/sciapp/action/advanced/filter.py @@ -21,6 +21,7 @@ def process_channels(plg, ips, src, des, para): return des def process_one(plg, ips, src, img, para, callafter=None): + plg.app.record_macros('{}>{}'.format(plg.title, para)) plg.app.add_task(plg) start = time() transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16) @@ -38,12 +39,13 @@ def process_one(plg, ips, src, img, para, callafter=None): if 'auto_msk' in plg.note and not ips.mask('out') is None: msk = ips.mask('out') img[msk] = src[msk] - plg.app.info('%s: cost %.3fs'%(ips.title, time()-start)) + plg.app.info('%s: cost %.3fs'%(plg.title, time()-start)) ips.update() plg.app.remove_task(plg) if not callafter is None:callafter() def process_stack(plg, ips, src, imgs, para, callafter=None): + plg.app.record_macros('{}>{}'.format(plg.title, para)) plg.app.add_task(plg) start = time() transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16) @@ -77,6 +79,7 @@ def process_stack(plg, ips, src, imgs, para, callafter=None): class Filter: title = 'Filter' modal = True + asyn = True note = [] 'all, 8-bit, 16-bit, int, rgb, float, not_channel, not_slice, req_roi, auto_snap, auto_msk, preview, 2int, 2float' para = None @@ -131,10 +134,12 @@ def ok(self, ips, para=None, callafter=None): # = WidgetsManager.getref('Macros Recorder') if ips.slices==1 or 'not_slice' in self.note: # process_one(self, ips, ips.snap, ips.img, para) - threading.Thread(target = process_one, args = - (self, ips, ips.snap, ips.img, para, callafter)).start() + if self.asyn and self.app.asyn: + threading.Thread(target = process_one, args = + (self, ips, ips.snap, ips.img, para, callafter)).start() + else: process_one(self, ips, ips.snap, ips.img, para, callafter) # if win!=None: win.write('{}>{}'.format(self.title, para)) - self.app.record_macros('{}>{}'.format(self.title, para)) + elif ips.slices>1: has, rst = 'stack' in para, None if not has: @@ -143,15 +148,18 @@ def ok(self, ips, para=None, callafter=None): if has and para['stack'] or rst == 'yes': para['stack'] = True #process_stack(self, ips, ips.snap, ips.imgs, para) - threading.Thread(target = process_stack, args = - (self, ips, ips.snap, ips.imgs, para, callafter)).start() - self.app.record_macros('{}>{}'.format(self.title, para)) + if self.asyn and self.app.asyn: + threading.Thread(target = process_stack, args = + (self, ips, ips.snap, ips.imgs, para, callafter)).start() + else: process_stack(self, ips, ips.snap, ips.imgs, para, callafter) + elif has and not para['stack'] or rst == 'no': para['stack'] = False #process_one(self, ips, ips.snap, ips.img, para) - threading.Thread(target = process_one, args = - (self, ips, ips.snap, ips.img, para, callafter)).start() - self.app.record_macros('{}>{}'.format(self.title, para)) + if self.asyn and self.app.asyn: + threading.Thread(target = process_one, args = + (self, ips, ips.snap, ips.img, para, callafter)).start() + else: process_one(self, ips, ips.snap, ips.img, para, callafter) elif rst == 'cancel': pass #ips.update() diff --git a/sciapp/action/advanced/free.py b/sciapp/action/advanced/free.py index 897e2c4c..ddea9616 100644 --- a/sciapp/action/advanced/free.py +++ b/sciapp/action/advanced/free.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- -""" -Created on Sat Dec 3 03:57:53 2016 -@author: yxl -""" import threading from time import time @@ -15,24 +10,30 @@ class Free: def progress(self, i, n): self.prgs = int(i*100/n) - def run(self, para=None): - print('this is a plugin') - + def run(self, para=None): print('this is a plugin') + def runasyn(self, para, callback=None): + self.app.add_task(self) + self.app.record_macros('{}>{}'.format(self.title, para)) start = time() self.run(para) self.app.info('%s: cost %.3fs'%(self.title, time()-start)) + self.app.remove_task(self) if callback!=None:callback() def load(self):return True - + def show(self): if self.view==None:return True return self.app.show_para(self.title, self.view, self.para, None) - + def start(self, app, para=None, callback=None): self.app = app if not self.load():return if para!=None or self.show(): if para==None:para = self.para - threading.Thread(target = self.runasyn, args = (para, callback)).start() + if self.asyn and app.asyn: + threading.Thread(target = self.runasyn, + args = (para, callback)).start() + else: + self.runasyn(para, callback) \ No newline at end of file diff --git a/sciapp/action/advanced/simple.py b/sciapp/action/advanced/simple.py index 8d780343..da165c98 100644 --- a/sciapp/action/advanced/simple.py +++ b/sciapp/action/advanced/simple.py @@ -77,17 +77,17 @@ def on_help(self): def ok(self, ips, para=None, callafter=None): if para == None: para = self.para - if self.asyn : + if self.asyn and self.app.asyn: threading.Thread(target = self.runasyn, args = (ips, ips.imgs, para, callafter)).start() else: self.runasyn(ips, ips.imgs, para, callafter) - self.app.record_macros('{}>{}'.format(self.title, para)) def runasyn(self, ips, imgs, para = None, callback = None): + self.app.record_macros('{}>{}'.format(self.title, para)) self.app.add_task(self) start = time() self.run(ips, imgs, para) - self.app.info('%s: cost %.3fs'%(ips.title, time()-start)) + self.app.info('%s: cost %.3fs'%(self.title, time()-start)) ips.update() self.app.remove_task(self) if callback!=None:callback() diff --git a/sciapp/action/advanced/table.py b/sciapp/action/advanced/table.py index 706c7a63..887ff3e1 100644 --- a/sciapp/action/advanced/table.py +++ b/sciapp/action/advanced/table.py @@ -47,13 +47,13 @@ def on_help(self): def ok(self, tps, para=None, callafter=None): if para == None: para = self.para - if self.asyn: + if self.asyn and self.app.asyn: threading.Thread(target = self.runasyn, args = (tps, tps.data, tps.snap, para, callafter)).start() else: self.runasyn(tps, tps.data, tps.snap, para, callafter) - self.app.record_macros('{}>{}'.format(self.title, para)) def runasyn(self, tps, snap, data, para = None, callback = None): + self.app.record_macros('{}>{}'.format(self.title, para)) self.app.add_task(self) start = time() self.run(tps, data, snap, para) @@ -86,7 +86,7 @@ def check(self, tps): return True def start(self, app, para=None, callback=None): - self.app, self.tps = app, app.get_tab() + self.app, self.tps = app, app.get_table() #print self.title, para if not self.check(self.tps):return if not self.load(self.tps):return diff --git a/sciapp/action/plugin/__init__.py b/sciapp/action/plugin/__init__.py new file mode 100644 index 00000000..93b7946f --- /dev/null +++ b/sciapp/action/plugin/__init__.py @@ -0,0 +1 @@ +from .generalio import OpenFile, SaveImage \ No newline at end of file diff --git a/sciapp/action/plugin/filters.py b/sciapp/action/plugin/filters.py new file mode 100644 index 00000000..77c74677 --- /dev/null +++ b/sciapp/action/plugin/filters.py @@ -0,0 +1,11 @@ +from ..advanced import Filter +from scipy.ndimage import gaussian_filter + +class Gaussian(Filter): + title = 'Gaussian' + note = ['all', 'auto_msk', 'auto_snap','preview'] + para = {'sigma':2} + view = [(float, 'sigma', (0,30), 1, 'sigma', 'pix')] + + def run(self, ips, snap, img, para = None): + gaussian_filter(snap, para['sigma'], output=img) \ No newline at end of file diff --git a/sciapp/action/plugin/generalio.py b/sciapp/action/plugin/generalio.py new file mode 100644 index 00000000..489c0771 --- /dev/null +++ b/sciapp/action/plugin/generalio.py @@ -0,0 +1,20 @@ +from ..advanced import dataio +from skimage.io import imread, imsave + +for i in ('bmp', 'jpg', 'tif'): + dataio.ReaderManager.add(i, imread, 'img') + dataio.WriterManager.add(i, imsave, 'img') + +class OpenFile(dataio.Reader): + title = 'Open' + + def load(self): + self.filt = [i for i in sorted(dataio.ReaderManager.names())] + return True + +class SaveImage(dataio.ImageWriter): + title = 'Save Image' + + def load(self, ips): + self.filt = [i for i in sorted(dataio.WriterManager.names())] + return True \ No newline at end of file diff --git a/sciapp/action/plugin/meaact.py b/sciapp/action/plugin/mea_tools.py similarity index 100% rename from sciapp/action/plugin/meaact.py rename to sciapp/action/plugin/mea_tools.py diff --git a/sciapp/action/plugin/roiact.py b/sciapp/action/plugin/roi_tools.py similarity index 98% rename from sciapp/action/plugin/roiact.py rename to sciapp/action/plugin/roi_tools.py index cb8e4b66..d20002c7 100644 --- a/sciapp/action/plugin/roiact.py +++ b/sciapp/action/plugin/roi_tools.py @@ -1,4 +1,4 @@ -from .shpact import * +from .shp_tools import * from .shpbase import BaseEditor from ..tolact import ImageTool from ...object import ROI diff --git a/sciapp/action/plugin/shpact.py b/sciapp/action/plugin/shp_tools.py similarity index 100% rename from sciapp/action/plugin/shpact.py rename to sciapp/action/plugin/shp_tools.py diff --git a/sciapp/action/tabact.py b/sciapp/action/tabact.py index ab88e2eb..b1008773 100644 --- a/sciapp/action/tabact.py +++ b/sciapp/action/tabact.py @@ -21,7 +21,7 @@ def run(self, tps, snap, data, para = None): print('I am running!!!') def start(self, app, para=None, callback=None): - self.app, self.tps = app, app.get_tab() + self.app, self.tps = app, app.get_table() if 'auto_snap' in self.note: if 'auto_msk' in self.note: mode = True elif 'msk_not' in self.note: mode = False diff --git a/sciapp/app.py b/sciapp/app.py index 7eeccb18..0165b3d5 100644 --- a/sciapp/app.py +++ b/sciapp/app.py @@ -1,80 +1,18 @@ -import json, os.path as osp - -class Manager: - def __init__(self, unique=True, path=None): - self.objs, self.unique, self.path = [], unique, path - if not path is None: self.read(path) - - def add(self, name, obj, tag=None): - if self.unique: self.remove(name, tag) - self.objs.insert(0, (name, obj, tag)) - - def active(self, name=None, tag=None, obj=None): - objs = self.gets(name, tag, obj) - for i in objs: self.objs.remove(i) - for i in objs: self.objs.insert(0, i) - - def set(self, name, obj, tag=None): - self.remove(name, tag) - self.objs.insert(0, (name, obj, tag)) - - def adds(self, objs): - for i in objs: self.add(*i) - - def get(self, name=None, tag=None, obj=None): - rst = self.gets(name, tag, obj) - return None if len(rst)==0 else rst[0][1] - - def gets(self, name=None, tag=None, obj=None): - rst = [i for i in self.objs if name is None or name == i[0]] - rst = [i for i in rst if obj is None or obj is i[1]] - return [i for i in rst if tag is None or tag == i[2]] - - def has(self, name=None, tag=None, obj=None): - return len(self.gets(name, tag, obj))>0 - - def remove(self, name=None, tag=None, obj=None): - for i in self.gets(name, tag, obj): self.objs.remove(i) - - def names(self, tag=None): - return [i[0] for i in self.gets(tag=tag)] - - def name(self, name): - names = self.names() - if not name in names : return name - for i in range(1, 100) : - n = "%s-%s"%(name, i) - if not n in names: return n - - def write(self, path=None): - with open(path or self.path, 'w') as f: - f.write(json.dumps(self.objs)) - - def read(self, path): - self.path = path - if not osp.exists(path): return - with open(path) as f: - self.adds(json.loads(f.read())) - -class Source: - managers = {} - - @classmethod - def manager(cls, name): - if not name in cls.managers: - cls.managers[name] = Manager() - return cls.managers[name] +from .manager import Manager +from .object import Image, Table class App(): - def __init__(self): + def __init__(self, asyn=True): + self.asyn = asyn self.managers = {} self.img_manager = self.manager('img') - self.wimg_manager = self.manager('wimg') + #self.wimg_manager = self.manager('wimg') self.tab_manager = self.manager('tab') - self.wtab_manager = self.manager('wtab') + #self.wtab_manager = self.manager('wtab') self.mesh_manager = self.manager('mesh') - self.wmesh_manager = self.manager('wmesh') + #self.wmesh_manager = self.manager('wmesh') self.task_manager = self.manager('task') + self.plugin_manager = self.manager('plugin') def add_plugin(self, name, plg, tag=None): @@ -91,60 +29,51 @@ def manager(self, name, value=None): self.managers[name] = Manager() return self.managers[name] - def show_img(self, img): pass - def show_table(self, img): pass - def show_md(self, img, title=''): pass - def show_txt(self, img, title=''): pass - def show_plot(self): pass - def show_mesh(self): pass - - def add_img(self, img): - if not self.img_manager.has(img.name, obj=img): - img.name = self.img_manager.name(img.name) - self.img_manager.add(img.name, img) + def show_img(self, img, name): + if not isinstance(img, Image): + img = Image(img, name) + img.name = self.img_manager.name(name) + self.img_manager.add(name, img) + print(img.info) - def remove_img(self, img): - print('remove', img.name) - self.img_manager.remove(obj=img) - print(self.img_manager.objs, 'close') + def close_img(self, name): + self.img_manager.remove(name) + print('close image:', name) - def add_img_win(self, win): - self.wimg_manager.add(win.name, win) + def active_img(self, name): + self.img_manager.active(name) + print('active image:', name) - def remove_img_win(self, win): - self.wimg_manager.remove(obj=win) - def get_img(self, name=None): return self.img_manager.get(name) - - def get_img_name(self): - return self.img_manager.names() - - def get_img_win(self, name=None): - return self.wimg_manager.get(name) - def add_tab(self, tab): - if not self.tab_manager.has(tab.name, obj=tab): - tab.name = self.tab_manager.name(tab.name) - self.tab_manager.add(tab.name, tab) + def img_names(self): + return self.img_manager.names() - def remove_tab(self, tab): - self.tab_manager.remove(obj=tab) + def show_table(self, tab, name): + if not isinstance(tab, Table): + tab = Table(tab, name) + tab.name = self.tab_manager.name(name) + self.tab_manager.add(name, tab) + print(tab.info) - def add_tab_win(self, win): - self.wtab_manager.add(win.name, win) + def close_table(self, name): + self.tab_manager.remove(name) + print('close table:', name) - def remove_tab_win(self, win): - self.wtab_manager.remove(obj=win) + def active_table(self, name): + self.tab_manager.active(name) + print('active image:', name) - def get_tab(self, name=None): + def get_table(self, name=None): return self.tab_manager.get(name) - - def get_tab_name(self): + + def table_names(self): return self.tab_manager.names() - - def get_tab_win(self, name=None): - return self.wtab_manager.get(name) + + def show_plot(self): pass + + def show_mesh(self): pass def add_mesh(self, mesh): if not self.mesh_manager.has(mesh.name, obj=mesh): @@ -203,7 +132,7 @@ def show_para(self, title, view, para, on_handle=None, on_ok=None, if i[0]==str: para[i[1]] = input(i[2]+': ? '+i[3]+' ') if i[0]==int: para[i[1]] = int(input(i[4]+': ? '+i[5]+' ')) if i[0]==float: para[i[1]] = float(input(i[4]+': ? '+i[5]+' ')) - if i[0]=='slide': para[i[1]] = float(input(i[4]+': ? '+i[5]+' ')) + if i[0]=='slide': para[i[1]] = float(input(i[4]+': ? ')) if i[0]==bool: para[i[1]] = bool(input(i[2]+': ')) if i[0]==list: para[i[1]] = i[3](input('%s %s: %s'%(i[4],i[5],i[2])+' ')) if i[0]=='chos':para[i[1]] = input('%s:%s '%(i[3],i[2])).split(',') @@ -214,27 +143,29 @@ def run_macros(self, cmd, callafter=None): cmds = [i for i in cmd] def one(cmds, after): cmd = cmds.pop(0) - title, para = cmd.split('>') + if not isinstance(cmd, str): title, para = cmd + else: title, para = eval(cmd.replace('>', ',')) plg = self.manager('plugin').get(name=title)() after = lambda cmds=cmds: one(cmds, one) if len(cmds)==0: after = callafter - plg.start(self, eval(para), after) + plg.start(self, para, after) one(cmds, None) -if __name__ == '__main__': - app = App() - app.alert('Hello, SciApp!') - - para = {'name':'yxdragon', 'age':10, 'h':1.72, 'w':70, 'sport':True, 'sys':'Mac', 'lan':['C/C++', 'Python'], 'c':(255,0,0)} - - view = [('lab', 'lab', 'This is a questionnaire'), - (str, 'name', 'name', 'please'), - (int, 'age', (0,150), 0, 'age', 'years old'), - (float, 'h', (0.3, 2.5), 2, 'height', 'm'), - ('slide', 'w', (1, 150), 0, 'weight','kg'), - (bool, 'sport', 'do you like sport'), - (list, 'sys', ['Windows','Mac','Linux'], str, 'favourite', 'system'), - ('chos', 'lan', ['C/C++','Java','Python'], 'lanuage you like(multi)'), - ('color', 'c', 'which', 'you like')] - - app.show_para('parameter', view, para) + def show(self, tag, cont, title): + tag = tag or 'img' + if tag=='img': + self.show_img([cont], title) + elif tag=='imgs': + self.show_img(cont, title) + elif tag=='tab': + self.show_table(cont, title) + elif tag=='mc': + self.run_macros(cont) + elif tag=='md': + self.show_md(cont, title) + elif tag=='wf': + self.show_workflow(cont, title) + else: self.alert('no view for %s!'%tag) + + def record_macros(self, cmd): + print('>>>', cmd) \ No newline at end of file diff --git a/sciapp/demo/app_test.py b/sciapp/demo/app_test.py new file mode 100644 index 00000000..27cffb83 --- /dev/null +++ b/sciapp/demo/app_test.py @@ -0,0 +1,27 @@ +import sys +sys.path.append('../../') + +cmds = [ + ("Open", {'path': 'C:/Users/54631/Desktop/1.jpg'}), + ("Gaussian", {'sigma': 5.0}), + ("Save Image", {'path': 'C:/Users/54631/Desktop/1_blur.bmp'})] + +if __name__ == '__main__': + from sciapp import App + from sciapp.action.plugin import OpenFile, SaveImage + from sciapp.action.plugin.filters import Gaussian + + app = App(asyn=False) + + for i in [OpenFile, SaveImage, Gaussian]: + app.add_plugin(i.title, i) + + ''' + app.alert('Hello, SciApp!') + OpenFile().start(app, {'path': 'C:/Users/54631/Desktop/1.jpg'}) + Gaussian().start(app, {'sigma': 5.0}) + SaveImage().start(app,{'path': 'C:/Users/54631/Desktop/1_blur.bmp'}) + ''' + app.run_macros(cmds) + # C:/Users/54631/Desktop/1.jpg + diff --git a/sciapp/demo/imagepy_console.py b/sciapp/demo/imagepy_console.py new file mode 100644 index 00000000..90994fac --- /dev/null +++ b/sciapp/demo/imagepy_console.py @@ -0,0 +1,12 @@ +from imagepy.app import Console + +con = Console() +con.load_plugins() + +cmds = [('coins',None), + ('Up And Down Watershed',{'thr1': 29, 'thr2': 178, 'type': 'up area'}), + ('Fill Holes',None), + ('Geometry Filter',{'con': '4-connect', 'inv': False, 'area': 10.0, 'l': 0.0, 'holes': 0, 'solid': 0.0, 'e': 0.0, 'front': 255, 'back': 0}), + ('Geometry Analysis',{'con': '8-connect', 'center': True, 'area': True, 'l': True, 'extent': False, 'cov': True, 'slice': False, 'ed': False, 'holes': False, 'ca': False, 'fa': False, 'solid': False}), + ('PNG Save',{'path': 'C:/Users/54631/Desktop/conis.png'}), + ('CSV Save',{'path': 'C:/Users/54631/Desktop/coins.csv'})] diff --git a/sciapp/manager.py b/sciapp/manager.py new file mode 100644 index 00000000..650c96f8 --- /dev/null +++ b/sciapp/manager.py @@ -0,0 +1,57 @@ +import json, os.path as osp + +class Manager: + def __init__(self, unique=True, path=None): + self.objs, self.unique, self.path = [], unique, path + if not path is None: self.read(path) + + def add(self, name, obj, tag=None): + if self.unique: self.remove(name, tag) + self.objs.insert(0, (name, obj, tag)) + + def active(self, name=None, tag=None, obj=None): + objs = self.gets(name, tag, obj) + for i in objs: self.objs.remove(i) + for i in objs: self.objs.insert(0, i) + + def set(self, name, obj, tag=None): + self.remove(name, tag) + self.objs.insert(0, (name, obj, tag)) + + def adds(self, objs): + for i in objs: self.add(*i) + + def get(self, name=None, tag=None, obj=None): + rst = self.gets(name, tag, obj) + return None if len(rst)==0 else rst[0][1] + + def gets(self, name=None, tag=None, obj=None): + rst = [i for i in self.objs if name is None or name == i[0]] + rst = [i for i in rst if obj is None or obj is i[1]] + return [i for i in rst if tag is None or tag == i[2]] + + def has(self, name=None, tag=None, obj=None): + return len(self.gets(name, tag, obj))>0 + + def remove(self, name=None, tag=None, obj=None): + for i in self.gets(name, tag, obj): self.objs.remove(i) + + def names(self, tag=None): + return [i[0] for i in self.gets(tag=tag)] + + def name(self, name): + names = self.names() + if not name in names : return name + for i in range(1, 100) : + n = "%s-%s"%(name, i) + if not n in names: return n + + def write(self, path=None): + with open(path or self.path, 'w') as f: + f.write(json.dumps(self.objs)) + + def read(self, path): + self.path = path + if not osp.exists(path): return + with open(path) as f: + self.adds(json.loads(f.read())) \ No newline at end of file diff --git a/sciapp/object/table.py b/sciapp/object/table.py index 725ee186..980e7a0d 100644 --- a/sciapp/object/table.py +++ b/sciapp/object/table.py @@ -1,8 +1,8 @@ import numpy as np class Table(): - def __init__(self, df=None): - self.name = 'Table' + def __init__(self, df=None, name='Table'): + self.name = name self.df = df self.rg = None self.props = None diff --git a/sciwx/app/miniapp.py b/sciwx/app/miniapp.py index 57902731..f946cc68 100644 --- a/sciwx/app/miniapp.py +++ b/sciwx/app/miniapp.py @@ -235,7 +235,7 @@ def show_widget(self, panel, title='Widgets'): self.auimgr.Update() def close_img(self, name=None): - names = self.get_img_name() if name is None else [name] + names = self.img_names() if name is None else [name] for name in names: idx = self.canvasnb.GetPageIndex(self.get_img_win(name)) self.remove_img(self.get_img_win(name).image) @@ -255,8 +255,7 @@ def run_macros(self, cmd, callafter=None): def one(cmds, after): cmd = cmds.pop(0) title, para = cmd.split('>') - print(title, para) - plg = Source.manager('plugin').get(name=title)() + plg = self.app.plugin_manager.get(name=title)() after = lambda cmds=cmds: one(cmds, one) if len(cmds)==0: after = callafter wx.CallAfter(plg.start, self, eval(para), after) diff --git a/sciwx/plugins/channels.py b/sciwx/plugins/channels.py index b432b110..3149265b 100644 --- a/sciwx/plugins/channels.py +++ b/sciwx/plugins/channels.py @@ -157,7 +157,7 @@ def __init__( self, parent , app): self.active = 0 def on_back(self, event): - self.com_back.SetItems(['None']+self.app.get_img_name()) + self.com_back.SetItems(['None']+self.app.img_names()) cur = self.app.get_img_win() if not cur is None: cur = cur.back if not cur is None: cur = cur.title diff --git a/sciwx/widgets/advanced.py b/sciwx/widgets/advanced.py index 451e07a2..0fdbcc90 100644 --- a/sciwx/widgets/advanced.py +++ b/sciwx/widgets/advanced.py @@ -4,7 +4,7 @@ class ImageList(Choice): def __init__(self, parent, title, unit, app=None): - Choice.__init__(self, parent, app.get_img_name(), str, title, unit) + Choice.__init__(self, parent, app.img_names(), str, title, unit) class TableList(Choice): def __init__(self, parent, title, unit, app=None): @@ -12,12 +12,12 @@ def __init__(self, parent, title, unit, app=None): class TableField(Choice): def __init__(self, parent, title, unit, app=None): - Choice.__init__(self, parent, ['None'] + list(app.get_tab().data.columns), lambda x:x, title, unit) + Choice.__init__(self, parent, ['None'] + list(app.get_table().data.columns), lambda x:x, title, unit) class TableFields(Choices): def __init__(self, parent, title, app=None): - self.tps = app.get_tab() - Choices.__init__(self, parent, app.get_tab().data.columns, title) + self.tps = app.get_table() + Choices.__init__(self, parent, app.get_table().data.columns, title) def SetValue(self, value): Choices.SetValue(self, self.tps.colmsk) From d7738f5ccc66d4658f03647670e8c81d926c3ac8 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 30 Jun 2020 08:25:59 +0800 Subject: [PATCH 281/343] add manager again --- imagepy/app/__init__.py | 4 +- imagepy/app/imagej.py | 103 +++++++++--------- imagepy/app/imagepy.py | 66 +++++------ imagepy/app/loader.py | 24 ++-- imagepy/app/manager.py | 44 ++++++++ imagepy/app/source.py | 44 -------- imagepy/app/startup.py | 8 +- imagepy/data/config.json | 2 +- .../Region Analysis/regionprops_plgs.py | 4 +- imagepy/menus/Edit/edit_plg.py | 1 - imagepy/menus/File/BMP/bmp_plgs.py | 1 - imagepy/menus/File/DAT/dat_plgs.py | 1 - imagepy/menus/File/DICOM/dicom_plgs.py | 3 - imagepy/menus/File/Export/sequence_plg.py | 1 - imagepy/menus/File/GIF/gif_plgs.py | 1 - imagepy/menus/File/Import/sequence_plg.py | 1 - imagepy/menus/File/JPG/jpg_plgs.py | 1 - imagepy/menus/File/MAT/mat_plgs.py | 1 - imagepy/menus/File/MarkDown/md_plg.py | 1 - imagepy/menus/File/Numpy/ndarray_plgs.py | 1 - .../{recent_plgs.py => recent_plgs_.py} | 2 +- imagepy/menus/File/PNG/png_plgs.py | 1 - imagepy/menus/File/TIF/tif_plgs.py | 1 - imagepy/menus/File/open_plg.py | 2 +- imagepy/menus/File/save_plg.py | 1 - imagepy/menus/Help/Language/language_plgs.py | 7 +- .../Lookup table/Others/lookuptables_plg.py | 4 +- .../Image/Lookup table/lookuptables_plg.py | 4 +- imagepy/menus/Image/Mark/mark_plgs.py | 4 +- .../menus/Kit3D/Viewer 3D/tablepoints_plg.py | 4 +- .../menus/Plugins/Contribute/pmanager_wgt.py | 3 +- imagepy/menus/Plugins/Macros/recorder_plg.py | 2 +- imagepy/menus/Plugins/Manager/console_wgt.py | 11 +- imagepy/menus/Plugins/Manager/plglist_wgt.py | 1 - imagepy/menus/Plugins/Manager/plgtree_wgt.py | 9 +- imagepy/menus/Plugins/Manager/shotcut_wgt.py | 8 +- imagepy/menus/Plugins/Manager/toltree_wgt.py | 9 +- imagepy/menus/Plugins/temporal_plg.py | 6 +- imagepy/menus/Process/Classify/io_plgs.py | 4 +- imagepy/menus/Process/Classify/predict_plg.py | 1 - imagepy/menus/Selection/select_plg.py | 4 +- imagepy/menus/Table/Chart/plot_plgs.py | 4 +- imagepy/menus/Table/Table IO/tableio_plgs.py | 1 - .../menus/Window/Windows Style/style_plgs.py | 6 +- imagepy/menus/Window/develop_wgts.py | 11 +- imagepy/tools/Draw/aibrush_tol.py | 1 - imagepy/tools/Measure/setting_tol.py | 5 +- sciapp/action/advanced/filter.py | 7 +- sciapp/app.py | 15 ++- sciapp/doc/readme.md | 42 +++++++ sciapp/manager.py | 3 +- sciwx/__init__.py | 11 +- sciwx/plugins/histogram.py | 3 +- sciwx/plugins/viewport.py | 2 - sciwx/widgets/colormap.py | 4 +- sciwx/widgets/paradialog.py | 2 +- 56 files changed, 265 insertions(+), 252 deletions(-) create mode 100644 imagepy/app/manager.py delete mode 100644 imagepy/app/source.py rename imagepy/menus/File/Open Recent/{recent_plgs.py => recent_plgs_.py} (61%) create mode 100644 sciapp/doc/readme.md diff --git a/imagepy/app/__init__.py b/imagepy/app/__init__.py index c6d5ab9d..b691dd4e 100644 --- a/imagepy/app/__init__.py +++ b/imagepy/app/__init__.py @@ -1,4 +1,6 @@ from .imagepy import ImagePy from .imagej import ImageJ from .console import Console -from .import startup \ No newline at end of file +from .import startup + +from .manager import ConfigManager, ShortcutManager, ColorManager, DictManager, DocumentManager \ No newline at end of file diff --git a/imagepy/app/imagej.py b/imagepy/app/imagej.py index 6ea66d5d..f6a0fe1b 100644 --- a/imagepy/app/imagej.py +++ b/imagepy/app/imagej.py @@ -11,9 +11,10 @@ from sciwx.plot import PlotFrame from skimage.data import camera from sciapp import App, Source -from sciapp.object import Image +from sciapp.object import Image, Table from imagepy import root_dir from .startup import load_plugins, load_tools, load_widgets +from .manager import ConfigManager, DictManager, ShortcutManager, DocumentManager #from .source import * class ImageJ(wx.Frame, App): @@ -70,9 +71,6 @@ def OnDropFiles(self, x, y, path): self.auimgr.AddPane( stapanel, aui.AuiPaneInfo() .Bottom() .CaptionVisible( False ).PinButton( True ) .PaneBorder( False ).Dock().Resizable().FloatingSize( wx.DefaultSize ).DockFixed( True ) . MinSize(wx.Size(-1, 20)). MaxSize(wx.Size(-1, 20)).Layer( 10 ) ) - - def add_plugin(self, name, plg): - self.manager('plugin').add(name, plg) def flatten(self, plgs, lst=None): if lst is None: lst = [] @@ -84,19 +82,24 @@ def flatten(self, plgs, lst=None): return lst def _load_all(self): - lang = Source.manager('config').get('language') - dic = Source.manager('dictionary').get('common', tag=lang) or {} + lang = ConfigManager.get('language') + dic = DictManager.get('common', tag=lang) or {} self.auimgr.GetPane(self.widgets).Caption('Widgets') for i in self.auimgr.GetAllPanes(): i.Caption(dic[i.caption] if i.caption in dic else i.caption) self.auimgr.Update() plgs, errplg = load_plugins() - for name, plg in self.flatten(plgs): self.add_plugin(name, plg) + self.plugin_manager.remove() + for name, plg in self.flatten(plgs): + self.add_plugin(name, plg, 'plugin') self.load_menu(plgs) - dtool = Source.manager('tools').get('default') tols, errtol = load_tools() - self.load_tool(tols, dtool or 'Transform') + for name, plg in self.flatten(tols): + self.add_plugin(name, plg, 'tool') + self.load_tool(tols, 'Transform') wgts, errwgt = load_widgets() + for name, plg in self.flatten(wgts): + self.add_plugin(name, plg, 'widget') self.load_widget(wgts) err = errplg + errtol + errwgt if len(err)>0: @@ -108,17 +111,17 @@ def load_all(self): wx.CallAfter(self._load_all) def load_menu(self, data): self.menubar.clear() - lang = Source.manager('config').get('language') - ls = Source.manager('dictionary').gets(tag=lang) - short = Source.manager('shortcut').gets() + lang = ConfigManager.get('language') + ls = DictManager.gets(tag=lang) + short = ShortcutManager.gets() acc = self.menubar.load(data, dict([i[:2] for i in short])) self.translate(dict([(i,j[i]) for i,j,_ in ls]))(self.menubar) self.SetAcceleratorTable(acc) def load_tool(self, data, default=None): self.toolbar.clear() - lang = Source.manager('config').get('language') - ls = Source.manager('dictionary').gets(tag=lang) + lang = ConfigManager.get('language') + ls = DictManager.gets(tag=lang) dic = dict([(i,j[i]) for i,j,_ in ls]) for i, (name, tols) in enumerate(data[1]): name = dic[name] if name in dic else name @@ -129,11 +132,11 @@ def load_tool(self, data, default=None): def load_widget(self, data): self.widgets.clear() - lang = Source.manager('config').get('language') + lang = ConfigManager.get('language') self.widgets.load(data) for cbk in self.widgets.GetChildren(): for i in range(cbk.GetPageCount()): - dic = Source.manager('dictionary').get(cbk.GetPageText(i), tag=lang) or {} + dic = DictManager.get(cbk.GetPageText(i), tag=lang) or {} translate = self.translate(dic) title = cbk.GetPageText(i) cbk.SetPageText(i, dic[title] if title in dic else title) @@ -147,9 +150,9 @@ def init_tool(self): sizer = wx.BoxSizer(wx.VERTICAL) self.toolbar = ToolBar(self, False) def on_help(evt, tol): - lang = Source.manager('config').get('language') - doc = Source.manager('document').get(tol.title, tag=lang) - doc = doc or Source.manager('document').get(tol.title, tag='English') + lang = ConfigManager.get('language') + doc = DocumentManager.get(tol.title, tag=lang) + doc = doc or DocumentManager.get(tol.title, tag='English') self.show_md(doc or 'No Document!', tol.title) self.toolbar.on_help = on_help self.toolbar.Fit() @@ -200,23 +203,20 @@ def on_pan_close(self, event): if hasattr(event.GetPane().window, 'close'): event.GetPane().window.close() - def on_new_img(self, event): - self.add_img(event.GetEventObject().canvas.image) - self.add_img_win(event.GetEventObject().canvas) + def on_active_img(self, event): + self.active_img(event.GetEventObject().canvas.image.name) + # self.add_img_win(event.GetEventObject().canvas) def on_close_img(self, event): - event.GetEventObject().Bind(wx.EVT_ACTIVATE, None) - self.remove_img_win(event.GetEventObject().canvas) - self.remove_img(event.GetEventObject().canvas.image) + #event.GetEventObject().Bind(wx.EVT_ACTIVATE, None) + App.close_img(self, event.GetEventObject().canvas.image.title) event.Skip() - def on_new_tab(self, event): - self.add_tab(event.GetEventObject().grid.table) - self.add_tab_win(event.GetEventObject().grid) + def on_active_table(self, event): + self.active_table(event.GetEventObject().grid.table.title) - def on_close_tab(self, event): - self.remove_tab_win(event.GetEventObject().grid) - self.remove_tab(event.GetEventObject().grid.table) + def on_close_table(self, event): + App.close_table(self, event.GetEventObject().grid.table.title) event.Skip() def on_new_mesh(self, event): @@ -229,8 +229,8 @@ def on_close_mesh(self, event): event.Skip() def info(self, value): - lang = Source.manager('config').get('language') - dics = Source.manager('dictionary').gets(tag=lang) + lang = ConfigManager.get('language') + dics = DictManager.gets(tag=lang) dic = dict(j for i in dics for j in i[1].items()) value = dic[value] if value in dic else value wx.CallAfter(self.txt_info.SetLabel, value) @@ -251,18 +251,18 @@ def on_close(self, event): self.auimgr.UnInit() del self.auimgr self.Destroy() - Source.manager('config').write() + ConfigManager.write() sys.exit() def _show_img(self, img, title=None): cframe = CanvasFrame(self, True) canvas = cframe.canvas - if not title is None: - canvas.set_imgs(img) - canvas.image.name = title - else: canvas.set_img(img) - cframe.Bind(wx.EVT_ACTIVATE, self.on_new_img) + if not isinstance(img, Image): + img = Image(img, title) + App.show_img(self, img, img.title) + cframe.Bind(wx.EVT_ACTIVATE, self.on_active_img) cframe.Bind(wx.EVT_CLOSE, self.on_close_img) + canvas.set_img(img) cframe.SetIcon(self.GetIcon()) cframe.Show() @@ -272,11 +272,12 @@ def show_img(self, img, title=None): def _show_table(self, tab, title): cframe = GridFrame(self) grid = cframe.grid + if not isinstance(tab, Table): + tab = Table(tab, title) + App.show_table(self, tab, tab.title) grid.set_data(tab) - if not title is None: - grid.table.name = title - cframe.Bind(wx.EVT_ACTIVATE, self.on_new_tab) - cframe.Bind(wx.EVT_CLOSE, self.on_close_tab) + cframe.Bind(wx.EVT_ACTIVATE, self.on_active_table) + cframe.Bind(wx.EVT_CLOSE, self.on_close_table) cframe.SetIcon(self.GetIcon()) cframe.Show() @@ -351,8 +352,8 @@ def show_widget(self, panel, title='Widgets'): self.manager('widget').add(panel.title, obj) self.auimgr.AddPane(obj, aui.AuiPaneInfo().Caption(title).Left().Layer( 15 ).PinButton( True ) .Float().Resizable().FloatingSize( wx.DefaultSize ).Dockable(True)) #.DestroyOnClose()) - lang = Source.manager('config').get('language') - dic = Source.manager('dictionary').get(obj.title, tag=lang) or {} + lang = ConfigManager.get('language') + dic = DictManager.get(obj.title, tag=lang) or {} info = self.auimgr.GetPane(obj) info.Show(True).Caption(dic[obj.title] if obj.title in dic else obj.title) self.translate(dic)(obj) @@ -426,8 +427,8 @@ def show(self, tag, cont, title): else: self.alert('no view for %s!'%tag) def _alert(self, info, title='ImagePy'): - lang = Source.manager('config').get('language') - dics = Source.manager('dictionary').gets(tag=lang) + lang = ConfigManager.get('language') + dics = DictManager.gets(tag=lang) dialog = wx.MessageDialog(self, info, title, wx.OK) self.translate([i[1] for i in dics])(dialog) dialog.ShowModal() == wx.ID_OK @@ -455,10 +456,10 @@ def getpath(self, title, filt, io, name=''): def show_para(self, title, view, para, on_handle=None, on_ok=None, on_cancel=None, on_help=None, preview=False, modal=True): - lang = Source.manager('config').get('language') - dic = Source.manager('dictionary').get(name=title, tag=lang) - doc = Source.manager('document').get(title, tag=lang) - doc = doc or Source.manager('document').get(title, tag='English') + lang = ConfigManager.get('language') + dic = DictManager.get(name=title, tag=lang) + doc = DocumentManager.get(title, tag=lang) + doc = doc or DocumentManager.get(title, tag='English') on_help = lambda x=doc:self.show_md(x or 'No Document!', title) dialog = ParaDialog(self, title) dialog.init_view(view, para, preview, modal=modal, app=self) diff --git a/imagepy/app/imagepy.py b/imagepy/app/imagepy.py index d1b0ae4d..e91d7f80 100644 --- a/imagepy/app/imagepy.py +++ b/imagepy/app/imagepy.py @@ -13,6 +13,7 @@ from sciapp.object import Image, Table from imagepy import root_dir from .startup import load_plugins, load_tools, load_widgets, load_document, load_dictionary +from .manager import ConfigManager, DictManager, ShortcutManager, DocumentManager class ImagePy(wx.Frame, App): def __init__( self, parent ): @@ -81,8 +82,8 @@ def flatten(self, plgs, lst=None): return lst def _load_all(self): - lang = Source.manager('config').get('language') - dic = Source.manager('dictionary').get('common', tag=lang) or {} + lang = ConfigManager.get('language') + dic = DictManager.get('common', tag=lang) or {} self.auimgr.GetPane(self.widgets).Caption('Widgets') self.auimgr.GetPane(self.tablenbwrap).Caption('Table') for i in self.auimgr.GetAllPanes(): @@ -93,11 +94,10 @@ def _load_all(self): for name, plg in self.flatten(plgs): self.add_plugin(name, plg, 'plugin') self.load_menu(plgs) - dtool = Source.manager('tools').get('default') tols, errtol = load_tools() for name, plg in self.flatten(tols): self.add_plugin(name, plg, 'tool') - self.load_tool(tols, dtool or 'Transform') + self.load_tool(tols, 'Transform') wgts, errwgt = load_widgets() for name, plg in self.flatten(wgts): self.add_plugin(name, plg, 'widget') @@ -112,9 +112,9 @@ def load_all(self): wx.CallAfter(self._load_all) def load_menu(self, data): self.menubar.clear() - lang = Source.manager('config').get('language') - ls = Source.manager('dictionary').gets(tag=lang) - short = Source.manager('shortcut').gets() + lang = ConfigManager.get('language') + ls = DictManager.gets(tag=lang) + short = ShortcutManager.gets() acc = self.menubar.load(data, dict([i[:2] for i in short])) self.translate(dict([(i,j[i]) for i,j,_ in ls]))(self.menubar) @@ -122,8 +122,8 @@ def load_menu(self, data): def load_tool(self, data, default=None): self.toolbar.clear() - lang = Source.manager('config').get('language') - ls = Source.manager('dictionary').gets(tag=lang) + lang = ConfigManager.get('language') + ls = DictManager.gets(tag=lang) dic = dict([(i,j[i]) for i,j,_ in ls]) for i, (name, tols) in enumerate(data[1]): name = dic[name] if name in dic else name @@ -134,11 +134,11 @@ def load_tool(self, data, default=None): def load_widget(self, data): self.widgets.clear() - lang = Source.manager('config').get('language') + lang = ConfigManager.get('language') self.widgets.load(data) for cbk in self.widgets.GetChildren(): for i in range(cbk.GetPageCount()): - dic = Source.manager('dictionary').get(cbk.GetPageText(i), tag=lang) or {} + dic = DictManager.get(cbk.GetPageText(i), tag=lang) or {} translate = self.translate(dic) title = cbk.GetPageText(i) cbk.SetPageText(i, dic[title] if title in dic else title) @@ -152,9 +152,9 @@ def init_tool(self): sizer = wx.BoxSizer(wx.VERTICAL) self.toolbar = ToolBar(self, True) def on_help(evt, tol): - lang = Source.manager('config').get('language') - doc = Source.manager('document').get(tol.title, tag=lang) - doc = doc or Source.manager('document').get(tol.title, tag='English') + lang = ConfigManager.get('language') + doc = DocumentManager.get(tol.title, tag=lang) + doc = doc or DocumentManager.get(tol.title, tag='English') self.show_md(doc or 'No Document!', tol.title) self.toolbar.on_help = on_help self.toolbar.Fit() @@ -234,8 +234,8 @@ def remove_task(self, task): self.pro_bar.SetValue(tasks) def init_widgets(self): - lang = Source.manager('config').get('language') - dic = Source.manager('dictionary').get('common', tag=lang) + lang = ConfigManager.get('language') + dic = DictManager.get('common', tag=lang) self.widgets = ChoiceBook(self) self.auimgr.AddPane( self.widgets, aui.AuiPaneInfo() .Right().Caption('Widgets') .PinButton( True ) .Dock().Resizable().FloatingSize( wx.DefaultSize ).MinSize( wx.Size( 266,-1 ) ).Layer( 10 ) ) @@ -295,8 +295,8 @@ def on_close_mesh(self, event): self.remove_mesh_win(canvas3d) def info(self, value): - lang = Source.manager('config').get('language') - dics = Source.manager('dictionary').gets(tag=lang) + lang = ConfigManager.get('language') + dics = DictManager.gets(tag=lang) dic = dict(j for i in dics for j in i[1].items()) value = dic[value] if value in dic else value wx.CallAfter(self.txt_info.SetLabel, value) @@ -317,7 +317,7 @@ def on_close(self, event): self.auimgr.UnInit() del self.auimgr self.Destroy() - Source.manager('config').write() + ConfigManager.write() sys.exit() def _show_img(self, img, title=None): @@ -413,8 +413,8 @@ def show_widget(self, panel, title='Widgets'): self.manager('widget').add(panel.title, obj) self.auimgr.AddPane(obj, aui.AuiPaneInfo().Caption(title).Left().Layer( 15 ).PinButton( True ) .Float().Resizable().FloatingSize( wx.DefaultSize ).Dockable(True)) #.DestroyOnClose()) - lang = Source.manager('config').get('language') - dic = Source.manager('dictionary').get(obj.title, tag=lang) or {} + lang = ConfigManager.get('language') + dic = DictManager.get(obj.title, tag=lang) or {} info = self.auimgr.GetPane(obj) info.Show(True).Caption(dic[obj.title] if obj.title in dic else obj.title) self.translate(dic)(obj) @@ -443,6 +443,12 @@ def close_img(self, name): if self.canvasnb.GetPageText(i)==name: return self.canvasnb.DeletePage(i) + def get_img_win(self, name=None): + name = name or self.get_img().name + for i in range(self.canvasnb.GetPageCount()): + if self.canvasnb.GetPageText(i)==name: + return self.canvasnb.GetPage(i) + def close_table(self, name=None): App.close_tab(self, name) for i in range(self.tablenb.GetPageCount()): @@ -482,8 +488,8 @@ def show(self, tag, cont, title): else: self.alert('no view for %s!'%tag) def _alert(self, info, title='ImagePy'): - lang = Source.manager('config').get('language') - dics = Source.manager('dictionary').gets(tag=lang) + lang = ConfigManager.get('language') + dics = DictManager.gets(tag=lang) dialog = wx.MessageDialog(self, info, title, wx.OK) self.translate([i[1] for i in dics])(dialog) dialog.ShowModal() == wx.ID_OK @@ -508,18 +514,12 @@ def getpath(self, title, filt, io, name=''): dialog.Destroy() return path - - lang = Source.manager('config').get('language') - doc = Source.manager('document').get(self.title, tag=lang) - doc = doc or Source.manager('document').get(tol.title, tag='English') - self.app.show_md(doc or 'No Document!', self.title) - def show_para(self, title, view, para, on_handle=None, on_ok=None, on_cancel=None, on_help=None, preview=False, modal=True): - lang = Source.manager('config').get('language') - dic = Source.manager('dictionary').get(name=title, tag=lang) - doc = Source.manager('document').get(title, tag=lang) - doc = doc or Source.manager('document').get(title, tag='English') + lang = ConfigManager.get('language') + dic = DictManager.get(name=title, tag=lang) + doc = DocumentManager.get(title, tag=lang) + doc = doc or DocumentManager.get(title, tag='English') on_help = lambda x=doc:self.show_md(x or 'No Document!', title) dialog = ParaDialog(self, title) dialog.init_view(view, para, preview, modal=modal, app=self) diff --git a/imagepy/app/loader.py b/imagepy/app/loader.py index a617f8c1..53794896 100644 --- a/imagepy/app/loader.py +++ b/imagepy/app/loader.py @@ -7,8 +7,8 @@ import os, sys, os.path as osp from glob import glob from sciapp.action import Macros, Widget#, Report -from sciapp import Source from .. import root_dir +from .manager import DocumentManager, DictManager from codecs import open def getpath(root, path): @@ -26,19 +26,15 @@ def extend_plugins(path, lst, err): elif i[-3:] == 'rpt': pt = os.path.join(root_dir,path) rst.append(Report(i[:-4], pt+'/'+i)) - # Source.manager('plugin').add(obj=rst[-1], name=rst[-1].title) elif i[-3:] in {'.md', '.mc', '.wf'}: p = os.path.join(os.path.join(root_dir, path), i).replace('\\','/') rst.append(Macros(i[:-3], ['Open>{"path":"%s"}'%p])) - # Source.manager('plugin').add(rst[-1].title, rst[-1]) elif i[-6:] in ['wgt.py', 'gts.py']: try: rpath = path.replace('/', '.').replace('\\','.') plg = __import__('imagepy.'+ rpath+'.'+i[:-3],'','',['']) if hasattr(plg, 'wgts'): rst.extend([j if j=='-' else Widget(j) for j in plg.wgts]) - for p in plg.wgts: - if not isinstance(p, str):Source.manager('widget').add(p.title, p) else: rst.append(Widget(plg.Plugin)) except Exception as e: @@ -51,10 +47,8 @@ def extend_plugins(path, lst, err): rst.extend([j for j in plg.plgs]) for p in plg.plgs: if not isinstance(p, str): pass - # Source.manager('plugin').add(p.title, p) else: rst.append(plg.Plugin) - # Source.manager('plugin').add(plg.Plugin.title, plg.Plugin) except Exception as e: err.append((path, i, sys.exc_info()[1])) return rst @@ -112,7 +106,6 @@ def extend_tools(path, lst, err): os.path.join(root_dir, path)+'/'+i.split('_')[0]+'.gif')) except Exception as e: err.append((path, i, sys.exc_info()[1])) - # for i in rst:Source.manager('tool').add(obj=i[0], name=i[0].title) return rst def sort_tools(catlog, lst): @@ -161,7 +154,6 @@ def extend_widgets(path, lst, err): rst.append(plg.Plugin) except Exception as e: err.append((path, i, sys.exc_info()[1])) - #for i in rst:Source.manager('widget').add(obj=i, name=i.title) return rst def sort_widgets(catlog, lst): @@ -206,10 +198,8 @@ def build_document(path): for filename in filenames: if filename[-3:] != '.md': continue docs.append(os.path.join(dirpath, filename)) - f = open(docs[-1], encoding='utf-8') - cont = f.read() - f.close() - Source.manager('document').add(filename[:-3], cont, lang) + with open(docs[-1], encoding='utf-8') as f: + DocumentManager.add(filename[:-3], f.read(), lang) return docs def build_dictionary(path): @@ -231,12 +221,12 @@ def build_dictionary(path): dic[-1] = (dic[-1][0][0], dict(dic[-1])) dic = dict(dic) for i in dic: - obj = Source.manager('dictionary').get(i, tag=lang) + obj = DictManager.get(i, tag=lang) if not obj is None: obj.update(dic[i]) - else: Source.manager('dictionary').add(i, dic[i], lang) - common = Source.manager('dictionary').get('common', tag=lang) + else: DictManager.add(i, dic[i], lang) + common = DictManager.get('common', tag=lang) if common is None: return - objs = Source.manager('dictionary').gets(tag=lang) + objs = DictManager.gets(tag=lang) for i in objs: i[1].update(common) if __name__ == "__main__": diff --git a/imagepy/app/manager.py b/imagepy/app/manager.py new file mode 100644 index 00000000..48b77946 --- /dev/null +++ b/imagepy/app/manager.py @@ -0,0 +1,44 @@ +from sciapp import Manager +from imagepy import root_dir +import os.path as osp +from glob import glob +import numpy as np + +ConfigManager = Manager().read(osp.join(root_dir, 'data/config.json')) +ShortcutManager = Manager().read(osp.join(root_dir, 'data/shortcut.json')) +ColorManager = Manager() +DictManager = Manager() +DocumentManager = Manager() + +if ConfigManager.get('language') is None: + ConfigManager.add('language', 'english') + +from sciapp.object import Shape +mark_style = ConfigManager.get('mark_style') +if not mark_style is None: + for i in mark_style: Shape.default[i] = mark_style[i] + +from sciapp.object import ROI +mark_style = ConfigManager.get('roi_style') +if not mark_style is None: + for i in mark_style: ROI.default[i] = mark_style[i] + +from sciapp.action import Measure +mark_style = ConfigManager.get('mea_style') +if not mark_style is None: + for i in mark_style: Measure.default[i] = mark_style[i] + +filenames = glob(osp.join(root_dir,'data/luts/*/*.lut')) +keys = [osp.split(filename)[-1][:-4] for filename in filenames] +values = [np.fromfile(i, dtype=np.uint8).reshape((3,256)).T.copy() for i in filenames] +for k,v in zip(keys[::-1], values[::-1]): ColorManager.add(k, v, 'adv') + +filenames = glob(osp.join(root_dir, 'data/luts/*.lut')) +keys = [osp.split(filename)[-1][:-4] for filename in filenames] +values = [np.fromfile(i, dtype=np.uint8).reshape((3,256)).T.copy() for i in filenames] +for k,v in zip(keys[::-1], values[::-1]): ColorManager.add(k, v, 'base') +ColorManager.add('Grays', ColorManager.get('Grays'), 'base') + +from sciwx import ColorManager as ColorMap +ColorMap.remove() +ColorMap.adds(ColorManager.gets(tag='base')[::-1]) diff --git a/imagepy/app/source.py b/imagepy/app/source.py deleted file mode 100644 index 517e9292..00000000 --- a/imagepy/app/source.py +++ /dev/null @@ -1,44 +0,0 @@ -from sciapp import Source -from imagepy import root_dir -import os.path as osp -from glob import glob -import numpy as np - -Source.manager('plugin') -Source.manager('tool') -Source.manager('widget') -Source.manager('macros') -Source.manager('config') - -Source.manager('config').read(osp.join(root_dir, 'data/config.json')) -Source.manager('shortcut').read(osp.join(root_dir, 'data/shortcut.json')) -if Source.manager('config').get('language') is None: - Source.manager('config').add('language', 'english') - -from sciapp.object import Shape -mark_style = Source.manager('config').get('mark_style') -if not mark_style is None: - for i in mark_style: Shape.default[i] = mark_style[i] - -from sciapp.object import ROI -mark_style = Source.manager('config').get('roi_style') -if not mark_style is None: - for i in mark_style: ROI.default[i] = mark_style[i] - -from sciapp.action import Measure -mark_style = Source.manager('config').get('mea_style') -if not mark_style is None: - for i in mark_style: Measure.default[i] = mark_style[i] - -Source.manager('colormap').remove() - -filenames = glob(osp.join(root_dir,'data/luts/*/*.lut')) -keys = [osp.split(filename)[-1][:-4] for filename in filenames] -values = [np.fromfile(i, dtype=np.uint8).reshape((3,256)).T.copy() for i in filenames] -for k,v in zip(keys[::-1], values[::-1]): Source.manager('colormap').add(k, v, 'adv') - -filenames = glob(osp.join(root_dir, 'data/luts/*.lut')) -keys = [osp.split(filename)[-1][:-4] for filename in filenames] -values = [np.fromfile(i, dtype=np.uint8).reshape((3,256)).T.copy() for i in filenames] -for k,v in zip(keys[::-1], values[::-1]): Source.manager('colormap').add(k, v, 'base') -Source.manager('colormap').add('Grays', Source.manager('colormap').get('Grays'), 'base') \ No newline at end of file diff --git a/imagepy/app/startup.py b/imagepy/app/startup.py index dc3c5e37..4300cecc 100644 --- a/imagepy/app/startup.py +++ b/imagepy/app/startup.py @@ -1,8 +1,8 @@ import wx, sys, os -from .source import * -from sciapp import Source +from .manager import * from . import loader from imagepy import root_dir +from .manager import ConfigManager, DictManager def extend_plgs(plg): if isinstance(plg, tuple): @@ -73,7 +73,7 @@ def load_dictionary(): lans = [i for i in lans if os.path.isdir(i)] lans = [os.path.split(i) for i in lans] lan = sorted(set([i[1] for i in lans])) - Source.manager('dictionary').add('language', lan) + DictManager.add('language', lan) lans = sorted(set([i[0] for i in lans])) for i in lans: loader.build_dictionary(i) @@ -91,7 +91,7 @@ def start(): asp.Update() load_document() load_dictionary() - uistyle = Source.manager('config').get('uistyle') or 'imagepy' + uistyle = ConfigManager.get('uistyle') or 'imagepy' frame = ImageJ(None) if uistyle == 'imagej' else ImagePy(None) frame.Show() app.MainLoop() \ No newline at end of file diff --git a/imagepy/data/config.json b/imagepy/data/config.json index 92f32548..fe7b9a33 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["roi_style", {"color": [255, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 255, 0], "size": 8}, null], ["language", "Chinese", null], ["uistyle", "imagepy", null], ["recent", ["C:/Users/54631/Desktop/\u6d77\u51b0\u62a5\u4ef7/testmacros.mc", "C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc"], null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null]] \ No newline at end of file +[["uistyle", "imagepy", null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["recent", ["C:/Users/54631/Desktop/\u6d77\u51b0\u62a5\u4ef7/testmacros.mc", "C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc"], null], ["roi_style", {"color": [255, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 255, 0], "size": 8}, null], ["language", "English", null]] \ No newline at end of file diff --git a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py index 2106bf2e..995115d4 100644 --- a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py @@ -9,7 +9,7 @@ from skimage.measure import regionprops from sciapp.object import mark2shp import pandas as pd -from sciapp import Source +from imagepy.app import ColorManager # center, area, l, extent, cov class RegionCounter(Simple): @@ -195,6 +195,6 @@ def run(self, ips, snap, img, para = None): else: ps = ps / ps.max() idx[1:] = ps * 245 + 10 img[:] = idx[lab] - ips.lut = Source.manager('colormap').get(para['cm']) + ips.lut = ColorManager.get(para['cm']) plgs = [RegionCounter, RegionFilter, PropertyMarker] \ No newline at end of file diff --git a/imagepy/menus/Edit/edit_plg.py b/imagepy/menus/Edit/edit_plg.py index 13b621d2..aa48b3c3 100644 --- a/imagepy/menus/Edit/edit_plg.py +++ b/imagepy/menus/Edit/edit_plg.py @@ -6,7 +6,6 @@ import numpy as np from sciapp.action import Simple, Filter from sciapp.action import ImageTool -from sciapp import Source class PasteMove(ImageTool): def __init__(self): diff --git a/imagepy/menus/File/BMP/bmp_plgs.py b/imagepy/menus/File/BMP/bmp_plgs.py index 104d12cd..45b3a0f8 100644 --- a/imagepy/menus/File/BMP/bmp_plgs.py +++ b/imagepy/menus/File/BMP/bmp_plgs.py @@ -1,6 +1,5 @@ from sciapp.action import dataio from skimage.io import imread, imsave -from sciapp import Source dataio.ReaderManager.add('bmp', imread, 'img') dataio.WriterManager.add('bmp', imsave, 'img') diff --git a/imagepy/menus/File/DAT/dat_plgs.py b/imagepy/menus/File/DAT/dat_plgs.py index b960e9de..ed302d52 100644 --- a/imagepy/menus/File/DAT/dat_plgs.py +++ b/imagepy/menus/File/DAT/dat_plgs.py @@ -1,6 +1,5 @@ from sciapp.action import dataio import numpy as np -from sciapp import Source def imread(path): return np.loadtxt(path,dtype=float) diff --git a/imagepy/menus/File/DICOM/dicom_plgs.py b/imagepy/menus/File/DICOM/dicom_plgs.py index 8052f200..50e75233 100644 --- a/imagepy/menus/File/DICOM/dicom_plgs.py +++ b/imagepy/menus/File/DICOM/dicom_plgs.py @@ -1,8 +1,5 @@ from sciapp.action import dataio import pydicom -from sciapp import Source - - def imread(path): return pydicom.read_file(path, force=True).pixel_array diff --git a/imagepy/menus/File/Export/sequence_plg.py b/imagepy/menus/File/Export/sequence_plg.py index 05d43e8f..aa690958 100644 --- a/imagepy/menus/File/Export/sequence_plg.py +++ b/imagepy/menus/File/Export/sequence_plg.py @@ -6,7 +6,6 @@ """ from skimage.io import imsave from sciapp.action import Simple -from sciapp import Source class Plugin(Simple): title = 'Save Sequence' diff --git a/imagepy/menus/File/GIF/gif_plgs.py b/imagepy/menus/File/GIF/gif_plgs.py index dd8030ca..21c0ca04 100644 --- a/imagepy/menus/File/GIF/gif_plgs.py +++ b/imagepy/menus/File/GIF/gif_plgs.py @@ -1,7 +1,6 @@ from sciapp.action import dataio from sciapp.action import Simple from skimage.io import imread, imsave -from sciapp import Source import imageio dataio.ReaderManager.add('gif', imread, 'img') diff --git a/imagepy/menus/File/Import/sequence_plg.py b/imagepy/menus/File/Import/sequence_plg.py index beb48d67..48489595 100644 --- a/imagepy/menus/File/Import/sequence_plg.py +++ b/imagepy/menus/File/Import/sequence_plg.py @@ -6,7 +6,6 @@ from sciapp.action import dataio from skimage.io import imread -from sciapp import Source from sciapp.action import Free from glob import glob import os.path as osp diff --git a/imagepy/menus/File/JPG/jpg_plgs.py b/imagepy/menus/File/JPG/jpg_plgs.py index ab3cf782..ff977ff4 100644 --- a/imagepy/menus/File/JPG/jpg_plgs.py +++ b/imagepy/menus/File/JPG/jpg_plgs.py @@ -1,6 +1,5 @@ from sciapp.action import dataio from imageio import imread, imsave -from sciapp import Source dataio.ReaderManager.add('jpg', imread, 'img') dataio.WriterManager.add('jpg', imsave, 'img') diff --git a/imagepy/menus/File/MAT/mat_plgs.py b/imagepy/menus/File/MAT/mat_plgs.py index f71e7fe0..1dee31f1 100644 --- a/imagepy/menus/File/MAT/mat_plgs.py +++ b/imagepy/menus/File/MAT/mat_plgs.py @@ -1,6 +1,5 @@ from sciapp.action import dataio from scipy.io import savemat, loadmat -from sciapp import Source import os dataio.ReaderManager.add('mat', lambda path: loadmat(path)['img'], 'img') diff --git a/imagepy/menus/File/MarkDown/md_plg.py b/imagepy/menus/File/MarkDown/md_plg.py index 0c108589..3c01a48a 100644 --- a/imagepy/menus/File/MarkDown/md_plg.py +++ b/imagepy/menus/File/MarkDown/md_plg.py @@ -1,5 +1,4 @@ from sciapp.action import dataio -from sciapp import Source def read(path): with open(path) as f: return f.read() diff --git a/imagepy/menus/File/Numpy/ndarray_plgs.py b/imagepy/menus/File/Numpy/ndarray_plgs.py index 044f20bf..79ee8a38 100644 --- a/imagepy/menus/File/Numpy/ndarray_plgs.py +++ b/imagepy/menus/File/Numpy/ndarray_plgs.py @@ -1,6 +1,5 @@ from sciapp.action import dataio import numpy as np -from sciapp import Source import os dataio.ReaderManager.add('npy', np.load, 'img') diff --git a/imagepy/menus/File/Open Recent/recent_plgs.py b/imagepy/menus/File/Open Recent/recent_plgs_.py similarity index 61% rename from imagepy/menus/File/Open Recent/recent_plgs.py rename to imagepy/menus/File/Open Recent/recent_plgs_.py index 0ad7bb5a..87addb41 100644 --- a/imagepy/menus/File/Open Recent/recent_plgs.py +++ b/imagepy/menus/File/Open Recent/recent_plgs_.py @@ -1,3 +1,3 @@ from sciapp.action import dataio -plgs = dataio.rlist \ No newline at end of file +# plgs = dataio.rlist \ No newline at end of file diff --git a/imagepy/menus/File/PNG/png_plgs.py b/imagepy/menus/File/PNG/png_plgs.py index 44a90f94..8f1f1d80 100644 --- a/imagepy/menus/File/PNG/png_plgs.py +++ b/imagepy/menus/File/PNG/png_plgs.py @@ -1,6 +1,5 @@ from sciapp.action import dataio from skimage.io import imread, imsave -from sciapp import Source dataio.ReaderManager.add('png', imread, 'img') dataio.WriterManager.add('png', imsave, 'img') diff --git a/imagepy/menus/File/TIF/tif_plgs.py b/imagepy/menus/File/TIF/tif_plgs.py index bde4bf8e..74899dae 100644 --- a/imagepy/menus/File/TIF/tif_plgs.py +++ b/imagepy/menus/File/TIF/tif_plgs.py @@ -1,6 +1,5 @@ from sciapp.action import dataio from skimage.io import imread, imsave -from sciapp import Source dataio.ReaderManager.add('tif', imread, 'img') dataio.ReaderManager.add('tiff', imread, 'img') diff --git a/imagepy/menus/File/open_plg.py b/imagepy/menus/File/open_plg.py index fe807744..a3b34046 100644 --- a/imagepy/menus/File/open_plg.py +++ b/imagepy/menus/File/open_plg.py @@ -6,7 +6,7 @@ from sciapp.action import Free from sciapp.action import dataio -from sciapp import Source + class OpenFile(dataio.Reader): title = 'Open' diff --git a/imagepy/menus/File/save_plg.py b/imagepy/menus/File/save_plg.py index 00a857b4..b72bc0ab 100644 --- a/imagepy/menus/File/save_plg.py +++ b/imagepy/menus/File/save_plg.py @@ -4,7 +4,6 @@ @author: yxl """ from sciapp.action import dataio -from sciapp import Source from sciapp.action import Simple class SaveImage(dataio.ImageWriter): diff --git a/imagepy/menus/Help/Language/language_plgs.py b/imagepy/menus/Help/Language/language_plgs.py index 53da6686..241c7f16 100644 --- a/imagepy/menus/Help/Language/language_plgs.py +++ b/imagepy/menus/Help/Language/language_plgs.py @@ -1,5 +1,5 @@ -from sciapp import Source from sciapp.action import Free +from imagepy.app import ConfigManager, DictManager class Language(Free): def __init__(self, key): @@ -7,11 +7,10 @@ def __init__(self, key): asyn = False def run(self, para = None): - Source.manager('config').remove('language') - Source.manager('config').add('language', self.title) + ConfigManager.add('language', self.title) self.app.load_all() def __call__(self): return self -plgs = [Language(i) for i in Source.manager('dictionary').get('language')] \ No newline at end of file +plgs = [Language(i) for i in DictManager.get('language')] \ No newline at end of file diff --git a/imagepy/menus/Image/Lookup table/Others/lookuptables_plg.py b/imagepy/menus/Image/Lookup table/Others/lookuptables_plg.py index 608cb8f2..99711fa2 100644 --- a/imagepy/menus/Image/Lookup table/Others/lookuptables_plg.py +++ b/imagepy/menus/Image/Lookup table/Others/lookuptables_plg.py @@ -1,4 +1,4 @@ from ..lookuptables_plg import LUT -from sciapp import Source +from imagepy.app import ColorManager -plgs = [LUT(i, j) for i, j, _ in Source.manager('colormap').gets(tag='adv')] \ No newline at end of file +plgs = [LUT(i, j) for i, j, _ in ColorManager.gets(tag='adv')] \ No newline at end of file diff --git a/imagepy/menus/Image/Lookup table/lookuptables_plg.py b/imagepy/menus/Image/Lookup table/lookuptables_plg.py index 614cc7a3..e737a0c3 100644 --- a/imagepy/menus/Image/Lookup table/lookuptables_plg.py +++ b/imagepy/menus/Image/Lookup table/lookuptables_plg.py @@ -7,7 +7,7 @@ import numpy as np #from imagepy.core import ImagePlus from sciapp.action import Free -from sciapp import Source +from imagepy.app import ColorManager from sciapp.object import Image class LUT(Free): @@ -28,5 +28,5 @@ def run(self, para = None): def __call__(self): return self -plgs = [LUT(i, j) for i, j, _ in Source.manager('colormap').gets(tag='base')] +plgs = [LUT(i, j) for i, j, _ in ColorManager.gets(tag='base')] \ No newline at end of file diff --git a/imagepy/menus/Image/Mark/mark_plgs.py b/imagepy/menus/Image/Mark/mark_plgs.py index 9a8610d2..ab40e40d 100644 --- a/imagepy/menus/Image/Mark/mark_plgs.py +++ b/imagepy/menus/Image/Mark/mark_plgs.py @@ -1,5 +1,5 @@ from sciapp.action import Simple, Free -from sciapp import Source +from imagepy.app import ConfigManager from sciapp.object import Shape from sciapp.util import mark2shp import json @@ -54,6 +54,6 @@ class Setting(Free): def run(self, para=None): for i in para: Shape.default[i] = para[i] - Source.manager('config').add('mark_style', para) + ConfigManager.set('mark_style', para) plgs = [Open, Save, Clear, '-', Setting] \ No newline at end of file diff --git a/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py b/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py index 436bb38a..8efba4af 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py +++ b/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py @@ -1,5 +1,5 @@ from sciapp.action import Table -from sciapp import Source +from imagepy.app import ColorManager import numpy as np class Plugin(Table): @@ -27,7 +27,7 @@ def load(self, para): def run(self, tps, snap, data, para = None): pts = np.array(data[[para['x'], para['y'], para['z']]]) rs = data[para['rs']]*para['r'] if para['rs'] != 'None' else [para['r']]*len(pts) - cm = Source.manager('colormap').get(para['cm'])/255.0 + cm = ColorManager.get(para['cm'])/255.0 clip = lambda x : (x-x.min())/(x.max()-x.min())*255 if para['cs'] == 'None': cs = [np.array(para['c'])/255.0]*len(pts) else: cs = cm[clip(data[para['cs']]).astype(np.uint8)] diff --git a/imagepy/menus/Plugins/Contribute/pmanager_wgt.py b/imagepy/menus/Plugins/Contribute/pmanager_wgt.py index 92f00423..1017065d 100644 --- a/imagepy/menus/Plugins/Contribute/pmanager_wgt.py +++ b/imagepy/menus/Plugins/Contribute/pmanager_wgt.py @@ -6,7 +6,6 @@ """ import wx, os, glob, shutil, random from imagepy import root_dir -from sciapp import Source from sciwx.text import MDPad #from imagepy.ui.mkdownwindow import HtmlPanel, md2html @@ -143,7 +142,7 @@ def on_install(self, event): i = self.lst_plgs.GetFirstSelected() if i==-1: return path = self.buf[i][-1]['path'] - Source.manager('plugin').get('Install Plugins')().start( + self.app.plugin_manager.get('Install Plugins')().start( self.app, {'repo':self.buf[i][-1]['path']}, self.load) def on_remove(self, event): diff --git a/imagepy/menus/Plugins/Macros/recorder_plg.py b/imagepy/menus/Plugins/Macros/recorder_plg.py index f8df3096..c34a9f82 100644 --- a/imagepy/menus/Plugins/Macros/recorder_plg.py +++ b/imagepy/menus/Plugins/Macros/recorder_plg.py @@ -1,5 +1,5 @@ from sciapp.action import dataio -from sciapp import Source + def readmc(path): with open(path) as f: return f.readlines() diff --git a/imagepy/menus/Plugins/Manager/console_wgt.py b/imagepy/menus/Plugins/Manager/console_wgt.py index 081a01ba..5f673335 100644 --- a/imagepy/menus/Plugins/Manager/console_wgt.py +++ b/imagepy/menus/Plugins/Manager/console_wgt.py @@ -4,20 +4,19 @@ import scipy.ndimage as ndimg import numpy as np # from imagepy import IPy - from sciapp.action import Free -from sciapp import Source + cmds = {'app':'app', 'np':np, 'ndimg':ndimg, 'update':None, 'get_img':None} class Macros(dict): - def __init__(self): - for i in Source.manager('plugin').names(): + def __init__(self, app): + for i in app.plugin_manager.names(): if not isinstance(i, str) or i == 'Command Line': #print(PluginsManager.plgs[i]) continue name = ''.join(list(filter(str.isalnum, i))) - exec("self.run_%s = lambda para=None, plg=Source.manager('plugin').get(i):plg().start(cmds['app'], para)"%name) + exec("self.run_%s = lambda para=None, plg=app.plugin_manager.get(i):plg().start(cmds['app'], para)"%name) class Plugin(wx.Panel): title = 'Command Line' @@ -33,6 +32,6 @@ def __init__(self, parent, app=None): bSizer = wx.BoxSizer( wx.VERTICAL ) bSizer.Add( shell, 1, wx.EXPAND|wx.ALL, 5 ) self.SetSizer(bSizer) - cmds['plgs'] = Macros() + cmds['plgs'] = Macros(app) shell.run('# plgs.run_name() to call a ImagePy plugin.\n') shell.run('# app is avalible here, and get_img() to get the current ImagePlus, update() to redraw.\n') \ No newline at end of file diff --git a/imagepy/menus/Plugins/Manager/plglist_wgt.py b/imagepy/menus/Plugins/Manager/plglist_wgt.py index 45015fa9..cca16cca 100644 --- a/imagepy/menus/Plugins/Manager/plglist_wgt.py +++ b/imagepy/menus/Plugins/Manager/plglist_wgt.py @@ -6,7 +6,6 @@ """ import wx, os #from imagepy import IPy, root_dir -from sciapp import Source class VirtualListCtrl(wx.ListCtrl): def __init__(self, parent, title, data=[]): diff --git a/imagepy/menus/Plugins/Manager/plgtree_wgt.py b/imagepy/menus/Plugins/Manager/plgtree_wgt.py index 16b606b2..a632d681 100644 --- a/imagepy/menus/Plugins/Manager/plgtree_wgt.py +++ b/imagepy/menus/Plugins/Manager/plgtree_wgt.py @@ -8,10 +8,9 @@ from sciapp.action import Free import wx,os from imagepy import root_dir -from imagepy.app import loader +from imagepy.app import loader, ConfigManager from wx.py.editor import EditorFrame from sciwx.text import MDPad -from sciapp import Source from glob import glob class Plugin ( wx.Panel ): @@ -102,9 +101,9 @@ def on_select( self, event ): if plg!=None: self.plg = plg name = self.tre_plugins.GetItemText(event.GetItem()) - lang = Source.manager('config').get('language') - doc = Source.manager('document').get(name, tag=lang) - doc = doc or Source.manager('document').get(name, tag='English') + lang = ConfigManager.get('language') + doc = DocumentManager.get(name, tag=lang) + doc = doc or DocumentManager.get(name, tag='English') self.txt_info.set_cont(doc or 'No Document!') def on_source(self, event): diff --git a/imagepy/menus/Plugins/Manager/shotcut_wgt.py b/imagepy/menus/Plugins/Manager/shotcut_wgt.py index 796f377a..a7fb8e64 100644 --- a/imagepy/menus/Plugins/Manager/shotcut_wgt.py +++ b/imagepy/menus/Plugins/Manager/shotcut_wgt.py @@ -1,7 +1,7 @@ import wx, os from sciapp.action import Free -from sciapp import Source from imagepy import root_dir +from imagepy.app import ShortcutManager class VirtualListCtrl(wx.ListCtrl): def __init__(self, parent, title, data=[]): @@ -64,7 +64,7 @@ def __init__( self, parent, app=None): #def list_plg(self, lst, items def load(self): lst = self.app.plugin_names() - self.plgs = [[i, Source.manager('shortcut').get(i)] for i in lst] + self.plgs = [[i, ShortcutManager.get(i)] for i in lst] for i in self.plgs: if i[1]==None:i[1]='' self.plgs.sort() @@ -114,8 +114,8 @@ def on_run(self, event): if len(txt)>0 and txt[-1]=='-':txt=txt[:-1] self.buf[event.GetIndex()][1] = txt self.lst_plgs.RefreshItem(event.GetIndex()) - if txt!='': Source.manager('shortcut').add(title, txt) + if txt!='': ShortcutManager.add(title, txt) #PluginsManager.plgs[self.buf[event.GetIndex()][0]]().start() def close(self): - Source.manager('shortcut').write(os.path.join(root_dir,'data/shortcut.json')) + ShortcutManager.write(os.path.join(root_dir,'data/shortcut.json')) diff --git a/imagepy/menus/Plugins/Manager/toltree_wgt.py b/imagepy/menus/Plugins/Manager/toltree_wgt.py index 3425ad48..dfa801e5 100644 --- a/imagepy/menus/Plugins/Manager/toltree_wgt.py +++ b/imagepy/menus/Plugins/Manager/toltree_wgt.py @@ -8,9 +8,8 @@ from sciapp.action import Free import wx,os from imagepy import root_dir -from imagepy.app import loader +from imagepy.app import loader, ConfigManager, DocumentManager from wx.py.editor import EditorFrame -from sciapp import Source #from imagepy.ui.mkdownwindow import HtmlPanel, md2html from sciwx.text import MDPad from glob import glob @@ -100,9 +99,9 @@ def on_select( self, event ): if plg!=None: self.plg = plg name = self.tre_plugins.GetItemText(event.GetItem()) - lang = Source.manager('config').get('language') - doc = Source.manager('document').get(name, tag=lang) - doc = doc or Source.manager('document').get(name, tag='English') + lang = ConfigManager.get('language') + doc = DocumentManager.get(name, tag=lang) + doc = doc or DocumentManager.get(name, tag='English') self.txt_info.set_cont(doc or 'No Document!') def on_source(self, event): diff --git a/imagepy/menus/Plugins/temporal_plg.py b/imagepy/menus/Plugins/temporal_plg.py index 0c27ae75..4e777c4a 100644 --- a/imagepy/menus/Plugins/temporal_plg.py +++ b/imagepy/menus/Plugins/temporal_plg.py @@ -3,7 +3,7 @@ @author: weisong """ from sciapp.action import Simple -from sciapp import Source +from imagepy.app import ColorManager import numpy as np def color_code(img, lut): @@ -26,14 +26,14 @@ class Plugin(Simple): def load(self, ips): self.slength = len(ips.imgs) self.para['End image'] = self.slength - self.view = [(list, 'LUT', Source.manager('colormap').names(), str, 'LUT',''), + self.view = [(list, 'LUT', ColorManager.names(), str, 'LUT',''), (int, 'Start image', (1,self.slength),0,'Start image','1~%d'%self.slength), (int, 'End image', (2,self.slength),0,'End image','start~%d'%self.slength), (bool, 'Creatbar', 'Creat time color scale bar')] return True def run(self, ips, imgs, para = None): - cmap = Source.manager('colormap').get(para['LUT']) + cmap = ColorManager.get(para['LUT']) imglut = color_code(imgs[para['Start image']-1: para['End image']], cmap) self.app.show_img([imglut],'Color-coded %s'%ips.title) if para['Creatbar']: diff --git a/imagepy/menus/Process/Classify/io_plgs.py b/imagepy/menus/Process/Classify/io_plgs.py index 8ce18657..e5284857 100644 --- a/imagepy/menus/Process/Classify/io_plgs.py +++ b/imagepy/menus/Process/Classify/io_plgs.py @@ -1,5 +1,5 @@ from sciapp.action import Filter, Simple -from sciapp import Source +from imagepy.app import ColorManager # from imagepy.core import ImagePlus import numpy as np @@ -21,7 +21,7 @@ def run(self, ips, imgs, para = None): newips.back = ips idx = ['None', 'Max', 'Min', 'Mask', '2-8mix', '4-6mix', '5-5mix', '6-4mix', '8-2mix'] modes = ['set', 'max', 'min', 'msk', 0.2, 0.4, 0.5, 0.6, 0.8] - newips.lut = Source.manager('colormap').get(para['cm']) + newips.lut = ColorManager.get(para['cm']) newips.chan_mode = modes[idx.index(para['mode'])] #newips.range = (0, para['n']) IPy.show_ips(newips) diff --git a/imagepy/menus/Process/Classify/predict_plg.py b/imagepy/menus/Process/Classify/predict_plg.py index da118ef8..aa5d7332 100644 --- a/imagepy/menus/Process/Classify/predict_plg.py +++ b/imagepy/menus/Process/Classify/predict_plg.py @@ -4,7 +4,6 @@ import os.path as osp import joblib from imagepy.ipyalg import feature -from sciapp import Source class Plugin(Simple): title = 'Feature Predictor' diff --git a/imagepy/menus/Selection/select_plg.py b/imagepy/menus/Selection/select_plg.py index 36231fe9..85d7c6a3 100644 --- a/imagepy/menus/Selection/select_plg.py +++ b/imagepy/menus/Selection/select_plg.py @@ -1,7 +1,7 @@ from sciapp.action import Simple, Free -from sciapp import Source from sciapp.object import ROI, Rectangle from sciapp.util.shputil import geom2shp, geom_flatten, geom_union, mark2shp +from imagepy.app import ConfigManager import json, time class SelectAll(Simple): @@ -231,7 +231,7 @@ class Setting(Free): def run(self, para=None): for i in para: ROI.default[i] = para[i] - Source.manager('config').add('roi_style', para) + ConfigManager.set('roi_style', para) plgs = [SelectAll, SelectNone, '-', Inflate, Shrink, Convex, Box, Clip, Invert, diff --git a/imagepy/menus/Table/Chart/plot_plgs.py b/imagepy/menus/Table/Chart/plot_plgs.py index 47f0348c..d6d72066 100644 --- a/imagepy/menus/Table/Chart/plot_plgs.py +++ b/imagepy/menus/Table/Chart/plot_plgs.py @@ -2,7 +2,7 @@ import matplotlib.pyplot as plt #from imagepy.core.manager import ColorManager from matplotlib import colors -from sciapp import Source +from imagepy.app import ColorManager class Plot(Table): title = 'Plot Chart' @@ -113,7 +113,7 @@ class Scatter(Table): def run(self, tps, snap, data, para = None): rs = data[para['rs']] * para['s'] if para['rs'] != 'None' else para['s'] cs = data[para['cs']] if para['cs'] != 'None' else '#%.2x%.2x%.2x'%para['c'] - cm = Source.manager('colormap').get(para['cm'])/255.0 + cm = ColorManager.get(para['cm'])/255.0 cm = None if para['cs'] == 'None' else colors.ListedColormap(cm, N=256) plt = self.app.show_plot(para['title']) data.plot.scatter(x=para['x'], y=para['y'], s=rs, c=cs, alpha=para['alpha'], diff --git a/imagepy/menus/Table/Table IO/tableio_plgs.py b/imagepy/menus/Table/Table IO/tableio_plgs.py index 9020cd1e..5267b714 100644 --- a/imagepy/menus/Table/Table IO/tableio_plgs.py +++ b/imagepy/menus/Table/Table IO/tableio_plgs.py @@ -1,6 +1,5 @@ from sciapp.action import dataio from pandas import read_csv, read_excel, read_hdf -from sciapp import Source def show(data, title): IPy.show_table(data, title) diff --git a/imagepy/menus/Window/Windows Style/style_plgs.py b/imagepy/menus/Window/Windows Style/style_plgs.py index a323d1ac..e1dd9005 100644 --- a/imagepy/menus/Window/Windows Style/style_plgs.py +++ b/imagepy/menus/Window/Windows Style/style_plgs.py @@ -1,18 +1,18 @@ from sciapp.action import Free -from sciapp import Source +from imagepy.app import ConfigManager class ImageJStyle(Free): title = 'Pay Tribute To ImageJ' asyn = False def run(self, para = None): - Source.manager('config').add('uistyle', 'imagej') + ConfigManager.add('uistyle', 'imagej') self.app.alert('Shown in ImageJ style when next setup!') class ImagePyStyle(Free): title = 'Elegant ImagePy Style' asyn = False def run(self, para = None): - Source.manager('config').add('uistyle', 'imagepy') + ConfigManager.add('uistyle', 'imagepy') self.app.alert('Shown in ImagePy style when next setup!') plgs = [ImageJStyle, ImagePyStyle] \ No newline at end of file diff --git a/imagepy/menus/Window/develop_wgts.py b/imagepy/menus/Window/develop_wgts.py index d650d8f8..6a45358b 100644 --- a/imagepy/menus/Window/develop_wgts.py +++ b/imagepy/menus/Window/develop_wgts.py @@ -1,5 +1,4 @@ import wx, wx.lib.agw.aui as aui -from sciapp import Source from imagepy.menus.Plugins.Macros.recorder_wgt import Plugin as recorder from imagepy.menus.Plugins.Manager.console_wgt import Plugin as console from imagepy.menus.Plugins.Manager.plglist_wgt import Plugin as plglist @@ -17,12 +16,12 @@ def __init__( self, parent, app=None): mrecorder = recorder(self) self.notebook = aui.AuiNotebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, aui.AUI_NB_DEFAULT_STYLE ) self.notebook.AddPage( mrecorder, mrecorder.title, True, wx.NullBitmap ) - self.notebook.AddPage( console(self), console.title, False, wx.NullBitmap ) - self.notebook.AddPage( plglist(self), plglist.title, False, wx.NullBitmap ) - self.notebook.AddPage( plgtree(self), plgtree.title, False, wx.NullBitmap ) - self.notebook.AddPage( toltree(self), toltree.title, False, wx.NullBitmap ) + self.notebook.AddPage( console(self, app), console.title, False, wx.NullBitmap ) + self.notebook.AddPage( plglist(self, app), plglist.title, False, wx.NullBitmap ) + self.notebook.AddPage( plgtree(self, app), plgtree.title, False, wx.NullBitmap ) + self.notebook.AddPage( toltree(self, app), toltree.title, False, wx.NullBitmap ) for i in range(5): self.notebook.GetPage(i).app = parent - Source.manager('widget').add('Macros Recorder', mrecorder) + app.manager('widget').add('Macros Recorder', mrecorder) sizer.Add( self.notebook, 1, wx.EXPAND |wx.ALL, 0 ) self.SetSizer( sizer ) self.Fit() diff --git a/imagepy/tools/Draw/aibrush_tol.py b/imagepy/tools/Draw/aibrush_tol.py index 3718a839..5a50caba 100644 --- a/imagepy/tools/Draw/aibrush_tol.py +++ b/imagepy/tools/Draw/aibrush_tol.py @@ -1,7 +1,6 @@ from sciapp.action import ImageTool import numpy as np from time import time -from sciapp import Source from skimage.morphology import flood_fill, flood from skimage.draw import line, circle from skimage.segmentation import felzenszwalb diff --git a/imagepy/tools/Measure/setting_tol.py b/imagepy/tools/Measure/setting_tol.py index d227aad7..3a638e02 100644 --- a/imagepy/tools/Measure/setting_tol.py +++ b/imagepy/tools/Measure/setting_tol.py @@ -1,7 +1,6 @@ -from sciapp.action import ImageTool from sciapp.action import Measure from sciapp.action import Free -from sciapp import Source +from imagepy.app import ConfigManager class Plugin(Free): title = 'Measure Setting' @@ -15,4 +14,4 @@ class Plugin(Free): def run(self, para=None): for i in para: Measure.default[i] = para[i] - Source.manager('config').add('mea_style', para) \ No newline at end of file + ConfigManager.set('mea_style', para) \ No newline at end of file diff --git a/sciapp/action/advanced/filter.py b/sciapp/action/advanced/filter.py index 4196bc84..8502d3c4 100644 --- a/sciapp/action/advanced/filter.py +++ b/sciapp/action/advanced/filter.py @@ -21,7 +21,8 @@ def process_channels(plg, ips, src, des, para): return des def process_one(plg, ips, src, img, para, callafter=None): - plg.app.record_macros('{}>{}'.format(plg.title, para)) + if callafter != 'no record': + plg.app.record_macros('{}>{}'.format(plg.title, para)) plg.app.add_task(plg) start = time() transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16) @@ -42,7 +43,7 @@ def process_one(plg, ips, src, img, para, callafter=None): plg.app.info('%s: cost %.3fs'%(plg.title, time()-start)) ips.update() plg.app.remove_task(plg) - if not callafter is None:callafter() + if not callafter in (None, 'no record'):callafter() def process_stack(plg, ips, src, imgs, para, callafter=None): plg.app.record_macros('{}>{}'.format(plg.title, para)) @@ -121,7 +122,7 @@ def check(self, ips): return True def preview(self, ips, para): - process_one(self, ips, ips.snap, ips.img, para, None) + process_one(self, ips, ips.snap, ips.img, para, 'no record') def load(self, ips):return True diff --git a/sciapp/app.py b/sciapp/app.py index 0165b3d5..d5994de9 100644 --- a/sciapp/app.py +++ b/sciapp/app.py @@ -15,6 +15,12 @@ def __init__(self, asyn=True): self.plugin_manager = self.manager('plugin') + def manager(self, name, value=None): + if not name in self.managers: + self.managers[name] = Manager() + return self.managers[name] + + # ========== Plugin ========== def add_plugin(self, name, plg, tag=None): self.plugin_manager.add(name, plg, tag) @@ -24,11 +30,7 @@ def get_plugin(self, name=None): def plugin_names(self, tag=None): return self.plugin_manager.names(tag) - def manager(self, name, value=None): - if not name in self.managers: - self.managers[name] = Manager() - return self.managers[name] - + # ========== Image ========== def show_img(self, img, name): if not isinstance(img, Image): img = Image(img, name) @@ -50,6 +52,7 @@ def get_img(self, name=None): def img_names(self): return self.img_manager.names() + # ========== Table ========== def show_table(self, tab, name): if not isinstance(tab, Table): tab = Table(tab, name) @@ -70,7 +73,7 @@ def get_table(self, name=None): def table_names(self): return self.tab_manager.names() - + # ========== Others ========== def show_plot(self): pass def show_mesh(self): pass diff --git a/sciapp/doc/readme.md b/sciapp/doc/readme.md new file mode 100644 index 00000000..e4721728 --- /dev/null +++ b/sciapp/doc/readme.md @@ -0,0 +1,42 @@ +# SciApp + +SciApp是一个交互式科学计算的后端框架,主要用于搭建科学分析应用。SciApp并不是算法库,也不包含任何界面,SciApp的目的是为算法类应用提供一个标准接口,具体如下: + +1. 定义了科学计算常用的数据结构封装类 +2. 对数据结构定义了一些基础操作函数 +3. 定义了一个通用Manager,一个App,将各种数据进行管理 +4. 定义了Action对象,可以对App进行操作 + + + +### Object模块 + +object模块定义了科学计算中常用的基础数据结构封装类,当然,如果仅仅为了计算,绝大多数时候,Numpy,Pandas等数据类型已经可以胜任,这里的封装,主要是面向交互与展示的,例如Image对象是图像数据,里面带了一个lut成员,用于在展示时映射成伪彩色。 + +1. Image:多维图像,基于Numpy + +2. Table:表格,基于DataFrame + +3. Shape: 点线面,任意多边形,基于Shapely + +4. Surface:三维表面 + + + +### Util模块 + +Util定义了一些针对Object数据类型的基础操作函数,这些函数也是为了完成交互与展示,并非为了数据分析。 + +1. imutil: 主要实现图像的快速采样,多图层融合等算法 + +2. shputil: 主要实现多边形与给定点之间的几何关系,用于鼠标编辑。 + + + +### Action模块 + +App: + +\1. Manager:Manager是一个通用容器,类似于mongodb的用法,具有add,get, + +Sciapp: \ No newline at end of file diff --git a/sciapp/manager.py b/sciapp/manager.py index 650c96f8..41d9d4f9 100644 --- a/sciapp/manager.py +++ b/sciapp/manager.py @@ -54,4 +54,5 @@ def read(self, path): self.path = path if not osp.exists(path): return with open(path) as f: - self.adds(json.loads(f.read())) \ No newline at end of file + self.adds(json.loads(f.read())) + return self \ No newline at end of file diff --git a/sciwx/__init__.py b/sciwx/__init__.py index dff7b6fe..72e33fc0 100644 --- a/sciwx/__init__.py +++ b/sciwx/__init__.py @@ -1,14 +1,15 @@ -from sciapp import Source +from sciapp import Manager import numpy as np +ColorManager = Manager() import matplotlib.pyplot as plt for i in plt.colormaps()[::-1]: cm = plt.get_cmap(i) if i[-2:]=='_r': continue vs = np.linspace(0, cm.N, 256, endpoint=False) lut = cm(vs.astype(np.int), bytes=True)[:,:3] - Source.manager('colormap').add(i, lut) + ColorManager.add(i, lut) del plt -graylut = Source.manager('colormap').get('gray') -Source.manager('colormap').add('Grays', graylut) -Source.manager('colormap').remove('gray') \ No newline at end of file +graylut = ColorManager.get('gray') +ColorManager.add('Grays', graylut) +ColorManager.remove('gray') \ No newline at end of file diff --git a/sciwx/plugins/histogram.py b/sciwx/plugins/histogram.py index 7e4fb7d6..e666ec0c 100644 --- a/sciwx/plugins/histogram.py +++ b/sciwx/plugins/histogram.py @@ -53,8 +53,7 @@ def __init__( self, parent, app): bSizer1.Add( bSizer2, 0, wx.EXPAND |wx.ALL, 5 ) self.cmapsel = CMapSelCtrl(self) - - self.cmapsel.SetItems(Source.manager('colormap').gets()) + bSizer1.Add(self.cmapsel, 0, wx.ALL|wx.EXPAND, 5 ) self.cmap = CMapPanel(self) diff --git a/sciwx/plugins/viewport.py b/sciwx/plugins/viewport.py index 1b351981..6d7c6b03 100644 --- a/sciwx/plugins/viewport.py +++ b/sciwx/plugins/viewport.py @@ -45,10 +45,8 @@ def __init__( self, parent , app): self.label.Wrap( -1 ) bSizer2.Add( self.label, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) - bSizer1.Add( bSizer2, 0, wx.EXPAND, 5 ) - self.SetSizer( bSizer1 ) self.Layout() diff --git a/sciwx/widgets/colormap.py b/sciwx/widgets/colormap.py index 5a8ab817..02718bfb 100644 --- a/sciwx/widgets/colormap.py +++ b/sciwx/widgets/colormap.py @@ -1,11 +1,13 @@ import numpy as np import wx.adv import sys, wx +from .. import ColorManager class CMapSelCtrl(wx.adv.OwnerDrawnComboBox): def __init__(self, parent): wx.adv.OwnerDrawnComboBox.__init__(self, parent, choices=[], style=wx.CB_READONLY, pos=(20,40), size=(256, 30)) + self.SetItems(ColorManager.gets()) def SetItems(self, kvs): self.Clear() @@ -70,7 +72,7 @@ def OnMeasureItemWidth(self, item): return -1; # default - will be measured from text width class CMapSelPanel(wx.Panel): - def __init__( self, parent, title): + def __init__( self, parent, title, app=None): wx.Panel.__init__(self, parent) sizer = wx.BoxSizer(wx.VERTICAL) lab_title = wx.StaticText( self, wx.ID_ANY, title, diff --git a/sciwx/widgets/paradialog.py b/sciwx/widgets/paradialog.py index 095836b7..de095a23 100644 --- a/sciwx/widgets/paradialog.py +++ b/sciwx/widgets/paradialog.py @@ -7,7 +7,7 @@ widgets = { 'ctrl':None, 'slide':FloatSlider, int:NumCtrl, 'path':PathCtrl, float:NumCtrl, 'lab':Label, bool:Check, str:TextCtrl, list:Choice, - 'color':ColorCtrl, 'any':AnyType, 'chos':Choices, 'hist':ThresholdPanel, + 'color':ColorCtrl, 'cmap':CMapSelPanel, 'any':AnyType, 'chos':Choices, 'hist':ThresholdPanel, 'curve':CurvePanel, 'img':ImageList, 'tab':TableList, 'field':TableField, 'fields':TableFields} def add_widget(key, value): widgets[key] = value From d12e422ca353f946e7311da9a165645d7a362eaf Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 30 Jun 2020 15:51:15 +0800 Subject: [PATCH 282/343] exprot sequence --- imagepy/app/imagepy.py | 7 +++++++ imagepy/menus/File/Export/sequence_plg.py | 14 ++++++-------- .../menus/Kit3D/Analysis 3D/regionprops3d_plgs.py | 2 +- sciwx/widgets/normal.py | 10 +++++----- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/imagepy/app/imagepy.py b/imagepy/app/imagepy.py index e91d7f80..b9035fae 100644 --- a/imagepy/app/imagepy.py +++ b/imagepy/app/imagepy.py @@ -514,6 +514,13 @@ def getpath(self, title, filt, io, name=''): dialog.Destroy() return path + def getdir(self, title, name=''): + dialog = wx.DirDialog(self, title, name, wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST) + rst = dialog.ShowModal() + path = dialog.GetPath() if rst == wx.ID_OK else None + dialog.Destroy() + return path + def show_para(self, title, view, para, on_handle=None, on_ok=None, on_cancel=None, on_help=None, preview=False, modal=True): lang = ConfigManager.get('language') diff --git a/imagepy/menus/File/Export/sequence_plg.py b/imagepy/menus/File/Export/sequence_plg.py index aa690958..22ef10c9 100644 --- a/imagepy/menus/File/Export/sequence_plg.py +++ b/imagepy/menus/File/Export/sequence_plg.py @@ -6,24 +6,22 @@ """ from skimage.io import imsave from sciapp.action import Simple +from sciapp.action import dataio class Plugin(Simple): title = 'Save Sequence' note = ['all'] para = {'path':'','name':'','format':'png'} #para = {'path':'./','name':'','format':'png'} + view = [('path', 'path', 'path', ''), + (str, 'name', 'name', 'number'), + (None)] def load(self, ips): - self.view = [(str, 'name', 'Name', ''), - (list, 'format',list(sorted(WriterManager.get())), str, 'Format', '')] + names = [i[0] for i in dataio.WriterManager.gets(tag='img')] + self.view[2] = (list, 'format',list(sorted(names)), str, 'format', '') return True - def show(self): - self.para['name'] = self.ips.title - rst = IPy.get_para('Save sequence', self.view, self.para) - if not rst :return rst - return IPy.getdir('Save sequence', '', self.para) - #process def run(self, ips, imgs, para = None): path = para['path']+'/'+para['name'] diff --git a/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py b/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py index f9149b3c..2ca4e55d 100644 --- a/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py +++ b/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py @@ -82,7 +82,7 @@ def run(self, ips, imgs, para = None): ites = np.array([i.inertia_tensor_eigvals for i in ls]) rst = np.sqrt(np.clip(ites.sum(axis=1)//2-ites.T, 0, 1e10)) * 4 for i in rst[::-1]: dt.append(np.abs(i)) - IPy.show_table(pd.DataFrame(list(zip(*dt)), columns=titles), ips.title+'-region') + self.app.show_table(pd.DataFrame(list(zip(*dt)), columns=titles), ips.title+'-region') # center, area, l, extent, cov class RegionFilter(Simple): diff --git a/sciwx/widgets/normal.py b/sciwx/widgets/normal.py index 7d7430b5..397822b8 100644 --- a/sciwx/widgets/normal.py +++ b/sciwx/widgets/normal.py @@ -159,12 +159,12 @@ def Bind(self, z, f): self.f = f def ontext(self, event): print('ColorCtrl') def onselect(self, event): - from ...core.manager import ConfigManager - filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in self.filt]) - dpath = ConfigManager.get('defaultpath') or '.' + filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in self.filt.split(',')]) + #dpath = ConfigManager.get('defaultpath') or '.' #if dpath==None: dpath = root_dir # './' - dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} - dialog = wx.FileDialog(self, 'Path Select', dpath, '', filt, wx.FD_OPEN) + if self.filt == '': + dialog = wx.DirDialog(self, 'Path Select', '.', wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST) + else: dialog = wx.FileDialog(self, 'Path Select', dpath, '', filt, wx.FD_OPEN) rst = dialog.ShowModal() if rst == wx.ID_OK: path = dialog.GetPath() From f9f26f6aa5ed1fffadbeef5e4b182a4c3c4c20e5 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 30 Jun 2020 20:01:37 +0800 Subject: [PATCH 283/343] update --- imagepy/menus/Plugins/update_plg.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imagepy/menus/Plugins/update_plg.py b/imagepy/menus/Plugins/update_plg.py index 6f19f2b5..910af0f1 100644 --- a/imagepy/menus/Plugins/update_plg.py +++ b/imagepy/menus/Plugins/update_plg.py @@ -19,11 +19,11 @@ class Update(Free): title = 'Update Software' def run(self, para=None): - IPy.info('update now, waiting...') + self.app.info('update now, waiting...') self.download_zip() self.deal_file() #self.delete_cache() - IPy.alert('imagepy update done!') + self.app.alert('imagepy update done!') def download_zip(self): url='https://github.com/Image-Py/imagepy/archive/master.zip' From 395f441a6bd5e3cd1c418a251a4def06a14535b5 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Thu, 2 Jul 2020 13:49:26 +0800 Subject: [PATCH 284/343] support to read files with Capital Letter extension --- sciapp/action/advanced/dataio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sciapp/action/advanced/dataio.py b/sciapp/action/advanced/dataio.py index dd2222de..abb864ac 100644 --- a/sciapp/action/advanced/dataio.py +++ b/sciapp/action/advanced/dataio.py @@ -22,7 +22,7 @@ def run(self, para = None): #add_recent(para['path']) fp, fn = os.path.split(para['path']) fn, fe = os.path.splitext(fn) - readers = ReaderManager.gets(fe[1:], tag=self.tag) + readers = ReaderManager.gets(fe[1:].lower(), tag=self.tag) if len(readers)==0: return self.app.alert('no reader found for %s file'%fe[1:]) if not self.tag is None: From 118836be3936c7938d951a9c3c0af57c4034df90 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 2 Jul 2020 14:40:16 +0800 Subject: [PATCH 285/343] sciapp doc --- imagepy/app/console.py | 3 - imagepy/app/imagej.py | 2 +- imagepy/app/imagepy.py | 14 +- imagepy/app/loader.py | 2 +- imagepy/menus/File/BMP/bmp_plgs.py | 4 +- imagepy/menus/File/DAT/dat_plgs.py | 4 +- imagepy/menus/File/DICOM/dicom_plgs.py | 2 +- imagepy/menus/File/Export/sequence_plg.py | 4 +- imagepy/menus/File/GIF/gif_plgs.py | 10 +- imagepy/menus/File/Import/raw_plg.py | 4 +- imagepy/menus/File/Import/roi_plg.py | 4 +- imagepy/menus/File/Import/sequence_plg.py | 2 +- imagepy/menus/File/MAT/mat_plgs.py | 8 +- imagepy/menus/File/MarkDown/md_plg.py | 2 +- imagepy/menus/File/Numpy/ndarray_plgs.py | 8 +- imagepy/menus/File/PNG/png_plgs.py | 4 +- imagepy/menus/File/save_plg.py | 2 +- imagepy/menus/Image/Mark/mark_plgs.py | 4 +- imagepy/menus/Plugins/Macros/recorder_plg.py | 4 +- imagepy/menus/Plugins/update_plg.py | 1 + .../menus/Process/Classify/classify_wgt.py | 4 +- imagepy/menus/Selection/select_plg.py | 4 +- imagepy/menus/Table/Table IO/tableio_plgs.py | 2 +- sciapp/action/action.py | 12 +- sciapp/action/advanced/dataio.py | 6 +- sciapp/action/advanced/filter.py | 2 +- sciapp/action/advanced/free.py | 2 +- sciapp/action/advanced/simple.py | 2 +- sciapp/action/advanced/table.py | 2 +- sciapp/action/imgact.py | 2 +- sciapp/action/readme.md | 115 +++++++++++++++ sciapp/action/tabact.py | 2 +- sciapp/action/tolact.py | 10 +- sciapp/app.py | 9 +- sciapp/demo/app_test.py | 27 ---- sciapp/demo/demo1_manager.py | 117 +++++++++++++++ sciapp/demo/demo2_app.py | 53 +++++++ sciapp/demo/demo3_img_action.py | 136 +++++++++++++++++ sciapp/demo/demo4_io_action.py | 106 +++++++++++++ sciapp/demo/demo5_process_action.py | 73 +++++++++ sciapp/demo/demo6_adv_action.py | 63 ++++++++ sciapp/demo/imagepy_console.py | 12 -- sciapp/doc/cn_app.md | 106 +++++++++++++ sciapp/doc/cn_imgaction.md | 137 +++++++++++++++++ sciapp/doc/cn_ioaction.md | 88 +++++++++++ sciapp/doc/cn_iprocesso.md | 94 ++++++++++++ sciapp/doc/cn_manager.md | 139 ++++++++++++++++++ sciapp/doc/cn_readme.md | 115 +++++++++++++++ sciapp/doc/readme.md | 42 ------ sciwx/app/imgapp.py | 2 +- sciwx/app/miniapp.py | 2 +- sciwx/plugins/io.py | 4 +- sciwx/widgets/normal.py | 18 +-- sciwx/widgets/util.py | 4 +- 54 files changed, 1429 insertions(+), 171 deletions(-) create mode 100644 sciapp/action/readme.md delete mode 100644 sciapp/demo/app_test.py create mode 100644 sciapp/demo/demo1_manager.py create mode 100644 sciapp/demo/demo2_app.py create mode 100644 sciapp/demo/demo3_img_action.py create mode 100644 sciapp/demo/demo4_io_action.py create mode 100644 sciapp/demo/demo5_process_action.py create mode 100644 sciapp/demo/demo6_adv_action.py delete mode 100644 sciapp/demo/imagepy_console.py create mode 100644 sciapp/doc/cn_app.md create mode 100644 sciapp/doc/cn_imgaction.md create mode 100644 sciapp/doc/cn_ioaction.md create mode 100644 sciapp/doc/cn_iprocesso.md create mode 100644 sciapp/doc/cn_manager.md create mode 100644 sciapp/doc/cn_readme.md delete mode 100644 sciapp/doc/readme.md diff --git a/imagepy/app/console.py b/imagepy/app/console.py index 64aa0d18..8999dbe5 100644 --- a/imagepy/app/console.py +++ b/imagepy/app/console.py @@ -12,9 +12,6 @@ def load_plugins(self, plgs=None): if callable(plgs[1]): name, plg = plgs[:2] self.add_plugin(name, plg, 'plugin') - for i in ' _.-': - name = name.replace(i,'_') - exec('self._%s_ = plg'%name) else: self.load_plugins(plgs[1]) if isinstance(plgs, list): for i in plgs: self.load_plugins(i) diff --git a/imagepy/app/imagej.py b/imagepy/app/imagej.py index f6a0fe1b..313b2679 100644 --- a/imagepy/app/imagej.py +++ b/imagepy/app/imagej.py @@ -445,7 +445,7 @@ def yes_no(self, info, title='ImagePy'): dic = {wx.ID_YES:'yes', wx.ID_NO:'no', wx.ID_CANCEL:'cancel'} return dic[rst] - def getpath(self, title, filt, io, name=''): + def get_path(self, title, filt, io, name=''): filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in filt]) dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} dialog = wx.FileDialog(self, title, '', name, filt, dic[io]) diff --git a/imagepy/app/imagepy.py b/imagepy/app/imagepy.py index b9035fae..8be2d385 100644 --- a/imagepy/app/imagepy.py +++ b/imagepy/app/imagepy.py @@ -505,17 +505,13 @@ def yes_no(self, info, title='ImagePy'): dic = {wx.ID_YES:'yes', wx.ID_NO:'no', wx.ID_CANCEL:'cancel'} return dic[rst] - def getpath(self, title, filt, io, name=''): + def get_path(self, title, filt, io, name=''): + if isinstance(filt, str): filt = filt.split(',') filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in filt]) dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} - dialog = wx.FileDialog(self, title, '', name, filt, dic[io]) - rst = dialog.ShowModal() - path = dialog.GetPath() if rst == wx.ID_OK else None - dialog.Destroy() - return path - - def getdir(self, title, name=''): - dialog = wx.DirDialog(self, title, name, wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST) + if io in {'open', 'save'}: + dialog = wx.FileDialog(self, title, '', name, filt, dic[io] | wx.FD_CHANGE_DIR) + else: dialog = wx.DirDialog(self, title, '', wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST | wx.FD_CHANGE_DIR) rst = dialog.ShowModal() path = dialog.GetPath() if rst == wx.ID_OK else None dialog.Destroy() diff --git a/imagepy/app/loader.py b/imagepy/app/loader.py index 53794896..1c024132 100644 --- a/imagepy/app/loader.py +++ b/imagepy/app/loader.py @@ -11,7 +11,7 @@ from .manager import DocumentManager, DictManager from codecs import open -def getpath(root, path): +def get_path(root, path): for i in range(10,0,-1): if not '../'*i in path: continue s = root diff --git a/imagepy/menus/File/BMP/bmp_plgs.py b/imagepy/menus/File/BMP/bmp_plgs.py index 45b3a0f8..9dc61226 100644 --- a/imagepy/menus/File/BMP/bmp_plgs.py +++ b/imagepy/menus/File/BMP/bmp_plgs.py @@ -7,11 +7,11 @@ class OpenFile(dataio.Reader): title = 'BMP Open' tag = 'img' - filt = ['BMP'] + filt = 'BMP' class SaveFile(dataio.ImageWriter): title = 'BMP Save' tag = 'img' - filt = ['BMP'] + filt = 'BMP' plgs = [OpenFile, SaveFile] \ No newline at end of file diff --git a/imagepy/menus/File/DAT/dat_plgs.py b/imagepy/menus/File/DAT/dat_plgs.py index ed302d52..6bba08f5 100644 --- a/imagepy/menus/File/DAT/dat_plgs.py +++ b/imagepy/menus/File/DAT/dat_plgs.py @@ -13,11 +13,11 @@ def imsave(path,img): class OpenFile(dataio.Reader): title = 'DAT Open' tag = 'img' - filt = ['DAT'] + filt = 'DAT' class SaveFile(dataio.ImageWriter): title = 'DAT Save' tag = 'img' - filt = ['DAT'] + filt = 'DAT' plgs = [OpenFile,SaveFile] \ No newline at end of file diff --git a/imagepy/menus/File/DICOM/dicom_plgs.py b/imagepy/menus/File/DICOM/dicom_plgs.py index 50e75233..1601c311 100644 --- a/imagepy/menus/File/DICOM/dicom_plgs.py +++ b/imagepy/menus/File/DICOM/dicom_plgs.py @@ -8,7 +8,7 @@ def imread(path): class OpenFile(dataio.Reader): title = 'DCM Open' - filt = ['DCM'] + filt = 'DCM' tag = 'img' plgs = [OpenFile] \ No newline at end of file diff --git a/imagepy/menus/File/Export/sequence_plg.py b/imagepy/menus/File/Export/sequence_plg.py index 22ef10c9..5d612fb7 100644 --- a/imagepy/menus/File/Export/sequence_plg.py +++ b/imagepy/menus/File/Export/sequence_plg.py @@ -13,7 +13,7 @@ class Plugin(Simple): note = ['all'] para = {'path':'','name':'','format':'png'} #para = {'path':'./','name':'','format':'png'} - view = [('path', 'path', 'path', ''), + view = [('path', 'path', 'path', '', 'folder'), (str, 'name', 'name', 'number'), (None)] @@ -26,7 +26,7 @@ def load(self, ips): def run(self, ips, imgs, para = None): path = para['path']+'/'+para['name'] write = dataio.WriterManager.get(para['format']) - print(path) + for i in range(len(imgs)): self.progress(i, len(imgs)) name = '%s-%.4d.%s'%(path,i,para['format']) diff --git a/imagepy/menus/File/GIF/gif_plgs.py b/imagepy/menus/File/GIF/gif_plgs.py index 21c0ca04..f1bc6ae9 100644 --- a/imagepy/menus/File/GIF/gif_plgs.py +++ b/imagepy/menus/File/GIF/gif_plgs.py @@ -10,22 +10,22 @@ class OpenFile(dataio.Reader): title = 'GIF Open' tag = 'img' - filt = ['GIF'] + filt = 'GIF' class SaveFile(dataio.ImageWriter): title = 'GIF Save' tag = 'img' - filt = ['GIF'] + filt = 'GIF' class SaveAnimate(Simple): title = 'GIF Animate Save' note = ['all'] - filt = ['gif'] + filt = 'GIF' para={'path':'', 'dur':0.2} view = [(int, 'dur', (0.01, 10), 2, 'duration', 's')] def load(self, ips): - self.para['path'] = self.app.getpath('Save..', self.filt, 'save', '') + self.para['path'] = self.app.get_path('Save..', self.filt, 'save', '') return not self.para['path'] is None def run(self, ips, imgs, para = None): @@ -33,7 +33,7 @@ def run(self, ips, imgs, para = None): class OpenAnimate(dataio.Reader): title = 'GIF Animate Open' - filt = ['GIF'] + filt = 'GIF' tag = 'imgs' note = ['8-bit', 'rgb', 'stack'] diff --git a/imagepy/menus/File/Import/raw_plg.py b/imagepy/menus/File/Import/raw_plg.py index 11686cbf..c1930ea0 100644 --- a/imagepy/menus/File/Import/raw_plg.py +++ b/imagepy/menus/File/Import/raw_plg.py @@ -15,8 +15,8 @@ class Plugin(Free): (list, 'c', [1,3], int, 'channel', '')] def load(self): - filt = ['raw'] - self.para['path'] = self.app.getpath('Open..', filt, 'open', '') + filt = 'raw' + self.para['path'] = self.app.get_path('Open..', filt, 'open', '') return not self.para['path'] is None #process diff --git a/imagepy/menus/File/Import/roi_plg.py b/imagepy/menus/File/Import/roi_plg.py index afed24ca..1f43be26 100644 --- a/imagepy/menus/File/Import/roi_plg.py +++ b/imagepy/menus/File/Import/roi_plg.py @@ -19,8 +19,8 @@ class Plugin(Free): (int, 'height', (1, 3000), 0, 'height', 'pix')] def load(self): - filt = ['zip'] - self.para['path'] = self.app.getpath(self.title, filt, 'open', self.para['name']) + filt = 'zip' + self.para['path'] = self.app.get_path(self.title, filt, 'open', self.para['name']) return not self.para['path'] is None def run(self, para=None): diff --git a/imagepy/menus/File/Import/sequence_plg.py b/imagepy/menus/File/Import/sequence_plg.py index 48489595..ec2770cb 100644 --- a/imagepy/menus/File/Import/sequence_plg.py +++ b/imagepy/menus/File/Import/sequence_plg.py @@ -20,7 +20,7 @@ def load(self): def show(self): filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in self.filt]) - rst = self.app.getpath('Import sequence', self.filt, 'open') + rst = self.app.get_path('Import sequence', self.filt, 'open') if rst is None: return rst self.para['path'] = rst files = self.getfiles(self.para['path']) diff --git a/imagepy/menus/File/MAT/mat_plgs.py b/imagepy/menus/File/MAT/mat_plgs.py index 1dee31f1..69c7ad35 100644 --- a/imagepy/menus/File/MAT/mat_plgs.py +++ b/imagepy/menus/File/MAT/mat_plgs.py @@ -10,22 +10,22 @@ class OpenFile(dataio.Reader): title = 'Mat Open' tag = 'img' - filt = ['Mat'] + filt = 'Mat' class SaveFile(dataio.ImageWriter): title = 'Mat Save' tag = 'img' - filt = ['Mat'] + filt = 'Mat' class Open3D(dataio.Reader): title = 'Mat 3D Open' tag = 'imgs' - filt = ['Mat'] + filt = 'Mat' class Save3D(dataio.ImageWriter): title = 'Mat 3D Save' tag = 'imgs' - filt = ['Mat'] + filt = 'Mat' note = ['8-bit', 'rgb', 'stack'] plgs = [OpenFile, SaveFile, '-', Open3D, Save3D] \ No newline at end of file diff --git a/imagepy/menus/File/MarkDown/md_plg.py b/imagepy/menus/File/MarkDown/md_plg.py index 3c01a48a..2874249c 100644 --- a/imagepy/menus/File/MarkDown/md_plg.py +++ b/imagepy/menus/File/MarkDown/md_plg.py @@ -8,4 +8,4 @@ def read(path): class Plugin(dataio.Reader): title = 'MarkDown Open' tag = 'md' - filt = ['MD'] \ No newline at end of file + filt = 'MD' \ No newline at end of file diff --git a/imagepy/menus/File/Numpy/ndarray_plgs.py b/imagepy/menus/File/Numpy/ndarray_plgs.py index 79ee8a38..ad3f3e98 100644 --- a/imagepy/menus/File/Numpy/ndarray_plgs.py +++ b/imagepy/menus/File/Numpy/ndarray_plgs.py @@ -10,22 +10,22 @@ class OpenFile(dataio.Reader): title = 'Numpy Open' tag = 'img' - filt = ['npy'] + filt = 'npy' class SaveFile(dataio.ImageWriter): title = 'Numpy Save' tag = 'img' - filt = ['npy'] + filt = 'npy' class Open3D(dataio.Reader): title = 'Numpy 3D Open' tag = 'imgs' - filt = ['npy'] + filt = 'npy' class Save3D(dataio.ImageWriter): title = 'Numpy 3D Save' tag = 'imgs' - filt = ['npy'] + filt = 'npy' note = ['all', 'stack'] plgs = [OpenFile, SaveFile, '-', Open3D, Save3D] \ No newline at end of file diff --git a/imagepy/menus/File/PNG/png_plgs.py b/imagepy/menus/File/PNG/png_plgs.py index 8f1f1d80..99c15052 100644 --- a/imagepy/menus/File/PNG/png_plgs.py +++ b/imagepy/menus/File/PNG/png_plgs.py @@ -7,11 +7,11 @@ class OpenFile(dataio.Reader): title = 'PNG Open' tag = 'img' - filt = ['PNG'] + filt = 'PNG' class SaveFile(dataio.ImageWriter): title = 'PNG Save' tag = 'img' - filt = ['PNG'] + filt = 'PNG' plgs = [OpenFile, SaveFile] \ No newline at end of file diff --git a/imagepy/menus/File/save_plg.py b/imagepy/menus/File/save_plg.py index b72bc0ab..3dece40e 100644 --- a/imagepy/menus/File/save_plg.py +++ b/imagepy/menus/File/save_plg.py @@ -15,7 +15,7 @@ def load(self, ips): class WindowCapture(dataio.ImageWriter): title = 'Save With Mark' - filt = ['PNG'] + filt = 'PNG' def run(self, ips, imgs, para = None): self.app.get_img_win().canvas.save_buffer(para['path']) diff --git a/imagepy/menus/Image/Mark/mark_plgs.py b/imagepy/menus/Image/Mark/mark_plgs.py index ab40e40d..252b0235 100644 --- a/imagepy/menus/Image/Mark/mark_plgs.py +++ b/imagepy/menus/Image/Mark/mark_plgs.py @@ -22,7 +22,7 @@ def load(self, ips): return True def show(self): - self.para['path'] = self.app.getpath('Save..', ['mrk'], 'save') + self.para['path'] = self.app.get_path('Save..', ['mrk'], 'save') return not self.para['path'] is None def run(self, ips, imgs, para = None): @@ -35,7 +35,7 @@ class Open(Simple): para = {'path':''} def show(self): - self.para['path'] = self.app.getpath('Open..', ['mrk'], 'open') + self.para['path'] = self.app.get_path('Open..', ['mrk'], 'open') return not self.para['path'] is None def run(self, ips, imgs, para = None): diff --git a/imagepy/menus/Plugins/Macros/recorder_plg.py b/imagepy/menus/Plugins/Macros/recorder_plg.py index c34a9f82..9de2ded7 100644 --- a/imagepy/menus/Plugins/Macros/recorder_plg.py +++ b/imagepy/menus/Plugins/Macros/recorder_plg.py @@ -9,7 +9,7 @@ def readmc(path): class Macros(dataio.Reader): title = 'Run Macros' tag = 'mc' - filt = ['MC'] + filt = 'MC' def readwf(path): with open(path) as f: return f.read() @@ -19,6 +19,6 @@ def readwf(path): class WorkFlow(dataio.Reader): title = 'Run WorkFlow' tag = 'wf' - filt = ['wf'] + filt = 'wf' plgs = [Macros, WorkFlow] \ No newline at end of file diff --git a/imagepy/menus/Plugins/update_plg.py b/imagepy/menus/Plugins/update_plg.py index 910af0f1..f1cbd7d4 100644 --- a/imagepy/menus/Plugins/update_plg.py +++ b/imagepy/menus/Plugins/update_plg.py @@ -2,6 +2,7 @@ import os, sys, os.path as osp import zipfile, urllib from io import BytesIO +from imagepy import root_dir import shutil if sys.version_info[0]==2: diff --git a/imagepy/menus/Process/Classify/classify_wgt.py b/imagepy/menus/Process/Classify/classify_wgt.py index fd787da9..888b58d7 100644 --- a/imagepy/menus/Process/Classify/classify_wgt.py +++ b/imagepy/menus/Process/Classify/classify_wgt.py @@ -81,7 +81,7 @@ def on_saveas(self, event): return IPy.alert('you must train your model first!') para = {'path':''} filt = '|'.join(['%s files (*.%s)|*.%s'%('FCL', 'fcl', 'fcl')]) - if not IPy.getpath('Save..', filt, 'save', para): return + if not IPy.get_path('Save..', filt, 'save', para): return joblib.dump( manager.model_para, para['path']) def on_export(self, event): @@ -89,7 +89,7 @@ def on_export(self, event): if idx==-1: return IPy.alert('no model selected!') para = {'path':''} filt = '|'.join(['%s files (*.%s)|*.%s'%('FCL', 'fcl', 'fcl')]) - if not IPy.getpath('Save..', filt, 'save', para): return + if not IPy.get_path('Save..', filt, 'save', para): return oldname = osp.join(IPy.root_dir, 'data/ilastik/%s'%self.lst_model.GetStringSelection()) print(para['path']) shutil.copyfile(oldname, para['path']) diff --git a/imagepy/menus/Selection/select_plg.py b/imagepy/menus/Selection/select_plg.py index 85d7c6a3..453befcb 100644 --- a/imagepy/menus/Selection/select_plg.py +++ b/imagepy/menus/Selection/select_plg.py @@ -122,7 +122,7 @@ class Save(Simple): para={'path':''} def show(self): - self.para['path'] = self.app.getpath('ROI Save', ['roi'], 'save') + self.para['path'] = self.app.get_path('ROI Save', ['roi'], 'save') return not self.para['path'] is None def run(self, ips, imgs, para = None): @@ -135,7 +135,7 @@ class Open(Simple): para={'path':''} def show(self): - self.para['path'] = self.app.getpath('ROI Save', ['roi'], 'open') + self.para['path'] = self.app.get_path('ROI Save', ['roi'], 'open') return not self.para['path'] is None def run(self, ips, imgs, para = None): diff --git a/imagepy/menus/Table/Table IO/tableio_plgs.py b/imagepy/menus/Table/Table IO/tableio_plgs.py index 5267b714..de327b9e 100644 --- a/imagepy/menus/Table/Table IO/tableio_plgs.py +++ b/imagepy/menus/Table/Table IO/tableio_plgs.py @@ -13,7 +13,7 @@ def show(data, title): class OpenCSV(dataio.Reader): title = 'CSV Open' tag = 'tab' - filt = ['csv'] + filt = 'csv' class SaveCSV(dataio.TableWriter): title = 'CSV Save' diff --git a/sciapp/action/action.py b/sciapp/action/action.py index be4ecef0..76343901 100644 --- a/sciapp/action/action.py +++ b/sciapp/action/action.py @@ -1,6 +1,8 @@ class SciAction: - name = 'SciAction' - def __init__(self): pass - def start(self, app): - self.app = app - print(self.name, 'started!') + name = 'SciAction' + + def __init__(self): pass + + def start(self, app, para=None, callafter=None): + self.app = app + print(self.name, 'started!') \ No newline at end of file diff --git a/sciapp/action/advanced/dataio.py b/sciapp/action/advanced/dataio.py index dd2222de..f61568b5 100644 --- a/sciapp/action/advanced/dataio.py +++ b/sciapp/action/advanced/dataio.py @@ -14,7 +14,7 @@ class Reader(Free): def show(self): filt = [i.lower() for i in self.filt] - self.para['path'] = self.app.getpath('Open..', filt, 'open', '') + self.para['path'] = self.app.get_path('Open..', filt, 'open', '') return not self.para['path'] is None #process @@ -36,7 +36,7 @@ class ImageWriter(Simple): def show(self): filt = [i.lower() for i in self.filt] - self.para['path'] = self.app.getpath('Save..', filt, 'save', '') + self.para['path'] = self.app.get_path('Save..', filt, 'save', '') return not self.para['path'] is None #process @@ -53,7 +53,7 @@ class TableWriter(Table): def show(self): filt = [i.lower() for i in self.filt] - self.para['path'] = self.app.getpath('Save..', filt, 'save', '') + self.para['path'] = self.app.get_path('Save..', filt, 'save', '') return not self.para['path'] is None #process diff --git a/sciapp/action/advanced/filter.py b/sciapp/action/advanced/filter.py index 8502d3c4..0a01cb2e 100644 --- a/sciapp/action/advanced/filter.py +++ b/sciapp/action/advanced/filter.py @@ -93,7 +93,7 @@ def progress(self, i, n): self.prgs = int(i*100/n) def show(self): preview = lambda para, ips=self.ips: self.preview(ips, para) or ips.update() - return self.app.show_para(self.title, self.view, self.para, preview, + return self.app.show_para(self.title, self.para, self.view, preview, on_ok=lambda : self.ok(self.ips), on_help=self.on_help, on_cancel=lambda : self.cancel(self.ips) or self.ips.update(), preview='preview' in self.note, modal=self.modal) diff --git a/sciapp/action/advanced/free.py b/sciapp/action/advanced/free.py index ddea9616..07d33945 100644 --- a/sciapp/action/advanced/free.py +++ b/sciapp/action/advanced/free.py @@ -25,7 +25,7 @@ def load(self):return True def show(self): if self.view==None:return True - return self.app.show_para(self.title, self.view, self.para, None) + return self.app.show_para(self.title, self.para, self.view, None) def start(self, app, para=None, callback=None): self.app = app diff --git a/sciapp/action/advanced/simple.py b/sciapp/action/advanced/simple.py index da165c98..e444399e 100644 --- a/sciapp/action/advanced/simple.py +++ b/sciapp/action/advanced/simple.py @@ -63,7 +63,7 @@ def progress(self, i, n): self.prgs = int(i*100/n) def show(self): preview = lambda para, ips=self.ips: self.preview(ips, para) or ips.update() - return self.app.show_para(self.title, self.view, self.para, preview, + return self.app.show_para(self.title, self.para, self.view, preview, on_ok=lambda : self.ok(self.ips), on_help=self.on_help, on_cancel=lambda : self.cancel(self.ips) or self.ips.update(), preview='preview' in self.note, modal=self.modal) diff --git a/sciapp/action/advanced/table.py b/sciapp/action/advanced/table.py index 887ff3e1..7f9130af 100644 --- a/sciapp/action/advanced/table.py +++ b/sciapp/action/advanced/table.py @@ -30,7 +30,7 @@ def preview(self, tps, para): def show(self): preview = lambda para, tps=self.tps: self.preview(tps, para) or tps.update() - return self.app.show_para(self.title, self.view, self.para, preview, + return self.app.show_para(self.title, self.para, self.view, preview, on_ok=lambda : self.ok(self.tps), on_help=self.on_help, on_cancel=lambda : self.cancel(self.tps) or self.tps.update(), preview='preview' in self.note, modal=self.modal) diff --git a/sciapp/action/imgact.py b/sciapp/action/imgact.py index 7c90f030..69822678 100644 --- a/sciapp/action/imgact.py +++ b/sciapp/action/imgact.py @@ -9,7 +9,7 @@ def __init__(self): pass def show(self): ips, img, snap = self.ips, self.ips.img, self.ips.snap f = lambda p: self.run(ips, img, snap, p) or self.ips.update() - return self.app.show_para(self.title, self.view, self.para, f, on_ok=None, + return self.app.show_para(self.title, self.para, self.view, f, on_ok=None, on_cancel=lambda x=self.ips:self.cancel(x), preview='preview' in self.note, modal=True) diff --git a/sciapp/action/readme.md b/sciapp/action/readme.md new file mode 100644 index 00000000..ef59122b --- /dev/null +++ b/sciapp/action/readme.md @@ -0,0 +1,115 @@ +# SciApp + +SciApp是一个交互式科学计算的后端框架,主要用于搭建科学分析应用。SciApp并不是算法库,也不包含任何界面,SciApp的目的是为算法类应用提供一个标准接口,具体如下: + +1. 定义了科学计算常用的数据结构封装类 +2. 对数据结构定义了一些基础操作函数 +3. 定义了一个通用Manager,将各种数据进行管理,聚合为一个App对象 +4. 定义了Action对象,可以对App对象进行操作 + + + +### Object模块 + +object模块定义了科学计算中常用的基础数据结构封装类,当然,如果仅仅为了计算,绝大多数时候,Numpy,Pandas等数据类型已经可以胜任,这里的封装,主要是面向交互与展示的,例如Image对象是图像数据,里面带了一个lut成员,用于在展示时映射成伪彩色。 + +1. Image:多维图像,基于Numpy +2. Table:表格,基于DataFrame +3. Shape: 点线面,任意多边形,可与GeoJson,Shapely互转 +4. Surface:三维表面 + + + +### Util模块 + +Util定义了一些针对Object数据类型的基础操作函数,这些函数也是为了完成交互与展示,并非为了数据分析。 + +1. imutil: 主要实现图像的快速采样,多图层融合等算法 + +2. shputil: 主要实现多边形与给定点之间的几何关系,用于鼠标编辑。 + + + +### App容器 + +Manager:通用管理器对象,类似一个键值对管理器,里面装入key, value, tag样式的三元组,可以对元素进行增删查改。 + +App:一个科学容器,里面包含若干Manager,用于管理App所持有的Image,Table等Object,同时定义了一套标准展示接口,例如show_img, get_img, close_img等,(Table,Shape类似) + + + +* [[Manager 对象]](./manager.md) 可以增删查改的对象容器 +* [[App 对象]]() 科研应用接口,各类Object的大容器 + + + +### Action模块 + +Action:对App对象的一个操作,例如获取当前图像,做某种滤波,然后从图像中获取某种信息,最后show一个Table处理。其通用模板是Action().start(app)。可以在其子类中重载start,对app进行任何操作。 + +* [如何衍生出图像滤波类Action]() +* [如何衍生出数据读写类Action]() +* [读取-处理-写入,完成具体工作]() + +以上例子是为了说明Action的作用机制,也展示了框架的构建思路,但并不意味着我们需要按照以上方法,从SciAction构建各种模板,我们已经构建了相当丰富,功能也更为完善的Action派生树,以下进行列举。 + + + +### Action的继承树 + +因为绝大多数的Action都和交互有关,而SciApp自身主要实现对象管理功能,交互只能通过print进行展示,所以很多功能这里只是列距,具体用法我们会在sciwx实现的一个App实例中展示。 + + + +**SciAction:** 所有Action的基类,start内获取app对象,进行处理 + +* ImageAction: 用于处理图像,自动获取当前图像,需要重载para,view进行交互,重载run进行图像处理 + +* TableAction: 用于处理表格,自动获取当前表格,需要重载para,view进行交互,重载run进行图像处理 + +* Tool: 工具,用于在某种控件上的鼠标交互 + + * ImageTool: 图像工具,例如画笔,魔术棒等,需要重载一系列鼠标事件(参数坐标已转入图像坐标系) + + * TableTool:表格工具,需要重载一系列鼠标事件(参数坐标已自动转如单元格行列) + * ShapeTool: 矢量编辑,例如点线面,多边形绘制(参数坐标已自动转如数据坐标系) + +**Advanced:** 这个包下面是一些高级的Action模板,也是我们扩展插件主要使用的 + +* dataio: 里面实现了Reader,Writer类的Action,我们只需要将读写函数注册给对于的Manager +* Free: 继承SciAction,添加了para, view交互,添加了多线程支持。 +* Filter: 继承ImageAction,主要用于做图像滤波,自动多通道,自带批量特性,多线程支持。 +* Simple: 继承ImageAction,主要用于图像操作,自带多线程支持。 +* Table: 继承TableAction,主要用于表格操作,比如表格统计,数据绘图等。 +* Macros: 将一段字符串作为宏执行,构造时传入字符串,start后依次质心。 + +**Plugins:** 这个包下面有一些带有具体功能,开箱即用的Action + +* filters:滤波类 + * Gaussian:高斯滤波 +* generalio: 数据读取类 + * bmp, jpg, png, tif格式图像的读写支持 +* ShapeTool: 矢量图像编辑工具 + * PointEditor: 编辑点 + * LineEditor: 编辑线 + * PolygonEditor: 编辑多边形 + * RectangleEditor: 编辑矩形 + * EllipseEditor: 编辑椭圆 + * FreeLineEditor: 编辑任意线 + * FreePolygonEditor: 编辑任意多边形 + +* ROITool: 类似于Shape,但作用对象是图像上的ROI + * PointEditor: 编辑点 + * LineEditor: 编辑线 + * PolygonEditor: 编辑多边形 + * RectangleEditor: 编辑矩形 + * EllipseEditor: 编辑椭圆 + * FreeLineEditor: 编辑任意线 + * FreePolygonEditor: 编辑任意多边形 + +* MeasureTool: 类似于Shape,绘制同时会展示数值 + * CoordinateTool: 坐标测量 + * DistanceTool: 距离测量 + * AngleTool: 角度测量 + * SlopeTool: 梯度测量 + * AreaTool: 面积测量 \ No newline at end of file diff --git a/sciapp/action/tabact.py b/sciapp/action/tabact.py index b1008773..1a6147b9 100644 --- a/sciapp/action/tabact.py +++ b/sciapp/action/tabact.py @@ -9,7 +9,7 @@ def __init__(self): pass def show(self): tps, data, snap = self.tps, self.tps.data, self.tps.snap f = lambda p: self.run(tps, data, snap, p) or tps.update() - return self.app.show_para(self.title, self.view, self.para, f, on_ok=None, + return self.app.show_para(self.title, self.para, self.view, f, on_ok=None, on_cancel=lambda x=self.tps:self.cancel(x), preview='preview' in self.note, modal=True) diff --git a/sciapp/action/tolact.py b/sciapp/action/tolact.py index ecc5bc62..37c52f5f 100644 --- a/sciapp/action/tolact.py +++ b/sciapp/action/tolact.py @@ -12,7 +12,7 @@ def mouse_down(self, canvas, x, y, btn, **key): pass def mouse_up(self, canvas, x, y, btn, **key): pass def mouse_move(self, canvas, x, y, btn, **key): pass def mouse_wheel(self, canvas, x, y, d, **key): pass - def start(self, app): + def start(self, app, para=None, callafter=None): self.app, self.default = app, self if not app is None: app.tool = self @@ -39,7 +39,7 @@ def mouse_wheel(self, obj, x, y, d, **key): if d>0: key['canvas'].zoomout(x, y, coord='data') if d<0: key['canvas'].zoomin(x, y, coord='data') - def start(self, app): + def start(self, app, para=None): self.app = app Tool.default = self if not app is None: app.tool = self @@ -55,7 +55,7 @@ def mouse_move(self, img, x, y, btn, **key): s = 'x:%d y:%d value:%s'%(x, y, img.img[r,c]) self.app.info(s) - def start(self, app): + def start(self, app, para=None, callafter=None): self.app = app ImageTool.default = self if not app is None: app.tool = self @@ -67,7 +67,7 @@ class ShapeTool(DefaultTool): def mouse_move(self, img, x, y, btn, **key): if self.app: self.app.info('%d, %d'%(x, y)) - def start(self, app): + def start(self, app, para=None, callafter=None): self.app = app ShapeTool.default = self if not app is None: app.tool = self @@ -76,7 +76,7 @@ class TableTool(DefaultTool): default = None title = 'Table Tool' - def start(self, app): + def start(self, app, para=None, callafter=None): self.app = app TableTool.default = self if not app is None: app.tool = self diff --git a/sciapp/app.py b/sciapp/app.py index d5994de9..7b189e44 100644 --- a/sciapp/app.py +++ b/sciapp/app.py @@ -23,6 +23,8 @@ def manager(self, name, value=None): # ========== Plugin ========== def add_plugin(self, name, plg, tag=None): self.plugin_manager.add(name, plg, tag) + for i in ' _.-': name = name.replace(i,'_') + self.__dict__['_%s_'%name] = plg def get_plugin(self, name=None): return self.plugin_manager.get(name) @@ -124,13 +126,12 @@ def yes_no(self, cont, title='ImagePy'): print(title, '\n', cont, 'Y/N?') return input() in 'yY' - def getpath(self, title, filt, io, name=''): + def get_path(self, title='sciapp', filt='', io='save', name=''): print('input file path:') return input() - def show_para(self, title, view, para, on_handle=None, on_ok=None, + def show_para(self, title, para, view, on_handle=None, on_ok=None, on_cancel=None, on_help=None, preview=False, modal=True): - print(title+':') for i in view: if i[0]==str: para[i[1]] = input(i[2]+': ? '+i[3]+' ') if i[0]==int: para[i[1]] = int(input(i[4]+': ? '+i[5]+' ')) @@ -148,7 +149,7 @@ def one(cmds, after): cmd = cmds.pop(0) if not isinstance(cmd, str): title, para = cmd else: title, para = eval(cmd.replace('>', ',')) - plg = self.manager('plugin').get(name=title)() + plg = self.plugin_manager.get(name=title)() after = lambda cmds=cmds: one(cmds, one) if len(cmds)==0: after = callafter plg.start(self, para, after) diff --git a/sciapp/demo/app_test.py b/sciapp/demo/app_test.py deleted file mode 100644 index 27cffb83..00000000 --- a/sciapp/demo/app_test.py +++ /dev/null @@ -1,27 +0,0 @@ -import sys -sys.path.append('../../') - -cmds = [ - ("Open", {'path': 'C:/Users/54631/Desktop/1.jpg'}), - ("Gaussian", {'sigma': 5.0}), - ("Save Image", {'path': 'C:/Users/54631/Desktop/1_blur.bmp'})] - -if __name__ == '__main__': - from sciapp import App - from sciapp.action.plugin import OpenFile, SaveImage - from sciapp.action.plugin.filters import Gaussian - - app = App(asyn=False) - - for i in [OpenFile, SaveImage, Gaussian]: - app.add_plugin(i.title, i) - - ''' - app.alert('Hello, SciApp!') - OpenFile().start(app, {'path': 'C:/Users/54631/Desktop/1.jpg'}) - Gaussian().start(app, {'sigma': 5.0}) - SaveImage().start(app,{'path': 'C:/Users/54631/Desktop/1_blur.bmp'}) - ''' - app.run_macros(cmds) - # C:/Users/54631/Desktop/1.jpg - diff --git a/sciapp/demo/demo1_manager.py b/sciapp/demo/demo1_manager.py new file mode 100644 index 00000000..3788a0fc --- /dev/null +++ b/sciapp/demo/demo1_manager.py @@ -0,0 +1,117 @@ +import sys +sys.path.append('../../') + +from sciapp import Manager + +def add_get_test(): + ''' + add(name, object, tag=None): add one name, object pair (at first). + has(name=None, tag=None, obj=None): check exists, None matches any. + + get(name=None, tag=None): get the first matched object, or None. + None matches any, so get() return the first object. + gets(name=None, tag=None): get all matched [(name, obj, tag)...] + None matches any, so gets() return all records. + remove(name=None, tag=None, obj=None): remove all matched records + None matches any, so remove() would clear the list. + ''' + print('manager test add, get, gets:') + manager = Manager() + # add a name, value pair + manager.add(name='one', obj='object1') + # you can also omit the name, obj + manager.add('two', 'object2') + + print(manager.names()) + # >>> ['two', 'one'] , the later inserted is at first! + print(manager.get()) + # >>> object1 , return the first object, or None + print(manager.get(name='one')) + # >>> object1 , return the first matched object, or None + print(manager.gets()) + # >>> [('two', 'object2', None), ('one', 'object1', None)] + manager.remove(name='two') + # >>> [('one', 'object1', None)] + print(manager.gets()) + print() + +def tag_test(): + ''' + another information for searching. default tag is None. + ''' + print('manager test with tag:') + manager = Manager() + manager.add('one', 'object1', 'low') + manager.add('two', 'object2', 'low') + manager.add('one', 'OBJECT1', 'up') + manager.add('two', 'OBJECT2', 'up') + + print(manager.gets(tag='low')) + # >>> [('two', 'object2', 'low'), ('one', 'object1', 'low')] + print(manager.gets(tag='up')) + # >>> [('two', 'OBJECT2', 'up'), ('one', 'OBJECT1', 'up')] + print() + +def order_test(): + ''' + add method puts object at first, get() means the lasted one. + you can use active to move record to the first. + ''' + print('manager order test:') + manager = Manager() + manager.add('one', 'object1') + manager.add('two', 'object2') + manager.add('three', 'object3') + print(manager.names()) + # >>> ['three', 'two', 'one'] + manager.active('one') + print(manager.names()) + # >>> ['one', 'three', 'two'] + print() + +def unique_test(): + ''' + unique means add a object with same name and tag would replace the old one. + if you want allow the duplicate records, please use unique=False. + ''' + print('manager unique test:') + manager = Manager() + manager.add('one', 'object1') + manager.add('one', 'OBJECT1') + print(manager.gets()) + # >>> [('one', 'OBJECT1', None)] + + manager = Manager(unique=False) + manager.add('one', 'object1') + manager.add('one', 'OBJECT1') + print(manager.gets()) + # >>> [('one', 'OBJECT1', None), ('one', 'object1', None)] + print() + +def io_test(): + ''' + manager object has read and write method to transform with file. + ''' + print('show how to read and write a manager:') + manager = Manager() + manager.add('one', 'object1') + manager.add('two', 'object2') + manager.write('manager.json') + + manager = Manager() + manager.read('manager.json') + print(manager.gets()) + # >>> [('one', 'object1', None), ('two', 'object2', None)] + + # you can also pass path parameter when init a Manager + manager = Manager(path='manager.json') + print(manager.gets()) + # >>> [('one', 'object1', None), ('two', 'object2', None)] + print() + +if __name__ == '__main__': + add_get_test() + tag_test() + order_test() + unique_test() + io_test() \ No newline at end of file diff --git a/sciapp/demo/demo2_app.py b/sciapp/demo/demo2_app.py new file mode 100644 index 00000000..f819c91f --- /dev/null +++ b/sciapp/demo/demo2_app.py @@ -0,0 +1,53 @@ +import sys +sys.path.append('../../') +from sciapp import App + +def basic_test(): + app = App() + # alert a message + app.alert('Hello!', title='SciApp') + # show a text, here just print it + app.show_txt('Hello', title='SciApp') + # show a markdown text, here just print it + app.show_md('Hello', title='SciApp') + # yes or no + rst = app.yes_no('Are you ok?', 'SciApp') + print(rst) + +def para_test(): + app = App() + para = {'name':'', 'age':5} + view = [(str, 'name', 'your', 'name'), + (int, 'age', (0,120), 0, 'your', 'age')] + rst = app.show_para('Personal Information', para, view) + # >>> your: ? name yxdragon + # >>> your: ? age 32 + print(rst) + # >>> {'name':'yxdragon', 'age':32} + +def object_test(): + ''' + there is a manager for every type of object. + such as img_manager, we can call app's: + show_img, close_img, active_img, get_img + ''' + from sciapp.object import Image + from skimage.data import camera + + app = App() + image = Image([camera()], 'camera') + app.show_img(image, 'camera') + # >>> UINT8 512x512 S:1/1 C:0/1 0.25M + print(app.get_img()) + # >>> + print(app.img_names()) + # >>> ['camera'] + app.close_img('camera') + # >>> close image: camera + print(app.img_names()) + # >>> [] + +if __name__ == '__main__': + basic_test() + para_test() + object_test() diff --git a/sciapp/demo/demo3_img_action.py b/sciapp/demo/demo3_img_action.py new file mode 100644 index 00000000..f5ea3677 --- /dev/null +++ b/sciapp/demo/demo3_img_action.py @@ -0,0 +1,136 @@ +import sys +sys.path.append('../../') +from sciapp import App +from sciapp.object import Image +from skimage.data import camera +from scipy.ndimage import gaussian_filter +from skimage.feature import canny +import matplotlib.pyplot as plt + +class SciAction: + '''base action, just has a start method, alert a hello''' + name = 'SciAction' + + def start(self, app, para=None): + self.app = app + app.alert('Hello, I am SciAction!\n') + +def action_demo1(): + app = App() + SciAction().start(app) + +class GaussianAction1(SciAction): + '''get current image object, and do a gaussian filter with sigma 5''' + name = 'GaussianAction1' + + def start(self, app, para=None): + image = app.get_img() + image.img[:] = gaussian_filter(image.img, 5) + +def action_demo2(): + app = App() + + image = Image([camera()], 'camera') + app.show_img(image, 'camera') + GaussianAction1().start(app) + + plt.subplot(121).imshow(camera()) + plt.subplot(122).imshow(image.img) + plt.show() + +class GaussianAction3(SciAction): + '''follow the version 2, use show para to get sigma''' + name = 'GaussianAction3' + + def start(self, app, para=None): + image = app.get_img() + para = {'sigma':5} + view = [(int, 'sigma', (0,30), 0, 'sigma', 'px')] + app.show_para('GaussianAction3', para, view) + image.img[:] = gaussian_filter(image.img, para['sigma']) + +def action_demo3(): + app = App() + + image = Image([camera()], 'camera') + app.show_img(image, 'camera') + GaussianAction3().start(app) + + plt.subplot(121).imshow(camera()) + plt.subplot(122).imshow(image.img) + plt.show() + +class GaussianAction4(SciAction): + '''split para, view to class field, and split gaussian to run method''' + name = 'GaussianAction4' + para = {'sigma':5} + view = [(int, 'sigma', (0,30), 0, 'sigma', 'px')] + + def run(self, img, para): + img[:] = gaussian_filter(img, para['sigma']) + + def start(self, app, para=None): + image = app.get_img() + app.show_para(self.name, self.para, self.view) + self.run(image.img, self.para) + +def action_demo4(): + app = App() + + image = Image([camera()], 'camera') + app.show_img(image, 'camera') + GaussianAction3().start(app) + + plt.subplot(121).imshow(camera()) + plt.subplot(122).imshow(image.img) + plt.show() + +class ImageAction(SciAction): + ''' + this is a general image filter action + we just need to define the para, view + and overwrite the run method + the start method will help us to check if there is a image opened. + and show parameter if needed (para, view is redefined) + then call the run method with current image and input parameter. + ''' + name = 'ImageAction' + para, view = None, None + + def run(self, img, para=None):pass + + def start(self, app, para=None): + image = app.get_img() + if image is None: return app.alert('no image!') + if self.para != None: + app.show_para(self.name, self.para, self.view) + self.run(image.img, self.para) + +class Gaussian(ImageAction): + '''now a gaussian filter should be like this''' + name = 'Gaussian' + para = {'sigma':5} + view = [(int, 'sigma', (0,30), 0, 'sigma', 'px')] + + def run(self, img, para): + img[:] = gaussian_filter(img, para['sigma']) + +def action_demo5(): + app = App() + image = Image([camera()], 'camera') + app.show_img(image, 'camera') + Gaussian().start(app) + + plt.subplot(121).imshow(camera()) + plt.subplot(122).imshow(image.img) + plt.show() + +if __name__ == '__main__': + action_demo1() + action_demo2() + action_demo3() + action_demo4() + action_demo5() + action_demo6() + + diff --git a/sciapp/demo/demo4_io_action.py b/sciapp/demo/demo4_io_action.py new file mode 100644 index 00000000..7eeec4d8 --- /dev/null +++ b/sciapp/demo/demo4_io_action.py @@ -0,0 +1,106 @@ +from sciapp import App, Manager +from sciapp.action import SciAction +import os.path as osp +from skimage.io import imread, imsave +from skimage.data import camera +from pandas import read_csv +import pandas as pd +import numpy as np + +# overwrite the imread, read_csv, to make the demo works +# you can annotation it, but you need give a true path in this demo +def imread(path): return camera() +def read_csv(path): return pd.DataFrame(np.arange(25).reshape(5,5)) + +class ImageReader1(SciAction): + '''read a image and show it''' + name = 'ImageReader1' + + def start(self, app): + path = input('input the file path, or just a.png for test:') + name, ext = osp.splitext(osp.split(path)[1]) + app.show_img([imread(path)], name) + +def image_read_demo1(): + app = App() + ImageReader1().start(app) + print(app.img_names()) + +ReaderManager = Manager() + +class ImageReader(SciAction): + '''supporting different image format''' + name = 'ImageReader' + + def start(self, app): + # input the file path, or just a.png for test + path = app.get_path() + name, ext = osp.splitext(osp.split(path)[1]) + reader = ReaderManager.get(ext[1:]) + if reader is None: + return app.alert('no reader for %s!'%ext[1:]) + app.show_img([reader(path)], name) + +ReaderManager.add('png', imread) +# if you want support other format, just add it to the manager +# ReaderManager.add('dicom', 'xxx') + +def image_read_demo2(): + app = App() + ImageReader().start(app) + +class FileReader(SciAction): + '''supporting different type, image/table or other...''' + name = 'FileReader' + + def start(self, app): + # input the file path, or just a.png/a.csv for test + path = app.get_path() + name, ext = osp.splitext(osp.split(path)[1]) + readers = ReaderManager.gets(ext[1:]) + if len(readers)!=1: + return app.alert('no reader or many readers for %s!'%ext[1:]) + else: key, reader, tag = readers[0] + if tag=='img':app.show_img([reader(path)], name) + if tag=='tab':app.show_table(reader(path), name) + +ReaderManager = Manager() +ReaderManager.add('png', imread, tag='img') +ReaderManager.add('csv', read_csv, tag='tab') + +#ReaderManager.add('dicom', 'xxx') + +def image_read_demo3(): + app = App() + FileReader().start(app) + print('images:', app.img_names(), 'tables:', app.table_names()) + +WriterManager = Manager() + +class ImageWriter(SciAction): + '''write current image''' + name = 'ImageWriter' + + def start(self, app): + img = app.get_img() + if img is None: return app.alert('no image') + # input the file path to save + path = app.get_path() + name, ext = osp.splitext(osp.split(path)[1]) + writer = WriterManager.get(ext[1:]) + if writer is None: + return app.alert('no writer for %s!'%ext[1:]) + writer(path, img.img) + +WriterManager.add('png', imsave) + +def img_write_demo4(): + app = App() + FileReader().start(app) + ImageWriter().start(app) + +if __name__ == '__main__': + #image_read_demo1() + #image_read_demo2() + #image_read_demo3() + img_write_demo4() diff --git a/sciapp/demo/demo5_process_action.py b/sciapp/demo/demo5_process_action.py new file mode 100644 index 00000000..9c4132a2 --- /dev/null +++ b/sciapp/demo/demo5_process_action.py @@ -0,0 +1,73 @@ +from sciapp import App, Manager +from sciapp.action import SciAction +import os.path as osp +from skimage.io import imread, imsave +from skimage.data import camera +from scipy.ndimage import gaussian_filter + +# overwrite the imread, read_csv, to make the demo works +# you can annotation it, but you need give a true path in this demo +def imread(path): return camera() + +ReaderManager = Manager() +ReaderManager.add('png', imread) + +class ImageReader(SciAction): + '''supporting different image format''' + name = 'ImageReader' + + def start(self, app, para=None): + path = input('input the file path, or just a.png for test:') + name, ext = osp.splitext(osp.split(path)[1]) + reader = ReaderManager.get(ext[1:]) + if reader is None: + return app.alert('no reader for %s!'%ext[1:]) + app.show_img([reader(path)], name) + +WriterManager = Manager() +WriterManager.add('png', imsave) + +class ImageWriter(SciAction): + '''write current image''' + name = 'ImageWriter' + + def start(self, app, para=None): + img = app.get_img() + if img is None: return app.alert('no image') + path = input('input the file path to save:') + name, ext = osp.splitext(osp.split(path)[1]) + writer = WriterManager.get(ext[1:]) + if writer is None: + return app.alert('no writer for %s!'%ext[1:]) + writer(path, img.img) + +class ImageAction(SciAction): + name = 'ImageAction' + para, view = None, None + + def run(self, img, para):pass + + def start(self, app, para=None): + image = app.get_img() + if image is None: return app.alert('no image!') + if self.para != None: + app.show_para(self.name, self.para, self.view) + self.run(image.img, self.para) + +class Gaussian(ImageAction): + '''now a gaussian filter should be like this''' + name = 'Gaussian' + para = {'sigma':5} + view = [(int, 'sigma', (0,30), 0, 'sigma', 'px')] + + def run(self, img, para): + img[:] = gaussian_filter(img, para['sigma']) + +def read_gaussian_write(): + app = App() + ImageReader().start(app) + Gaussian().start(app) + ImageWriter().start(app) + +if __name__ == '__main__': + read_gaussian_write() diff --git a/sciapp/demo/demo6_adv_action.py b/sciapp/demo/demo6_adv_action.py new file mode 100644 index 00000000..aba869a5 --- /dev/null +++ b/sciapp/demo/demo6_adv_action.py @@ -0,0 +1,63 @@ +from sciapp.action.advanced import dataio, Filter +from scipy.ndimage import gaussian_filter +from skimage.io import imread, imsave +from skimage.data import camera +from sciapp import App + +def imread(path): return camera() + +for i in ('bmp', 'jpg', 'tif', 'png'): + dataio.ReaderManager.add(i, imread, 'img') + dataio.WriterManager.add(i, imsave, 'img') + +class OpenFile(dataio.Reader): + title = 'Open' + + def load(self): + self.filt = sorted(dataio.ReaderManager.names()) + return True + +class SaveImage(dataio.ImageWriter): + title = 'Save Image' + + def load(self, ips): + self.filt = sorted(dataio.WriterManager.names()) + return True + +class Gaussian(Filter): + title = 'Gaussian' + note = ['all', 'auto_msk', 'auto_snap','preview'] + para = {'sigma':2} + view = [(float, 'sigma', (0,30), 1, 'sigma', 'pix')] + + def run(self, ips, snap, img, para = None): + gaussian_filter(snap, para['sigma'], output=img) + +def io_process_test(): + app = App(asyn=False) + OpenFile().start(app) + Gaussian().start(app) + SaveImage().start(app) + +def macros_test(): + app = App(asyn=False) + for i in [OpenFile, SaveImage, Gaussian]: + app.add_plugin(i.title, i) + + app.run_macros([('Open', None), + ('Gaussian', None), + ('Save Image', None)]) + +def macros_with_para(): + app = App(asyn=False) + for i in [OpenFile, SaveImage, Gaussian]: + app.add_plugin(i.title, i) + + app.run_macros([('Open', {'path':'camera.png'}), + ('Gaussian', {'sigma':2}), + ('Save Image', {'path':'blur.png'})]) + +if __name__ == '__main__': + #io_process_test() + #macros_test() + macros_with_para() diff --git a/sciapp/demo/imagepy_console.py b/sciapp/demo/imagepy_console.py deleted file mode 100644 index 90994fac..00000000 --- a/sciapp/demo/imagepy_console.py +++ /dev/null @@ -1,12 +0,0 @@ -from imagepy.app import Console - -con = Console() -con.load_plugins() - -cmds = [('coins',None), - ('Up And Down Watershed',{'thr1': 29, 'thr2': 178, 'type': 'up area'}), - ('Fill Holes',None), - ('Geometry Filter',{'con': '4-connect', 'inv': False, 'area': 10.0, 'l': 0.0, 'holes': 0, 'solid': 0.0, 'e': 0.0, 'front': 255, 'back': 0}), - ('Geometry Analysis',{'con': '8-connect', 'center': True, 'area': True, 'l': True, 'extent': False, 'cov': True, 'slice': False, 'ed': False, 'holes': False, 'ca': False, 'fa': False, 'solid': False}), - ('PNG Save',{'path': 'C:/Users/54631/Desktop/conis.png'}), - ('CSV Save',{'path': 'C:/Users/54631/Desktop/coins.csv'})] diff --git a/sciapp/doc/cn_app.md b/sciapp/doc/cn_app.md new file mode 100644 index 00000000..2f3d44af --- /dev/null +++ b/sciapp/doc/cn_app.md @@ -0,0 +1,106 @@ +# App + +App是一个科研应用接口,里面有若干Manager,用于管理应用进行中的各种Object,同时App定义了show_img, active_img, close_img, get_img, img_names等方法,对于table等其他类型数据有类似支持。这里的App仅仅实现了容器管理功能,而对于各种交互功能,都只是print实现。因而需要在某个示例中,用UI框架重载这些方法。 + + + +### 方法列举: + +* **alert(self, info, title='sciapp'):** 弹出一个提示框,需要用户确认 + +* **yes_no(self, info, title='sciapp'):** 要求用户输入True/False + +* **show_txt(self, cont, title='sciapp'):** 对用户进行文字提示 + +* **show_md(self, cont, title='sciapp'):** 以MarkDown语法书写,向用户弹出格式化文档 + + --- + +* **show_para(self, title, para, view, on_handle=None, on_ok=None, on_cancel=None, on_help=None, preview=False, modal=True):** 展示交互对话框,para是参数字典,view指定了交互方式。而在这个命令行版的App对象中,只能通过打印完成交互,因而其他参数这里没有作用。但App是一个交互式应用接口,对于界面应用,其他参数分别是,参数变化回调,对话框确认,取消回调,是否自动添加预览选项,对话框是否以模态方式展示 + + --- + + **以下功能通过app.img_manager管理器实现** + +* **show_img(self, img, title=None):** 展示一个Image对象,并添加到app.img_manager。 + +* **get_img(self, title=None):** 根据title获取Image,如果缺省则返回manager的第一个Image + +* **img_names(self):** 返回当前app持有的Image对象名称列表 + +* **active_img(self, title=None):** 将指定名称的Image对象置顶,以便于get_img可以优先获得 + +* **close_img(self, title=None):** 关闭指定图像,并从app.img_manager移除 + + --- + **以下功能通过app.table_manager管理器实现** + +* **show_table(self, tab, title=None):** 展示一个Table对象,并添加到app.tab_manager。 + +* **get_table(self, title=None):** 根据title获取Table,如果缺省则返回manager的第一个Table + +* **table_names(self):** 返回当前app持有的Table对象名称列表 + +* **active_table(self, title=None):** 将指定名称的Table对象置顶,以便于get_tab可以优先获得 + +* **close_table(self, title=None):** 关闭指定图像,并从app.tab_manager移除 + + + +### 用法举例 + +这个例子演示app的一些基础交互功能,由于命令行只有简单的打印功能,因而这几个功能这里本质都是print + +```python +def basic_test(): + app = App() + # alert a message + app.alert('Hello!', title='SciApp') + # show a text, here just print it + app.show_txt('Hello', title='SciApp') + # show a markdown text, here just print it + app.show_md('Hello', title='SciApp') + # yes or no + rst = app.yes_no('Are you ok?', 'SciApp') + print(rst) +``` + + + +这个例子演示app进行一组参数交互 + +```python +def para_test(): + app = App() + para = {'name':'', 'age':5} + view = [(str, 'name', 'your', 'name'), + (int, 'age', (0,120), 0, 'your', 'age')] + rst = app.show_para('Personal Information', para, view) + # >>> your: ? name yxdragon + # >>> your: ? age 32 + print(rst) + # >>> {'name':'yxdragon', 'age':32} +``` + + + +这个例子用Image说明,通过app对象的show,get,close方法来展示图像 + +```python +def object_test(): + from sciapp.object import Image + from skimage.data import camera + + app = App() + image = Image([camera()], 'camera') + app.show_img(image, 'camera') + # >>> UINT8 512x512 S:1/1 C:0/1 0.25M + print(app.get_img()) + # >>> + print(app.img_names()) + # >>> ['camera'] + app.close_img('camera') + # >>> close image: camera + print(app.img_names()) + # >>> [] +``` diff --git a/sciapp/doc/cn_imgaction.md b/sciapp/doc/cn_imgaction.md new file mode 100644 index 00000000..cc7154bb --- /dev/null +++ b/sciapp/doc/cn_imgaction.md @@ -0,0 +1,137 @@ +# ImageAction + +App是科学应用容器,具备各种数据类型的展示,关闭功能,并对其进行管理。SciAction是对app对象的某种操作,而后续SciApp功能的丰富,主要就是依赖于Action的丰富。 + + + +### 原始SciAction + +SciAction是对app对象的某种操作,接口函数非常简单,只有一个 **start(self, app, para)**,你可以在start内对app对象进行各种操作。而原始的SciApp只是用app对象弹出了一个提示。 + +```python +class SciAction: + '''base action, just has a start method, alert a hello''' + name = 'SciAction' + + def start(self, app, para=None): + self.app = app + app.alert('Hello, I am SciAction!\n') + +app = App() +SciAction().start(app) +``` + + + +### 用SciAction实现滤波 + +这里我们在start内,获取当前图像,并对齐进行一个sigma为5的高斯滤波 + +```python +class GaussianAction1(SciAction): + name = 'GaussianAction1' + + def start(self, app, para=None): + image = app.get_img() + image.img[:] = gaussian_filter(image.img, 5) + +app = App() +from skimage.data import camera +app.show_img([camera()], 'camera') +GaussianAction1().start(app) +``` + + + +### 带参数交互的滤波 + +这里我们使用app.show_para来和用户进行交互,获取参数。当然,这个例子中,我们可以使用python自带的input函数,然而app对象作为通用接口,我们在action中进来使用通用函数,以便在App对象的界面实现中依然可以使用。 + +```python +class GaussianAction3(SciAction): + name = 'GaussianAction3' + + def start(self, app, para=None): + image = app.get_img() + para = {'sigma':5} + view = [(int, 'sigma', (0,30), 0, 'sigma', 'px')] + app.show_para('GaussianAction3', para, view) + image.img[:] = gaussian_filter(image.img, para['sigma']) + +app = App() +from skimage.data import camera +app.show_img([camera()], 'camera') +GaussianAction3().start(app) +``` + + + +### 参数标准化分离 + +在上一个例子的基础上,我们将para,view提升为类变量,把滤波函数提出来,定义为run函数,这样做的好处是,start部分就相对固定化了。 + +```python +class GaussianAction4(SciAction): + name = 'GaussianAction4' + para = {'sigma':5} + view = [(int, 'sigma', (0,30), 0, 'sigma', 'px')] + + def run(self, img, para): + img[:] = gaussian_filter(img, para['sigma']) + + def start(self, app, para=None): + image = app.get_img() + app.show_para(self.name, self.para, self.view) + self.run(image.img, self.para) + +app = App() +from skimage.data import camera +app.show_img([camera()], 'camera') +GaussianAction4().start(app) +``` + + + +### ImageAction 初步版 + +上一个版本已经非常完善,我们把具体功能移除,形成一个抽象版的ImageAction,start内做如下事情: + +1. 获取当前图像,如果没有打开图像则弹出提示,终止后续 +2. 判断是否有参数,如果有则调用show_para进行交互 +3. 将当前图像和交互完成后的para送入run进行处理 + +```python +class ImageAction(SciAction): + name = 'ImageAction' + para, view = None, None + + def run(self, img, para=None):pass + + def start(self, app, para=None): + image = app.get_img() + if image is None: return app.alert('no image!') + if self.para != None: + app.show_para(self.name, self.para, self.view) + self.run(image.img, self.para) +``` + +现在我们的Gaussian滤波器写法成了这样,只需继承ImageAction,定义para,view,然后重载run函数即可。 + +```python +class Gaussian(ImageAction): + '''now a gaussian filter should be like this''' + name = 'Gaussian' + para = {'sigma':5} + view = [(int, 'sigma', (0,30), 0, 'sigma', 'px')] + + def run(self, img, para): + img[:] = gaussian_filter(img, para['sigma']) + +app = App() +from skimage.data import camera +app.show_img([camera()], 'camera') +Gaussian().start(app) +``` + +**备注:**本章只是为了讲解Action如何作用,并且如何通过逐层继承,使得Action子类分化出越来越具体的功能,但并不是说我们需要按照这里的方法从底层搭建Action,况且这里的ImageAction依然不够完善,比如可以继续添加图像类型的判定,多线程支持等。好在advanced里面已经定义好了更为丰富的Action模板,我们可以直接继承高级模板。 + diff --git a/sciapp/doc/cn_ioaction.md b/sciapp/doc/cn_ioaction.md new file mode 100644 index 00000000..db01f89a --- /dev/null +++ b/sciapp/doc/cn_ioaction.md @@ -0,0 +1,88 @@ +# IOAction + +继上一篇ImageAction之后,其实可以类似的拓展到TableAction,然而上一篇的例子中的图像,我们都是利用代码show,进而载入到app中的,本章我们就来编写数据读写的IOAction。 + + + +### 原始实现 + +我们在start内部获取用户输入的路径,然后解析出文件名和扩展名,调用app.show_img进行展示 + +```python +from skimage.io import imread + +class ImageReader1(SciAction): + '''read a image and show it''' + name = 'ImageReader1' + + def start(self, app, para=None): + path = input('input the file path, or just a.png for test:') + name, ext = osp.splitext(osp.split(path)[1]) + app.show_img([imread(path)], name) + +app = App() +ImageReader1().start(app) +``` + + + +### 支持多种格式数据 + +这个例子我们通过扩展名对读取方法进行管理,放入ReadManager,这样当添加新的读取格式时,只需将扩展名和读取方法加入ReaderManager。同样我们这里使用了get_path函数来获取路径,这同样是为了后续界面版本的App可以通用。 + +```python +from skimage.io import imread +ReaderManager = Manager() + +class ImageReader(SciAction): + name = 'ImageReader' + + def start(self, app, para=None): + # input the file path, or just a.png for test + path = app.get_path() + name, ext = osp.splitext(osp.split(path)[1]) + reader = ReaderManager.get(ext[1:]) + if reader is None: + return app.alert('no reader for %s!'%ext[1:]) + app.show_img([reader(path)], name) + +ReaderManager.add('png', imread) +ReaderManager.add('jpg', imread) +# ReaderManager.add('dcm', read_dicom) other format + +app = App() +ImageReader().start(app) +``` + + + +### ImageWriter + +保存操作和打开类似,不同的是需要获取当前图像,其实某种意义上,可以当作一个ImageAction。 + +```python +from skimage.io import imsave +WriterManager = Manager() + +class ImageWriter(SciAction): + '''write current image''' + name = 'ImageWriter' + + def start(self, app): + img = app.get_img() + if img is None: return app.alert('no image') + # input the file path to save + path = app.get_path() + name, ext = osp.splitext(osp.split(path)[1]) + writer = WriterManager.get(ext[1:]) + if writer is None: + return app.alert('no writer for %s!'%ext[1:]) + writer(path, img.img) + +WriterManager.add('png', imsave) +``` + + + +**备注:**本章只是为了讲解Action如何作用,并且如何通过逐层继承,使得Action子类分化出越来越具体的功能,但并不是说我们需要按照这里的方法从底层搭建Action,况且这里的IOAction依然不够完善,具体我们可以直接使用advanced.dataio模板。 + diff --git a/sciapp/doc/cn_iprocesso.md b/sciapp/doc/cn_iprocesso.md new file mode 100644 index 00000000..96a3d202 --- /dev/null +++ b/sciapp/doc/cn_iprocesso.md @@ -0,0 +1,94 @@ +# Process Demo + +我们这里用一个具体的例子,演示如何读取图像,滤波处理,保存图像。我们结合前两节的内容, + + + +### IO操作 + +如同前一节,我们添加ImageReader, ImageWriter。 + +```python +from sciapp import App, Manager +from sciapp.action import SciAction +import os.path as osp +from skimage.io import imread, imsave +from skimage.data import camera +from scipy.ndimage import gaussian_filter + +ReaderManager = Manager() +ReaderManager.add('png', imread) + +class ImageReader(SciAction): + '''supporting different image format''' + name = 'ImageReader' + + def start(self, app, para=None): + path = input('input the file path, or just a.png for test:') + name, ext = osp.splitext(osp.split(path)[1]) + reader = ReaderManager.get(ext[1:]) + if reader is None: + return app.alert('no reader for %s!'%ext[1:]) + app.show_img([reader(path)], name) + +WriterManager = Manager() +WriterManager.add('png', imsave) + +class ImageWriter(SciAction): + '''write current image''' + name = 'ImageWriter' + + def start(self, app, para=None): + img = app.get_img() + if img is None: return app.alert('no image') + path = input('input the file path to save:') + name, ext = osp.splitext(osp.split(path)[1]) + writer = WriterManager.get(ext[1:]) + if writer is None: + return app.alert('no writer for %s!'%ext[1:]) + writer(path, img.img) +``` + + + +### Gaussian滤镜 + +如同前一节,我们添加Gaussian滤波器。然后我们依次start三个Action,按照提示,输入读取图像路径,高斯滤波sigma,输出图像路径,最后完成。 + +```python +class ImageAction(SciAction): + name = 'ImageAction' + para, view = None, None + + def run(self, img, para):pass + + def start(self, app, para=None): + image = app.get_img() + if image is None: return app.alert('no image!') + if self.para != None: + app.show_para(self.name, self.para, self.view) + self.run(image.img, self.para) + +class Gaussian(ImageAction): + '''now a gaussian filter should be like this''' + name = 'Gaussian' + para = {'sigma':5} + view = [(int, 'sigma', (0,30), 0, 'sigma', 'px')] + + def run(self, img, para): + img[:] = gaussian_filter(img, para['sigma']) + + +app = App() +ImageReader().start(app) +Gaussian().start(app) +ImageWriter().start(app) + +# >>> input the file path, or just a.png for test:camera.png +# >>> UINT8 512x512 S:1/1 C:0/1 0.25M +# >>> sigma: ? px 5 +# >>> input the file path to save:blur.png +``` + +**备注:**本章只是为了讲解Action如何作用,并且如何通过逐层继承,使得Action子类分化出越来越具体的功能,但并不是说我们需要按照这里的方法从底层搭建Action,况且这里的IOAction依然不够完善,具体我们可以直接使用advanced.dataio模板。 + diff --git a/sciapp/doc/cn_manager.md b/sciapp/doc/cn_manager.md new file mode 100644 index 00000000..39cd2a8a --- /dev/null +++ b/sciapp/doc/cn_manager.md @@ -0,0 +1,139 @@ +# Manager + +Manager是一个通用容器,类似一个内存数据库,负责对象的增删查改 + + + +### 方法列举: + +* **add(self, name, obj, tag=None):** 添加一个对象,tag作为可选标签,对象添加在有序列表的最前面 + +* **adds(self, objs): ** 批量添加name, obj, tag列表 + +* **gets(self, name=None, tag=None, obj=None):** 获取满足条件的对象,返回name, obj, tag列表,None表示不作为条件 + +* **get(self, name=None, tag=None, obj=None):** 获取满足条件的第一个object,如果没有满足返回None,空缺参数用来获取第一个对象。 + +* **has(self, name=None, tag=None, obj=None):** 返回是否存在匹配条件的对象 + +* **active(self, name=None, tag=None, obj=None):** 将满足筛选条件的记录移动到列表最前 + +* **set(self, name, obj, tag=None):** 通过name,tag筛选记录,将obj赋值 + +* **remove(self, name=None, tag=None, obj=None):** 删除满足条件的记录 + +* **names(self, tag=None):** 返回所有的name列表 + +* **write(self, path=None):** 将记录写入文件,只有可以json序列号的对象才支持写入。 + +* **def read(self, path):** 从文件读取记录到当前manager + + + +### 用法举例 + +这个例子演示如何使用add, get + +```python +def add_get_test(): + print('manager test add, get, gets:') + manager = Manager() + # add a name, value pair + manager.add(name='one', obj='object1') + # you can also omit the name, obj + manager.add('two', 'object2') + + print(manager.names()) + # >>> ['two', 'one'] , the later inserted is at first! + print(manager.get()) + # >>> object1 , return the first object, or None + print(manager.get(name='one')) + # >>> object1 , return the first matched object, or None + print(manager.gets()) + # >>> [('two', 'object2', None), ('one', 'object1', None)] + manager.remove(name='two') + print(manager.gets()) + # >>> [('one', 'object1', None)] +``` + + + +这个例子演示如何利用tag进行筛选 + +```python +def tag_test(): + print('manager test with tag:') + manager = Manager() + manager.add('one', 'object1', 'low') + manager.add('two', 'object2', 'low') + manager.add('one', 'OBJECT1', 'up') + manager.add('two', 'OBJECT2', 'up') + + print(manager.gets(tag='low')) + # >>> [('two', 'object2', 'low'), ('one', 'object1', 'low')] + print(manager.gets(tag='up')) + # >>> [('two', 'OBJECT2', 'up'), ('one', 'OBJECT1', 'up')] +``` + + + +这个例子说明容器内部是按照添加的倒叙存放的,调用active可以将满足条件的元素置顶。 + +```python +def order_test(): + print('manager order test:') + manager = Manager() + manager.add('one', 'object1') + manager.add('two', 'object2') + manager.add('three', 'object3') + print(manager.names()) + # >>> ['three', 'two', 'one'] + manager.active('one') + print(manager.names()) + # >>> ['one', 'three', 'two'] +``` + + + +这个例子演示了默认情况下,相同name, tag的对象重复添加,会覆盖之前的记录,但是在构造时用unique=False则允许重复添加。 + +```python +def unique_test(): + print('manager unique test:') + manager = Manager() + manager.add('one', 'object1') + manager.add('one', 'OBJECT1') + print(manager.gets()) + # >>> [('one', 'OBJECT1', None)] + + manager = Manager(unique=False) + manager.add('one', 'object1') + manager.add('one', 'OBJECT1') + print(manager.gets()) + # >>> [('one', 'OBJECT1', None), ('one', 'object1', None)] +``` + + + +manager对象可以write到文件,也可以从文件read,构造时如果传入path参数,则自动从路径进行read。记录为name, obj, tag的列表,以json格式存储,因此manager可以读写的条件是,所有元素都可以json化。 + +```python +def io_test(): + print('show how to read and write a manager:') + manager = Manager() + manager.add('one', 'object1') + manager.add('two', 'object2') + manager.write('manager.json') + + manager = Manager() + manager.read('manager.json') + print(manager.gets()) + # >>> [('one', 'object1', None), ('two', 'object2', None)] + + # you can also pass path parameter when init a Manager + manager = Manager(path='manager.json') + print(manager.gets()) + # >>> [('one', 'object1', None), ('two', 'object2', None)] + print() +``` + diff --git a/sciapp/doc/cn_readme.md b/sciapp/doc/cn_readme.md new file mode 100644 index 00000000..af084fac --- /dev/null +++ b/sciapp/doc/cn_readme.md @@ -0,0 +1,115 @@ +# SciApp + +SciApp是一个交互式科学计算的后端框架,主要用于搭建科学分析应用。SciApp并不是算法库,也不包含任何界面,SciApp的目的是为算法类应用提供一个标准接口,具体如下: + +1. 定义了科学计算常用的数据结构封装类 +2. 对数据结构定义了一些基础操作函数 +3. 定义了一个通用Manager,将各种数据进行管理,聚合为一个App对象 +4. 定义了Action对象,可以对App对象进行操作 + +![SciApp](https://user-images.githubusercontent.com/24822467/86324215-cd99f700-bc70-11ea-8851-1de44e313a1f.png) + +### Object模块 + +object模块定义了科学计算中常用的基础数据结构封装类,当然,如果仅仅为了计算,绝大多数时候,Numpy,Pandas等数据类型已经可以胜任,这里的封装,主要是面向交互与展示的,例如Image对象是图像数据,里面带了一个lut成员,用于在展示时映射成伪彩色。 + +1. Image:多维图像,基于Numpy +2. Table:表格,基于DataFrame +3. Shape: 点线面,任意多边形,可与GeoJson,Shapely互转 +4. Surface:三维表面 + + + +### Util模块 + +Util定义了一些针对Object数据类型的基础操作函数,这些函数也是为了完成交互与展示,并非为了数据分析。 + +1. imutil: 主要实现图像的快速采样,多图层融合等算法 + +2. shputil: 主要实现多边形与给定点之间的几何关系,用于鼠标编辑。 + + + +### App容器 + +Manager:通用管理器对象,类似一个键值对管理器,里面装入key, value, tag样式的三元组,可以对元素进行增删查改。 + +App:一个科学容器,里面包含若干Manager,用于管理App所持有的Image,Table等Object,同时定义了一套标准展示接口,例如show_img, get_img, close_img等,(Table,Shape类似) + + + +* [[Manager 对象]](./cn_manager) 可以增删查改的对象容器 +* [[App 对象]](./cn_app) 科研应用接口,各类Object的大容器 + + + +### Action模块 + +Action:对App对象的一个操作,例如获取当前图像,做某种滤波,然后从图像中获取某种信息,最后show一个Table处理。其通用模板是Action().start(app)。可以在其子类中重载start,对app进行任何操作。 + +* [如何衍生出图像滤波类Action](./cn_imgaction) +* [如何衍生出数据读写类Action](./cn_ioaction) +* [读取-处理-写入,完成具体工作](./cn_iprocesso) + +以上例子是为了说明Action的作用机制,也展示了框架的构建思路,但并不意味着我们需要按照以上方法,从SciAction构建各种模板,我们已经构建了相当丰富,功能也更为完善的Action派生树,以下进行列举。 + + + +### Action的继承树 + +因为绝大多数的Action都和交互有关,而SciApp自身主要实现对象管理功能,交互只能通过print进行展示,所以很多功能这里只是列距,具体用法我们会在sciwx实现的一个App实例中展示。 + + + +**SciAction:** 所有Action的基类,start内获取app对象,进行处理 + +* ImageAction: 用于处理图像,自动获取当前图像,需要重载para,view进行交互,重载run进行图像处理 + +* TableAction: 用于处理表格,自动获取当前表格,需要重载para,view进行交互,重载run进行图像处理 + +* Tool: 工具,用于在某种控件上的鼠标交互 + + * ImageTool: 图像工具,例如画笔,魔术棒等,需要重载一系列鼠标事件(参数坐标已转入图像坐标系) + + * TableTool:表格工具,需要重载一系列鼠标事件(参数坐标已自动转如单元格行列) + * ShapeTool: 矢量编辑,例如点线面,多边形绘制(参数坐标已自动转如数据坐标系) + +**Advanced:** 这个包下面是一些高级的Action模板,也是我们扩展插件主要使用的 + +* dataio: 里面实现了Reader,Writer类的Action,我们只需要将读写函数注册给对于的Manager +* Free: 继承SciAction,添加了para, view交互,添加了多线程支持。 +* Filter: 继承ImageAction,主要用于做图像滤波,自动多通道,自带批量特性,多线程支持。 +* Simple: 继承ImageAction,主要用于图像操作,自带多线程支持。 +* Table: 继承TableAction,主要用于表格操作,比如表格统计,数据绘图等。 +* Macros: 将一段字符串作为宏执行,构造时传入字符串,start后依次质心。 + +**Plugins:** 这个包下面有一些带有具体功能,开箱即用的Action + +* filters:滤波类 + * Gaussian:高斯滤波 +* generalio: 数据读取类 + * bmp, jpg, png, tif格式图像的读写支持 +* ShapeTool: 矢量图像编辑工具 + * PointEditor: 编辑点 + * LineEditor: 编辑线 + * PolygonEditor: 编辑多边形 + * RectangleEditor: 编辑矩形 + * EllipseEditor: 编辑椭圆 + * FreeLineEditor: 编辑任意线 + * FreePolygonEditor: 编辑任意多边形 + +* ROITool: 类似于Shape,但作用对象是图像上的ROI + * PointEditor: 编辑点 + * LineEditor: 编辑线 + * PolygonEditor: 编辑多边形 + * RectangleEditor: 编辑矩形 + * EllipseEditor: 编辑椭圆 + * FreeLineEditor: 编辑任意线 + * FreePolygonEditor: 编辑任意多边形 + +* MeasureTool: 类似于Shape,绘制同时会展示数值 + * CoordinateTool: 坐标测量 + * DistanceTool: 距离测量 + * AngleTool: 角度测量 + * SlopeTool: 梯度测量 + * AreaTool: 面积测量 \ No newline at end of file diff --git a/sciapp/doc/readme.md b/sciapp/doc/readme.md deleted file mode 100644 index e4721728..00000000 --- a/sciapp/doc/readme.md +++ /dev/null @@ -1,42 +0,0 @@ -# SciApp - -SciApp是一个交互式科学计算的后端框架,主要用于搭建科学分析应用。SciApp并不是算法库,也不包含任何界面,SciApp的目的是为算法类应用提供一个标准接口,具体如下: - -1. 定义了科学计算常用的数据结构封装类 -2. 对数据结构定义了一些基础操作函数 -3. 定义了一个通用Manager,一个App,将各种数据进行管理 -4. 定义了Action对象,可以对App进行操作 - - - -### Object模块 - -object模块定义了科学计算中常用的基础数据结构封装类,当然,如果仅仅为了计算,绝大多数时候,Numpy,Pandas等数据类型已经可以胜任,这里的封装,主要是面向交互与展示的,例如Image对象是图像数据,里面带了一个lut成员,用于在展示时映射成伪彩色。 - -1. Image:多维图像,基于Numpy - -2. Table:表格,基于DataFrame - -3. Shape: 点线面,任意多边形,基于Shapely - -4. Surface:三维表面 - - - -### Util模块 - -Util定义了一些针对Object数据类型的基础操作函数,这些函数也是为了完成交互与展示,并非为了数据分析。 - -1. imutil: 主要实现图像的快速采样,多图层融合等算法 - -2. shputil: 主要实现多边形与给定点之间的几何关系,用于鼠标编辑。 - - - -### Action模块 - -App: - -\1. Manager:Manager是一个通用容器,类似于mongodb的用法,具有add,get, - -Sciapp: \ No newline at end of file diff --git a/sciwx/app/imgapp.py b/sciwx/app/imgapp.py index 5a84ed86..b50bb89e 100644 --- a/sciwx/app/imgapp.py +++ b/sciwx/app/imgapp.py @@ -152,7 +152,7 @@ def yes_no(self, info, title='ImagePy'): dic = {wx.ID_YES:'yes', wx.ID_NO:'no', wx.ID_CANCEL:'cancel'} return dic[rst] - def getpath(self, title, filt, io, name=''): + def get_path(self, title, filt, io, name=''): filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in filt]) dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} dialog = wx.FileDialog(self, title, '', name, filt, dic[io]) diff --git a/sciwx/app/miniapp.py b/sciwx/app/miniapp.py index f946cc68..c7cb9a17 100644 --- a/sciwx/app/miniapp.py +++ b/sciwx/app/miniapp.py @@ -295,7 +295,7 @@ def yes_no(self, info, title='ImagePy'): dic = {wx.ID_YES:'yes', wx.ID_NO:'no', wx.ID_CANCEL:'cancel'} return dic[rst] - def getpath(self, title, filt, io, name=''): + def get_path(self, title, filt, io, name=''): filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in filt]) dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} dialog = wx.FileDialog(self, title, '', name, filt, dic[io]) diff --git a/sciwx/plugins/io.py b/sciwx/plugins/io.py index b6b5068f..1f0b3da6 100644 --- a/sciwx/plugins/io.py +++ b/sciwx/plugins/io.py @@ -4,7 +4,7 @@ class Open(SciAction): name = 'Open' def start(self, app, para=None): - path = app.getpath('Open', ['png','bmp','jpg'], 'open') + path = app.get_path('Open', ['png','bmp','jpg'], 'open') if path is None: return app.show_img(imread(path)) @@ -13,7 +13,7 @@ class Save(ImgAction): para = {'path':''} def show(self): - path = self.app.getpath('Open', ['png','bmp','jpg'], 'save') + path = self.app.get_path('Open', ['png','bmp','jpg'], 'save') if path is None: return self.para['path'] = path return True diff --git a/sciwx/widgets/normal.py b/sciwx/widgets/normal.py index 397822b8..e67323ac 100644 --- a/sciwx/widgets/normal.py +++ b/sciwx/widgets/normal.py @@ -16,7 +16,7 @@ def __init__(self, parent, rang, accury, title, unit, app=None): sizer.Add( self.ctrl, 2, wx.ALL, 5 ) self.postfix = lab_unit = wx.StaticText( self, wx.ID_ANY, unit, - wx.DefaultPosition, wx.DefaultSize) + wx.DefaultPosition, wx.DefaultSize) lab_unit.Wrap( -1 ) sizer.Add( lab_unit, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) @@ -139,12 +139,12 @@ def GetValue(self): return wx.Colour(rgb).Get(False)[::-1] class PathCtrl(wx.Panel): - def __init__(self, parent, title, filt, app=None): + def __init__(self, parent, title, filt, io, app=None): wx.Panel.__init__(self, parent) sizer = wx.BoxSizer( wx.HORIZONTAL ) self.prefix = lab_title = wx.StaticText( self, wx.ID_ANY, title, wx.DefaultPosition, wx.DefaultSize) - self.filt = filt + self.filt, self.io = filt, io lab_title.Wrap( -1 ) sizer.Add( lab_title, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) self.ctrl = wx.TextCtrl(self, wx.TE_RIGHT) @@ -159,12 +159,12 @@ def Bind(self, z, f): self.f = f def ontext(self, event): print('ColorCtrl') def onselect(self, event): - filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in self.filt.split(',')]) - #dpath = ConfigManager.get('defaultpath') or '.' - #if dpath==None: dpath = root_dir # './' - if self.filt == '': - dialog = wx.DirDialog(self, 'Path Select', '.', wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST) - else: dialog = wx.FileDialog(self, 'Path Select', dpath, '', filt, wx.FD_OPEN) + if isinstance(self.filt, str): self.filt = self.filt.split(',') + filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in self.filt]) + dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} + if self.io=='folder': + dialog = wx.DirDialog(self, 'Path Select', '.', wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST | wx.FD_CHANGE_DIR) + else: dialog = wx.FileDialog(self, 'Path Select', dpath, '', filt, dict[self.io] | wx.FD_CHANGE_DIR) rst = dialog.ShowModal() if rst == wx.ID_OK: path = dialog.GetPath() diff --git a/sciwx/widgets/util.py b/sciwx/widgets/util.py index 8495c0c0..3328b1b1 100644 --- a/sciwx/widgets/util.py +++ b/sciwx/widgets/util.py @@ -5,7 +5,7 @@ def alert(info, title='SciWx'): dialog.ShowModal() == wx.ID_OK dialog.Destroy() -def getpath(title, filt, io, name=''): +def get_path(title, filt, io, name=''): filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in filt]) dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} dialog = wx.FileDialog(None, title, '', name, filt, dic[io]) @@ -22,6 +22,6 @@ def getpath(title, filt, io, name=''): frame2 = wx.Frame(None) frame2.Show() - path = getpath('file', ['png','bmp'], 'save') + path = get_path('file', ['png','bmp'], 'save') print(path) app.MainLoop() \ No newline at end of file From 97cdbd89b70e9cc34f2df3bf816f4fbc7073f6fe Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 2 Jul 2020 14:47:08 +0800 Subject: [PATCH 286/343] sciapp doc --- sciapp/demo/demo3_img_action.py | 2 +- sciapp/doc/cn_readme.md | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sciapp/demo/demo3_img_action.py b/sciapp/demo/demo3_img_action.py index f5ea3677..813285ba 100644 --- a/sciapp/demo/demo3_img_action.py +++ b/sciapp/demo/demo3_img_action.py @@ -19,7 +19,7 @@ def action_demo1(): app = App() SciAction().start(app) -class GaussianAction1(SciAction): +class GaussianAction2(SciAction): '''get current image object, and do a gaussian filter with sigma 5''' name = 'GaussianAction1' diff --git a/sciapp/doc/cn_readme.md b/sciapp/doc/cn_readme.md index af084fac..763f90d4 100644 --- a/sciapp/doc/cn_readme.md +++ b/sciapp/doc/cn_readme.md @@ -38,8 +38,8 @@ App:一个科学容器,里面包含若干Manager,用于管理App所持有 -* [[Manager 对象]](./cn_manager) 可以增删查改的对象容器 -* [[App 对象]](./cn_app) 科研应用接口,各类Object的大容器 +* [[Manager 对象]](./cn_manager.md) 可以增删查改的对象容器 +* [[App 对象]](./cn_app.md) 科研应用接口,各类Object的大容器 @@ -47,9 +47,9 @@ App:一个科学容器,里面包含若干Manager,用于管理App所持有 Action:对App对象的一个操作,例如获取当前图像,做某种滤波,然后从图像中获取某种信息,最后show一个Table处理。其通用模板是Action().start(app)。可以在其子类中重载start,对app进行任何操作。 -* [如何衍生出图像滤波类Action](./cn_imgaction) -* [如何衍生出数据读写类Action](./cn_ioaction) -* [读取-处理-写入,完成具体工作](./cn_iprocesso) +* [如何衍生出图像滤波类Action](./cn_imgaction.md) +* [如何衍生出数据读写类Action](./cn_ioaction.md) +* [读取-处理-写入,完成具体工作](./cn_iprocesso.md) 以上例子是为了说明Action的作用机制,也展示了框架的构建思路,但并不意味着我们需要按照以上方法,从SciAction构建各种模板,我们已经构建了相当丰富,功能也更为完善的Action派生树,以下进行列举。 From d6074d94c25ba4d5faa76fa99acb83ba75d5e8da Mon Sep 17 00:00:00 2001 From: qixinbo Date: Thu, 2 Jul 2020 15:38:52 +0800 Subject: [PATCH 287/343] label widget update --- imagepy/menus/Process/Classify/label_wgt.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/imagepy/menus/Process/Classify/label_wgt.py b/imagepy/menus/Process/Classify/label_wgt.py index 796a30e7..e63495b2 100644 --- a/imagepy/menus/Process/Classify/label_wgt.py +++ b/imagepy/menus/Process/Classify/label_wgt.py @@ -1,5 +1,5 @@ -# from imagepy.ui.widgets import CMapSelCtrl -# from imagepy.core.manager import ColorManager, ImageManager, WindowsManager, ToolsManager +from sciwx.widgets import CMapSelCtrl +from imagepy.app import ColorManager from sciapp.action import Macros import numpy as np import wx, os.path as osp @@ -12,11 +12,12 @@ def make_bitmap(bmp): # apply, paint, fill, width, slic class Plugin ( wx.Panel ): title = 'Label Tool' - def __init__( self, parent ): + def __init__( self, parent, app=None): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size(-1,-1), style = wx.TAB_TRAVERSAL ) outsizer = wx.BoxSizer(wx.HORIZONTAL) sizer = wx.BoxSizer( wx.VERTICAL ) sizer_color = wx.BoxSizer( wx.HORIZONTAL ) + self.app = app self.btns = [] self.btn_make = wx.Button( self, wx.ID_ANY, 'New Mark', wx.DefaultPosition, wx.DefaultSize, 0 ) sizer_color.Add(self.btn_make, 0, wx.ALL, 2) @@ -37,7 +38,7 @@ def __init__( self, parent ): sizer_other.Add( self.btn_update, 0, wx.ALL, 2) self.cmapsel = CMapSelCtrl(self) - self.cmapsel.SetItems(ColorManager.luts) + self.cmapsel.SetItems(ColorManager.gets(tag='base')) sizer_other.Add(self.cmapsel, 0, wx.ALL|wx.EXPAND, 2 ) com_backChoices = [ u"No Background" ] @@ -89,7 +90,7 @@ def __init__( self, parent ): self.btn_make.Bind( wx.EVT_BUTTON, self.on_make) def on_make(self, event): - Macros(None, ['Build Mark Image>None']).start() + Macros(None, ['Build Mark Image>None']).start(self.app) def on_fill(self, event): tol = ToolsManager.get('Flood Fill')() @@ -113,7 +114,7 @@ def on_items(self, event): def on_cmapsel(self, event): key = self.cmapsel.GetValue() - lut = ColorManager.get_lut(key) + lut = ColorManager.gets(key) n = self.spn_num.GetValue()+1 idx = np.linspace(0, 255, n).astype(int) cs = list(lut[idx]) + [(128,128,128)]*(16-n) From 70d43a4a743ddeb309fa40f5a0afb5abf93a4f2b Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 2 Jul 2020 16:10:18 +0800 Subject: [PATCH 288/343] para view --- imagepy/app/imagej.py | 2 +- imagepy/app/imagepy.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/imagepy/app/imagej.py b/imagepy/app/imagej.py index 313b2679..84af485b 100644 --- a/imagepy/app/imagej.py +++ b/imagepy/app/imagej.py @@ -454,7 +454,7 @@ def get_path(self, title, filt, io, name=''): dialog.Destroy() return path - def show_para(self, title, view, para, on_handle=None, on_ok=None, + def show_para(self, title, para, view, on_handle=None, on_ok=None, on_cancel=None, on_help=None, preview=False, modal=True): lang = ConfigManager.get('language') dic = DictManager.get(name=title, tag=lang) diff --git a/imagepy/app/imagepy.py b/imagepy/app/imagepy.py index 8be2d385..636f76b5 100644 --- a/imagepy/app/imagepy.py +++ b/imagepy/app/imagepy.py @@ -517,7 +517,7 @@ def get_path(self, title, filt, io, name=''): dialog.Destroy() return path - def show_para(self, title, view, para, on_handle=None, on_ok=None, + def show_para(self, title, para, view, on_handle=None, on_ok=None, on_cancel=None, on_help=None, preview=False, modal=True): lang = ConfigManager.get('language') dic = DictManager.get(name=title, tag=lang) From 5a7670065b4ef838eaed51c73ac894244084c4e9 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Thu, 2 Jul 2020 22:30:53 +0800 Subject: [PATCH 289/343] typos in doc are modified --- sciapp/doc/cn_app.md | 2 -- sciapp/doc/cn_imgaction.md | 4 ++-- sciapp/doc/cn_manager.md | 4 ++-- sciapp/doc/cn_readme.md | 10 +++++----- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/sciapp/doc/cn_app.md b/sciapp/doc/cn_app.md index 2f3d44af..7daf37e8 100644 --- a/sciapp/doc/cn_app.md +++ b/sciapp/doc/cn_app.md @@ -14,8 +14,6 @@ App是一个科研应用接口,里面有若干Manager,用于管理应用进 * **show_md(self, cont, title='sciapp'):** 以MarkDown语法书写,向用户弹出格式化文档 - --- - * **show_para(self, title, para, view, on_handle=None, on_ok=None, on_cancel=None, on_help=None, preview=False, modal=True):** 展示交互对话框,para是参数字典,view指定了交互方式。而在这个命令行版的App对象中,只能通过打印完成交互,因而其他参数这里没有作用。但App是一个交互式应用接口,对于界面应用,其他参数分别是,参数变化回调,对话框确认,取消回调,是否自动添加预览选项,对话框是否以模态方式展示 --- diff --git a/sciapp/doc/cn_imgaction.md b/sciapp/doc/cn_imgaction.md index cc7154bb..99718322 100644 --- a/sciapp/doc/cn_imgaction.md +++ b/sciapp/doc/cn_imgaction.md @@ -25,7 +25,7 @@ SciAction().start(app) ### 用SciAction实现滤波 -这里我们在start内,获取当前图像,并对齐进行一个sigma为5的高斯滤波 +这里我们在start内,获取当前图像,并对其进行一个sigma为5的高斯滤波 ```python class GaussianAction1(SciAction): @@ -115,7 +115,7 @@ class ImageAction(SciAction): self.run(image.img, self.para) ``` -现在我们的Gaussian滤波器写法成了这样,只需继承ImageAction,定义para,view,然后重载run函数即可。 +现在我们的Gaussian滤波器写成了这样,只需继承ImageAction,定义para,view,然后重载run函数即可。 ```python class Gaussian(ImageAction): diff --git a/sciapp/doc/cn_manager.md b/sciapp/doc/cn_manager.md index 39cd2a8a..1f957eed 100644 --- a/sciapp/doc/cn_manager.md +++ b/sciapp/doc/cn_manager.md @@ -8,7 +8,7 @@ Manager是一个通用容器,类似一个内存数据库,负责对象的增 * **add(self, name, obj, tag=None):** 添加一个对象,tag作为可选标签,对象添加在有序列表的最前面 -* **adds(self, objs): ** 批量添加name, obj, tag列表 +* **adds(self, objs):** 批量添加name, obj, tag列表 * **gets(self, name=None, tag=None, obj=None):** 获取满足条件的对象,返回name, obj, tag列表,None表示不作为条件 @@ -77,7 +77,7 @@ def tag_test(): -这个例子说明容器内部是按照添加的倒叙存放的,调用active可以将满足条件的元素置顶。 +这个例子说明容器内部是按照添加的倒序存放的,调用active可以将满足条件的元素置顶。 ```python def order_test(): diff --git a/sciapp/doc/cn_readme.md b/sciapp/doc/cn_readme.md index 763f90d4..70832414 100644 --- a/sciapp/doc/cn_readme.md +++ b/sciapp/doc/cn_readme.md @@ -57,7 +57,7 @@ Action:对App对象的一个操作,例如获取当前图像,做某种滤 ### Action的继承树 -因为绝大多数的Action都和交互有关,而SciApp自身主要实现对象管理功能,交互只能通过print进行展示,所以很多功能这里只是列距,具体用法我们会在sciwx实现的一个App实例中展示。 +因为绝大多数的Action都和交互有关,而SciApp自身主要实现对象管理功能,交互只能通过print进行展示,所以很多功能这里只是列举,具体用法我们会在sciwx实现的一个App实例中展示。 @@ -71,17 +71,17 @@ Action:对App对象的一个操作,例如获取当前图像,做某种滤 * ImageTool: 图像工具,例如画笔,魔术棒等,需要重载一系列鼠标事件(参数坐标已转入图像坐标系) - * TableTool:表格工具,需要重载一系列鼠标事件(参数坐标已自动转如单元格行列) - * ShapeTool: 矢量编辑,例如点线面,多边形绘制(参数坐标已自动转如数据坐标系) + * TableTool:表格工具,需要重载一系列鼠标事件(参数坐标已自动转入单元格行列) + * ShapeTool: 矢量编辑,例如点线面,多边形绘制(参数坐标已自动转入数据坐标系) **Advanced:** 这个包下面是一些高级的Action模板,也是我们扩展插件主要使用的 -* dataio: 里面实现了Reader,Writer类的Action,我们只需要将读写函数注册给对于的Manager +* dataio: 里面实现了Reader,Writer类的Action,我们只需要将读写函数注册给对应的Manager * Free: 继承SciAction,添加了para, view交互,添加了多线程支持。 * Filter: 继承ImageAction,主要用于做图像滤波,自动多通道,自带批量特性,多线程支持。 * Simple: 继承ImageAction,主要用于图像操作,自带多线程支持。 * Table: 继承TableAction,主要用于表格操作,比如表格统计,数据绘图等。 -* Macros: 将一段字符串作为宏执行,构造时传入字符串,start后依次质心。 +* Macros: 将一段字符串作为宏执行,构造时传入字符串,start后依次执行。 **Plugins:** 这个包下面有一些带有具体功能,开箱即用的Action From 20249c44f063fdadef8f986bc5d504d41580923e Mon Sep 17 00:00:00 2001 From: qixinbo Date: Fri, 3 Jul 2020 08:50:56 +0800 Subject: [PATCH 290/343] sequence plugin fixed --- imagepy/menus/File/Import/sequence_plg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/menus/File/Import/sequence_plg.py b/imagepy/menus/File/Import/sequence_plg.py index ec2770cb..4ee5cdb9 100644 --- a/imagepy/menus/File/Import/sequence_plg.py +++ b/imagepy/menus/File/Import/sequence_plg.py @@ -30,7 +30,7 @@ def show(self): (int, 'start', (0, nfs-1), 0, 'Start', '0~{}'.format(nfs-1)), (int, 'end', (0, nfs-1), 0, 'End', '0~{}'.format(nfs-1)), (int, 'step', (0, nfs-1), 0, 'Step', '')] - return self.app.show_para('Import sequence', self.view, self.para) + return self.app.show_para('Import sequence', self.para, self.view) def getfiles(self, name): p,f = osp.split(name) From b3a3a0933f324d83a64eb2f858af3de0025947eb Mon Sep 17 00:00:00 2001 From: qixinbo Date: Tue, 7 Jul 2020 11:17:34 +0800 Subject: [PATCH 291/343] para in toolbar corrected --- sciwx/widgets/toolbar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sciwx/widgets/toolbar.py b/sciwx/widgets/toolbar.py index 8c9ae9a7..993fd7ee 100644 --- a/sciwx/widgets/toolbar.py +++ b/sciwx/widgets/toolbar.py @@ -49,7 +49,7 @@ def on_tool(self, evt, tol): def on_config(self, evt, tol): if not hasattr(tol, 'view'): return - self.app.show_para(tol.title, tol.view, tol.para) + self.app.show_para(tol.title, tol.para, tol.view) tol.config() def on_help(self, evt, tol): pass From 36fc992b109d1d5541d3d4321284cc60e602da38 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Tue, 7 Jul 2020 15:57:14 +0800 Subject: [PATCH 292/343] file extension fixed --- imagepy/menus/File/BMP/bmp_plgs.py | 4 ++-- imagepy/menus/File/DAT/dat_plgs.py | 4 ++-- imagepy/menus/File/DICOM/dicom_plgs.py | 2 +- imagepy/menus/File/GIF/gif_plgs.py | 8 ++++---- imagepy/menus/File/MAT/mat_plgs.py | 8 ++++---- imagepy/menus/File/Numpy/ndarray_plgs.py | 8 ++++---- imagepy/menus/File/PNG/png_plgs.py | 4 ++-- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/imagepy/menus/File/BMP/bmp_plgs.py b/imagepy/menus/File/BMP/bmp_plgs.py index 9dc61226..45b3a0f8 100644 --- a/imagepy/menus/File/BMP/bmp_plgs.py +++ b/imagepy/menus/File/BMP/bmp_plgs.py @@ -7,11 +7,11 @@ class OpenFile(dataio.Reader): title = 'BMP Open' tag = 'img' - filt = 'BMP' + filt = ['BMP'] class SaveFile(dataio.ImageWriter): title = 'BMP Save' tag = 'img' - filt = 'BMP' + filt = ['BMP'] plgs = [OpenFile, SaveFile] \ No newline at end of file diff --git a/imagepy/menus/File/DAT/dat_plgs.py b/imagepy/menus/File/DAT/dat_plgs.py index 6bba08f5..ed302d52 100644 --- a/imagepy/menus/File/DAT/dat_plgs.py +++ b/imagepy/menus/File/DAT/dat_plgs.py @@ -13,11 +13,11 @@ def imsave(path,img): class OpenFile(dataio.Reader): title = 'DAT Open' tag = 'img' - filt = 'DAT' + filt = ['DAT'] class SaveFile(dataio.ImageWriter): title = 'DAT Save' tag = 'img' - filt = 'DAT' + filt = ['DAT'] plgs = [OpenFile,SaveFile] \ No newline at end of file diff --git a/imagepy/menus/File/DICOM/dicom_plgs.py b/imagepy/menus/File/DICOM/dicom_plgs.py index 1601c311..50e75233 100644 --- a/imagepy/menus/File/DICOM/dicom_plgs.py +++ b/imagepy/menus/File/DICOM/dicom_plgs.py @@ -8,7 +8,7 @@ def imread(path): class OpenFile(dataio.Reader): title = 'DCM Open' - filt = 'DCM' + filt = ['DCM'] tag = 'img' plgs = [OpenFile] \ No newline at end of file diff --git a/imagepy/menus/File/GIF/gif_plgs.py b/imagepy/menus/File/GIF/gif_plgs.py index f1bc6ae9..03b5ba8c 100644 --- a/imagepy/menus/File/GIF/gif_plgs.py +++ b/imagepy/menus/File/GIF/gif_plgs.py @@ -10,17 +10,17 @@ class OpenFile(dataio.Reader): title = 'GIF Open' tag = 'img' - filt = 'GIF' + filt = ['GIF'] class SaveFile(dataio.ImageWriter): title = 'GIF Save' tag = 'img' - filt = 'GIF' + filt = ['GIF'] class SaveAnimate(Simple): title = 'GIF Animate Save' note = ['all'] - filt = 'GIF' + filt = ['GIF'] para={'path':'', 'dur':0.2} view = [(int, 'dur', (0.01, 10), 2, 'duration', 's')] @@ -33,7 +33,7 @@ def run(self, ips, imgs, para = None): class OpenAnimate(dataio.Reader): title = 'GIF Animate Open' - filt = 'GIF' + filt = ['GIF'] tag = 'imgs' note = ['8-bit', 'rgb', 'stack'] diff --git a/imagepy/menus/File/MAT/mat_plgs.py b/imagepy/menus/File/MAT/mat_plgs.py index 69c7ad35..1dee31f1 100644 --- a/imagepy/menus/File/MAT/mat_plgs.py +++ b/imagepy/menus/File/MAT/mat_plgs.py @@ -10,22 +10,22 @@ class OpenFile(dataio.Reader): title = 'Mat Open' tag = 'img' - filt = 'Mat' + filt = ['Mat'] class SaveFile(dataio.ImageWriter): title = 'Mat Save' tag = 'img' - filt = 'Mat' + filt = ['Mat'] class Open3D(dataio.Reader): title = 'Mat 3D Open' tag = 'imgs' - filt = 'Mat' + filt = ['Mat'] class Save3D(dataio.ImageWriter): title = 'Mat 3D Save' tag = 'imgs' - filt = 'Mat' + filt = ['Mat'] note = ['8-bit', 'rgb', 'stack'] plgs = [OpenFile, SaveFile, '-', Open3D, Save3D] \ No newline at end of file diff --git a/imagepy/menus/File/Numpy/ndarray_plgs.py b/imagepy/menus/File/Numpy/ndarray_plgs.py index ad3f3e98..79ee8a38 100644 --- a/imagepy/menus/File/Numpy/ndarray_plgs.py +++ b/imagepy/menus/File/Numpy/ndarray_plgs.py @@ -10,22 +10,22 @@ class OpenFile(dataio.Reader): title = 'Numpy Open' tag = 'img' - filt = 'npy' + filt = ['npy'] class SaveFile(dataio.ImageWriter): title = 'Numpy Save' tag = 'img' - filt = 'npy' + filt = ['npy'] class Open3D(dataio.Reader): title = 'Numpy 3D Open' tag = 'imgs' - filt = 'npy' + filt = ['npy'] class Save3D(dataio.ImageWriter): title = 'Numpy 3D Save' tag = 'imgs' - filt = 'npy' + filt = ['npy'] note = ['all', 'stack'] plgs = [OpenFile, SaveFile, '-', Open3D, Save3D] \ No newline at end of file diff --git a/imagepy/menus/File/PNG/png_plgs.py b/imagepy/menus/File/PNG/png_plgs.py index 99c15052..8f1f1d80 100644 --- a/imagepy/menus/File/PNG/png_plgs.py +++ b/imagepy/menus/File/PNG/png_plgs.py @@ -7,11 +7,11 @@ class OpenFile(dataio.Reader): title = 'PNG Open' tag = 'img' - filt = 'PNG' + filt = ['PNG'] class SaveFile(dataio.ImageWriter): title = 'PNG Save' tag = 'img' - filt = 'PNG' + filt = ['PNG'] plgs = [OpenFile, SaveFile] \ No newline at end of file From d16c88804dddfa7bd0a3aca8722164f3d844e1ba Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 8 Jul 2020 10:54:59 +0800 Subject: [PATCH 293/343] nothing --- imagepy/app/loader.py | 6 +++--- imagepy/data/config.json | 2 +- imagepy/menus/File/Export/sequence_plg.py | 2 +- imagepy/menus/Plugins/Contribute/pmanager_wgt.py | 3 ++- imagepy/menus/Plugins/Install/installplg_plgs.py | 1 - sciapp/action/tolact.py | 3 +++ sciapp/doc/cn_readme.md | 6 +++--- sciwx/canvas/canvas.py | 1 + sciwx/grid/grid.py | 7 ++++--- sciwx/grid/widget.py | 1 + sciwx/mesh/canvas.py | 5 +++++ sciwx/mesh/scene.py | 1 + sciwx/widgets/normal.py | 10 ++++++---- 13 files changed, 31 insertions(+), 17 deletions(-) diff --git a/imagepy/app/loader.py b/imagepy/app/loader.py index 1c024132..81a8d35f 100644 --- a/imagepy/app/loader.py +++ b/imagepy/app/loader.py @@ -23,9 +23,9 @@ def extend_plugins(path, lst, err): rst = [] for i in lst: if isinstance(i, tuple) or i=='-': rst.append(i) - elif i[-3:] == 'rpt': - pt = os.path.join(root_dir,path) - rst.append(Report(i[:-4], pt+'/'+i)) + elif i[-3:] == 'rpt': pass + #pt = os.path.join(root_dir,path) + #rst.append(Report(i[:-4], pt+'/'+i)) elif i[-3:] in {'.md', '.mc', '.wf'}: p = os.path.join(os.path.join(root_dir, path), i).replace('\\','/') rst.append(Macros(i[:-3], ['Open>{"path":"%s"}'%p])) diff --git a/imagepy/data/config.json b/imagepy/data/config.json index fe7b9a33..b7722ba7 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["uistyle", "imagepy", null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["recent", ["C:/Users/54631/Desktop/\u6d77\u51b0\u62a5\u4ef7/testmacros.mc", "C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc"], null], ["roi_style", {"color": [255, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 255, 0], "size": 8}, null], ["language", "English", null]] \ No newline at end of file +[["language", "English", null], ["roi_style", {"color": [255, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 255, 0], "size": 8}, null], ["recent", ["C:/Users/54631/Desktop/\u6d77\u51b0\u62a5\u4ef7/testmacros.mc", "C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc"], null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["uistyle", "imagepy", null]] \ No newline at end of file diff --git a/imagepy/menus/File/Export/sequence_plg.py b/imagepy/menus/File/Export/sequence_plg.py index 5d612fb7..e2c7e949 100644 --- a/imagepy/menus/File/Export/sequence_plg.py +++ b/imagepy/menus/File/Export/sequence_plg.py @@ -13,7 +13,7 @@ class Plugin(Simple): note = ['all'] para = {'path':'','name':'','format':'png'} #para = {'path':'./','name':'','format':'png'} - view = [('path', 'path', 'path', '', 'folder'), + view = [('path', 'path', '', 'folder', 'path'), (str, 'name', 'name', 'number'), (None)] diff --git a/imagepy/menus/Plugins/Contribute/pmanager_wgt.py b/imagepy/menus/Plugins/Contribute/pmanager_wgt.py index 1017065d..38af18a2 100644 --- a/imagepy/menus/Plugins/Contribute/pmanager_wgt.py +++ b/imagepy/menus/Plugins/Contribute/pmanager_wgt.py @@ -142,8 +142,9 @@ def on_install(self, event): i = self.lst_plgs.GetFirstSelected() if i==-1: return path = self.buf[i][-1]['path'] + self.app.plugin_manager.get('Install Plugins').para['repo'] = path self.app.plugin_manager.get('Install Plugins')().start( - self.app, {'repo':self.buf[i][-1]['path']}, self.load) + self.app, None, self.load) def on_remove(self, event): i = self.lst_plgs.GetFirstSelected() diff --git a/imagepy/menus/Plugins/Install/installplg_plgs.py b/imagepy/menus/Plugins/Install/installplg_plgs.py index c572090a..7d6e6564 100644 --- a/imagepy/menus/Plugins/Install/installplg_plgs.py +++ b/imagepy/menus/Plugins/Install/installplg_plgs.py @@ -34,7 +34,6 @@ class Install(Free): (str, 'Port', 'Port', '')] def run(self, para=None): - para = self.para url = para['repo'] if 'github.com' in url: if url[-4:] == '.git': diff --git a/sciapp/action/tolact.py b/sciapp/action/tolact.py index 37c52f5f..ba589411 100644 --- a/sciapp/action/tolact.py +++ b/sciapp/action/tolact.py @@ -76,6 +76,9 @@ class TableTool(DefaultTool): default = None title = 'Table Tool' + def mouse_down(self, data, x, y, btn, **others): + print('you click on cell', x, y) + def start(self, app, para=None, callafter=None): self.app = app TableTool.default = self diff --git a/sciapp/doc/cn_readme.md b/sciapp/doc/cn_readme.md index 763f90d4..adb73116 100644 --- a/sciapp/doc/cn_readme.md +++ b/sciapp/doc/cn_readme.md @@ -47,9 +47,9 @@ App:一个科学容器,里面包含若干Manager,用于管理App所持有 Action:对App对象的一个操作,例如获取当前图像,做某种滤波,然后从图像中获取某种信息,最后show一个Table处理。其通用模板是Action().start(app)。可以在其子类中重载start,对app进行任何操作。 -* [如何衍生出图像滤波类Action](./cn_imgaction.md) -* [如何衍生出数据读写类Action](./cn_ioaction.md) -* [读取-处理-写入,完成具体工作](./cn_iprocesso.md) +* [[如何衍生出图像滤波类Action](./cn_imgaction.md) +* [[如何衍生出数据读写类Action](./cn_ioaction.md) +* [[读取-处理-写入,完成具体工作](./cn_iprocesso.md) 以上例子是为了说明Action的作用机制,也展示了框架的构建思路,但并不意味着我们需要按照以上方法,从SciAction构建各种模板,我们已经构建了相当丰富,功能也更为完善的Action派生树,以下进行列举。 diff --git a/sciwx/canvas/canvas.py b/sciwx/canvas/canvas.py index ead97f1b..ff8186c5 100644 --- a/sciwx/canvas/canvas.py +++ b/sciwx/canvas/canvas.py @@ -103,6 +103,7 @@ def update_box(self): box = [1e10, 1e10, -1e10, -1e10] for i in self.images: box = merge(box, i.box) shapes = [i for i in self.marks.values() if isinstance(i, Shape)] + shapes = [i for i in shapes if not i.box is None] for i in shapes: box = merge(box, i.box) if box[2]<=box[0]: box[0], box[2] = box[0]-1e-3, box[2]+1e-3 if box[1]<=box[3]: box[1], box[3] = box[1]-1e-3, box[3]+1e-3 diff --git a/sciwx/grid/grid.py b/sciwx/grid/grid.py index 605309d6..864b79b7 100644 --- a/sciwx/grid/grid.py +++ b/sciwx/grid/grid.py @@ -200,8 +200,8 @@ def on_label(self, evt): print(para) if col!=-1: props[self.table.columns[col]] = [para[i] for i in ['accu', 'tc', 'lc', 'ln']] - print(props[self.table.columns[col]]) - print('===========') + #print(props[self.table.columns[col]]) + #print('===========') if col==-1: for c in self.table.columns: props[c] = [para[i] for i in ['accu', 'tc', 'lc', 'ln']] @@ -253,9 +253,10 @@ def select(self): def on_idle(self, event): if not self.IsShown() or self.table is None\ or self.table.dirty == False: return - self.select() + self.tablebase.ResetView(self) self.table.dirty = False + # self.select() print('update') def __del__(self): diff --git a/sciwx/grid/widget.py b/sciwx/grid/widget.py index a1e33e67..e2ca1623 100644 --- a/sciwx/grid/widget.py +++ b/sciwx/grid/widget.py @@ -27,6 +27,7 @@ def table(self): return self.grid.table def name(self): return self.grid.table.name def on_idle(self, event): + if self.table.data is None: return if self.lab_info.GetLabel() != self.table.info: self.lab_info.SetLabel(self.table.info) diff --git a/sciwx/mesh/canvas.py b/sciwx/mesh/canvas.py index 7598bbf2..162b0d6c 100644 --- a/sciwx/mesh/canvas.py +++ b/sciwx/mesh/canvas.py @@ -28,12 +28,17 @@ def __init__(self, parent, scene=None): self.Bind(wx.EVT_RIGHT_UP, self.OnMouseUp) self.Bind(wx.EVT_MOTION, self.OnMouseMotion) self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel) + self.Bind(wx.EVT_IDLE, self.OnIdle) self.lastx, self.lasty = None, None #self.update() #print('init===========') pub.subscribe(self.add_surf, 'add_surf') # pub.subscribe(self.add_mark, 'add_mark') + def OnIdle(self, event): + if sum([i.update for i in self.scene.objs.values()])>0: + self.Refresh(False) + def InitGL(self): self.scene.on_ctx(moderngl.create_context()) self.DoSetViewport() diff --git a/sciwx/mesh/scene.py b/sciwx/mesh/scene.py index 13aae06e..3c13c533 100644 --- a/sciwx/mesh/scene.py +++ b/sciwx/mesh/scene.py @@ -132,6 +132,7 @@ def draw_obj(self, name, obj, ctx, prog, mvp, light, bright, scatter): mode = {'mesh':moderngl.TRIANGLES, 'grid':moderngl.LINES}[obj.mode] if obj.update: self.vabo[name][0][:,6:9] = obj.cs + self.vabo[name][0][:,:3] = obj.vts self.vabo[name][2].write(self.vabo[name][0].tobytes()) obj.update = False self.vabo[name][1].render(mode) diff --git a/sciwx/widgets/normal.py b/sciwx/widgets/normal.py index e67323ac..901f882f 100644 --- a/sciwx/widgets/normal.py +++ b/sciwx/widgets/normal.py @@ -139,7 +139,7 @@ def GetValue(self): return wx.Colour(rgb).Get(False)[::-1] class PathCtrl(wx.Panel): - def __init__(self, parent, title, filt, io, app=None): + def __init__(self, parent, filt, io, title, app=None): wx.Panel.__init__(self, parent) sizer = wx.BoxSizer( wx.HORIZONTAL ) self.prefix = lab_title = wx.StaticText( self, wx.ID_ANY, title, @@ -152,11 +152,12 @@ def __init__(self, parent, title, filt, io, app=None): self.SetSizer(sizer) self.ctrl.Bind(wx.EVT_KEY_UP, self.ontext) - self.ctrl.Bind( wx.EVT_LEFT_DOWN, self.onselect) + self.ctrl.Bind( wx.EVT_LEFT_DCLICK, self.onselect) def Bind(self, z, f): self.f = f - def ontext(self, event): print('ColorCtrl') + def ontext(self, event): + self.f(self) def onselect(self, event): if isinstance(self.filt, str): self.filt = self.filt.split(',') @@ -164,11 +165,12 @@ def onselect(self, event): dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} if self.io=='folder': dialog = wx.DirDialog(self, 'Path Select', '.', wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST | wx.FD_CHANGE_DIR) - else: dialog = wx.FileDialog(self, 'Path Select', dpath, '', filt, dict[self.io] | wx.FD_CHANGE_DIR) + else: dialog = wx.FileDialog(self, 'Path Select', None, '', filt, dict[self.io] | wx.FD_CHANGE_DIR) rst = dialog.ShowModal() if rst == wx.ID_OK: path = dialog.GetPath() self.ctrl.SetValue(path) + self.f(self) dialog.Destroy() def SetValue(self, value): From abf4227c94fea4b57a458e61b11b0f700db798bf Mon Sep 17 00:00:00 2001 From: qixinbo Date: Wed, 8 Jul 2020 22:46:05 +0800 Subject: [PATCH 294/343] build plugins fixed --- imagepy/app/loader.py | 6 +++--- imagepy/app/startup.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/imagepy/app/loader.py b/imagepy/app/loader.py index 81a8d35f..99a34a2e 100644 --- a/imagepy/app/loader.py +++ b/imagepy/app/loader.py @@ -68,10 +68,10 @@ def build_plugins(path, err='root'): root = err=='root' if root: err=[] subtree = [] - cont = os.listdir(os.path.join(root_dir, path)) + cont = os.listdir(path) for i in cont: subp = os.path.join(path,i) - if os.path.isdir(os.path.join(root_dir, subp)): + if os.path.isdir(subp): sub = build_plugins(subp, err) if len(sub)!=0:subtree.append(sub[:2]) elif i[-6:] in ('plg.py', 'lgs.py', 'wgt.py', 'gts.py'): @@ -80,8 +80,8 @@ def build_plugins(path, err='root'): subtree.append(i) if len(subtree)==0:return [] + path = path[path.index(root_dir)+len(root_dir)+1:] rpath = path.replace('/', '.').replace('\\','.') - #rpath = rpath[rpath.index('imagepy.'):] pg = __import__('imagepy.'+rpath,'','',['']) pg.title = os.path.basename(path) if hasattr(pg, 'catlog'): diff --git a/imagepy/app/startup.py b/imagepy/app/startup.py index 4300cecc..9d36f0b3 100644 --- a/imagepy/app/startup.py +++ b/imagepy/app/startup.py @@ -26,8 +26,8 @@ def extend_wgts(wgt): else: return (wgt.title, wgt) def load_plugins(): - data = loader.build_plugins('menus') - extends = glob('plugins/*/menus') + data = loader.build_plugins(root_dir+'/menus') + extends = glob(root_dir+'/plugins/*/menus') keydata = {} for i in data[1]: if isinstance(i, tuple): keydata[i[0].title] = i[1] From 5b2e9965bf272d191ffe4b62e431266fa7da99be Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 9 Jul 2020 12:17:58 +0800 Subject: [PATCH 295/343] nothing --- imagepy/data/config.json | 2 +- imagepy/lang/Chinese/plugins/analysis.dic | 159 +++++++++++++----- imagepy/lang/Chinese/plugins/process.dic | 38 ++++- .../Analysis/Pixel Cluster/cluster_plgs.py | 4 +- .../Analysis/Pixel Cluster/kmeans_plgs.py | 5 +- imagepy/menus/Analysis/label_plg.py | 5 +- imagepy/menus/Analysis/statistic_plg.py | 3 +- sciapp/object/image.py | 4 +- 8 files changed, 152 insertions(+), 68 deletions(-) diff --git a/imagepy/data/config.json b/imagepy/data/config.json index b7722ba7..a9c8eb05 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["language", "English", null], ["roi_style", {"color": [255, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 255, 0], "size": 8}, null], ["recent", ["C:/Users/54631/Desktop/\u6d77\u51b0\u62a5\u4ef7/testmacros.mc", "C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc"], null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["uistyle", "imagepy", null]] \ No newline at end of file +[["uistyle", "imagepy", null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["recent", ["C:/Users/54631/Desktop/\u6d77\u51b0\u62a5\u4ef7/testmacros.mc", "C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc"], null], ["roi_style", {"color": [255, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 255, 0], "size": 8}, null], ["language", "Chinese", null]] \ No newline at end of file diff --git a/imagepy/lang/Chinese/plugins/analysis.dic b/imagepy/lang/Chinese/plugins/analysis.dic index d24f6ec7..3904ec3e 100644 --- a/imagepy/lang/Chinese/plugins/analysis.dic +++ b/imagepy/lang/Chinese/plugins/analysis.dic @@ -1,57 +1,124 @@ -label image::标记图 - -mark boundaries::标记边线 - -label render::标记着色 - color::标记颜色个数 - -frequence::灰度值频数 - -pixel statistic::灰度值统计特性 - -histogram::直方图 - -points value::点ROI灰度值 - -shortest route::灰度图最短路径 - step::通路路径成本值 - -pixel cluster::像素聚类 - -gray cluster::灰度图聚类 - torlerance::公差 - -color cluster::彩色图聚类 - torlerance::公差 - -gray cluster 3d::3D灰度图聚类 - -color cluster 3d::3D彩色图聚类 - -gray points statistic::灰度图点ROI统计特性 - -color points statistic::彩色图点ROI统计特性 - -k-means::K均值聚类 +Label Image::区域标记 + +Mark Boundaries::标记边界 + structure::联通 + connect::邻域 + slice::序列 + mode::边界模式 + +Label Render::标记着色 + structure::联通 + connect::邻域 + colors::颜色数量 + background::标记背景 + slice::序列 + +Frequence::频数统计 + count frequence::统计频率 + slice::序列 + +Pixel Statistic::像素统计 + max::最大值 + min::最小值 + mean::平均值 + var::方差 + std::标准差 + slice::序列 + +Histogram::直方图 + +Points Value::ROI灰度 + buffer by the value::以中心为中心,灰度为半径 + slice::序列 + +Shortest Route::最小代价路径 + step::几何距离 + cost::权重 + output::输出类型 + geometric::几何 + fully connected::允许走对角线 + max cost::最大代价 + +Pixel Cluster::像素聚类 + +Gray Cluster::灰度聚类 + torlerance::容错 + use cov instead of distance::使用协方差椭圆 + only points within::仅提取与种子点联通的区域 + result as new mask::结果作为掩膜 + mask::掩膜方式 + +Color Cluster::色彩聚类 + torlerance::容错 + use cov instead of distance::使用协方差椭圆 + only points within::仅提取与种子点联通的区域 + result as new mask::结果作为掩膜 + mask::掩膜方式 + +Gray Cluster 3D::3D灰度图聚类 + torlerance::容错 + use cov instead of distance::使用协方差椭圆 + only points within::仅提取与种子点联通的区域 + result as new mask::结果作为掩膜 + mask::掩膜方式 + +Color Cluster 3D::3D彩色图聚类 + torlerance::容错 + use cov instead of distance::使用协方差椭圆 + only points within::仅提取与种子点联通的区域 + result as new mask::结果作为掩膜 + mask::掩膜方式 + +Gray Points Statistic::灰度ROI统计 + +Color Points Statistic::彩色图ROI统计 + +K-Means::K均值聚类 k::质心数 iterate::迭代次数 threshold::变化阈值 - resample::重采样 + resample::采样间隔 -region analysis::区域分析 +Region Analysis::区域分析 -geometry analysis::几何分析 - -geometry filter::几何筛选 - front color::前景灰度值 - back color::背景灰度值 +Geometry Analysis::几何分析 + conection::联通 + pix::邻域 + slice::序列 + ========= indecate =========::========= 指标 ========= + center::质心 + area::面积 + perimeter::周长 + extent::外接矩形 + equivalent diameter::等效直径 + convex area::凸包面积 + holes::镂空 + filled area::填充面积 + solidity::丰满度 + cov::质量协方差 + +Geometry Filter::几何特征过滤 + front color::前景色 + back color::背景色 area::面积 perimeter::周长 - holes::孔洞数 - solidity::紧固度 + holes::镂空数 + solidity::丰满度 eccentricity::偏心率 - -property marker::属性标记 + ratio::比例 + unit::单位 + unit^2::单位^2 + invert::过滤黑色区域 + conection::联通 + pix::邻域 + num::个 + Filter: "+" means >=, "-" means <::"+" 代表 >=, "-" 代表 < + +Property Marker::属性标记 + conection::联通 + property::染色属性 + color map::色谱 + pix::邻域 connective analysis::连通性分析 diff --git a/imagepy/lang/Chinese/plugins/process.dic b/imagepy/lang/Chinese/plugins/process.dic index a00d7664..f4f1c6e9 100644 --- a/imagepy/lang/Chinese/plugins/process.dic +++ b/imagepy/lang/Chinese/plugins/process.dic @@ -67,6 +67,7 @@ Minimum::最小值滤波 Median::中值滤波 size::核大小 + pix::像素 Percent::百分比滤波 size::核大小 @@ -75,6 +76,8 @@ Percent::百分比滤波 Prewitt::Prewitt梯度滤波 Sobel::Sobel梯度滤波 + direction::方向 + aixs::轴 Laplace::拉普拉斯滤波 @@ -224,13 +227,20 @@ SLIC Superpixel::SLIC超像素 segments::分割数量 campactness::紧凑性 max_iter::最大迭代次数 - sigma::高斯平滑标准差 + sigma::高斯平滑 + smooth::标准差 + n:: + color-space::色彩 Quick Shift::快速Shift聚类 - ratio::色彩/空间平衡比例 - kernel_size::高斯平滑内核的宽度 - distance::数据的切点距离 + ratio::色彩/空间平衡 + kernel_size::卷积核宽度 + distance::到切点距离 + cut off:: sigma::高斯平滑标准差 + color-space::比例 + sigma::高斯平滑 + smooth::标准差 Felzenszwalb::Felzenszwalb聚类 scale::尺度参数 @@ -241,18 +251,28 @@ SLIC Superpixel Label::SLIC超像素标记 segments::分割数量 campactness::紧凑性 max_iter::最大迭代次数 - sigma::高斯平滑标准差 + sigma::高斯平滑 + smooth::标准差 + n:: + color-space::色彩 + stack::序列 Quickshift Label::快速Shift聚类标记 - ratio::色彩/空间平衡比例 - kernel_size::高斯平滑内核的宽度 - distance::数据的切点距离 + ratio::色彩/空间平衡 + kernel_size::卷积核宽度 + distance::到切点距离 + cut off:: sigma::高斯平滑标准差 + color-space::比例 + sigma::高斯平滑 + smooth::标准差 + stack::序列 -Felzenszwalb Lable::Felzenszwalb聚类标记 +Felzenszwalb Label::Felzenszwalb聚类标记 scale::尺度参数 sigma::高斯平滑内核的宽度 min_size::最小区域面积 + stack::序列 RAG Threshold::相邻区域阈值融合 threshold::阈值 diff --git a/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py b/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py index c85749e7..09f3fbd1 100644 --- a/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py +++ b/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py @@ -74,7 +74,7 @@ class GrayCluster(Filter): note = ['8-bit', '16-bit', 'int', 'float', 'auto_snap', 'not_channel', 'not_slice', 'req_roi', 'preview'] para = {'sigma':3, 'cov':True, 'new':True, 'within':False, 'msk':'nothing','msk':'nothing'} - view = [(float, 'sigma', (0,255), 1, 'torlerance', 'pix'), + view = [(float, 'sigma', (0,255), 1, 'torlerance', ''), (bool, 'cov', 'use cov instead of distance'), (bool, 'within', 'only points within'), (bool, 'new', 'result as new mask'), @@ -120,7 +120,7 @@ class GrayCluster3D(Simple): note = ['8-bit', '16-bit', 'int', 'float', 'req_roi', 'stack3d', 'preview'] modal = False para = {'sigma':3, 'cov':True, 'new':True, 'within':False, 'msk':'nothing','msk':'nothing'} - view = [(float, 'sigma', (0,255), 1, 'torlerance', 'pix'), + view = [(float, 'sigma', (0,255), 1, 'torlerance', ''), (bool, 'cov', 'use cov instead of distance'), (bool, 'within', 'only points within'), (bool, 'new', 'result as new mask'), diff --git a/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py b/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py index 7ff90bd4..c4c87633 100644 --- a/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py +++ b/imagepy/menus/Analysis/Pixel Cluster/kmeans_plgs.py @@ -2,7 +2,7 @@ import numpy as np from scipy.cluster.vq import kmeans, vq -class K_mean(Filter): +class Plugin(Filter): title = 'K-Means' note = ['all', 'not_channel', 'auto_msk', 'auto_snap', 'preview'] @@ -19,7 +19,6 @@ def run(self, ips, snap, img, para = None): para['k'],para['iter'],para['thresh'])[0] img[:] = ms[vq(pts, ms)[0]].reshape(img.shape) -plgs = [K_mean] if __name__ == '__main__': from skimage.data import camera, astronaut @@ -27,4 +26,4 @@ def run(self, ips, snap, img, para = None): ImageApp.start( imgs = [('astronaut', astronaut())], - plgs=[('K', K_mean)]) \ No newline at end of file + plgs=[('K', Plugin)]) \ No newline at end of file diff --git a/imagepy/menus/Analysis/label_plg.py b/imagepy/menus/Analysis/label_plg.py index ab7dd8d0..6de9d348 100644 --- a/imagepy/menus/Analysis/label_plg.py +++ b/imagepy/menus/Analysis/label_plg.py @@ -32,7 +32,7 @@ class Boundaries(Simple): title = 'Mark Boundaries' note = ['8-bit', '16-bit','int'] para = {'slice':False, 'mode':'outer', 'con':'4-Connect'} - view = [(list, 'con', ['4-Connect','8-Connect'], str, 'Structure', 'connect'), + view = [(list, 'con', ['4-Connect','8-Connect'], str, 'structure', 'connect'), (list, 'mode', ['thick', 'inner', 'outer', 'subpixel'], str, 'mode', ''), (bool, 'slice', 'slice')] @@ -52,14 +52,13 @@ class Render(Simple): title = 'Label Render' note = ['8-bit', '16-bit', 'int'] para = {'slice':False, 'con':'8-Connect', 'colors':6, 'back':False} - view = [(list, 'con', ['4-Connect','8-Connect'], str, 'Structure', 'connect'), + view = [(list, 'con', ['4-Connect','8-Connect'], str, 'structure', 'connect'), (int, 'colors', (4, 255), 0, 'colors', 'n'), (bool, 'back', 'background'), (bool, 'slice', 'slice')] def run(self, ips, imgs, para = None): if not para['slice']: imgs = [ips.img] - print(para) labels = [] for i in range(len(imgs)): self.progress(i, len(imgs)) diff --git a/imagepy/menus/Analysis/statistic_plg.py b/imagepy/menus/Analysis/statistic_plg.py index f46a9b0a..392841d0 100644 --- a/imagepy/menus/Analysis/statistic_plg.py +++ b/imagepy/menus/Analysis/statistic_plg.py @@ -80,14 +80,13 @@ def run(self, ips, imgs, para = None): hist = np.histogram(img, np.arange(257))[0] show_hist(self.app, ips.title+'-Histogram', hist) - class Frequence(Simple): title = 'Frequence' note = ['8-bit', '16-bit'] para = {'fre':True, 'slice':False} view = [(bool, 'fre', 'count frequence'), - (bool, 'slice', 'each slices')] + (bool, 'slice', 'slice')] def run(self, ips, imgs, para = None): if not para['slice']: imgs = [ips.img] diff --git a/sciapp/object/image.py b/sciapp/object/image.py index 35a88b25..ee386c82 100644 --- a/sciapp/object/image.py +++ b/sciapp/object/image.py @@ -140,8 +140,8 @@ def update(self): self.dirty = True def reset(self): self.cn = [0, (0,1,2)][self.channels==3] - if self.dtype == np.uint8: - self.rg = [(0, 255)] * self.channels + if self.dtype == np.uint8: pass + # self.rg = [(0, 255)] * self.channels else: self.rg = self.get_updown('all', 'all', step=512) From 1c09cee9778a68bb3ac3cd1b929349c371f1effb Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 9 Jul 2020 13:09:38 +0800 Subject: [PATCH 296/343] analysis chinese dic --- imagepy/lang/Chinese/plugins/analysis.dic | 60 ++++++++++++++++++----- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/imagepy/lang/Chinese/plugins/analysis.dic b/imagepy/lang/Chinese/plugins/analysis.dic index 3904ec3e..68590b36 100644 --- a/imagepy/lang/Chinese/plugins/analysis.dic +++ b/imagepy/lang/Chinese/plugins/analysis.dic @@ -120,24 +120,62 @@ Property Marker::属性标记 color map::色谱 pix::邻域 -connective analysis::连通性分析 +Connective Analysis::区域邻接分析 + conection::联通 + pix::邻域 + it is a label image::图像已标记 + nonzero::只分析非0区域 + slice::序列 + +Intensity Analysis::区域灰度分析 + intensity::目标图像 + conection::联通 + pix::邻域 + slice::序列 + has labeled::图像已标记 + ========= indecate =========::========= 指标 ========= + center::质心 + extent::外接矩 + max::最大值 + min::最小值 + mean::平均值 + standard::标准差 + sum::求和 -intensity analysis::灰度值分析 +Intensity Filter::区域灰度过滤 + intensity::目标图像 + conection::联通 + pix::邻域 + slice::序列 + Filter: "+" means >=, "-" means <::"+" 代表 >=, "-" + front color::前景色 + back color::背景色 + max::最大值 + min::最小值 + mean::平均值 + std::标准差 + sum::求和 -intensity filter::灰度值统计特性过滤 +Skeleton Network::骨架网络 -skeleton network::骨架网络 +Graph Statistic::边,节点统计 -build graph::建图 +Build Graph::从骨架建立图 -graph summarise::图摘要 +Graph Summarise::图信息汇总 -remove isolate node::移除孤立节点 +Remove Isolate Node::移除孤立节点 -remove 2path node::移除第二路径节点 +Remove 2Path Node::移除双路径节点 -cut branch::分支剪切 +Cut Branch::分支剪切 + limit::阈值 + unit::单位 + recursion::递归剪枝 -cut by roi::ROI剪切 +Cut By ROI::用ROI剪枝 -graph shortest path::图最短路径 \ No newline at end of file +Graph Shortest Path::图最短路径 + start::出发 + end::到达 + id::编号 \ No newline at end of file From 6778d9f13aafc7161c783e66e2ab02d3909f7f67 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Sun, 12 Jul 2020 15:57:39 +0800 Subject: [PATCH 297/343] Bugs in demos are fixed --- sciapp/demo/demo3_img_action.py | 4 ++-- sciapp/demo/demo4_io_action.py | 9 ++++++--- sciapp/demo/demo5_process_action.py | 3 +++ sciapp/demo/demo6_adv_action.py | 3 +++ sciwx/app/imgapp.py | 26 +++++++++++--------------- 5 files changed, 25 insertions(+), 20 deletions(-) diff --git a/sciapp/demo/demo3_img_action.py b/sciapp/demo/demo3_img_action.py index 813285ba..69819dd7 100644 --- a/sciapp/demo/demo3_img_action.py +++ b/sciapp/demo/demo3_img_action.py @@ -19,7 +19,7 @@ def action_demo1(): app = App() SciAction().start(app) -class GaussianAction2(SciAction): +class GaussianAction1(SciAction): '''get current image object, and do a gaussian filter with sigma 5''' name = 'GaussianAction1' @@ -131,6 +131,6 @@ def action_demo5(): action_demo3() action_demo4() action_demo5() - action_demo6() + # action_demo6() diff --git a/sciapp/demo/demo4_io_action.py b/sciapp/demo/demo4_io_action.py index 7eeec4d8..efd1cfc3 100644 --- a/sciapp/demo/demo4_io_action.py +++ b/sciapp/demo/demo4_io_action.py @@ -1,3 +1,6 @@ +import sys +sys.path.append('../../') + from sciapp import App, Manager from sciapp.action import SciAction import os.path as osp @@ -100,7 +103,7 @@ def img_write_demo4(): ImageWriter().start(app) if __name__ == '__main__': - #image_read_demo1() - #image_read_demo2() - #image_read_demo3() + image_read_demo1() + image_read_demo2() + image_read_demo3() img_write_demo4() diff --git a/sciapp/demo/demo5_process_action.py b/sciapp/demo/demo5_process_action.py index 9c4132a2..16dffd16 100644 --- a/sciapp/demo/demo5_process_action.py +++ b/sciapp/demo/demo5_process_action.py @@ -1,3 +1,6 @@ +import sys +sys.path.append('../../') + from sciapp import App, Manager from sciapp.action import SciAction import os.path as osp diff --git a/sciapp/demo/demo6_adv_action.py b/sciapp/demo/demo6_adv_action.py index aba869a5..b1af699c 100644 --- a/sciapp/demo/demo6_adv_action.py +++ b/sciapp/demo/demo6_adv_action.py @@ -1,3 +1,6 @@ +import sys +sys.path.append('../../') + from sciapp.action.advanced import dataio, Filter from scipy.ndimage import gaussian_filter from skimage.io import imread, imsave diff --git a/sciwx/app/imgapp.py b/sciwx/app/imgapp.py index b50bb89e..4181e690 100644 --- a/sciwx/app/imgapp.py +++ b/sciwx/app/imgapp.py @@ -10,6 +10,7 @@ from sciwx.text import MDFrame, TextFrame from sciwx.plot import PlotFrame from sciapp import App, Source +from sciapp.object import Image, Table class ImageApp(wx.Frame, App): def __init__( self, parent ): @@ -25,7 +26,7 @@ def __init__( self, parent ): sizer.Add(self.toolbar, 0, wx.EXPAND |wx.ALL, 0) self.canvasnb = CanvasNoteBook(self) - self.canvasnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_new_img) + self.canvasnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_active_img) self.canvasnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close_img) sizer.Add(self.canvasnb, 1, wx.EXPAND |wx.ALL, 0) @@ -56,14 +57,13 @@ def remove_task(self, task): tasks = [(p.title, lambda t=p:p.prgs) for n,p,t in tasks] self.pro_bar.SetValue(tasks) - def on_new_img(self, event): - self.add_img(self.canvasnb.canvas().image) - self.add_img_win(self.canvasnb.canvas()) + def on_active_img(self, event): + self.active_img(self.canvasnb.canvas().image.name) + # self.add_img_win(self.canvasnb.canvas()) def on_close_img(self, event): canvas = event.GetEventObject().GetPage(event.GetSelection()) - self.remove_img_win(canvas) - self.remove_img(canvas.image) + App.close_img(self, canvas.image.title) def on_new_tab(self, event): self.add_tab(event.GetEventObject().grid.table) @@ -93,14 +93,10 @@ def on_close(self, event): def _show_img(self, img, title=None): canvas = self.canvasnb.add_canvas() - self.remove_img(canvas.image) - self.remove_img_win(canvas) - if not title is None: - canvas.set_imgs(img) - canvas.image.name = title - else: canvas.set_img(img) - self.add_img(canvas.image) - self.add_img_win(canvas) + if not isinstance(img, Image): + img = Image(img, title) + App.show_img(self, img, img.title) + canvas.set_img(img) def show_img(self, img, title=None): wx.CallAfter(self._show_img, img, title) @@ -161,7 +157,7 @@ def get_path(self, title, filt, io, name=''): dialog.Destroy() return path - def show_para(self, title, view, para, on_handle=None, on_ok=None, on_cancel=None, preview=False, modal=True): + def show_para(self, title, para, view, on_handle=None, on_ok=None, on_cancel=None, preview=False, modal=True): dialog = ParaDialog(self, title) dialog.init_view(view, para, preview, modal=modal, app=self) dialog.Bind('cancel', on_cancel) From be6ee1f8f67156a948ffa30786da4fe2146238d4 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Tue, 14 Jul 2020 21:29:23 +0800 Subject: [PATCH 298/343] histgram bug fixed --- sciapp/object/image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sciapp/object/image.py b/sciapp/object/image.py index ee386c82..f20fe1ef 100644 --- a/sciapp/object/image.py +++ b/sciapp/object/image.py @@ -170,7 +170,7 @@ def get_updown(self, slices='all', chans='all', step=512): def lookup(self, img=None): if img is None: img = self.img - return lookup(img, self.cn, self.rg, self.lut) + return lookup(img, self.cn, [self.rg], self.lut) if __name__ == '__main__': img = Image(np.zeros((5,5))) From 9fde7688a9cb5d0946b6e5e5c8338aa4b7786f73 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Fri, 17 Jul 2020 13:11:53 +0800 Subject: [PATCH 299/343] filter name in save with mark --- imagepy/menus/File/save_plg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/menus/File/save_plg.py b/imagepy/menus/File/save_plg.py index 3dece40e..b72bc0ab 100644 --- a/imagepy/menus/File/save_plg.py +++ b/imagepy/menus/File/save_plg.py @@ -15,7 +15,7 @@ def load(self, ips): class WindowCapture(dataio.ImageWriter): title = 'Save With Mark' - filt = 'PNG' + filt = ['PNG'] def run(self, ips, imgs, para = None): self.app.get_img_win().canvas.save_buffer(para['path']) From 135e1ea9d6c28106039c7725982a0e651bc57be8 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Fri, 17 Jul 2020 13:20:07 +0800 Subject: [PATCH 300/343] table open csv and translate --- imagepy/lang/Chinese/plugins/Plugins.dic | 2 ++ imagepy/menus/Table/Table IO/tableio_plgs.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/imagepy/lang/Chinese/plugins/Plugins.dic b/imagepy/lang/Chinese/plugins/Plugins.dic index e50e3e9f..bae8493b 100644 --- a/imagepy/lang/Chinese/plugins/Plugins.dic +++ b/imagepy/lang/Chinese/plugins/Plugins.dic @@ -1,3 +1,5 @@ +New::新建 + New Filter::新建Filter New Simple::新建Simple diff --git a/imagepy/menus/Table/Table IO/tableio_plgs.py b/imagepy/menus/Table/Table IO/tableio_plgs.py index de327b9e..5267b714 100644 --- a/imagepy/menus/Table/Table IO/tableio_plgs.py +++ b/imagepy/menus/Table/Table IO/tableio_plgs.py @@ -13,7 +13,7 @@ def show(data, title): class OpenCSV(dataio.Reader): title = 'CSV Open' tag = 'tab' - filt = 'csv' + filt = ['csv'] class SaveCSV(dataio.TableWriter): title = 'CSV Save' From e7432a0e7d5684436a506cb833ad31c17f202f22 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 17 Jul 2020 17:36:42 +0800 Subject: [PATCH 301/343] nothing --- sciapp/object/image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sciapp/object/image.py b/sciapp/object/image.py index ee386c82..6e03c020 100644 --- a/sciapp/object/image.py +++ b/sciapp/object/image.py @@ -53,7 +53,7 @@ def __init__(self, imgs=None, name='Image'): self.msk = None self.pos = (0,0) self.cn = 0 - self.rg = (0,255) + self.rg = [(0,255)] self.lut = default_lut self.log = False self.mode = 'set' From fce47bca61fc2154f60412a2216ba96c14c97451 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Mon, 20 Jul 2020 13:04:27 +0800 Subject: [PATCH 302/343] translation for table plugin --- imagepy/lang/Chinese/plugins/table.dic | 162 ++++++++++++++++++ .../menus/Table/Basic Operator/basic_plgs.py | 6 +- .../menus/Table/Statistic/statistic_plgs.py | 2 +- 3 files changed, 166 insertions(+), 4 deletions(-) create mode 100644 imagepy/lang/Chinese/plugins/table.dic diff --git a/imagepy/lang/Chinese/plugins/table.dic b/imagepy/lang/Chinese/plugins/table.dic new file mode 100644 index 00000000..cda5fd21 --- /dev/null +++ b/imagepy/lang/Chinese/plugins/table.dic @@ -0,0 +1,162 @@ +Table IO::表格输入输出 + +CSV Open::打开CSV文件 + +CSV Save::保存CSV文件 + +Excel Open::打开Excel文件 + +Excel Save::保存Excel文件 + +Universal Generator::通用生成器 + +Unit Matrix::单元矩阵 + size::尺寸 + +Uniform Random::均匀随机分布矩阵 + low::最小值 + high::最大值 + row::行数 + col::列数 + +Gaussian Random::高斯随机分布矩阵 + mean::均值 + std::标准差 + row::行数 + col::列数 + +Calendar::日历 + year::年份 + month::月份 + +Basic Operator::基础操作 + +Table Transpose::表格转置 + +Table Duplicate::表格复制 + name::名称 + +Table Crop::表格裁剪 + +Delete Rows::删除行 + +Delete Columns::删除列 + +Append Rows::添加行 + count::行数 + fill by last row::用最后一行填充 + +Add Column::添加列 + name::名称 + value::数值 + +Statistic::统计 + +Table Statistic::表格统计 + axis::轴 + sum::和 + mean::均值 + max::最大值 + min::最小值 + var::方差 + std::标准差 + skew::偏斜度 + kurt::峭度 + +Group Statistic::分组统计 + field to statistic::选择统计区域 + group by::组类别 + major::主组 + minor::副组 + sum::和 + mean::均值 + max::最大值 + min::最小值 + var::方差 + std::标准差 + skew::偏斜度 + +Values Frequency::数值频率 + fields to count::选择统计区域 + +Table Bins Frequency::表格直方图频率 + bins::直方图 + n::个数 + auto scale::自动变换范围 + min range::最小值 + max range::最大值 + fields to count::选择统计区域 + count times::统计出现次数 + count frequency::统计出现频率 + count weight::统计权重 + +Table Sort By Key::通过键值排序 + major::主类 + key::键值 + minor::副类 + descend::降序 + +Selection::选择 + +Signal::信号处理 + +Signal Uniform Filter::信号均匀滤波器 + size::尺寸 + +Chart::作图 + +Plot Chart::折线图 + title::名称 + select fields::选择作图区域 + line width::线宽 + grid::显示网格 + +Area Chart::面积图 + title::名称 + select fields::选择作图区域 + alpha::透明度 + stacked::层叠显示 + grid::显示网格 + +Bar Chart::条形图 + title::名称 + select fields::选择作图区域 + horizon::水平显示 + stacked::层叠 + grid::显示网格 + +Box Chart::箱形图 + title::名称 + select fields::选择作图区域 + by::通过 + group::组别 + horizontal::水平显示 + grid::显示网格 + +Hist Chart::直方图 + title::名称 + select fields::选择作图区域 + bins::直方图区间个数 + alpha::透明度 + orientation::显示方向 + stacked::层叠显示 + draw every columns in one::所有列在一张图中绘制 + grid::显示网格 + +Pie Chart::饼图 + title::名称 + select fields::选择作图区域 + +Scatter Chart::散点图 + title::名称 + x data::x轴数据 + y data::y轴数据 + size::尺寸 + pix::像素数 + radius column::半径按照此列 + optional::可选 + alpha::透明度 + color::颜色 + color column::颜色按照此列 + color map::颜色映射 + grid::显示网格 \ No newline at end of file diff --git a/imagepy/menus/Table/Basic Operator/basic_plgs.py b/imagepy/menus/Table/Basic Operator/basic_plgs.py index ccaa70b3..d6588c84 100644 --- a/imagepy/menus/Table/Basic Operator/basic_plgs.py +++ b/imagepy/menus/Table/Basic Operator/basic_plgs.py @@ -6,8 +6,8 @@ class Transpose(Table): def run(self, tps, snap, data, para = None): tps.data = data.T -class Corp(Table): - title = 'Table Corp' +class Crop(Table): + title = 'Table Crop' note = ['req_sel'] def run(self, tps, snap, data, para): tps.data = tps.subtab() @@ -57,4 +57,4 @@ class AddCol(Table): def run(self, tps, snap, data, para = None): data[para['name']] = para['value'] -plgs = [Transpose, Duplicate, Corp, '-', DeleteRow, DeleteCol, AppendRow, AddCol] \ No newline at end of file +plgs = [Transpose, Duplicate, Crop, '-', DeleteRow, DeleteCol, AppendRow, AddCol] \ No newline at end of file diff --git a/imagepy/menus/Table/Statistic/statistic_plgs.py b/imagepy/menus/Table/Statistic/statistic_plgs.py index 1fb67b38..b0168570 100644 --- a/imagepy/menus/Table/Statistic/statistic_plgs.py +++ b/imagepy/menus/Table/Statistic/statistic_plgs.py @@ -41,7 +41,7 @@ class GroupStatistic(Table): view = [('fields', 'cn', 'field to statistic'), ('field', 'major', 'group by', 'major'), - ('field', 'minor', 'group by', 'key'), + ('field', 'minor', 'group by', 'minor'), (bool, 'sum', 'sum'), (bool, 'mean', 'mean'), From 0cddbd3f7c53286623c2dca37b9224e923546996 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Tue, 21 Jul 2020 09:43:05 +0800 Subject: [PATCH 303/343] complete translation for table --- imagepy/lang/Chinese/plugins/table.dic | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/imagepy/lang/Chinese/plugins/table.dic b/imagepy/lang/Chinese/plugins/table.dic index cda5fd21..c2ff9e90 100644 --- a/imagepy/lang/Chinese/plugins/table.dic +++ b/imagepy/lang/Chinese/plugins/table.dic @@ -98,6 +98,13 @@ Table Sort By Key::通过键值排序 Selection::选择 +Sel By C Name R Count::通过列名和行号选择 + fields::所选列 + all columns::选择所有列 + start rows::起始行 + end rows::结束行 + all rows::选择所有行 + Signal::信号处理 Signal Uniform Filter::信号均匀滤波器 From 749026c3bc8b8d10eea34a24250d5e32f1cb41b1 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Wed, 29 Jul 2020 15:35:18 +0800 Subject: [PATCH 304/343] fix the error of filter name in macros --- imagepy/menus/Plugins/Macros/recorder_plg.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imagepy/menus/Plugins/Macros/recorder_plg.py b/imagepy/menus/Plugins/Macros/recorder_plg.py index 9de2ded7..c34a9f82 100644 --- a/imagepy/menus/Plugins/Macros/recorder_plg.py +++ b/imagepy/menus/Plugins/Macros/recorder_plg.py @@ -9,7 +9,7 @@ def readmc(path): class Macros(dataio.Reader): title = 'Run Macros' tag = 'mc' - filt = 'MC' + filt = ['MC'] def readwf(path): with open(path) as f: return f.read() @@ -19,6 +19,6 @@ def readwf(path): class WorkFlow(dataio.Reader): title = 'Run WorkFlow' tag = 'wf' - filt = 'wf' + filt = ['wf'] plgs = [Macros, WorkFlow] \ No newline at end of file From ffe23ad794b3e39f1acf555ea74ae2f5eb9c382e Mon Sep 17 00:00:00 2001 From: qixinbo Date: Wed, 29 Jul 2020 16:54:41 +0800 Subject: [PATCH 305/343] fix bug when running histogram macro --- sciwx/plugins/histogram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sciwx/plugins/histogram.py b/sciwx/plugins/histogram.py index e666ec0c..daee9211 100644 --- a/sciwx/plugins/histogram.py +++ b/sciwx/plugins/histogram.py @@ -3,7 +3,7 @@ from sciapp import Source class Histogram( wx.Panel ): - title = 'Histogram' + title = 'Histogram Widget' def __init__( self, parent, app): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 255,0 ), style = wx.TAB_TRAVERSAL ) From 319127bedc85f4c18e139f9d403e5c7688ade171 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Thu, 30 Jul 2020 15:31:42 +0800 Subject: [PATCH 306/343] fix the bug of wrong active image --- sciapp/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sciapp/app.py b/sciapp/app.py index 7b189e44..ad974f2e 100644 --- a/sciapp/app.py +++ b/sciapp/app.py @@ -37,7 +37,7 @@ def show_img(self, img, name): if not isinstance(img, Image): img = Image(img, name) img.name = self.img_manager.name(name) - self.img_manager.add(name, img) + self.img_manager.add(img.name, img) print(img.info) def close_img(self, name): From 290708e5670fc9eddb72d9bcf09d794d7bd0281b Mon Sep 17 00:00:00 2001 From: qixinbo Date: Tue, 4 Aug 2020 13:48:02 +0800 Subject: [PATCH 307/343] fix bug in gray stair --- imagepy/menus/Image/Adjust/graystairs_plg.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/imagepy/menus/Image/Adjust/graystairs_plg.py b/imagepy/menus/Image/Adjust/graystairs_plg.py index d812b4a3..fa5ef013 100644 --- a/imagepy/menus/Image/Adjust/graystairs_plg.py +++ b/imagepy/menus/Image/Adjust/graystairs_plg.py @@ -27,12 +27,12 @@ def load(self, ips): #process def run(self, ips, snap, img, para = None): - if not ips.dtype != np.uint8: + if ips.dtype != np.uint8: ips.range = para['thre_lh'] return img[:] = snap - np.subtract(img, para['thr1'], out=img, casting='unsafe') - k = 255.0/max(para['thr2']-para['thr1'], 1e-10) + np.subtract(img, para['thre_lh'][0], out=img, casting='unsafe') + k = 255.0/max(para['thre_lh'][1]-para['thre_lh'][0], 1e-10) np.multiply(img, k, out=img, casting='unsafe') img[snappara['thre_lh'][1]] = 255 \ No newline at end of file From 6c7211327ecfe65b086512f1cf4cdeb810c7a47f Mon Sep 17 00:00:00 2001 From: qixinbo Date: Tue, 4 Aug 2020 15:08:37 +0800 Subject: [PATCH 308/343] tranlation for hydrology --- imagepy/lang/Chinese/plugins/process.dic | 31 ++++++++++++++----- .../menus/Process/Hydrology/hydrology_plgs.py | 2 +- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/imagepy/lang/Chinese/plugins/process.dic b/imagepy/lang/Chinese/plugins/process.dic index f4f1c6e9..67639b75 100644 --- a/imagepy/lang/Chinese/plugins/process.dic +++ b/imagepy/lang/Chinese/plugins/process.dic @@ -140,21 +140,38 @@ Find Minimum::查找极小值 tolerance::容错 Find IsoLine::查找等高线 - low::低阈值 - high::高阈值 + low::下阈值 + high::上阈值 step::步长 + output::输出 -Find Riedge::查找山脊线 +Find Ridge::查找山脊线 sigma::标准差 + Low::阈值 + ascend::涨水 + output::输出 Active Ridge::交互查找山脊线 + Low::阈值 + ascend::涨水 + output::输出 Find Watershed::分水岭 sigma::标准差 - -Up And Down Watershed::高低阈值分水岭 - -Watershed With ROI::区分作为种子分水岭 + Low::阈值 + full connectivity::八邻域连通 + ascend::涨水 + output::输出 + +Up And Down Watershed::上下阈值分水岭 + Low::下阈值 + High::上阈值 + output::输出 + +Watershed With ROI::带ROI的分水岭 + full connectivity::八邻域连通 + ascend::涨水 + output::输出 Features::特征点选取 diff --git a/imagepy/menus/Process/Hydrology/hydrology_plgs.py b/imagepy/menus/Process/Hydrology/hydrology_plgs.py index 171d61bc..6c0cf765 100644 --- a/imagepy/menus/Process/Hydrology/hydrology_plgs.py +++ b/imagepy/menus/Process/Hydrology/hydrology_plgs.py @@ -58,7 +58,7 @@ def run(self, ips, snap, img, para = None): ips.update() class UPRidge(Filter): - title = 'Find Riedge' + title = 'Find Ridge' note = ['8-bit', 'not_slice', 'auto_snap', 'not_channel', 'preview'] para = {'sigma':1.0, 'thr':0, 'ud':True, 'type':'white line'} From 5e3046dbf5902823e4dedfe2964b48cba8cda3cc Mon Sep 17 00:00:00 2001 From: qixinbo Date: Fri, 14 Aug 2020 11:43:11 +0800 Subject: [PATCH 309/343] point value should consider multiple points --- imagepy/menus/Analysis/statistic_plg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/menus/Analysis/statistic_plg.py b/imagepy/menus/Analysis/statistic_plg.py index 392841d0..8710c8d5 100644 --- a/imagepy/menus/Analysis/statistic_plg.py +++ b/imagepy/menus/Analysis/statistic_plg.py @@ -175,7 +175,7 @@ class PointsValue(Simple): (bool, 'slice', 'slice')] def load(self, ips): - if ips.roi.roitype != 'point': + if ips.roi.roitype != 'point' and ips.roi.roitype != 'points': return self.app.alert('a PointRoi needed!') return True From e0ce68f0e4af001d92fd6b711c45b3410a690095 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Wed, 19 Aug 2020 15:55:51 +0800 Subject: [PATCH 310/343] label tool corrected --- imagepy/menus/Process/Classify/io_plgs.py | 9 +++++---- imagepy/menus/Process/Classify/label_wgt.py | 22 ++++++++++----------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/imagepy/menus/Process/Classify/io_plgs.py b/imagepy/menus/Process/Classify/io_plgs.py index e5284857..09375e37 100644 --- a/imagepy/menus/Process/Classify/io_plgs.py +++ b/imagepy/menus/Process/Classify/io_plgs.py @@ -1,4 +1,5 @@ from sciapp.action import Filter, Simple +from sciapp.object import Image from imagepy.app import ColorManager # from imagepy.core import ImagePlus import numpy as np @@ -16,14 +17,14 @@ class BuildMark(Simple): def run(self, ips, imgs, para = None): shp = ips.img.shape[:2] - imgs = [np.zeros(shp, dtype=np.uint8) for i in range([1, len(imgs)][para['slice']])] - newips = ImagePlus(imgs, ips.title+'-mark') + imgs = [np.zeros(shp+(3,), dtype=np.uint8) for i in range([1, len(imgs)][para['slice']])] + newips = Image(imgs, ips.title+'-mark') newips.back = ips idx = ['None', 'Max', 'Min', 'Mask', '2-8mix', '4-6mix', '5-5mix', '6-4mix', '8-2mix'] modes = ['set', 'max', 'min', 'msk', 0.2, 0.4, 0.5, 0.6, 0.8] newips.lut = ColorManager.get(para['cm']) - newips.chan_mode = modes[idx.index(para['mode'])] + newips.mode = modes[idx.index(para['mode'])] #newips.range = (0, para['n']) - IPy.show_ips(newips) + self.app.show_img(newips) plgs = [BuildMark] \ No newline at end of file diff --git a/imagepy/menus/Process/Classify/label_wgt.py b/imagepy/menus/Process/Classify/label_wgt.py index e63495b2..c5199342 100644 --- a/imagepy/menus/Process/Classify/label_wgt.py +++ b/imagepy/menus/Process/Classify/label_wgt.py @@ -103,10 +103,10 @@ def on_pen(self, width): tol.start() def on_color(self, event): - ColorManager.set_front(self.btns.index(event.GetEventObject())) + self.app.manager('color').add('front', tuple(self.cs[self.btns.index(event.GetEventObject())])) def on_items(self, event): - items = ['No Background Image']+ImageManager.get_titles() + items = ['No Background Image']+self.app.img_names() self.com_back.SetItems(items) if self.com_back.GetValue() in items: self.com_back.Select(items.index(self.com_back.GetValue())) @@ -114,14 +114,14 @@ def on_items(self, event): def on_cmapsel(self, event): key = self.cmapsel.GetValue() - lut = ColorManager.gets(key) + lut = ColorManager.get(key) n = self.spn_num.GetValue()+1 idx = np.linspace(0, 255, n).astype(int) - cs = list(lut[idx]) + [(128,128,128)]*(16-n) - for btn, c in zip(self.btns, cs): + self.cs = list(lut[idx]) + [(128,128,128)]*(16-n) + for btn, c in zip(self.btns, self.cs): btn.SetBackgroundColour(c) - ips = ImageManager.get() + ips = self.app.get_img() if ips is None: return newlut = lut*0 newlut[:n] = lut[idx] @@ -131,19 +131,19 @@ def on_cmapsel(self, event): def on_setback(self, event): name = self.com_back.GetValue() if name is None: return - ImageManager.get().back = ImageManager.get(name) + self.app.get_img().back = self.app.get_img(name) #curwin = WindowsManager.get() #curwin.set_back(ImageManager.get(name)) - ImageManager.get().update() + self.app.get_img().update() def on_mode(self, event): - ips = ImageManager.get() + ips = self.app.get_img() if ips is None: return if self.chk_hide.GetValue(): - ips.chan_mode = 0.0 + ips.mode = 0.0 return ips.update() modes = ['set', 'max', 'min', 'msk', 0.2, 0.4, 0.5, 0.6, 0.8] - ips.chan_mode = modes[self.com_mode.GetSelection()] + ips.mode = modes[self.com_mode.GetSelection()] ips.update() def __del__( self ): From 927f693835570b23d598ee157e16924cf108ab7d Mon Sep 17 00:00:00 2001 From: qixinbo Date: Thu, 20 Aug 2020 22:29:12 +0800 Subject: [PATCH 311/343] Add correct tool name to plugin manager --- imagepy/app/imagej.py | 2 +- imagepy/app/imagepy.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/imagepy/app/imagej.py b/imagepy/app/imagej.py index 84af485b..dcc7d7b6 100644 --- a/imagepy/app/imagej.py +++ b/imagepy/app/imagej.py @@ -95,7 +95,7 @@ def _load_all(self): self.load_menu(plgs) tols, errtol = load_tools() for name, plg in self.flatten(tols): - self.add_plugin(name, plg, 'tool') + self.add_plugin(plg.title, plg, 'tool') self.load_tool(tols, 'Transform') wgts, errwgt = load_widgets() for name, plg in self.flatten(wgts): diff --git a/imagepy/app/imagepy.py b/imagepy/app/imagepy.py index 636f76b5..d5662a0a 100644 --- a/imagepy/app/imagepy.py +++ b/imagepy/app/imagepy.py @@ -96,7 +96,7 @@ def _load_all(self): self.load_menu(plgs) tols, errtol = load_tools() for name, plg in self.flatten(tols): - self.add_plugin(name, plg, 'tool') + self.add_plugin(plg.title, plg, 'tool') self.load_tool(tols, 'Transform') wgts, errwgt = load_widgets() for name, plg in self.flatten(wgts): From 985efaaf4a083b8e79ea06fb7064b4380c6e0982 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Sun, 23 Aug 2020 16:01:46 +0800 Subject: [PATCH 312/343] label tool and classifier are ok --- .../menus/Process/Classify/classify_plgs.py | 25 ++++----- .../menus/Process/Classify/classify_wgt.py | 53 ++++++++++--------- imagepy/menus/Process/Classify/io_plgs.py | 2 +- imagepy/menus/Process/Classify/label_wgt.py | 10 ++-- imagepy/menus/Process/Classify/predict_plg.py | 17 +++--- 5 files changed, 55 insertions(+), 52 deletions(-) diff --git a/imagepy/menus/Process/Classify/classify_plgs.py b/imagepy/menus/Process/Classify/classify_plgs.py index bbfe1bd1..54746364 100644 --- a/imagepy/menus/Process/Classify/classify_plgs.py +++ b/imagepy/menus/Process/Classify/classify_plgs.py @@ -1,5 +1,5 @@ from sciapp.action import Filter, Simple -# from imagepy.core import ImagePlus +from sciapp.object import Image import numpy as np from sklearn.ensemble import RandomForestClassifier, \ @@ -12,7 +12,8 @@ class Base(Simple): """Closing: derived from sciapp.action.Filter """ - def load(self, ips): + def load(self, ips): + print("len(ips.imgs) = ", len(ips.imgs)) if len(ips.imgs)==1: ips.snapshot() return True @@ -31,29 +32,29 @@ def run(self, ips, imgs, para = None, preview=False): if len(ips.imgs)==1: ips.img[:] = ips.snap key = {'chans':None, 'grade':para['grade'], 'w':para['w']} key['items'] = [i for i in ['ori', 'blr', 'sob', 'eig'] if para[i]] - slir, slic = ips.get_rect() + slir, slic = ips.rect labs = [i[slir, slic] for i in imgs] - ori = ImageManager.get(para['img']).imgs - if len(imgs)==1: ori = [ImageManager.get(para['img']).img] + ori = self.app.get_img(para['img']).imgs + if len(imgs)==1: ori = [self.app.get_img(para['img']).img] oris = [i[slir, slic] for i in ori] - IPy.info('extract features...') + self.app.info('extract features...') feat, lab, key = feature.get_feature(oris, labs, key, callback=self.progress) - IPy.info('training data...') - self.progress(None, 1) + self.app.info('training data...') + # self.progress(None, 1) model = self.classify(para) model.fit(feat, lab) - IPy.info('predict data...') + self.app.info('predict data...') if preview: return feature.get_predict(oris, model, key, labs, callback=self.progress) if len(imgs) == 1: ips.swap() outs = feature.get_predict(oris, model, key, callback=self.progress) - nips = ImagePlus(outs, ips.title+'rst') + nips = Image(outs, ips.title+'rst') nips.range, nips.lut = ips.range, ips.lut - nips.back, nips.chan_mode = ips.back, 0.4 - IPy.show_ips(nips) + nips.back, nips.mode = ips.back, 0.4 + self.app.show_img(nips) global model_para model_para = model, key diff --git a/imagepy/menus/Process/Classify/classify_wgt.py b/imagepy/menus/Process/Classify/classify_wgt.py index 888b58d7..5f68ad08 100644 --- a/imagepy/menus/Process/Classify/classify_wgt.py +++ b/imagepy/menus/Process/Classify/classify_wgt.py @@ -3,7 +3,7 @@ #from imagepy.core.manager import RoiManager, ImageManager, ReaderManager, ViewerManager from . import classify_plgs as manager from .predict_plg import Plugin as FCL -#from imagepy import IPy +from imagepy import root_dir #ReaderManager.add('fcl', lambda x:x, 'fcl') #ViewerManager.add('fcl', lambda x,n:wx.CallAfter(FCL(path=x).start)) @@ -11,9 +11,9 @@ class Plugin( wx.Panel ): title = 'Feature Classify Panel' - def __init__( self, parent ): + def __init__( self, parent, app=None): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size(256, 256), style = wx.TAB_TRAVERSAL ) - + self.app = app sizer = wx.BoxSizer( wx.HORIZONTAL ) sizer_btns = wx.BoxSizer( wx.VERTICAL ) @@ -53,7 +53,7 @@ def __init__( self, parent ): self.Layout() def LoadModel(self): - fs = glob(osp.join(IPy.root_dir, 'data/ilastik/*.fcl')) + fs = glob(osp.join(root_dir, 'data/ilastik/*.fcl')) self.models = [osp.split(i)[1] for i in fs] self.lst_model.SetItems(self.models) @@ -68,52 +68,53 @@ def AddEvent(self): def on_save(self, event): if manager.model_para is None: - return IPy.alert('you must train your model first!') + return self.app.alert('you must train your model first!') para = {'name':'New Model'} - if not IPy.get_para('name', [(str, 'name', 'model', 'name')], para): return - if not osp.exists(osp.join(IPy.root_dir, 'data/ilastik')): - os.mkdir(osp.join(IPy.root_dir, 'data/ilastik')) - joblib.dump( manager.model_para, osp.join(IPy.root_dir, 'data/ilastik/%s.fcl'%para['name'])) + if not self.app.show_para('name', para, [(str, 'name', 'model', 'name')]): return + if not osp.exists(osp.join(root_dir, 'data/ilastik')): + os.mkdir(osp.join(root_dir, 'data/ilastik')) + joblib.dump( manager.model_para, osp.join(root_dir, 'data/ilastik/%s.fcl'%para['name'])) self.LoadModel() def on_saveas(self, event): if manager.model_para is None: - return IPy.alert('you must train your model first!') - para = {'path':''} - filt = '|'.join(['%s files (*.%s)|*.%s'%('FCL', 'fcl', 'fcl')]) - if not IPy.get_path('Save..', filt, 'save', para): return + return self.app.alert('you must train your model first!') + para = {'path':'', 'name':''} + filt = ['fcl'] + print("filt = ", filt) + para['path'] = self.app.get_path('Save..', filt, 'save', para['name']) joblib.dump( manager.model_para, para['path']) def on_export(self, event): idx = self.lst_model.GetSelection() - if idx==-1: return IPy.alert('no model selected!') - para = {'path':''} - filt = '|'.join(['%s files (*.%s)|*.%s'%('FCL', 'fcl', 'fcl')]) - if not IPy.get_path('Save..', filt, 'save', para): return - oldname = osp.join(IPy.root_dir, 'data/ilastik/%s'%self.lst_model.GetStringSelection()) + if idx==-1: return self.app.alert('no model selected!') + para = {'path':'', 'name':''} + filt = ['fcl'] + para['path'] = self.app.get_path('Save..', filt, 'save', para['name']) + oldname = osp.join(root_dir, 'data/ilastik/%s'%self.lst_model.GetStringSelection()) print(para['path']) shutil.copyfile(oldname, para['path']) self.LoadModel() def on_rename(self, event): idx = self.lst_model.GetSelection() - if idx==-1: return IPy.alert('no model selected!') + if idx==-1: return self.app.alert('no model selected!') para = {'name':'New Model'} - if not IPy.get_para('name', [(str, 'name', 'model', 'name')], para): return - oldname = osp.join(IPy.root_dir, 'data/ilastik/%s'%self.lst_model.GetStringSelection()) - os.rename(oldname, osp.join(IPy.root_dir, 'data/ilastik/%s.fcl'%para['name'])) + if not self.app.show_para('name', para, [(str, 'name', 'model', 'name')]): return + oldname = osp.join(root_dir, 'data/ilastik/%s'%self.lst_model.GetStringSelection()) + os.rename(oldname, osp.join(root_dir, 'data/ilastik/%s.fcl'%para['name'])) self.LoadModel() def on_remove(self, event): idx = self.lst_model.GetSelection() - if idx==-1: return IPy.alert('no model selected!') - os.remove(osp.join(IPy.root_dir, 'data/ilastik/%s'%self.lst_model.GetStringSelection())) + if idx==-1: return self.app.alert('no model selected!') + os.remove(osp.join(root_dir, 'data/ilastik/%s'%self.lst_model.GetStringSelection())) self.LoadModel() def on_run(self, event): idx = self.lst_model.GetSelection() - if idx==-1: return IPy.alert('no model selected!') - FCL(path=self.lst_model.GetStringSelection()).start() + if idx==-1: return self.app.alert('no model selected!') + FCL(path=self.lst_model.GetStringSelection()).start(self.app) def __del__( self ): pass diff --git a/imagepy/menus/Process/Classify/io_plgs.py b/imagepy/menus/Process/Classify/io_plgs.py index 09375e37..f1ea3346 100644 --- a/imagepy/menus/Process/Classify/io_plgs.py +++ b/imagepy/menus/Process/Classify/io_plgs.py @@ -17,7 +17,7 @@ class BuildMark(Simple): def run(self, ips, imgs, para = None): shp = ips.img.shape[:2] - imgs = [np.zeros(shp+(3,), dtype=np.uint8) for i in range([1, len(imgs)][para['slice']])] + imgs = [np.zeros(shp, dtype=np.uint8) for i in range([1, len(imgs)][para['slice']])] newips = Image(imgs, ips.title+'-mark') newips.back = ips idx = ['None', 'Max', 'Min', 'Mask', '2-8mix', '4-6mix', '5-5mix', '6-4mix', '8-2mix'] diff --git a/imagepy/menus/Process/Classify/label_wgt.py b/imagepy/menus/Process/Classify/label_wgt.py index c5199342..c87955df 100644 --- a/imagepy/menus/Process/Classify/label_wgt.py +++ b/imagepy/menus/Process/Classify/label_wgt.py @@ -93,17 +93,17 @@ def on_make(self, event): Macros(None, ['Build Mark Image>None']).start(self.app) def on_fill(self, event): - tol = ToolsManager.get('Flood Fill')() + tol = self.app.get_plugin('Flood Fill')() tol.para['tor'] = 0 - tol.start() + tol.start(self.app) def on_pen(self, width): - tol = ToolsManager.get('Pencil')() + tol = self.app.get_plugin('Pencil')() tol.para['width'] = width - tol.start() + tol.start(self.app) def on_color(self, event): - self.app.manager('color').add('front', tuple(self.cs[self.btns.index(event.GetEventObject())])) + self.app.manager('color').add('front', self.btns.index(event.GetEventObject())) def on_items(self, event): items = ['No Background Image']+self.app.img_names() diff --git a/imagepy/menus/Process/Classify/predict_plg.py b/imagepy/menus/Process/Classify/predict_plg.py index aa5d7332..9aab7a4d 100644 --- a/imagepy/menus/Process/Classify/predict_plg.py +++ b/imagepy/menus/Process/Classify/predict_plg.py @@ -1,9 +1,10 @@ from sciapp.action import Simple -# from imagepy.core import ImagePlus +from sciapp.object import Image from glob import glob import os.path as osp import joblib from imagepy.ipyalg import feature +from imagepy import root_dir class Plugin(Simple): title = 'Feature Predictor' @@ -14,13 +15,13 @@ class Plugin(Simple): (bool, 'slice', 'slice')] def __init__(self, ips=None, path=''): - Simple.__init__(self, ips) + Simple.__init__(self) self.model = None - self.root = osp.join(IPy.root_dir, 'data/ilastik') + self.root = osp.join(root_dir, 'data/ilastik') fs = glob(osp.join(self.root, '*.fcl')) fs = [osp.split(i)[1] for i in fs] if '/' in path: fs = [path] - if len(fs) == 0: return IPy.alert('No feature classfier found!') + if len(fs) == 0: return self.app.alert('No feature classfier found!') self.para['predictor'] = fs[0] self.view[0] = (list, 'predictor', fs, str, 'predictor', '') @@ -30,11 +31,11 @@ def run(self, ips, imgs, para=None): else: path = self.root+'/'+para['predictor'] model, key = joblib.load(path) if not para['slice']: imgs = [ips.img] - slir, slic = ips.get_rect() + slir, slic = ips.rect imgs = [i[slir, slic] for i in imgs] rst = feature.get_predict(imgs, model, key, callback=self.progress) if rst is None: - return IPy.alert('image channels dismatch this predictor!') - ips = ImagePlus(rst, ips.title+'-mark') + return self.app.alert('image channels dismatch this predictor!') + ips = Image(rst, ips.title+'-mark') ips.range = ips.get_updown('all', 'one', step=512) - IPy.show_ips(ips) \ No newline at end of file + self.app.show_img(ips) \ No newline at end of file From 5c6b94214af6f8f20aa9ac29e8273a117594a0e0 Mon Sep 17 00:00:00 2001 From: qixinbo Date: Sun, 23 Aug 2020 19:06:41 +0800 Subject: [PATCH 313/343] back img automatically appointed --- .../menus/Process/Classify/classify_plgs.py | 35 ++++++++----------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/imagepy/menus/Process/Classify/classify_plgs.py b/imagepy/menus/Process/Classify/classify_plgs.py index 54746364..a660c671 100644 --- a/imagepy/menus/Process/Classify/classify_plgs.py +++ b/imagepy/menus/Process/Classify/classify_plgs.py @@ -34,9 +34,8 @@ def run(self, ips, imgs, para = None, preview=False): key['items'] = [i for i in ['ori', 'blr', 'sob', 'eig'] if para[i]] slir, slic = ips.rect labs = [i[slir, slic] for i in imgs] - ori = self.app.get_img(para['img']).imgs - if len(imgs)==1: ori = [self.app.get_img(para['img']).img] - oris = [i[slir, slic] for i in ori] + ori = ips.back + oris = [ori.img[slir, slic]] self.app.info('extract features...') feat, lab, key = feature.get_feature(oris, labs, key, callback=self.progress) @@ -61,10 +60,9 @@ def run(self, ips, imgs, para = None, preview=False): class RandomForest(Base): title = 'Random Forest Classify' note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] - para = {'img':None, 'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, + para = {'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, 'eig':True, 'n_estimators':100, 'max_features':'sqrt', 'max_depth':0} - view = [('img', 'img', 'img', 'back'), - ('lab', None, '===== Classifier Parameter ====='), + view = [('lab', None, '===== Classifier Parameter ====='), (int, 'n_estimators', (10,1024), 0, 'estimators', 'n'), (int, 'max_depth', (0,64), 0, 'depth', 'max'), (list, 'max_features', ['sqrt', 'log2', 'None'], str, 'features', 'max'), @@ -86,10 +84,9 @@ class AdaBoost(Base): """Closing: derived from sciapp.action.Filter """ title = 'AdaBoost Classify' note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] - para = {'img':None, 'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, + para = {'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, 'eig':True, 'n_estimators':50, 'learning_rate':1, 'algorithm':'SAMME.R'} - view = [('img', 'img', 'img', 'back'), - ('lab', None, '===== Classifier Parameter ====='), + view = [('lab', None, '===== Classifier Parameter ====='), (int, 'n_estimators', (10,1024), 0, 'estimators', 'n'), (float, 'learning_rate', (0.1, 10), 1, 'learn', 'rate'), (list, 'algorithm', ['SAMME', 'SAMME.R'], str, 'algorithm', ''), @@ -109,10 +106,9 @@ class Bagging(Base): """Closing: derived from sciapp.action.Filter """ title = 'Bagging Classify' note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] - para = {'img':None, 'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, + para = {'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, 'eig':True, 'n_estimators':50, 'max_features':1.0} - view = [('img', 'img', 'img', 'back'), - ('lab', None, '===== Classifier Parameter ====='), + view = [('lab', None, '===== Classifier Parameter ====='), (int, 'n_estimators', (10,1024), 0, 'estimators', 'n'), (float, 'max_features', (0.2, 1), 1, 'features', 'k'), ('lab', None, '===== Feature Parameter ====='), @@ -131,10 +127,9 @@ class ExtraTrees(Base): """Closing: derived from sciapp.action.Filter """ title = 'ExtraTrees Classify' note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] - para = {'img':None, 'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, + para = {'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, 'eig':True, 'n_estimators':10, 'max_features':'sqrt', 'max_depth':0} - view = [('img', 'img', 'img', 'back'), - ('lab', None, '===== Classifier Parameter ====='), + view = [('lab', None, '===== Classifier Parameter ====='), (int, 'n_estimators', (10,1024), 0, 'estimators', 'n'), (int, 'max_depth', (0,64), 0, 'depth', 'max'), (list, 'max_features', ['sqrt', 'log2', 'None'], str, 'features', 'max'), @@ -156,11 +151,10 @@ class GradientBoosting(Base): """Closing: derived from sciapp.action.Filter """ title = 'Gradient Boosting Classify' note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] - para = {'img':None, 'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, + para = {'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, 'eig':True, 'n_estimators':100, 'max_features':'sqrt', 'max_depth':3, 'learning_rate':0.1, 'loss':'deviance'} - view = [('img', 'img', 'img', 'back'), - ('lab', None, '===== Classifier Parameter ====='), + view = [('lab', None, '===== Classifier Parameter ====='), (list, 'loss', ['deviance', 'exponential'], str, 'loss', ''), (int, 'n_estimators', (10,1024), 0, 'estimators', 'n'), (int, 'max_depth', (1,10), 0, 'depth', 'max'), @@ -184,11 +178,10 @@ class Voting(Base): """Closing: derived from sciapp.action.Filter """ title = 'Voting Classify' note = ['8-bit', 'auto_msk', 'not_slice', 'auto_snap', 'preview'] - para = {'img':None, 'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, + para = {'grade':3, 'w':1, 'ori':True, 'blr':True, 'sob':True, 'eig':True, 'n_estimators':100, 'max_features':'sqrt', 'max_depth':3, 'learning_rate':0.1, 'loss':'deviance'} - view = [('img', 'img', 'img', 'back'), - ('lab', None, '===== Classifier Parameter ====='), + view = [('lab', None, '===== Classifier Parameter ====='), (list, 'loss', ['deviance', 'exponential'], str, 'loss', ''), (int, 'n_estimators', (10,1024), 0, 'estimators', 'n'), (int, 'max_depth', (1,10), 0, 'depth', 'max'), From 7d51798166293264a4d6f2dd92c590f4e15524a6 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 4 Sep 2020 11:37:46 +0800 Subject: [PATCH 314/343] nothing --- imagepy/ipyalg/transform/__init__.py | 0 imagepy/ipyalg/transform/transform.py | 57 +++++++++++++++++++ imagepy/menus/Analysis/statistic_plg.py | 4 +- .../menus/Image/Transform/Transform_plgs.py | 18 +++++- imagepy/menus/Plugins/Cyclic Wave/__init__.py | 0 .../menus/Plugins/Cyclic Wave/dewave_plg.py | 25 ++++++++ sciapp/action/advanced/dataio.py | 1 - sciapp/action/advanced/filter.py | 4 +- sciapp/object/image.py | 8 +-- sciwx/widgets/normal.py | 2 +- 10 files changed, 108 insertions(+), 11 deletions(-) create mode 100644 imagepy/ipyalg/transform/__init__.py create mode 100644 imagepy/ipyalg/transform/transform.py create mode 100644 imagepy/menus/Plugins/Cyclic Wave/__init__.py create mode 100644 imagepy/menus/Plugins/Cyclic Wave/dewave_plg.py diff --git a/imagepy/ipyalg/transform/__init__.py b/imagepy/ipyalg/transform/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/ipyalg/transform/transform.py b/imagepy/ipyalg/transform/transform.py new file mode 100644 index 00000000..a2ead1eb --- /dev/null +++ b/imagepy/ipyalg/transform/transform.py @@ -0,0 +1,57 @@ +from skimage.io import imread +from skimage.data import astronaut, camera +from scipy.ndimage import map_coordinates +import numpy as np +# import matplotlib.pyplot as plt + +def linear_polar(img, o=None, r=None, output=None, order=1, cont=0): + if o is None: o = np.array(img.shape[:2])/2 - 0.5 + if r is None: r = (np.array(img.shape[:2])**2).sum()**0.5/2 + cns = 1 if img.ndim == 2 else img.shape[2] + if output is None: + shp = int(round(r)), int(round(r*2*np.pi)) + output = np.zeros(shp+(cns,), dtype=img.dtype) + elif isinstance(output, tuple): + output = np.zeros(output+(cns,), dtype=img.dtype) + out_h, out_w, _ = output.shape + out_img = np.zeros((out_h, out_w), dtype=img.dtype) + rs = np.linspace(0, r, out_h) + ts = np.linspace(0, np.pi*2, out_w) + xs = rs[:,None] * np.cos(ts) + o[1] + ys = rs[:,None] * np.sin(ts) + o[0] + img = img.reshape(img.shape[:2]+(-1,)) + for i in range(cns): + map_coordinates(img[:,:,i], (ys, xs), order=order, output=output[:,:,i]) + return output.reshape(output.shape[:2]) if output.shape[2]==1 else output + +def polar_linear(img, o=None, r=None, output=None, order=1, cont=0): + if r is None: r = img.shape[0] + cns = 1 if img.ndim == 2 else img.shape[2] + if output is None: + output = np.zeros((r*2, r*2, cns), dtype=img.dtype) + elif isinstance(output, tuple): + output = np.zeros(output+(cns,), dtype=img.dtype) + if o is None: o = np.array(output.shape[:2])/2 - 0.5 + out_h, out_w, _ = output.shape + ys, xs = np.mgrid[:out_h, :out_w] - o[:,None,None] + rs = (ys**2+xs**2)**0.5 + ts = np.arccos(xs/rs) + ts[ys<0] = np.pi*2 - ts[ys<0] + ts *= (img.shape[1]-1)/(np.pi*2) + img = img.reshape(img.shape[:2]+(-1,)) + for i in range(cns): + map_coordinates(img[:,:,i], (rs, ts), order=order, output=output[:,:,i]) + return output.reshape(output.shape[:2]) if output.shape[2]==1 else output + + +if __name__ == '__main__': + img = camera() + ax = plt.subplot(311) + ax.imshow(img) + out = linear_polar(img) + ax = plt.subplot(312) + ax.imshow(out) + img = polar_linear(out, output=img.shape[:2]) + ax = plt.subplot(313) + ax.imshow(img) + plt.show() \ No newline at end of file diff --git a/imagepy/menus/Analysis/statistic_plg.py b/imagepy/menus/Analysis/statistic_plg.py index 392841d0..8323b782 100644 --- a/imagepy/menus/Analysis/statistic_plg.py +++ b/imagepy/menus/Analysis/statistic_plg.py @@ -118,8 +118,8 @@ class Statistic(Simple): view = [(bool, 'max', 'max'), (bool, 'min', 'min'), (bool, 'mean', 'mean'), - (bool, 'variance', 'var'), - (bool, 'standard', 'std'), + (bool, 'var', 'variance'), + (bool, 'std', 'standard'), (bool, 'slice', 'slice')] def count(self, img, para): diff --git a/imagepy/menus/Image/Transform/Transform_plgs.py b/imagepy/menus/Image/Transform/Transform_plgs.py index 309a79dd..b27f7793 100644 --- a/imagepy/menus/Image/Transform/Transform_plgs.py +++ b/imagepy/menus/Image/Transform/Transform_plgs.py @@ -5,7 +5,8 @@ """ import numpy as np import scipy.ndimage as nimg -from sciapp.action import Filter +from sciapp.action import Filter, Simple +from imagepy.ipyalg.transform class Rotate(Filter): title = 'Rotate' @@ -40,5 +41,20 @@ def run(self, ips, snap, img, para = None): trans = np.array([[k,0],[0,k]]) offset = o-trans.dot(o) nimg.affine_transform(snap, trans, output=img, offset=offset) +''' +class LinearPolar(Simple): + title = 'Linear To Polar' + note = ['all'] + para = {'slice':False, 'order':1} + view = [(list, 'con', ['4-Connect','8-Connect'], str, 'Structure', 'connect'), + (bool, 'slice', 'slice')] + + def run(self, ips, imgs, para = None): + if not para['slice']: imgs = [ips.img] + labels = [] + for i in range(len(imgs)): + labels.append(lab) + self.app.show_img(labels, ips.title+'-label') +''' plgs = [Rotate, Scale] \ No newline at end of file diff --git a/imagepy/menus/Plugins/Cyclic Wave/__init__.py b/imagepy/menus/Plugins/Cyclic Wave/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/imagepy/menus/Plugins/Cyclic Wave/dewave_plg.py b/imagepy/menus/Plugins/Cyclic Wave/dewave_plg.py new file mode 100644 index 00000000..e5b3233c --- /dev/null +++ b/imagepy/menus/Plugins/Cyclic Wave/dewave_plg.py @@ -0,0 +1,25 @@ +import numpy as np +from sciapp.action import Filter +from imagepy.ipyalg.transform import transform +from numpy.fft import fft2, ifft2, fftshift, ifftshift + +class Plugin(Filter): + title = 'Depress Cyclic Wave' + note = ['all', 'auto_msk', 'auto_snap','preview'] + para = {'gap':0.01, 'lim':0.01} + view = [(float, 'lim', (0.001, 0.1), 3, 'limit', 'width'), + (float, 'gap', (0.001, 0.1), 3, 'gap', 'width')] + + def run(self, ips, snap, img, para = None): + lim, gap = para['lim'], para['gap'] + poimg = transform.linear_polar(snap) + h, w = poimg.shape[:2] + lim = max(1, int(w*lim)) + gap = max(1, int(gap*h)) + fftpoimg = fftshift(fft2(poimg)) + fftpoimg[:h//2-gap,w//2-lim:w//2+lim] = 0 + fftpoimg[h//2+gap:,w//2-lim:w//2+lim] = 0 + poimg = ifft2(ifftshift(fftpoimg)) + poimg = np.clip(poimg.real, snap.min(), snap.max()) + poimg = poimg.astype(snap.dtype) + transform.polar_linear(poimg, output=img[:,:,None]) \ No newline at end of file diff --git a/sciapp/action/advanced/dataio.py b/sciapp/action/advanced/dataio.py index 16b8132b..8824ba5a 100644 --- a/sciapp/action/advanced/dataio.py +++ b/sciapp/action/advanced/dataio.py @@ -1,6 +1,5 @@ import os from sciapp import Source -from imagepy import root_dir from . import Free, Simple, Table #from .macros import Macros from ... import Manager diff --git a/sciapp/action/advanced/filter.py b/sciapp/action/advanced/filter.py index 0a01cb2e..72a11131 100644 --- a/sciapp/action/advanced/filter.py +++ b/sciapp/action/advanced/filter.py @@ -61,7 +61,7 @@ def process_stack(plg, ips, src, imgs, para, callafter=None): for i,n in zip(imgs,list(range(len(imgs)))): #sleep(0.5) - plg.progress(n, len(imgs)) + plg.progress(n+1, len(imgs)) if 'auto_snap' in plg.note : src[:] = i if transint or transfloat: buf[:] = i rst = process_channels(plg, ips, src, buf if transint or transfloat else i, para) @@ -145,7 +145,7 @@ def ok(self, ips, para=None, callafter=None): has, rst = 'stack' in para, None if not has: rst = self.app.yes_no('Run every slice in current stacks?') - if 'auto_snap' in self.note and self.modal:ips.reset() + if 'auto_snap' in self.note and self.modal:ips.img[:] = ips.snap if has and para['stack'] or rst == 'yes': para['stack'] = True #process_stack(self, ips, ips.snap, ips.imgs, para) diff --git a/sciapp/object/image.py b/sciapp/object/image.py index b6744d93..b996944a 100644 --- a/sciapp/object/image.py +++ b/sciapp/object/image.py @@ -139,9 +139,9 @@ def subimg(self, s1=None, s2=None): def update(self): self.dirty = True def reset(self): - self.cn = [0, (0,1,2)][self.channels==3] - if self.dtype == np.uint8: pass - # self.rg = [(0, 255)] * self.channels + self.cn = [0, [0,1,2]][self.channels==3] + if self.dtype == np.uint8: + self.rg = [(0, 255)] * self.channels else: self.rg = self.get_updown('all', 'all', step=512) @@ -170,7 +170,7 @@ def get_updown(self, slices='all', chans='all', step=512): def lookup(self, img=None): if img is None: img = self.img - return lookup(img, self.cn, [self.rg], self.lut) + return lookup(img, self.cn, self.rg, self.lut) if __name__ == '__main__': img = Image(np.zeros((5,5))) diff --git a/sciwx/widgets/normal.py b/sciwx/widgets/normal.py index 901f882f..a5c21d7d 100644 --- a/sciwx/widgets/normal.py +++ b/sciwx/widgets/normal.py @@ -165,7 +165,7 @@ def onselect(self, event): dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} if self.io=='folder': dialog = wx.DirDialog(self, 'Path Select', '.', wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST | wx.FD_CHANGE_DIR) - else: dialog = wx.FileDialog(self, 'Path Select', None, '', filt, dict[self.io] | wx.FD_CHANGE_DIR) + else: dialog = wx.FileDialog(self, 'Path Select', '', '.', filt, dic[self.io] | wx.FD_CHANGE_DIR) rst = dialog.ShowModal() if rst == wx.ID_OK: path = dialog.GetPath() From 9e1af72bd2d2899c59190fcc9386aaf2be7a9979 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 4 Sep 2020 16:49:55 +0800 Subject: [PATCH 315/343] polar transform --- imagepy/ipyalg/__init__.py | 3 +- imagepy/ipyalg/transform/transform.py | 4 +- .../menus/Image/Transform/Transform_plgs.py | 45 +++++++++++++------ imagepy/menus/Plugins/Games/lifegame_plg.py | 4 +- imagepy/tools/Transform/rotate_tol.py | 11 ++--- imagepy/tools/Transform/scale_tol.py | 11 ++--- sciapp/action/tolact.py | 13 ++++-- sciapp/object/image.py | 1 + sciwx/canvas/mcanvas.py | 1 + 9 files changed, 54 insertions(+), 39 deletions(-) diff --git a/imagepy/ipyalg/__init__.py b/imagepy/ipyalg/__init__.py index 120831fb..c6b8367e 100644 --- a/imagepy/ipyalg/__init__.py +++ b/imagepy/ipyalg/__init__.py @@ -3,4 +3,5 @@ from .hydrology.isoline import stair, isoline from .hydrology.watershed import watershed from .hydrology.edt import distance_transform_edt -from .classify import feature \ No newline at end of file +from .classify import feature +from .transform.transform import linear_polar, polar_linear \ No newline at end of file diff --git a/imagepy/ipyalg/transform/transform.py b/imagepy/ipyalg/transform/transform.py index a2ead1eb..2ca87abd 100644 --- a/imagepy/ipyalg/transform/transform.py +++ b/imagepy/ipyalg/transform/transform.py @@ -4,7 +4,7 @@ import numpy as np # import matplotlib.pyplot as plt -def linear_polar(img, o=None, r=None, output=None, order=1, cont=0): +def linear_polar(img, o=None, r=None, order=1, cont=0, output=None): if o is None: o = np.array(img.shape[:2])/2 - 0.5 if r is None: r = (np.array(img.shape[:2])**2).sum()**0.5/2 cns = 1 if img.ndim == 2 else img.shape[2] @@ -24,7 +24,7 @@ def linear_polar(img, o=None, r=None, output=None, order=1, cont=0): map_coordinates(img[:,:,i], (ys, xs), order=order, output=output[:,:,i]) return output.reshape(output.shape[:2]) if output.shape[2]==1 else output -def polar_linear(img, o=None, r=None, output=None, order=1, cont=0): +def polar_linear(img, o=None, r=None, order=1, cont=0, output=None): if r is None: r = img.shape[0] cns = 1 if img.ndim == 2 else img.shape[2] if output is None: diff --git a/imagepy/menus/Image/Transform/Transform_plgs.py b/imagepy/menus/Image/Transform/Transform_plgs.py index b27f7793..8027c8a7 100644 --- a/imagepy/menus/Image/Transform/Transform_plgs.py +++ b/imagepy/menus/Image/Transform/Transform_plgs.py @@ -6,7 +6,7 @@ import numpy as np import scipy.ndimage as nimg from sciapp.action import Filter, Simple -from imagepy.ipyalg.transform +from imagepy.ipyalg import linear_polar, polar_linear class Rotate(Filter): title = 'Rotate' @@ -41,20 +41,37 @@ def run(self, ips, snap, img, para = None): trans = np.array([[k,0],[0,k]]) offset = o-trans.dot(o) nimg.affine_transform(snap, trans, output=img, offset=offset) -''' + class LinearPolar(Simple): title = 'Linear To Polar' - note = ['all'] - para = {'slice':False, 'order':1} + note = ['all'] + para = {'ext':'crop', 'order':1, 'slices':False} + view = [(list, 'ext', ['full', 'crop'], str, 'extent', ''), + (int, 'order', (0, 5), 0, 'interpolate', 'order'), + (bool, 'slices', 'slices')] - view = [(list, 'con', ['4-Connect','8-Connect'], str, 'Structure', 'connect'), - (bool, 'slice', 'slice')] - - def run(self, ips, imgs, para = None): - if not para['slice']: imgs = [ips.img] - labels = [] + def run(self, ips, imgs, para): + if not para['slices']: imgs = [ips.img] + r, rst = min(ips.shape[:2])/2 if para['ext']=='crop' else None, [] + for i in range(len(imgs)): + self.progress(i, len(imgs)) + rst.append(linear_polar(imgs[i], None, r, para['order'])) + self.app.show_img(rst, ips.title + '-polar') + +class PolarLinear(Simple): + title = 'Polar To Linear' + note = ['all'] + para = {'ext':'crop', 'order':1, 'slices':False} + view = [(list, 'ext', ['full', 'crop'], str, 'extent', ''), + (int, 'order', (0, 5), 0, 'interpolate', 'order'), + (bool, 'slices', 'slices')] + + def run(self, ips, imgs, para): + if not para['slices']: imgs = [ips.img] + r, rst = round(ips.shape[0]/(2**0.5 if para['ext']=='crop' else 1)), [] for i in range(len(imgs)): - labels.append(lab) - self.app.show_img(labels, ips.title+'-label') -''' -plgs = [Rotate, Scale] \ No newline at end of file + self.progress(i, len(imgs)) + rst.append(polar_linear(imgs[i], None, int(r), para['order'])) + self.app.show_img(rst, ips.title + '-polar') + +plgs = [Rotate, Scale, LinearPolar, PolarLinear] \ No newline at end of file diff --git a/imagepy/menus/Plugins/Games/lifegame_plg.py b/imagepy/menus/Plugins/Games/lifegame_plg.py index d497caa5..103467cb 100644 --- a/imagepy/menus/Plugins/Games/lifegame_plg.py +++ b/imagepy/menus/Plugins/Games/lifegame_plg.py @@ -40,7 +40,7 @@ def mouse_down(self, ips, x, y, btn, **key): for i in range(len(ips.imgs)): ips.imgs[i][:] = generate(img, self.size) img = run(img) - IPy.alert('Complete!') + self.app.alert('Complete!') class Plugin(Free): title = 'Game Of Life' @@ -56,5 +56,5 @@ def run(self, para = None): first = generate(np.zeros((para['height'], para['width'])), para['size']) imgs = [first.copy() for i in range(para['slice'])] ips = Image(imgs, para['name']) - ips.tool = Painter(para['size']) + ips.tool = Painter(para['size']).start(self.app, 'local') self.app.show_img(ips) \ No newline at end of file diff --git a/imagepy/tools/Transform/rotate_tol.py b/imagepy/tools/Transform/rotate_tol.py index b771e227..6c6b3780 100644 --- a/imagepy/tools/Transform/rotate_tol.py +++ b/imagepy/tools/Transform/rotate_tol.py @@ -66,22 +66,17 @@ def load(self, ips): self.para['ox'] = int((box[0]+box[2])/2) self.make_mark() ips.update() - win = self.app.get_img_win() - win.canvas.tool = RotateTool(self) + ips.tool = RotateTool(self).start(self.app, 'local') return True def cancel(self, ips): Filter.cancel(self, ips) ips.roi = self.bufroi - win = self.app.get_img_win() - ips.mark = None - win.canvas.tool = None + ips.mark = ips.tool = None def ok(self, ips, para=None): + ips.mark = ips.tool = None Filter.ok(self, ips, para) - win = self.app.get_img_win() - ips.mark = None - win.canvas.tool = None def draw(self, dc, f, **key): dc.SetPen(wx.Pen((0,255,0), width=1, style=wx.SOLID)) diff --git a/imagepy/tools/Transform/scale_tol.py b/imagepy/tools/Transform/scale_tol.py index 67f14a1c..0b586d2a 100644 --- a/imagepy/tools/Transform/scale_tol.py +++ b/imagepy/tools/Transform/scale_tol.py @@ -102,8 +102,7 @@ def load(self, ips): self.make_mark() ips.update() - win = self.app.get_img_win() - win.canvas.tool = ScaleTool(self) + ips.tool = ScaleTool(self).start(self.app, 'local') return True def count(self, dir=True): @@ -119,17 +118,13 @@ def count(self, dir=True): self.tp = self.para['oy']+self.orih*self.para['ky']/2 def ok(self, ips, para=None): + ips.mark = ips.tool = None Filter.ok(self, ips, para) - win = self.app.get_img_win() - win.canvas.tool = None - ips.mark = None def cancel(self, ips): Filter.cancel(self, ips) ips.roi = self.bufroi - win = self.app.get_img_win() - win.canvas.tool = None - ips.mark = None + ips.mark = ips.tool = None def preview(self, ips, para): Filter.preview(self, ips, para) diff --git a/sciapp/action/tolact.py b/sciapp/action/tolact.py index ba589411..b8d970d8 100644 --- a/sciapp/action/tolact.py +++ b/sciapp/action/tolact.py @@ -14,6 +14,7 @@ def mouse_move(self, canvas, x, y, btn, **key): pass def mouse_wheel(self, canvas, x, y, d, **key): pass def start(self, app, para=None, callafter=None): self.app, self.default = app, self + if para == 'local': return self if not app is None: app.tool = self class DefaultTool(Tool): @@ -41,8 +42,9 @@ def mouse_wheel(self, obj, x, y, d, **key): def start(self, app, para=None): self.app = app + if para == 'local': return self Tool.default = self - if not app is None: app.tool = self + #if not app is None: app.tool = self class ImageTool(DefaultTool): default = None @@ -57,8 +59,9 @@ def mouse_move(self, img, x, y, btn, **key): def start(self, app, para=None, callafter=None): self.app = app + if para == 'local': return self ImageTool.default = self - if not app is None: app.tool = self + #if not app is None: app.tool = self class ShapeTool(DefaultTool): default = None @@ -69,8 +72,9 @@ def mouse_move(self, img, x, y, btn, **key): def start(self, app, para=None, callafter=None): self.app = app + if para == 'local': return self ShapeTool.default = self - if not app is None: app.tool = self + # if not app is None: app.tool = self class TableTool(DefaultTool): default = None @@ -81,8 +85,9 @@ def mouse_down(self, data, x, y, btn, **others): def start(self, app, para=None, callafter=None): self.app = app + if para == 'local': return self TableTool.default = self - if not app is None: app.tool = self + # if not app is None: app.tool = self DefaultTool().start(None) ImageTool().start(None) diff --git a/sciapp/object/image.py b/sciapp/object/image.py index b996944a..cc167d94 100644 --- a/sciapp/object/image.py +++ b/sciapp/object/image.py @@ -60,6 +60,7 @@ def __init__(self, imgs=None, name='Image'): self.dirty = False self.snap = None self.back = None + self.tool = None @property def box(self): diff --git a/sciwx/canvas/mcanvas.py b/sciwx/canvas/mcanvas.py index 2482fdca..90505f1d 100644 --- a/sciwx/canvas/mcanvas.py +++ b/sciwx/canvas/mcanvas.py @@ -84,6 +84,7 @@ def on_idle(self, event): self.marks['mark'] = self.image.mark.body[self.image.cur] elif 'mark' in self.marks: del self.marks['mark'] else: self.marks['mark'] = self.image.mark + self.tool = self.image.tool Canvas.on_idle(self, event) class VCanvas(Canvas): From 4a7bcbf5d8974f7eb16d54d56c7f76d15fa81fcc Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 8 Nov 2020 22:17:02 +0800 Subject: [PATCH 316/343] new start --- imagepy/data/config.json | 2 +- imagepy/lang/Chinese/plugins/Kit3D.dic | 143 +++++++ .../menus/Kit3D/Filters 3D/filters3d_plgs.py | 1 - .../menus/Process/Classify/classify_plgs.py | 4 +- sciapp/action/plugin/generalio.py | 2 +- sciapp/action/tolact.py | 3 +- sciapp/object/image.py | 1 + sciapp/object/shape.py | 5 +- sciapp/util/imgutil.py | 2 +- sciwx/app/__init__.py | 3 +- sciwx/app/imgapp.py | 2 +- sciwx/app/miniapp.py | 119 ++++-- sciwx/app/sciapp_.py | 379 ++++++++++++++++++ sciwx/canvas/canvas.py | 3 +- sciwx/canvas/mcanvas.py | 3 + sciwx/canvas/vwidget.py | 4 +- sciwx/demo/canvas1_demo.py | 2 +- sciwx/mesh/mcanvas.py | 4 + sciwx/mesh/scene.py | 5 + sciwx/widgets/__init__.py | 1 + sciwx/widgets/colormap.py | 2 +- sciwx/widgets/menubar.py | 24 +- sciwx/widgets/ribbonbar.py | 145 +++++++ 23 files changed, 797 insertions(+), 62 deletions(-) create mode 100644 imagepy/lang/Chinese/plugins/Kit3D.dic create mode 100644 sciwx/app/sciapp_.py create mode 100644 sciwx/widgets/ribbonbar.py diff --git a/imagepy/data/config.json b/imagepy/data/config.json index a9c8eb05..fe7b9a33 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["uistyle", "imagepy", null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["recent", ["C:/Users/54631/Desktop/\u6d77\u51b0\u62a5\u4ef7/testmacros.mc", "C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc"], null], ["roi_style", {"color": [255, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 255, 0], "size": 8}, null], ["language", "Chinese", null]] \ No newline at end of file +[["uistyle", "imagepy", null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["recent", ["C:/Users/54631/Desktop/\u6d77\u51b0\u62a5\u4ef7/testmacros.mc", "C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc"], null], ["roi_style", {"color": [255, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 255, 0], "size": 8}, null], ["language", "English", null]] \ No newline at end of file diff --git a/imagepy/lang/Chinese/plugins/Kit3D.dic b/imagepy/lang/Chinese/plugins/Kit3D.dic new file mode 100644 index 00000000..795ab5d3 --- /dev/null +++ b/imagepy/lang/Chinese/plugins/Kit3D.dic @@ -0,0 +1,143 @@ +Filters 3D::三维滤波 + +Gaussian 3D::三维高斯滤波 + sigma::方差 + +Uniform 3D::三维均值滤波 + size::核大小 + +Sobel 3D::三维Sobel滤波 + sigma::方差 + wight::权重 + +Unsharp Mask 3D::三维非锐化掩膜 + +Up Down Watershed 3D::三维上下限分水岭 + +Features 3D::三维特征增强 + +Frangi 3D::三维Frangi特征增强 + sigma start::标准差上限 + sigma end::标准差下限 + sigma step::步长 + alpha::alpha值 + beta::beta值 + gamma::gamma增益 + +Meijering 3D::三维Meijering特征增强 + sigma start::标准差上限 + sigma end::标准差下限 + sigma step::步长 + +Sato 3D::三维Sato特征增强 + sigma start::标准差上限 + sigma end::标准差下限 + sigma step::步长 + +Hessian 3D::三维Hessian特征增强 + sigma start::标准差上限 + sigma end::标准差下限 + sigma step::步长 + alpha::alpha值 + beta::beta值 + gamma::gamma增益 + +Binary 3D::三维二值化 + +Dilation 3D::三维膨胀 + r::半径 + +Erosion 3D::三维腐蚀 + r::半径 + +Opening 3D::三维开运算 + r::半径 + +Closing 3D::三维闭运算 + r::半径 + +Fill Holes 3D::三维填充孔洞 + +Skeleton 3D::三维骨架提取 + +Distance 3D::三维距离变换 + +Binary Watershed 3D::三维二值分水岭 + tolerance::容错 + +Analysis 3D::三维分析 + +Pixel Statistic 3D::三维像素统计 + +Frequence 3D::三维频数计算 + bins::直方图箱数 + +Region Label 3D::三维区域标记 + +Geometry Analysis 3D::三维几何分析 + +Geometry Filter 3D::三维几何过滤 + front color::前景色 + back color::背景色 + volume::体积 + diagonal::对角线 + +Measure Surface And Volume::表面体积测量 + +Network 3D::三维网络图 + +Skeleton 3D::三维骨架提取 + +Build Graph 3D::三维拓扑建立 + +Graph Cut Branch 3D::三维剪枝 + limit::阈值 + +Remove Isolate 3D::三维移除孤立点 + +Remove 2Path Node 3D::三维移除2路径节点 + +Graph Statistic 3D::三维拓扑关系统计 + +Graph Summarise 3D::三维拓扑关系汇总 + +Show Graph 3D::三维拓扑可视化 + radius::半径 + node::节点 + line::线 + path::路径 + +Show Graph R 3D::三维球棍模型可视化 + +Viewer 3D::三维画布 + +Show Viewer 3D::新建三维画布 + +2D Surface::2D地表重建 + Name::名字 + down scale::降采样 + sigma::平滑标准差 + scale z::z轴缩放 + +3D Surface::三维表面重建 + Name::名字 + down scale::降采样 + march step::重建步长 + color::颜色 + +3D Image Cube::三维立方体 + +RGB Points Cloud::RGB点云 + Name::名字 + number::数量 + radius::半径 + +Table Point Cloud::点云表格 + +2DSurface Demo::2D表面演示 + +Lines Demo::三维线演示 + +Random Balls Demo::随机球演示 + +Decoration Demo::小饰品演示 \ No newline at end of file diff --git a/imagepy/menus/Kit3D/Filters 3D/filters3d_plgs.py b/imagepy/menus/Kit3D/Filters 3D/filters3d_plgs.py index 9f46adb1..596be745 100644 --- a/imagepy/menus/Kit3D/Filters 3D/filters3d_plgs.py +++ b/imagepy/menus/Kit3D/Filters 3D/filters3d_plgs.py @@ -85,7 +85,6 @@ def preview(self, ips, para): ips.update() def run(self, ips, snap, img, para = None): - print('hahahaha') imgs = ips.imgs gradient = np.zeros(imgs.shape, dtype=np.float32) gradient += ndimg.sobel(imgs, axis=0, output=np.float32)**2 diff --git a/imagepy/menus/Process/Classify/classify_plgs.py b/imagepy/menus/Process/Classify/classify_plgs.py index a660c671..06fe78e8 100644 --- a/imagepy/menus/Process/Classify/classify_plgs.py +++ b/imagepy/menus/Process/Classify/classify_plgs.py @@ -34,8 +34,8 @@ def run(self, ips, imgs, para = None, preview=False): key['items'] = [i for i in ['ori', 'blr', 'sob', 'eig'] if para[i]] slir, slic = ips.rect labs = [i[slir, slic] for i in imgs] - ori = ips.back - oris = [ori.img[slir, slic]] + ori = ips.back.imgs + oris = [i[slir, slic] for i in ori] self.app.info('extract features...') feat, lab, key = feature.get_feature(oris, labs, key, callback=self.progress) diff --git a/sciapp/action/plugin/generalio.py b/sciapp/action/plugin/generalio.py index 489c0771..b298bdf4 100644 --- a/sciapp/action/plugin/generalio.py +++ b/sciapp/action/plugin/generalio.py @@ -1,7 +1,7 @@ from ..advanced import dataio from skimage.io import imread, imsave -for i in ('bmp', 'jpg', 'tif'): +for i in ('bmp', 'jpg', 'tif', 'png', 'gif'): dataio.ReaderManager.add(i, imread, 'img') dataio.WriterManager.add(i, imsave, 'img') diff --git a/sciapp/action/tolact.py b/sciapp/action/tolact.py index b8d970d8..2d3218df 100644 --- a/sciapp/action/tolact.py +++ b/sciapp/action/tolact.py @@ -30,7 +30,7 @@ def mouse_up(self, obj, x, y, btn, **key): self.oldxy = None def mouse_move(self, obj, x, y, btn, **key): - if self.oldxy is None: return + if not hasattr(self, 'oldxy') or self.oldxy is None: return ox, oy = self.oldxy up = (1,-1)[key['canvas'].up] key['canvas'].move(key['px']-ox, (key['py']-oy)*up) @@ -51,6 +51,7 @@ class ImageTool(DefaultTool): title = 'Image Tool' def mouse_move(self, img, x, y, btn, **key): + DefaultTool.mouse_move(self, img, x, y, btn, **key) if self.app is None: return r, c = int(y), int(x) if (r>0) & (c>0) & (r{'path':'%s'}"%i.replace('\\', '/') for i in path]) + stapanel.SetDropTarget(OpenDrop(self)) + self.auimgr.AddPane( stapanel, aui.AuiPaneInfo() .Bottom() .CaptionVisible( False ).PinButton( True ) + .PaneBorder( False ).Dock().Resizable().FloatingSize( wx.DefaultSize ).DockFixed( True ) + . MinSize(wx.Size(-1, 20)). MaxSize(wx.Size(-1, 20)).Layer( 10 ) ) + + def init_menu(self): + self.menubar = MenuBar(self) + + def init_menu(self): + self.menubar = RibbonBar(self) + self.auimgr.AddPane( self.menubar, aui.AuiPaneInfo() .CaptionVisible(False) .Top() .PinButton( True ).Dock().Resizable().FloatingSize( wx.DefaultSize ).Layer(5) ) + + + def load_menu(self, data): + self.menubar.load(data, {}) + + def init_canvas(self): + self.canvasnbwrap = wx.Panel(self) + sizer = wx.BoxSizer( wx.VERTICAL ) + self.canvasnb = CanvasNoteBook(self.canvasnbwrap) + + sizer.Add( self.canvasnb, 1, wx.EXPAND |wx.ALL, 0 ) + self.canvasnbwrap.SetSizer( sizer ) + self.canvasnbwrap.Layout() + self.auimgr.AddPane( self.canvasnbwrap, aui.AuiPaneInfo() .Center() .CaptionVisible( False ).PinButton( True ).Dock() + .PaneBorder( False ).Resizable().FloatingSize( wx.DefaultSize ). BottomDockable( True ).TopDockable( False ) + .LeftDockable( True ).RightDockable( True ) ) + self.canvasnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_active_img) + self.canvasnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close_img) + + def init_table(self): + self.tablenbwrap = wx.Panel(self) + sizer = wx.BoxSizer( wx.VERTICAL ) + self.tablenb = GridNoteBook( self.tablenbwrap) + sizer.Add( self.tablenb, 1, wx.EXPAND |wx.ALL, 0 ) + self.tablenbwrap.SetSizer( sizer ) + self.tablenbwrap.Layout() + + self.auimgr.AddPane( self.tablenbwrap, aui.AuiPaneInfo() .Bottom() .CaptionVisible( True ).PinButton( True ).Dock().Hide() + .MaximizeButton( True ).Resizable().FloatingSize((800, 600)).BestSize(( 120,120 )). Caption('Table') . + BottomDockable( True ).TopDockable( False ).LeftDockable( True ).RightDockable( True ) ) + self.tablenb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_active_table) + self.tablenb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close_table) + + def init_tool(self): + sizer = wx.BoxSizer(wx.VERTICAL) + self.toolbar = ToolBar(self, False) + self.toolbar.Fit() + + self.auimgr.AddPane(self.toolbar, aui.AuiPaneInfo() .Top() .PinButton( True ).PaneBorder( False ) + .CaptionVisible( False ).Dock().FloatingSize( wx.DefaultSize ).MinSize(wx.Size( -1,34 )).DockFixed( True ) + . BottomDockable( False ).TopDockable( False ).Layer( 10 ) ) + + def add_task(self, task): + self.task_manager.add(task.title, task) + tasks = self.task_manager.gets() + tasks = [(p.title, lambda t=p:p.prgs) for n,p,t in tasks] + self.pro_bar.SetValue(tasks) + + def remove_task(self, task): + self.task_manager.remove(obj=task) + tasks = self.task_manager.gets() + tasks = [(p.title, lambda t=p:p.prgs) for n,p,t in tasks] + self.pro_bar.SetValue(tasks) + + def init_text(self): return + #self.mdframe = MDNoteFrame(self, 'Sci Document') + #self.txtframe = TextNoteFrame(self, 'Sci Text') + + def on_pan_close(self, event): + if event.GetPane().window in [self.toolbar, self.widgets]: + event.Veto() + if hasattr(event.GetPane().window, 'close'): + event.GetPane().window.close() + + def on_new_img(self, event): + self.add_img(self.canvasnb.canvas().image) + self.add_img_win(self.canvasnb.canvas()) + + def on_active_img(self, event): + self.active_img(self.canvasnb.canvas().image.name) + #self.add_img_win(self.canvasnb.canvas()) + + def on_close_img(self, event): + canvas = event.GetEventObject().GetPage(event.GetSelection()) + self.remove_img_win(canvas) + self.remove_img(canvas.image) + + def on_new_tab(self, event): + self.add_tab(event.GetEventObject().grid.table) + self.add_tab_win(event.GetEventObject().grid) + + def on_active_table(self, event): + self.active_table(self.tablenb.grid().table.title) + + def on_close_table(self, event): + grid = event.GetEventObject().GetPage(event.GetSelection()) + App.close_table(self, grid.table.title) + + def on_new_mesh(self, event): + self.add_mesh(event.GetEventObject().canvas.mesh) + self.add_mesh_win(event.GetEventObject().canvas) + + def on_close_mesh(self, event): + self.remove_mesh(event.GetEventObject().canvas.mesh) + self.remove_mesh_win(event.GetEventObject().canvas) + event.Skip() + + def info(self, value): + wx.CallAfter(self.txt_info.SetLabel, value) + + def set_progress(self, value): + v = max(min(value, 100), 0) + self.pro_bar.SetValue(v) + if value==-1: + self.pro_bar.Hide() + elif not self.pro_bar.IsShown(): + self.pro_bar.Show() + self.stapanel.GetSizer().Layout() + self.pro_bar.Update() + + def on_close(self, event): + self.Destroy() + sys.exit() + + def _show_img(self, img, title=None): + print(img) + canvas = self.canvasnb.add_canvas() + if not isinstance(img, Image): + img = Image(img, title) + App.show_img(self, img, img.title) + canvas.set_img(img) + + def show_img(self, img, title=None): + wx.CallAfter(self._show_img, img, title) + + def _show_table(self, tab, title): + grid = self.tablenb.add_grid() + if not isinstance(tab, Table): + tab = Table(tab, title) + App.show_table(self, tab, tab.title) + grid.set_data(tab) + info = self.auimgr.GetPane(self.tablenbwrap) + info.Show(True) + self.auimgr.Update() + + def show_table(self, tab, title=None): + wx.CallAfter(self._show_table, tab, title) + + def show_plot(self, title): + fig = PlotFrame(self) + fig.figure.title = title + return fig + + def _show_md(self, cont, title='ImagePy'): + mdframe = MDFrame(self) + mdframe.set_cont(cont) + mdframe.mdpad.title = title + mdframe.Show(True) + + def show_md(self, cont, title='ImagePy'): + wx.CallAfter(self._show_md, cont, title) + + def _show_workflow(self, cont, title='ImagePy'): + pan = WorkFlowPanel(self) + pan.SetValue(cont) + info = aui.AuiPaneInfo(). DestroyOnClose(True). Left(). Caption(title) .PinButton( True ) \ + .Resizable().FloatingSize( wx.DefaultSize ).Dockable(False).Float().Top().Layer( 5 ) + pan.Bind(None, lambda x:self.run_macros(['%s>None'%x])) + self.auimgr.AddPane(pan, info) + self.auimgr.Update() + + def show_workflow(self, cont, title='ImagePy'): + wx.CallAfter(self._show_workflow, cont, title) + + def _show_txt(self, cont, title='ImagePy'): + TextFrame(self, title, cont).Show() + + def show_txt(self, cont, title='ImagePy'): + wx.CallAfter(self._show_txt, cont, title) + + def _show_mesh(self, mesh=None, title=None): + if mesh is None: + cframe = Canvas3DFrame(self) + canvas = cframe.canvas + canvas.mesh.name = 'Surface' + + elif hasattr(mesh, 'vts'): + canvas = self.get_mesh_win() + if canvas is None: + cframe = Canvas3DFrame(self) + canvas = cframe.canvas + canvas.mesh.name = 'Surface' + canvas.add_surf(title, mesh) + else: + cframe = Canvas3DFrame(self) + canvas = cframe.canvas + canvas.set_mesh(mesh) + canvas.GetParent().Show() + canvas.GetParent().Bind(wx.EVT_ACTIVATE, self.on_new_mesh) + canvas.GetParent().Bind(wx.EVT_CLOSE, self.on_close_mesh) + self.add_mesh(canvas.mesh) + self.add_mesh_win(canvas) + + def show_mesh(self, mesh=None, title=None): + wx.CallAfter(self._show_mesh, mesh, title) + + def show_widget(self, panel, title='Widgets'): + obj = self.manager('widget').get(panel.title) + if obj is None: + pan = panel(self) + self.manager('widget').add(obj=pan, name=panel.title) + self.auimgr.AddPane(pan, aui.AuiPaneInfo().Caption(panel.title).Left().Layer( 15 ).PinButton( True ) + .Float().Resizable().FloatingSize( wx.DefaultSize ).Dockable(True)) #.DestroyOnClose()) + else: + info = self.auimgr.GetPane(obj) + info.Show(True) + self.Layout() + self.auimgr.Update() + + def close_img(self, name=None): + names = self.img_names() if name is None else [name] + for name in names: + idx = self.canvasnb.GetPageIndex(self.get_img_win(name)) + self.remove_img(self.get_img_win(name).image) + self.remove_img_win(self.get_img_win(name)) + self.canvasnb.DeletePage(idx) + + def close_table(self, name=None): + names = self.get_tab_name() if name is None else [name] + for name in names: + idx = self.tablenb.GetPageIndex(self.get_tab_win(name)) + self.remove_tab(self.get_tab_win(name).table) + self.remove_tab_win(self.get_tab_win(name)) + self.tablenb.DeletePage(idx) + + def run_macros(self, cmd, callafter=None): + cmds = [i for i in cmd] + def one(cmds, after): + cmd = cmds.pop(0) + title, para = cmd.split('>') + plg = self.app.plugin_manager.get(name=title)() + after = lambda cmds=cmds: one(cmds, one) + if len(cmds)==0: after = callafter + wx.CallAfter(plg.start, self, eval(para), after) + one(cmds, None) + + def show(self, tag, cont, title): + tag = tag or 'img' + if tag=='img': + self.show_img([cont], title) + elif tag=='imgs': + self.show_img(cont, title) + elif tag=='tab': + self.show_table(cont, title) + elif tag=='mc': + self.run_macros(cont) + elif tag=='md': + self.show_md(cont, title) + elif tag=='wf': + self.show_workflow(cont, title) + else: self.alert('no view for %s!'%tag) + + def info(self, cont): + wx.CallAfter(self.txt_info.SetLabel, cont) + + def _alert(self, info, title='ImagePy'): + dialog=wx.MessageDialog(self, info, title, wx.OK) + dialog.ShowModal() == wx.ID_OK + dialog.Destroy() + + def alert(self, info, title='ImagePy'): + wx.CallAfter(self._alert, info, title) + + def yes_no(self, info, title='ImagePy'): + dialog = wx.MessageDialog(self, info, title, wx.YES_NO | wx.CANCEL) + rst = dialog.ShowModal() + dialog.Destroy() + dic = {wx.ID_YES:'yes', wx.ID_NO:'no', wx.ID_CANCEL:'cancel'} + return dic[rst] + + def get_path(self, title, filt, io, name=''): + filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in filt]) + dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} + dialog = wx.FileDialog(self, title, '', name, filt, dic[io]) + rst = dialog.ShowModal() + path = dialog.GetPath() if rst == wx.ID_OK else None + dialog.Destroy() + return path + + def show_para(self, title, para, view, on_handle=None, on_ok=None, + on_cancel=None, on_help=None, preview=False, modal=True): + on_help = lambda x=None:self.show_md(x or 'No Document!', title) + dialog = ParaDialog(self, title) + dialog.init_view(view, para, preview, modal=modal, app=self) + dialog.Bind('cancel', on_cancel) + dialog.Bind('parameter', on_handle) + dialog.Bind('commit', on_ok) + dialog.Bind('help', on_help) + return dialog.show() + +if __name__ == '__main__': + import numpy as np + import pandas as pd + + app = wx.App(False) + frame = SciApp(None) + frame.Show() + frame.show_img([np.zeros((512, 512), dtype=np.uint8)], 'zeros') + frame.show_table(pd.DataFrame(np.arange(100).reshape((10,10))), 'title') + + plgs = ('root', [('file', [('IO', [('Open', OpenFile), ('Save', SaveImage)]), ('Gaussian', Gaussian)])]) + + frame.load_menu(plgs) + + ''' + frame.show_md('abcdefg', 'md') + frame.show_md('ddddddd', 'md') + frame.show_txt('abcdefg', 'txt') + frame.show_txt('ddddddd', 'txt') + ''' + + app.MainLoop() \ No newline at end of file diff --git a/sciwx/canvas/canvas.py b/sciwx/canvas/canvas.py index ff8186c5..ba31e383 100644 --- a/sciwx/canvas/canvas.py +++ b/sciwx/canvas/canvas.py @@ -131,10 +131,9 @@ def draw_image(self, dc, img, back, mode): buf = memoryview(self.outrgb) self.outbmp = wx.Bitmap.FromBuffer(*shp[::-1], buf) if not back is None: - mix_img(back.img, m, o, shp, self.outbak, + mix_img(back.imgs[img.cur], m, o, shp, self.outbak, self.outrgb, self.outint, back.rg, back.lut, back.log, cns=back.cn, mode='set') - mix_img(img.img, m, o, shp, self.outimg, self.outrgb, self.outint, img.rg, img.lut, img.log, cns=img.cn, mode=img.mode) diff --git a/sciwx/canvas/mcanvas.py b/sciwx/canvas/mcanvas.py index 90505f1d..8551bdd8 100644 --- a/sciwx/canvas/mcanvas.py +++ b/sciwx/canvas/mcanvas.py @@ -52,6 +52,9 @@ def set_tool(self, tool): @property def image(self): return self.images[0] + @property + def name(self): return self.image.title + @property def back(self): return self.images[0].back diff --git a/sciwx/canvas/vwidget.py b/sciwx/canvas/vwidget.py index fb67079e..d6981cd1 100644 --- a/sciwx/canvas/vwidget.py +++ b/sciwx/canvas/vwidget.py @@ -1,9 +1,11 @@ import wx, wx.lib.agw.aui as aui from .mcanvas import SCanvas as Canvas from ..widgets import ToolBar, MenuBar +from sciapp import App -class VectorFrame(wx.Frame): +class VectorFrame(wx.Frame, App): def __init__(self, parent=None, autofit=False): + App.__init__(self) wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = 'VectorFrame', pos = wx.DefaultPosition, diff --git a/sciwx/demo/canvas1_demo.py b/sciwx/demo/canvas1_demo.py index 379a3fe8..10b4bc53 100644 --- a/sciwx/demo/canvas1_demo.py +++ b/sciwx/demo/canvas1_demo.py @@ -23,7 +23,7 @@ def rgb_gray_blend(): frame = wx.Frame(None, title='blend') canvas = Canvas(frame, autofit=True) canvas.set_img(astronaut()) - canvas.set_cn((0,1,2)) + canvas.set_cn((2,-1,-1)) canvas.set_img(camera(), True) canvas.set_cn(0, True) canvas.set_mode(0.5) diff --git a/sciwx/mesh/mcanvas.py b/sciwx/mesh/mcanvas.py index fc790d89..8230e478 100644 --- a/sciwx/mesh/mcanvas.py +++ b/sciwx/mesh/mcanvas.py @@ -176,6 +176,10 @@ def on_open(self, evt): def get_obj(self, name): return self.canvas.scene.meshset.get_obj(name) + def set_style(self, name, **key): + self.get_obj(name).set_style(**key) + self.canvas.Refresh() + def on_visible(self, evt): self.curobj.set_style(visible=evt.IsChecked()) self.canvas.Refresh(False) diff --git a/sciwx/mesh/scene.py b/sciwx/mesh/scene.py index 3c13c533..b5e6a2ed 100644 --- a/sciwx/mesh/scene.py +++ b/sciwx/mesh/scene.py @@ -110,11 +110,16 @@ def ctx_txt(self, ctx, prog, obj, name): self.vabo[name] = [buf, vao, vbo] def add_surf(self, name, obj): + if isinstance(obj, tuple): + if isinstance(obj[3], (int, float)): + obj = MarkText(*obj) + else: obj = Surface(*obj) if not self.ctx is None: if isinstance(obj, MarkText): self.ctx_txt(self.ctx, self.prog_txt, obj, name) elif isinstance(obj, Surface): self.ctx_obj(self.ctx, self.prog_suf, obj, name) + self.objs[name] = obj self.count_box() diff --git a/sciwx/widgets/__init__.py b/sciwx/widgets/__init__.py index 643ef710..3401e2d3 100644 --- a/sciwx/widgets/__init__.py +++ b/sciwx/widgets/__init__.py @@ -7,6 +7,7 @@ from .normal import * from .toolbar import ToolBar from .menubar import MenuBar +from .ribbonbar import RibbonBar from .viewport import ViewPort from .choicebook import ChoiceBook from .workflow import WorkFlowPanel diff --git a/sciwx/widgets/colormap.py b/sciwx/widgets/colormap.py index 02718bfb..b28a51cd 100644 --- a/sciwx/widgets/colormap.py +++ b/sciwx/widgets/colormap.py @@ -1,7 +1,7 @@ import numpy as np import wx.adv import sys, wx -from .. import ColorManager +from sciwx import ColorManager class CMapSelCtrl(wx.adv.OwnerDrawnComboBox): def __init__(self, parent): diff --git a/sciwx/widgets/menubar.py b/sciwx/widgets/menubar.py index 6e8f1397..fd7b29ab 100644 --- a/sciwx/widgets/menubar.py +++ b/sciwx/widgets/menubar.py @@ -54,26 +54,30 @@ class P: def __init__(self, name): self.name = name - def start(self): + def start(self, app): print(self.name) def __call__(self): return self data = ('menu', [ - ('File', [('Open', P('O')), - '-', - ('Close', P('C'))]), - ('Edit', [('Copy', P('C')), - ('A', [('B', P('B')), - ('C', P('C'))]), - ('Paste', P('P'))])]) + ('File', [ + ('Open', P('O')), + '-', + ('Close', P('C'))]), + ('Edit', [ + ('Copy', P('C')), + ('A', [ + ('B', P('B')), + ('C', P('C'))]), + ('Paste', P('P'))])]) app = wx.App() frame = wx.Frame(None) - menubar = MenuBar() - menubar.load(data) + menubar = MenuBar(frame) + acc = menubar.load(data, {'Open':'Ctrl-O'}) frame.SetMenuBar(menubar) + menubar.SetAcceleratorTable(acc) frame.Show() app.MainLoop() diff --git a/sciwx/widgets/ribbonbar.py b/sciwx/widgets/ribbonbar.py new file mode 100644 index 00000000..80638446 --- /dev/null +++ b/sciwx/widgets/ribbonbar.py @@ -0,0 +1,145 @@ +import wx.lib.agw.ribbon as rb +import wx +import numpy as np + +def make_logo(path, w=40): + if isinstance(path, str): + img = wx.Bitmap(path).ConvertToImage() + else: img = wx.Image(1, 1, np.array(path, dtype=np.uint8).tobytes()) + return img.Rescale(w, w).ConvertToBitmap() + +def make_logo(obj, w=40): + bmp = None + if isinstance(obj, str) and '.' in obj: + bmp = wx.Bitmap(obj).ConvertToImage() + if isinstance(obj, str) and not '.' in obj: + bmp = wx.Bitmap(w, w) + dc = wx.MemoryDC() + dc.SelectObject(bmp) + dc.SetBackground(wx.Brush((255,255,255))) + dc.Clear() + dc.SetTextForeground((100,0,128)) + font = dc.GetFont() + font.SetPointSize(22) + dc.SetFont(font) + ww, hh = dc.GetTextExtent(obj) + dc.DrawText(obj, (w-ww)//2, (w-hh)//2) + rgb = bytes(w * w * 3) + dc.SelectObject(wx.NullBitmap) + bmp.CopyToBuffer(rgb) + a = memoryview(rgb[::3]).tolist() + a = bytes([255-i for i in a]) + bmp = wx.Bitmap.FromBufferAndAlpha(w, w, rgb, a) + bmp = bmp.ConvertToImage() + if isinstance(obj, tuple): + bmp = wx.Image(1, 1, np.array(obj, dtype=np.uint8).tobytes()) + + return bmp.Rescale(w, w).ConvertToBitmap() + +def hot_key(txt): + sep = txt.split('-') + acc, code = wx.ACCEL_NORMAL, -1 + if 'Ctrl' in sep: acc|= wx.ACCEL_CTRL + if 'Alt' in sep: acc|= wx.ACCEL_ALT + if 'Shift' in sep: acc|= wx.ACCEL_SHIFT + fs = ['F%d'%i for i in range(1,13)] + if sep[-1] in fs: + code = 340+fs.index(sep[-1]) + elif len(sep[-1])==1: code = ord(sep[-1]) + return acc, code + +class RibbonBar(rb.RibbonBar): + def __init__(self, app): + rb.RibbonBar.__init__(self, app, wx.ID_ANY, wx.DefaultPosition, (-1, 140), rb.RIBBON_BAR_DEFAULT_STYLE|rb.RIBBON_BAR_SHOW_PANEL_EXT_BUTTONS ) + self.app = app + + def parse(self, ks, vs, pt): + if isinstance(vs, list): + menu = wx.Menu() + for kv in vs: + if kv == '-': menu.AppendSeparator() + else: self.parse(*kv, menu) + pt.Append(1, ks, menu) + else: + item = wx.MenuItem(pt, -1, ks) + f = lambda e, p=vs: p().start(self.app) + self.Bind(wx.EVT_MENU, f, item) + pt.Append(item) + + def parse(self, ks, vs, pt, short, rst): + page = rb.RibbonPage( self, wx.ID_ANY, ks , wx.NullBitmap , 0 ) + panel = toolbar = None + for kv1 in vs: + if kv1 == '-': continue + pname = kv1[0] if isinstance(kv1[2], list) else '--' + if panel is None and not isinstance(kv1[2], list): + panel = rb.RibbonPanel( page, wx.ID_ANY, pname , make_logo(kv1[1] or kv1[0][0]), wx.DefaultPosition, wx.DefaultSize, rb.RIBBON_PANEL_DEFAULT_STYLE ) + toolbar = rb.RibbonButtonBar( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0) + if not isinstance(kv1[2], list): + btn = toolbar.AddSimpleButton( wx.NewId(), kv1[0], make_logo(kv1[1] or kv1[0][0]), wx.EmptyString) + toolbar.Bind(rb.EVT_RIBBONBUTTONBAR_CLICKED, lambda e, p=kv1[2]:p().start(self.app), id=btn.id) + self.GetParent().Bind(wx.EVT_MENU, lambda e, p=kv1[2]:p().start(self.app), id=btn.id) + if kv1[0] in short: rst.append((short[kv1[0]], btn.id)) + + else: + panel = rb.RibbonPanel( page, wx.ID_ANY, pname , make_logo(kv1[1] or kv1[0][0]) , wx.DefaultPosition, wx.DefaultSize, rb.RIBBON_PANEL_DEFAULT_STYLE ) + toolbar = rb.RibbonButtonBar( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0) + for kv2 in kv1[2]: + if kv2 == '-': continue + btn = toolbar.AddSimpleButton( wx.NewId(), kv2[0], make_logo(kv2[1] or kv2[0][0]), wx.EmptyString) + toolbar.Bind(rb.EVT_RIBBONBUTTONBAR_CLICKED, lambda e, p=kv2[2]:p().start(self.app), id=btn.id) + self.GetParent().Bind(wx.EVT_MENU, lambda e, p=kv2[2]:p().start(self.app), id=btn.id) + if kv2[0] in short: rst.append((short[kv2[0]], btn.id)) + panel = toolbar = None + self.Realize() + + def Append(self, id, item, menu): + wx.MenuBar.Append(self, menu, item) + + def load(self, data, shortcut={}): + rst = [] + for k,l,v in data[1]: + self.parse(k, v, self, shortcut, rst) + rst = [(*hot_key(i[0]), i[1]) for i in rst] + acc = wx.AcceleratorTable(rst) + self.GetParent().SetAcceleratorTable(acc) + + def on_menu(self, event): + print('here') + + def clear(self): + self._pages.clear() + self._current_page = -1 + self.DestroyChildren() + self.Realize() + +if __name__ == '__main__': + class P: + def __init__(self, name): + self.name = name + + def start(self, app): + print(self.name) + + def __call__(self): + return self + + data = ('menu', [ + ('File', None, [ + ('Open CV', (255,0,0), P('O')), + '-', + ('Close', None, P('C'))]), + ('Edit', None, [('Copy', None, P('C')), + ('A', None, [('B', None, P('B')), + ('C', None, P('C'))]), + ('Paste', None, P('P'))])]) + + app = wx.App() + frame = wx.Frame(None) + menubar = RibbonBar(frame) + acc = menubar.load(data, {'Open CV':'Ctrl-O'}) + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add( menubar, 0, wx.ALL | wx.EXPAND, 0 ) + frame.SetSizer(sizer) + frame.Show() + app.MainLoop() From a033696ed94a34da48d2f7e1e022a02c1e608250 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 23 Nov 2020 11:26:07 +0800 Subject: [PATCH 317/343] tif, png read --- imagepy/menus/File/PNG/png_plgs.py | 10 +++++++++- imagepy/menus/File/TIF/tif_plgs.py | 8 ++++---- sciwx/plugins/histogram.py | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/imagepy/menus/File/PNG/png_plgs.py b/imagepy/menus/File/PNG/png_plgs.py index 8f1f1d80..117eed06 100644 --- a/imagepy/menus/File/PNG/png_plgs.py +++ b/imagepy/menus/File/PNG/png_plgs.py @@ -1,7 +1,15 @@ from sciapp.action import dataio from skimage.io import imread, imsave -dataio.ReaderManager.add('png', imread, 'img') +def read_png(path): + img = imread(path) + if img.ndim==3 and img.shape[-1]==4: + msk = img[:,:,3] + img = img[:,:,:3].copy() + img[msk==0] = 255 + return img + +dataio.ReaderManager.add('png', read_png, 'img') dataio.WriterManager.add('png', imsave, 'img') class OpenFile(dataio.Reader): diff --git a/imagepy/menus/File/TIF/tif_plgs.py b/imagepy/menus/File/TIF/tif_plgs.py index 74899dae..e9df8540 100644 --- a/imagepy/menus/File/TIF/tif_plgs.py +++ b/imagepy/menus/File/TIF/tif_plgs.py @@ -1,14 +1,14 @@ from sciapp.action import dataio from skimage.io import imread, imsave -dataio.ReaderManager.add('tif', imread, 'img') -dataio.ReaderManager.add('tiff', imread, 'img') -dataio.WriterManager.add('tif', imsave, 'img') - dataio.ReaderManager.add('tif', imread, 'imgs') dataio.ReaderManager.add('tiff', imread, 'imgs') dataio.WriterManager.add('tif', imsave, 'imgs') +dataio.ReaderManager.add('tif', imread, 'img') +dataio.ReaderManager.add('tiff', imread, 'img') +dataio.WriterManager.add('tif', imsave, 'img') + class OpenTIF(dataio.Reader): title = 'TIF Open' tag = 'img' diff --git a/sciwx/plugins/histogram.py b/sciwx/plugins/histogram.py index daee9211..f4aafd55 100644 --- a/sciwx/plugins/histogram.py +++ b/sciwx/plugins/histogram.py @@ -129,7 +129,7 @@ def on_8bit( self, event ): def on_minmax( self, event ): ips = self.app.get_img() if ips is None: return - minv, maxv = ips.get_updown() + minv, maxv = ips.get_updown()[0] self.range = ips.range = (minv, maxv) hist = ips.histogram() self.histpan.SetValue(hist) From 434f4770661430c975596cb1324fe715a86d3d96 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Thu, 10 Dec 2020 11:41:25 +0800 Subject: [PATCH 318/343] nothing --- imagepy/menus/Process/Features/ridge_plgs.py | 22 +++++++++++++++++++- sciwx/plugins/histogram.py | 6 +++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/imagepy/menus/Process/Features/ridge_plgs.py b/imagepy/menus/Process/Features/ridge_plgs.py index cb1b2b00..23db212c 100644 --- a/imagepy/menus/Process/Features/ridge_plgs.py +++ b/imagepy/menus/Process/Features/ridge_plgs.py @@ -1,5 +1,7 @@ from skimage.filters import frangi, sato, hessian ,meijering +from skimage.feature import structure_tensor, structure_tensor_eigvals from sciapp.action import Filter, Simple +import numpy as np def scale(img, low, high): img *= (high-low)/(max(img.ptp(), 1e-5)) @@ -70,4 +72,22 @@ def run(self, ips, snap, img, para = None): beta=para['beta'], gamma=para['gamma'], black_ridges=para['bridges']) img[:] = scale(rst, ips.range[0], ips.range[1]) -plgs = [Frangi, Meijering, Sato, Hessian] +class StructureTensor(Filter): + title = 'Structure Tensor' + note = ['all', 'auto_msk', 'auto_snap', 'preview', '2float'] + para = {'sigma':2, 'axis':'major', 'log':False} + + view = [(float, 'sigma', (0, 20), 1, 'sigma','pix'), + (list, 'axis', ['major', 'minor', 'both'], str, 'axis', ''), + (bool, 'log', 'log')] + + def run(self, ips, snap, img, para = None): + axx, axy, ayy = structure_tensor(snap, sigma=para['sigma']) + l1, l2 = structure_tensor_eigvals(axx, axy, ayy) + if para['axis']=='major': rst = l1 + elif para['axis']=='minor': rst = l2 + else: rst = (l1**2 + l2**2)**0.5 + if para['log']: rst += 1; np.log(rst, out=rst) + img[:] = scale(rst, ips.range[0], ips.range[1]) + +plgs = [Frangi, Meijering, Sato, Hessian, StructureTensor] diff --git a/sciwx/plugins/histogram.py b/sciwx/plugins/histogram.py index f4aafd55..4e9529c1 100644 --- a/sciwx/plugins/histogram.py +++ b/sciwx/plugins/histogram.py @@ -117,7 +117,7 @@ def on_8bit( self, event ): ips = self.app.get_img() if ips is None: return self.range = ips.range = (0,255) - hist = ips.histogram() + hist = ips.histogram(step=1024) self.histpan.SetValue(hist) self.sli_low.set_para((0,255), 0) self.sli_high.set_para((0,255), 0) @@ -131,7 +131,7 @@ def on_minmax( self, event ): if ips is None: return minv, maxv = ips.get_updown()[0] self.range = ips.range = (minv, maxv) - hist = ips.histogram() + hist = ips.histogram(step=1024) self.histpan.SetValue(hist) self.sli_low.set_para(self.range, 10) self.sli_high.set_para(self.range, 10) @@ -143,7 +143,7 @@ def on_minmax( self, event ): def on_slice( self, event ): ips = self.app.get_img() if ips is None: return - hist = ips.histogram() + hist = ips.histogram(step=1024) self.histpan.SetValue(hist) def on_stack( self, event ): From 7fee76be33ffab2cc9f4dc487283e5a12b931208 Mon Sep 17 00:00:00 2001 From: Qi Date: Wed, 23 Dec 2020 15:19:26 +0800 Subject: [PATCH 319/343] Parameter error fixed in threshold plgs --- imagepy/menus/Process/Threshold/threshold_plgs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imagepy/menus/Process/Threshold/threshold_plgs.py b/imagepy/menus/Process/Threshold/threshold_plgs.py index 44247c6f..2d46189a 100644 --- a/imagepy/menus/Process/Threshold/threshold_plgs.py +++ b/imagepy/menus/Process/Threshold/threshold_plgs.py @@ -51,7 +51,7 @@ def run(self, ips, snap, img, para = None): class Auto(Filter): title = 'Auto Threshold' note = ['all', 'auto_msk', 'auto_snap', 'preview'] - para = {'method':'otsu'} + para = {'method':'Otsu'} view = [(list, 'method', ['Otsu', 'Yen', 'Isodata', 'Li', 'Mini', 'Mean', 'Triangle'], str, 'Method', '')] @@ -65,7 +65,7 @@ def run(self, ips, snap, img, para = None): class Local(Filter): title = 'Adaptive Threshold' note = ['all', 'auto_msk', 'auto_snap', 'preview'] - para = {'method':'mean', 'size':9, 'offset':2} + para = {'method':'Mean', 'size':9, 'offset':2} view = [(list, 'method', ['Gaussian', 'Mean', 'Median'], str, 'method', ''), (int, 'size', (3, 31), 0, 'blocksize', 'pix'), (int, 'offset', (0, 50), 0, 'offset', '')] From 1386b9de8fbd58043393525153acc8f7f8c409ca Mon Sep 17 00:00:00 2001 From: Qi Date: Mon, 1 Feb 2021 11:03:40 +0800 Subject: [PATCH 320/343] make canvas independent --- sciwx/canvas/canvas.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sciwx/canvas/canvas.py b/sciwx/canvas/canvas.py index ba31e383..2235f269 100644 --- a/sciwx/canvas/canvas.py +++ b/sciwx/canvas/canvas.py @@ -190,7 +190,7 @@ def move(self, dx, dy, coord='win'): self.update() def on_size(self, event): - if max(self.GetClientSize())>20: + if max(self.GetClientSize())>20 and self.images[0].img is not None: self.initBuffer() if len(self.images)+len(self.marks)==0: return if self.conbox[2] - self.conbox[0] > 1: self.update() From 8bc6f45fd90a6b8e0b2a3a660119b8281e964b0f Mon Sep 17 00:00:00 2001 From: Qi Date: Sun, 7 Feb 2021 14:47:25 +0800 Subject: [PATCH 321/343] fix the error in frequency analysis --- imagepy/menus/Analysis/statistic_plg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/menus/Analysis/statistic_plg.py b/imagepy/menus/Analysis/statistic_plg.py index ee3d041a..0cc1d79c 100644 --- a/imagepy/menus/Analysis/statistic_plg.py +++ b/imagepy/menus/Analysis/statistic_plg.py @@ -96,7 +96,7 @@ def run(self, ips, imgs, para = None): img = imgs[i] if msk is None else imgs[i][msk] maxv = img.max() if maxv==0:continue - ct = np.histogram(img, maxv, [1,maxv+1])[0] + ct = np.histogram(img, maxv+1, [0,maxv])[0] titles = ['slice','value','count'] dt = [[i]*len(ct), list(range(maxv+1)), ct] if not para['slice']: From c941603a2ed9bee87b377af8cd366142ad273808 Mon Sep 17 00:00:00 2001 From: Qi Date: Fri, 19 Feb 2021 13:38:01 +0800 Subject: [PATCH 322/343] add img win to app --- sciapp/app.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sciapp/app.py b/sciapp/app.py index ad974f2e..afb3627b 100644 --- a/sciapp/app.py +++ b/sciapp/app.py @@ -6,7 +6,7 @@ def __init__(self, asyn=True): self.asyn = asyn self.managers = {} self.img_manager = self.manager('img') - #self.wimg_manager = self.manager('wimg') + self.wimg_manager = self.manager('wimg') self.tab_manager = self.manager('tab') #self.wtab_manager = self.manager('wtab') self.mesh_manager = self.manager('mesh') @@ -40,6 +40,9 @@ def show_img(self, img, name): self.img_manager.add(img.name, img) print(img.info) + def add_img_win(self, win, name): + self.wimg_manager.add(name, win) + def close_img(self, name): self.img_manager.remove(name) print('close image:', name) From a128ed72ff907d4ea9e0b05f6792742ece736076 Mon Sep 17 00:00:00 2001 From: Qi Date: Fri, 19 Feb 2021 13:40:38 +0800 Subject: [PATCH 323/343] detect null image when initializing --- sciwx/canvas/mcanvas.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sciwx/canvas/mcanvas.py b/sciwx/canvas/mcanvas.py index 8551bdd8..bafc9be8 100644 --- a/sciwx/canvas/mcanvas.py +++ b/sciwx/canvas/mcanvas.py @@ -258,6 +258,8 @@ def on_scroll(self, event): self.canvas.on_idle(event) def on_idle(self, event): + if self.image.img is None: + return image, info = self.image, self.lab_info.GetLabel() imgs = image.slices, image.channels, image.cn, image.cur selfs = self.pages ,self.chans, self.cn, self.cur From faedbc9446c3cbc691b498bbf5e1affac4cc7006 Mon Sep 17 00:00:00 2001 From: Xin Bo Qi Date: Tue, 23 Feb 2021 16:54:48 +0800 Subject: [PATCH 324/343] fix ribbon bar, but the default is still the original bar --- imagepy/app/imagepy.py | 4 +++- imagepy/menus/File/new_plg.py | 2 +- sciwx/widgets/ribbonbar.py | 42 ++++++++++++++++++++++++++--------- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/imagepy/app/imagepy.py b/imagepy/app/imagepy.py index d5662a0a..042b77f3 100644 --- a/imagepy/app/imagepy.py +++ b/imagepy/app/imagepy.py @@ -2,7 +2,7 @@ import time, threading sys.path.append('../../../') import wx.lib.agw.aui as aui -from sciwx.widgets import MenuBar, ToolBar, ChoiceBook, ParaDialog, WorkFlowPanel, ProgressBar +from sciwx.widgets import MenuBar, RibbonBar, ToolBar, ChoiceBook, ParaDialog, WorkFlowPanel, ProgressBar from sciwx.canvas import CanvasNoteBook from sciwx.grid import GridNoteBook from sciwx.mesh import Canvas3DNoteBook @@ -147,6 +147,8 @@ def load_widget(self, data): def init_menu(self): self.menubar = MenuBar(self) + # self.menubar = RibbonBar(self) + # self.auimgr.AddPane( self.menubar, aui.AuiPaneInfo() .CaptionVisible(False) .Top() .PinButton( True ).Dock().Resizable().MinSize(wx.Size(1000, 130)).FloatingSize( wx.DefaultSize ).Layer(5) ) def init_tool(self): sizer = wx.BoxSizer(wx.VERTICAL) diff --git a/imagepy/menus/File/new_plg.py b/imagepy/menus/File/new_plg.py index 50c8b99d..bd543927 100644 --- a/imagepy/menus/File/new_plg.py +++ b/imagepy/menus/File/new_plg.py @@ -8,7 +8,7 @@ import numpy as np class Plugin(Free): - title = 'New Image' + title = 'New' para = {'name':'Undefined','width':300, 'height':300, 'type':'8-bit','slice':1} view = [(str, 'name', 'name', ''), (int, 'width', (1,10240), 0, 'width', 'pix'), diff --git a/sciwx/widgets/ribbonbar.py b/sciwx/widgets/ribbonbar.py index 80638446..a8e61471 100644 --- a/sciwx/widgets/ribbonbar.py +++ b/sciwx/widgets/ribbonbar.py @@ -69,7 +69,10 @@ def parse(self, ks, vs, pt): def parse(self, ks, vs, pt, short, rst): page = rb.RibbonPage( self, wx.ID_ANY, ks , wx.NullBitmap , 0 ) panel = toolbar = None + for kv1 in vs: + if len(kv1) == 2: + kv1 = (kv1[0], None, kv1[1]) if kv1 == '-': continue pname = kv1[0] if isinstance(kv1[2], list) else '--' if panel is None and not isinstance(kv1[2], list): @@ -85,6 +88,8 @@ def parse(self, ks, vs, pt, short, rst): panel = rb.RibbonPanel( page, wx.ID_ANY, pname , make_logo(kv1[1] or kv1[0][0]) , wx.DefaultPosition, wx.DefaultSize, rb.RIBBON_PANEL_DEFAULT_STYLE ) toolbar = rb.RibbonButtonBar( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0) for kv2 in kv1[2]: + if len(kv2) == 2: + kv2 = (kv2[0], None, kv2[1]) if kv2 == '-': continue btn = toolbar.AddSimpleButton( wx.NewId(), kv2[0], make_logo(kv2[1] or kv2[0][0]), wx.EmptyString) toolbar.Bind(rb.EVT_RIBBONBUTTONBAR_CLICKED, lambda e, p=kv2[2]:p().start(self.app), id=btn.id) @@ -98,11 +103,14 @@ def Append(self, id, item, menu): def load(self, data, shortcut={}): rst = [] - for k,l,v in data[1]: - self.parse(k, v, self, shortcut, rst) + for klv in data[1]: + if len(klv) == 2: + self.parse(klv[0], klv[1], self, shortcut, rst) + else: + self.parse(klv[0], klv[2], self, shortcut, rst) + rst = [(*hot_key(i[0]), i[1]) for i in rst] - acc = wx.AcceleratorTable(rst) - self.GetParent().SetAcceleratorTable(acc) + return wx.AcceleratorTable(rst) def on_menu(self, event): print('here') @@ -124,15 +132,27 @@ def start(self, app): def __call__(self): return self + # data = ('menu', [ + # ('File', None, [ + # ('Open CV', (255,0,0), P('O')), + # '-', + # ('Close', None, P('C'))]), + # ('Edit', None, [('Copy', None, P('C')), + # ('A', None, [('B', None, P('B')), + # ('C', None, P('C'))]), + # ('Paste', None, P('P'))])]) + data = ('menu', [ - ('File', None, [ - ('Open CV', (255,0,0), P('O')), + ('File', [ + ('Open', P('O')), '-', - ('Close', None, P('C'))]), - ('Edit', None, [('Copy', None, P('C')), - ('A', None, [('B', None, P('B')), - ('C', None, P('C'))]), - ('Paste', None, P('P'))])]) + ('Close', P('C'))]), + ('Edit', [ + ('Copy', P('C')), + ('A', [ + ('B', P('B')), + ('C', P('C'))]), + ('Paste', P('P'))])]) app = wx.App() frame = wx.Frame(None) From 3b74dfa0f771f65ad5456c956190ff9e771da37f Mon Sep 17 00:00:00 2001 From: Xin Bo Qi Date: Thu, 25 Feb 2021 15:29:34 +0800 Subject: [PATCH 325/343] report plugin fixed --- imagepy/app/loader.py | 8 +- imagepy/data/config.json | 2 +- imagepy/menus/Plugins/Coins Report.rpt | Bin 0 -> 26804 bytes sciapp/action/__init__.py | 2 +- sciapp/action/advanced/__init__.py | 3 +- sciapp/action/advanced/report.py | 66 ++++++++++++++ sciapp/util/xlreport.py | 116 +++++++++++++++++++++++++ sciwx/widgets/propertygrid.py | 88 +++++++++++++++++++ 8 files changed, 278 insertions(+), 7 deletions(-) create mode 100644 imagepy/menus/Plugins/Coins Report.rpt create mode 100644 sciapp/action/advanced/report.py create mode 100644 sciapp/util/xlreport.py create mode 100644 sciwx/widgets/propertygrid.py diff --git a/imagepy/app/loader.py b/imagepy/app/loader.py index 99a34a2e..1a411e18 100644 --- a/imagepy/app/loader.py +++ b/imagepy/app/loader.py @@ -6,7 +6,7 @@ """ import os, sys, os.path as osp from glob import glob -from sciapp.action import Macros, Widget#, Report +from sciapp.action import Macros, Widget, Report from .. import root_dir from .manager import DocumentManager, DictManager from codecs import open @@ -23,9 +23,9 @@ def extend_plugins(path, lst, err): rst = [] for i in lst: if isinstance(i, tuple) or i=='-': rst.append(i) - elif i[-3:] == 'rpt': pass - #pt = os.path.join(root_dir,path) - #rst.append(Report(i[:-4], pt+'/'+i)) + elif i[-3:] == 'rpt': + pt = os.path.join(root_dir,path) + rst.append(Report(i[:-4], pt+'/'+i)) elif i[-3:] in {'.md', '.mc', '.wf'}: p = os.path.join(os.path.join(root_dir, path), i).replace('\\','/') rst.append(Macros(i[:-3], ['Open>{"path":"%s"}'%p])) diff --git a/imagepy/data/config.json b/imagepy/data/config.json index fe7b9a33..b7722ba7 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["uistyle", "imagepy", null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["recent", ["C:/Users/54631/Desktop/\u6d77\u51b0\u62a5\u4ef7/testmacros.mc", "C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc"], null], ["roi_style", {"color": [255, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 255, 0], "size": 8}, null], ["language", "English", null]] \ No newline at end of file +[["language", "English", null], ["roi_style", {"color": [255, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 255, 0], "size": 8}, null], ["recent", ["C:/Users/54631/Desktop/\u6d77\u51b0\u62a5\u4ef7/testmacros.mc", "C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc"], null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["uistyle", "imagepy", null]] \ No newline at end of file diff --git a/imagepy/menus/Plugins/Coins Report.rpt b/imagepy/menus/Plugins/Coins Report.rpt new file mode 100644 index 0000000000000000000000000000000000000000..3dc3d615645aa5d66c6f6a268a27b41f94f53417 GIT binary patch literal 26804 zcmeFYWmKF?w=ImjyF0<%T>>2l65L&ay9alNKyY_=cXzj-!7aEugxh5A*Ul^7IX~`? zZ=A~*)YFgksx@n_xmNY4l9K|5zyN^)fdK&lAq8pVN-^sO1p!e4o}z=mfN8$Bv34}J zcGOjJvo&_mW^}c(B*}pQqs{^W13v%%{{9yuFqSYWyUmO;a3=AD6jNC`07+3ND#HWf zuJu)qxNC8Cpu|u!19Z93WUBxsd!~+k=9Kbv|H*T^S|C}A0bjLESQOubQg*bOy6Z@< zEK_ysZ2N4QNuGqpCJvs6wi*mWb(Waw+n_Q8J@>L9rFo7k4do#oUZ8Qv#=|&$9sqT{ zpgH0r(;UIhlB)-)(>X*2V9q?`U&~DB)u;LJtuHUy;UxiK)9U3Rkt5hP zf+7HY&RRU_sYyhsL>zQ#e-03iiilxW76A}csvuEo<9D{MICY~XJ42QT7Wfxbg})%P zJ+GWpw0{sLlB(If#ruFavqcji-mJMZl|B4kiF4#J@{I8LnPtR-mvB)c zRToQeYH-PWYx|k_fkelF{WJ z0n5n6?zzcDR6uK-2-a8-abV|!1^#q!qJKSjRj-k<^1H*2i~~q)G%x>b2R*)+N8yo! zqm;%rU#jZ0pQl?6;Vqv??Q!4x^&(no$l?ox(L;pNw{fLgrQY$yX#SkWeZP{6n8aY2 z{0duO10?P1D>#VUKSX}LGBf!#@QySvu;GD`uWN5?>A=MJ*8abU|6d&Df9HBxtjwog zW`v+KiRa*<&QiZ zBE8*WFAGP*;3IEvDGy3~v~z-{eP^Eluq)f_Lv{Xs^ZhpY!$&u2=k{pY(x#$pso`~U z!0d%sHOeHTDjqC)K0y$cK(epKfQhjcTvTY@}R0luB^jvld0Yd2?Ym;LLofT z$1};8Lk{yfy0BFGkbd`nX_wONY*f{lbZh)__UypM&K32AU5Yct;%q1OyKR2GrG(>5n*Z zv9Y%>u(7dtOOStw8Bk!B1U~z}`>!o-(j3T{fiwSh|7lOVSZi%ur!R{**DX%SdX|(( zGRJyu-j}y*JQVP8$7o+zBzo5G-q*AVJ=Y;ViGHfD1*@Te3{v6IUisKHcyMD49pP7} z!8wuRLj;S|e=`0H5g&JplGW)0eTf>Hp6JgEZYtxd__!t{tFpL>M%KP5)wXYk^mIFJ zDb~%erJ()N-w5kLyyx)s3Om9lUM*T{$}l^)ZZ33hgdoSu zpoRMC2DxdW1BL+t`06XLpQRZ@c2rL#Iv~Dj z!klfhwHH~L5ac@i%S+}7Ph_A%8a2U$h>ltg^BN1e<3#uhEBSK@fg49GY2-gIK=BTmI)#sw?6L$2R%j9D6 zyM5QRHS>dDX3c(CJY<|;FYWNFWhHnzm33PoHK>e~-ABmpE4ew{@c8QPZwwCi9clQ=nr_vG&1bnF-$-j{{vqm*8(+XdR-1>J|@ zD%yWpsJMME-j9~q=QhFZOz~du%Yu_RYW)`Fa*BDUF!8d|bZ`yz2j146{eHvLXG%uo zXA=(uQ`QCNZE5Ze4MP-X4q{j?bG!V3`y8xS<#pX+mX4yc+SaAmSit6PWTs^f2Ib`H z((_2;D{##IStzu$#I@p~KtOiz-|Fb!qv~jCY-P;!yJdNkiDUI~YkV%04(y+NDDI!= zmtqZZW5A^nw&v{&7C(j&6l2zphO}yZjU!wQ+fmCBo~|3p7oL_v7GD8@odT1|954%G zhrEO%jHNT9k*`k2`KfE!mC!_N5_ENCH`MO2Yuz(NiW+-aM-<1D046r`_0AOUyMJo> zZD2i!WZ*2(XMV~XJuxF&`F*l( zxQ;N>Vybi0A(_P)`{KHmIK31o>VDXe_UW_PuURHSua{e?!QBIDU&4LElq3aESnaZm z(M`g-Z=sAMzmhK*fe{+0APdV!6FUuQW0C~r`$6Ai8F~5TM~+Il3S{=p`85Z+SzUdb z9HqL38U+-Fw4mqjI_Eo^S&*%RB&7`2Ym|R3<&QA|;YQHUpHIjc`{-W&180=p=XlU< zX%6-!UM3-*&oCdB&KUP~qJhRYTzJl@|3EK+9 zl%zMTKq8WLW{@<`4xLI{#LkB`%^ns!e^;Tk4$S+;rr)E)^u{%PqlcdlW2BkX3N|)7 zE4c)k*l#Wi#O>z#N%a$PF7Tgl-QC{ftZ@73F8b`n)R+go(GCxjs^j^#R;cq4=jG!5 zC=t2ieA^iHbp<(-|LMj-2<6bC<=4Kh;N#11?mn`R=fx^GvWMb^HR@V6Y|Kwy9?>U$ z-+J7^ri5=5+$#~X5yfoagCr~DE%^q}uATwO$O|MDSJtpuSj6Xt=TU}7BJ@FFU)az3 zLcxFRrg1c#^Xh##qIGRzlP2%9XQAJ^Wf8nSu-=-3wX#S8EfgJ@c!Cf898@PaIAhS}=Kmd0%`7i@ z%V#!lvqL--{q$St#=A&@Zw0V9nFxVY397IzM|ORWNjM3hJua^N*#}1w>GFCvxC)gW zx~!R(Jn6*R?q- zxaxQ^+*8p0371cf98e)S+bFe|@x=;<9U9J|B#mIJ21ju30|YcStmPg__xBG<{m?8E zS^Z3@^&zmur3DGd8yPF51{EMTb9&K*KAQN{?&6-N}xV=8FDE1hIV|oWPIoMSxZQsP&%0@CUhD#C6NQp%SME-u?)8BtOf{{sg*#(v-@q!Mf;*nV*u9 zs(O|+L3?B9%zc$WWNgJsvb~X6HMe?L)S*auix#3>NbQ%YU_(w1Y1WXIx$<|PQMFV8 zKQ7h@RVT@`$mXhF6&mAO%a*P0V;!FhzspGC z#M`%2;Kb@`h;qN}XBf6Ql*u5=s6pCnIpee)rDjv{b|~UrYW;i?w8ho3HI~b5ep0MZ zVLry}y>|mfduA78|Hn@sNCHZYePb#XzH|kK@l?rS%~mmNXpGxJ8^LQK_ZmvjHf?ae zLh{VLT8*pY7L;wK72lc#Km@)>^!t4k$fEq_Nd-f<@1BS`Y%lUI7jAE4EM=84IKYV= zy5g{1K$s=3MgLnG6oNJ9>yI-xNGI~uoo7WpV$CrVD`ZC(JxzMP*SL0xeBsBy@~pkp zC+SZd+5QWb$0k3_84SnEU7NogmUH@Bylc`{`HD`8NlN}M@+N3`^Qo&GX3cZ)7EVM( zw<;2P2Ey#18b0zHVcl?^_)-w%ne}9Qq#yoe+id#JKw8glvUd>^8SEw%yoFf0>z*0o zOU=QAy4ZZ$(WTj@CQ?d`AGZA?U?bedRXk7!T3tw{G|xx$hvMp(&`*2v$eTXb~i=)|I zWL{3fd+b4=@p81^J5E*3y0W+gOJ4T~p}t!Ijx0uBT5#kYqBAw9hP2@R(tzlc) z4Jq~XzQqnFmc-fX(jz5X19HJKIK;^1+x1JGtIcYl0JiG=2BH2y23>b@?etMb;%Bpf zIvpn+Ug~N(zmD&j*vnfoKc?OT+Fn*^l1!2835Q+QB)X#Q@djD0a2KMLl zE9OPMiiD2kWUaN6s6kDJc;&@bY{M!+w%gL!jnKawQy!N3m=)liDd4!0|I?WMF`M~4 zvcHRz-*cI=nDMt7-sjA}!+*Nnd8QX9SC+|e>S$TO$Am6aoeW<}bD=TQg_=IUYSj8s zBvUZ^O`$ znm#N*%(E%N6ROD|uW{b;fnsv{TphwYzSmPI_SAxAu3awh7D&Yt|G*k#e7Qk~iqzN7 zg&omEnX#`)F*^fr=wW={M`>r`LHqsY6Y+HYR8)xL;~^?0%*;}~^h2A4wu%R+b zjUUlVG61V$XsUHw>Mk@qQDckev&wf-OMR?in*rM-yPGmaK#7sG@r{CEvh?ld$|qB2 znOd&hO7I`)iiA+`yUcZ2vI|;?nXR+j13qUo&t(#qlL5co&yf%qQa%|CFc3NVaGT`v zNB|pgG`8yC0-SLCGX(6}u(AIw2-yBQ)%`03zP}-$3RLjh)e!@&P_bIywEgico0JajNg zT^YVQP@aM~QeSB6SgkNZY542Naozx$wDYf1u$1?+{Hd2KpsPK_743ocCwE!a(OQYG zE`|CbM3ckb6B3t$zaemYAM@pF>2kae5CRU(eX18!Sm}k7{;N~ye+2=O*!6lFkOh=~ zV}X&qzKfZ)iNl|>{Xb9t79j!A<8VF9C;?}FXDGMUGha|*3fe=aO6Q=}W=@bBB!>Xi zHa1zME^nG&iD>8QZ zu!g)_`q%Vm{WU?P0B?n`abdO^#|%q*UhK~iW(e(x;c zpbjzBs7D&*>9zp7I{ZU4TkorLrbL3A7{^B}c&YMTb&Cu5cT|L!V<)VrRH`dOwTu(Gs}YY=chM={ zY7({P)a--qqR#oIc3}p&d&tAMoLtmrAP&7c*N3z@)MIgEYD{b9%*U7cm|jrU z+ywADBdL0#H(xPWefjSlT6l7o$Lc!9_^~N|fn%U?dprf}o}NW{t?>)2TNAIv%j&c< z5?T>eG;%c}6R*eb)ef9>G7$=#%_DOiwVw3uX&)TzHD3qcdB3{YbXsP5efd?lbR0F+ z(|grAQ@DCs?cwq4@nYuC>E%`L`GddfQvb=Zb8R#86LEAVaVFczp)f=$%f zLL+j1UEt-c)bgU&y{&tDu~ntex($)D8TqHw)*Ga4?_Z8j_hsd)x}AB=PdBrlGKsyt z+uOax(1q64R!=)l@fiqLbrV;Ji5os05uY)i>B>$lEG*ytBA$DGIk}m=b$imkgBvF- z1mER(wSDeo(fIOuyNAUF6a4bi>}pe+hKGjgTlm$?#Z%)Gd0o@0ha2asXPxFN_v#O; z3YS!TiW0|bHiETyq6K2E_M3&1eeHvv(JLn}*IzsvaRdClpeq+7OaO(Xn#DGzT4SWq zLe`B!kaK|1N|w5PPa%ENqodW6$<)I=q;kp^boRSvn*-ZkA?EpQ%c|Q>r;SL@+Yg^U zaHS$2O}exm-del4*FIfWI6fXek3Zb)o2+Zc&2cYznORv2c`fT3nN+UDMO6q1oi*Iy z_D&;9Qm=cITV+(ncp}_>SI6nQxkVD#@{T^W=~a)|DfjMv?c{m-)#i8|=9=o3~w!!kL?MBA2=vJbQ1>6h8* zFEo{dIih(V^8k6Wc_Mj|dE$98dGGV2@;>B!G7>hDFcLG8HWD@ZXaq2lH4-tBG!oy5 z9m0&E(C<^1;Vy^W=h*Cfm$nq3m}+Z{E^Ccu%*ES1iR9=P?7S8~6aGo9f=*nH!A}Eq zE<8&fVw}trQ=}D!SA2@WPY3lnTu%<7mkbB9y9wrtXGTQ&dHBcqM6>uV?e4AOA}F{9)0qt>Q$*c(AgR&rLV8j>0c z`}o*dVoNCRkil;h$%iITrW|R znj-hMk_|`25ZceeN7qH8Gx1~2ZjDo z-@pMUeS#Yl*`7Dp`Mh+w0;aGqkVD{Vc;1nWgUpExkxYh+f$StMZ8K5v7u!8Okv`Sc zG^%sxb;xxnLI^@=L`X!abcl55Sjbo?eF%MMS%?-|Gg&M}1Z_k~1jX#ohmL~U#GU0Y zk~IvmmYQ*ed=`Qu))ancNaTKBOo4GnmMWhMlOjGv>*owNro=1OBKAe>Q4C5fNDLr0 zAofnIK#&$ z*tgv|CA(?;blq0PY9hEaBK<#RpOU_kai5zEcQvdD=jPnIw3UEwsbWi!vTi>F+OFf) zJzLs;71tn69=_~fV!KYI3%(+ipF(~WK%h{dR3KNNnjx8?m?4{aH$yr@IYT}}#Y)0T z!Ai#Zj+K=4uY{-}vzOcnlZa(5Vcx2Hm$ndqo@)CUUDm3nS%$k=GH&&!<9cE(lENme z6AS;@z>DH9i<+i^Gk5=Q4+6tQ14R=>-9^Ji{YB$Ny+xx%gVg0v0Cc>ng|E{g(`wUq zOYJLSQ)U1la(GyT_u=!e1`vk(` zAunmQARMH(qZ9s~a0clC<_jW@WX83`HEAlc3XU;C8YQeHEG2B_%;v1-Eaq&7%!jOp zEQf4(%y_JLEO>My-nlXm>dJW^n5oz9CIl~ zykvXiF^q+%cB#W_GQT-u0Q#9cS-MmJy|b6SrotnpD&RC>?7~G2#GqcQdmoGct-%^R z3FN5n!ZBkM^fMrYvygX(zbcKywv9j z5d&tHL@L0|5GN=K?PGH!zyr*Wl}+aK=8WbH=04Bq&l%4d&Y7_5u^X`)u&Wqxd?g|% z^(9M|RLcY9Er36@88+?z89R+hZtBw#0F|UnxVM9kx1mP0Jq7Z_!xId($TZrL>nq2^ z7XmfYqnQUvbG_tFDq4>&>I6Up$N(4sRscePHb4O22>=JY2ap2{#Z|x+CfGYs!i?VO z$3XoAVQlK8%*D)=0;oo0fgYma>8Us+eX}w3F-(tm4}QGALx88(Nq-atC2Dso*f-K5{1ka_RE^^55jc~F(4%;ZHq4nn= z4~@id_OHJOgeu@L0xu#ZLL*{1f;XZi;!DJ11XM&&1R!D{tPE;~T%SXZf(E05Xu{T`}%F2qwC`KGz|EvQ=lcrs!L#BPEWkaUIU#CH0l2J-Qtv$L; z|H4`ih4^`Wna|(CdVhzZR_C%kNJT_|sc^cdG>i(hujGQ1dJUj32kn)u?Bw(jKXp_}MXcQ}+y&BhpHn(~YJitZhP zaU0q96R>~}uYZ%WHtT25hT|Rmlt2ggYWPJKmun~{Im2}R(e}q+?!oA&=cIY6Ta|BTJ{@Z;7pL>v=UdQlSKimSzx!b z0fV%9LNab;z>j&qKf0HJ9buPl$n~Eo7S9?83=!L4axNAZeS7ecm1m4k$x53G+*vk* z=~k6nA`-)x&#B@JcA_qm(icEFQe5=u2_oRksbUF~;kNv!&jy$3u;|y5fYGp3B_$Gr zHgi4jQLg~E!Asd1snLX>R6Q!b%S(nzMUhY!nOmjS>gC#%Z_*1{8523RC7@mVQnNCQ zj1KamMnPg)-D_`w@|<7j3P0>pv#x*yeQ~Rc6WSSL_s&^c{K2WDkh)X$hR3$c_dODU z-9{2P6#@4y&xC7~L&ml!_?06)=I((S&B&}>@z5HzWRQITKIP|>Gc}xJRR;r(-D+lT zPN^2$@XrERP=n@c>G=6o)r%sBTTej-nAEJaaz;udLnXprm3_%_!H_KN^isk3!Xn@> zaNlljbb&%6@!0nH8X~3HZp7_#Z}UvJMZqJozrB_y3t$Lt^FIg$rMGdWle4ZbMI4z2ivam11 z!8Akz=B_WHxC-P}MHpNXm#p-&o6=$rX&0Npw^oX~L9%ISSIR z434^rQUE}k)fM$c9Wx~H50fg87~FGv<#5!tWjb!r=tyaBVM@Z#CKbL9-Rh=U%Dx0l zz<#N`y$vG*IYtXqr?S1OmR>w03ts73qEes1Lbp35J&V=#OBcwA8HJn|?JOX!ZDnu@ z7{w&wF>ny-m~whjUm}!*eF<-BXi@(9b=0;+Uq8*1%ka7BZ~uHv*={%i{kWpFzi zYM3yMGl+D(N*xD&;HNtSabbKdOdv^+<8~1lh3sB105O%p}bRY^@f1xo7YYB2vVpthmp4)Gj1G2zAV+Jb|syL{v z>LUho5EiHh)3~F{&swWfg+(gsS*)z*fjt~A{9^nA9$~ncJxMd14~w&t{4~nHX@z9z zp!Xq0+4tJ?53aP6=X7Or+MI>Fjqq($$d|M*RYwLO*84n+>{BJeQGX@Ezc3!H+_gR1 zR_Ij?XDP|!c+u!3^<33pY$uNBhqAF>71A0KdN0`8qIROl!k+a6!VjO~bh?%$*Nrc7^w zCJum%I1ipoTdoEcO{F|#r&U3{DM+(rA82i#-z38Fco78yV1A4eiEqHITBlCu+a15ZFck7d@=|yC;s_#gIz5%MPnjdJmB-xXTWh z$G?<;6*?3213IH7-$$OHJe{22(`v!bJQA+;j&}qqGPyHkq1kG;Wtkva0*z&LVL>7TdXdMHk7Yd#pI`=-KXBPYpa zJdI@seda(pX;H&w^0(nlt6OTD!lU6qu(kVN~Xw!f>A7-GMN~{GijIYs{(1?c1#!pWG-!sj+tf* zTob3J-Sa;HJxIH8;gqwe2AMtJjDnfh-|}03f!ZPwKUv~4k{fxRtV2zz_9l(($AqYm z&U2%`MHBeM8^hiLwh}1JDoj^LN#KQkWSCRk$yfg<$j4`3qW9W=6Gv$WB3<|<-8a;4 zXWQI6;YAV8N&a3Qvo1|vv<^#tyZEQqI`BmhKa_!zObdGVeYC{qW7qpPbs`%4u^sSN zv%ldc83cClt1>8NU}atTPYq#f$^MJCRKP6b=JBD=lRaJS&2ZF-SoK9c$=o7Rj$fS` zcJW(&1;SK(xnuzv{{er~rWX_F)}q4O&;zs3lEoh}yYO2;{+fnvHU1j3Jj7{%P#Ty+ zz})#;@O0o|HZ}gO&kKHXhI}D#GX~nfB?%aBc_Svu!n@N@zwSwkfm-pV2mdi_|4c*s z{8ub4zG>5sk9&zf=@T55SDZzs!wo1Qgw>e4rj;EXo5x z{)-bpWeQ$y12Qk_4f8iW&I9A+b`a!-NBp-rK536%1@x{|9Vp0NZ@KSZ@k%(X=+@;8 zZ#YYR|Ftc?{4gL#{>~`$a+@dMZwW94c;b+DL9ow17)YYzQ{(ax^DPAaQ?6eKHOO6u zTvT{qrd{O1Sb+bdTtYD_{p@%AO#!Gn1b>!rzso3KvDEdCMeC&9U*h-AGU`pN0Fql( zZ9DpUtk?`NnpZ1_-DKVg${^+1z~BTCt&Bl8XJ7m4|z)u25zO3d(kkbA{C<*3``fA z5ziU}*4z{_{tI!b)S0J7;x(%9M+&of@8A=w5pLes=iB?Jn@^^5jMkdL-S=Gy zpNz{0I=?VRJQRJxkMuB`Ji zfRgZIJ#0Fk%pJdr@;+cS_6_yXIxDO>>53qZv_Sz@4ZY10x-qmVxP%MGKVL(}Jt7R-sv9$w|24g-W^xC~zHlXC=*eSx&s z>S{@Fo{Y`@z~7$4oh7&KNYR{oWBa_6gN@C@6DXprxT}-%awtO$+04YGVsF9|^W=^M zhhizj9aO(YPN`5%6Sz*EJ{`!IW%P@>W zAv4&I&^&V2rB$3ry&m$U4kL(SMI4bPos-GF#MOvtm_2^Keks6L-ayoV(FHFErdI4+ z)G}9y#IWNYD&Og7`Olb0GQi=p3bI z->;r(oVRlhGcRRLVH=~sja=gqzj9lbLS44!I1 z6~+)!_henuTMw`Ud*x#H9T0Etefix=>L3XLfXgC(kt&{bGMmOqtk{r2gRvxRq6R{K-6~tpJV>xf*%h@L$sU5ZE88pZDVf+bRdTz3m1lsYTcCJn!|? zo3W0uLow&IY4l+E1bFEn>8%$0(g|X$T=IP=ExQhk@#z9By5FTHMY|95*4Q&FLSkLn zEy%0&{3>7bsETUAI-U%BXaX+9vn)L-Gcd7X_VcU`tx&gN2AlNLwts5{Y=JFsyezeM z2&cKYb21=T&^JTAU>yqukOO<;jkULJ2HI+s`XIq$a?9)u7~g~0MvRu zY^>o$^QkTPc_MrzzGEaxr~>zO8Q5lAtJez#-5G2T2lfYHlMs9d!zR5JG81hB$29?! zep3qM3`bAdnZb31c(BU`ae>|ge78QxBEh2D#mIELom_CRU8qBKbN<{IegfrgjX0sf zqFa@QoA#4bB)wxe$C-or^~&5P5Q#UyPh3&7T6zmEH5re z`}0cV(w|q9jmHMVn|YbGEohhol+}1Ec4eA((-ls*U~F%kedCGaan2%oM$R zE1t2{9xhyGd2IAxkp zl+TpBHU-=wKT_w;{ZM{I#iQGQ3ES^k%QC4tetoRN#*o#1K9C>07NF0lxY#GSNc-MS z+l}_?ILko=c<4&|eXz}P7zj@M^rQIn2*rDoM9f}iR^5587Kzy&ySJ;A&nse>~!!+Z-pni&LHoyr{|A0IswblmEcUSPm_%+!BacikJ|;EUXQL%=N^uY4?d6m zN2>Qq`v#Zl-zdFx3_M=x;t=%hu9;vTeX7qH*)x1=#o?a8roI2J%J-c2v zdHvv*v3U)dxLCWpJ#fCN(mb1bX`R`=HM^_pSk3OdWB@$Obu3M?Sv6JLT)eEDjLTT8 z&pvr;tPB0JaC+^`7ka((em;AB-tT;V_~iZA`TBUL@I-Wl<0&pfS?6}`dWXx#tKq$7 z<^8&OdwN>*YxHLC)U>hLoB#3RXd3Y&*I*;*j%(wu3+t;}?lW>nDuKe)alutVi`M(f z=+xlx*)NUFu!v@jolkWu0twO4ogNRD=If{13#u!H(XO+!HYX=`{N<^IH;F63(GPQX zM^hJxC$pcP9%r6jfqRSo#quTmtx@TINDvSodJqtde^|}s;OJ&){CkeyO zk!`HU=^qX5mdZVk$YT~C68DSe-zK9SkidzUA*w@@Rz#lGJ9cUnI6Fj32+l0>$O=Q9 zH8>D}&F-CDv~Xu2hodo+5=9H@QyukmYe9L(z6A4Hbz3^5*2;k97R&CQ_k4-!hQd8he7-wZi>*HTQ%qzt)eF7??n8r1 zi@gyCCqK^)@o%fiP_eHpKE>8V##PA*qyR4+WxquEj7mMI z+HA$0+s##Vkq~s)gvg<#PtC091lDb01`^bjo3yb}$u4Z35mTLb8Rzw02X;83C!|-TS%*P|H+070uiQYa_Aj9 zW#CvwUVB>lhH4>*{S64lUL{Sd-El$8OPxMicv84mV`gT2F2ooeVwq=UL*WgY+9qrU znh$T+wqC{Q4^O`qzSb6k?;=*I#zz2fmx9~IJ1`qW-F8GJZB)_HK~&O@cI2XSauqMX z61+ga$ht8Ho02un$fFF8y#fN2(CNPuVxhS35rNrcU0)z~2|fl5{B(~v(a}gRd*_$Gq;PpdTfvy7RPjxu<)QEi+M4IH=f3jCkR(6*oX~s)}jzX-~U@U1ScwdVAd^} zvroO30_JN-brh8fX-t~LB0iBgnlVA$s&hWMIzlUp`xIg6>WQQi1mGz&wDCK^K;#rt zpetFo#d8~h$PW0bhAo2qPD2UhYYPl7%TxhZ6wl~Aw*-VJ zJv^pTC`WPjxC8ho7BfjMv~!&I`l&q5!7o>2P8eo-go`OTIfvHi_1y|3s(g3(u^;PK z4M1aPaRBKPTpjb|;QeH(;bfNh`A=n}ry0E4M4oo8^+z{;29o?^04>=kRY6+Lhq6NY{ zh_@O&}d#FOas)slu4K?a@hu>@`7Q5@ItOaW-vS zx@R^0L~H}&0vnR6M~BDRYE9Kj(?2~0mOOXT3Mu^19;%7737#+??b@16)z&zL1Y9n_ zOsp;!pf6;nS-qS|Ua=i_DmsrvlJW=C3OYU1Bad`wH0Z z7wQ!nqBdB@THBvS*M+%nc}HafIgQL`!^?9weqq4{!bjus+!S^2RF~OYp%SSOHdrq( zPF0A$HoDS!{F^oY1Q>-yFF?7s1}^c&{0G@Lw6V0YclfRQdEZ2=foqnq2Al*6vv(QvwGftx$b|30iqz7s8`y)OdLI50(znB zHWMeTPgg(WX*+b&hsh3s$t0(bZt++TL(Yu^D>-gj6lQ|>OG0g{XJQS?u(4DDm^_3_ z*mX6XtG}MsGx~d7Wt+GeC8Zf0V^w=yjV1%BgIBFFC(QsQPst0TA~n6 z(6V~zXq+{Kt#t6X!zL{oFKujIS&OnqP~TE?3%xj1WxL?Oxc%8y8TXQ| z{`pcQ&rklwmhttHJ;O$byjLue-74KqA?o;W)z8*klOkFkAre-nGXio=o96?oQ9O5oqAF89Jq>d6 zbK=wiYF_bLuGx-oMkpGIat#B(83{dRuV)rL>7x!oDb)&Z_d8)J>ZJw)8kp+>;P$=u zOYp_iQu$6iY8tF%a0&08Xbca0{hfpv@LkC^yR(+KnHXC+4^fa#oA6ZhwDHG8d%3lcSau?3xtg zA+jiP<%J}WtS$Ww_dCG7y5jr?&RN3#2ha7RC;d+BrWd^_F5>Su;I2@G`}Z#y=r@mE zpHA>YYWUgV81reXL@pY3WNCA=+P;RDFEAxqtYPRiNXJhd#0_j^q0PYPTOLaBxU{nn zkLN9I=$0k_Fqk~rO>pnHO)w%2YrHg$W~D8%gL$VFUxqT%<@mE@Lhzc%Q|dkfuDp%? z1I4W7FQkF(AnTon>@(Mn=-18Ilk0{rzQhnZ7>}NX>qlF6;sVX^I~CCtHxN>55VZ4B`T0S;c`6kqndNHna^&x!lfE1*HjEB;?*Fjdx;Bs zxJZk$?O1M}q*ZHigs2^%g%5B>$4+<;8fqvEcj9g|dkgKk`?Sy({YZ|vzsv$bY9xaI zd$>i%V&v8~6`$#?$KNx;GmSTYDQVJxmzwYtzv|Vr?aVCpRhtVn;HagEGRWO3B+fv% z;WH&lCMZ5iht>xt%5Dj192vYh5(Pl?M&3={VZF+iwXx#DW-)fAg31Gg?1#BN#@fVK ze^`H0Cm7TIFkU78uN&PuJBv+T0ho0R**Yj3ls`64QY2gVzGxLVt2Be#G-l!<9woVG zwgvf=Z{Yp1hFB29O69;O9G&g?yawL=xe(tQ@O3}+VVUeXWCm+wdN3pEF1O*>nYq;Ujf*L6=0eb8Jt z!F>*IL+OcT9AIxxi2PfS_0DRUam5oBD5VJaqxW|Y;WY@3!?3iJCOo6}7 zjeb*ZIv5_Y$}ADh_odr1J89bdstvc+V4tALy@DXh)HVy7%>ze&*0Z-^I}5w{8DTk6 zH4uv5*BVp0ygo8#z@}fUm^e6(oHt%E&6;?U2J=)X|x2bp>Rpp$eB$DId zpnRO|BC7`9CE5Hc6mKV=h~UDNPI%Vt7;XSc17LpgRPnX=YVP6tBUKugJ~lVD1~(m3 z(*1iRyOKQm`l`hDl0H^_y}773scLXBOf_`&-jzGQIvBW8IP8yBqFH1~S0-LN9wsM0 zus_@+dh>L8hCfqZ`&pQSpvWw0ncPJ+V&9Tu&z}meA3=EWA0V_< zFI@Bt!IDk7bu_a53UC&Z*)>Lyv0V<#l`4+ds!CVJFynNF7oX3VO~X2a6gNj`Ck?x< zSQGm}7dE%O3Le(R8}k7}eO>aoSrsLkTEVyQ1I7ckNX)=K-B6pKT&Dqy>)p??8yeJk zKUBmqBWce;UxtOAkpiqP*vS*71rSw82x3~0cHQ!JrL5b0sQOg%6NjF4y!Qt;TF5ol zq3vTbM)2;*vF`2Nih8Q699Fje``(|A^9Ng@kDZ*zq8E=)-OGoY1lKmYKm0^+pwkK-7=+y6!QS@Exg(u;(DN=T zXHLs|l9nw~B4PCD9E>ZuS#F2NQzkX$N>y(MH)F)OR}2*+!Jd1kZJ3J1vp(FR*Xv!` zDE?K<3n3(~uSAPdwJ;tGP5QTY2eb~qIx(NE@G{%L)MZ^NB-Fwx67wH!;=zdP*)({x zrdCA5s}qvQ(V5@*7jtVVq7(}6k9^GYDfNIN!%g+wrQ|VoA+_Tu{<(`GK=N zc-nl>R@-rJm$fs8_DHLgg7PKN-cb6|iUxA*oh5XEK@lf2_IP&sH)P$M<+@ai8g75Z!o@}$bs ze+0A6@Y2$k55GsECmz4|aBR{=!hPH(I5jSLf(kAzaW3Z01@4*%^XA%Tugtj255#O3+m#M@T6ya~#392Cxa4ZQ(-8t)puDriXsZodjg3jW_ zTAKV-s+`lQC)NH;2+?u{;q&U=2e_0#Q)+O0%a+)}X6^#8PS~##yhI=QB_F)xecH0X z>?J>An7SwI#XrCJ*M3I^W%v%>t_je^{4hJ^@Yr;EVrTpg(=fCdt`}YY zsm=3--VNsnfQZOrfyQ72)s?5^mmYr!TPqNbjv*Ca3XKupjAZu6b+g&CYg$7nA%_N4 z!PB0({tO76`Z1?)_VMv*a5XUv?I@{2-K38Cptj0U(eqawkvVmgGdu)r9mBcW%zSf) z1lp)(c2YY_`&5z@0`=ZGOluyLvzYmJ$|cK&kCw=O-y1M{tOgMxJ&xfx`|}Eve~uB3 zb4@$xa@Zh7$bkbQQMt6^8RkQYy9cwu{09q)YdqXqdGqLQRz~o4pk5aLK9GWZvtwNBGSuHBSnhh(2Ml) z2F97-9p+v0t@Zu+CM)aauDkYg_sKaocki8Zeo4HjAVm5ytC!V#`;cpgN_~nfn46{S znw?^3OJ3~Z!S}Bd?XcKB4$@UPH1u)emQ_mzc0~&HOS@K0txv_aN6l=yAq^BV{$Aj7 z13FZDTSqPMc1Cv3XuSe_7@Y(nm6kOuyruYV817N!mc*MGONVV!!o`+Z{9d8p?yjn` z>NwfY_k;I95-Ih`mzZQyGEuN%F;g|}7!-r(SZnhYg27#Jqc`Xk>woX6ZAU2 zc?m2ml0WjIlRB8o@mkhTMxBu;u@gNO0?>8*Vh&Up|Ioweu?Rmb6$;mCS-+PA)}dvg z$}g_;GDzKmz4@-Q!Rj$DDcf6Z_hgeFl*M5bRxr?gyYv&@L%l5JF7d`+KNH`AT!t|j z)ySttg-TJ!UofDGb|9y$YtP;8T&x$lh?n+zQ7)?KmiB{SuhrFs^=>Yiv5~pEPsypx z35L?buG}87Qdx_jc6L4TFNnDSyE-KaditU|Zoe|vwcj>{(dhTKHCboZg|G$!s}DTF zA(@^qvH3_jL?vVu2bH+At^Z>3(zLB=!sFfd=W~p*iy3-{>!az%8-kR)EPF3pg0MY* zN)o4-tCEDv-pRGp@~hNuw8Ui(zUeG1<_Au8icl&@FouAU8`MJBL0+uw#dy%LTJBL& z?fYgoch~BQV|EVm1=6SUmFN!4buWrqzVVmQ3UL!F4e@+JJY2)k0yAD4OexpHqT4nv zyi0kM0)K_l%oMCWUmD7-nFuit7^5zI8$b9yz<+vil`Tz|_ zM`5b=D^6tN&O+2*_m!sGY6;BDhZKx(ubCscO~SD6)|%pe`cMHz&4=SOcTBg^N~`w= z<$<)Q3sJ#lPS0(~;Vdk(c&lpgy%+&)QFEc3WOYYts3t#|fZoDQPKWI}mN@@s2z9jr zfzW(Zhkxef)zlJGWS=1QHzH%tnOib6r0g;2nmM#Ayx1Hc!2!5LB%RqGpN?R~+6zx~ z+h;8K;mmaV27M6)XJ*YLThtV+Fz6;2;xUnYVWET}*1jP6i%A2V0u|)m_A8oPNy_UJ zm@Z;>;qQD9e?6msKATv~_DPbLPNz;@c$Ing3r9U8uC^jQk|M6cCw&i(m=tZuXXsk1 zVEUq)g>E4m$WU0JJ`EA?P1OedwYJNw5EABq^lLmFT(uDiMFGaj3(-oY4QmSL!gyL| zt7rlP9(Z7XzhvR28Cq6Sd0V!1sv!B}h>w??S@9xMduRZFGURx!D^`ypns9!HD!mL5|6c4hI1SoYWKW^Xb!~XTf%Dq1+6~U4zM>tfXGBJV zbXTlecA@WahD}5C$9c+G{7SiAiG1TF5)gi1l|vvTRw)gYXre4Zj^W2Ft}-#2a3U?N zW1rg(xUu;$Z-X56ksSup_yac`_9eMSJq|PqOH^q4J@&abZgPVAt7;H=(He6$d0?3g zt6ZX;z_(d^GnBPanbGmAN2!+={H&ke`BpKNA5ktlj$nqjwzdCGVtVa&HIbc_VVWy=yYDf`1jZt^|86Y$iePus9zwCPIDpV+Or?H}P-09CJ-j-#qPAg69^6K=8uMRs3dDmCmGB!JRXSB{`kKPOsC6=@eC}T=fXq&O&S16eAYPMsaLGTZV zfxAYu?z7Kuy-gRJ7fgabkYo_wFQH{z4oeYMMF^qN@F<51Gjg{-AME-197%o&qh)Yx ztPGlj)3)zyNt>&=cuJIVmABK!nfyLwJF=MsJ!P z1+KBaocg>YSvQL%$JsNv<@%&%`u?^FSqo}aidm~5l*%ojQK{RzW&rExX+-d=sVEJB z;L-2>?$si(0!k@9WVuZ0Z|awF4)WXQ=U;O@n$1tM)cfr+TrR>aD}Yktw(kN89?^Nh zhI-A%B_}zo6pWO9M{=%Ydbn~)*5?JEv+ek^c!+O$6ev?pW3l5-wx{%*narK9_QZ9D zBz0`6VHZE)SWgZJHItT=GN`+WHmn*ZTJelG22ynfy4GY%?2(;yAEK+;RF^^Dp>CjS zPCtH@aX~M#x3SiCbv~9EIKCZK=t9H$WyyVL_sQg!RB}{C8$B+oMD?+4WSG9@<6*Pu z?BdKH8LN>hk}+G4Q{ZzRY!es|iuh8W3e_s`}cJDpZFjJ_v3#{SC)MA+cFltPPdc>X%$%f`jW2dv-+>Eu3^P%I#e-Lvov zs^@XrT9r^B!TtTH9>Sx84j*(>^Y^&NjQ4%29Qw#7^o#qCxCf3NXs$NS+BUAPC&Cth zeUWRHc21V&>BNF%f+b_v^bPxIett22&n#%F36+SVo4d&ynsBq^IL-yI2M|8pWO1+! zbe0uubtOMwCg>?dpW+NJd^hx#ZlRC-doP4ec~O4sML~BfvK9GJFdP-gj@LS68)9yW z(?_A!A=?+z5D4~+t`q@FvLq3P$Zhztz_*AMOVPpJ8J_&#KM&zkp8S7(9#hVj(q+)O z2NeBHK!Fw$wT4+~I>X>D0#-0*o8zwlwC?WzG(^!I_)9EQwNa4j=>*=9{728^tQ2{9 zlwVsaBY`f}1lYNnI$4yX*Xd}yBBe4wdi(LRAHx?K3(`|WvpC>RanhxA?xs#VaW-giLm(i>ALj|y23Rcwt5{*p2rFm;WP zl&R^y1NBx$g_L8>_KXygmsPa@>GDzfgS4O+B}GcKmYVXqO)@qMUeI}}d&`3AJcsKZ zC2b~Nf;=Lx@(y|fQy>q%I`S$$aAr1^rmHJbt#u5cSkvBr>+V6P8-ZR|avO`!g*UMT z>Fvkrj(!`j8Jc(r8twXmfkcM6ECJDqm7r zGf6uQ^FeF*mUnt-1|;A}-dHzIxXhX^g~AuxYH3|V^YF}}&pg+kGKfBM5k2V0(c5Ev z7Yj!#`Fj?2PRC=;x>wx+t@U(%($nIoVeTzxK1u5~;xY{l0WOY#i+JQ9?mmU1$f&Ow z*{JZ#aM`GyaM|WnOVK?!=DW6d^@Sql_u?6BJ!#;(yIy1GEdzNzRIoK^!rZa% zzr_=K#s1{mMBB2=jRRJam`LSYS=|%MJ5h)vWsNF(HvSR%5K#pX8+DA>#C{!|rH}ku z$cBPF+kn~UZrJm)HEzuPY~Tp=g(ree`pHjUcouN@|E@dqp!o4XJZnG~JHb@?hqAFyWGb_>h6P^&HjV90`t*7_IFRsIr~Diy4(-WA+e=L}F%I^q|!uc+$BQf1n}SEwZ2f zXh?Lc_H%f3zV|JpmRBC1Lj^cV%SQ;bJ$FH^=plWdmjAQyfzgtQ(Wp**NjnA}2`5Kl5b4E8LcR0+O~uV%JTLsmefUmvpiiW> z1~`*B-H%xAXRp4?j;y$Or;dASFMMvaW_k6c;g0Ilm9mxT7y8fnWFg`p#u-g=mr=Kp zh>y2&Khduxq*jC1Z#dQ6WKpthbdBws^}OC-W>)9*mrM~h4nI10Jo}Fu{_Md&w`cr~ zgQn`wfS>i>|1><_?xHV~Q)=))!=E*=Pa86$<+c70Axb~fc=x~2nYqHHyEg~;lF{NBs)OpKYRM+ zUqMgu79jNJ!~^3e7M7zC7S`X=5TH3Qgv0;M1& literal 0 HcmV?d00001 diff --git a/sciapp/action/__init__.py b/sciapp/action/__init__.py index 3faee3d4..bf720152 100644 --- a/sciapp/action/__init__.py +++ b/sciapp/action/__init__.py @@ -4,4 +4,4 @@ from .plugin.mea_tools import * from .plugin.shp_tools import * from .plugin.roi_tools import * -from .advanced import Filter, Free, Simple, Table, Macros, Widget, dataio \ No newline at end of file +from .advanced import Filter, Free, Simple, Table, Macros, Widget, dataio, Report \ No newline at end of file diff --git a/sciapp/action/advanced/__init__.py b/sciapp/action/advanced/__init__.py index 1ad42725..8ee8c376 100644 --- a/sciapp/action/advanced/__init__.py +++ b/sciapp/action/advanced/__init__.py @@ -3,4 +3,5 @@ from .table import Table from .free import Free from .macros import Macros -from .widget import Widget \ No newline at end of file +from .widget import Widget +from .report import Report \ No newline at end of file diff --git a/sciapp/action/advanced/report.py b/sciapp/action/advanced/report.py new file mode 100644 index 00000000..a42922bd --- /dev/null +++ b/sciapp/action/advanced/report.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +""" +Created on Thu Dec 29 01:48:23 2016 +@author: yxl +""" +import wx +from sciapp import app +from sciapp.action.advanced.dataio import ReaderManager +# from imagepy.core.manager import ReaderManager, ViewerManager +from sciwx.widgets.propertygrid import GridDialog +from sciapp.util import xlreport +from time import time +import openpyxl as pyxl + +class Report: + def __init__(self, title, cont): + self.title = title + self.cont = cont + + def __call__(self): return self + + def runasyn(self, wb, info, key, para = None, callback = None): + self.app.add_task(self) + for i in para: + if i in key and key[i][0] == 'img': + ips = self.app.get_img(para[i]) + para[i] = ips if ips is None else ips.img + + if i in key and key[i][0] == 'tab': + tps = self.app.get_table(para[i]) + para[i] = tps if tps is None else tps.data + + start = time() + xlreport.fill_value(wb, info, para) + wb.save(para['path']) + self.app.info('%s: cost %.3fs'%(self.title, time()-start)) + self.app.remove_task(self) + if callback!=None:callback() + + def start(self, app, para=None, callafter=None): + self.app = app + wb = pyxl.load_workbook(self.cont) + xlreport.repair(wb) + info, key = xlreport.parse(wb) + + if para is not None: + return self.runasyn(wb, info, para, callafter) + dialog = GridDialog(self.app, self.title, info, key) + rst = dialog.ShowModal() + para = dialog.GetValue() + + dialog.Destroy() + if rst != 5100: return + filt = ['XLSX', 'xlsx', 'xlsx'] + path = self.app.get_path('Save..', filt, 'save') + if not path: return + para['path'] = path + self.app.record_macros('{}>{}'.format(self.title, para)) + self.runasyn(wb, info, key, para, callafter) + +def show_rpt(data, title): + wx.CallAfter(Report(title, data).start) + +# ViewerManager.add('rpt', show_rpt) +def read_rpt(path): return path +ReaderManager.add('rpt', read_rpt, tag='rpt') \ No newline at end of file diff --git a/sciapp/util/xlreport.py b/sciapp/util/xlreport.py new file mode 100644 index 00000000..18b3931c --- /dev/null +++ b/sciapp/util/xlreport.py @@ -0,0 +1,116 @@ +import openpyxl as pyxl +from openpyxl.utils.units import cm_to_EMU, EMU_to_pixels +from io import BytesIO +from openpyxl.drawing.image import Image +from PIL import Image as PImage +import numpy as np +import pandas as pd +from copy import copy + +if not '.rpt' in pyxl.reader.excel.SUPPORTED_FORMATS: + pyxl.reader.excel.SUPPORTED_FORMATS += ('.rpt',) + +def parse(wb): + rst, key = [], {} + for ws in wb.worksheets: + rst.append((ws.title, [])) + for row in ws.rows: + for cell in row: + if not isinstance(cell.value, str):continue + if cell.value[0]+cell.value[-1] != '{}': continue + cont = cell.value[1:-1].strip() + tp = cont.split(' ')[0] + cont = cont[len(tp):].strip() + note, value = 'no description', None + if '#' in cont: + note = cont.split('#')[-1].strip() + cont = cont[:cont.index('#')].strip() + if '=' in cont: + value = cont.split('=')[1].strip() + name = cont[:cont.index('=')].strip() + else: name = cont + + rst[-1][-1].append(((cell.row, cell.col_idx), + [tp, name, value, note])) + key[name] = [tp, name, value, note] + return rst, key + +def trans(img, W, H, margin, scale): + h, w = img.shape[:2] + h2, w2 = int(h/margin), int(w/margin) + if scale: + if W/H > w/h: w2 = int(W/H*h2) + if H/W > h/w: h2 = int(H/W*w2) + newshp = (h2, w2) if img.ndim==2 else (h2, w2, 3) + blank = np.ones(newshp, dtype=np.uint8) * 255 + blank[(h2-h)//2:(h2-h)//2+h, (w2-w)//2:(w2-w)//2+w] = img + return blank + +def add_image(wb, ws, pos, key, img): + if img is None: return + w, h, margin, scale = eval(key[2]) + img = trans(img, w, h, margin, scale==0) + img = PImage.fromarray(img) + image_file = BytesIO() + img.save(image_file, 'png') + ref = BytesIO(image_file.getvalue()) + image = Image(img) + image.ref = ref + image.height = EMU_to_pixels(cm_to_EMU(h)) + image.width = EMU_to_pixels(cm_to_EMU(w)) + wb[ws].add_image(image, wb[ws].cell(*pos).coordinate) + +def add_table(wb, ws, pos, key, data): + if data is None: return + vs = data.values + idx, cols = data.index, data.columns + dr, dc, ir, ic = 1, 1, 0, 0 + if key[2] != None: dr, dc, ir, ic = eval(key[2]) + for r in range(vs.shape[0]): + if ir!=0: wb[ws].cell(pos[0]+r*dr, pos[1]+ir, idx[r]) + for c in range(vs.shape[1]): + if ic!=0: wb[ws].cell(pos[0]+ic, pos[1]+c*dc, cols[c]) + for r in range(vs.shape[0]): + for c in range(vs.shape[1]): + wb[ws].cell(pos[0]+r*dr, pos[1]+c*dc, vs[r,c]) + +def fill_value(wb, infos, para): + for worksheet in infos: + ws, info = worksheet + for pos, key in info: + if not key[1] in para: continue + if key[0] in ('str', 'int', 'float', 'bool', 'txt', 'list', 'date'): + wb[ws].cell(pos[0], pos[1], para[key[1]]) + if key[0] == 'img': + add_image(wb, ws, pos, key, para[key[1]]) + if key[0] == 'tab': + add_table(wb, ws, pos, key, para[key[1]]) + +def repair(wb): + for ws in wb.worksheets: + for cr in ws.merged_cells: + ltc = ws.cell(cr.min_row, cr.min_col) + vb, hb = ltc.border.left, ltc.border.top + for r in range(cr.min_row, cr.max_row+1): + for c in range(cr.min_col, cr.max_col+1): + cur = copy(ws.cell(r, c).border) + cur.left, cur.right = copy(vb), copy(vb) + cur.top, cur.bottom = copy(hb), copy(hb) + ws.cell(r, c).border = cur + +if __name__ == '__main__': + rst = pd.read_csv('rst.csv') + img = np.arange(10000, dtype=np.uint8).reshape((100,100)) + data = {'Sample_ID':'Coins-0001', 'Operator_Name':'YX Dragon', 'Date':'2019-02-05', + 'Record':rst, 'Original_Image':img, 'Mask_Image':img} + + wb = pyxl.load_workbook('Coins Report.xlsx',) + repair(wb) + ws = wb.active + + + infos = parse(wb) + print(infos) + fill_value(wb, infos, data) + wb.save('new.xlsx') + diff --git a/sciwx/widgets/propertygrid.py b/sciwx/widgets/propertygrid.py new file mode 100644 index 00000000..60eb5f60 --- /dev/null +++ b/sciwx/widgets/propertygrid.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python + +import sys, time, math, os.path + +import wx, wx.adv +import wx.propgrid as wxpg + +from six import exec_ +_ = wx.GetTranslation + +data = [('Sheet1', [((4, 5), ['str', 'Sample_ID', '5', "image's name"]), ((4, 17), ['str', 'Operator_Name', None, 'your name']), ((4, 30), ['date', 'Date', None, 'today']), ((10, 1), ['img', 'Original_Image', '[8.16,5.76,0.9,0]', 'the original image']), ((10, 20), ['img', 'Mask_Image', '[8.16,5.76,0.9,0]', '']), ((28, 1), ['tab', 'Record', '[1,3,0,0]', 'records'])]), ('Sheet2', [((0,0), ['list', 'a', '[1,2,345]', 'nothing'])]), ('Sheet3', [])] +key = {'Sample_ID': ['str', 'Sample_ID', '5', "image's name"], 'Operator_Name': ['str', 'Operator_Name', None, 'your name'], 'Date': ['date', 'Date', None, 'today'], 'Original_Image': ['img', 'Original_Image', '[8.16,5.76,0.9,0]', 'the original image'], 'Mask_Image': ['img', 'Mask_Image', '[8.16,5.76,0.9,0]', ''], 'Record': ['tab', 'Record', '[1,3,0,0]', 'records']} + +class GridDialog( wx.Dialog ): + + def __init__( self, parent, title, tree, key): + wx.Dialog.__init__ (self, parent, -1, title, style = wx.DEFAULT_DIALOG_STYLE, size = wx.Size((300, 480))) + # wx.Panel.__init__(self, parent, wx.ID_ANY) + self.app = parent + + self.panel = panel = wx.Panel(self, wx.ID_ANY) + topsizer = wx.BoxSizer(wx.VERTICAL) + + # Difference between using PropertyGridManager vs PropertyGrid is that + # the manager supports multiple pages and a description box. + self.pg = pg = wxpg.PropertyGridManager(panel, + style=wxpg.PG_SPLITTER_AUTO_CENTER) + + # Show help as tooltips + pg.SetExtraStyle(wxpg.PG_EX_HELP_AS_TOOLTIPS) + + pg.Bind( wxpg.EVT_PG_CHANGED, self.OnPropGridChange ) + pg.Bind( wxpg.EVT_PG_SELECTED, self.OnPropGridSelect ) + + pg.AddPage('Page 1') + self.key = key + for page in tree: + pg.Append(wxpg.PropertyCategory(page[0])) + for item in page[1]: + v = item[1] + if v[0] == 'int': pg.Append( wxpg.IntProperty(v[1], value=int(v[2]) or 0)) + if v[0] == 'float': pg.Append( wxpg.FloatProperty(v[1], value=float(v[2]) or 0)) + if v[0] == 'str': pg.Append( wxpg.StringProperty(v[1], value=v[2] or '')) + if v[0] == 'txt': pg.Append( wxpg.LongStringProperty(v[1], value=v[2] or '')) + if v[0] == 'bool': pg.Append( wxpg.BoolProperty(v[1])) + if v[0] == 'date': pg.Append( wxpg.DateProperty(v[1], value=wx.DateTime.Now())) + if v[0] == 'list': pg.Append( wxpg.EnumProperty(v[1], v[1], [i.strip() for i in v[2][1:-1].split(',')])) + if v[0] == 'img': pg.Append( wxpg.EnumProperty(v[1], v[1], self.app.img_names())) + if v[0] == 'tab': pg.Append( wxpg.EnumProperty(v[1], v[1], self.app.table_names())) + + topsizer.Add(pg, 1, wx.EXPAND) + self.txt_info = wx.TextCtrl( self, wx.ID_ANY, 'information', wx.DefaultPosition, wx.Size(80, 80), wx.TE_MULTILINE|wx.TRANSPARENT_WINDOW ) + topsizer.Add(self.txt_info, 0, wx.EXPAND|wx.ALL, 0) + rowsizer = wx.BoxSizer(wx.HORIZONTAL) + but = wx.Button(panel, wx.ID_OK, "OK") + rowsizer.Add(but,1) + #but.Bind( wx.EVT_BUTTON, self.OnGetPropertyValues ) + but = wx.Button(panel, wx.ID_CANCEL,"Cancel") + rowsizer.Add(but,1) + topsizer.Add(rowsizer,0,wx.EXPAND) + + panel.SetSizer(topsizer) + topsizer.SetSizeHints(panel) + + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(panel, 1, wx.EXPAND) + self.SetSizer(sizer) + self.SetAutoLayout(True) + + def GetValue(self): + return self.pg.GetPropertyValues(as_strings=True) + + def OnGetPropertyValues(self,event): + para = self.pg.GetPropertyValues(inc_attributes=True) + + def OnPropGridChange(self, event): + p = event.GetProperty() + if p: print('%s changed to "%s"\n' % (p.GetName(),p.GetValueAsString())) + + def OnPropGridSelect(self, event): + p = event.GetProperty() + if p: self.txt_info.SetValue('%s: %s'%(p.GetName(), self.key[p.GetName()][3])) + +if __name__ == '__main__': + app = wx.App(False) + frame = GridDialog(None, 'Property Grid', data, key) + rst = frame.ShowModal() == wx.ID_OK + app.MainLoop() \ No newline at end of file From 817e008f8743cbd0a4f2a6a319144f4755af92a0 Mon Sep 17 00:00:00 2001 From: Xin Bo Qi Date: Tue, 9 Mar 2021 14:59:29 +0800 Subject: [PATCH 326/343] fix bugs in threshold plugin --- imagepy/menus/Process/Threshold/threshold_plgs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imagepy/menus/Process/Threshold/threshold_plgs.py b/imagepy/menus/Process/Threshold/threshold_plgs.py index 2d46189a..063487ea 100644 --- a/imagepy/menus/Process/Threshold/threshold_plgs.py +++ b/imagepy/menus/Process/Threshold/threshold_plgs.py @@ -81,7 +81,7 @@ class Niblack(Filter): (float, 'k', (0, 1), 2, 'offset', '')] def run(self, ips, snap, img, para = None): - if para['size']%2==0: return IPy.alert('size must be Odd') + if para['size']%2==0: return self.app.alert('size must be Odd') img[:] = (snap>threshold_niblack(snap, para['size'], para['k']))*ips.range[1] class Sauvola(Filter): @@ -92,7 +92,7 @@ class Sauvola(Filter): (float, 'k', (0, 1), 2, 'offset', '')] def run(self, ips, snap, img, para = None): - if para['size']%2==0: return IPy.alert('size must be Odd') + if para['size']%2==0: return self.app.alert('size must be Odd') img[:] = (snap>threshold_sauvola(snap, para['size'], para['k']))*ips.range[1] plgs = [SimpleThreshold, Auto, '-', Local, Niblack, Sauvola, '-', Hysteresis] \ No newline at end of file From bb99996f595b575541bc0a0a37a4419fcf47135d Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 14 Mar 2021 21:41:19 +0800 Subject: [PATCH 327/343] add cellpose-planer plugin --- .../Contributions/CellPose Planer.md | 196 ++++++++++++++++++ sciwx/text/mdutil.py | 2 +- 2 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 imagepy/menus/Plugins/Contribute/Contributions/CellPose Planer.md diff --git a/imagepy/menus/Plugins/Contribute/Contributions/CellPose Planer.md b/imagepy/menus/Plugins/Contribute/Contributions/CellPose Planer.md new file mode 100644 index 00000000..0b7b768f --- /dev/null +++ b/imagepy/menus/Plugins/Contribute/Contributions/CellPose Planer.md @@ -0,0 +1,196 @@ +# cellpose-planer +**Path:** https://github.com/Image-Py/cellpose-planer + +**Version:** 0.1 + +**Author:** YXDragon, Y.Dong + +**Email:** yxdragon@imagepy.org + +**Keyword:** cellpose, segment, unet + +**Description:** cellpose on planer framework for imagepy. + + +[Cellpose](https://github.com/MouseLand/cellpose) is a generalist algorithm for cellular segmentation, Which written by Carsen Stringer and Marius Pachitariu. + +[Planer](https://github.com/Image-Py/planer) is a light-weight CNN framework implemented in pure Numpy-like interface. It can run only with Numpy. Or change different backends. (Cupy accelerated with CUDA, ClPy accelerated with OpenCL). + +So Cellpose-Planer is the **cellpose** models on **planer** framework. We generate onnx from torch models, then deduce it to planer model. + +**We just use cellpose's models, but we rewrite all the pre-after processing and render algorithm, So the result is not same as the official one** + +## Features +* cellpose-planer is very light, only depend on [Numpy](https://github.com/numpy/numpy) and Scipy. +* cellpose-planer can be accelerated with [Cupy](https://github.com/cupy/cupy). +* without ui, with out object or class, pure function oriented designed. +* optimize cellpose 's pre-after processing and render algorithm, having a better performance and result. + +## Install +**pip install cellpose-planer** + +Option: *pip install cupy-cuda101* on envidia gpu, install cuda and cupy would get a large acceleration. + +# Usage +```python +import cellpose_planer as cellpp +from skimage.data import coins + +img = coins() +x = img.astype(np.float32)/255 + +net = cellpp.load_model('cyto_0') +flowpb, style = cellpp.get_flow(net, x, size=480) +lab = cellpp.flow2msk(flowpb, level=0.2) + +flowpb = cellpp.asnumpy(flowpb) +lab = cellpp.asnumpy(lab) +cellpp.show(img, flowpb, lab) +``` +![demo](https://user-images.githubusercontent.com/24822467/111028247-4d549580-83aa-11eb-9bf4-2cb87332530e.png) + +## first time: search and download models +search the models you need, and download them. (just one time) +```python +>>> import cellpose_planer as cellpp +>>> cellpp.search_models() +cyto_0 : -- +cyto_1 : -- +cyto_2 : -- +cyto_3 : -- +nuclei_0 : -- +nuclei_1 : -- +nuclei_2 : -- +nuclei_3 : -- + +>>> cellpp.download(['cyto_0', 'cyto_1', 'cyto_2', 'cyto_3']) +download cyto_0 from http://release.imagepy.org/cellpose-planer/cyto_0.npy +100%|█████████████████████████████████████| 100/100 [00:10<00:00, 2.37it/s] +download cyto_1 from http://release.imagepy.org/cellpose-planer/cyto_1.npy +100%|█████████████████████████████████████| 100/100 [00:10<00:00, 2.37it/s] +download cyto_2 from http://release.imagepy.org/cellpose-planer/cyto_2.npy +100%|█████████████████████████████████████| 100/100 [00:10<00:00, 2.37it/s] +download cyto_3 from http://release.imagepy.org/cellpose-planer/cyto_3.npy +100%|█████████████████████████████████████| 100/100 [00:10<00:00, 2.37it/s] + +>>> cellpp.list_models() +['cyto_0', 'cyto_1', 'cyto_2', 'cyto_3'] +``` + +## 1. load models +you can load one model or more, when multi models, you would get a mean output. +```python +nets = cellpp.load_model('cyto_0') +nets = cellpp.load_model(['cyto_0', 'cyto_1', 'cyto_2', 'cyto_3']) +``` + +## 2. get flow image +**def get_flow(nets, img, cn=[0,0], sample=1, size=512, tile=True, work=1, callback=progress)** + +* *nets:* the nets loaded upon. + +* *img:* the image to process + +* *cn:* the cytoplasm and nucleus channels + +* *sample:* if not 1, we scale it. (only avalible when tile==True) + +* *size:* when tile==True, this is the tile size, when tile==False, we scale the image to size. + +* *tile:* if True, method try to process image in tiles. else resize the image. + +* *work:* open multi-thread to process the image. (GPU not recommend) +```python +flowpb, style = cellpp.get_flow(net, coins(), [0,0], work=4) +``` + +## 3. flow to mask +**def flow2msk(flowpb, level=0.5, grad=0.5, area=None, volume=None)** + +* *flowpb:* get_flow 's output + +* *level:* below level means background, where water can not flow. So level decide the outline. + +* *grad:* if the flow gradient is smaller than this value, we set it 0. became a watershed. bigger gradient threshold could suppress the over-segmentation. especially in narrow-long area. + +* *area:* at end of the flow process, every watershed should be small enough. (volume), default is 0 (auto). + +```python +msk = cellpp.flow2msk(flowpb, level=0.5, grad=0.5, area=None, volume=None) +``` +## 4. render +cellpose-planer implements some render styles. +```python +import cellpose_planer as cellpp + +# get edge from label msask +edge = cellpp.msk2edge(lab) +# get build flow as hsv 2 rgb +hsv = cellpp.flow2hsv(flow) +# 5 colors render (different in neighborhood) +rgb = cellpp.rgb_mask(img, lab) +# draw edge as red line +line = cellpp.draw_edge(img, lab) +``` +![cell](https://user-images.githubusercontent.com/24822467/111029250-93acf300-83b0-11eb-9e83-41bc0cf045dd.png) + +## 5. backend and performance +Planer can run with numpy or cupy backend, by default, cellpose-planer try to use cupy backend, if failed, use numpy backend. But we can change the backend manually. (if you switch backend, the net loaded befor would be useless, reload them pleanse) +```python +import cellpose-planer as cellpp + +# use numpy and scipy as backend +import numpy as np +import scipy.ndimage as ndimg +cellpp.engine(np, ndimg) + +# use cupy and cupy.scipy as backend +import cupy as cp +import cupyx.scipy.ndimage as cpimg +cellpp.engine(cp, cpimg) +``` +here we time a 1024x1024 image on I7 CPU and 2070 GPU. +``` +user switch engine: numpy + net cost: 11.590 + flow cost: 0.0797 + +user switch engine: cupy + net cost: 0.0139 + flow cost: 0.009 +``` + +# Model deducing and releasing +Planer only has forward, so we need train the models in torch. then deduc it in planer. + +## deduce from torch +```python +# train in cellpose with torch, and export torch as onnx file. +from planer import onnx2planer +onnx2planer(xxx.onnx) +``` +then you would get a json file (graph structure), and a npy file (weights). + +## model releasing +if you want to share your model in cellpose-planer, just upload the json and npy file generated upon to any public container, then append a record in the **models list** tabel below, and give a pull request. +*infact, when we call cellpp.search_models, cellpp pull the text below and parse them.* + +## models list +| model name | auther | description | url | +| --- | --- | --- | --- | +| cyto_0 | carsen-stringer | [for cell cyto segmentation](http://www.cellpose.org/) | [download](http://release.imagepy.org/cellpose-planer/cyto_0.npy) | +| cyto_1 | carsen-stringer | [for cell cyto segmentation](http://www.cellpose.org/) | [download](http://release.imagepy.org/cellpose-planer/cyto_1.npy) | +| cyto_2 | carsen-stringer | [for cell cyto segmentation](http://www.cellpose.org/) | [download](http://release.imagepy.org/cellpose-planer/cyto_2.npy) | +| cyto_3 | carsen-stringer | [for cell cyto segmentation](http://www.cellpose.org/) | [download](http://release.imagepy.org/cellpose-planer/cyto_3.npy) | +| nuclei_0 | carsen-stringer | [for cell nuclear segmentation](http://www.cellpose.org/) | [download](http://release.imagepy.org/cellpose-planer/nuclei_0.npy) | +| nuclei_1 | carsen-stringer | [for cell nuclear segmentation](http://www.cellpose.org/) | [download](http://release.imagepy.org/cellpose-planer/nuclei_1.npy) | +| nuclei_2 | carsen-stringer | [for cell nuclear segmentation](http://www.cellpose.org/) | [download](http://release.imagepy.org/cellpose-planer/nuclei_2.npy) | +| nuclei_3 | carsen-stringer | [for cell nuclear segmentation](http://www.cellpose.org/) | [download](http://release.imagepy.org/cellpose-planer/nuclei_3.npy) | + + *cellpp.search_models function pull the text below and parse them, welcom to release your models here!* + + ## Use cellpose-planer as ImagePy plugins + cellpose-planer can also start as ImagePy's plugins. supporting interactive and bat processing. + ![image](https://user-images.githubusercontent.com/24822467/111069844-ce339000-8483-11eb-9dce-caa8f6ab80af.png) \ No newline at end of file diff --git a/sciwx/text/mdutil.py b/sciwx/text/mdutil.py index 3a58d9a2..3ebe601a 100644 --- a/sciwx/text/mdutil.py +++ b/sciwx/text/mdutil.py @@ -3,7 +3,7 @@ def md2html(mdstr, css=None): exts = ['markdown.extensions.extra', 'markdown.extensions.codehilite', - 'markdown.extensions.tables','markdown.extensions.toc', 'mdx_math'] + 'markdown.extensions.tables','markdown.extensions.toc']#, 'mdx_math'] html = ''' From 1986e5ad6d700ecf2080ef464c7f67cf2a756a57 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 14 Mar 2021 21:56:27 +0800 Subject: [PATCH 328/343] master 2 main --- imagepy/menus/Plugins/Install/installplg_plgs.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/imagepy/menus/Plugins/Install/installplg_plgs.py b/imagepy/menus/Plugins/Install/installplg_plgs.py index 7d6e6564..835d8b89 100644 --- a/imagepy/menus/Plugins/Install/installplg_plgs.py +++ b/imagepy/menus/Plugins/Install/installplg_plgs.py @@ -4,7 +4,7 @@ import os, subprocess, zipfile, shutil import zipfile, sys, urllib -path = 'https://github.com/Image-Py/imagepy/archive/master.zip' +path = 'https://github.com/Image-Py/imagepy/archive/main.zip' from urllib.request import urlretrieve import urllib @@ -37,9 +37,9 @@ def run(self, para=None): url = para['repo'] if 'github.com' in url: if url[-4:] == '.git': - url = url.replace('.git', '/archive/master.zip') + url = url.replace('.git', '/archive/main.zip') elif url[-4:] != '.zip': - url = url + '/archive/master.zip' + url = url + '/archive/main.zip' domain, name = url.split('/')[-4:-2] else: domain, name = (url[:-4].replace('.','-')).split('/')[-2:] @@ -62,7 +62,7 @@ def run(self, para=None): zipf = zipfile.ZipFile(os.path.join(path_cache, domain+'_'+name+'.zip')) folder = zipf.namelist()[0] zipf.extractall(path_cache) - destpath = os.path.join(path_plgs, domain+'_'+folder.replace('-master','')) + destpath = os.path.join(path_plgs, domain+'_'+folder.replace('-main','')) if os.path.exists(destpath): shutil.rmtree(destpath) os.rename(os.path.join(path_cache, folder), destpath) zipf.close() From e13cd7d9b9360939dddbcc199fc49f568774d7bc Mon Sep 17 00:00:00 2001 From: Xin Bo Qi Date: Thu, 18 Mar 2021 17:17:59 +0800 Subject: [PATCH 329/343] fix the toolbar color with wx=4.1.1 --- sciwx/widgets/toolbar.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sciwx/widgets/toolbar.py b/sciwx/widgets/toolbar.py index 993fd7ee..41c84da5 100644 --- a/sciwx/widgets/toolbar.py +++ b/sciwx/widgets/toolbar.py @@ -23,9 +23,10 @@ def make_logo(obj): a = memoryview(rgb[::3]).tolist() a = bytes([255-i for i in a]) bmp = wx.Bitmap.FromBufferAndAlpha(16, 16, rgb, a) - img = bmp.ConvertToImage() - img.Resize((20,20), (2,2)) - return img.ConvertToBitmap() + # img = bmp.ConvertToImage() + # img.Resize((20,20), (2,2)) + # return img.ConvertToBitmap() + return bmp class ToolBar(wx.Panel): def __init__(self, parent, vertical=False): From 06183d9758e94a6bd3a600cf068e4db700adee96 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 20 Mar 2021 14:24:24 +0800 Subject: [PATCH 330/343] nothing --- .../menus/Plugins/Install/installplg_plgs.py | 64 +++-------------- imagepy/menus/Plugins/update_plg.py | 68 ++++++++----------- .../menus/Process/Classify/classify_wgt.py | 16 ++--- sciapp/app.py | 6 +- 4 files changed, 50 insertions(+), 104 deletions(-) diff --git a/imagepy/menus/Plugins/Install/installplg_plgs.py b/imagepy/menus/Plugins/Install/installplg_plgs.py index 835d8b89..8cb34c16 100644 --- a/imagepy/menus/Plugins/Install/installplg_plgs.py +++ b/imagepy/menus/Plugins/Install/installplg_plgs.py @@ -1,21 +1,11 @@ # -*- coding: utf-8 -*- from imagepy import root_dir from sciapp.action import Free -import os, subprocess, zipfile, shutil - -import zipfile, sys, urllib -path = 'https://github.com/Image-Py/imagepy/archive/main.zip' - -from urllib.request import urlretrieve -import urllib -from io import BytesIO as StringIO +import os, shutil, sys, subprocess +from dulwich import porcelain path_plgs = os.path.join(root_dir, 'plugins') -path_cache = os.path.join(path_plgs, 'cache') -if not os.path.exists(path_plgs): - os.mkdir(path_plgs) -if not os.path.exists(path_cache): - os.mkdir(path_cache) +if not os.path.exists(path_plgs): os.mkdir(path_plgs) def Schedule(a,b,c, plg): per = 100.0 * a * b / c @@ -25,50 +15,16 @@ def Schedule(a,b,c, plg): class Install(Free): title = 'Install Plugins' - para = {'repo':'https://github.com/Image-Py/IBook', 'proxy': False, 'Protocol': 'https', 'IP': '127.0.0.1', 'Port': '1080'} - view = [('lab', None, 'input a zipfile url or github url as http://github.com/username/project'), - (str, 'repo', 'package', ''), - (bool, 'proxy', 'Use proxy'), - (list, 'Protocol', ['socks5', 'http', 'https'], str, 'Protocol', ''), - (str, 'IP', 'IP Address', ''), - (str, 'Port', 'Port', '')] + para = {'repo':'https://github.com/Image-Py/IBook'} + view = [('lab', None, 'input git url as http://github.com/username/project'), + (str, 'repo', 'package', '')] def run(self, para=None): - url = para['repo'] - if 'github.com' in url: - if url[-4:] == '.git': - url = url.replace('.git', '/archive/main.zip') - elif url[-4:] != '.zip': - url = url + '/archive/main.zip' - domain, name = url.split('/')[-4:-2] - else: - domain, name = (url[:-4].replace('.','-')).split('/')[-2:] - domain, name = domain.replace('_', '-'), name.replace('_', '-') - - self.app.info('downloading plugin from %s'%para['repo']) - - if True == para['proxy']: - proxy=para['Protocol']+"://"+para['IP']+":"+para['Port'] - print("proxy = ", proxy) - # Build ProxyHandler object by given proxy - proxy_support=urllib.request.ProxyHandler({para['Protocol']:proxy}) - # Build opener with ProxyHandler object - opener = urllib.request.build_opener(proxy_support) - # Install opener to request - urllib.request.install_opener(opener) - - urlretrieve(url, os.path.join(path_cache, domain+'_'+name+'.zip'), - lambda a,b,c, p=self: Schedule(a,b,c,p)) - zipf = zipfile.ZipFile(os.path.join(path_cache, domain+'_'+name+'.zip')) - folder = zipf.namelist()[0] - zipf.extractall(path_cache) - destpath = os.path.join(path_plgs, domain+'_'+folder.replace('-main','')) - if os.path.exists(destpath): shutil.rmtree(destpath) - os.rename(os.path.join(path_cache, folder), destpath) - zipf.close() + path = os.path.join(path_plgs, os.path.split(para['repo'])[-1]) + porcelain.clone(para['repo'], path, depth=1).close() + shutil.rmtree(os.path.join(path, '.git')) self.app.info('installing requirement liberies') - self.prgs = None - cmds = [sys.executable, '-m', 'pip', 'install', '-r', '%s/requirements.txt'%destpath] + cmds = [sys.executable, '-m', 'pip', 'install', '-r', '%s/requirements.txt'%path] subprocess.call(cmds) self.app.load_all() diff --git a/imagepy/menus/Plugins/update_plg.py b/imagepy/menus/Plugins/update_plg.py index f1cbd7d4..bbe4b349 100644 --- a/imagepy/menus/Plugins/update_plg.py +++ b/imagepy/menus/Plugins/update_plg.py @@ -1,54 +1,44 @@ from sciapp.action import Free import os, sys, os.path as osp -import zipfile, urllib -from io import BytesIO from imagepy import root_dir +from dulwich import porcelain import shutil -if sys.version_info[0]==2: - from urllib import urlretrieve -else: - from urllib.request import urlretrieve - -def Schedule(a,b,c, plg): - per = 100.0 * a * b / c - if per > 100 : per = 100 - print('%-3d%%'%per) - plg.prgs = (int(per), 100) - class Update(Free): title = 'Update Software' def run(self, para=None): self.app.info('update now, waiting...') - self.download_zip() - self.deal_file() - #self.delete_cache() - self.app.alert('imagepy update done!') - - def download_zip(self): - url='https://github.com/Image-Py/imagepy/archive/master.zip' - path=osp.dirname(root_dir) - zipname = osp.join(path, 'imagepy_cache.zip') - print('downloading from %s'%url) - urlretrieve(url, zipname, - lambda a,b,c, p=self: Schedule(a,b,c,p)) - - def deal_file(self): - path = osp.dirname(root_dir) - #remove + url = 'https://gitee.com/mirrors/imagepy' + path = osp.dirname(root_dir); rpath = osp.dirname(path) + newpath = osp.join(rpath, 'imagepy_new') + if osp.exists(newpath): shutil.rmtree(newpath) + porcelain.clone(url, os.path.join(rpath, 'imagepy_new'), depth=1).close() + shutil.rmtree(os.path.join(os.path.join(rpath, 'imagepy_new'), '.git')) + shutil.copytree(osp.join(path, 'imagepy/plugins'), + osp.join(rpath, 'imagepy_new/imagepy/plugins')) + shutil.copyfile(osp.join(path, 'imagepy/data/config.json'), + osp.join(rpath, 'imagepy_new/imagepy/data/config.json')) + shutil.copyfile(osp.join(path, 'imagepy/data/shortcut.json'), + osp.join(rpath, 'imagepy_new/imagepy/data/shortcut.json')) + newpath = os.path.join(rpath, 'imagepy_new') + fs = os.listdir(os.path.join(rpath, 'imagepy_new')) + fs = [i for i in fs if osp.isdir(osp.join(newpath, i))] + fs = [i for i in fs if osp.exists(osp.join(path, i))] + for i in [j for j in fs if j!='imagepy']: shutil.rmtree(osp.join(path, i)) + for i in [j for j in fs if j!='imagepy']: + shutil.copytree(osp.join(newpath, i), osp.join(path, i)) for i in os.listdir(root_dir): - if i in ['plugins', 'ilastik', 'preference.cfg', '.gitignore']: continue - if osp.isdir(osp.join(root_dir,i)): shutil.rmtree(osp.join(root_dir, i)) + if osp.isdir(osp.join(root_dir,i)): + shutil.rmtree(osp.join(root_dir, i)) else : os.remove(osp.join(root_dir,i)) - - source = zipfile.ZipFile(osp.join(path, 'imagepy_cache.zip'), 'r') - target = zipfile.ZipFile(BytesIO(), 'w') - for i in source.namelist()[1:]: target.writestr(i[15:], source.read(i)) - target.extractall(path) - target.close() - source.close() - os.remove(osp.join(path,'imagepy_cache.zip')) + newdir = os.path.join(newpath, 'imagepy') + for i in os.listdir(newdir): + if osp.isdir(osp.join(newdir, i)): + shutil.copytree(osp.join(newdir, i), osp.join(root_dir, i)) + else: shutil.copyfile(osp.join(newdir, i), osp.join(root_dir, i)) + shutil.rmtree(newpath) + self.app.alert('imagepy update done!') class Refresh(Free): title = 'Reload Plugins' diff --git a/imagepy/menus/Process/Classify/classify_wgt.py b/imagepy/menus/Process/Classify/classify_wgt.py index 5f68ad08..3af4a821 100644 --- a/imagepy/menus/Process/Classify/classify_wgt.py +++ b/imagepy/menus/Process/Classify/classify_wgt.py @@ -53,7 +53,7 @@ def __init__( self, parent, app=None): self.Layout() def LoadModel(self): - fs = glob(osp.join(root_dir, 'data/ilastik/*.fcl')) + fs = glob(osp.join(root_dir, 'plugins/ilastik/*.fcl')) self.models = [osp.split(i)[1] for i in fs] self.lst_model.SetItems(self.models) @@ -71,9 +71,9 @@ def on_save(self, event): return self.app.alert('you must train your model first!') para = {'name':'New Model'} if not self.app.show_para('name', para, [(str, 'name', 'model', 'name')]): return - if not osp.exists(osp.join(root_dir, 'data/ilastik')): - os.mkdir(osp.join(root_dir, 'data/ilastik')) - joblib.dump( manager.model_para, osp.join(root_dir, 'data/ilastik/%s.fcl'%para['name'])) + if not osp.exists(osp.join(root_dir, 'plugins/ilastik')): + os.mkdir(osp.join(root_dir, 'plugins/ilastik')) + joblib.dump( manager.model_para, osp.join(root_dir, 'plugins/ilastik/%s.fcl'%para['name'])) self.LoadModel() def on_saveas(self, event): @@ -91,7 +91,7 @@ def on_export(self, event): para = {'path':'', 'name':''} filt = ['fcl'] para['path'] = self.app.get_path('Save..', filt, 'save', para['name']) - oldname = osp.join(root_dir, 'data/ilastik/%s'%self.lst_model.GetStringSelection()) + oldname = osp.join(root_dir, 'plugins/ilastik/%s'%self.lst_model.GetStringSelection()) print(para['path']) shutil.copyfile(oldname, para['path']) self.LoadModel() @@ -101,14 +101,14 @@ def on_rename(self, event): if idx==-1: return self.app.alert('no model selected!') para = {'name':'New Model'} if not self.app.show_para('name', para, [(str, 'name', 'model', 'name')]): return - oldname = osp.join(root_dir, 'data/ilastik/%s'%self.lst_model.GetStringSelection()) - os.rename(oldname, osp.join(root_dir, 'data/ilastik/%s.fcl'%para['name'])) + oldname = osp.join(root_dir, 'plugins/ilastik/%s'%self.lst_model.GetStringSelection()) + os.rename(oldname, osp.join(root_dir, 'plugins/ilastik/%s.fcl'%para['name'])) self.LoadModel() def on_remove(self, event): idx = self.lst_model.GetSelection() if idx==-1: return self.app.alert('no model selected!') - os.remove(osp.join(root_dir, 'data/ilastik/%s'%self.lst_model.GetStringSelection())) + os.remove(osp.join(root_dir, 'plugins/ilastik/%s'%self.lst_model.GetStringSelection())) self.LoadModel() def on_run(self, event): diff --git a/sciapp/app.py b/sciapp/app.py index ad974f2e..3cd77b9c 100644 --- a/sciapp/app.py +++ b/sciapp/app.py @@ -6,11 +6,11 @@ def __init__(self, asyn=True): self.asyn = asyn self.managers = {} self.img_manager = self.manager('img') - #self.wimg_manager = self.manager('wimg') + self.wimg_manager = self.manager('wimg') self.tab_manager = self.manager('tab') - #self.wtab_manager = self.manager('wtab') + self.wtab_manager = self.manager('wtab') self.mesh_manager = self.manager('mesh') - #self.wmesh_manager = self.manager('wmesh') + self.wmesh_manager = self.manager('wmesh') self.task_manager = self.manager('task') self.plugin_manager = self.manager('plugin') From 3f67c9b5d6da401b3f2e76438300a343194c95f2 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 20 Mar 2021 14:45:02 +0800 Subject: [PATCH 331/343] nothing --- .../menus/Plugins/Contribute/Contributions/CellPose Planer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imagepy/menus/Plugins/Contribute/Contributions/CellPose Planer.md b/imagepy/menus/Plugins/Contribute/Contributions/CellPose Planer.md index 0b7b768f..370538c0 100644 --- a/imagepy/menus/Plugins/Contribute/Contributions/CellPose Planer.md +++ b/imagepy/menus/Plugins/Contribute/Contributions/CellPose Planer.md @@ -1,5 +1,5 @@ # cellpose-planer -**Path:** https://github.com/Image-Py/cellpose-planer +**Path:** https://gitee.com/imagepy/cellpose-planer **Version:** 0.1 From 993fbed856f285047a489013ea6ce8bde558e330 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sat, 20 Mar 2021 14:48:18 +0800 Subject: [PATCH 332/343] add dulwich depend --- environment.yml | 1 + requirements.txt | 2 +- setup.py | 5 +++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/environment.yml b/environment.yml index 09bc18b0..bec63780 100644 --- a/environment.yml +++ b/environment.yml @@ -19,4 +19,5 @@ dependencies: - markdown - python-markdown-math - moderngl + - dulwich - pystackreg diff --git a/requirements.txt b/requirements.txt index b385485d..1be86b5a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,6 @@ numpy-stl openpyxl pandas pydicom - pypubsub read-roi scikit-image @@ -15,3 +14,4 @@ xlwt markdown python-markdown-math moderngl +dulwich diff --git a/setup.py b/setup.py index d61b9ae1..869c7955 100644 --- a/setup.py +++ b/setup.py @@ -44,6 +44,7 @@ def get_data_files(): 'openpyxl', 'markdown', 'python-markdown-math', - 'numba' + 'numba', + 'dulwich' ], - ) \ No newline at end of file + ) From f653d630b11cc364e5f699f795488ec74683e643 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 21 Mar 2021 04:10:53 +0800 Subject: [PATCH 333/343] merge and review --- .../Region Analysis/regionprops_plgs.py | 7 ++-- imagepy/menus/Analysis/statistic_plg.py | 38 ++++--------------- imagepy/menus/File/new_plg.py | 2 +- .../menus/Image/Color/splitandmerge_plgs.py | 2 +- imagepy/menus/Window/windowskiller_plg.py | 2 +- sciapp/app.py | 8 ++-- sciwx/canvas/mcanvas.py | 3 +- sciwx/widgets/paradialog.py | 4 +- sciwx/widgets/ribbonbar.py | 27 ++++--------- 9 files changed, 29 insertions(+), 64 deletions(-) diff --git a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py index 995115d4..a133c80c 100644 --- a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py @@ -16,7 +16,7 @@ class RegionCounter(Simple): title = 'Geometry Analysis' note = ['8-bit', '16-bit', 'int'] para = {'con':'8-connect', 'center':True, 'area':True, 'l':True, 'extent':False, 'cov':False, 'slice':False, - 'ed':False, 'holes':False, 'ca':False, 'fa':False, 'solid':False} + 'ed':False, 'holes':False, 'ca':False, 'fa':False, 'solid':False, 'labeled':False} view = [(list, 'con', ['4-connect', '8-connect'], str, 'conection', 'pix'), (bool, 'slice', 'slice'), ('lab', None, '========= indecate ========='), @@ -29,7 +29,8 @@ class RegionCounter(Simple): (bool, 'holes', 'holes'), (bool, 'fa', 'filled area'), (bool, 'solid', 'solidity'), - (bool, 'cov', 'cov')] + (bool, 'cov', 'cov'), + (bool, 'labeled', 'has been labeled')] #process def run(self, ips, imgs, para = None): @@ -51,7 +52,7 @@ def run(self, ips, imgs, para = None): data, mark = [], {'type':'layers', 'body':{}} strc = generate_binary_structure(2, 1 if para['con']=='4-connect' else 2) for i in range(len(imgs)): - label(imgs[i], strc, output=buf) + np.copyto(imgs[i], buf) if para['labeled'] else label(imgs[i], strc, output=buf) ls = regionprops(buf) dt = [[i]*len(ls), list(range(len(ls)))] diff --git a/imagepy/menus/Analysis/statistic_plg.py b/imagepy/menus/Analysis/statistic_plg.py index 0cc1d79c..5f1d63fb 100644 --- a/imagepy/menus/Analysis/statistic_plg.py +++ b/imagepy/menus/Analysis/statistic_plg.py @@ -68,16 +68,15 @@ def show_hist(parent, title, hist): class Histogram(Simple): title = 'Histogram' - note = ['8-bit', '16-bit', 'rgb'] + note = ['all'] def run(self, ips, imgs, para = None): msk = ips.mask('in') + rg = np.linspace(*ips.range, 257) + img = ips.img if msk is None else ips.img[msk] if ips.channels == 3: - img = ips.img if msk is None else ips.img[msk] - hist = [np.histogram(img.ravel()[i::3], np.arange(257))[0] for i in (0,1,2)] - else: - img = ips.lookup() if msk is None else ips.lookup()[msk] - hist = np.histogram(img, np.arange(257))[0] + hist = [np.histogram(img.ravel()[i::3], rg)[0] for i in (0,1,2)] + else: hist = np.histogram(img,rg)[0] show_hist(self.app, ips.title+'-Histogram', hist) class Frequence(Simple): @@ -94,11 +93,9 @@ def run(self, ips, imgs, para = None): msk = ips.mask('in') for i in range(len(imgs)): img = imgs[i] if msk is None else imgs[i][msk] - maxv = img.max() - if maxv==0:continue - ct = np.histogram(img, maxv+1, [0,maxv])[0] + ct = np.bincount(img.ravel()) #np.histogram(img, maxv+1, [0,maxv])[0] titles = ['slice','value','count'] - dt = [[i]*len(ct), list(range(maxv+1)), ct] + dt = [np.ones(len(ct), dtype=np.uint32)+i, np.arange(len(ct)), ct] if not para['slice']: titles, dt = titles[1:], dt[1:] if self.para['fre']: @@ -144,27 +141,6 @@ def run(self, ips, imgs, para = None): data.append(self.count(img, para)) self.progress(n, len(imgs)) self.app.show_table(pd.DataFrame(data, columns=titles), ips.title+'-statistic') - -class Mark: - def __init__(self, data): - self.data = data - - def draw(self, dc, f, **key): - dc.SetPen(wx.Pen((255,255,0), width=1, style=wx.SOLID)) - dc.SetTextForeground((255,255,0)) - font = wx.Font(8, wx.FONTFAMILY_DEFAULT, - wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) - - dc.SetFont(font) - data = self.data[0 if len(self.data)==1 else key['cur']] - - for i in range(len(data)): - pos = f(*(data[i][0], data[i][1])) - dc.SetBrush(wx.Brush((255,255,255))) - dc.DrawCircle(pos[0], pos[1], 2) - dc.SetBrush(wx.Brush((0,0,0), wx.BRUSHSTYLE_TRANSPARENT)) - dc.DrawCircle(pos[0], pos[1], data[i][2]*key['k']) - dc.DrawText('id={}, r={}'.format(i, data[i][2]), pos[0], pos[1]) class PointsValue(Simple): title = 'Points Value' diff --git a/imagepy/menus/File/new_plg.py b/imagepy/menus/File/new_plg.py index bd543927..50c8b99d 100644 --- a/imagepy/menus/File/new_plg.py +++ b/imagepy/menus/File/new_plg.py @@ -8,7 +8,7 @@ import numpy as np class Plugin(Free): - title = 'New' + title = 'New Image' para = {'name':'Undefined','width':300, 'height':300, 'type':'8-bit','slice':1} view = [(str, 'name', 'name', ''), (int, 'width', (1,10240), 0, 'width', 'pix'), diff --git a/imagepy/menus/Image/Color/splitandmerge_plgs.py b/imagepy/menus/Image/Color/splitandmerge_plgs.py index 2bfe3f36..772265c7 100644 --- a/imagepy/menus/Image/Color/splitandmerge_plgs.py +++ b/imagepy/menus/Image/Color/splitandmerge_plgs.py @@ -21,7 +21,7 @@ def run(self, ips, imgs, para = None): for c,ci in zip((r,g,b),(0,1,2)): if self.para['copy']:c.append(i[:,:,ci].copy()) else: c.append(i[:,:,ci]) - self.progress(i, n) + self.progress(n+1, len(imgs)) for im, tl in zip([r,g,b],['red','green','blue']): self.app.show_img(im, ips.title+'-'+tl) if self.para['destory']: diff --git a/imagepy/menus/Window/windowskiller_plg.py b/imagepy/menus/Window/windowskiller_plg.py index a58ea663..7b8824fc 100644 --- a/imagepy/menus/Window/windowskiller_plg.py +++ b/imagepy/menus/Window/windowskiller_plg.py @@ -16,7 +16,7 @@ class ImageKiller(Free): (bool, 'all', 'close all images')] def run(self, para = None): - self.app.close_image(None if para['all'] else para['img']) + self.app.close_img(None if para['all'] else para['img']) class TableKiller(Free): title = 'Kill Table' diff --git a/sciapp/app.py b/sciapp/app.py index 59d05dba..069df7ab 100644 --- a/sciapp/app.py +++ b/sciapp/app.py @@ -6,9 +6,9 @@ def __init__(self, asyn=True): self.asyn = asyn self.managers = {} self.img_manager = self.manager('img') - self.wimg_manager = self.manager('wimg') + #self.wimg_manager = self.manager('wimg') self.tab_manager = self.manager('tab') - self.wtab_manager = self.manager('wtab') + #self.wtab_manager = self.manager('wtab') self.mesh_manager = self.manager('mesh') self.wmesh_manager = self.manager('wmesh') self.task_manager = self.manager('task') @@ -40,8 +40,8 @@ def show_img(self, img, name): self.img_manager.add(img.name, img) print(img.info) - def add_img_win(self, win, name): - self.wimg_manager.add(name, win) + #def add_img_win(self, win, name): + # self.wimg_manager.add(name, win) def close_img(self, name): self.img_manager.remove(name) diff --git a/sciwx/canvas/mcanvas.py b/sciwx/canvas/mcanvas.py index bafc9be8..20a97027 100644 --- a/sciwx/canvas/mcanvas.py +++ b/sciwx/canvas/mcanvas.py @@ -258,8 +258,7 @@ def on_scroll(self, event): self.canvas.on_idle(event) def on_idle(self, event): - if self.image.img is None: - return + if self.image.img is None: return image, info = self.image, self.lab_info.GetLabel() imgs = image.slices, image.channels, image.cn, image.cur selfs = self.pages ,self.chans, self.cn, self.cur diff --git a/sciwx/widgets/paradialog.py b/sciwx/widgets/paradialog.py index de095a23..767db2da 100644 --- a/sciwx/widgets/paradialog.py +++ b/sciwx/widgets/paradialog.py @@ -52,8 +52,10 @@ def init_view(self, items, para, preview=False, modal=True, app=None): self.para, self.modal = para, modal for item in items: self.add_ctrl_(widgets[item[0]], item[1], item[2:], app=app) - if preview:self.add_ctrl_(Check, 'preview', ('preview',), app=app) + if preview: self.add_ctrl_(Check, 'preview', ('preview',), app=app) self.reset(para) + for p in self.ctrl_dic: + if p in para: para[p] = self.ctrl_dic[p].GetValue() self.add_confirm(modal) wx.Dialog.Bind(self, wx.EVT_WINDOW_DESTROY, self.OnDestroy) #wx.Dialog.Bind(self, wx.EVT_IDLE, lambda e: self.reset()) diff --git a/sciwx/widgets/ribbonbar.py b/sciwx/widgets/ribbonbar.py index a8e61471..386524e0 100644 --- a/sciwx/widgets/ribbonbar.py +++ b/sciwx/widgets/ribbonbar.py @@ -132,28 +132,15 @@ def start(self, app): def __call__(self): return self - # data = ('menu', [ - # ('File', None, [ - # ('Open CV', (255,0,0), P('O')), - # '-', - # ('Close', None, P('C'))]), - # ('Edit', None, [('Copy', None, P('C')), - # ('A', None, [('B', None, P('B')), - # ('C', None, P('C'))]), - # ('Paste', None, P('P'))])]) - data = ('menu', [ - ('File', [ - ('Open', P('O')), + ('File', None, [ + ('Open CV', (255,0,0), P('O')), '-', - ('Close', P('C'))]), - ('Edit', [ - ('Copy', P('C')), - ('A', [ - ('B', P('B')), - ('C', P('C'))]), - ('Paste', P('P'))])]) - + ('Close', None, P('C'))]), + ('Edit', None, [('Copy', None, P('C')), + ('A', None, [('B', None, P('B')), + ('C', None, P('C'))]), + ('Paste', P('P'))])]) app = wx.App() frame = wx.Frame(None) menubar = RibbonBar(frame) From 31733090aa203d1fe98672e91f10e3013bf2c9d3 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 21 Mar 2021 14:26:50 +0800 Subject: [PATCH 334/343] plugin installer --- .../Plugins/Contribute/Contribute Plugin.md | 0 .../Plugins/Contribute/Site Plugins List.md | 6 +++++ imagepy/menus/Plugins/Contribute/__init__.py | 2 +- .../menus/Plugins/Contribute/pmanager_wgt.py | 24 ++++++++++++------- .../menus/Plugins/Contribute/update_plg.py | 19 +++++++++++++++ imagepy/menus/Plugins/Manager/plgtree_wgt.py | 21 ++++++++-------- 6 files changed, 52 insertions(+), 20 deletions(-) delete mode 100644 imagepy/menus/Plugins/Contribute/Contribute Plugin.md create mode 100644 imagepy/menus/Plugins/Contribute/Site Plugins List.md create mode 100644 imagepy/menus/Plugins/Contribute/update_plg.py diff --git a/imagepy/menus/Plugins/Contribute/Contribute Plugin.md b/imagepy/menus/Plugins/Contribute/Contribute Plugin.md deleted file mode 100644 index e69de29b..00000000 diff --git a/imagepy/menus/Plugins/Contribute/Site Plugins List.md b/imagepy/menus/Plugins/Contribute/Site Plugins List.md new file mode 100644 index 00000000..dc1e24d2 --- /dev/null +++ b/imagepy/menus/Plugins/Contribute/Site Plugins List.md @@ -0,0 +1,6 @@ +# Site Plugins Catlog + +| name | version | author | mail | keyword | description | +| --- | --- | --- | --- | --- | --- | +| [cellpose-planer](https://gitee.com/imagepy/cellpose-planer) | 0.1 | yxdragon | yxdragon@imagepy.org | cellpose,segment | generalist algorithm for cell and nucleus segmentation | +| [Demo Plugin](https://github.com/Image-Py/demoplugin) | 0.1 | yxdragon | yxdragon@imagepy.org | demo,tutorial | This is a demo project to show How to write ImagePy plugin. Including the usage of all kinds of plugin, with document wrote in detail. Developers can take this project as example. \ No newline at end of file diff --git a/imagepy/menus/Plugins/Contribute/__init__.py b/imagepy/menus/Plugins/Contribute/__init__.py index 7d7f6c72..e66d945b 100644 --- a/imagepy/menus/Plugins/Contribute/__init__.py +++ b/imagepy/menus/Plugins/Contribute/__init__.py @@ -1 +1 @@ -catlog = ['Contribute Document', 'Contribute Plugin', '-', 'pmanager_wgt', 'Contributions'] \ No newline at end of file +catlog = ['Contribute Document', 'Site Plugins List', 'update_plg', '-', 'pmanager_wgt', 'Contributions'] \ No newline at end of file diff --git a/imagepy/menus/Plugins/Contribute/pmanager_wgt.py b/imagepy/menus/Plugins/Contribute/pmanager_wgt.py index 38af18a2..5955bad5 100644 --- a/imagepy/menus/Plugins/Contribute/pmanager_wgt.py +++ b/imagepy/menus/Plugins/Contribute/pmanager_wgt.py @@ -7,6 +7,7 @@ import wx, os, glob, shutil, random from imagepy import root_dir from sciwx.text import MDPad +from sciapp.action import Macros #from imagepy.ui.mkdownwindow import HtmlPanel, md2html class VirtualListCtrl(wx.ListCtrl): @@ -36,15 +37,16 @@ def parse(path): f = open(path, encoding='utf-8') body = {'file':path} try: - for i in range(13): + line = f.readline() + if line[0] == '#':body['name'] = line.split('#')[-1].strip() + while line: line = f.readline() - if line[0] == '#':body['name'] = line.split('#')[-1].strip() - if 'Path:' in line: body['path'] = line.split('**')[-1].strip() - if 'Version:' in line: body['version'] = line.split('**')[-1].strip() - if 'Author:' in line: body['author'] = line.split('**')[-1].strip() - if 'Email:' in line: body['email'] = line.split('**')[-1].strip() - if 'Keyword:' in line: body['keyword'] = line.split('**')[-1].strip() - if 'Description' in line: body['Description'] = line.split('**')[-1].strip() + if line.startswith('**Path:'): body['path'] = line.split('**')[-1].strip() + if line.startswith('**Version:'): body['version'] = line.split('**')[-1].strip() + if line.startswith('**Author:'): body['author'] = line.split('**')[-1].strip() + if line.startswith('**Email:'): body['email'] = line.split('**')[-1].strip() + if line.startswith('**Keyword:'): body['keyword'] = line.split('**')[-1].strip() + if line.startswith('**Description'): body['Description'] = line.split('**')[-1].strip() f.close() except: body = [0] finally: f.close() @@ -67,6 +69,8 @@ def __init__( self, parent, app=None): self.txt_search = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) bSizer2.Add( self.txt_search, 1, wx.ALL, 5 ) + self.btn_update = wx.Button( self, wx.ID_ANY, 'Refresh List Online', wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) + bSizer2.Add( self.btn_update, 0, wx.ALL, 5 ) bSizer3 = wx.BoxSizer( wx.HORIZONTAL ) self.btn_install = wx.Button( self, wx.ID_ANY, 'Install/Update', wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) @@ -95,6 +99,7 @@ def __init__( self, parent, app=None): # Connect Events self.txt_search.Bind( wx.EVT_TEXT, self.on_search) self.lst_plgs.Bind( wx.EVT_LIST_ITEM_SELECTED, self.on_run) + self.btn_update.Bind(wx.EVT_BUTTON, self.on_update) self.btn_install.Bind(wx.EVT_BUTTON, self.on_install) self.btn_uninstall.Bind(wx.EVT_BUTTON, self.on_remove) self.chk_has.Bind( wx.EVT_CHECKBOX, self.on_check) @@ -131,6 +136,9 @@ def on_search( self, event ): self.lst_plgs.set_data(self.buf) self.lst_plgs.Refresh() + def on_update(self, event): + Macros('', ['Update Plugins List>None']).start(self.app, callafter=self.load) + def on_run(self, event): f = open(self.buf[event.GetIndex()][-1]['file'], encoding='utf-8') cont = f.read() diff --git a/imagepy/menus/Plugins/Contribute/update_plg.py b/imagepy/menus/Plugins/Contribute/update_plg.py new file mode 100644 index 00000000..950b31e7 --- /dev/null +++ b/imagepy/menus/Plugins/Contribute/update_plg.py @@ -0,0 +1,19 @@ +from sciapp.action import Free +import sys, requests, re, os.path as osp +from urllib.request import urlretrieve + +class Plugin(Free): + title = 'Update Plugins List' + + def run(self, para = None): + try: + here = osp.abspath(osp.dirname(__file__)) + url = 'https://gitee.com/mirrors/imagepy/tree/master/imagepy/menus/Plugins/Contribute/Contributions' + temp = re.compile('mirrors/imagepy/blob/master/imagepy/menus/Plugins/Contribute/Contributions/.*?md') + + rst = requests.get(url).text + records = ['https://gitee.com/'+i.replace('blob', 'raw') for i in temp.findall(rst)] + for i in records: urlretrieve(i, osp.join(here, 'Contributions', osp.split(i)[-1].replace('%20',' '))) + self.app.alert('site plugins list updated!') + except Exception as e: + self.app.alert('update failed!\tErrof:%s'%sys.exc_info()[1]) \ No newline at end of file diff --git a/imagepy/menus/Plugins/Manager/plgtree_wgt.py b/imagepy/menus/Plugins/Manager/plgtree_wgt.py index a632d681..0b0ece09 100644 --- a/imagepy/menus/Plugins/Manager/plgtree_wgt.py +++ b/imagepy/menus/Plugins/Manager/plgtree_wgt.py @@ -8,7 +8,7 @@ from sciapp.action import Free import wx,os from imagepy import root_dir -from imagepy.app import loader, ConfigManager +from imagepy.app import loader, ConfigManager, DocumentManager from wx.py.editor import EditorFrame from sciwx.text import MDPad from glob import glob @@ -74,22 +74,21 @@ def addnode(self, parent, data): self.tre_plugins.SetItemData(item, i) def load(self): - datas = loader.build_plugins('menus') + data = loader.build_plugins(root_dir+'/menus') + extends = glob(root_dir+'/plugins/*/menus') keydata = {} - for i in datas[1]: - if isinstance(i, tuple): keydata[i[0].__name__.split('.')[-1]] = i[1] - #print(keydata) - extends = glob('plugins/*/menus') + for i in data[1]: + if isinstance(i, tuple): keydata[i[0].title] = i[1] for i in extends: plgs = loader.build_plugins(i) + data[2].extend(plgs[2]) for j in plgs[1]: if not isinstance(j, tuple): continue - name = j[0].__name__.split('.')[-1] - if name in keydata: - keydata[name].extend(j[1]) - else: datas[1].append(j) + name = j[0].title + if name in keydata: keydata[name].extend(j[1]) + else: data[1].append(j) root = self.tre_plugins.AddRoot('Plugins') - self.addnode(root, datas[1]) + self.addnode(root, data[1]) # Virtual event handlers, overide them in your derived class def on_run( self, event ): From 5d2cb74441150bf7d345ce63b4fc9783366fffd7 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 21 Mar 2021 14:37:10 +0800 Subject: [PATCH 335/343] nothing --- imagepy/menus/Plugins/Contribute/update_plg.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/imagepy/menus/Plugins/Contribute/update_plg.py b/imagepy/menus/Plugins/Contribute/update_plg.py index 950b31e7..6133415c 100644 --- a/imagepy/menus/Plugins/Contribute/update_plg.py +++ b/imagepy/menus/Plugins/Contribute/update_plg.py @@ -1,6 +1,6 @@ from sciapp.action import Free -import sys, requests, re, os.path as osp -from urllib.request import urlretrieve +import sys, re, os.path as osp +from urllib.request import urlretrieve, urlopen class Plugin(Free): title = 'Update Plugins List' @@ -10,8 +10,7 @@ def run(self, para = None): here = osp.abspath(osp.dirname(__file__)) url = 'https://gitee.com/mirrors/imagepy/tree/master/imagepy/menus/Plugins/Contribute/Contributions' temp = re.compile('mirrors/imagepy/blob/master/imagepy/menus/Plugins/Contribute/Contributions/.*?md') - - rst = requests.get(url).text + rst = urlopen(url).read().decode('utf-8') records = ['https://gitee.com/'+i.replace('blob', 'raw') for i in temp.findall(rst)] for i in records: urlretrieve(i, osp.join(here, 'Contributions', osp.split(i)[-1].replace('%20',' '))) self.app.alert('site plugins list updated!') From 9f762100ddbe760c9135603d964120ee20f0c4b2 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 22 Mar 2021 01:05:55 +0800 Subject: [PATCH 336/343] nothing --- .../menus/Plugins/Contribute/Contributions/Demo Repo.md | 2 +- imagepy/menus/Plugins/Contribute/Contributions/IBook.md | 4 ++-- imagepy/menus/Plugins/Contribute/Contributions/OpenCV.md | 2 +- .../menus/Plugins/Contribute/Contributions/SimpleITK.md | 2 +- imagepy/menus/Plugins/Contribute/Site Plugins List.md | 7 +++++-- imagepy/menus/Plugins/Contribute/update_plg.py | 7 ++++--- imagepy/menus/Plugins/update_plg.py | 2 +- 7 files changed, 15 insertions(+), 11 deletions(-) diff --git a/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md b/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md index 2050d0da..d49a55c7 100644 --- a/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md +++ b/imagepy/menus/Plugins/Contribute/Contributions/Demo Repo.md @@ -1,6 +1,6 @@ # Demo Plugin -**Path:** https://github.com/Image-Py/demoplugin +**Path:** https://gitee.com/imagepy/demoplugin **Version:** 0.1 diff --git a/imagepy/menus/Plugins/Contribute/Contributions/IBook.md b/imagepy/menus/Plugins/Contribute/Contributions/IBook.md index e217be69..62e9be80 100644 --- a/imagepy/menus/Plugins/Contribute/Contributions/IBook.md +++ b/imagepy/menus/Plugins/Contribute/Contributions/IBook.md @@ -1,6 +1,6 @@ # IBook -**Path:** https://github.com/Image-Py/IBook +**Path:** https://gitee.com/imagepy/IBook **Version:** 0.1 @@ -10,7 +10,7 @@ **Keyword:** book, tutorial -**Description:** ImagePy's plugins to show some image processing method, which is friendly to beginner +**Description:** ImagePy's plugins to show some image processing method, which is friendly to beginner. you must fill the information upon, and you can not remove or insert line, you can write free below. diff --git a/imagepy/menus/Plugins/Contribute/Contributions/OpenCV.md b/imagepy/menus/Plugins/Contribute/Contributions/OpenCV.md index 9e6b6f95..5d738cd9 100644 --- a/imagepy/menus/Plugins/Contribute/Contributions/OpenCV.md +++ b/imagepy/menus/Plugins/Contribute/Contributions/OpenCV.md @@ -1,6 +1,6 @@ # opencv-plgs -**Path:** https://github.com/Image-Py/opencv-plgs +**Path:** https://gitee.com/imagepy/opencv-plgs **Version:** 0.1 diff --git a/imagepy/menus/Plugins/Contribute/Contributions/SimpleITK.md b/imagepy/menus/Plugins/Contribute/Contributions/SimpleITK.md index 0f4928f7..f5b4ab4b 100644 --- a/imagepy/menus/Plugins/Contribute/Contributions/SimpleITK.md +++ b/imagepy/menus/Plugins/Contribute/Contributions/SimpleITK.md @@ -1,6 +1,6 @@ # itk-plgs -**Path:** https://github.com/Image-Py/itk-plgs +**Path:** https://gitee.com/imagepy/itk-plgs **Version:** 0.1 diff --git a/imagepy/menus/Plugins/Contribute/Site Plugins List.md b/imagepy/menus/Plugins/Contribute/Site Plugins List.md index dc1e24d2..817d547a 100644 --- a/imagepy/menus/Plugins/Contribute/Site Plugins List.md +++ b/imagepy/menus/Plugins/Contribute/Site Plugins List.md @@ -2,5 +2,8 @@ | name | version | author | mail | keyword | description | | --- | --- | --- | --- | --- | --- | -| [cellpose-planer](https://gitee.com/imagepy/cellpose-planer) | 0.1 | yxdragon | yxdragon@imagepy.org | cellpose,segment | generalist algorithm for cell and nucleus segmentation | -| [Demo Plugin](https://github.com/Image-Py/demoplugin) | 0.1 | yxdragon | yxdragon@imagepy.org | demo,tutorial | This is a demo project to show How to write ImagePy plugin. Including the usage of all kinds of plugin, with document wrote in detail. Developers can take this project as example. \ No newline at end of file +| [IBook](https://github.com/Image-Py/IBook) | 0.1 | yxdragon | yxdragon@imagepy.org | book, tutorial | ImagePy's plugins to show some image processing method, which is friendly to beginner. | +| [cellpose-planer](https://github.com/Image-Py/cellpose-planer) | 0.1 | yxdragon | yxdragon@imagepy.org | cellpose, segment | generalist algorithm for cell and nucleus segmentation | +| [Demo Plugin](https://github.com/Image-Py/demoplugin) | 0.1 | yxdragon | yxdragon@imagepy.org | demo, tutorial | This is a demo project to show How to write ImagePy plugin. Including the usage of all kinds of plugin, with document wrote in detail. Developers can take this project as example. | +| [OpenCV](https://github.com/Image-Py/opencv-plgs) | 0.1 | yxdragon | yxdragon@imagepy.org | opencv | OpenCV plugin set for ImagePy | +| [SimpleITK](https://github.com/Image-Py/itk-plgs) | 0.1 | yxdragon | yxdragon@imagepy.org | itk, segment | SimpleITK plugin set for ImagePy | \ No newline at end of file diff --git a/imagepy/menus/Plugins/Contribute/update_plg.py b/imagepy/menus/Plugins/Contribute/update_plg.py index 6133415c..81275d87 100644 --- a/imagepy/menus/Plugins/Contribute/update_plg.py +++ b/imagepy/menus/Plugins/Contribute/update_plg.py @@ -8,11 +8,12 @@ class Plugin(Free): def run(self, para = None): try: here = osp.abspath(osp.dirname(__file__)) - url = 'https://gitee.com/mirrors/imagepy/tree/master/imagepy/menus/Plugins/Contribute/Contributions' - temp = re.compile('mirrors/imagepy/blob/master/imagepy/menus/Plugins/Contribute/Contributions/.*?md') - rst = urlopen(url).read().decode('utf-8') + url = 'https://gitee.com/imagepy/imagepy/tree/master/imagepy/menus/Plugins/Contribute' + temp = re.compile('imagepy/imagepy/blob/master/imagepy/menus/Plugins/Contribute/Contributions/.*?md') + rst = urlopen(url+'/Contributions').read().decode('utf-8') records = ['https://gitee.com/'+i.replace('blob', 'raw') for i in temp.findall(rst)] for i in records: urlretrieve(i, osp.join(here, 'Contributions', osp.split(i)[-1].replace('%20',' '))) + urlretrieve(url.replace('tree', 'raw')+'/Site%20Plugins%20List.md', osp.join(here, 'Site Plugins List.md')) self.app.alert('site plugins list updated!') except Exception as e: self.app.alert('update failed!\tErrof:%s'%sys.exc_info()[1]) \ No newline at end of file diff --git a/imagepy/menus/Plugins/update_plg.py b/imagepy/menus/Plugins/update_plg.py index bbe4b349..3f783b55 100644 --- a/imagepy/menus/Plugins/update_plg.py +++ b/imagepy/menus/Plugins/update_plg.py @@ -9,7 +9,7 @@ class Update(Free): def run(self, para=None): self.app.info('update now, waiting...') - url = 'https://gitee.com/mirrors/imagepy' + url = 'https://gitee.com/imagepy/imagepy' path = osp.dirname(root_dir); rpath = osp.dirname(path) newpath = osp.join(rpath, 'imagepy_new') if osp.exists(newpath): shutil.rmtree(newpath) From c53905eb5074f6804f7777d5d4549fb6be76684f Mon Sep 17 00:00:00 2001 From: yxdragon Date: Tue, 6 Apr 2021 20:44:22 +0800 Subject: [PATCH 337/343] nothing --- .../Region Analysis/regionprops_plgs.py | 2 +- sciapp/action/__init__.py | 1 + sciapp/action/advanced/simple.py | 2 +- sciapp/action/plugin/generalio.py | 29 +++++++++- sciapp/action/plugin/img_tools.py | 55 +++++++++++++++++++ 5 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 sciapp/action/plugin/img_tools.py diff --git a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py index a133c80c..f5e25fa4 100644 --- a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py @@ -64,7 +64,7 @@ def run(self, ips, imgs, para = None): if para['cov']: ellips = [i.centroid[::-1] + (i.major_axis_length/2,i.minor_axis_length/2, i.orientation+np.pi/2) for i in ls] layer['body'].append({'type':'ellipses', 'body':ellips}) - print(i,i,i,i,i) + if len(ls)>0: mark['body'][i] = layer if para['center']: diff --git a/sciapp/action/__init__.py b/sciapp/action/__init__.py index bf720152..27cdf12b 100644 --- a/sciapp/action/__init__.py +++ b/sciapp/action/__init__.py @@ -4,4 +4,5 @@ from .plugin.mea_tools import * from .plugin.shp_tools import * from .plugin.roi_tools import * +from .plugin.img_tools import * from .advanced import Filter, Free, Simple, Table, Macros, Widget, dataio, Report \ No newline at end of file diff --git a/sciapp/action/advanced/simple.py b/sciapp/action/advanced/simple.py index e444399e..a4a7e0c0 100644 --- a/sciapp/action/advanced/simple.py +++ b/sciapp/action/advanced/simple.py @@ -42,7 +42,7 @@ def check(self, ips): elif ips.dtype==np.int32 and not 'int' in note: self.app.alert('Do not surport 32-bit int uint image') return False - elif ips.dtype in {np.float32, np.float64} and not 'float' in note: + elif (ips.dtype==np.float32 or ips.dtype==np.float64) and not 'float' in note: self.app.alert('Do not surport float image') return False if sum([i in note for i in ('stack','stack2d','stack3d')])>0: diff --git a/sciapp/action/plugin/generalio.py b/sciapp/action/plugin/generalio.py index b298bdf4..08b0d6ee 100644 --- a/sciapp/action/plugin/generalio.py +++ b/sciapp/action/plugin/generalio.py @@ -1,7 +1,7 @@ from ..advanced import dataio from skimage.io import imread, imsave -for i in ('bmp', 'jpg', 'tif', 'png', 'gif'): +for i in ('bmp', 'jpg', 'jpeg', 'tif', 'png', 'gif'): dataio.ReaderManager.add(i, imread, 'img') dataio.WriterManager.add(i, imsave, 'img') @@ -17,4 +17,29 @@ class SaveImage(dataio.ImageWriter): def load(self, ips): self.filt = [i for i in sorted(dataio.WriterManager.names())] - return True \ No newline at end of file + return True + +from pandas import read_csv, read_excel +read_csv2 = lambda p:read_csv(p, index_col=0) +read_excel2 = lambda p:read_excel(p, index_col=0) + +save_csv = lambda path, data:data.to_csv(path) +dataio.ReaderManager.add('csv', read_csv2, 'tab') +dataio.WriterManager.add('csv', save_csv, 'tab') + + +save_excel = lambda path, data:data.to_excel(path) +dataio.ReaderManager.add('xls', read_excel2, 'tab') +dataio.WriterManager.add('xls', save_excel, 'tab') +dataio.ReaderManager.add('xlsx', read_excel2, 'tab') +dataio.WriterManager.add('xlsx', save_excel, 'tab') + +class OpenTable(dataio.Reader): + title = 'Excel Open' + tag = 'tab' + filt = ['csv', 'xls','xlsx'] + +class SaveTable(dataio.TableWriter): + title = 'Excel Save' + tag = 'tab' + filt = ['csv', 'xls', 'xlsx'] \ No newline at end of file diff --git a/sciapp/action/plugin/img_tools.py b/sciapp/action/plugin/img_tools.py new file mode 100644 index 00000000..9858f6d3 --- /dev/null +++ b/sciapp/action/plugin/img_tools.py @@ -0,0 +1,55 @@ +from sciapp.action import ImageTool + +class MoveTool(ImageTool): + title = 'Move And Scale' + def __init__(self): + self.oldxy = None + + def mouse_down(self, obj, x, y, btn, **key): + if btn==1: self.oldxy = key['px'], key['py'] + if btn==3: key['canvas'].fit() + + def mouse_up(self, obj, x, y, btn, **key): + self.oldxy = None + + def mouse_move(self, obj, x, y, btn, **key): + if self.oldxy is None: return + ox, oy = self.oldxy + up = (1,-1)[key['canvas'].up] + key['canvas'].move(key['px']-ox, (key['py']-oy)*up) + self.oldxy = key['px'], key['py'] + + def mouse_wheel(self, obj, x, y, d, **key): + if d>0: key['canvas'].zoomout(x, y, coord='data') + if d<0: key['canvas'].zoomin(x, y, coord='data') + +class ScaleTool(ImageTool): + title = 'Scope' + def __init__(self): + self.ox, self.oy = 0, 0 + + def mouse_down(self, ips, x, y, btn, **key): + if btn==2: + self.ox, self.oy = key['canvas'].to_panel_coor(x,y) + print(self.ox, self.oy) + #print 'down', self.ox, self.oy + if btn==1: key['canvas'].zoomout(x, y, 'data') + if btn==3: key['canvas'].zoomin(x, y, 'data') + ips.update() + + def mouse_up(self, ips, x, y, btn, **key): + pass + + def mouse_move(self, ips, x, y, btn, **key): + if btn==2: + x,y = key['canvas'].to_panel_coor(x,y) + #print 'x,y',x,y + #print 'dx,dy:', x-self.ox, y-self.oy + key['canvas'].move(x-self.ox, y-self.oy) + ips.update() + self.ox, self.oy = x,y + + def mouse_wheel(self, ips, x, y, d, **key): + if d>0:key['canvas'].zoomout(x, y, 'data') + if d<0:key['canvas'].zoomin(x, y, 'data') + ips.update() \ No newline at end of file From 95cd7631588aed24d70a32e657a8bedcbe87064f Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 9 Apr 2021 15:43:44 +0800 Subject: [PATCH 338/343] nothing --- sciapp/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sciapp/app.py b/sciapp/app.py index 069df7ab..4968f85e 100644 --- a/sciapp/app.py +++ b/sciapp/app.py @@ -151,7 +151,7 @@ def run_macros(self, cmd, callafter=None): def one(cmds, after): cmd = cmds.pop(0) if not isinstance(cmd, str): title, para = cmd - else: title, para = eval(cmd.replace('>', ',')) + else: title, para = eval('"'+cmd.replace('>', '",')) plg = self.plugin_manager.get(name=title)() after = lambda cmds=cmds: one(cmds, one) if len(cmds)==0: after = callafter From e584e02c6d701c719ef3e4ce3d532333204e55df Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 11 Jun 2021 11:56:36 +0800 Subject: [PATCH 339/343] vispy --- imagepy/app/imagepy.py | 49 ++- imagepy/data/config.json | 2 +- .../menus/Kit3D/Network 3D/toolkit3d_plgs.py | 22 +- .../menus/Kit3D/Viewer 3D/2DSurface Demo.mc | 4 +- imagepy/menus/Kit3D/Viewer 3D/colorpts_plg.py | 16 +- imagepy/menus/Kit3D/Viewer 3D/demo_plgs.py | 35 +- imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py | 48 +- .../menus/Kit3D/Viewer 3D/tablepoints_plg.py | 59 ++- sciapp/action/__init__.py | 3 +- sciapp/action/plugin/mesh_tools.py | 71 +++ sciapp/action/tolact.py | 29 +- sciapp/app.py | 30 +- sciapp/object/__init__.py | 2 +- sciapp/object/shape.py | 12 +- sciapp/object/surface.py | 231 +++++++--- sciapp/util/__init__.py | 1 + sciapp/util/meshutil.py | 304 +++++++++++++ sciwx/canvas/canvas.py | 2 +- sciwx/canvas/mark.py | 13 +- sciwx/demo/mesh1_demo.py | 31 +- sciwx/demo/mesh2_mesh_demo.py | 37 +- sciwx/demo/mesh3_geoutil.py | 133 +++--- sciwx/mesh/__init__.py | 2 +- sciwx/mesh/canvas.py | 411 ++++++++++-------- sciwx/mesh/matutil.py | 49 --- sciwx/mesh/mcanvas.py | 104 +++-- sciwx/mesh/scene.py | 225 ---------- sciwx/mesh/widget.py | 20 +- 28 files changed, 1182 insertions(+), 763 deletions(-) create mode 100644 sciapp/action/plugin/mesh_tools.py create mode 100644 sciapp/util/meshutil.py delete mode 100644 sciwx/mesh/matutil.py delete mode 100644 sciwx/mesh/scene.py diff --git a/imagepy/app/imagepy.py b/imagepy/app/imagepy.py index 042b77f3..54184751 100644 --- a/imagepy/app/imagepy.py +++ b/imagepy/app/imagepy.py @@ -10,7 +10,7 @@ from sciwx.plot import PlotFrame from skimage.data import camera from sciapp import App, Source -from sciapp.object import Image, Table +from sciapp.object import Image, Table, Scene, Mesh from imagepy import root_dir from .startup import load_plugins, load_tools, load_widgets, load_document, load_dictionary from .manager import ConfigManager, DictManager, ShortcutManager, DocumentManager @@ -191,7 +191,7 @@ def init_canvas(self): self.canvasnbwrap.Layout() self.auimgr.AddPane( self.canvasnbwrap, aui.AuiPaneInfo() .Center() .CaptionVisible( False ).PinButton( True ).Dock() .PaneBorder( False ).Resizable().FloatingSize( wx.DefaultSize ). BottomDockable( True ).TopDockable( False ) - .LeftDockable( True ).RightDockable( True ) ) + .LeftDockable( True ).RightDockable( True ). Caption('Image') ) self.canvasnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_active_img) self.canvasnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close_img) @@ -203,7 +203,7 @@ def init_table(self): self.tablenbwrap.SetSizer( sizer ) self.tablenbwrap.Layout() - self.auimgr.AddPane( self.tablenbwrap, aui.AuiPaneInfo() .Bottom() .CaptionVisible( True ).PinButton( True ).Dock().Hide() + self.auimgr.AddPane( self.tablenbwrap, aui.AuiPaneInfo() .Center() .CaptionVisible( True ).PinButton( True ).Dock() .MaximizeButton( True ).Resizable().FloatingSize((800, 600)).BestSize(( 120,120 )). Caption('Table') . BottomDockable( True ).TopDockable( False ).LeftDockable( True ).RightDockable( True ) ) self.tablenb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_active_table) @@ -218,9 +218,9 @@ def init_mesh(self): self.meshnbwrap.Layout() self.auimgr.AddPane( self.meshnbwrap, aui.AuiPaneInfo() .Bottom() .CaptionVisible( True ).PinButton( True ).Float().Hide() - .MaximizeButton( True ).Resizable().FloatingSize((800, 600)).BestSize(( 120,120 )). Caption('Meshes') . + .MaximizeButton( True ).Resizable().FloatingSize((800, 600)).BestSize(( 120,120 )). Caption('Mesh') . BottomDockable( True ).TopDockable( False ).LeftDockable( True ).RightDockable( True ) ) - self.meshnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_new_mesh) + self.meshnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_active_mesh) self.meshnb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_close_mesh) def add_task(self, task): @@ -287,14 +287,14 @@ def on_close_table(self, event): grid = event.GetEventObject().GetPage(event.GetSelection()) App.close_table(self, grid.table.title) - def on_new_mesh(self, event): - self.add_mesh(self.meshnb.canvas().mesh) - self.add_mesh_win(self.meshnb.canvas()) + def on_active_mesh(self, event): + self.active_mesh(self.meshnb.canvas().scene3d.name) + # self.add_mesh_win(self.meshnb.canvas()) def on_close_mesh(self, event): - canvas3d = event.GetEventObject().GetPage(event.GetSelection()) - self.remove_mesh(canvas3d.mesh) - self.remove_mesh_win(canvas3d) + # canvas3d = event.GetEventObject().GetPage(event.GetSelection()) + self.close_mesh(self.meshnb.canvas().scene3d.name) + # self.remove_mesh_win(canvas3d) def info(self, value): lang = ConfigManager.get('language') @@ -385,10 +385,27 @@ def _show_txt(self, cont, title='ImagePy'): def show_txt(self, cont, title='ImagePy'): wx.CallAfter(self._show_txt, cont, title) - def _show_mesh(self, mesh=None, title=None): + def _show_mesh(self, obj=None, title='Scene'): + # show a scence or create a new scene + if isinstance(obj, Scene) or obj is None: + canvas = self.meshnb.add_canvas(obj) + App.show_mesh(self, canvas.scene3d, title) + else: + if self.get_mesh() is None: + canvas = self.meshnb.add_canvas(None) + App.show_mesh(self, canvas.scene3d, 'Scene') + scene = self.get_mesh() + scene.add_obj(title, obj) + info = self.auimgr.GetPane(self.meshnbwrap) + info.Show(True) + self.auimgr.Update() + return + + if mesh is None: + scene = self.get_mesh() canvas = self.meshnb.add_canvas() - canvas.mesh.name = 'Surface' + canvas.scene3d.name = 'Surface' elif hasattr(mesh, 'vts'): canvas = self.get_mesh_win() if canvas is None: @@ -398,14 +415,14 @@ def _show_mesh(self, mesh=None, title=None): else: canvas = self.meshnb.add_canvas() canvas.set_mesh(mesh) - self.add_mesh(canvas.mesh) - self.add_mesh_win(canvas) + # self.add_mesh(canvas.mesh) + # self.add_mesh_win(canvas) info = self.auimgr.GetPane(self.meshnbwrap) info.Show(True) self.auimgr.Update() - def show_mesh(self, mesh=None, title=None): + def show_mesh(self, mesh=None, title='Scene'): wx.CallAfter(self._show_mesh, mesh, title) def show_widget(self, panel, title='Widgets'): diff --git a/imagepy/data/config.json b/imagepy/data/config.json index b7722ba7..fe7b9a33 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["language", "English", null], ["roi_style", {"color": [255, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 255, 0], "size": 8}, null], ["recent", ["C:/Users/54631/Desktop/\u6d77\u51b0\u62a5\u4ef7/testmacros.mc", "C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc"], null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["uistyle", "imagepy", null]] \ No newline at end of file +[["uistyle", "imagepy", null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["recent", ["C:/Users/54631/Desktop/\u6d77\u51b0\u62a5\u4ef7/testmacros.mc", "C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc"], null], ["roi_style", {"color": [255, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 255, 0], "size": 8}, null], ["language", "English", null]] \ No newline at end of file diff --git a/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py b/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py index 1e082127..b722ff14 100644 --- a/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py +++ b/imagepy/menus/Kit3D/Network 3D/toolkit3d_plgs.py @@ -5,8 +5,8 @@ import networkx as nx import numpy as np import pandas as pd -from sciapp.object import Surface, MarkText -from sciapp.util import surfutil +from sciapp.object import Mesh, TextSet +from sciapp.util import meshutil norm = np.linalg.norm class Skeleton3D(Simple): @@ -57,19 +57,19 @@ def run(self, ips, imgs, para = None): rs = [para['r']] * len(balls) cs = tuple(np.array(para['ncolor'])/255.0) - vts, fs, ns, cs = surfutil.build_balls(balls, rs, cs) - self.app.show_mesh(Surface(vts, fs, ns, cs), 'balls') - - vts, fs, pos, h, color = surfutil.build_marks(['ID:%s'%i for i in ids], balls, para['r'], para['r'], (1,1,1)) - self.app.show_mesh(MarkText(vts, fs, pos, h, color), 'txt') + vts, fs, cs = meshutil.create_balls(balls, rs, cs) + self.app.show_mesh(Mesh(verts=vts, faces=fs, colors=cs), 'balls') cs = tuple(np.array(para['lcolor'])/255.0) - vts, fs, ns, cs = surfutil.build_lines(xs, ys, zs, cs) - self.app.show_mesh(Surface(vts, fs, ns, cs, mode='grid'), 'path') + vts, fs, cs = meshutil.create_lines(xs, ys, zs, cs) + self.app.show_mesh(Mesh(verts=vts, faces=fs, colors=cs, mode='grid'), 'path') cs = tuple(np.array(para['pcolor'])/255.0) - vts, fs, ns, cs = surfutil.build_lines(lxs, lys, lzs, cs) - self.app.show_mesh(Surface(vts, fs, ns, cs, mode='grid'), 'lines') + vts, fs, cs = meshutil.create_lines(lxs, lys, lzs, cs) + self.app.show_mesh(Mesh(verts=vts, faces=fs, colors=cs, mode='grid'), 'lines') + + self.app.show_mesh(TextSet(['ID:%s'%i for i in ids], verts=balls, size=para['r']*256, colors=(1,1,1)), 'txt') + class Show3DGraphR(Simple): diff --git a/imagepy/menus/Kit3D/Viewer 3D/2DSurface Demo.mc b/imagepy/menus/Kit3D/Viewer 3D/2DSurface Demo.mc index e174005c..eb263ab3 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/2DSurface Demo.mc +++ b/imagepy/menus/Kit3D/Viewer 3D/2DSurface Demo.mc @@ -1,2 +1,2 @@ -Open Url>{'url': 'http://data.imagepy.org/testdata/dem.jpg'} -2D Surface>{'name': 'dem', 'scale': 1, 'sigma': 1, 'h': 0.3} \ No newline at end of file +moon>None +2D Surface>{'name': 'moon', 'sample': 2, 'sigma': 0.7, 'h': 0.3, 'cm': 'Green_Fire_Blue'} \ No newline at end of file diff --git a/imagepy/menus/Kit3D/Viewer 3D/colorpts_plg.py b/imagepy/menus/Kit3D/Viewer 3D/colorpts_plg.py index f2cdb69b..3fdcbedd 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/colorpts_plg.py +++ b/imagepy/menus/Kit3D/Viewer 3D/colorpts_plg.py @@ -1,6 +1,6 @@ from sciapp.action import Simple -from sciapp.object import Surface, MarkText -from sciapp.util import surfutil +from sciapp.object import Mesh +from sciapp.util import meshutil import numpy as np class Plugin(Simple): @@ -8,7 +8,7 @@ class Plugin(Simple): note = ['rgb'] para = {'name':'undifine', 'num':100, 'r':1} view = [(str, 'name', 'Name', ''), - (int, 'num', (10,1024), 0, 'number', 'points'), + (int, 'num', (10,10240), 0, 'number', 'points'), (float, 'r', (0.1,30), 1, 'radius', '')] def run(self, ips, imgs, para = None): @@ -16,15 +16,15 @@ def run(self, ips, imgs, para = None): if ips.roi != None: pts = ips.img[ips.get_msk()] else: pts = ips.img.reshape((-1,3)) pts = pts[::len(pts)//num] - vts, fs, ns, cs = surfutil.build_balls(pts, np.ones(len(pts))*r, pts/255) - self.app.show_mesh(Surface(vts, fs, ns, cs), para['name']) + vts, fs, cs = meshutil.create_balls(pts, np.ones(len(pts))*r, pts/255) + self.app.show_mesh(Mesh(pts, colors=pts/255), para['name']) (r1,g1,b1),(r2,g2,b2) = (0,0,0),(1,1,1) rs = (r1,r2,r2,r1,r1,r1,r1,r1,r1,r2,r2,r1,r2,r2,r2,r2) gs = (g1,g1,g1,g1,g1,g2,g2,g1,g2,g2,g2,g2,g2,g1,g1,g2) bs = (b1,b1,b2,b2,b1,b1,b2,b2,b2,b2,b1,b1,b1,b1,b2,b2) - vts, fs, ns, cs = surfutil.build_cube((0,0,0),(255,255,255)) - cs = list(zip(rs,gs,bs)) - self.app.show_mesh(Surface(vts, fs, ns, cs, mode='grid'), 'cube') + vts, fs, ls = meshutil.create_cube((0,0,0),(255,255,255)) + cs = np.array(list(zip(rs,gs,bs))) + self.app.show_mesh(Mesh(vts, ls, colors=vts/255, mode='grid'), 'cube') if __name__ == '__main__': pass \ No newline at end of file diff --git a/imagepy/menus/Kit3D/Viewer 3D/demo_plgs.py b/imagepy/menus/Kit3D/Viewer 3D/demo_plgs.py index 65ffb778..43d24624 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/demo_plgs.py +++ b/imagepy/menus/Kit3D/Viewer 3D/demo_plgs.py @@ -1,6 +1,6 @@ from sciapp.action import Free -from sciapp.object import Surface, MarkText -from sciapp.util import surfutil +from sciapp.object import Mesh +from sciapp.util import meshutil import numpy as np class Decoration(Free): @@ -13,19 +13,15 @@ def run(self, para=None): r = np.sin(m0*phi)**m1 + np.cos(m2*phi)**m3 + np.sin(m4*theta)**m5 + np.cos(m6*theta)**m7 x = r*np.sin(phi)*np.cos(theta) y = r*np.cos(phi) - z = r*np.sin(phi)*np.sin(theta) - vts, fs, ns, cs = surfutil.build_mesh(x, y, z) - cs[:] = surfutil.auto_lookup(vts[:,2], surfutil.linear_color('jet'))/255 - self.app.show_mesh(Surface(vts, fs, ns, cs), 'decoration') + z = r*np.sin(phi)*np.sin(theta) + vts, fs = meshutil.create_grid_mesh(x, y, z) + mesh = Mesh(vts, fs.astype(np.uint32), vts[:,2], mode='grid', cmap='jet') + self.app.show_mesh(mesh, 'decoration') class Lines(Free): title = 'Lines Demo' def run(self, para=None): - vts = np.array([(0,0,0),(1,1,0),(2,1,0),(1,0,0)], dtype=np.float32) - fs = np.array([(0,1,2),(1,2,3)], dtype=np.uint32) - ns = np.ones((4,3), dtype=np.float32) - n_mer, n_long = 6, 11 pi = np.pi dphi = pi / 1000.0 @@ -35,20 +31,21 @@ def run(self, para=None): y = np.sin(mu) * (1 + np.cos(n_long * mu / n_mer) * 0.5) z = np.sin(n_long * mu / n_mer) * 0.5 - vts, fs, ns, cs = surfutil.build_line(x, y, z, (1, 0, 0)) - cs[:] = surfutil.auto_lookup(vts[:,2], surfutil.linear_color('jet'))/255 - self.app.show_mesh(Surface(vts, fs, ns, cs, mode='grid'), 'line') + vts = np.array([x, y, z]).T.astype(np.float32) + fs = np.arange(len(vts), dtype=np.uint32) + fs = np.array([fs[:-1], fs[1:]]).T + mesh = Mesh(vts, fs, vts[:,2], cmap='jet', mode='grid') + self.app.show_mesh(mesh, 'line') class Balls(Free): title = 'Random Balls Demo' def run(self, para=None): os = np.random.rand(30).reshape((-1,3)) - rs = np.random.rand(10)/5 - cs = (np.random.rand(10)*255).astype(np.uint8) - cs = surfutil.linear_color('jet')[cs]/255 - - vts, fs, ns, cs = surfutil.build_balls(os, rs, cs) - self.app.show_mesh(Surface(vts, fs, ns, cs), 'balls') + rs = np.random.rand(10)/7+0.05 + cs = np.random.rand(10) + vts_b, fs_b, cs_b = meshutil.create_balls(os, rs, cs) + mesh = Mesh(verts=vts_b, faces=fs_b, colors=cs_b, cmap='jet') + self.app.show_mesh(mesh, 'balls') plgs = [Lines, Balls, Decoration] \ No newline at end of file diff --git a/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py b/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py index 1ee73c63..2f64180b 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py +++ b/imagepy/menus/Kit3D/Viewer 3D/surface_plgs.py @@ -6,27 +6,34 @@ """ from sciapp.action import Simple, Filter, Free from scipy.ndimage.filters import gaussian_filter -from sciapp.object import Surface, MarkText -from sciapp.util import surfutil +from sciapp.object import Mesh, Scene, Surface2d, Surface3d, Volume3d +from imagepy.app import ColorManager +from sciapp.util import meshutil class Show(Free): title = 'Show Viewer 3D' + para = {'name':'Scene', 'bg':(0,0,0)} + view = [(str, 'name', 'name', ''), + ('color', 'bg', 'background', 'color')] + def run(self, para): - self.app.show_mesh() + scene = Scene(bg_color=[i/255 for i in para['bg']]) + self.app.show_mesh(scene, para['name']) class Surface2D(Simple): title = '2D Surface' note = ['8-bit', '16-bit', 'float'] - para = {'name':'undifine', 'scale':2, 'sigma':2,'h':1} + para = {'name':'undifine', 'sample':2, 'sigma':2,'h':0.3, 'cm':'gray'} view = [(str, 'name', 'Name', ''), - (int, 'scale', (1,5), 0, 'down scale', 'pix'), + (int, 'sample', (1,10), 0, 'down sample', 'pix'), (int, 'sigma', (0,30), 0, 'sigma', ''), - (float, 'h', (0.1,10), 1, 'scale z', '')] + (float, 'h', (0.1,10), 1, 'scale z', ''), + ('cmap', 'cm', 'color map')] def run(self, ips, imgs, para = None): - ds, sigma = para['scale'], para['sigma'] - vts, fs, ns, cs = surfutil.build_surf2d(ips.img, ds=ds, sigma=para['sigma'], k=para['h']) - self.app.show_mesh(Surface(vts, fs, ns, cs), para['name']) + ds, sigma, cm = para['sample'], para['sigma'], ColorManager.get(para['cm']) + mesh = Surface2d(ips.img, sample=ds, sigma=sigma, k=para['h'], cmap=cm) + self.app.show_mesh(mesh, para['name']) class Surface3D(Simple): modal = False @@ -54,8 +61,8 @@ def cancel(self, ips): def run(self, ips, imgs, para = None): ips.lut = self.buflut cs = tuple([int(i/255.0) for i in para['color']]) - vts, fs, ns, cs = surfutil.build_surf3d(ips.imgs, para['ds'], para['thr'], para['step'], cs) - self.app.show_mesh(Surface(vts, fs, ns, cs), para['name']) + surf3d = Surface3d(imgs=ips.imgs, level=para['thr'], sample=para['ds'], step=para['step'], colors=cs) + self.app.show_mesh(surf3d, para['name']) class ImageCube(Simple): modal = False @@ -76,4 +83,21 @@ def run(self, ips, imgs, para = None): vts, fs, ns, cs = surfutil.build_img_box(imgs, para['color']) self.app.show_mesh(Surface(vts, fs, ns, cs, mode='grid'), para['name']+'-box') -plgs = [Show, Surface2D, Surface3D, ImageCube] +class Volume3D(Simple): + modal = False + title = '3D Volume' + note = ['8-bit', 'stack3d'] + para = {'name':'undifine', 'step':1, 'cm':'gray', 'cube':True} + view = [(str, 'name', 'Name', ''), + (int, 'step', (1,10), 0, 'march step', 'pix'), + ('cmap', 'cm', 'color map'), + (bool, 'cube', 'draw outline cube')] + + def run(self, ips, imgs, para = None): + cmap = ColorManager.get(para['cm']) + self.app.show_mesh(Volume3d(imgs, step=para['step'], cmap=cmap), para['name']) + if para['cube']: + vts, fs = meshutil.create_bound((0,0,0), imgs.shape) + self.app.show_mesh(Mesh(verts=vts, faces=fs, colors=(1,1,1), mode='grid'), 'box') + +plgs = [Show, Surface2D, Surface3D, ImageCube, Volume3D] diff --git a/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py b/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py index 8efba4af..efe472ab 100644 --- a/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py +++ b/imagepy/menus/Kit3D/Viewer 3D/tablepoints_plg.py @@ -1,14 +1,18 @@ from sciapp.action import Table from imagepy.app import ColorManager +from sciapp.util import meshutil +from sciapp.object import Mesh import numpy as np +''' class Plugin(Table): title = 'Table Point Cloud' - para = {'x':None, 'y':None, 'z':None, 'r':5, 'rs':None, 'c':(0,0,255), + para = {'name':'undefined', 'x':None, 'y':None, 'z':None, 'r':5, 'rs':None, 'c':(0,0,255), 'cs':None, 'cm':None, 'cube':False} - view = [('field', 'x', 'x data', ''), + view = [(str, 'name', 'name', ''), + ('field', 'x', 'x data', ''), ('field', 'y', 'y data', ''), ('field', 'z', 'z data', ''), (float, 'r', (0, 1024), 3, 'radius', 'pix'), @@ -20,23 +24,50 @@ class Plugin(Table): ('cmap', 'cm', 'color map when color column is set'), (bool, 'cube', 'draw outline cube')] - def load(self, para): - self.frame = myvi.Frame3D.figure(IPy.curapp, title='3D Canvas') - return True def run(self, tps, snap, data, para = None): pts = np.array(data[[para['x'], para['y'], para['z']]]) - rs = data[para['rs']]*para['r'] if para['rs'] != 'None' else [para['r']]*len(pts) + rs = data[para['rs']]*para['r'] if para['rs'] != 'None' else np.ones(len(pts))*para['r'] cm = ColorManager.get(para['cm'])/255.0 clip = lambda x : (x-x.min())/(x.max()-x.min())*255 - if para['cs'] == 'None': cs = [np.array(para['c'])/255.0]*len(pts) - else: cs = cm[clip(data[para['cs']]).astype(np.uint8)] - vts, fs, ns, cs = myvi.build_balls(pts.astype(np.float32), list(rs), cs) - self.frame.viewer.add_surf_asyn('ball', vts, fs, ns, cs) + if para['cs'] == 'None': cs = tuple(np.array(para['c'])/255) + else: cs = data[para['cs']] + print(pts, rs, cs) + vts, fs, cs = meshutil.create_balls(pts, rs, cs) + mesh = Mesh(verts=vts, faces=fs, colors=cs, cmap=cm) + self.app.show_mesh(mesh, para['name']) if para['cube']: p1 = data[[para['x'], para['y'], para['z']]].min(axis=0) p2 = data[[para['x'], para['y'], para['z']]].max(axis=0) - vts, fs, ns, cs = myvi.build_cube(p1, p2) - self.frame.viewer.add_surf_asyn('cube', vts, fs, ns, cs, mode='grid') - self.frame.Raise() - self.frame = None \ No newline at end of file + vts, fs = meshutil.create_bound(p1, p2) + self.app.show_mesh(Mesh(verts=vts, faces=fs, colors=(1,1,1), mode='grid'), 'box') +''' + +class Plugin(Table): + title = 'Table Point Cloud' + + para = {'name':'undefined', 'x':None, 'y':None, 'z':None, 'ref':None, 'c':(0,0,255), 'cm':None, 'cube':False} + + view = [(str, 'name', 'name', ''), + ('field', 'x', 'x data', ''), + ('field', 'y', 'y data', ''), + ('field', 'z', 'z data', ''), + ('field', 'ref', 'reflectivity', 'column'), + ('cmap', 'cm', 'color map for reflectivity'), + ('color', 'c', 'color', 'when no ref'), + (bool, 'cube', 'draw outline cube')] + + + def run(self, tps, snap, data, para = None): + pts = np.array(data[[para['x'], para['y'], para['z']]]) + cm = ColorManager.get(para['cm'])/255.0 + clip = lambda x : (x-x.min())/(x.max()-x.min())*255 + if para['ref'] == 'None': cs = tuple(np.array(para['c'])/255) + else: cs = data[para['ref']] + mesh = Mesh(verts=pts, colors=cs, cmap=cm, mode='points') + self.app.show_mesh(mesh, para['name']) + if para['cube']: + p1 = data[[para['x'], para['y'], para['z']]].min(axis=0) + p2 = data[[para['x'], para['y'], para['z']]].max(axis=0) + vts, fs = meshutil.create_bound(p1, p2) + self.app.show_mesh(Mesh(verts=vts, faces=fs, colors=(1,1,1), mode='grid'), 'box') \ No newline at end of file diff --git a/sciapp/action/__init__.py b/sciapp/action/__init__.py index 27cdf12b..992eceb9 100644 --- a/sciapp/action/__init__.py +++ b/sciapp/action/__init__.py @@ -1,8 +1,9 @@ from .action import SciAction from .imgact import ImgAction -from .tolact import Tool, DefaultTool, ImageTool, ShapeTool, TableTool +from .tolact import Tool, DefaultTool, ImageTool, ShapeTool, TableTool, MeshTool from .plugin.mea_tools import * from .plugin.shp_tools import * from .plugin.roi_tools import * from .plugin.img_tools import * +from .plugin.mesh_tools import * from .advanced import Filter, Free, Simple, Table, Macros, Widget, dataio, Report \ No newline at end of file diff --git a/sciapp/action/plugin/mesh_tools.py b/sciapp/action/plugin/mesh_tools.py new file mode 100644 index 00000000..5fefd73f --- /dev/null +++ b/sciapp/action/plugin/mesh_tools.py @@ -0,0 +1,71 @@ +from math import sin, cos, pi, atan, sqrt, asin +import numpy as np +from .. import MeshTool + +class MeshViewTool(MeshTool): + def __init__(self): + self.oldxy = None + self.status = '' + self.curobj = None + + def mouse_down(self, obj, x, y, btn, **key): + self.oldxy = x, y + if btn == 1 and not key['shift']: + picked = key['canvas'].at(x,y) + if not picked is None: + self.curobj = picked + self.curobj.set_data(high_light=0xffaaffff) + self.status = 'view' + if btn == 1 and key['shift']: + self.status = 'offset' + if btn == 2: self.status = 'offset' + if btn == 3: + self.status = 'right' + + def mouse_up(self, obj, x, y, btn, **key): + if self.status == 'right': + key['canvas'].fit() + if not self.curobj is None: + self.curobj.set_data(high_light=False) + self.curobj = None + self.oldxy = None + self.status = '' + + def mouse_move(self, obj, x, y, btn, **key): + if self.status == 'view': + dx = x - self.oldxy[0] + dy = y - self.oldxy[1] + camera = key['canvas'].camera + camera.orbit(-dx/2, dy/2) + self.oldxy = x, y + if self.status == 'offset': + camera = key['canvas'].camera + dx = x - self.oldxy[0] + dy = y - self.oldxy[1] + norm = np.mean(camera._viewbox.size) + k = 1 / norm * camera._scale_factor + camera = key['canvas'].camera + dx, dy, dz = camera._dist_to_trans((-dx*k, dy*k)) + self.oldxy = x, y + cx, cy, cz = camera.center + camera.center = cx + dx, cy + dy, cz + dz + if self.status in {'right', 'light'}: + self.status = 'light' + lx, ly, lz = obj.light_dir + dx = (x - self.oldxy[0])/360 + dy = (y - self.oldxy[1])/360 + ay = asin(lz/sqrt(lx**2+ly**2+lz**2))-dy + xx = cos(dx)*lx - sin(dx)*ly + yy = sin(dx)*lx + cos(dx)*ly + ay = max(min(pi/2-1e-4, ay), -pi/2+1e-4) + zz, k = sin(ay), cos(ay)/sqrt(lx**2+ly**2) + obj.set_style(light_dir = (xx*k, yy*k, zz)) + self.oldxy = x, y + + + def mouse_wheel(self, obj, x, y, d, **key): + s = 1.1 ** - d + camera = key['canvas'].camera + if camera._distance is not None: + camera._distance *= s + camera.scale_factor *= s \ No newline at end of file diff --git a/sciapp/action/tolact.py b/sciapp/action/tolact.py index 2d3218df..8d5934db 100644 --- a/sciapp/action/tolact.py +++ b/sciapp/action/tolact.py @@ -51,6 +51,7 @@ class ImageTool(DefaultTool): title = 'Image Tool' def mouse_move(self, img, x, y, btn, **key): + if img.img is None: return DefaultTool.mouse_move(self, img, x, y, btn, **key) if self.app is None: return r, c = int(y), int(x) @@ -90,7 +91,33 @@ def start(self, app, para=None, callafter=None): TableTool.default = self # if not app is None: app.tool = self +class MeshTool(DefaultTool): + default = None + title = 'Shape Tool' + + def start(self, app, para=None, callafter=None): + self.app = app + if para == 'local': return self + MeshTool.default = self + # if not app is None: app.tool = self + + def mouse_move(self, obj, x, y, btn, **key): + if not self.oldxy is None: + dx = x - self.oldxy[0] + dy = y - self.oldxy[1] + camera = key['canvas'].camera + camera.orbit(-dx/2, dy/2) + self.oldxy = x, y + + def mouse_wheel(self, obj, x, y, d, **key): + s = 1.1 ** - d + camera = key['canvas'].camera + if camera._distance is not None: + camera._distance *= s + camera.scale_factor *= s + DefaultTool().start(None) ImageTool().start(None) ShapeTool().start(None) -TableTool().start(None) \ No newline at end of file +TableTool().start(None) +MeshTool().start(None) \ No newline at end of file diff --git a/sciapp/app.py b/sciapp/app.py index 4968f85e..32801d52 100644 --- a/sciapp/app.py +++ b/sciapp/app.py @@ -1,5 +1,5 @@ from .manager import Manager -from .object import Image, Table +from .object import Image, Table, Scene class App(): def __init__(self, asyn=True): @@ -78,24 +78,26 @@ def get_table(self, name=None): def table_names(self): return self.tab_manager.names() + # ========== Others ========== def show_plot(self): pass - def show_mesh(self): pass - - def add_mesh(self, mesh): - if not self.mesh_manager.has(mesh.name, obj=mesh): - mesh.name = self.mesh_manager.name(mesh.name) + def show_mesh(self, mesh, name): + mesh.name = self.mesh_manager.name(name) self.mesh_manager.add(mesh.name, mesh) - def remove_mesh(self, mesh): - self.mesh_manager.remove(obj=mesh) + def active_mesh(self, name): + self.mesh_manager.active(name) + print('active mesh:', name) + + def close_mesh(self, name): + self.mesh_manager.remove(name) - def add_mesh_win(self, win): - self.wmesh_manager.add(win.name, win) + #def add_mesh_win(self, win): + # self.wmesh_manager.add(win.name, win) - def remove_mesh_win(self, win): - self.wmesh_manager.remove(obj=win) + #def remove_mesh_win(self, win): + # self.wmesh_manager.remove(obj=win) def get_mesh(self, name=None): return self.mesh_manager.get(name) @@ -103,8 +105,8 @@ def get_mesh(self, name=None): def get_mesh_name(self): return self.mesh_manager.names() - def get_mesh_win(self, name=None): - return self.wmesh_manager.get(name) + #def get_mesh_win(self, name=None): + # return self.wmesh_manager.get(name) def add_task(self, task): self.task_manager.add(task.title, task) diff --git a/sciapp/object/__init__.py b/sciapp/object/__init__.py index f3766183..de8ba6a9 100644 --- a/sciapp/object/__init__.py +++ b/sciapp/object/__init__.py @@ -2,4 +2,4 @@ from .image import Image from .table import Table from .roi import * -from .surface import Surface, MarkText, MeshSet \ No newline at end of file +from .surface import * \ No newline at end of file diff --git a/sciapp/object/shape.py b/sciapp/object/shape.py index cef529c6..e20bcea2 100644 --- a/sciapp/object/shape.py +++ b/sciapp/object/shape.py @@ -17,12 +17,12 @@ def __init__(self, body=None, **key): self.name = 'shape' self.body = [] if body is None else body self.color = key['color'] if 'color' in key else None - self.fcolor = key['fcolor'] if 'fcolor' in key else None - self.lstyle = key['lstyle'] if 'lstyle' in key else None - self.tcolor = key['tcolor'] if 'tcolor' in key else None - self.lw = key['lw'] if 'lw' in key else None - self.r = key['r'] if 'r' in key else None - self.fill = key['fill'] if 'fill' in key else None + self.fcolor = key.get('fcolor', None) + self.lstyle = key.get('style', None) + self.tcolor = key.get('tcolor', None) + self.lw = key.get('lw', None) + self.r = key.get('r', None) + self.fill = key.get('fill', None) self._box = None self.dirty = True diff --git a/sciapp/object/surface.py b/sciapp/object/surface.py index 121e6a32..56e4a229 100644 --- a/sciapp/object/surface.py +++ b/sciapp/object/surface.py @@ -1,71 +1,188 @@ import numpy as np -import moderngl -import numpy as np -from math import sin, cos, tan, pi - -class Surface: - def __init__(self, vts, ids, ns, cs=(0,0,1), **key): - self.vts = vts - self.ids = ids - self.ns = ns - self.cs = cs - self.box = np.vstack( - (vts.min(axis=0), - vts.max(axis=0))) - self.mode = 'mesh' - self.alpha = 1 - self.visible = True - self.width = 1 - self.update = False - self.color = (0,0,0) +import scipy.ndimage as ndimg + +class Scene: + def __init__(self, **key): + self.name = key.get('name', 'scene') + self.objects = {} + self._light_dir = (10, 5, -5) + self._light_color = (0.7, 0.7, 0.7, 0.7) + self._bg_color = (0.3, 0.3, 0.3, 1.0) + self._ambient_color = (0.3, 0.3, 0.3, 1.0) self.set_style(**key) + self.dirty = True - def set_style(self, **key): - if 'mode' in key: self.mode = key['mode'] - if 'alpha' in key: self.alpha = key['alpha'] - if 'visible' in key: self.visible = key['visible'] - if 'color' in key: - self.cs = key['color'] - self.update = True - -class MarkText(Surface): - def __init__(self, vts, ids, os, h, color): - self.vts = vts - self.ids = ids - self.cs = color - self.os = os - self.h = h - self.box = None - self.alpha = 1 - self.visible = True - self.width = 1 - self.mode = 'grid' - self.update = False - self.color = (0,0,0) + @property + def title(self): return self.name + + @property + def surface(self): return self.name + + def apply(self): + for i in self.objects.values(): + i.light_dir = self.light_dir + i.light_color = self.light_color + i.ambient_color = self.ambient_color + + def set_style(self, bg_color=None, light_dir=None, light_color=None, ambient_color=None, **key): + self._bg_color = bg_color or self._bg_color + self._light_dir = light_dir or self._light_dir + self._light_color = light_color or self._light_color + self._ambient_color = ambient_color or self._ambient_color + self.dirty = True; self.apply() + + + @property + def bg_color(self): return self._bg_color + + @bg_color.setter + def bg_color(self, value): + return self.set_style(bg_color=value) + + @property + def light_dir(self): return self._light_dir -class MeshSet: - def __init__(self, name='meshset', objs=None): - self.name = name - self.objs = objs or {} + @light_dir.setter + def light_dir(self, value): + return self.set_style(light_dir=value) @property - def box(self): - minb = [i.box[0] for i in self.objs.values() if not i.box is None] - maxb = [i.box[1] for i in self.objs.values() if not i.box is None] - minb, maxb = np.array(minb).min(axis=0), np.array(maxb).max(axis=0) - return np.vstack((minb, maxb)) + def light_color(self): return self._light_color + + @light_color.setter + def light_color(self, value): + return self.set_style(light_color=value) @property - def center(self): return self.box.mean(axis=0) + def ambient_color(self): return self._ambient_color + + @ambient_color.setter + def ambient_color(self, value): + return self.set_style(ambient_color=value) + + def add_obj(self, name, obj): + self.objects[name] = obj + + def get_obj(self, name): + return self.objects[name] @property - def dial(self): return np.linalg.norm(self.box[1]-self.box[0]) + def names(self): + return list(self.objects.keys()) + +class Mesh: + def __init__(self, verts=None, faces=None, colors=None, cmap=None, **key): + if faces is None and not verts is None: + faces = np.arange(len(verts), dtype=np.uint32) + self.verts = verts.astype(np.float32, copy=False) if not verts is None else None + self.faces = faces.astype(np.uint32, copy=False) if not faces is None else None + self.colors = colors + self.mode, self.visible, self.dirty = 'mesh', True, 'geom' + self.alpha = 1; self.edges = None + self.high_light = False; self.cmap = 'gray' if cmap is None else cmap + self.set_data(**key) + + def set_data(self, verts=None, faces=None, colors=None, **key): + if faces is None and not verts is None: + faces = np.arange(len(verts), dtype=np.uint32) + if not verts is None: self.verts = verts.astype(np.float32, copy=False) + if not faces is None: self.faces = faces.astype(np.uint32, copy=False) + if not colors is None: self.colors = colors + if not faces is None: self.edge = None + if sum([i is None for i in [verts, faces, colors]])<3: self.dirty = 'geom' + if not self.faces is None and self.faces.ndim==1: key['mode'] = 'points' + elif not self.faces is None and self.faces.shape[1]==2: + if key.get('mode', self.mode)=='mesh': key['mode'] = 'grid' + if key.get('mode', self.mode) != self.mode: self.dirty = 'geom' + self.mode = key.get('mode', self.mode) + self.visible = key.get('visible', self.visible) + self.alpha = key.get('alpha', self.alpha) + self.high_light = key.get('high_light', False) + self.cmap = key.get('cmap', self.cmap) + self.dirty = self.dirty or True - def add_surf(self, name, obj): - self.objs[name] = obj + def get_edges(self): + if self.faces.ndim==1 or self.faces.shape[1]==2: return self.faces + if not self.edges is None: return self.edges + edges = np.vstack([self.faces[:,i] for i in ([0,1],[0,2],[1,2])]) + edges = np.sort(edges, axis=-1).ravel().view(dtype=np.uint64) + self.edges = np.unique(edges).view(dtype=np.uint32).reshape(-1,2) + return self.edges - def get_obj(self, key): - if not key in self.objs: return None - return self.objs[key] + def update(self, state=True): self.dirty = state +class TextSet: + def __init__(self, texts=None, verts=None, colors=(1,1,1), size=12, **key): + self.texts, self.verts, self.size, self.colors = texts, verts, size, colors + self.visible, self.dirty = True, 'geom' + self.alpha = 1; self.edges = None + self.high_light = False; + self.set_data(**key) + + def set_data(self, texts=None, verts=None, colors=None, size=None, **key): + if not texts is None: self.texts = texts + if not verts is None: self.verts = verts + if not colors is None: self.colors = colors + if not size is None: self.size = size + if sum([i is None for i in [texts, verts, colors, size]])<4: self.dirty = 'geom' + self.visible = key.get('visible', self.visible) + self.alpha = key.get('alpha', self.alpha) + self.high_light = key.get('high_light', False) + self.dirty = self.dirty or True + +class Surface2d(Mesh): + def __init__(self, img=None, sample=1, sigma=0, k=0.3, **key): + self.img, self.sample, self.sigma, self.k = img, sample, sigma, k + Mesh.__init__(self, **key) + self.set_data(img, sample, sigma, k) + + def set_data(self, img=None, sample=None, sigma=None, k=None, **key): + if not img is None: self.img = img + if not sample is None: self.sample = sample + if not sigma is None: self.sigma = sigma + if not k is None: self.k = k + if sum([not i is None for i in (img, sample, sigma, k)])>0: + from ..util import meshutil + vert, fs = meshutil.create_surface2d(self.img, self.sample, self.sigma, self.k) + Mesh.set_data(self, verts=vert, faces=fs.astype(np.uint32), colors=vert[:,2], **key) + else: Mesh.set_data(self, **key) + +class Surface3d(Mesh): + def __init__(self, imgs=None, level=0, sample=1, sigma=0, step=1, **key): + self.imgs, self.sample, self.sigma, self.step = imgs, sample, sigma, step + self.level, self.step = level, step + Mesh.__init__(self, **key) + self.set_data(imgs, level, sample, sigma, step) + + def set_data(self, imgs=None, level=None, sample=None, sigma=None, step=None, **key): + if not imgs is None: self.imgs = imgs + if not level is None: self.level = level + if not sample is None: self.sample = sample + if not sigma is None: self.sigma = sigma + if not step is None: self.step = step + if sum([not i is None for i in (imgs, level, sample, sigma, step)])>0: + from ..util import meshutil + vert, fs = meshutil.create_surface3d(self.imgs, self.level, self.sample, self.sigma, self.step) + Mesh.set_data(self, verts=vert, faces=fs.astype(np.uint32), **key) + else: Mesh.set_data(self, **key) + +class Volume3d: + def __init__(self, imgs=None, level=0.25, sample=1, step=1, cmap=None, **key): + self.imgs, self.level, self.sample, self.step = imgs, level, sample, step + self.visible, self.dirty = True, 'geom' + self.cmap = 'gray' if cmap is None else cmap + self.alpha = 1 + self.high_light = False; + self.set_data(**key) + def set_data(self, imgs=None, level=None, step=None, **key): + if not imgs is None: self.imgs = imgs + if not level is None: self.level = level + if not step is None: self.step = step + if sum([not i is None for i in (imgs, level, step)])>0: + self.dirty = 'geom' + self.visible = key.get('visible', self.visible) + self.alpha = key.get('alpha', self.alpha) + self.high_light = key.get('high_light', False) + self.cmap = key.get('cmap', self.cmap) + self.dirty = self.dirty or True diff --git a/sciapp/util/__init__.py b/sciapp/util/__init__.py index d1b21603..b24cca4c 100644 --- a/sciapp/util/__init__.py +++ b/sciapp/util/__init__.py @@ -1,3 +1,4 @@ from .surfutil import * +from .meshutil import * from .shputil import * from .imgutil import * \ No newline at end of file diff --git a/sciapp/util/meshutil.py b/sciapp/util/meshutil.py new file mode 100644 index 00000000..6bff12d2 --- /dev/null +++ b/sciapp/util/meshutil.py @@ -0,0 +1,304 @@ +import numpy as np + +def create_cube(p1=(0,0,0), p2=(1,1,1)): + p = np.array([[1, 1, 1], [0, 1, 1], [0, 0, 1], [1, 0, 1], + [1, 0, 0], [1, 1, 0], [0, 1, 0], [0, 0, 0]]) + faces_p = [0, 1, 2, 3, 0, 3, 4, 5, 0, 5, 6, 1, 1, 6, 7, 2, 7, 4, 3, 2, 4, 7, 6, 5] + vertices = p[faces_p] + vertices *= np.subtract(p2, p1); vertices += p1 + filled = np.resize(np.array([0, 1, 2, 0, 2, 3], dtype=np.uint32), 6 * (2 * 3)) + filled += np.repeat(4 * np.arange(6, dtype=np.uint32), 6) + filled = filled.reshape((len(filled) // 3, 3)) + outline = np.resize(np.array([0, 1, 1, 2, 2, 3, 3, 0], dtype=np.uint32), 6 * (2 * 4)) + outline += np.repeat(4 * np.arange(6, dtype=np.uint32), 8) + return vertices, filled, outline.reshape(-1,2) + + +def create_plane(width=1, height=1, width_segments=1, height_segments=1, + direction='+z'): + x_grid = width_segments + y_grid = height_segments + + x_grid1 = x_grid + 1 + y_grid1 = y_grid + 1 + + # Positions, normals and texcoords. + positions = np.zeros(x_grid1 * y_grid1 * 3) + normals = np.zeros(x_grid1 * y_grid1 * 3) + texcoords = np.zeros(x_grid1 * y_grid1 * 2) + + y = np.arange(y_grid1) * height / y_grid - height / 2 + x = np.arange(x_grid1) * width / x_grid - width / 2 + + positions[::3] = np.tile(x, y_grid1) + positions[1::3] = -np.repeat(y, x_grid1) + + normals[2::3] = 1 + + texcoords[::2] = np.tile(np.arange(x_grid1) / x_grid, y_grid1) + texcoords[1::2] = np.repeat(1 - np.arange(y_grid1) / y_grid, x_grid1) + + # Faces and outline. + faces, outline = [], [] + for i_y in range(y_grid): + for i_x in range(x_grid): + a = i_x + x_grid1 * i_y + b = i_x + x_grid1 * (i_y + 1) + c = (i_x + 1) + x_grid1 * (i_y + 1) + d = (i_x + 1) + x_grid1 * i_y + + faces.extend(((a, b, d), (b, c, d))) + outline.extend(((a, b), (b, c), (c, d), (d, a))) + + positions = np.reshape(positions, (-1, 3)) + texcoords = np.reshape(texcoords, (-1, 2)) + normals = np.reshape(normals, (-1, 3)) + + faces = np.reshape(faces, (-1, 3)).astype(np.uint32) + outline = np.reshape(outline, (-1, 2)).astype(np.uint32) + + direction = direction.lower() + if direction in ('-x', '+x'): + shift, neutral_axis = 1, 0 + elif direction in ('-y', '+y'): + shift, neutral_axis = -1, 1 + elif direction in ('-z', '+z'): + shift, neutral_axis = 0, 2 + + sign = -1 if '-' in direction else 1 + + positions = np.roll(positions, shift, -1) + normals = np.roll(normals, shift, -1) * sign + colors = np.ravel(positions) + colors = np.hstack((np.reshape(np.interp(colors, + (np.min(colors), + np.max(colors)), + (0, 1)), + positions.shape), + np.ones((positions.shape[0], 1)))) + colors[..., neutral_axis] = 0 + + vertices = np.zeros(positions.shape[0], + [('position', np.float32, 3), + ('texcoord', np.float32, 2), + ('normal', np.float32, 3), + ('color', np.float32, 4)]) + + vertices['position'] = positions + vertices['texcoord'] = texcoords + vertices['normal'] = normals + vertices['color'] = colors + + return vertices, faces, outline + + +def create_box(width=1, height=1, depth=1, width_segments=1, height_segments=1, + depth_segments=1, planes=None): + planes = (('+x', '-x', '+y', '-y', '+z', '-z') + if planes is None else + [d.lower() for d in planes]) + + w_s, h_s, d_s = width_segments, height_segments, depth_segments + + planes_m = [] + if '-z' in planes: + planes_m.append(create_plane(width, depth, w_s, d_s, '-z')) + planes_m[-1][0]['position'][..., 2] -= height / 2 + if '+z' in planes: + planes_m.append(create_plane(width, depth, w_s, d_s, '+z')) + planes_m[-1][0]['position'][..., 2] += height / 2 + + if '-y' in planes: + planes_m.append(create_plane(height, width, h_s, w_s, '-y')) + planes_m[-1][0]['position'][..., 1] -= depth / 2 + if '+y' in planes: + planes_m.append(create_plane(height, width, h_s, w_s, '+y')) + planes_m[-1][0]['position'][..., 1] += depth / 2 + + if '-x' in planes: + planes_m.append(create_plane(depth, height, d_s, h_s, '-x')) + planes_m[-1][0]['position'][..., 0] -= width / 2 + if '+x' in planes: + planes_m.append(create_plane(depth, height, d_s, h_s, '+x')) + planes_m[-1][0]['position'][..., 0] += width / 2 + + positions = np.zeros((0, 3), dtype=np.float32) + texcoords = np.zeros((0, 2), dtype=np.float32) + normals = np.zeros((0, 3), dtype=np.float32) + + faces = np.zeros((0, 3), dtype=np.uint32) + outline = np.zeros((0, 2), dtype=np.uint32) + + offset = 0 + for vertices_p, faces_p, outline_p in planes_m: + positions = np.vstack((positions, vertices_p['position'])) + texcoords = np.vstack((texcoords, vertices_p['texcoord'])) + normals = np.vstack((normals, vertices_p['normal'])) + + faces = np.vstack((faces, faces_p + offset)) + outline = np.vstack((outline, outline_p + offset)) + offset += vertices_p['position'].shape[0] + + vertices = np.zeros(positions.shape[0], + [('position', np.float32, 3), + ('texcoord', np.float32, 2), + ('normal', np.float32, 3), + ('color', np.float32, 4)]) + + colors = np.ravel(positions) + colors = np.hstack((np.reshape(np.interp(colors, + (np.min(colors), + np.max(colors)), + (0, 1)), + positions.shape), + np.ones((positions.shape[0], 1)))) + + vertices['position'] = positions + vertices['texcoord'] = texcoords + vertices['normal'] = normals + vertices['color'] = colors + + return vertices, faces, outline + + +def create_sphere(rows, cols): + verts = np.empty((rows+1, cols, 3), dtype=np.float32) + + # compute vertices + phi = (np.arange(rows+1) * np.pi / rows).reshape(rows+1, 1) + s = np.sin(phi) + verts[..., 2] = np.cos(phi) + th = ((np.arange(cols) * 2 * np.pi / cols).reshape(1, cols)) + th = th + ((np.pi / cols) * np.arange(rows+1).reshape(rows+1, 1)) + verts[..., 0] = s * np.cos(th) + verts[..., 1] = s * np.sin(th) + # remove redundant vertices from top and bottom + verts = verts.reshape((rows+1)*cols, 3)[cols-1:-(cols-1)] + + # compute faces + faces = np.empty((rows*cols*2, 3), dtype=np.uint32) + rowtemplate1 = (((np.arange(cols).reshape(cols, 1) + + np.array([[1, 0, 0]])) % cols) + + np.array([[0, 0, cols]])) + rowtemplate2 = (((np.arange(cols).reshape(cols, 1) + + np.array([[1, 0, 1]])) % cols) + + np.array([[0, cols, cols]])) + for row in range(rows): + start = row * cols * 2 + faces[start:start+cols] = rowtemplate1 + row * cols + faces[start+cols:start+(cols*2)] = rowtemplate2 + row * cols + # cut off zero-area triangles at top and bottom + faces = faces[cols:-cols] + + # adjust for redundant vertices that were removed from top and bottom + vmin = cols-1 + faces[faces < vmin] = vmin + faces -= vmin + vmax = verts.shape[0]-1 + faces[faces > vmax] = vmax + return verts, faces + +def create_grid_mesh(xs, ys, zs): + h, w = xs.shape + vts = np.array([xs, ys, zs], dtype=np.float32) + did = np.array([[0, 1, 1+w, 0, 1+w, w]], dtype=np.uint32) + rcs = np.arange(0,w*h-w,w)[:,None] + np.arange(0,w-1,1) + faces = rcs.reshape(-1,1) + did + return vts.reshape(3,-1).T.copy(), faces.reshape(-1,3) + +def create_ball(o, r, rows=16, cols=16): + ball = create_sphere(rows, cols) + ball[0][:] *= r; ball[0][:] += o; + return ball + +def create_balls(os, rs, cs=None, rows=16, cols=16): + os, rs = np.asarray(os), np.asarray(rs) + verts, faces = create_sphere(rows, cols) + if not isinstance(cs, tuple) and not cs is None: + cs = np.repeat(cs, len(verts)) + offset = np.arange(len(os)) * len(verts) + verts = verts[None, :, :] * rs[:, None, None] + verts += os[:, None, :] + faces = faces[None,:,:] + offset[:,None,None] + return verts.reshape(-1,3), faces.reshape(-1,3), cs + +def create_line(xs, ys, zs): + vts = np.array([xs, ys, zs], dtype=np.float32) + fs = np.arange(len(xs)) + return vts.T, np.array([fs[:-1], fs[1:]], np.uint32).T + +def mesh_merge(vts, fs, cs): + cc = [None]*len(vts) if isinstance(cs, tuple) else cs + def makecolor(c, vts): + if c is None: return None + if len(c)==len(vts): return c + return np.repeat([c], len(vts), axis=0) + cc = [makecolor(c,v) for c,v in zip(cc, vts)] + offset = np.cumsum([0]+[len(i) for i in vts[:-1]]) + if not isinstance(cs, tuple): cs = np.vstack(cc) + for f,s in zip(fs, offset): f += s + return np.vstack(vts), np.vstack(fs), cs + +def create_lines(xs, ys, zs, cs): + vtsfs = [create_line(x,y,z) for x,y,z in zip(xs,ys,zs)] + return mesh_merge(*list(zip(*vtsfs)), cs) + +def create_bound(p1, p2, nx=1, ny=1, nz=1): + vts, fs, ls = create_box(1, 1, 1, nx, ny, nz) + vts['position'] *= np.subtract(p2, p1) + vts['position'] += p1 - vts['position'].min(axis=0) + return vts['position'], ls + +def create_surface2d(img, sample=1, sigma=0, k=0.3): + from scipy.ndimage import gaussian_filter + #start = time() + img = img[::sample, ::sample].astype(np.float32) + if sigma>0: img = gaussian_filter(img, sigma) + xs, ys = np.mgrid[:img.shape[0],:img.shape[1]] + xs *= sample; ys *= sample + return create_grid_mesh(xs, ys, img*k) + +def create_surface3d(imgs, level, sample=1, sigma=0, step=1): + from skimage.measure import marching_cubes_lewiner + from scipy.ndimage import gaussian_filter + imgs = imgs[::sample,::sample,::sample] + if sigma>0: imgs = gaussian_filter(imgs, sigma) + vts, fs, ns, cs = marching_cubes_lewiner(imgs, level, step_size=step) + return vts * sample, fs + +def build_arrow(v1, v2, rs, re, ts, te, c): + v = (v2-v1)/np.linalg.norm(v2-v1) + ss, ee = v1 + v*rs*ts, v2 - v*re*te + vx = np.cross(v, np.random.rand(3)) + vx /= np.linalg.norm(vx) + vy = np.cross(vx, v) + angs = np.linspace(0, np.pi*2, 17) + vas = np.array([np.cos(angs), np.sin(angs)]) + vxy = np.dot(vas.T, np.array([vx, vy])) + vts = np.vstack((v1, ss + rs * vxy, ee + re * vxy, v2)) + fs1 = build_pringidx(0, 16, 1) + fs = build_twringidx(16, 1) + fs2 = build_pringidx(35, 16, 18) + face = np.vstack((fs1, fs, fs2)) + ns = np.vstack((-v, vxy, vxy, v)).astype(np.float32) + cs = (np.ones((len(vts), 3))*c).astype(np.float32) + return vts.astype(np.float32), face, ns, cs + +def build_arrows(v1s, v2s, rss, res, tss, tes, cs): + if not isinstance(cs, list): cs = [cs] * len(v1s) + if not isinstance(tss, list): tss = [tss] * len(v1s) + if not isinstance(tes, list): tes = [tes] * len(v1s) + if not isinstance(rss, list): rss = [rss] * len(v1s) + if not isinstance(res, list): res = [res] * len(v1s) + vtss, fss, nss, css = [], [], [], [] + s = 0 + for v1, v2, rs, re, ts, te, c in zip(v1s, v2s, rss, res, tss, tes, cs): + if np.linalg.norm(v1-v2) < 0.1: continue + vv, ff, nn, cc = build_arrow(v1, v2, rs, re, ts, te, c) + fss.append(ff+s) + s += len(vv) + vtss.append(vv) + nss.append(nn) + css.append(cc) + print(np.vstack(vtss).shape, np.vstack(fss).shape, np.vstack(nss).shape, np.vstack(css).shape) + return np.vstack(vtss), np.vstack(fss), np.vstack(nss), np.vstack(css) diff --git a/sciwx/canvas/canvas.py b/sciwx/canvas/canvas.py index 2235f269..2e3d305d 100644 --- a/sciwx/canvas/canvas.py +++ b/sciwx/canvas/canvas.py @@ -57,7 +57,7 @@ def on_mouse(self, me): 'shift':sta[2], 'px':px, 'py':py, 'canvas':self} if me.Moving() and not (ld or md or rd): for i in (ImageTool, ShapeTool): - if isinstance(tool, i): i.mouse_move(tool, obj, x, y, btn) + if isinstance(tool, i): i.mouse_move(tool, obj, x, y, btn, **others) if me.ButtonDown(): self.SetFocus() tool.mouse_down(obj, x, y, btn, **others) diff --git a/sciwx/canvas/mark.py b/sciwx/canvas/mark.py index 012b30c7..392e22f1 100644 --- a/sciwx/canvas/mark.py +++ b/sciwx/canvas/mark.py @@ -263,13 +263,16 @@ def draw_text(pts, dc, f, **key): size = font.GetPointSize() tcolor = dc.GetTextForeground() bcolor = dc.GetTextBackground() - + dc.SetTextForeground(color) + dc.SetTextBackground(fcolor) if not pts.color is None: pen.SetColour(pts.color) dc.SetTextForeground(pts.color) brush.SetColour(pen.GetColour()) brush.SetStyle(100) - if not pts.color is None: + if not pts.fill is None: + dc.SetBackgroundMode((106, 100)[pts.fill]) + if not pts.fcolor is None: dc.SetTextBackground(pts.fcolor) if not pts.lw is None: font.SetPointSize(pts.lw) @@ -281,8 +284,8 @@ def draw_text(pts, dc, f, **key): if pts.dtype == 'text': (x, y), text = pts.body, pts.txt x, y = f(x, y) - dc.DrawText(text, x+3, y+3) - if pts.fill: + dc.DrawText(text, x+1, y+1) + if not pts.lstyle is None: dc.DrawEllipse(x-2,y-2,4,4) if pts.dtype == 'texts': tlst, clst, elst = [], [], [] @@ -328,8 +331,6 @@ def draw_layer(pts, dc, f, **key): brush.SetColour(pts.fcolor) if pts.lw != None: pen.SetWidth(pts.lw) - if pts.fill: - brush.SetStyle((106,100)[pts.fill]) if not pts.fill is None: brush.SetStyle((106,100)[pts.fill]) diff --git a/sciwx/demo/mesh1_demo.py b/sciwx/demo/mesh1_demo.py index f8a4e168..278ee4dc 100644 --- a/sciwx/demo/mesh1_demo.py +++ b/sciwx/demo/mesh1_demo.py @@ -1,47 +1,48 @@ import sys, wx +import numpy as np sys.path.append('../../') -from sciapp.object import Surface, MarkText, MeshSet -from sciwx.mesh import Canvas3DFrame, Canvas3DNoteBook, Canvas3DNoteFrame -from sciapp.util import surfutil -vts, fs, ns, cs = surfutil.build_ball((100,100,100),50, (1,0,0)) +from sciapp.object import Mesh, Scene +from sciwx.mesh import Canvas3D, MCanvas3D, Canvas3DFrame, Canvas3DNoteBook, Canvas3DNoteFrame +from sciapp.util import meshutil + +# vts, fs, ns, cs = surfutil.build_ball((100,100,100), 50, (1,0,0)) +verts, faces = meshutil.create_ball((0,0,0), 1) +ball = Mesh(verts=verts, faces=faces, colors=verts[:,2], cmap='jet') +ball2 = Mesh(verts=verts, faces=faces, colors=verts[:,2], mode='grid') def canvas3d_test(): frame = wx.Frame(None, title='Canvas3D') canvas3d = Canvas3D(frame) - canvas3d.add_surf('ball', vts, fs, ns, cs) + canvas3d.add_obj('ball', ball) frame.Show() def mcanvas3d_test(): frame = wx.Frame(None, title='MCanvas3D') canvas3d = MCanvas3D(frame) - canvas3d.add_surf('ball', vts, fs, ns, cs) + canvas3d.add_obj('ball', ball) frame.Show() def canvas3d_frame_test(): cnf = Canvas3DFrame(None) - cnf.add_surf('ball', vts, fs, ns, cs) + cnf.add_obj('ball', ball) cnf.Show() def canvas3d_note_book(): frame = wx.Frame(None, title='Canvas3D NoteBook') cnb = Canvas3DNoteBook(frame) canvas1 = cnb.add_canvas() - canvas1.add_surf('ball', vts, fs, ns, cs) + canvas1.add_obj('ball', ball) canvas2 = cnb.add_canvas() - canvas2.add_surf('ball', vts, fs, ns, cs, mode='grid') + canvas2.add_obj('ball2', ball2) frame.Show() def canvas3d_note_frame(): cnf = Canvas3DNoteFrame(None) canvas1 = cnf.add_canvas() - ball = Surface(vts, fs, ns, cs) - canvas1.add_surf('ball', ball) + canvas1.add_obj('ball', ball) canvas2 = cnf.add_canvas() - ball = Surface(vts, fs, ns, cs) - meshset = MeshSet('ABC', {'ball':ball}) - ball.mode = 'grid' - canvas2.set_mesh(meshset) + canvas2.add_obj('ball', ball2) cnf.Show() if __name__ == '__main__': diff --git a/sciwx/demo/mesh2_mesh_demo.py b/sciwx/demo/mesh2_mesh_demo.py index 076d254d..bac13352 100644 --- a/sciwx/demo/mesh2_mesh_demo.py +++ b/sciwx/demo/mesh2_mesh_demo.py @@ -1,38 +1,35 @@ import sys, wx sys.path.append('../../') -from sciwx.mesh import Canvas3D, MCanvas3D, MeshSet -from sciapp.util import surfutil -from sciapp.object import Surface +from sciwx.mesh import Canvas3D, MCanvas3D +from sciapp.util import meshutil +from sciapp.object import Scene, Mesh from sciwx.mesh import Canvas3DFrame, Canvas3DNoteBook, Canvas3DNoteFrame -vts, fs, ns, cs = surfutil.build_ball((100,100,100),50, (1,0,0)) +verts, faces = meshutil.create_sphere(16, 16, 16) +ball = Mesh(verts=verts, faces=faces, colors=(1,0,0), mode='grid') def add_with_para(): cnf = Canvas3DFrame(None) - surf = Surface(vts, fs, ns, cs, mode='grid') - cnf.add_surf('gridball', surf) + cnf.add_obj('gridball', ball) cnf.Show() def mesh_obj_test(): cnf = Canvas3DFrame(None) - meshes = MeshSet() - vts, fs, ns, cs = surfutil.build_ball((100,100,100),50, (1,0,0)) - redball = Surface(vts, fs, ns, cs) - meshes.add_surf('redball', redball) - vts, fs, ns, cs = surfutil.build_ball((300,100,100),50, (1,1,0)) - yellowball = Surface(vts, fs, ns, cs, mode='grid') - meshes.add_surf('yellowball', yellowball) - hideball = Surface(vts, fs, ns, cs) - vts, fs, ns, cs = surfutil.build_ball((300,-300,100),50, (0,1,0)) - hideball = Surface(vts, fs, ns, cs, visible=False) - hideball = meshes.add_surf('hideball', hideball) - meshes.background = (0, 0, 0.3) - cnf.set_mesh(meshes) + scene = cnf.canvas.scene3d + verts, faces = meshutil.create_sphere(16, 16, 16) + redball = Mesh(verts=verts*50+(100,100,100), faces=faces, colors=(1,0,0)) + scene.add_obj('redball', redball) + verts, faces = meshutil.create_sphere(16, 16, 16) + yellowball = Mesh(verts=verts*50+(300,100,100), faces=faces, colors=(1,1,0)) + scene.add_obj('yellowball', yellowball) + verts, faces = meshutil.create_sphere(16, 16, 16) + hideball = Mesh(verts=verts*50+(300,-300,100), faces=faces, colors=(0,1,0), visible=False) + scene.add_obj('hideball', hideball) cnf.Show() if __name__ == '__main__': app = wx.App() - add_with_para() + # add_with_para() mesh_obj_test() app.MainLoop() diff --git a/sciwx/demo/mesh3_geoutil.py b/sciwx/demo/mesh3_geoutil.py index ff939994..94f7427c 100644 --- a/sciwx/demo/mesh3_geoutil.py +++ b/sciwx/demo/mesh3_geoutil.py @@ -2,35 +2,33 @@ sys.path.append('../../') from sciwx.mesh import Canvas3D, MCanvas3D -from sciapp.util import surfutil -from sciapp.object import Surface, MarkText +from sciapp.util import surfutil, meshutil +from sciapp.object import Scene, Mesh, Surface2d, Surface3d, TextSet, Volume3d from sciwx.mesh import Canvas3DFrame, Canvas3DNoteBook, Canvas3DNoteFrame import sys, wx - import scipy.ndimage as ndimg from skimage.data import moon, camera import numpy as np def dem_test(): cnf = Canvas3DFrame(None) - vts, fs, ns, cs = surfutil.build_surf2d(moon(), ds=1, k=0.3, sigma=2) - cnf.add_surf('dem', Surface(vts, fs, ns, cs)) + cnf.add_obj('dem', Surface2d(img=moon(), sample=1, sigma=1, k=0.3, cmap='jet')) cnf.Show() def ball_test(): cnf = Canvas3DFrame(None) - vts, fs, ns, cs = geoutil.build_ball((100,100,100),50, (1,0,0)) - cnf.add_surf('ball', vts, fs, ns, cs) + vts, fs = meshutil.create_ball((100,100,100), 1) + cnf.add_obj('ball', Mesh(vts, fs, colors=(1,0,0))) + cnf.add_obj('line', TextSet(texts=['TEXT'], verts=[(101,100,100)], size=256)) cnf.Show() def random_ball_test(): cnf = Canvas3DFrame(None) os = np.random.rand(30).reshape((-1,3)) - rs = np.random.rand(10)/5 - cs = (np.random.rand(10)*255).astype(np.uint8) - cs = geoutil.linear_color('jet')[cs]/255 - vts, fs, ns, cs = geoutil.build_balls(os, rs, cs) - cnf.add_surf('ball', vts, fs, ns, cs) + rs = np.random.rand(10)/7+0.05 + cs = np.random.rand(10) + vts_b, fs_b, cs_b = meshutil.create_balls(os, rs, cs) + cnf.add_obj('balls', Mesh(verts=vts_b, faces=fs_b, colors=cs_b, cmap='jet')) cnf.Show() def line_test(): @@ -48,9 +46,13 @@ def line_test(): y = np.sin(mu) * (1 + np.cos(n_long * mu / n_mer) * 0.5) z = np.sin(n_long * mu / n_mer) * 0.5 - vts, fs, ns, cs = geoutil.build_line(x, y, z, (1, 0, 0)) - cs[:] = geoutil.auto_lookup(vts[:,2], geoutil.linear_color('jet'))/255 - cnf.add_surf('ball', vts, fs, ns, cs, mode='grid') + vts = np.array([x, y, z]).T.astype(np.float32) + + fs = np.arange(len(x), dtype=np.uint32) + fs = np.array([fs[:-1], fs[1:]]).T + + # cs[:] = geoutil.auto_lookup(vts[:,2], geoutil.linear_color('jet'))/255 + cnf.add_obj('ball', Mesh(vts, fs, colors=vts[:,2], mode='grid', cmap='jet')) cnf.Show() def mesh_test(): @@ -62,62 +64,63 @@ def mesh_test(): x = r*np.sin(phi)*np.cos(theta) y = r*np.cos(phi) z = r*np.sin(phi)*np.sin(theta) - vts, fs, ns, cs = geoutil.build_mesh(x, y, z) - cs[:] = geoutil.auto_lookup(vts[:,2], geoutil.linear_color('jet'))/255 - cnf.add_surf('ball', vts, fs, ns, cs) + vts, fs = meshutil.create_grid_mesh(x, y, z) + mesh = Mesh(vts, fs.astype(np.uint32), vts[:,2], mode='grid', cmap='jet') + cnf.add_obj('ball', mesh) cnf.Show() def ball_ring_test(): cnf = Canvas3DFrame(None) os = np.random.rand(30).reshape((-1,3)) - rs = np.random.rand(10)/7 - cs = (np.random.rand(10)*255).astype(np.uint8) - cs = geoutil.linear_color('jet')[cs]/255 - - vts_b, fs_b, ns_b, cs_b = geoutil.build_balls(list(os), list(rs), list(cs)) - vts_l, fs_l, ns_l, cs_l = geoutil.build_line(os[:,0], os[:,1], os[:,2], list(cs)) - vts_c, fs_c, ns_c, cs_c = geoutil.build_cube((0,0,0), (1,1,1)) - cnf.add_surf('balls', vts_b, fs_b, ns_b, cs_b) - cnf.add_surf('line', vts_l, fs_l, ns_l, cs_l, mode='grid') - cnf.add_surf('box', vts_c, fs_c, ns_c, cs_c, mode='grid') + rs = np.random.rand(10)/7 + 0.05 + cs = np.random.rand(10) + vts_b, fs_b, cs_b = meshutil.create_balls(os, rs, cs) + cnf.add_obj('balls', Mesh(verts=vts_b, faces=fs_b, colors=cs_b, cmap='jet')) + + vts_l, fs_l = meshutil.create_line(*os.T) + cnf.add_obj('line', Mesh(verts=vts_l, faces=fs_l, colors=cs, cmap='jet', mode='grid')) + # vts_c, fs_c, ns_c, cs_c = geoutil.build_cube((0,0,0), (1,1,1)) + + vts_c, ls_c = meshutil.create_bound((0,0,0), (1,1,1), 3, 3, 3) + cnf.add_obj('box', Mesh(verts=vts_c, faces=ls_c, )) cnf.Show() def balls_mark_rest(): cnf = Canvas3DFrame(None) os = np.random.rand(30).reshape((-1,3)) - rs = np.random.rand(10)/7+0.01 - cs = (np.random.rand(10)*255).astype(np.uint8) - cs = surfutil.linear_color('jet')[cs]/255 - - vts_b, fs_b, ns_b, cs_b = surfutil.build_balls(os, rs, cs) + rs = np.random.rand(10)/7+0.05 + cs = np.random.rand(10) + vts_b, fs_b, cs_b = meshutil.create_balls(os, rs, cs) cont = ['ID:%s'%i for i in range(10)] - vtss, fss, pps, h, color = surfutil.build_marks(cont, os, rs, 0.05, (1,1,1)) - cnf.add_surf('balls', Surface(vts_b, fs_b, ns_b, cs_b)) - cnf.add_surf('line', MarkText(vtss, fss, pps, h, color)) + + # vtss, fss, pps, h, color = surfutil.build_marks(cont, os, rs, 0.05, (1,1,1)) + # cnf.add_obj('balls', Mesh(verts=vts_b.astype(np.float32), faces=fs_b.astype(np.uint32), colors=cs_b, cmap='jet')) + cnf.add_obj('line', TextSet(texts=a, verts=b, size=1600, colors=c)) cnf.Show() def surface2d_test(): cnf = Canvas3DFrame(None) x, y = np.ogrid[-2:2:20j, -2:2:20j] z = x * np.exp( - x**2 - y**2) - vts, fs, ns, cs = surfutil.build_surf2d(z, ds=1, k=20, sigma=2) - cs[:] = surfutil.auto_lookup(vts[:,2], surfutil.linear_color('jet'))/255 - dem = Surface(vts, fs, ns, cs) - cnf.add_surf('dem', dem) + + vts, fs = meshutil.create_surface2d(z, sample=1, k=10) + dem = Mesh(verts=vts, faces=fs.astype(np.uint32), colors=z.ravel(), cmap='jet') + cnf.add_obj('dem', dem) cnf.Show() def arrow_test(): cnf = Canvas3DFrame(None) v1, v2 = np.array([[[0,0,0],[5,5,5]],[[0,15,5],[2,8,3]]], dtype=np.float32) - vts, fs, ns, cs = geoutil.build_arrows(v1, v2, 1, 1, 1, 1, (1,0,0)) - cnf.add_surf('arrow', vts, fs, ns, cs) + vts, fs, ns, cs = meshutil.build_arrows(v1, v2, 1, 1, 1, 1, (1,0,0)) + # vts, fs = meshutil.create_arrow(15, 15) + cnf.add_obj('arrow', Mesh(vts, fs, colors=(1,0,0))) cnf.Show() def cube_test(): cnf = Canvas3DFrame(None) - vts, fs, ns, cs = geoutil.build_cube((0,0,0), (1,1,1)) - cnf.add_surf('box', vts, fs, ns, cs, mode='grid') + vts, fs, ls = meshutil.create_cube() + cnf.add_obj('box', Mesh(vts, ls, colors=(1,0,0), mode='grid')) cnf.Show() def cube_surf_test(): @@ -131,32 +134,38 @@ def cube_surf_test(): cnf.add_surf('box', vts, fs, ns, cs, mode='grid') cnf.Show() +def isosurface_test(): + cnf = Canvas3DFrame(None) + cube = np.zeros((100,100,100), dtype=np.float32) + x,y,z = np.random.randint(10,90,900).reshape(3,-1) + cube[x,y,z] = 1000 + surf3d = Surface3d(cube, level=1.5, sigma=3, step=2, colors=(1,0,0)) + cnf.add_obj('volume', surf3d) + cnf.Show() + def volume_test(): cnf = Canvas3DFrame(None) cube = np.zeros((100,100,100), dtype=np.float32) x,y,z = np.random.randint(10,90,900).reshape(3,-1) cube[x,y,z] = 1000 - cube = ndimg.gaussian_filter(cube, 3) - vts, fs, ns, vs = geoutil.build_surf3d(cube, 1, 2) - cnf.add_surf('volume', vts, fs, ns, (1,0,0)) + surf3d = Volume3d(cube, level=1.5, step=2, cmap='gray') + cnf.add_obj('volume', surf3d) cnf.Show() if __name__ == '__main__': app = wx.App() - ''' - balls_mark_rest() - dem_test() - ball_test() - random_ball_test() - line_test() - mesh_test() - ball_ring_test() - balls_mark_rest() - surface2d_test() - arrow_test() - cube_test() - cube_surf_test() + # balls_mark_rest() + # dem_test() + # ball_test() + # random_ball_test() + # line_test() + # mesh_test() + # ball_ring_test() + # balls_mark_rest() + # surface2d_test() + # arrow_test() # bad + # cube_test() + # cube_surf_test() # bad + # isosurface_test() volume_test() - ''' - dem_test() app.MainLoop() diff --git a/sciwx/mesh/__init__.py b/sciwx/mesh/__init__.py index a9007f9b..440e18bd 100644 --- a/sciwx/mesh/__init__.py +++ b/sciwx/mesh/__init__.py @@ -1,4 +1,4 @@ from .canvas import Canvas3D from .mcanvas import MCanvas3D from .widget import Canvas3DFrame, Canvas3DNoteBook, Canvas3DNoteFrame -from .scene import Surface, MarkText, MeshSet \ No newline at end of file +# from .scene import Surface, MarkText, MeshSet \ No newline at end of file diff --git a/sciwx/mesh/canvas.py b/sciwx/mesh/canvas.py index 162b0d6c..4d1c6174 100644 --- a/sciwx/mesh/canvas.py +++ b/sciwx/mesh/canvas.py @@ -1,178 +1,235 @@ -import sys, platform -import moderngl +import wx, weakref import numpy as np -import wx, math -import wx.glcanvas as glcanvas -from .scene import Scene -import os.path as osp -from pubsub import pub -from sciapp.util.surfutil import * - -class Canvas3D(glcanvas.GLCanvas): - def __init__(self, parent, scene=None): - attribList = (glcanvas.WX_GL_CORE_PROFILE, glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24) - glcanvas.GLCanvas.__init__(self, parent, -1, attribList=attribList[platform.system() == 'Windows':]) - self.init = False - self.context = glcanvas.GLContext(self) - self.SetBackgroundStyle(wx.BG_STYLE_PAINT) - self.scene = self.scene = Scene() if scene is None else scene - self.size = None - - self.SetBackgroundStyle(wx.BG_STYLE_PAINT) - - self.Bind(wx.EVT_SIZE, self.OnSize) - self.Bind(wx.EVT_PAINT, self.OnPaint) - self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseDown) - self.Bind(wx.EVT_LEFT_UP, self.OnMouseUp) - self.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseDown) - self.Bind(wx.EVT_RIGHT_UP, self.OnMouseUp) - self.Bind(wx.EVT_MOTION, self.OnMouseMotion) - self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel) - self.Bind(wx.EVT_IDLE, self.OnIdle) - self.lastx, self.lasty = None, None - #self.update() - #print('init===========') - pub.subscribe(self.add_surf, 'add_surf') - # pub.subscribe(self.add_mark, 'add_mark') - - def OnIdle(self, event): - if sum([i.update for i in self.scene.objs.values()])>0: - self.Refresh(False) - - def InitGL(self): - self.scene.on_ctx(moderngl.create_context()) - self.DoSetViewport() - self.scene.reset() - - def OnDraw(self): - self.scene.set_viewport(0, 0, self.Size.width, self.Size.height) - #self.meshset.count_mvp() - self.scene.draw() - self.SwapBuffers() - - def OnSize(self, event): - self.scene.set_pers() - self.Refresh(False) - - def DoSetViewport(self): - size = self.size = self.GetClientSize() - self.SetCurrent(self.context) - if not self.scene is None and not self.scene.ctx is None: - self.scene.set_viewport(0, 0, self.Size.width, self.Size.height) - - def OnPaint(self, event): - self.SetCurrent(self.context) - #print(self, '=====', self.init) - if not self.init: - self.InitGL() - self.init = True - self.OnDraw() - - def OnMouseDown(self, evt): - self.CaptureMouse() - self.lastx, self.lasty = evt.GetPosition() - - def OnMouseUp(self, evt): - self.ReleaseMouse() - - def OnMouseMotion(self, evt): - self.SetFocus() - if evt.Dragging() and evt.LeftIsDown(): - x, y = evt.GetPosition() - dx, dy = x-self.lastx, y-self.lasty - self.lastx, self.lasty = x, y - angx = self.scene.angx - dx/200 - angy = self.scene.angy + dy/200 - self.scene.set_pers(angx=angx, angy=angy) - self.Refresh(False) - if evt.Dragging() and evt.RightIsDown(): - light = self.scene.light - x, y = evt.GetPosition() - dx, dy = x-self.lastx, y-self.lasty - self.lastx, self.lasty = x, y - angx, angy = dx/200, dy/200 - vx, vy, vz = self.scene.light - ay = math.asin(vz/math.sqrt(vx**2+vy**2+vz**2))-angy - xx = math.cos(angx)*vx - math.sin(angx)*vy - yy = math.sin(angx)*vx + math.cos(angx)*vy - ay = max(min(math.pi/2-1e-4, ay), -math.pi/2+1e-4) - zz, k = math.sin(ay), math.cos(ay)/math.sqrt(vx**2+vy**2) - self.scene.set_light((xx*k, yy*k, zz)) - self.Refresh(False) - - def save_bitmap(self, path): - context = wx.ClientDC( self ) - memory = wx.MemoryDC( ) - x, y = self.ClientSize - bitmap = wx.Bitmap( x, y, -1 ) - memory.SelectObject( bitmap ) - memory.Blit( 0, 0, x, y, context, 0, 0) - memory.SelectObject( wx.NullBitmap) - bitmap.SaveFile( path, wx.BITMAP_TYPE_PNG ) - - def save_stl(self, path): - from stl import mesh - objs = [i for i in self.scene.objs.values() if i.visible] - vers = [i.vts[i.ids] for i in objs if isinstance(i, Surface)] - vers = np.vstack(vers) - model = mesh.Mesh(np.zeros(vers.shape[0], dtype=mesh.Mesh.dtype)) - model.vectors = vers - model.save(path) - - def OnMouseWheel(self, evt): - k = 0.9 if evt.GetWheelRotation()>0 else 1/0.9 - self.scene.set_pers(l=self.scene.l*k) - self.Refresh(False) - #self.update() - - def set_mesh(self, mesh): - self.scene.set_mesh(mesh) - self.Refresh() - - def view_x(self, evt): - self.scene.reset(angx=0) - self.Refresh(False) - - def view_y(self, evt): - self.scene.reset(angx=pi/2) - self.Refresh(False) - - def view_z(self, evt): - self.scene.reset(angy=pi/2-1e-4) - self.Refresh(False) - - def set_pers(self, b): - self.scene.set_pers(pers=b) - self.Refresh(False) - - def set_background(self, c): - self.scene.set_background(c) - self.Refresh(False) - - def set_scatter(self, scatter): - self.scene.set_bright_scatter(scatter=scatter) - self.Refresh(False) - - def set_bright(self, bright): - self.scene.set_bright_scatter(bright=bright) - self.Refresh(False) - - def get_obj(self, name): - return self.scene.get_obj(name) - - def add_surf_asyn(self, name, obj): - wx.CallAfter(pub.sendMessage, 'add_surf', obj=obj) - - def add_surf(self, name, obj): - surf = self.scene.add_surf(name, obj) - if len(self.scene.objs)==1: - self.scene.reset() - self.Refresh(False) - -if __name__ == '__main__': - app = wx.App(False) - frm = wx.Frame(None, title='GLCanvas Sample') - canvas = Canvas3D(frm) - - frm.Show() - app.MainLoop() +from vispy import app, scene, gloo +import platform, os.path as osp +from vispy.visuals.transforms import STTransform +from vispy.color import Colormap +from sciapp.object import Mesh, TextSet, Volume3d, Scene +from sciapp.action import MeshTool + +# verts, faces, colors, mode, alpha, visible +# light_dir, light, ambiass, background + +class MeshVisual(scene.visuals.Mesh): + def __init__(self, *p, **key): + scene.visuals.Mesh.__init__(self, *p, **key) + self.unfreeze() + self._light_color = (0.7,0.7,0.7, 1.0) + self.freeze() + + @property + def light_color(self): + return self._light_color + + @light_color.setter + def light_color(self, light): + self._light_color = light + self.mesh_data_changed() + + def _update_data(self): + rst = scene.visuals.Mesh._update_data(self) + if self.shading is not None: + self.shared_program.vert['light_color'] = self._light_color + return rst + +class VolumeVisual(scene.visuals.Volume): + def set_data(self, vol, clim=None, copy=True): + scene.visuals.Volume.set_data(self, vol.transpose(2,1,0), clim, copy) + + def _compute_bounds(self, axis, view): + return 0, self._vol_shape[::-1][axis] + +def viewmesh(mesh, view): + if mesh.dirty == 'geom': + faces = mesh.faces + if mesh.mode == 'grid': faces = mesh.get_edges() + if mesh.mode == 'points': faces = np.arange(len(mesh.verts)) + shading = 'smooth' if mesh.mode == 'mesh' else None + if view is None or view.shading!=shading: + if not view is None: view.parent = None + view = MeshVisual(shading=shading) + dic = {'mesh':'triangles', 'grid':'lines', 'points':'points'} + view._draw_mode = dic[mesh.mode] + key = {'vertices':mesh.verts, 'faces':faces} + if isinstance(mesh.colors, tuple): colorkey = 'color' + elif mesh.colors.ndim == 2: colorkey = 'vertex_colors' + elif mesh.colors.ndim == 1: colorkey = 'vertex_values' + + key[colorkey] = mesh.colors + view.set_data(**key) + + view.interactive = True + view.shininess = 0 + if isinstance(mesh.cmap, str): cmap = mesh.cmap + elif mesh.cmap.max()>1+1e-5: cmap = Colormap(mesh.cmap/255) + else: cmap = Colormap(mesh.cmap) + view.cmap = cmap + view.visible = mesh.visible + view.opacity = mesh.alpha + if mesh.high_light is False: + view._picking_filter.enabled = False + view._picking_filter.id = view._id + else: + view._picking_filter.enabled = True + view._picking_filter.id = mesh.high_light + # view.shading = 'flat' + mesh.dirty = False + return view + +def viewtext(text, view): + if text.dirty=='geom': + if view is None: + view = scene.visuals.Text() + + view.text = text.texts + view.pos = text.verts + view.color = text.colors + view.font_size = text.size + + view.interactive = True + view.visible = text.visible + view.opacity = text.alpha + if text.high_light is False: + view._picking_filter.enabled = False + view._picking_filter.id = view._id + else: + view._picking_filter.enabled = True + view._picking_filter.id = mesh.high_light + # view.shading = 'flat' + text.dirty = False + return view + +def viewvolume(vol, view): + if isinstance(vol.cmap, str): cmap = vol.cmap + elif vol.cmap.max()>1+1e-5: cmap = Colormap(vol.cmap/255) + else: cmap = Colormap(vol.cmap) + if vol.dirty=='geom': + if view is None: + view = VolumeVisual(vol.imgs, emulate_texture=False, cmap=cmap) + view.relative_step_size = vol.step + view.threshold = vol.level + + view.interactive = True + view.visible = vol.visible + view.opacity = vol.alpha + if vol.high_light is False: + view._picking_filter.enabled = False + view._picking_filter.id = view._id + else: + view._picking_filter.enabled = True + view._picking_filter.id = mesh.high_light + # view.shading = 'flat' + vol.dirty = False + return view + + +def viewobj(obj, view): + if isinstance(obj, Mesh): return viewmesh(obj, view) + if isinstance(obj, TextSet): return viewtext(obj, view) + if isinstance(obj, Volume3d): return viewvolume(obj, view) + +class Canvas3D(scene.SceneCanvas): + def __new__(cls, parent, scene3d=None): + self = super().__new__(cls) + scene.SceneCanvas.__init__(self, app="wx", parent=parent, keys='interactive', show=True, dpi=150) + canvas = parent.GetChildren()[-1] + self.unfreeze() + self.canvas = weakref.ref(canvas) + self.view = self.central_widget.add_view() + self.set_scene(scene3d or Scene()) + self.visuals = {} + self.curobj = None + self.freeze() + canvas.Bind(wx.EVT_IDLE, self.on_idle) + canvas.tool = None + canvas.camera = scene.cameras.TurntableCamera(parent=self.view.scene, fov=45, name='Turntable') + canvas.set_camera = self.set_camera + canvas.fit = lambda : self.set_camera(auto=True) + canvas.at = self.at + self.view.camera = canvas.camera + return canvas + + def __init__(self, **kwargs): pass + + def set_scene(self, scene): + self.scene3d = scene + self.canvas().scene3d = self.scene3d + self.canvas().add_obj = self.scene3d.add_obj + + def on_idle(self, event): + need = 'ignore' + if self.scene3d.dirty: + need = 'update' + self.bgcolor = self.scene3d.bg_color + for i in self.visuals: + self.visuals[i].ambient_light_color = self.scene3d.ambient_color + self.visuals[i].light_color = self.scene3d.light_color + self.visuals[i].light_dir = self.scene3d.light_dir + self.scene3d.dirty = False + for i in self.scene3d.names: + obj = self.scene3d.objects[i] + if not i in self.visuals: + self.visuals[i] = viewobj(obj, None) + need = 'add' + vis = self.visuals[i] + if obj.dirty != False: + self.visuals[i] = viewobj(obj, vis) + if need=='ignore': need = 'update' + self.visuals[i].parent = self.view.scene + if need == 'add': self.canvas().fit() + if need != 'ignore': + print('need') + self.update() + + def set_camera(self, azimuth=None, elevation=None, dist=None, fov=None, auto=False): + if not azimuth is None: self.canvas().camera.azimuth = azimuth + if not elevation is None: self.canvas().camera.elevation = elevation + if not fov is None: self.canvas().camera.fov = fov + if auto: self.canvas().camera.set_range() + + def at(self, x, y): + self.view.interactive = False + vis = self.visual_at((x, y)) + self.view.interactive = True + for k in self.visuals: + if self.visuals[k] is vis: + return self.scene3d.objects[k] + return None + + def _process_mouse_event(self, event): + # self.measure_fps() + # return scene.SceneCanvas._process_mouse_event(self, event) + px, py = x, y = tuple(event.pos) + canvas, tool, btn = self.canvas(), self.canvas().tool or MeshTool.default, event._button + btn = {2:3, 3:2}.get(btn, btn) + ld, rd, md = [i in event.buttons for i in (1,2,3)] + sta = [i in [j.name for j in event.modifiers] for i in ('Alt', 'Control', 'Shift')] + others = {'alt':sta[0], 'ctrl':sta[1], + 'shift':sta[2], 'px':px, 'py':py, 'canvas':canvas} + + if event.type == 'mouse_press': + canvas.SetFocus() + tool.mouse_down(canvas.scene3d, x, y, btn, **others) + if event.type == 'mouse_release': + tool.mouse_up(canvas.scene3d, x, y, btn, **others) + if event.type == 'mouse_move': + tool.mouse_move(canvas.scene3d, x, y, None, **others) + + wheel = np.sign(event.delta[1]) + #if me.Dragging(): + # tool.mouse_move(canvas.scene3d, x, y, btn, **others) + if wheel!=0: + tool.mouse_wheel(canvas.scene3d, x, y, wheel, **others) + ckey = {'arrow':1,'cross':5,'hand':6} + cursor = ckey[tool.cursor] if tool.cursor in ckey else 1 + canvas.SetCursor(wx.Cursor(cursor)) + event.handled = True + + def __del__(self): + # self.img = self.back = None + print('========== canvas del') + +def make_bitmap(bmp): + img = bmp.ConvertToImage() + img.Resize((20, 20), (2, 2)) + return img.ConvertToBitmap() diff --git a/sciwx/mesh/matutil.py b/sciwx/mesh/matutil.py deleted file mode 100644 index 0743bcca..00000000 --- a/sciwx/mesh/matutil.py +++ /dev/null @@ -1,49 +0,0 @@ -import numpy as np - -def look_at(eye, target, up, dtype=None): - forward = (target - eye)/np.linalg.norm(target - eye) - side = (np.cross(forward, up))/np.linalg.norm(np.cross(forward, up)) - up = (np.cross(side, forward)/np.linalg.norm(np.cross(side, forward))) - - return np.array(( - (side[0], up[0], -forward[0], 0.), - (side[1], up[1], -forward[1], 0.), - (side[2], up[2], -forward[2], 0.), - (-np.dot(side, eye), -np.dot(up, eye), np.dot(forward, eye), 1.0) - ), dtype=np.float32) - -def perspective(xmax, ymax, near, far): - left, right = -xmax, xmax - bottom, top = -ymax, ymax - - A = (right + left) / (right - left) - B = (top + bottom) / (top - bottom) - C = -(far + near) / (far - near) - D = -2. * far * near / (far - near) - E = 2. * near / (right - left) - F = 2. * near / (top - bottom) - return np.array(( - ( E, 0., 0., 0.), - ( 0., F, 0., 0.), - ( A, B, C,-1.), - ( 0., 0., D, 0.), - ), dtype=np.float32) - -def orthogonal(xmax, ymax, near, far): - rml = xmax * 2 - tmb = ymax * 2 - fmn = far - near - - A = 2. / rml - B = 2. / tmb - C = -2. / fmn - Tx = 0 - Ty = 0 - Tz = -(far + near) / fmn - - return np.array(( - ( A, 0., 0., 0.), - (0., B, 0., 0.), - (0., 0., C, 0.), - (Tx, Ty, Tz, 1.), - ), dtype=np.float32) \ No newline at end of file diff --git a/sciwx/mesh/mcanvas.py b/sciwx/mesh/mcanvas.py index 8230e478..7a9b62bc 100644 --- a/sciwx/mesh/mcanvas.py +++ b/sciwx/mesh/mcanvas.py @@ -1,11 +1,6 @@ -import wx -import os.path as osp -import platform, math -from pubsub import pub from .canvas import Canvas3D -from sciapp.util import count_ns +import wx, os.path as osp, platform import numpy as np -import math def make_bitmap(bmp): img = bmp.ConvertToImage() @@ -85,7 +80,7 @@ def __init__( self, parent, scene=None): self.m_staticText2.Wrap( -1 ) ssizer.Add( self.m_staticText2, 0, wx.ALIGN_CENTER|wx.LEFT, 10 ) - cho_objChoices = ['mesh', 'grid'] + cho_objChoices = ['mesh', 'grid', 'points'] self.cho_mode = wx.Choice( self.settingbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, cho_objChoices, 0 ) self.cho_mode.SetSelection( 0 ) ssizer.Add( self.cho_mode, 0, wx.ALIGN_CENTER|wx.ALL, 1 ) @@ -98,23 +93,23 @@ def __init__( self, parent, scene=None): self.Layout() self.Centre( wx.BOTH ) - self.view_x = self.canvas.view_x - self.view_y = self.canvas.view_y - self.view_z = self.canvas.view_z - self.set_pers = self.canvas.set_pers - self.set_background = self.canvas.set_background - self.set_scatter = self.canvas.set_scatter - self.set_bright = self.canvas.set_bright - - self.on_bgcolor = lambda e: self.canvas.set_background(np.array(e.GetColour()[:3])/255) - self.on_bg = lambda e: self.canvas.set_scatter((3-e.GetSelection())/3) - self.on_light = lambda e: self.canvas.set_bright((3-e.GetSelection())/3) + self.view_x = lambda e: self.canvas.set_camera(azimuth=0, elevation=0) + self.view_y = lambda e: self.canvas.set_camera(azimuth=90, elevation=0) + self.view_z = lambda e: self.canvas.set_camera(azimuth=0, elevation=90) + self.set_pers = lambda s: self.canvas.set_camera(fov=[0, 45][s]) + #self.set_background = self.canvas.set_background + #self.set_scatter = self.canvas.set_scatter + #self.set_bright = self.canvas.set_bright + + self.on_bgcolor = lambda e: self.canvas.scene3d.set_style(bg_color=tuple(np.array(e.GetColour()[:3])/255)) + self.on_bg = lambda e: self.canvas.scene3d.set_style(ambient_color=((3-e.GetSelection())/3,)*3+(1,)) + self.on_light = lambda e: self.canvas.scene3d.set_style(light_color=((3-e.GetSelection())/3,)*3+(1,)) self.btn_x.Bind( wx.EVT_BUTTON, self.view_x) self.btn_y.Bind( wx.EVT_BUTTON, self.view_y) self.btn_z.Bind( wx.EVT_BUTTON, self.view_z) - self.btn_open.Bind( wx.EVT_BUTTON, self.on_open) - self.btn_stl.Bind( wx.EVT_BUTTON, self.on_stl) + #self.btn_open.Bind( wx.EVT_BUTTON, self.on_open) + #self.btn_stl.Bind( wx.EVT_BUTTON, self.on_stl) self.btn_pers.Bind( wx.EVT_BUTTON, lambda evt, f=self.set_pers:f(True)) self.btn_orth.Bind( wx.EVT_BUTTON, lambda evt, f=self.set_pers:f(False)) self.btn_color.Bind( wx.EVT_COLOURPICKER_CHANGED, self.on_bgcolor ) @@ -127,17 +122,23 @@ def __init__( self, parent, scene=None): self.sli_alpha.Bind( wx.EVT_SCROLL, self.on_alpha ) self.col_color.Bind( wx.EVT_COLOURPICKER_CHANGED, self.on_color ) - if scene!=None: self.cho_obj.Set(list(scene.objs.keys())) + self.Bind(wx.EVT_IDLE, self.on_idle) + + self.cho_obj.Set(list(self.canvas.scene3d.names)) + def on_idle(self, event): + if set(self.canvas.scene3d.names) != set(self.cho_obj.Items): + self.cho_obj.Set(list(self.canvas.scene3d.names)) + @property - def name(self): return self.canvas.scene.meshset.name + def name(self): return self.canvas.scene3d.name def set_mesh(self, mesh): self.canvas.set_mesh(mesh) self.cho_obj.Set(list(mesh.objs.keys())) @property - def mesh(self): return self.canvas.scene.meshset + def scene3d(self): return self.canvas.scene3d def on_save(self, evt): dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} @@ -174,42 +175,75 @@ def on_open(self, evt): dialog.Destroy() def get_obj(self, name): - return self.canvas.scene.meshset.get_obj(name) + return self.canvas.scene3d.get_obj(name) def set_style(self, name, **key): self.get_obj(name).set_style(**key) self.canvas.Refresh() def on_visible(self, evt): - self.curobj.set_style(visible=evt.IsChecked()) - self.canvas.Refresh(False) + self.curobj.set_data(visible=evt.IsChecked()) + # self.canvas.Refresh(False) def on_alpha(self, evt): - self.curobj.set_style(alpha=evt.GetInt()/10.0) - self.canvas.Refresh(False) + self.curobj.set_data(alpha=evt.GetInt()/10.0) + # self.canvas.Refresh(False) def on_mode(self, evt): - self.curobj.set_style(mode=evt.GetString()) - self.canvas.Refresh(False) + self.curobj.set_data(mode=evt.GetString()) + # self.canvas.Refresh(False) def on_color(self, evt): c = tuple(np.array(evt.GetColour()[:3])/255) - self.curobj.set_style(color = c) - self.canvas.Refresh(False) + self.curobj.set_data(colors = c) + # self.canvas.Refresh(False) def on_select(self, evt): n = self.cho_obj.GetSelection() self.curobj = self.get_obj(self.cho_obj.GetString(n)) + + ''' self.chk_visible.SetValue(self.curobj.visible) color = (np.array(self.curobj.color)*255).astype(np.uint8) self.col_color.SetColour((tuple(color))) self.sli_alpha.SetValue(int(self.curobj.alpha*10)) self.cho_mode.SetSelection(['mesh', 'grid'].index(self.curobj.mode)) + ''' def add_surf_asyn(self, name, obj): self.canvas.add_surf_asyn(name, obj) self.cho_obj.Append(name) - def add_surf(self, name, obj): - self.canvas.add_surf(name, obj) - self.cho_obj.Append(name) \ No newline at end of file + def add_obj(self, name, obj): + self.canvas.scene3d.add_obj(name, obj) + self.cho_obj.Append(name) + +''' +from mesh import Mesh +import numpy as np + +colors = np.random.rand(4,4); colors[:,3] = 1 + +slz = Mesh(verts=np.array([(0,0,0),(1,1,0),(1,0,1),(0,1,1)], dtype='float32'), + faces=np.array([(0,1,2),(0,2,3),(0,1,3),(1,2,3)], dtype='int32'), + colors = colors, mode='mesh') + +from geom import create_sphere +verts, faces = create_sphere(16, 16, 16) + +ball1 = Mesh(verts=verts, faces=faces, colors=verts, mode='mesh', alpha=1) +ball2 = Mesh(verts=verts+(1,0,0), faces=faces, colors=(1,1,1), mode='mesh', alpha=1) + +class MdCanvas(wx.Frame): + def __init__(self, size=(800, 600), title='wx MdCanvas'): + wx.Frame.__init__(self, None, -1, title, wx.DefaultPosition, size=size) + canvas = MCanvas3D(self) + canvas.add_obj('ball1', ball1) + canvas.add_obj('ball2', ball2) +''' + +if __name__ == '__main__': + myapp = wx.App(0) + frame = MdCanvas() + frame.Show(True) + myapp.MainLoop() \ No newline at end of file diff --git a/sciwx/mesh/scene.py b/sciwx/mesh/scene.py deleted file mode 100644 index b5e6a2ed..00000000 --- a/sciwx/mesh/scene.py +++ /dev/null @@ -1,225 +0,0 @@ -import numpy as np -import moderngl -from .matutil import * -import numpy as np -from math import sin, cos, tan, pi -from sciapp.object import Surface, MarkText, MeshSet - -class Scene: - def __init__(self): - self.title = 'Scene' - self.h, self.v, self.r = 1.5, 0, 300 - self.ratio, self.dial = 1.0, 1.0 - self.pers, self.center = True, (0,0,0) - self.angx = self.angy = 0 - self.fovy = self.l = 1 - self.background = 0.4, 0.4, 0.4 - self.light = (1,0,0) - self.bright, self.scatter = 0.66, 0.66 - self.meshset = MeshSet() - self.vabo = {} - self.ctx = None - - def set_mesh(self, mesh): - self.meshset = mesh - self.vabo = {} - for i in self.objs: - self.add_surf(i, self.objs[i]) - - def on_ctx(self, ctx): - self.ctx = ctx - self.prog_suf = self.ctx.program( - vertex_shader=''' - #version 330 - uniform mat4 Mvp; - in vec3 v_vert; - in vec3 v_norm; - in vec3 v_color; - out vec3 f_norm; - out vec3 f_color; - void main() { - gl_Position = Mvp * vec4(v_vert, 1); - f_norm = v_norm; - f_color = v_color; - } - ''', - fragment_shader=''' - #version 330 - uniform vec3 light; - uniform float blend; - uniform float scatter; - uniform float bright; - in vec3 f_norm; - in vec3 f_color; - out vec4 color; - void main() { - float d = clamp(dot(light, f_norm)*bright+scatter, 0, 1); - color = vec4(f_color*d, blend); - } - ''' - ) - - self.prog_txt = self.ctx.program( - vertex_shader=''' - #version 330 - uniform mat4 mv; - uniform mat4 proj; - uniform float h; - in vec3 v_vert; - in vec3 v_pos; - void main() { - vec4 o = mv * vec4(v_pos, 1); - gl_Position = proj *(o + vec4(v_vert.x*h, v_vert.y*h, v_vert.z, 0)); - } - ''', - fragment_shader=''' - #version 330 - uniform vec3 f_color; - out vec4 color; - void main() { - color = vec4(f_color, 1); - } - ''') - for i in self.objs: - if isinstance(self.objs[i], MarkText): - self.ctx_txt(self.ctx, self.prog_txt, self.objs[i], i) - elif isinstance(self.objs[i], Surface): - self.ctx_obj(self.ctx, self.prog_suf, self.objs[i], i) - - @property - def objs(self): return self.meshset.objs - - def ctx_obj(self, ctx, prog, obj, name): - vts, ids, ns, cs = obj.vts, obj.ids, obj.ns, obj.cs - buf = np.zeros((len(vts), 9), dtype=np.float32) - buf[:,0:3], buf[:,3:6], buf[:,6:9] = vts, ns, cs - vbo = ctx.buffer(buf.tobytes()) - ibo = ctx.buffer(ids.tobytes()) - content = [(vbo, '3f 3f 3f', 'v_vert', 'v_norm', 'v_color')] - vao = ctx.vertex_array(prog, content, ibo) - self.vabo[name] = [buf, vao, vbo] - - def ctx_txt(self, ctx, prog, obj, name): - vts, ids, os, cs = obj.vts, obj.ids, obj.os, obj.cs - buf = self.buf = np.zeros((len(vts), 6), dtype=np.float32) - buf[:,0:3], buf[:,3:6] = vts, os - vbo = ctx.buffer(buf.tobytes()) - ibo = ctx.buffer(ids.tobytes()) - content = [(vbo, '3f 3f', 'v_vert', 'v_pos')] - vao = ctx.vertex_array(prog, content, ibo) - self.vabo[name] = [buf, vao, vbo] - - def add_surf(self, name, obj): - if isinstance(obj, tuple): - if isinstance(obj[3], (int, float)): - obj = MarkText(*obj) - else: obj = Surface(*obj) - if not self.ctx is None: - if isinstance(obj, MarkText): - self.ctx_txt(self.ctx, self.prog_txt, obj, name) - elif isinstance(obj, Surface): - self.ctx_obj(self.ctx, self.prog_suf, obj, name) - - self.objs[name] = obj - self.count_box() - - def get_obj(self, key): return self.meshset.get_obj(key) - - def draw_obj(self, name, obj, ctx, prog, mvp, light, bright, scatter): - if not obj.visible: return - ctx.line_width = obj.width - mvp = np.dot(*mvp) - prog['Mvp'].write(mvp.astype(np.float32).tobytes()) - prog['blend'].value = obj.alpha - prog['scatter'].value = scatter - prog['light'].value = tuple(light) - prog['bright'].value = bright - mode = {'mesh':moderngl.TRIANGLES, 'grid':moderngl.LINES}[obj.mode] - if obj.update: - self.vabo[name][0][:,6:9] = obj.cs - self.vabo[name][0][:,:3] = obj.vts - self.vabo[name][2].write(self.vabo[name][0].tobytes()) - obj.update = False - self.vabo[name][1].render(mode) - - def draw_txt(self, name, obj, ctx, prog, mvp, light, bright, scatter): - if not obj.visible: return - ctx.line_width = obj.width - prog['mv'].write(mvp[0].astype(np.float32).tobytes()) - prog['proj'].write(mvp[1].astype(np.float32).tobytes()) - prog['f_color'].write(np.array(obj.cs).astype(np.float32).tobytes()) - prog['h'].value = obj.h - self.vabo[name][1].render(moderngl.LINES) - - def draw(self): - self.ctx.clear(*self.background) - self.ctx.enable(moderngl.DEPTH_TEST) - #self.ctx.enable(ModernGL.CULL_FACE) - self.ctx.enable(moderngl.BLEND) - for i in self.objs: - prog = self.prog_txt if isinstance(self.objs[i], MarkText) else self.prog_suf - draw = self.draw_txt if isinstance(self.objs[i], MarkText) else self.draw_obj - draw(i, self.objs[i], self.ctx, prog, self.mvp, self.light, self.bright, self.scatter) - - def count_box(self): - minb = [i.box[0] for i in self.objs.values() if not i.box is None] - minb = np.array(minb).min(axis=0) - maxb = [i.box[1] for i in self.objs.values() if not i.box is None] - maxb = np.array(maxb).max(axis=0) - self.box = np.vstack((minb, maxb)) - #print(self.box) - self.center = self.box.mean(axis=0) - self.dial = np.linalg.norm(self.box[1]-self.box[0]) - - def count_mvp(self): - #print('mvp') - ymax = (1.0 if self.pers else self.l) * np.tan(self.fovy * np.pi / 360.0) - xmax = ymax * self.ratio - proj = (perspective if self.pers else orthogonal)(xmax, ymax, 1.0, 100000) - lookat = look_at(self.eye, self.center, (0.0,0.0,1.0)) - self.mvp = (lookat, proj) - - def set_viewport(self, x, y, width, height): - self.ctx.viewport = (x, y, width, height) - self.ratio = width*1.0/height - - def set_background(self, rgb): self.background = rgb - - def set_light(self, light): self.light = light - - def set_bright_scatter(self, bright=None, scatter=None): - if not bright is None: self.bright = bright - if not scatter is None: self.scatter = scatter - - def reset(self, fovy=45, angx=0, angy=0): - self.fovy, self.angx, self.angy = fovy, angx, angy - self.l = self.dial/2/(tan(fovy*pi/360)) - v = np.array([cos(angy)*cos(angx), cos(angy)*sin(angx), sin(angy)]) - self.eye = self.center + v*self.l*1 - self.count_mvp() - #print('reset', self.eye, self.center) - - def set_pers(self, fovy=None, angx=None, angy=None, l=None, pers=None): - if not pers is None: self.pers = pers - if not fovy is None: self.fovy = fovy - if not angx is None: self.angx = angx - if not angy is None: self.angy = angy - self.angx %= 2*pi - self.angy = max(min(pi/2-1e-4, self.angy), -pi/2+1e-4) - if not l is None: self.l = l - v = np.array([cos(self.angy)*cos(self.angx), - cos(self.angy)*sin(self.angx), sin(self.angy)]) - self.eye = self.center + v*self.l*1 - self.count_mvp() - - def show(self, title='Myvi'): - import wx - from .frame3d import Frame3D - app = wx.App(False) - self.locale = wx.Locale(wx.LANGUAGE_ENGLISH) - Frame3D(None, title, self).Show() - app.MainLoop() - -if __name__ == '__main__': - img = imread('gis.png') - build_surf2d(img) \ No newline at end of file diff --git a/sciwx/mesh/widget.py b/sciwx/mesh/widget.py index 5cfbc2a8..78d28b25 100644 --- a/sciwx/mesh/widget.py +++ b/sciwx/mesh/widget.py @@ -2,18 +2,19 @@ from .mcanvas import MCanvas3D class Canvas3DFrame(wx.Frame): - def __init__(self, parent=None): + def __init__(self, parent=None, scene=None): wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = 'Canvas3DFrame', pos = wx.DefaultPosition, size = wx.Size( 800, 600 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) sizer = wx.BoxSizer(wx.VERTICAL) - self.canvas = MCanvas3D(self) + self.canvas = MCanvas3D(self, scene) sizer.Add(self.canvas, 1, wx.EXPAND|wx.ALL, 0) self.SetSizer(sizer) self.Bind(wx.EVT_IDLE, self.on_idle) - + self.add_obj = self.canvas.add_obj + ''' self.view_x = self.canvas.view_x self.view_y = self.canvas.view_y self.view_z = self.canvas.view_z @@ -24,10 +25,11 @@ def __init__(self, parent=None): self.add_surf_asyn = self.canvas.add_surf_asyn self.add_surf = self.canvas.add_surf self.set_mesh = self.canvas.set_mesh + ''' def on_idle(self, event): - if self.GetTitle()!=self.canvas.mesh.name: - self.SetTitle(self.canvas.mesh.name) + if self.GetTitle()!=self.canvas.scene3d.name: + self.SetTitle(self.canvas.scene3d.name) def set_title(self, tab): self.SetTitle(tab.title) @@ -46,7 +48,7 @@ def __init__(self, parent): def on_idle(self, event): for i in range(self.GetPageCount()): - title = self.GetPage(i).mesh.name + title = self.GetPage(i).scene3d.name if self.GetPageText(i) != title: self.SetPageText(i, title) @@ -57,9 +59,9 @@ def canvas(self, i=None): def set_background(self, img): self.GetAuiManager().SetArtProvider(ImgArtabtProvider(img)) - def add_canvas(self, canvas=None): - if canvas is None: canvas = MCanvas3D(self) - self.AddPage(canvas, 'Image', True, wx.NullBitmap ) + def add_canvas(self, scene=None): + canvas = MCanvas3D(self, scene) + self.AddPage(canvas, 'Mesh', True, wx.NullBitmap ) return canvas def set_title(self, panel, title): From 0880590536923106fe0c0d5d2c7ffe489631376f Mon Sep 17 00:00:00 2001 From: yxdragon Date: Fri, 11 Jun 2021 12:34:32 +0800 Subject: [PATCH 340/343] vispy --- imagepy/app/imagepy.py | 2 +- sciapp/action/plugin/mesh_tools.py | 6 ++++-- sciwx/mesh/canvas.py | 5 +++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/imagepy/app/imagepy.py b/imagepy/app/imagepy.py index 54184751..f43dd5f6 100644 --- a/imagepy/app/imagepy.py +++ b/imagepy/app/imagepy.py @@ -203,7 +203,7 @@ def init_table(self): self.tablenbwrap.SetSizer( sizer ) self.tablenbwrap.Layout() - self.auimgr.AddPane( self.tablenbwrap, aui.AuiPaneInfo() .Center() .CaptionVisible( True ).PinButton( True ).Dock() + self.auimgr.AddPane( self.tablenbwrap, aui.AuiPaneInfo() .Bottom() .CaptionVisible( True ).PinButton( True ).Dock(). Hide() .MaximizeButton( True ).Resizable().FloatingSize((800, 600)).BestSize(( 120,120 )). Caption('Table') . BottomDockable( True ).TopDockable( False ).LeftDockable( True ).RightDockable( True ) ) self.tablenb.Bind( wx.lib.agw.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_active_table) diff --git a/sciapp/action/plugin/mesh_tools.py b/sciapp/action/plugin/mesh_tools.py index 5fefd73f..84143726 100644 --- a/sciapp/action/plugin/mesh_tools.py +++ b/sciapp/action/plugin/mesh_tools.py @@ -11,7 +11,7 @@ def __init__(self): def mouse_down(self, obj, x, y, btn, **key): self.oldxy = x, y if btn == 1 and not key['shift']: - picked = key['canvas'].at(x,y) + picked = key['canvas'].at(x,y) if key['ctrl'] else None if not picked is None: self.curobj = picked self.curobj.set_data(high_light=0xffaaffff) @@ -68,4 +68,6 @@ def mouse_wheel(self, obj, x, y, d, **key): camera = key['canvas'].camera if camera._distance is not None: camera._distance *= s - camera.scale_factor *= s \ No newline at end of file + camera.scale_factor *= s + +MeshViewTool().start(None) \ No newline at end of file diff --git a/sciwx/mesh/canvas.py b/sciwx/mesh/canvas.py index 4d1c6174..09a3ee06 100644 --- a/sciwx/mesh/canvas.py +++ b/sciwx/mesh/canvas.py @@ -94,7 +94,7 @@ def viewtext(text, view): view._picking_filter.id = view._id else: view._picking_filter.enabled = True - view._picking_filter.id = mesh.high_light + view._picking_filter.id = text.high_light # view.shading = 'flat' text.dirty = False return view @@ -117,7 +117,7 @@ def viewvolume(vol, view): view._picking_filter.id = view._id else: view._picking_filter.enabled = True - view._picking_filter.id = mesh.high_light + view._picking_filter.id = vol.high_light # view.shading = 'flat' vol.dirty = False return view @@ -162,6 +162,7 @@ def on_idle(self, event): need = 'update' self.bgcolor = self.scene3d.bg_color for i in self.visuals: + if not isinstance(self.visuals[i], MeshVisual): continue self.visuals[i].ambient_light_color = self.scene3d.ambient_color self.visuals[i].light_color = self.scene3d.light_color self.visuals[i].light_dir = self.scene3d.light_dir From 592365d5969030791873312bfd0e82010024d13c Mon Sep 17 00:00:00 2001 From: yxdragon Date: Sun, 24 Oct 2021 21:48:12 +0800 Subject: [PATCH 341/343] nothing --- imagepy/app/imagepy.py | 3 +- imagepy/ipyalg/graph/sknw.py | 119 ++++++++++++------ .../menus/Process/Hydrology/hydrology_plgs.py | 2 +- sciapp/app.py | 2 +- sciapp/object/image.py | 3 +- sciapp/object/surface.py | 9 +- sciwx/demo/mesh1_demo.py | 14 ++- sciwx/mesh/canvas.py | 83 ++++++------ sciwx/mesh/mcanvas.py | 53 ++++++-- sciwx/mesh/widget.py | 4 +- sciwx/widgets/advanced.py | 2 +- sciwx/widgets/ribbonbar.py | 2 +- 12 files changed, 192 insertions(+), 104 deletions(-) diff --git a/imagepy/app/imagepy.py b/imagepy/app/imagepy.py index f43dd5f6..8fe15ae8 100644 --- a/imagepy/app/imagepy.py +++ b/imagepy/app/imagepy.py @@ -293,7 +293,8 @@ def on_active_mesh(self, event): def on_close_mesh(self, event): # canvas3d = event.GetEventObject().GetPage(event.GetSelection()) - self.close_mesh(self.meshnb.canvas().scene3d.name) + App.close_mesh(self, self.meshnb.canvas().scene3d.name) + event.Skip() # self.remove_mesh_win(canvas3d) def info(self, value): diff --git a/imagepy/ipyalg/graph/sknw.py b/imagepy/ipyalg/graph/sknw.py index 631c192a..28a9e358 100644 --- a/imagepy/ipyalg/graph/sknw.py +++ b/imagepy/ipyalg/graph/sknw.py @@ -35,29 +35,28 @@ def idx2rc(idx, acc): @jit(nopython=True) # fill a node (may be two or more points) def fill(img, p, num, nbs, acc, buf): - back = img[p] img[p] = num buf[0] = p - cur = 0; s = 1; + cur = 0; s = 1; iso = True; while True: p = buf[cur] for dp in nbs: cp = p+dp - if img[cp]==back: + if img[cp]==2: img[cp] = num buf[s] = cp s+=1 + if img[cp]==1: iso=False cur += 1 if cur==s:break - return idx2rc(buf[:s], acc) + return iso, idx2rc(buf[:s], acc) @jit(nopython=True) # trace the edge and use a buffer, then buf.copy, if use [] numba not works def trace(img, p, nbs, acc, buf): c1 = 0; c2 = 0; newp = 0 - cur = 0 - + cur = 1 while True: buf[cur] = p img[p] = 0 @@ -65,28 +64,42 @@ def trace(img, p, nbs, acc, buf): for dp in nbs: cp = p + dp if img[cp] >= 10: - if c1==0:c1=img[cp] - else: c2 = img[cp] + if c1==0: + c1 = img[cp] + buf[0] = cp + else: + c2 = img[cp] + buf[cur] = cp if img[cp] == 1: newp = cp p = newp if c2!=0:break - return (c1-10, c2-10, idx2rc(buf[:cur], acc)) + return (c1-10, c2-10, idx2rc(buf[:cur+1], acc)) @jit(nopython=True) # parse the image then get the nodes and edges -def parse_struc(img, pts, nbs, acc): +def parse_struc(img, nbs, acc, iso, ring): img = img.ravel() buf = np.zeros(131072, dtype=np.int64) num = 10 nodes = [] - for p in pts: + for p in range(len(img)): if img[p] == 2: - nds = fill(img, p, num, nbs, acc, buf) + isiso, nds = fill(img, p, num, nbs, acc, buf) + if isiso and not iso: continue num += 1 nodes.append(nds) - edges = [] - for p in pts: + for p in range(len(img)): + if img[p] <10: continue + for dp in nbs: + if img[p+dp]==1: + edge = trace(img, p+dp, nbs, acc, buf) + edges.append(edge) + if not ring: return nodes, edges + for p in range(len(img)): + if img[p]!=1: continue + img[p] = num; num += 1 + nodes.append(idx2rc([p], acc)) for dp in nbs: if img[p+dp]==1: edge = trace(img, p+dp, nbs, acc, buf) @@ -94,50 +107,76 @@ def parse_struc(img, pts, nbs, acc): return nodes, edges # use nodes and edges build a networkx graph -def build_graph(nodes, edges, multi=False): +def build_graph(nodes, edges, multi=False, full=True): + os = np.array([i.mean(axis=0) for i in nodes]) + if full: os = os.round().astype(np.uint16) graph = nx.MultiGraph() if multi else nx.Graph() for i in range(len(nodes)): - graph.add_node(i, pts=nodes[i], o=nodes[i].mean(axis=0)) + graph.add_node(i, pts=nodes[i], o=os[i]) for s,e,pts in edges: + if full: pts[[0,-1]] = os[[s,e]] l = np.linalg.norm(pts[1:]-pts[:-1], axis=1).sum() graph.add_edge(s,e, pts=pts, weight=l) return graph -def buffer(ske): - buf = np.zeros(tuple(np.array(ske.shape)+2), dtype=np.uint16) - buf[tuple([slice(1,-1)]*buf.ndim)] = ske +def mark_node(ske): + buf = np.pad(ske, (1,1), mode='constant') + nbs = neighbors(buf.shape) + acc = np.cumprod((1,)+buf.shape[::-1][:-1])[::-1] + mark(buf, nbs) return buf - -def build_sknw(ske, multi=False): - buf = buffer(ske) + +def build_sknw(ske, multi=False, iso=True, ring=True, full=True): + buf = np.pad(ske, (1,1), mode='constant') nbs = neighbors(buf.shape) acc = np.cumprod((1,)+buf.shape[::-1][:-1])[::-1] mark(buf, nbs) - pts = np.array(np.where(buf.ravel()==2))[0] - nodes, edges = parse_struc(buf, pts, nbs, acc) - return build_graph(nodes, edges, multi) + nodes, edges = parse_struc(buf, nbs, acc, iso, ring) + return build_graph(nodes, edges, multi, full) # draw the graph def draw_graph(img, graph, cn=255, ce=128): acc = np.cumprod((1,)+img.shape[::-1][:-1])[::-1] img = img.ravel() + for (s, e) in graph.edges(): + eds = graph[s][e] + if isinstance(graph, nx.MultiGraph): + for i in eds: + pts = eds[i]['pts'] + img[np.dot(pts, acc)] = ce + else: img[np.dot(eds['pts'], acc)] = ce for idx in graph.nodes(): pts = graph.nodes[idx]['pts'] img[np.dot(pts, acc)] = cn - for (s, e) in graph.edges(): - eds = graph[s][e] - for i in eds: - pts = eds[i]['pts'] - img[np.dot(pts, acc)] = ce if __name__ == '__main__': - g = nx.MultiGraph() - g.add_nodes_from([1,2,3,4,5]) - g.add_edges_from([(1,2),(1,3),(2,3),(4,5),(5,4)]) - print(g.nodes()) - print(g.edges()) - a = g.subgraph(1) - print('d') - print(a) - print('d') - + import matplotlib.pyplot as plt + + img = np.array([ + [0,0,0,1,0,0,0,0,0], + [0,0,0,1,0,0,0,1,0], + [0,0,0,1,0,0,0,0,0], + [1,1,1,1,0,0,0,0,0], + [0,0,0,0,1,0,0,0,0], + [0,1,0,0,0,1,0,0,0], + [1,0,1,0,0,1,1,1,1], + [0,1,0,0,1,0,0,0,0], + [0,0,0,1,0,0,0,0,0]]) + + node_img = mark_node(img) + graph = build_sknw(img, False, iso=True, ring=True) + plt.imshow(node_img[1:-1,1:-1], cmap='gray') + + # draw edges by pts + for (s,e) in graph.edges(): + ps = graph[s][e]['pts'] + plt.plot(ps[:,1], ps[:,0], 'green') + + # draw node by o + nodes = graph.nodes() + ps = np.array([nodes[i]['o'] for i in nodes]) + plt.plot(ps[:,1], ps[:,0], 'r.') + + # title and show + plt.title('Build Graph') + plt.show() \ No newline at end of file diff --git a/imagepy/menus/Process/Hydrology/hydrology_plgs.py b/imagepy/menus/Process/Hydrology/hydrology_plgs.py index 6c0cf765..9d5c85b3 100644 --- a/imagepy/menus/Process/Hydrology/hydrology_plgs.py +++ b/imagepy/menus/Process/Hydrology/hydrology_plgs.py @@ -217,7 +217,7 @@ def run(self, ips, snap, img, para = None): if not para['ud']:img[:] = 255-img mark = watershed(img, markers, line=True, conn=para['con']+1) mark = np.multiply((mark==0), 255, dtype=np.uint8) - + if para['type'] == 'white line': img[:] = mark if para['type'] == 'gray line': diff --git a/sciapp/app.py b/sciapp/app.py index 32801d52..dee872b0 100644 --- a/sciapp/app.py +++ b/sciapp/app.py @@ -62,7 +62,7 @@ def show_table(self, tab, name): if not isinstance(tab, Table): tab = Table(tab, name) tab.name = self.tab_manager.name(name) - self.tab_manager.add(name, tab) + self.tab_manager.add(tab.name, tab) print(tab.info) def close_table(self, name): diff --git a/sciapp/object/image.py b/sciapp/object/image.py index be2f4b15..9f9daa0f 100644 --- a/sciapp/object/image.py +++ b/sciapp/object/image.py @@ -46,6 +46,7 @@ class Image: def __init__(self, imgs=None, name='Image'): self.name = name self.cur = 0 + self.rg = [(0, 255)] self.set_imgs(imgs) self.roi = None self.mark = None @@ -53,7 +54,7 @@ def __init__(self, imgs=None, name='Image'): self.msk = None self.pos = (0,0) self.cn = 0 - self.rg = [(0,255)] + self.lut = default_lut self.log = False self.mode = 'set' diff --git a/sciapp/object/surface.py b/sciapp/object/surface.py index 56e4a229..7f9d8f20 100644 --- a/sciapp/object/surface.py +++ b/sciapp/object/surface.py @@ -78,7 +78,7 @@ def __init__(self, verts=None, faces=None, colors=None, cmap=None, **key): self.faces = faces.astype(np.uint32, copy=False) if not faces is None else None self.colors = colors self.mode, self.visible, self.dirty = 'mesh', True, 'geom' - self.alpha = 1; self.edges = None + self.alpha = 1; self.edges = None; self.shiness = 60 self.high_light = False; self.cmap = 'gray' if cmap is None else cmap self.set_data(**key) @@ -98,6 +98,7 @@ def set_data(self, verts=None, faces=None, colors=None, **key): self.visible = key.get('visible', self.visible) self.alpha = key.get('alpha', self.alpha) self.high_light = key.get('high_light', False) + self.shiness = key.get('shiness', self.shiness) self.cmap = key.get('cmap', self.cmap) self.dirty = self.dirty or True @@ -116,7 +117,7 @@ def __init__(self, texts=None, verts=None, colors=(1,1,1), size=12, **key): self.texts, self.verts, self.size, self.colors = texts, verts, size, colors self.visible, self.dirty = True, 'geom' self.alpha = 1; self.edges = None - self.high_light = False; + self.high_light = False; self.shiness = 0 self.set_data(**key) def set_data(self, texts=None, verts=None, colors=None, size=None, **key): @@ -128,6 +129,7 @@ def set_data(self, texts=None, verts=None, colors=None, size=None, **key): self.visible = key.get('visible', self.visible) self.alpha = key.get('alpha', self.alpha) self.high_light = key.get('high_light', False) + self.shiness = key.get('shiness', self.shiness) self.dirty = self.dirty or True class Surface2d(Mesh): @@ -171,7 +173,7 @@ def __init__(self, imgs=None, level=0.25, sample=1, step=1, cmap=None, **key): self.imgs, self.level, self.sample, self.step = imgs, level, sample, step self.visible, self.dirty = True, 'geom' self.cmap = 'gray' if cmap is None else cmap - self.alpha = 1 + self.alpha = 1; self.shiness = 0 self.high_light = False; self.set_data(**key) @@ -184,5 +186,6 @@ def set_data(self, imgs=None, level=None, step=None, **key): self.visible = key.get('visible', self.visible) self.alpha = key.get('alpha', self.alpha) self.high_light = key.get('high_light', False) + self.shiness = key.get('shiness', self.shiness) self.cmap = key.get('cmap', self.cmap) self.dirty = self.dirty or True diff --git a/sciwx/demo/mesh1_demo.py b/sciwx/demo/mesh1_demo.py index 278ee4dc..28303164 100644 --- a/sciwx/demo/mesh1_demo.py +++ b/sciwx/demo/mesh1_demo.py @@ -10,10 +10,12 @@ verts, faces = meshutil.create_ball((0,0,0), 1) ball = Mesh(verts=verts, faces=faces, colors=verts[:,2], cmap='jet') ball2 = Mesh(verts=verts, faces=faces, colors=verts[:,2], mode='grid') +ball3 = Mesh(verts=verts, faces=faces, colors=verts[:,2], mode='grid') def canvas3d_test(): frame = wx.Frame(None, title='Canvas3D') - canvas3d = Canvas3D(frame) + canvas3d = Canvas3D(parent=frame) + canvas3d.SetBackgroundColour((255,0,0)) canvas3d.add_obj('ball', ball) frame.Show() @@ -35,17 +37,21 @@ def canvas3d_note_book(): canvas1.add_obj('ball', ball) canvas2 = cnb.add_canvas() canvas2.add_obj('ball2', ball2) + canvas3 = cnb.add_canvas() + canvas3.add_obj('ball3', ball3) frame.Show() def canvas3d_note_frame(): cnf = Canvas3DNoteFrame(None) canvas1 = cnf.add_canvas() - canvas1.add_obj('ball', ball) + canvas1.add_obj('ball1', ball) + canvas2 = cnf.add_canvas() + canvas2.add_obj('ball2', ball2) canvas2 = cnf.add_canvas() - canvas2.add_obj('ball', ball2) + canvas2.add_obj('ball3', ball3) cnf.Show() if __name__ == '__main__': app = wx.App() - canvas3d_note_frame() + canvas3d_note_book() app.MainLoop() diff --git a/sciwx/mesh/canvas.py b/sciwx/mesh/canvas.py index 09a3ee06..888a242a 100644 --- a/sciwx/mesh/canvas.py +++ b/sciwx/mesh/canvas.py @@ -1,4 +1,4 @@ -import wx, weakref +import wx, weakref, sys import numpy as np from vispy import app, scene, gloo import platform, os.path as osp @@ -28,8 +28,8 @@ def light_color(self, light): def _update_data(self): rst = scene.visuals.Mesh._update_data(self) - if self.shading is not None: - self.shared_program.vert['light_color'] = self._light_color + #if self.shading is not None: + # self.shared_program.vert['light_color'] = self._light_color return rst class VolumeVisual(scene.visuals.Volume): @@ -54,24 +54,26 @@ def viewmesh(mesh, view): if isinstance(mesh.colors, tuple): colorkey = 'color' elif mesh.colors.ndim == 2: colorkey = 'vertex_colors' elif mesh.colors.ndim == 1: colorkey = 'vertex_values' - key[colorkey] = mesh.colors view.set_data(**key) - view.interactive = True - view.shininess = 0 + if view.shading_filter: + view.shading_filter.shininess = mesh.shiness if isinstance(mesh.cmap, str): cmap = mesh.cmap elif mesh.cmap.max()>1+1e-5: cmap = Colormap(mesh.cmap/255) else: cmap = Colormap(mesh.cmap) view.cmap = cmap + view.clim = [-1, 1] view.visible = mesh.visible view.opacity = mesh.alpha + ''' if mesh.high_light is False: view._picking_filter.enabled = False view._picking_filter.id = view._id else: view._picking_filter.enabled = True view._picking_filter.id = mesh.high_light + ''' # view.shading = 'flat' mesh.dirty = False return view @@ -105,7 +107,7 @@ def viewvolume(vol, view): else: cmap = Colormap(vol.cmap) if vol.dirty=='geom': if view is None: - view = VolumeVisual(vol.imgs, emulate_texture=False, cmap=cmap) + view = VolumeVisual(vol.imgs, cmap=cmap) view.relative_step_size = vol.step view.threshold = vol.level @@ -128,44 +130,42 @@ def viewobj(obj, view): if isinstance(obj, TextSet): return viewtext(obj, view) if isinstance(obj, Volume3d): return viewvolume(obj, view) -class Canvas3D(scene.SceneCanvas): - def __new__(cls, parent, scene3d=None): - self = super().__new__(cls) - scene.SceneCanvas.__init__(self, app="wx", parent=parent, keys='interactive', show=True, dpi=150) - canvas = parent.GetChildren()[-1] - self.unfreeze() - self.canvas = weakref.ref(canvas) - self.view = self.central_widget.add_view() +class Canvas3D(wx.Panel): + def __init__(self, parent, scene3d=None): + wx.Panel.__init__(self, parent) + self.canvas = scene.SceneCanvas(app='wx', parent=self, keys='interactive', show=True, dpi=150) + box = wx.BoxSizer( wx.VERTICAL ) + box.Add(self.GetChildren()[-1], 1, wx.ALL|wx.EXPAND, 0 ) + self.SetSizer(box) self.set_scene(scene3d or Scene()) + self.Bind(wx.EVT_IDLE, self.on_idle) + self.view = self.canvas.central_widget.add_view() self.visuals = {} self.curobj = None - self.freeze() - canvas.Bind(wx.EVT_IDLE, self.on_idle) - canvas.tool = None - canvas.camera = scene.cameras.TurntableCamera(parent=self.view.scene, fov=45, name='Turntable') - canvas.set_camera = self.set_camera - canvas.fit = lambda : self.set_camera(auto=True) - canvas.at = self.at - self.view.camera = canvas.camera - return canvas + self.tool = None + self.camera = scene.cameras.TurntableCamera(parent=self.view.scene, fov=45, name='Turntable') + self.view.camera = self.camera - def __init__(self, **kwargs): pass + self.canvas._process_mouse_event = self._process_mouse_event def set_scene(self, scene): self.scene3d = scene - self.canvas().scene3d = self.scene3d - self.canvas().add_obj = self.scene3d.add_obj + + def add_obj(self, name, obj): + self.scene3d.add_obj(name, obj) def on_idle(self, event): need = 'ignore' if self.scene3d.dirty: need = 'update' - self.bgcolor = self.scene3d.bg_color + self.canvas.bgcolor = self.scene3d.bg_color for i in self.visuals: if not isinstance(self.visuals[i], MeshVisual): continue - self.visuals[i].ambient_light_color = self.scene3d.ambient_color - self.visuals[i].light_color = self.scene3d.light_color - self.visuals[i].light_dir = self.scene3d.light_dir + if self.visuals[i].shading_filter is None: continue + # self.visuals[i].shading_filter.shininess = 0 + self.visuals[i].shading_filter.ambient_light = self.scene3d.ambient_color + self.visuals[i].shading_filter.diffuse_light = self.scene3d.light_color + self.visuals[i].shading_filter.light_dir = self.scene3d.light_dir self.scene3d.dirty = False for i in self.scene3d.names: obj = self.scene3d.objects[i] @@ -177,16 +177,17 @@ def on_idle(self, event): self.visuals[i] = viewobj(obj, vis) if need=='ignore': need = 'update' self.visuals[i].parent = self.view.scene - if need == 'add': self.canvas().fit() + if need == 'add': self.fit() if need != 'ignore': - print('need') - self.update() + self.canvas.update() + + def fit(self): self.set_camera(auto=True) def set_camera(self, azimuth=None, elevation=None, dist=None, fov=None, auto=False): - if not azimuth is None: self.canvas().camera.azimuth = azimuth - if not elevation is None: self.canvas().camera.elevation = elevation - if not fov is None: self.canvas().camera.fov = fov - if auto: self.canvas().camera.set_range() + if not azimuth is None: self.camera.azimuth = azimuth + if not elevation is None: self.camera.elevation = elevation + if not fov is None: self.camera.fov = fov + if auto: self.camera.set_range() def at(self, x, y): self.view.interactive = False @@ -201,7 +202,7 @@ def _process_mouse_event(self, event): # self.measure_fps() # return scene.SceneCanvas._process_mouse_event(self, event) px, py = x, y = tuple(event.pos) - canvas, tool, btn = self.canvas(), self.canvas().tool or MeshTool.default, event._button + canvas, tool, btn = self, self.tool or MeshTool.default, event._button btn = {2:3, 3:2}.get(btn, btn) ld, rd, md = [i in event.buttons for i in (1,2,3)] sta = [i in [j.name for j in event.modifiers] for i in ('Alt', 'Control', 'Shift')] @@ -226,6 +227,10 @@ def _process_mouse_event(self, event): canvas.SetCursor(wx.Cursor(cursor)) event.handled = True + def close(self): + self.canvas._process_mouse_event = None + self.canvas = None + def __del__(self): # self.img = self.back = None print('========== canvas del') diff --git a/sciwx/mesh/mcanvas.py b/sciwx/mesh/mcanvas.py index 7a9b62bc..93a7f857 100644 --- a/sciwx/mesh/mcanvas.py +++ b/sciwx/mesh/mcanvas.py @@ -1,5 +1,6 @@ from .canvas import Canvas3D import wx, os.path as osp, platform +import math import numpy as np def make_bitmap(bmp): @@ -46,6 +47,13 @@ def __init__( self, parent, scene=None): self.cho_bg = wx.Choice( self.toolbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, ['force scatter', 'normal scatter', 'weak scatter', 'off scatter'], 0 ) self.cho_bg.SetSelection( 1 ) tsizer.Add( self.cho_bg, 0, wx.ALIGN_CENTER|wx.ALL, 1 ) + self.spn_dirv = wx.SpinButton( self.toolbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0 ) + tsizer.Add( self.spn_dirv, 0, wx.ALL|wx.EXPAND, 1 ) + self.spn_dirh = wx.SpinButton( self.toolbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.SP_HORIZONTAL ) + tsizer.Add( self.spn_dirh, 0, wx.ALL|wx.EXPAND, 1 ) + + self.spn_dirv.SetRange(-1e4, 1e4) + self.spn_dirh.SetRange(-1e4, 1e4) self.toolbar.SetSizer( tsizer ) tsizer.Layout() @@ -53,18 +61,19 @@ def __init__( self, parent, scene=None): self.settingbar = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) ssizer = wx.BoxSizer( wx.HORIZONTAL ) - self.m_staticText1 = wx.StaticText( self.settingbar, wx.ID_ANY, u"Object:", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.m_staticText1.Wrap( -1 ) - ssizer.Add( self.m_staticText1, 0, wx.ALIGN_CENTER|wx.LEFT, 10 ) - cho_objChoices = ['None'] + cho_objChoices = [''] self.cho_obj = wx.Choice( self.settingbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, cho_objChoices, 0 ) self.cho_obj.SetSelection( 0 ) ssizer.Add( self.cho_obj, 0, wx.ALIGN_CENTER|wx.ALL, 1 ) self.chk_visible = wx.CheckBox( self.settingbar, wx.ID_ANY, u"visible", wx.DefaultPosition, wx.DefaultSize, 0 ) ssizer.Add( self.chk_visible, 0, wx.ALIGN_CENTER|wx.LEFT, 10 ) - + + self.cho_hlight = wx.Choice( self.settingbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, ['force specular', 'normal specular', 'weak specular', 'off specular'], 0 ) + self.cho_hlight.SetSelection( 3 ) + ssizer.Add( self.cho_hlight, 0, wx.ALIGN_CENTER|wx.ALL, 1 ) + self.col_color = wx.ColourPickerCtrl( self.settingbar, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE ) ssizer.Add( self.col_color, 0, wx.ALIGN_CENTER|wx.ALL, 1 ) @@ -76,10 +85,6 @@ def __init__( self, parent, scene=None): ssizer.Add( self.sli_alpha, 0, wx.ALIGN_CENTER|wx.ALL, 1 ) self.settingbar.SetSizer(ssizer) - self.m_staticText2 = wx.StaticText( self.settingbar, wx.ID_ANY, u"Mode:", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.m_staticText2.Wrap( -1 ) - ssizer.Add( self.m_staticText2, 0, wx.ALIGN_CENTER|wx.LEFT, 10 ) - cho_objChoices = ['mesh', 'grid', 'points'] self.cho_mode = wx.Choice( self.settingbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, cho_objChoices, 0 ) self.cho_mode.SetSelection( 0 ) @@ -104,6 +109,7 @@ def __init__( self, parent, scene=None): self.on_bgcolor = lambda e: self.canvas.scene3d.set_style(bg_color=tuple(np.array(e.GetColour()[:3])/255)) self.on_bg = lambda e: self.canvas.scene3d.set_style(ambient_color=((3-e.GetSelection())/3,)*3+(1,)) self.on_light = lambda e: self.canvas.scene3d.set_style(light_color=((3-e.GetSelection())/3,)*3+(1,)) + self.on_shiness = lambda e: self.curobj.set_data(shiness=(3-e.GetSelection())*20) self.btn_x.Bind( wx.EVT_BUTTON, self.view_x) self.btn_y.Bind( wx.EVT_BUTTON, self.view_y) @@ -117,11 +123,15 @@ def __init__( self, parent, scene=None): self.cho_obj.Bind( wx.EVT_CHOICE, self.on_select ) self.cho_mode.Bind( wx.EVT_CHOICE, self.on_mode ) self.cho_light.Bind( wx.EVT_CHOICE, self.on_light ) + self.cho_hlight.Bind( wx.EVT_CHOICE, self.on_shiness ) self.cho_bg.Bind( wx.EVT_CHOICE, self.on_bg ) self.chk_visible.Bind( wx.EVT_CHECKBOX, self.on_visible) self.sli_alpha.Bind( wx.EVT_SCROLL, self.on_alpha ) self.col_color.Bind( wx.EVT_COLOURPICKER_CHANGED, self.on_color ) + self.spn_dirv.Bind( wx.EVT_SPIN, self.on_dirv ) + self.spn_dirh.Bind( wx.EVT_SPIN, self.on_dirh ) + self.Bind(wx.EVT_IDLE, self.on_idle) self.cho_obj.Set(list(self.canvas.scene3d.names)) @@ -129,6 +139,8 @@ def __init__( self, parent, scene=None): def on_idle(self, event): if set(self.canvas.scene3d.names) != set(self.cho_obj.Items): self.cho_obj.Set(list(self.canvas.scene3d.names)) + self.cho_obj.SetSelection(0) + self.on_select(0) @property def name(self): return self.canvas.scene3d.name @@ -140,6 +152,24 @@ def set_mesh(self, mesh): @property def scene3d(self): return self.canvas.scene3d + def light_dir_move(self, dx, dy): + from math import sin, cos, asin, sqrt, pi + lx, ly, lz = self.scene3d.light_dir + ay = asin(lz/sqrt(lx**2+ly**2+lz**2))-dy + xx = cos(dx)*lx - sin(dx)*ly + yy = sin(dx)*lx + cos(dx)*ly + ay = max(min(pi/2-1e-4, ay), -pi/2+1e-4) + zz, k = sin(ay), cos(ay)/sqrt(lx**2+ly**2) + self.scene3d.set_style(light_dir = (xx*k, yy*k, zz)) + + def on_dirv(self, evt): + self.light_dir_move(0, 5/180*math.pi*evt.GetInt()) + self.spn_dirv.SetValue(0) + + def on_dirh(self, evt): + self.light_dir_move(5/180*math.pi*evt.GetInt(), 0) + self.spn_dirh.SetValue(0) + def on_save(self, evt): dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} filt = 'PNG files (*.png)|*.png' @@ -212,12 +242,13 @@ def on_select(self, evt): def add_surf_asyn(self, name, obj): self.canvas.add_surf_asyn(name, obj) - self.cho_obj.Append(name) def add_obj(self, name, obj): self.canvas.scene3d.add_obj(name, obj) - self.cho_obj.Append(name) + def close(self): + self.canvas.close() + self.canvas = None ''' from mesh import Mesh import numpy as np diff --git a/sciwx/mesh/widget.py b/sciwx/mesh/widget.py index 78d28b25..62cfa5a2 100644 --- a/sciwx/mesh/widget.py +++ b/sciwx/mesh/widget.py @@ -69,7 +69,9 @@ def set_title(self, panel, title): def on_valid(self, event): pass - def on_close(self, event): pass + def on_close(self, event): + self.GetCurrentPage().close() + import gc; gc.collect() class Canvas3DNoteFrame(wx.Frame): def __init__(self, parent): diff --git a/sciwx/widgets/advanced.py b/sciwx/widgets/advanced.py index 0fdbcc90..10e08375 100644 --- a/sciwx/widgets/advanced.py +++ b/sciwx/widgets/advanced.py @@ -8,7 +8,7 @@ def __init__(self, parent, title, unit, app=None): class TableList(Choice): def __init__(self, parent, title, unit, app=None): - Choice.__init__(self, parent, app.get_tab_name(), str, title, unit) + Choice.__init__(self, parent, app.table_names(), str, title, unit) class TableField(Choice): def __init__(self, parent, title, unit, app=None): diff --git a/sciwx/widgets/ribbonbar.py b/sciwx/widgets/ribbonbar.py index 386524e0..3fa6634b 100644 --- a/sciwx/widgets/ribbonbar.py +++ b/sciwx/widgets/ribbonbar.py @@ -51,7 +51,7 @@ def hot_key(txt): class RibbonBar(rb.RibbonBar): def __init__(self, app): rb.RibbonBar.__init__(self, app, wx.ID_ANY, wx.DefaultPosition, (-1, 140), rb.RIBBON_BAR_DEFAULT_STYLE|rb.RIBBON_BAR_SHOW_PANEL_EXT_BUTTONS ) - self.app = app + self.app = app.GetTopLevelParent() def parse(self, ks, vs, pt): if isinstance(vs, list): From 3c378ebaf72762b94f0826a410897757ebafe689 Mon Sep 17 00:00:00 2001 From: yxdragon Date: Mon, 25 Oct 2021 21:30:07 +0800 Subject: [PATCH 342/343] nothing --- imagepy/ipyalg/graph/sknw.py | 4 ++-- sciapp/object/surface.py | 2 +- sciwx/mesh/canvas.py | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/imagepy/ipyalg/graph/sknw.py b/imagepy/ipyalg/graph/sknw.py index 28a9e358..a03289ab 100644 --- a/imagepy/ipyalg/graph/sknw.py +++ b/imagepy/ipyalg/graph/sknw.py @@ -120,14 +120,14 @@ def build_graph(nodes, edges, multi=False, full=True): return graph def mark_node(ske): - buf = np.pad(ske, (1,1), mode='constant') + buf = np.pad(ske, (1,1), mode='constant').astype(np.uint16) nbs = neighbors(buf.shape) acc = np.cumprod((1,)+buf.shape[::-1][:-1])[::-1] mark(buf, nbs) return buf def build_sknw(ske, multi=False, iso=True, ring=True, full=True): - buf = np.pad(ske, (1,1), mode='constant') + buf = np.pad(ske, (1,1), mode='constant').astype(np.uint16) nbs = neighbors(buf.shape) acc = np.cumprod((1,)+buf.shape[::-1][:-1])[::-1] mark(buf, nbs) diff --git a/sciapp/object/surface.py b/sciapp/object/surface.py index 7f9d8f20..6a946aac 100644 --- a/sciapp/object/surface.py +++ b/sciapp/object/surface.py @@ -89,7 +89,7 @@ def set_data(self, verts=None, faces=None, colors=None, **key): if not faces is None: self.faces = faces.astype(np.uint32, copy=False) if not colors is None: self.colors = colors if not faces is None: self.edge = None - if sum([i is None for i in [verts, faces, colors]])<3: self.dirty = 'geom' + if sum([i is None for i in [verts, faces]])<2: self.dirty = 'geom' if not self.faces is None and self.faces.ndim==1: key['mode'] = 'points' elif not self.faces is None and self.faces.shape[1]==2: if key.get('mode', self.mode)=='mesh': key['mode'] = 'grid' diff --git a/sciwx/mesh/canvas.py b/sciwx/mesh/canvas.py index 888a242a..98201c60 100644 --- a/sciwx/mesh/canvas.py +++ b/sciwx/mesh/canvas.py @@ -10,6 +10,20 @@ # verts, faces, colors, mode, alpha, visible # light_dir, light, ambiass, background +from vispy.geometry import MeshData + +def get_vertex_normals(self, indexed=None): + if self._vertex_normals is None: + faceNorms = self.get_face_normals() + self._vertex_normals = np.zeros(self._vertices.shape, dtype=np.float32) + np.add.at(self._vertex_normals, self._faces.T, faceNorms[None,:,:]) + v = np.linalg.norm(self._vertex_normals, axis=1)[:,None] + self._vertex_normals /= np.clip(v, 1e-5, 1e5, out=v) + if indexed is None: return self._vertex_normals + elif indexed == 'faces': return self._vertex_normals[self.get_faces()] + +MeshData.get_vertex_normals = get_vertex_normals + class MeshVisual(scene.visuals.Mesh): def __init__(self, *p, **key): scene.visuals.Mesh.__init__(self, *p, **key) @@ -62,6 +76,7 @@ def viewmesh(mesh, view): if isinstance(mesh.cmap, str): cmap = mesh.cmap elif mesh.cmap.max()>1+1e-5: cmap = Colormap(mesh.cmap/255) else: cmap = Colormap(mesh.cmap) + if isinstance(mesh.colors, tuple): view.color = mesh.colors view.cmap = cmap view.clim = [-1, 1] view.visible = mesh.visible From 2d853d235537888f20da2313c63ac6746297431f Mon Sep 17 00:00:00 2001 From: yxdragon Date: Wed, 14 Feb 2024 13:16:14 +0800 Subject: [PATCH 343/343] 2024 update to python3.10 with many libs. --- imagepy/data/config.json | 2 +- imagepy/ipyalg/classify/feature.py | 8 ++--- imagepy/ipyalg/graph/skel2d.py | 2 +- imagepy/ipyalg/hydrology/ridge.py | 2 +- .../Analysis/Pixel Cluster/cluster_plgs.py | 2 +- .../Region Analysis/regionprops_plgs.py | 4 +-- .../Region Analysis/statistic_plgs.py | 2 +- .../menus/File/Samples Local/samples_plgs.py | 2 +- imagepy/menus/Image/background_plg.py | 1 + .../Kit3D/Analysis 3D/regionprops3d_plgs.py | 2 +- .../Kit3D/Analysis 3D/surfacemeasure_plg.py | 2 +- imagepy/menus/Plugins/Games/drawstep_plg.py | 2 +- imagepy/menus/Process/Features/ridge_plgs.py | 2 +- imagepy/menus/Process/Segment/active_plgs.py | 4 +-- imagepy/menus/Process/Segment/graph_plgs.py | 2 +- imagepy/tools/Draw/aibrush_tol.py | 22 ++++++------- imagepy/tools/Draw/floodfill_tol.py | 2 +- imagepy/tools/Standard/magic_tol.py | 2 +- imagepy/tools/Standard/painter_tol.py | 4 +-- sciapp/action/advanced/dataio.py | 5 ++- sciapp/action/plugin/shpbase.py | 2 +- sciapp/action/tolact.py | 5 ++- sciapp/object/roi.py | 2 +- sciapp/object/shape.py | 12 ++++--- sciapp/util/shputil.py | 2 +- sciwx/__init__.py | 2 +- sciwx/canvas/__init__.py | 2 +- sciwx/canvas/canvas.py | 33 +++++++++++++------ sciwx/canvas/mark.py | 16 +++++---- sciwx/canvas/mcanvas.py | 8 ++--- sciwx/demo/shape_frame_demo.py | 5 +-- sciwx/text/mdpad.py | 25 +++++++------- sciwx/widgets/colormap.py | 4 +-- sciwx/widgets/curvepanel.py | 14 ++++---- sciwx/widgets/histpanel.py | 6 ++-- sciwx/widgets/normal.py | 3 +- sciwx/widgets/viewport.py | 10 +++--- 37 files changed, 129 insertions(+), 96 deletions(-) diff --git a/imagepy/data/config.json b/imagepy/data/config.json index fe7b9a33..2daab69b 100644 --- a/imagepy/data/config.json +++ b/imagepy/data/config.json @@ -1 +1 @@ -[["uistyle", "imagepy", null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["recent", ["C:/Users/54631/Desktop/\u6d77\u51b0\u62a5\u4ef7/testmacros.mc", "C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc"], null], ["roi_style", {"color": [255, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 255, 0], "size": 8}, null], ["language", "English", null]] \ No newline at end of file +[["language", "English", null], ["uistyle", "imagepy", null], ["mea_style", {"color": [0, 0, 255], "fcolor": [255, 255, 255], "fill": false, "lw": 2, "tcolor": [0, 255, 0], "size": 12}, null], ["mark_style", {"color": [0, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 0, 0], "size": 8}, null], ["recent", ["C:/Users/54631/Desktop/\u6d77\u51b0\u62a5\u4ef7/testmacros.mc", "C:\\Users\\Administrator\\Downloads\\\u7d20\u6750\u2014\u8f66\u5934\\\u6d4b\u8bd5-JK1-\u52ff\u5220\\20170201\\In-20170201151126911-\u6842J73220-\u9ec4-qj-1.jpg", "C:/Users/Administrator/Desktop/imagepy/imagepy/plugins/demoplugin/menus/Demos/Macros Demo/Macros Gaussian Invert.mc", "DEM.mc"], null], ["roi_style", {"color": [255, 255, 0], "fcolor": [255, 255, 255], "fill": false, "lw": 1, "tcolor": [255, 255, 0], "size": 8}, null]] \ No newline at end of file diff --git a/imagepy/ipyalg/classify/feature.py b/imagepy/ipyalg/classify/feature.py index b00a3c00..c9987956 100644 --- a/imagepy/ipyalg/classify/feature.py +++ b/imagepy/ipyalg/classify/feature.py @@ -1,7 +1,7 @@ import numpy as np import scipy.ndimage as ndimg from skimage.filters import sobel -from skimage.feature import structure_tensor, structure_tensor_eigvals +from skimage.feature import structure_tensor, structure_tensor_eigenvalues # chans 是通道,可以是int,list,默认None,代表所有通道 para = {'chans':None, 'grade':2, 'w':1, 'items':['ori', 'blr', 'sob', 'eig']} @@ -10,7 +10,7 @@ def get_feature_one(img, msk=None, para=para): chans, grade, w, items = para['chans'], para['grade'], para['w'], para['items'] feats, titles = [], [] img = img.reshape(img.shape[:2]+(-1,)) - if msk is None: msk = np.ones(img.shape[:2], dtype=np.bool) + if msk is None: msk = np.ones(img.shape[:2], dtype='bool') if chans is None: chans = range(img.shape[2]) for c in [chans] if isinstance(chans, int) else chans: if 'ori' in items: @@ -55,7 +55,7 @@ def get_feature(imgs, labs, key=para, size=1024, callback=print): out_slice, in_slice = grid_slice(*imgs[0].shape[:2], size, 2**(key['grade']-1)*3) m, n = len(out_slice), len(labs) feats, vs = [], [] - msk = np.zeros(imgs[0].shape[:2], dtype=np.bool) + msk = np.zeros(imgs[0].shape[:2], dtype='bool') for i, img, lab in zip(range(n), imgs, labs): for j, outs, ins in zip(range(m), out_slice, in_slice): callback(i*m+j, m*n) @@ -92,7 +92,7 @@ def get_predict(imgs, model, key=para, out=None, size=1024, callback=print): temp = imgs[0] if imgs[0].ndim==2 else imgs[0][:,:,0] out = [temp.astype(np.uint8)]; out[0] *= 0; for i in range(1, len(imgs)): out.append(out[0].copy()) - msk = np.zeros(imgs[0].shape[:2], dtype=np.bool) + msk = np.zeros(imgs[0].shape[:2], dtype='bool') for i, img, ot in zip(range(len(imgs)), imgs, out): for j, outs, ins in zip(range(len(out_slice)), out_slice, in_slice): callback(i*m+j, m*n) diff --git a/imagepy/ipyalg/graph/skel2d.py b/imagepy/ipyalg/graph/skel2d.py index 56292a65..2ea980a0 100644 --- a/imagepy/ipyalg/graph/skel2d.py +++ b/imagepy/ipyalg/graph/skel2d.py @@ -4,7 +4,7 @@ from scipy.ndimage import label, generate_binary_structure from scipy.ndimage import distance_transform_edt -strc = np.ones((3,3), dtype=np.bool) +strc = np.ones((3,3), dtype='bool') # check whether this pixcel can be removed def check(n): diff --git a/imagepy/ipyalg/hydrology/ridge.py b/imagepy/ipyalg/hydrology/ridge.py index 09a49527..b950d7ae 100644 --- a/imagepy/ipyalg/hydrology/ridge.py +++ b/imagepy/ipyalg/hydrology/ridge.py @@ -3,7 +3,7 @@ from scipy.ndimage import label, generate_binary_structure -strc = np.ones((3,3), dtype=np.bool) +strc = np.ones((3,3), dtype='bool') def count(n): a = [(n>>i) & 1 for i in range(8)] diff --git a/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py b/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py index 09f3fbd1..c8dfd95e 100644 --- a/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py +++ b/imagepy/menus/Analysis/Pixel Cluster/cluster_plgs.py @@ -26,7 +26,7 @@ def grayselect(img, pts, k, usecov=True): def within(msk, pts): lab, n = ndimg.label(msk) - hist = np.zeros(n+1, dtype=np.bool) + hist = np.zeros(n+1, dtype='bool') hist[lab[pts]] = 1 hist[0] = 0 return hist[lab] diff --git a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py index f5e25fa4..7749a1b1 100644 --- a/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/regionprops_plgs.py @@ -48,11 +48,11 @@ def run(self, ips, imgs, para = None): if para['fa']:titles.extend(['FilledArea']) if para['solid']:titles.extend(['Solidity']) if para['cov']:titles.extend(['Major','Minor','Ori']) - buf = imgs[0].astype(np.uint32) + buf = np.zeros(imgs[0].shape, dtype=np.uint32) data, mark = [], {'type':'layers', 'body':{}} strc = generate_binary_structure(2, 1 if para['con']=='4-connect' else 2) for i in range(len(imgs)): - np.copyto(imgs[i], buf) if para['labeled'] else label(imgs[i], strc, output=buf) + np.copyto(buf, imgs[i]) if para['labeled'] else label(imgs[i], strc, output=buf) ls = regionprops(buf) dt = [[i]*len(ls), list(range(len(ls)))] diff --git a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py index fa5d6183..d7144a20 100644 --- a/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py +++ b/imagepy/menus/Analysis/Region Analysis/statistic_plgs.py @@ -114,7 +114,7 @@ def run(self, ips, snap, img, para = None): buf, n = ndimage.label(snap, strc, output=np.uint32) index = range(1, n+1) idx = (np.ones(n+1)*para['front']).astype(np.uint8) - msk = np.ones(n, dtype=np.bool) + msk = np.ones(n, dtype='bool') if para['mean']>0: msk *= ndimage.mean(intenimg, buf, index)>=para['mean'] if para['mean']<0: msk *= ndimage.mean(intenimg, buf, index)<-para['mean'] diff --git a/imagepy/menus/File/Samples Local/samples_plgs.py b/imagepy/menus/File/Samples Local/samples_plgs.py index 811945c1..e5dd1dcb 100644 --- a/imagepy/menus/File/Samples Local/samples_plgs.py +++ b/imagepy/menus/File/Samples Local/samples_plgs.py @@ -14,7 +14,7 @@ def run(self, para = None): img = self.data() if isinstance(img, tuple): return self.app.show_img(list(img), self.title) - if img.dtype == np.bool: + if img.dtype == 'bool': img.dtype = np.uint8 img *= 255 self.app.show_img([img], self.title) diff --git a/imagepy/menus/Image/background_plg.py b/imagepy/menus/Image/background_plg.py index 690d79d2..2899b652 100644 --- a/imagepy/menus/Image/background_plg.py +++ b/imagepy/menus/Image/background_plg.py @@ -25,6 +25,7 @@ class BackgroundSelf(Simple): (float, 'k', (0, 1), 1, 'ratial', '')] def run(self, ips, imgs, para = None): + print(para) if ips.isarray: imgs = imgs.copy() else: imgs = [i.copy() for i in imgs] back = Image(imgs) diff --git a/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py b/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py index 2ca4e55d..ac29f79a 100644 --- a/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py +++ b/imagepy/menus/Kit3D/Analysis 3D/regionprops3d_plgs.py @@ -1,7 +1,7 @@ import numpy as np from sciapp.action import Simple, Filter from scipy.ndimage import label, generate_binary_structure -from skimage.measure import marching_cubes_lewiner, mesh_surface_area +from skimage.measure import marching_cubes, mesh_surface_area from skimage.segmentation import find_boundaries from skimage.measure import regionprops from numpy.linalg import norm diff --git a/imagepy/menus/Kit3D/Analysis 3D/surfacemeasure_plg.py b/imagepy/menus/Kit3D/Analysis 3D/surfacemeasure_plg.py index 2a8f6cd6..c77404fe 100644 --- a/imagepy/menus/Kit3D/Analysis 3D/surfacemeasure_plg.py +++ b/imagepy/menus/Kit3D/Analysis 3D/surfacemeasure_plg.py @@ -1,5 +1,5 @@ from sciapp.action import Filter -from skimage.measure import marching_cubes_lewiner, mesh_surface_area +from skimage.measure import marching_cubes, mesh_surface_area import numpy as np import pandas as pd diff --git a/imagepy/menus/Plugins/Games/drawstep_plg.py b/imagepy/menus/Plugins/Games/drawstep_plg.py index ceaec72a..423486d9 100644 --- a/imagepy/menus/Plugins/Games/drawstep_plg.py +++ b/imagepy/menus/Plugins/Games/drawstep_plg.py @@ -62,4 +62,4 @@ def run(self, ips, imgs, para = None): rst = build(ips.img) print(len(rst)) print(rst[0].shape) - IPy.show_img(rst, 'ips.title-%s'%'stroke') \ No newline at end of file + self.app.show_img(rst, 'ips.title-%s'%'stroke') \ No newline at end of file diff --git a/imagepy/menus/Process/Features/ridge_plgs.py b/imagepy/menus/Process/Features/ridge_plgs.py index 23db212c..d6c3e9f1 100644 --- a/imagepy/menus/Process/Features/ridge_plgs.py +++ b/imagepy/menus/Process/Features/ridge_plgs.py @@ -1,5 +1,5 @@ from skimage.filters import frangi, sato, hessian ,meijering -from skimage.feature import structure_tensor, structure_tensor_eigvals +from skimage.feature import structure_tensor, structure_tensor_eigenvalues from sciapp.action import Filter, Simple import numpy as np diff --git a/imagepy/menus/Process/Segment/active_plgs.py b/imagepy/menus/Process/Segment/active_plgs.py index b01a9e85..f099fae5 100644 --- a/imagepy/menus/Process/Segment/active_plgs.py +++ b/imagepy/menus/Process/Segment/active_plgs.py @@ -81,7 +81,7 @@ class MorphGeoChanVese(Filter): def preview(self, ips, para): snap, img = ips.snap, ips.img gimage = inverse_gaussian_gradient(img_as_float(snap)) - init = np.ones(img.shape, dtype=np.bool) + init = np.ones(img.shape, dtype='bool') msk = morphological_geodesic_active_contour(gimage, para['iter'], init_level_set=init, smoothing=para['smooth'], threshold='auto' if para['auto'] else para['thr'], balloon=para['balloon']) > 0 @@ -94,7 +94,7 @@ def run(self, ips, snap, img, para = None): stackimg = [] callback = lambda x: stackimg.append((x*255).astype(np.uint8)) if para['sub'] else 0 gimage = inverse_gaussian_gradient(img_as_float(snap)) - init = np.ones(img.shape, dtype=np.bool) + init = np.ones(img.shape, dtype='bool') msk = morphological_geodesic_active_contour(gimage, para['iter'], init_level_set=init, smoothing=para['smooth'], threshold='auto' if para['auto'] else para['thr'], diff --git a/imagepy/menus/Process/Segment/graph_plgs.py b/imagepy/menus/Process/Segment/graph_plgs.py index c44f4a83..7e0ff517 100644 --- a/imagepy/menus/Process/Segment/graph_plgs.py +++ b/imagepy/menus/Process/Segment/graph_plgs.py @@ -1,6 +1,6 @@ from sciapp.action import Simple from skimage import data, io, segmentation, color -from skimage.future import graph +from skimage import graph import numpy as np class RagThreshold(Simple): diff --git a/imagepy/tools/Draw/aibrush_tol.py b/imagepy/tools/Draw/aibrush_tol.py index 5a50caba..cb1b8da0 100644 --- a/imagepy/tools/Draw/aibrush_tol.py +++ b/imagepy/tools/Draw/aibrush_tol.py @@ -2,14 +2,14 @@ import numpy as np from time import time from skimage.morphology import flood_fill, flood -from skimage.draw import line, circle +from skimage.draw import line, disk from skimage.segmentation import felzenszwalb from sciapp.util import mark2shp from scipy.ndimage import binary_fill_holes, binary_dilation, binary_erosion def fill_normal(img, r, c, color, con, tor): img = img.reshape((img.shape+(1,))[:3]) - msk = np.ones(img.shape[:2], dtype=np.bool) + msk = np.ones(img.shape[:2], dtype='bool') for i in range(img.shape[2]): msk &= flood(img[:,:,i], (r, c), connectivity=con, tolerance=tor) img[msk] = color @@ -21,7 +21,7 @@ def local_brush(img, back, r, c, color, sigma, msize): def local_pen(img, r, c, R, color): img = img.reshape((img.shape+(1,))[:3]) - rs, cs = circle(r, c, R/2+1e-6, shape=img.shape) + rs, cs = disk((r, c), R/2+1e-6, shape=img.shape) img[rs, cs] = color def local_in_fill(img, r, c, R, color, bcolor): @@ -29,7 +29,7 @@ def local_in_fill(img, r, c, R, color, bcolor): msk = (img == color).min(axis=2) filled = binary_fill_holes(msk) filled ^= msk - rs, cs = circle(r, c, R/2+1e-6, shape=img.shape) + rs, cs = disk((r, c), R/2+1e-6, shape=img.shape) msk[:] = 0 msk[rs, cs] = 1 msk &= filled @@ -38,7 +38,7 @@ def local_in_fill(img, r, c, R, color, bcolor): def local_out_fill(img, r, c, R, color, bcolor): img = img.reshape((img.shape+(1,))[:3]) msk = (img != color).max(axis=2) - rs, cs = circle(r, c, R/2+1e-6, shape=img.shape) + rs, cs = disk((r, c), R/2+1e-6, shape=img.shape) buf = np.zeros_like(msk) buf[rs, cs] = 1 msk &= buf @@ -49,7 +49,7 @@ def local_sketch(img, r, c, R, color, bcolor): msk = (img == color).min(axis=2) dilation = binary_dilation(msk, np.ones((3,3))) dilation ^= msk - rs, cs = circle(r, c, R/2+1e-6, shape=img.shape) + rs, cs = disk((r, c), R/2+1e-6, shape=img.shape) msk[:] = 0 msk[rs, cs] = 1 msk &= dilation @@ -57,7 +57,7 @@ def local_sketch(img, r, c, R, color, bcolor): def global_both_line(img, r, c, color): img = img.reshape((img.shape+(1,))[:3]) - msk = np.ones(img.shape[:2], dtype=np.bool) + msk = np.ones(img.shape[:2], dtype='bool') for i in range(img.shape[2]): msk &= flood(img[:,:,i], (r, c), connectivity=2) dilation = binary_dilation(msk, np.ones((3,3))) @@ -66,7 +66,7 @@ def global_both_line(img, r, c, color): def global_out_line(img, r, c, color): img = img.reshape((img.shape+(1,))[:3]) - msk = np.ones(img.shape[:2], dtype=np.bool) + msk = np.ones(img.shape[:2], dtype='bool') for i in range(img.shape[2]): msk &= flood(img[:,:,i], (r, c), connectivity=2) msk = binary_fill_holes(msk) @@ -76,7 +76,7 @@ def global_out_line(img, r, c, color): def global_in_line(img, r, c, color): img = img.reshape((img.shape+(1,))[:3]) - msk = np.ones(img.shape[:2], dtype=np.bool) + msk = np.ones(img.shape[:2], dtype='bool') for i in range(img.shape[2]): msk &= flood(img[:,:,i], (r, c), connectivity=2) inarea = binary_fill_holes(msk) @@ -86,7 +86,7 @@ def global_in_line(img, r, c, color): def global_in_fill(img, r, c, color): img = img.reshape((img.shape+(1,))[:3]) - msk = np.ones(img.shape[:2], dtype=np.bool) + msk = np.ones(img.shape[:2], dtype='bool') for i in range(img.shape[2]): msk &= flood(img[:,:,i], (r, c), connectivity=2) filled = binary_fill_holes(msk) @@ -95,7 +95,7 @@ def global_in_fill(img, r, c, color): def global_out_fill(img, r, c, color): img = img.reshape((img.shape+(1,))[:3]) - ori = np.ones(img.shape[:2], dtype=np.bool) + ori = np.ones(img.shape[:2], dtype='bool') for i in range(img.shape[2]): ori &= flood(img[:,:,i], (r, c), connectivity=2) filled = binary_fill_holes(ori) diff --git a/imagepy/tools/Draw/floodfill_tol.py b/imagepy/tools/Draw/floodfill_tol.py index cae0df04..3a493bef 100644 --- a/imagepy/tools/Draw/floodfill_tol.py +++ b/imagepy/tools/Draw/floodfill_tol.py @@ -25,7 +25,7 @@ def mouse_down(self, ips, x, y, btn, **key): ips.snapshot() connectivity=(self.para['con']=='8-connect')+1 img = ips.img.reshape((ips.img.shape+(1,))[:3]) - msk = np.ones(img.shape[:2], dtype=np.bool) + msk = np.ones(img.shape[:2], dtype='bool') for i in range(img.shape[2]): msk &= flood(img[:,:,i], (int(y),int(x)), connectivity=connectivity, tolerance=self.para['tor']) diff --git a/imagepy/tools/Standard/magic_tol.py b/imagepy/tools/Standard/magic_tol.py index 1ae90ac1..7a99b63e 100644 --- a/imagepy/tools/Standard/magic_tol.py +++ b/imagepy/tools/Standard/magic_tol.py @@ -28,7 +28,7 @@ def polygonize(conts, withholes = True): def magic_cont(img, x, y, conn, tor): img = img.reshape((img.shape+(1,))[:3]) - msk = np.ones(img.shape[:2], dtype=np.bool) + msk = np.ones(img.shape[:2], dtype='bool') for i in range(img.shape[2]): msk &= flood(img[:,:,i], (int(y),int(x)), connectivity=conn, tolerance=tor) diff --git a/imagepy/tools/Standard/painter_tol.py b/imagepy/tools/Standard/painter_tol.py index 5642b348..6a2dcf5a 100644 --- a/imagepy/tools/Standard/painter_tol.py +++ b/imagepy/tools/Standard/painter_tol.py @@ -1,10 +1,10 @@ from sciapp.action import ImageTool -from skimage.draw import line, circle +from skimage.draw import line, disk def drawline(img, oldp, newp, w, value): if img.ndim == 2 and hasattr(value, '__iter__'): value = sum(value)/3 oy, ox = line(*[int(round(i)) for i in oldp+newp]) - cy, cx = circle(0, 0, w/2+1e-6) + cy, cx = disk((0, 0), w/2+1e-6) ys = (oy.reshape((-1,1))+cy).clip(0, img.shape[0]-1) xs = (ox.reshape((-1,1))+cx).clip(0, img.shape[1]-1) img[ys.ravel(), xs.ravel()] = value diff --git a/sciapp/action/advanced/dataio.py b/sciapp/action/advanced/dataio.py index 8824ba5a..375b9b7d 100644 --- a/sciapp/action/advanced/dataio.py +++ b/sciapp/action/advanced/dataio.py @@ -20,7 +20,10 @@ def show(self): def run(self, para = None): #add_recent(para['path']) fp, fn = os.path.split(para['path']) - fn, fe = os.path.splitext(fn) + fn = fn.split('.') + print(fn) + fn, fe = fn[0], '.'+'.'.join(fn[1:]) + # fn, fe = os.path.splitext(fn) readers = ReaderManager.gets(fe[1:].lower(), tag=self.tag) if len(readers)==0: return self.app.alert('no reader found for %s file'%fe[1:]) diff --git a/sciapp/action/plugin/shpbase.py b/sciapp/action/plugin/shpbase.py index e5bc360c..394488ef 100644 --- a/sciapp/action/plugin/shpbase.py +++ b/sciapp/action/plugin/shpbase.py @@ -292,7 +292,7 @@ def mouse_move(self, shp, x, y, btn, **key): pts = np.vstack(pts) key['canvas'].marks['anchor'] = Points(pts, color=(255,0,0)) self.p = x, y - self.pick_m.dirty =shp.dirty = True + self.pick_m.dirty = shp.dirty = True def mouse_wheel(self, shp, x, y, d, **key): if d>0: key['canvas'].zoomout(x, y, coord='data') diff --git a/sciapp/action/tolact.py b/sciapp/action/tolact.py index 8d5934db..faa85043 100644 --- a/sciapp/action/tolact.py +++ b/sciapp/action/tolact.py @@ -69,7 +69,10 @@ class ShapeTool(DefaultTool): default = None title = 'Shape Tool' - def mouse_move(self, img, x, y, btn, **key): + def mouse_move(self, shp, x, y, btn, **key): + DefaultTool.mouse_move(self, shp, x, y, btn, **key) + if self.app is None: return + r, c = int(y), int(x) if self.app: self.app.info('%d, %d'%(x, y)) def start(self, app, para=None, callafter=None): diff --git a/sciapp/object/roi.py b/sciapp/object/roi.py index f961659c..214b3bd2 100644 --- a/sciapp/object/roi.py +++ b/sciapp/object/roi.py @@ -39,7 +39,7 @@ def to_mask(self, msk, mode): if isinstance(mode, int): draw_shp(self.to_geom(), msk, 1, mode) np.clip(msk, 0, 1, out=msk) - msk.dtype, self.msk = np.bool, mode + msk.dtype, self.msk = 'bool', mode return msk class Measure(Layer): diff --git a/sciapp/object/shape.py b/sciapp/object/shape.py index e20bcea2..bbf7e060 100644 --- a/sciapp/object/shape.py +++ b/sciapp/object/shape.py @@ -1,6 +1,6 @@ import numpy as np from time import time -from collections import Iterable +from collections.abc import Iterable import shapely.geometry as geom from shapely.ops import unary_union @@ -266,11 +266,13 @@ class Text(Shape): dtype = 'text' def __init__(self, body=[], **key): Shape.__init__(self, body, **key) + self.offset = key.get('offset', [0,0]) self.body = np.array(body[:2], dtype=np.float32) self.txt = body[2] def to_mark(self): - return Shape.to_mark(self, tuple(self.body.tolist())+(self.txt,)) + mark = Shape.to_mark(self, tuple(self.body.tolist())+(self.txt,)) + mark['offset'] = self.offset; return mark def to_geom(self): return geom.Point(self.body) @@ -279,12 +281,14 @@ class Texts(Shape): dtype = 'texts' def __init__(self, body=[], **key): Shape.__init__(self, body, **key) - body = np.array(body, dtype=object) + self.offset = key.get('offset', [0,0]) + body = np.array(body, dtype=object).reshape(-1,3) self.body = body[:,:2].astype(np.float32) self.txt = body[:,2] def to_mark(self): - return Shape.to_mark(self, [(i,j,t) for (i,j), t in zip(self.body.tolist(), self.txt.tolist())]) + mark = Shape.to_mark(self, [(i,j,t) for (i,j), t in zip(self.body.tolist(), self.txt.tolist())]) + mark['offset'] = self.offset; return mark def to_geom(self): return geom.MultiPoint(self.body) diff --git a/sciapp/util/shputil.py b/sciapp/util/shputil.py index d30f60a5..b5cd9506 100644 --- a/sciapp/util/shputil.py +++ b/sciapp/util/shputil.py @@ -49,7 +49,7 @@ def geom_flatten(obj, geoms=None): if isinstance(obj, geom.GeometryCollection): for i in obj: geom_flatten(i, geoms) elif type(obj) in {geom.MultiPolygon, geom.MultiPoint, geom.MultiLineString}: - geoms.extend(list(obj)) + geoms.extend(list(obj.geoms)) else: geoms.append(obj) if root: return geom.GeometryCollection(geoms) diff --git a/sciwx/__init__.py b/sciwx/__init__.py index 72e33fc0..2a406043 100644 --- a/sciwx/__init__.py +++ b/sciwx/__init__.py @@ -7,7 +7,7 @@ cm = plt.get_cmap(i) if i[-2:]=='_r': continue vs = np.linspace(0, cm.N, 256, endpoint=False) - lut = cm(vs.astype(np.int), bytes=True)[:,:3] + lut = cm(vs.astype(np.uint8), bytes=True)[:,:3] ColorManager.add(i, lut) del plt graylut = ColorManager.get('gray') diff --git a/sciwx/canvas/__init__.py b/sciwx/canvas/__init__.py index 5e7a0026..800fe320 100644 --- a/sciwx/canvas/__init__.py +++ b/sciwx/canvas/__init__.py @@ -1,4 +1,4 @@ #from .canvas import Canvas -from .mcanvas import MCanvas, ICanvas, VCanvas +from .mcanvas import MCanvas, ICanvas, VCanvas, Canvas from .widget import CanvasFrame, CanvasNoteBook, CanvasNoteFrame from .vwidget import VectorFrame, VectorNoteBook, VectorNoteFrame \ No newline at end of file diff --git a/sciwx/canvas/canvas.py b/sciwx/canvas/canvas.py index 2e3d305d..787683cf 100644 --- a/sciwx/canvas/canvas.py +++ b/sciwx/canvas/canvas.py @@ -8,11 +8,12 @@ class Canvas (wx.Panel): scales = [0.03125, 0.0625, 0.125, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4, 5, 8, 10, 15, 20, 30, 50] - def __init__(self, parent, autofit=False, ingrade=False, up=False): + def __init__(self, parent, autofit=False, within=False, ingrade=False, up=False): wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.TAB_TRAVERSAL ) self.winbox = None + self.within = within self.conbox = [0,0,1,1] self.oribox = [0,0,1,1] @@ -119,7 +120,7 @@ def draw_image(self, dc, img, back, mode): if min(csbox[2]-csbox[0], csbox[3]-csbox[1])<5: return shp = csbox[3]-csbox[1], csbox[2]-csbox[0] o, m = mat(self.oribox, self.conbox, cellbox, csbox) - shp = tuple(np.array(shp).round().astype(np.int)) + shp = tuple(np.array(shp).round().astype(np.int32)) if out is None or (out.shape, out.dtype) != (shp, img.dtype): self.outimg = np.zeros(shp, dtype=img.dtype) if not back is None and not back.img is None and ( @@ -139,21 +140,28 @@ def draw_image(self, dc, img, back, mode): img.log, cns=img.cn, mode=img.mode) self.outbmp.CopyFromBuffer(memoryview(self.outrgb)) - dc.DrawBitmap(self.outbmp, *csbox[:2]) + dc.DrawBitmap(self.outbmp, int(csbox[0]), int(csbox[1])) def update(self, counter = [0,0]): #self.update_box() + #if self.conbox[2] - self.conbox[0]>1: self.update_box() + if None in [self.winbox, self.conbox]: return - if self.first: + if self.within :lay(self.winbox, self.conbox) + + if self.first and self.conbox[2] - self.conbox[0]>1: self.first = False return self.fit() + counter[0] += 1 start = time() # lay(self.winbox, self.conbox) dc = wx.BufferedDC(wx.ClientDC(self), self.buffer) #dc.SetBackground(wx.Brush((255,255,255))) dc.Clear() - for i in self.images: self.draw_image(dc, i, i.back, 0) + for i in self.images: + if i.img is None: continue + self.draw_image(dc, i, i.back, 0) for i in self.marks.values(): if i is None: continue @@ -190,10 +198,11 @@ def move(self, dx, dy, coord='win'): self.update() def on_size(self, event): - if max(self.GetClientSize())>20 and self.images[0].img is not None: + if max(self.GetClientSize())>20: # and self.images[0].img is not None: self.initBuffer() - if len(self.images)+len(self.marks)==0: return - if self.conbox[2] - self.conbox[0] > 1: self.update() + return self.update() + # if len(self.images)+len(self.marks)==0: return + # if self.conbox[2] - self.conbox[0] > 1: self.update() def on_idle(self, event): need = sum([i.dirty for i in self.images]) @@ -260,7 +269,7 @@ def to_panel_coor(self, x, y): x += -self.oribox[0] * self.scale + self.conbox[0] y += -self.oribox[1] * self.scale + self.conbox[1] if self.up: y = (self.winbox[3]-self.winbox[1]) - y - return x, y + return (x+0.5).astype(np.int32), (y+0.5).astype(np.int32) def save_buffer(self, path): dcSource = wx.BufferedDC(wx.ClientDC(self), self.buffer) @@ -308,6 +317,9 @@ def __del__(self): app = wx.App() frame = wx.Frame(None, title='Canvas') canvas = Canvas(frame, autofit=False, ingrade=True, up=True) + + ''' + canvas = Canvas(frame, autofit=False, ingrade=True, up=True) line = mark2shp({'type':'polygon', 'color':(255,0,0), 'lstyle':'o', 'fill':True, 'body':[[(0,0),(1000,1000),(2000,0), (0,0)], @@ -327,11 +339,12 @@ def __del__(self): canvas.marks.append(layer) ''' + image = Image() image.img = camera() image.pos = (0,0) canvas.images.append(image) - ''' + ''' image = Image() image.img = astronaut() diff --git a/sciwx/canvas/mark.py b/sciwx/canvas/mark.py index 392e22f1..bb47863e 100644 --- a/sciwx/canvas/mark.py +++ b/sciwx/canvas/mark.py @@ -75,7 +75,7 @@ def plot(pts, dc, f, **key): r = 2 x, y = pts.body.T[:2] xy = np.vstack(f(x, y)).T - lst = np.hstack((xy-r, np.ones((len(x),2))*(r*2))) + lst = np.hstack((xy-r, np.ones((len(x),2), dtype=np.int32)*(r*2))) isline = pts.lstyle and '-' in pts.lstyle ispoint = pts.lstyle and 'o' in pts.lstyle if pts.dtype == 'polygon': @@ -160,12 +160,12 @@ def draw_circle(pts, dc, f, **key): if pts.dtype == 'circle': x, y ,r = pts.body x, y = f(x, y) - dc.DrawCircle(x, y, r*key['k']) + dc.DrawCircle(x, y, int(r*key['k']+0.5)) if pts.dtype == 'circles': lst = [] x, y, r = pts.body.T x, y = f(x, y) - r = r * key['k'] + r = (r * key['k'] + 0.5).astype(np.int32) lst = np.vstack([x-r, y-r, r*2, r*2]).T dc.DrawEllipseList(lst) @@ -283,17 +283,21 @@ def draw_text(pts, dc, f, **key): if pts.dtype == 'text': (x, y), text = pts.body, pts.txt + ox, oy = pts.offset x, y = f(x, y) - dc.DrawText(text, x+1, y+1) + dc.DrawText(text, x+1+ox, y+1+oy) if not pts.lstyle is None: - dc.DrawEllipse(x-2,y-2,4,4) + dc.DrawEllipse(x+ox-2,y+oy-2,4,4) if pts.dtype == 'texts': tlst, clst, elst = [], [], [] x, y = pts.body.T + ox, oy = pts.offset tlst = pts.txt + x, y = f(x, y) + x += ox; y += oy; r = x * 0 + 4 - dc.DrawTextList(tlst, np.vstack((x,y)).T) + dc.DrawTextList(tlst, np.vstack((x+1,y+1)).T) if pts.fill: dc.DrawEllipseList(np.vstack((x,y,r,r)).T) diff --git a/sciwx/canvas/mcanvas.py b/sciwx/canvas/mcanvas.py index 20a97027..93662997 100644 --- a/sciwx/canvas/mcanvas.py +++ b/sciwx/canvas/mcanvas.py @@ -6,8 +6,8 @@ from sciapp.action import Tool, ImageTool, ShapeTool class ICanvas(Canvas): - def __init__(self, parent, autofit=False): - Canvas.__init__(self, parent, autofit) + def __init__(self, parent, autofit=False, within=False, ingrade=False, up=False): + Canvas.__init__(self, parent, autofit, within, ingrade, up) self.images.append(Image()) #self.images[0].back = Image() self.Bind(wx.EVT_IDLE, self.on_idle) @@ -91,8 +91,8 @@ def on_idle(self, event): Canvas.on_idle(self, event) class VCanvas(Canvas): - def __init__(self, parent, autofit=False, ingrade=True, up=True): - Canvas.__init__(self, parent, autofit, ingrade, up) + def __init__(self, parent, autofit=False, within=False, ingrade=True, up=True): + Canvas.__init__(self, parent, autofit, within, ingrade, up) def get_obj_tol(self): return self.shape, ShapeTool.default diff --git a/sciwx/demo/shape_frame_demo.py b/sciwx/demo/shape_frame_demo.py index f1be3a01..055daf2c 100644 --- a/sciwx/demo/shape_frame_demo.py +++ b/sciwx/demo/shape_frame_demo.py @@ -48,8 +48,9 @@ def note_frame_test(): frame.Show() if __name__ == '__main__': + from sciapp.action import ShapeTool app = wx.App() - # frame_test() + frame_test() # note_test() - note_frame_test() + # note_frame_test() app.MainLoop() diff --git a/sciwx/text/mdpad.py b/sciwx/text/mdpad.py index 83d9ecf1..b0045d52 100644 --- a/sciwx/text/mdpad.py +++ b/sciwx/text/mdpad.py @@ -5,7 +5,7 @@ from .mdutil import md2html class MDPad(wx.Panel): - def __init__(self, parent, cont='', url='', title='markdown pad'): + def __init__(self, parent, cont='', url='', home='.', title='markdown pad'): wx.Panel.__init__(self, parent) sizer = wx.BoxSizer(wx.VERTICAL) self.wv = webview.WebView.New(self) @@ -13,17 +13,19 @@ def __init__(self, parent, cont='', url='', title='markdown pad'): sizer.Add(self.wv, 1, wx.EXPAND) self.SetSizer(sizer) self.num = 0 + self.home = home if url != '': self.load_url(url) - elif cont!='': self.set_cont(cont) + elif cont!='': self.set_cont(cont, home) self.title = title - def set_cont(self, value, url=''): - return self.wv.SetPage(md2html(value), url) + def set_cont(self, value): + value = value.replace('](./', '](%s/'%self.home) + # return self.wv.SetPage(md2html(value), '') # I do not know why use SetPage the js would not run, So I write a file here here = osp.split(osp.abspath(__file__)) for n in range(1,10): path = osp.join(here[0], 'index%s.htm'%n) - if not osp.exists(path):break + if not osp.exists(path): break self.num = n with open(path, 'w') as f: f.write(md2html(value)) @@ -101,16 +103,16 @@ def __init__(self, parent, title='MarkDownNoteFrame'): self.Layout() if __name__ == '__main__': - ''' app = wx.App() frame = wx.Frame(None) - mnb = MDNoteBook(frame) - mdpanel1 = mnb.add_page('markdown1') - mdpanel1.set_cont('abc') - mdpanel2 = mnb.add_page('markdown2') - mdpanel2.set_cont('def') + + with open('./test.md', encoding='utf-8') as f: + cont = f.read() + mpd = MDPad(frame, home='E:/opensource/imagepy/sciwx/text') + mpd.set_cont(cont) frame.Show() app.MainLoop() + ''' app = wx.App() mnf = MDNoteFrame(None) @@ -120,3 +122,4 @@ def __init__(self, parent, title='MarkDownNoteFrame'): mdpanel2.set_cont('def') mnf.Show() app.MainLoop() + ''' diff --git a/sciwx/widgets/colormap.py b/sciwx/widgets/colormap.py index b28a51cd..e7f78255 100644 --- a/sciwx/widgets/colormap.py +++ b/sciwx/widgets/colormap.py @@ -39,8 +39,8 @@ def OnDrawItem(self, dc, rect, item, flags): # for painting the items in the popup dc.DrawText(self.GetString( item), - r.x + 3, - (r.y + 0) + ( (r.height/2) - dc.GetCharHeight() )/2 + int(r.x+0.5) + 3, + int((r.y + 0) + ((r.height/2) - dc.GetCharHeight())/2 + 0.5) ) #dc.DrawLine( r.x+5, r.y+((r.height/4)*3)+1, r.x+r.width - 5, r.y+((r.height/4)*3)+1 ) arr = np.zeros((10,256,3),dtype=np.uint8) diff --git a/sciwx/widgets/curvepanel.py b/sciwx/widgets/curvepanel.py index 58443ccc..40152422 100644 --- a/sciwx/widgets/curvepanel.py +++ b/sciwx/widgets/curvepanel.py @@ -104,7 +104,7 @@ def set_hist(self, hist): self.update() def set_pts(self, pts): - self.x1, self.x2 = x1, x2 + self.x1, self.x2 = int(x1+0.5), int(x2+0.5) self.update() def draw(self): @@ -118,11 +118,11 @@ def draw(self): if not self.hist is None: dc.SetPen(wx.Pen((200,200,200), width=1, style=wx.SOLID)) - for i in np.linspace(0,self.l,256).astype(np.int16): - dc.DrawLine(i+ox,self.l+1+oy,i+ox,self.l+1-self.logh[i]+oy) + for i in np.linspace(0,self.l,256).astype(np.int32): + dc.DrawLine(i+ox,self.l+1+oy,i+ox, int(self.l+1-self.logh[i]+oy+0.5)) dc.SetPen(wx.Pen((100,100,100), width=1, style=wx.SOLID)) - for i in np.linspace(0,self.l,256).astype(np.int16): - dc.DrawLine(i+ox,self.l+1+oy,i+ox,self.l+1-self.hist[i]+oy) + for i in np.linspace(0,self.l,256).astype(np.int32): + dc.DrawLine(i+ox,self.l+1+oy,i+ox,int(self.l+1-self.hist[i]+oy+0.5)) x, y = np.array(self.pts).T kind = 'linear' if len(self.pts)==2 else 'quadratic' f = interpolate.interp1d(x, y, kind=kind) @@ -133,10 +133,10 @@ def draw(self): for i in self.pts: dc.DrawCircle(round(i[0]*self.k+ox), round(self.l+1-i[1]*self.k+oy), 2) xs, ys = np.linspace(0,self.l, self.l+1)+ox, self.l+1-ys*self.k+oy - dc.DrawPointList(list(zip(xs.round(), ys.round()))) + dc.DrawPointList(list(zip(xs.astype(np.int32), ys.astype(np.int32)))) dc.SetPen(wx.Pen((0,0,0), width=1, style=wx.SOLID)) - for i in np.linspace(0, self.l+1, 5): + for i in np.linspace(0, self.l+1, 5).round().astype(np.int32): dc.DrawLine(0+ox, i+oy, self.l+1+ox, i+oy) dc.DrawLine(i+ox, 0+oy, i+ox, self.l+1+oy) dc.SetBrush(wx.Brush((0,0,0), wx.BRUSHSTYLE_TRANSPARENT)) diff --git a/sciwx/widgets/histpanel.py b/sciwx/widgets/histpanel.py index 217a2242..650b573c 100644 --- a/sciwx/widgets/histpanel.py +++ b/sciwx/widgets/histpanel.py @@ -58,12 +58,12 @@ def draw(self): hist = self.hist[np.linspace(0, len(self.hist)-1, self.w, dtype=np.int16)] dc.SetPen(wx.Pen((200,200,200), width=1, style=wx.SOLID)) for i in range(self.w): - dc.DrawLine(i,self.h,i,self.h-self.logh[i]) + dc.DrawLine(i,self.h,i,self.h-int(self.logh[i]+0.5)) dc.SetPen(wx.Pen((100,100,100), width=1, style=wx.SOLID)) for i in range(self.w): - dc.DrawLine(i,self.h,i,self.h-self.hist[i]) + dc.DrawLine(i,self.h,i,self.h-int(self.hist[i]+0.5)) dc.SetPen(wx.Pen((0,0,0), width=1, style=wx.SOLID)) - dc.DrawLine(self.x1, self.h, self.x2, 0) + dc.DrawLine(int(self.x1+0.5), self.h, int(self.x2+0.5), 0) dc.DrawLines([(0,0),(self.w-1,0),(self.w-1,self.h),(0,self.h),(0,0)]) if __name__ == '__main__': diff --git a/sciwx/widgets/normal.py b/sciwx/widgets/normal.py index a5c21d7d..170597cd 100644 --- a/sciwx/widgets/normal.py +++ b/sciwx/widgets/normal.py @@ -164,8 +164,9 @@ def onselect(self, event): filt = '|'.join(['%s files (*.%s)|*.%s'%(i.upper(),i,i) for i in self.filt]) dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} if self.io=='folder': - dialog = wx.DirDialog(self, 'Path Select', '.', wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST | wx.FD_CHANGE_DIR) + dialog = wx.DirDialog(self, 'Path Select', '', wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST | wx.FD_CHANGE_DIR) else: dialog = wx.FileDialog(self, 'Path Select', '', '.', filt, dic[self.io] | wx.FD_CHANGE_DIR) + rst = dialog.ShowModal() if rst == wx.ID_OK: path = dialog.GetPath() diff --git a/sciwx/widgets/viewport.py b/sciwx/widgets/viewport.py index c901db58..28a1f9dc 100644 --- a/sciwx/widgets/viewport.py +++ b/sciwx/widgets/viewport.py @@ -73,13 +73,13 @@ def set_img(self, img, size): bmp = wx.Bitmap.FromBuffer(img.shape[1], img.shape[0], memoryview(img.copy())) if 1.0*self.box[0]/self.box[1]<1.0*self.ibox[0]/self.ibox[1]: k = 1.0*self.box[0]/self.ibox[0] - self.imgw, self.imgh = self.box[0], self.ibox[1]*k - self.offx, self.offy = 0, (self.box[1]-self.imgh)/2.0 + self.imgw, self.imgh = int(self.box[0]+0.5), int(self.ibox[1]*k+0.5) + self.offx, self.offy = 0, int((self.box[1]-self.imgh)/2.0+0.5) self.img = bmp.ConvertToImage().Rescale(self.imgw, self.imgh).ConvertToBitmap() else: k = 1.0*self.box[1]/self.ibox[1] - self.imgw, self.imgh = self.ibox[0]*k, self.box[1] - self.offx, self.offy = (self.box[0]-self.imgw)/2.0, 0 + self.imgw, self.imgh = int(self.ibox[0]*k+0.5), int(self.box[1]+0.5) + self.offx, self.offy = int((self.box[0]-self.imgw)/2.0+0.5), 0 self.img = bmp.ConvertToImage().Rescale(self.imgw, self.imgh).ConvertToBitmap() def set_box(self, boximg, boxpan): @@ -109,7 +109,7 @@ def draw(self): dc.DrawRectangle(self.offx, self.offy, self.imgw, self.imgh) x,y,w,h = l*self.imgw+self.offx, t*self.imgh+self.offy,(r-l)*self.imgw,(b-t)*self.imgh dc.SetPen(wx.Pen((255,0,0), width=1, style=wx.SOLID)) - dc.DrawRectangle(x,y,w,h) + dc.DrawRectangle(int(x), int(y), int(w), int(h)) def on_view(self, event): print(event)