diff --git a/CHANGELOG.md b/CHANGELOG.md old mode 100755 new mode 100644 index 0aae298..f4f24ab --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,7 +29,12 @@ directory structures. when `python-bioformats` or `javabridge` are not installed. 16. (v0.92 20230207) Multiple image series in a file is supported but only the first one is used. Also imgio has been a lot changed; - `tifffile` version had been upgraded and ome.tif is written out by tifffile. + `tifffile` version had been upgraded and ome.tif is written out + by tifffile. Furthermore, with the `nd2` package, nd2 files are + directly read without installing bioformats. +17. (v0.93 20230707) Writing ome.tif is compatible for +`tifffile` 2021.7.2. +18. (v0.93 20230709) Bug fix for DeltaVision deconvolved files. v0.8(May. 2019) ---- diff --git a/Chromagnon.spec b/Chromagnon.spec index 5d9f877..911c4f0 100644 --- a/Chromagnon.spec +++ b/Chromagnon.spec @@ -53,21 +53,34 @@ if sys.platform.startswith('win'): code=os.path.abspath(os.path.join('Z:', 'py')) src = os.path.abspath(os.path.join('Z:', 'src', 'Chromagnon', 'Chromagnon')) - - binaries = [(os.path.join(libbin, 'mkl_avx.dll'), '.'), (os.path.join(libbin, 'mkl_avx2.dll'), '.')] + # if os.path.isfile(os.path.join(libbin, 'mkl_avx.dll')): + # binaries = [(os.path.join(libbin, 'mkl_avx.dll'), '.')]#, (os.path.join(libbin, 'mkl_avx2.dll'), '.')] + # elif os.path.isfile(os.path.join(libbin, 'mkl_avx.2.dll')): + # binaries = [(os.path.join(libbin, 'mkl_avx.2.dll'), '.')] if not PLUGIN: - if pyversion == 3: - glut = 'glut64.dll' - elif pyversion == 2: + if os.path.isfile(os.path.join(libbin, 'freeglut.dll')): glut = os.path.join(libbin, 'freeglut.dll') + else: + glut = 'glut64.dll' + #if pyversion == 3: + # glut = 'glut64.dll' + #elif pyversion == 2: + # glut = os.path.join(libbin, 'freeglut.dll') binaries += [(os.path.join(home, conda, glut), '.')] pylib = 'pyd' jvm = 'jvm.dll' suffix = 'Win' + ex_suffix else: # mac + linux - code=os.path.join(home, 'codes', 'py') - src = os.path.join(home, 'codes', 'src', 'Chromagnon', 'Chromagnon') + if os.path.isdir(os.path.expanduser('~/codes/py')): + CODE='codes' + elif os.path.isdir(os.path.expanduser('~/local/py')): + CODE='local' + else: + raise ValueError('directory for codes not found') + + code=os.path.join(home, CODE, 'py') + src = os.path.join(home, CODE, 'src', 'Chromagnon', 'Chromagnon') site=os.path.join(conda, 'lib', 'python%i.%i' % (pyversion, sys.version_info.minor), 'site-packages') print('site is', site) @@ -104,13 +117,31 @@ else: cversion = '' # ------- pyinstaller -datas = [(os.path.join(code, 'Priithon', '*.py'), 'Priithon'), (os.path.join(code, 'Priithon', 'plt', '*.py'), os.path.join('Priithon', 'plt')), (os.path.join(code, 'PriCommon', '*.py'), 'PriCommon'), (os.path.join(code, 'common', '*.py'), 'common'), (os.path.join(site, 'tifffile', '*.%s' % pylib), 'tifffile')] +datas = [(os.path.join(code, 'Priithon', '*.py'), 'Priithon'), (os.path.join(code, 'Priithon', 'plt', '*.py'), os.path.join('Priithon', 'plt')), (os.path.join(code, 'PriCommon', '*.py'), 'PriCommon'), (os.path.join(code, 'common', '*.py'), 'common')]#, (os.path.join(site, 'tifffile', '*.%s' % pylib), 'tifffile')] if 0:#sys.platform.startswith('linux'): datas += [(os.path.join(pylibpath, 'libglut.*'), 'lib')] if not PLUGIN: #datas += [(os.path.join(site, 'javabridge', '*.%s' % pylib), 'javabridge'), (os.path.join(site, 'javabridge', 'jars', '*'), os.path.join('javabridge', 'jars')), (os.path.join(site, 'bioformats', 'jars', '*'), os.path.join('bioformats', 'jars'))] - datas += [(os.path.join(site, 'javabridge', 'jars', '*'), os.path.join('javabridge', 'jars')), (os.path.join(site, 'bioformats', 'jars', '*'), os.path.join('bioformats', 'jars'))]#, (os.path.join(os.path.dirname(site), 'lib-dynload', '_posixsubprocess.cpython-37m-x86_64-linux-gnu.so'), os.path.join('lib', 'python3.7', 'lib-dynload'))] + try: + import javabridge, bioformats + jv = os.path.dirname(javabridge.__file__) + bf = os.path.dirname(os.path.dirname(bioformats.__file__)) + datas += [(os.path.join(jv, 'jars', '*'), os.path.join('javabridge', 'jars')), (os.path.join(bf, 'bioformats', 'jars', '*'), os.path.join('bioformats', 'jars'))] + except ImportError: + pass + try: + import nd2 + nd2 = os.path.dirname(nd2.__file__) + datas += [(os.path.join(nd2, '*.typed'), 'nd2')] + except ImportError: + pass + + #if 1:#sys.platform.startswith('win'): # v=0.7.1 + # datas += [(os.path.join(nd2, '*.typed'), 'nd2')] + #else: + # datas += [(os.path.join(nd2, '_sdk', '*'), os.path.join('nd2', '_sdk'))] + #datas += [(os.path.join(site, 'javabridge', 'jars', '*'), os.path.join('javabridge', 'jars')), (os.path.join(site, 'bioformats', 'jars', '*'), os.path.join('bioformats', 'jars'))] a = Analysis([prog], pathex=[code], diff --git a/Chromagnon/PriCommon/flatConv.py b/Chromagnon/PriCommon/flatConv.py index e0be962..15e445a 100755 --- a/Chromagnon/PriCommon/flatConv.py +++ b/Chromagnon/PriCommon/flatConv.py @@ -12,7 +12,7 @@ try: import wx import time - from common import guiFuncs as G + from . import guiFuncs as G _wx = True except ImportError: _wx = False @@ -263,7 +263,7 @@ def view(self, target): """ view with viewer """ - import ndviewer + from PriCommon import ndviewer import sys # prepare viewer if not self.aui: @@ -290,8 +290,8 @@ def view(self, target): usage = r"""%prog imgFiles [options]""" p = optparse.OptionParser(usage=usage) - p.add_option('--suffix', '-S', default=SUF, - help='suffix for the output file names (default inputfile + %s)' % SUF) + p.add_option('--out', '-O', + help='output file name (default inputfile + %s)' % EXT) p.add_option('--flatFile', '-F', help='flatFielding file required for flatfielding') p.add_option('--make', '-m', action='store_true', @@ -306,21 +306,25 @@ def view(self, target): else: make = options.make del options.make + #fns = arguments#[0] + #args = arguments[1:] fns = [] for fn in arguments: fns += glob.glob(os.path.expandvars(os.path.expanduser(fn))) if make: - outs = [makeFlatConv(fn, suffix=EXT) for fn in fns] + outs = [makeFlatConv(fn) for fn in fns] if len(outs) > 1: - out = os.path.commonprefix(outs) + EXT - out = imgio.merge(outs, out, along='w') + if options.out: + out = options.out + else: + out = os.path.commonprefix(outs) + EXT + out = O.copyImgs.merge(outs, out, mergeAlong='w') [os.remove(out) for out in outs] else: out = outs[0] print(out, ' saved') else: for fn in fns: - out = flatConv(fn, **options.__dict__) - print(out, ' done') + print(flatConv(fn, **options.__dict__), ' done') diff --git a/Chromagnon/PriCommon/imgFilters.py b/Chromagnon/PriCommon/imgFilters.py index a52c49b..4314838 100755 --- a/Chromagnon/PriCommon/imgFilters.py +++ b/Chromagnon/PriCommon/imgFilters.py @@ -1,10 +1,9 @@ #!/usr/bin/env python from __future__ import print_function - try: - from Priithon.all import N, U, Y, F + from ..Priithon.all import N, U, Y, F, Mrc except (ValueError, ImportError): - from ..Priithon.all import N, U, Y, F + from Priithon.all import N, U, Y, F, Mrc from scipy import optimize try: from . import imgFit @@ -955,7 +954,7 @@ def findMaxWithGFitAll(img, thre=0, sigma_peak=0.5, npts=100, win=11, mask_npxls else: v = ret[1] zyx = ret[2:2+ndim] - if N.any(N.abs(zyx - vzyx[-ndim:]) > win/2.):#zyx < 0 or zyx > img.shape or ): + if N.any(N.abs(zyx - vzyx[1:]) > win/2.):#zyx < 0 or zyx > img.shape or ): mask_value(img, vzyx[-ndim:], r=mask_npxls, value=img.min()) poses.append(list(vzyx)[0:1] + [vzyx[-ndim:]] + [sigma_peak]) else: diff --git a/Chromagnon/PriCommon/imgFit.py b/Chromagnon/PriCommon/imgFit.py index 7317858..702d664 100755 --- a/Chromagnon/PriCommon/imgFit.py +++ b/Chromagnon/PriCommon/imgFit.py @@ -1,9 +1,9 @@ ### Here all kinds of fitting from __future__ import print_function try: - from Priithon.all import N, U -except (ValueError, ImportError): from ..Priithon.all import N, U +except (ValueError, ImportError): + from Priithon.all import N, U try: from . import imgFilters, imgGeo except ValueError: diff --git a/Chromagnon/PriCommon/imgGeo.py b/Chromagnon/PriCommon/imgGeo.py index ccd9daa..4aacbe8 100755 --- a/Chromagnon/PriCommon/imgGeo.py +++ b/Chromagnon/PriCommon/imgGeo.py @@ -85,7 +85,7 @@ def affine(a, r, mag, tyx=(0,0), center=(0,0)): def affine_index(indexarray, r, mag, tyx=(0,0), center=None): if center is None: - center = N.array(indexarray.shape[-2:], N.float) / 2. + center = N.array(indexarray.shape[-2:], float) / 2. rotRadian = N.pi / 180. * r cosTheta = N.cos(rotRadian) diff --git a/Chromagnon/PriCommon/imgResample.py b/Chromagnon/PriCommon/imgResample.py index 34d6b9b..1caf7b2 100755 --- a/Chromagnon/PriCommon/imgResample.py +++ b/Chromagnon/PriCommon/imgResample.py @@ -1,10 +1,9 @@ try: - from Priithon.all import U, N - from common import ppro26 as ppro -except (ValueError, ImportError): from ..Priithon.all import U, N - from ..common import ppro26 as ppro +except (ValueError, ImportError): + from Priithon.all import U, N +from . import ppro26 as ppro NCPU = ppro.NCPU import scipy.ndimage.interpolation as ndii @@ -113,7 +112,7 @@ def trans3D_affine(arr, tzyx=(0,0,0), r=0, mag=1, dzyx=(0,0,0), rzy=0, ncpu=NCPU arr = canvas - if dtype in (N.int, N.uint8, N.uint16, N.uint32): + if dtype in (int, N.uint8, N.uint16, N.uint32): arr = N.where(arr < 0, 0, arr) return arr.astype(dtype) @@ -146,8 +145,8 @@ def getOffset(shape, invmat, ty, tx, start=0): def affine_transform(arr, invmat, offset=0.0, order=ORDER): return U.nd.affine_transform(arr, invmat, offset, - output=N.float32, cval=arr.min(), order=order) - #output=N.float32, cval=0, order=order) + #output=N.float32, cval=arr.min(), order=order) + output=N.float32, cval=0, order=order) # diff --git a/Chromagnon/PriCommon/xcorr.py b/Chromagnon/PriCommon/xcorr.py index 42a2d9a..97ce409 100755 --- a/Chromagnon/PriCommon/xcorr.py +++ b/Chromagnon/PriCommon/xcorr.py @@ -256,8 +256,6 @@ def Xcorr(a, b, phaseContrast=PHASE, nyquist=NYQUIST, gFit=True, win=11, ret=Non afa = af bfa = bf fact = N.std(a) * N.std(b) * a.size - if not fact: - fact = 1 del a, b, af, bf targetShape = shape #+ (npad * 2) diff --git a/Chromagnon/Priithon/plt/plot_objects.py b/Chromagnon/Priithon/plt/plot_objects.py index b08aa8a..77118ab 100755 --- a/Chromagnon/Priithon/plt/plot_objects.py +++ b/Chromagnon/Priithon/plt/plot_objects.py @@ -75,12 +75,13 @@ def draw_point_list(start,stop,pen,dc): start and stop are 2xN arrays of x,y point coordinates. N lines are drawn, one between each of the start,stop pairs using the specified pen. The lines are drawn on the given device context, dc. - """ + """ + #print('draw_point_list') dc.SetPen(pen) for i in range(len(start)): pt1 = start[i] pt2 = stop[i] - dc.DrawLine(pt1[0],pt1[1],pt2[0],pt2[1]) + dc.DrawLine(int(pt1[0]),int(pt1[1]),int(pt2[0]),int(pt2[1])) dc.SetPen(wx.NullPen) #-----------------------------------------------------------------------# @@ -752,6 +753,7 @@ def draw_fast(self,dc): color = get_color(self.color) pen = wx.Pen(color, self.weight, style) dc.SetPen(pen) + #print('draw_fast') dc.DrawLines(self.single_line) dc.SetPen(wx.NullPen) @@ -785,8 +787,10 @@ def draw(self, dc): style = line_style_map[self.style] dc.SetPen(wx.Pen(color, self.weight,style)) try: + #print('draw') dc.DrawLines(self.scaled) except: + #print('draw except') dc.DrawLines(list(map(tuple,self.scaled))) dc.SetPen(wx.NullPen) @@ -848,12 +852,12 @@ def _down_triangle(self, dc, xc, yc, size=1): (0.0,0.577350*size*6)],xc,yc) def _cross(self, dc, xc, yc, size=1): - dc.DrawLine(xc-3*size,yc-3*size,xc+3*size,yc+3*size) - dc.DrawLine(xc-3*size,yc+3*size,xc+3*size,yc-3*size) + dc.DrawLine(int(xc-3*size),int(yc-3*size),int(xc+3*size),int(yc+3*size)) + dc.DrawLine(int(xc-3*size),int(yc+3*size),int(xc+3*size),int(yc-3*size)) def _plus(self, dc, xc, yc, size=1): - dc.DrawLine(xc-3*size,yc,xc+3*size,yc) - dc.DrawLine(xc,yc-3*size,xc,yc+3*size) + dc.DrawLine(int(xc-3*size),int(yc),int(xc+3*size),int(yc)) + dc.DrawLine(int(xc),int(yc-3*size),int(xc),int(yc+3*size)) class line_object(poly_points): """ Combines poly_line and poly_marker into a single @@ -964,8 +968,8 @@ def bounding_box(self): return bb def scale_and_shift(self, scale=1, shift=0,upperleft=None,size=None): - if scale is 1: scale = array((1,1)) - if shift is 0: shift = array((0,0)) + if scale == 1: scale = array((1,1)) + if shift == 0: shift = array((0,0)) graph_pixels_per_axis_unit = scale self.scale = graph_pixels_per_axis_unit/self.image_pixels_per_axis_unit self.origin = shift diff --git a/Chromagnon/Priithon/splitND2.py b/Chromagnon/Priithon/splitND2.py index 4527f0f..0c3f014 100755 --- a/Chromagnon/Priithon/splitND2.py +++ b/Chromagnon/Priithon/splitND2.py @@ -137,7 +137,7 @@ def __init__(self, data, colorAxis=-3, title='', size=None, if not isinstance(data, F.mockNDarray): data = N.asanyarray(data) # 20060720 - numpy arrays don't have ndim attribute # 20180308 - if data.dtype.type in [N.bool, N.uint32, N.uint64]: + if data.dtype.type in [bool, N.uint32, N.uint64]: data = data.astype(N.uint16) elif data.dtype.type in [N.int32, N.int64, ]: data = data.astype(N.int16) diff --git a/Chromagnon/Priithon/viewerCommon.py b/Chromagnon/Priithon/viewerCommon.py index b11080f..eaf7e81 100755 --- a/Chromagnon/Priithon/viewerCommon.py +++ b/Chromagnon/Priithon/viewerCommon.py @@ -546,9 +546,13 @@ def OnMouse(self, ev): self.SetCurrent(self.context) #x,y = ev.m_x, self.m_h-ev.m_y - x, y = ev.GetPosition() # 20141127 - y = self.m_h - y - xEff_float, yEff_float= gluUnProject(x,y,0)[:2] + # x, y = ev.GetPosition() # 20141127 + # y = self.m_h - y + #xEff_float, yEff_float= gluUnProject(x,y,0)[:2] # does not work on M2 Mac 20230225 + x = ev.GetX() + y = self.m_h-ev.GetY() + x0, y0, scale, aspR = self.m_x0, self.m_y0, self.m_scale, self.m_aspectRatio + xEff_float, yEff_float = int( (x-x0)/scale ) , int( (y-y0)/(scale*aspR) ) # 20080701: in new coord system, integer pixel coord go through the center of pixel diff --git a/Chromagnon/__init__.py b/Chromagnon/__init__.py old mode 100755 new mode 100644 diff --git a/Chromagnon/aligner.py b/Chromagnon/aligner.py old mode 100755 new mode 100644 index d5c0154..0f7c6a3 --- a/Chromagnon/aligner.py +++ b/Chromagnon/aligner.py @@ -493,7 +493,7 @@ def findAlignParamWave(self, t=0, doWave=True, init_t=None, useQuadri4YX=True, t #print(yx) del ref, c # create 2D projection image - xs = N.round_(self.refxs-ret[w,2]).astype(N.int) + xs = N.round_(self.refxs-ret[w,2]).astype(int) if xs.max() >= self.img.nx: xsbool = (xs < self.img.nx) xsinds = N.nonzero(xsbool)[0] @@ -603,7 +603,7 @@ def findAlignParamWave(self, t=0, doWave=True, init_t=None, useQuadri4YX=True, t ret[w,0] = yz[1] / zzoom + zshift - zs = N.round_(self.refzs-ret[w,0]).astype(N.int) + zs = N.round_(self.refzs-ret[w,0]).astype(int) if zs.max() >= self.img.nz: zsbool = (zs < self.img.nz) zsinds = N.nonzero(zsbool)[0] @@ -720,7 +720,7 @@ def findNonLinear2D(self, t=0, npxls=af.MIN_PXLS_YX, phaseContrast=True): if N.all((N.array(self.mapyx.shape[-2:]) - self.img.shape[-2:]) >= 0): slcs = imgGeo.centerSlice(self.mapyx.shape[-2:], win=self.img.shape[-2:], center=None) - self.mapyx = self.mapyx[slcs] # Ellipsis already added + self.mapyx = self.mapyx[tuple(slcs)] # Ellipsis already added else: self.mapyx = imgFilters.paddingValue(self.mapyx, shape=self.img.shape[-2:], value=0) @@ -737,7 +737,7 @@ def findNonLinear2D(self, t=0, npxls=af.MIN_PXLS_YX, phaseContrast=True): img = self.img.get3DArr(w=w, t=t) #img = af.fixSaturation(img, self.getSaturation(w=w, t=t)) - zs = N.round_(self.refzs-self.alignParms[t,w,0]).astype(N.int) + zs = N.round_(self.refzs-self.alignParms[t,w,0]).astype(int) if zs.max() >= self.img.nz: zsbool = (zs < self.img.nz) zsinds = N.nonzero(zsbool)[0] @@ -806,7 +806,7 @@ def findNonLinear3D(self, t=0, npxls=32, phaseContrast=True): if w == self.refwave: continue - tzs = N.round_(N.arange(self.img.nz, dtype=N.float32)-self.alignParms[t,w,0]).astype(N.int) + tzs = N.round_(N.arange(self.img.nz, dtype=N.float32)-self.alignParms[t,w,0]).astype(int) arr3D = self.get3DArr(w=w, t=0) var = (refvar + arr3D.var()) / 2. threshold = var * 0.1 @@ -856,7 +856,7 @@ def findNonLinear3D2(self, t=0, npxl=32, phaseContrast=True): if w == self.refwave: continue - tzs = N.round_(N.arange(self.img.nz, dtype=N.float32)-self.alignParms[t,w,0]).astype(N.int) + tzs = N.round_(N.arange(self.img.nz, dtype=N.float32)-self.alignParms[t,w,0]).astype(int) arr3D = self.get3DArr(w=w, t=0) var = (refvar + arr3D.var()) / 2. threshold = var * 0.1 @@ -1025,7 +1025,7 @@ def setRegionCutOut(self, cutout=True): shiftZYX = N.array((mm[0], MM[0], mm[1], MM[1], mm[2], MM[2])) # rearrange as a slice - shiftZYX = shiftZYX.astype(N.int) + shiftZYX = shiftZYX.astype(int) slc = [Ellipsis, slice(shiftZYX[0], shiftZYX[1]), slice(shiftZYX[2], shiftZYX[3]), @@ -1115,14 +1115,14 @@ def get2DArray(self, w=0, t=0, yz=False, alignParms=None):#, doXcorr=True): #ret[w,1:3] = yx del ref, c print(yx) - zs = N.round_(self.refxs-yx[-1]).astype(N.int)#ret[w,2]).astype(N.int) + zs = N.round_(self.refxs-yx[-1]).astype(int)#ret[w,2]).astype(int) else: - zs = N.round_(self.refxs-alignParms[w,2]).astype(N.int) + zs = N.round_(self.refxs-alignParms[w,2]).astype(int) nz = self.img.nx removeEdge=True img = img.T else: - zs = N.round_(self.refzs-alignParms[w,0]).astype(N.int) + zs = N.round_(self.refzs-alignParms[w,0]).astype(int) nz = self.img.nz removeEdge=False diff --git a/Chromagnon/alignfuncs.py b/Chromagnon/alignfuncs.py old mode 100755 new mode 100644 index bceb187..30eaefd --- a/Chromagnon/alignfuncs.py +++ b/Chromagnon/alignfuncs.py @@ -960,7 +960,7 @@ def chopImage2D(arr, npxls=(32,32), shiftOrigin=(0,0)):#False): slcs = [] #for zs in zslc: for ys in yslc: - slc = [[ys,xs] for xs in xslc] + slc = [tuple([ys,xs]) for xs in xslc] arrs.append([arr[s] for s in slc]) slcs.append(slc) return slcs, arrs @@ -1111,7 +1111,7 @@ def xcorNonLinear(arr, ref, npxls=60, threshold=None, pxlshift_allow=MAX_SHIFT_L if var > threshold: yx, c = xcorr.Xcorr(a, b) #cs[[slice(i,i+1)]+tslcs[y][x]] += c - cs[[slice(yi,yi+1),slice(xi,xi+1)]+tslcs[y][x]] += c + cs[(slice(yi,yi+1),slice(xi,xi+1))+tslcs[y][x]] += c csd = c[:c.shape[0]//4].std() cqual = (c.max() - csd) / (c.sum() / cfact) region[2,2*y+yi,2*x+xi] = cqual @@ -1295,6 +1295,7 @@ def iterNonLinear(arr, ref, npxl=MIN_PXLS_YX, affine=None, initGuess=None, thres maxcutX = max(shiftZYX[4], shape[1]-shiftZYX[5]) slc = [slice(int(maxcutY), int(shape[0]-maxcutY)), slice(int(maxcutX), int(shape[1]-maxcutX))] + slc = tuple(slc) if initGuess is not None: ret += initGuess diff --git a/Chromagnon/chromagnon.py b/Chromagnon/chromagnon.py old mode 100755 new mode 100644 index bc81733..7e489d8 --- a/Chromagnon/chromagnon.py +++ b/Chromagnon/chromagnon.py @@ -468,7 +468,7 @@ def buttonsEnable(self, enable=0): def OnOutFormatChosen(self, evt=None): outext = self.outextch.GetStringSelection() - if outext == self.outext_choices[-1] and not imgio.bioformatsIO.HAS_JDK: + if outext == 'ome.tif' and 'ome.tif' not in imgio.multitifIO.WRITABLE_FORMAT and not imgio.bioformatsIO.HAS_JDK: listbox.imgio_dialog('Writing the file format (%s) requres Java Development Kit (JDK)' % outext) confdic = C.readConfig() outext = confdic.get('format', aligner.WRITABLE_FORMATS[0]) @@ -507,8 +507,8 @@ def OnGo(self, ev=None): return # check wavelengths - waves1 = [list(map(int, self.listRef.getFile(index)[2].split(','))) for index in self.listRef.columnkeys] - waves2 = [list(map(int, self.listTgt.getFile(index)[2].split(','))) for index in self.listTgt.columnkeys] + waves1 = [list(map(int, map(float, self.listRef.getFile(index)[2].split(',')))) for index in self.listRef.columnkeys] + waves2 = [list(map(int, map(float, self.listTgt.getFile(index)[2].split(',')))) for index in self.listTgt.columnkeys] nts = all([t == 1 for t in self.listRef.nts]) # combine wavelengths diff --git a/Chromagnon/chromeditor.py b/Chromagnon/chromeditor.py old mode 100755 new mode 100644 diff --git a/Chromagnon/chromformat.py b/Chromagnon/chromformat.py old mode 100755 new mode 100644 index c19a30b..c147281 --- a/Chromagnon/chromformat.py +++ b/Chromagnon/chromformat.py @@ -377,9 +377,9 @@ def readParmWave(self): def finalDim(self): if self.rdr is not None and hasattr(self.rdr, 'ny'): - nzyx = N.array((self.nz, self.rdr.ny, self.rdr.nx), N.int) + nzyx = N.array((self.nz, self.rdr.ny, self.rdr.nx), int) else: - nzyx = N.array((self.nz, self.reader.ny, self.reader.nx), N.int) + nzyx = N.array((self.nz, self.reader.ny, self.reader.nx), int) return nzyx def readMap3D(self, t=0, w=0): diff --git a/Chromagnon/cutoutAlign.py b/Chromagnon/cutoutAlign.py old mode 100755 new mode 100644 diff --git a/Chromagnon/extrapanel.py b/Chromagnon/extrapanel.py index 3e6f768..1facf38 100644 --- a/Chromagnon/extrapanel.py +++ b/Chromagnon/extrapanel.py @@ -128,7 +128,7 @@ def __init__(self, parent, listRef, confdic={}, outdir=None, refwave=None): btnsizer.AddButton(btn) btnsizer.Realize() - sizer.Add(btnsizer, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) + sizer.Add(btnsizer, 0)#, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) sizer.Fit(self) diff --git a/Chromagnon/flatfielder.py b/Chromagnon/flatfielder.py old mode 100755 new mode 100644 diff --git a/Chromagnon/imgio/Mrc.py b/Chromagnon/imgio/Mrc.py old mode 100644 new mode 100755 diff --git a/Chromagnon/imgio/__init__.py b/Chromagnon/imgio/__init__.py index 9ad4e17..5a35bb2 100755 --- a/Chromagnon/imgio/__init__.py +++ b/Chromagnon/imgio/__init__.py @@ -6,19 +6,19 @@ import numpy as N if sys.version_info.major == 2: - import generalIO, mrcIO, imgIO, imgSeqIO, multitifIO, bioformatsIO, arrayIO, nd2io + import generalIO, mrcIO, imgIO, imgSeqIO, multitifIO, bioformatsIO, arrayIO, nd2io, cziio elif sys.version_info.major >= 3: try: - from . import generalIO, mrcIO, imgIO, imgSeqIO, multitifIO, bioformatsIO, arrayIO, nd2io + from . import generalIO, mrcIO, imgIO, imgSeqIO, multitifIO, bioformatsIO, arrayIO, nd2io, cziio except ImportError: - from imgio import generalIO, mrcIO, imgIO, imgSeqIO, multitifIO, bioformatsIO, arrayIO, nd2io + from imgio import generalIO, mrcIO, imgIO, imgSeqIO, multitifIO, bioformatsIO, arrayIO, nd2io, cziio uninit_javabridge = bioformatsIO.uninit_javabridge READABLE_FORMATS = [] WRITABLE_FORMATS = [] -_names = ['seq', 'tif', 'mrc', 'bio'] -for module in [generalIO, imgIO, imgSeqIO, multitifIO, mrcIO, bioformatsIO, arrayIO, nd2io]: +#_names = ['seq', 'tif', 'mrc', 'bio'] +for module in [generalIO, imgIO, imgSeqIO, multitifIO, mrcIO, bioformatsIO, arrayIO, nd2io, cziio]: reload(module) READABLE_FORMATS += module.READABLE_FORMATS WRITABLE_FORMATS += module.WRITABLE_FORMATS @@ -53,13 +53,15 @@ def _switch(fn, read=True, *args, **kwds): 'tif': multitifIO.READABLE_FORMATS, 'mrc': mrcIO.READABLE_FORMATS, 'bio': bioformatsIO.READABLE_FORMATS, - 'nd2': nd2io.READABLE_FORMATS} + 'nd2': nd2io.READABLE_FORMATS, + 'czi': cziio.READABLE_FORMATS} klasses = {'seq': imgSeqIO.ImgSeqReader, 'tif': multitifIO.MultiTiffReader, 'mrc': mrcIO.MrcReader, 'bio': bioformatsIO.BioformatsReader, 'arr': arrayIO.ArrayReader, - 'nd2': nd2io.ND2Reader} + 'nd2': nd2io.ND2Reader, + 'czi': cziio.CZIReader} else: # write formats = {'seq': imgSeqIO.WRITABLE_FORMATS, 'tif': multitifIO.WRITABLE_FORMATS, @@ -79,7 +81,7 @@ def _switch(fn, read=True, *args, **kwds): ## --- JDK check---- - _allfmt = formats['tif'] + formats['mrc'] + formats['seq'] + formats['bio'] + _allfmt = formats['tif'] + formats['mrc'] + formats['seq'] + formats['bio'] + formats.get('nd2', ()) + formats.get('czi', ()) if ext not in _allfmt and ext in bioformatsIO.bioformats.READABLE_FORMATS:# and ext not in formats['bio'] and ext not in formats['seq']: raise ValueError(JDK_MSG % ext) #------------- @@ -102,7 +104,7 @@ def _switch(fn, read=True, *args, **kwds): # ome.tif is written by BioformatsWriter elif fn.endswith('ome.tif') and not read: - tifversion = tifffile.__version__.split('.') + tifversion = multitifIO.tifffile.__version__.split('.') if (int(tifversion[0]) == 2020 and int(tifversion[1]) >= 11) or int(tifversion[0]) > 2020: return klasses['tif'](fn, style='ome', *args, **kwds) else: @@ -122,6 +124,8 @@ def _switch(fn, read=True, *args, **kwds): raise ValueError('The input file %s was not readable' % fn) elif ext in nd2io.READABLE_FORMATS: return klasses['nd2'](fn, *args, **kwds) + elif ext in cziio.READABLE_FORMATS: + return klasses['czi'](fn, *args, **kwds) elif read and ext in imgIO.READABLE_FORMATS: return imgIO.load(fn) elif ext in formats['mrc']: diff --git a/Chromagnon/imgio/arrayIO.py b/Chromagnon/imgio/arrayIO.py old mode 100644 new mode 100755 diff --git a/Chromagnon/imgio/cziIO.py b/Chromagnon/imgio/cziIO.py index 9b7e033..60b7fae 100755 --- a/Chromagnon/imgio/cziIO.py +++ b/Chromagnon/imgio/cziIO.py @@ -2,13 +2,14 @@ from . import generalIO except ImportError: import generalIO -import czifile -def _eval(txt): - try: - return eval(txt) - except (NameError, TypeError, SyntaxError): - return txt +try: + import czifile + READABLE_FORMATS = ('czi',) +except ImportError: + READABLE_FORMATS = () + +WRITABLE_FORMATS = () class CZIReader(generalIO.GeneralReader): def __init__(self, fn): @@ -27,39 +28,69 @@ def openFile(self): self.readHeader() def readHeader(self): + ### FIXME: multiple axes needs to be handled + + self.readMetaData() + meta = self.metadata + img = meta['ImageDocument']['Metadata']['Information']['Image'] + nx = img['SizeX'] + ny = img['SizeY'] + nw = img.get('SizeC', 1) + nz = img.get('SizeZ', 1) + if 'T' in self.fp.axes: + nt = self.fp.shape[self.fp.axes.index('T')] + else: + nt = 1 - nt = self.fp.shape[self.fp.axes.index('T')] + axes = self.fp.axes[:-3].replace('C', 'W').replace('B', '').replace('V', '').replace('YX0', '') + imgSeq = self.findImgSequence(axes) - waves = [self.metadata['Channel%iEmissionWavelength' % w] for w in range(self.metadata['SizeC'])] - for i, imgsq in enumerate(generalIO.IMGSEQ): - if imgsq.replace('W', 'C') in self.fp.axes: - imgSeq = i - break + channels = img['Dimensions']['Channels']['Channel'] + waves = [] + self.exc = [] + if nw == 1: + waves.append(channels['EmissionWavelength']) + else: + for channel in channels: + waves.append(channel['EmissionWavelength']) + self.exc.append(channel['ExcitationWavelength']) - self.setDim(self.metadata['SizeX'], self.metadata['SizeY'], self.metadata['SizeZ'], nt, self.metadata['SizeC'], self.fp.dtype, waves, imgSeq) + self.setDim(nx, ny, nz=nz, nt=nt, nw=nw, dtype=self.fp.dtype, wave=waves, imgSequence=imgSeq) - def readMetaData(self): - tree = self.fp.metadata.getroottree() - root = tree.getroot() - self.readTree(root) + #pz = img['Dimensions']['Z']['Interval']['Increment'] + scl = meta['ImageDocument']['Metadata']['Scaling']['Items']['Distance'] + pdic = {} + for s in scl: + pdic['p%s' % s['Id'].lower()] = s['Value']*(10**6) + self.setPixelSize(**pdic) - def readTree(self, tree): - if tree.tag == 'Channels': - self.readChannels(tree) - else: - children = tree.getchildren() - if children: - for child in children: - self.readTree(child) - else: - #if tree.tag.endswith('Wavelength'): - # raise - self.metadata[tree.tag] = _eval(tree.text) + def readMetaData(self): + self.metadata = self.fp.metadata(False) + + def readSec(self, i): + xy0 = self.roi_start[1:] + xy1 = xy0 + self.roi_size[1:] - def readChannels(self, tree): - channels = tree.getchildren() - for w, channel in enumerate(channels): - for cha_info in channel: - self.metadata[('Channel%i' % w) + cha_info.tag] = _eval(cha_info.text) + sdir = self.fp.filtered_subblock_directory[i] + subblock = sdir.data_segment() + a = subblock.data(raw=False, resize=True, order=0) + return a.ravel().reshape((self.ny, self.nx))[xy0[0]:xy1[0], xy0[1]:xy1[1]] + +# map dimension character to description +DIMENSIONS = { + '0': 'Sample', # e.g. RGBA + 'X': 'Width', + 'Y': 'Height', + 'C': 'Channel', + 'Z': 'Slice', # depth + 'T': 'Time', + 'R': 'Rotation', + 'S': 'Scene', # contiguous regions of interest in a mosaic image + 'I': 'Illumination', # direction + 'B': 'Block', # acquisition + 'M': 'Mosaic', # index of tile for compositing a scene + 'H': 'Phase', # e.g. Airy detector fibers + 'V': 'View', # e.g. for SPIM +} diff --git a/Chromagnon/imgio/imgIO.py b/Chromagnon/imgio/imgIO.py old mode 100644 new mode 100755 diff --git a/Chromagnon/imgio/mrcIO.py b/Chromagnon/imgio/mrcIO.py index 0b70919..2f3451a 100755 --- a/Chromagnon/imgio/mrcIO.py +++ b/Chromagnon/imgio/mrcIO.py @@ -47,20 +47,41 @@ def readHeader(self): #self.metadata['title'] = self.hdr.title for ttl in self.hdr.title: if type(ttl) == N.bytes_: - ttl = ttl.decode('UTF-8', 'ignore') - if '=' in ttl: - key, val = ttl.split('=') - key = key.strip() - val = val.strip() - try: - val = eval(val) - except: - pass + #ttl = ttl.decode('UTF-8', 'ignore') + ttl = ttl.decode('ascii', 'ignore') # combatibility to TIFF + + neq = ttl.count('=') + if neq > 1:#'=' in ttl: # this does not find separation of = + for sep in [',', ':', ';', ' ']: + if sep in ttl: + ttls = ttl.split(',') + break + else: + ttls = [ttl] + elif neq == 1: + ttls = [ttl] else: key = ttl val = '' - if key: - self.metadata[key] = val + if key: + self.metadata[key] = val + ttls = [] + + for ttl0 in ttls: + kvs = ttl0.split('=') + nvs = len(kvs) + for i in range(nvs//2): + kv = kvs[i*2:(i+1)*2] + if len(kv) == 2: + key, val = kv + key = key.strip() + val = val.strip() + + try: + val = eval(val) + except: + self.metadata[key] = val + #pass self.fp._secByteSize = self._secByteSize @@ -230,15 +251,29 @@ def writeHeader(self, hdr=None): if self.byteorder == '<': self.hdr.dvid = -16224 # little_indian number + # writing metadata into title for key, val in self.metadata.items(): + if val: + msg = '%s = %s' % (key, val) + else: + msg = key + + if N.bytes_(msg) in self.hdr.title: + continue + + # try to write try: - if val: - msg = '%s = %s' % (key, val) - else: - msg = key Mrc.setTitle(self.hdr, msg) except ValueError: - print('WARNING: title is full, metadata "%s" is not written' % key) + written = False + for i, s in enumerate(self.hdr.title): + if not s: + Mrc.setTitle(hdr, msg[:80], i) + # Mrc only support title up to 80 characters + written = True + break + if not written: + print('WARNING: title is full, metadata "%s" is not written' % key) if (self.extInts is not None or self.extFloats is not None) or self.byteorder == '<': # old ImageJ assumes that the dv format (byteorder == <) has extended header diff --git a/Chromagnon/imgio/multitifIO.py b/Chromagnon/imgio/multitifIO.py index bf1a8ff..ac2fa9b 100755 --- a/Chromagnon/imgio/multitifIO.py +++ b/Chromagnon/imgio/multitifIO.py @@ -17,13 +17,16 @@ ## MEMO: "ImageJ does not support non-contiguous data" means shape does not match WRITABLE_FORMATS = ('tif', 'tiff') tifversion = tifffile.__version__.split('.') - if int(tifversion[0]) > 2021 or (int(tifversion[0]) >= 2021 and int(tifversion[1]) >= 11): + if int(tifversion[0]) > 2021 or (int(tifversion[0]) >= 2021 and int(tifversion[1]) >= 4):#11): WRITABLE_FORMATS += ('ome.tif', 'ome.tiff') READABLE_FORMATS = WRITABLE_FORMATS + ('lsm',) else: READABLE_FORMATS = WRITABLE_FORMATS + ('ome.tif', 'ome.tiff', 'lsm') except ImportError: WRITABLE_FORMATS = READABLE_FORMATS = () + class tifff: + def __init__(): + pass IMAGEJ_METADATA_TYPES = ['Info', 'Labels', 'Ranges', 'LUTs', 'Plot', 'ROI', 'Overlays'] @@ -535,7 +538,10 @@ def _makeMetadata(self): def makeOMEMetadata(self): axes = self.makeDimensionStr(squeeze=True).replace('W', 'C') - dimension = self.makeDimensionStr(squeeze=False).replace('W', 'C')[::-1] + dimension = self.makeDimensionStr(squeeze=False).replace('W', 'C')#[::-1] + # for compatibility to 2021.7.2 + extrdims = ''.join([dstr for dstr in dimension if dstr not in axes]) + dimension = axes[::-1] + extrdims[::-1] metadata = { 'Pixels': { diff --git a/Chromagnon/imgio/nd2io.py b/Chromagnon/imgio/nd2io.py old mode 100644 new mode 100755 diff --git a/Chromagnon/ndviewer/main.py b/Chromagnon/ndviewer/main.py index 531857b..0beeb0d 100755 --- a/Chromagnon/ndviewer/main.py +++ b/Chromagnon/ndviewer/main.py @@ -412,6 +412,19 @@ def onOrthogonal(self, evt=None): if self.orthogonal_toggle.GetValue() and len(self.viewers) == 1: self.loadImage2Memory() self._mgr.GetPane('Image').Left().Position(1) + if self.useCropbox: + if hasattr(self.doc, 'roi_start'): + mydoc = self.doc + else: + mydoc = self.doc.img + ly = mydoc.roi_start[-2] #cropbox_l[self.dims[0]] + uy = mydoc.roi_size[-2] + ly #cropbox_u[self.dims[0]] + lx = mydoc.roi_start[-1] #cropbox_l[self.dims[1]] + ux = mydoc.roi_size[-1] + lx #cropbox_u[self.dims[1]] + y0 = (ly + uy)//2 + x0 = (lx + ux)//2 + mydoc.y = y0 + mydoc.x = x0 self.OnAddX() self.OnAddY() self.OnAddLastViewer() diff --git a/Chromagnon/ndviewer/viewer2.py b/Chromagnon/ndviewer/viewer2.py index 8e8f4f9..c075eaf 100755 --- a/Chromagnon/ndviewer/viewer2.py +++ b/Chromagnon/ndviewer/viewer2.py @@ -89,7 +89,7 @@ def setLeftDown(self, ld=None): ld: None uses self.mydoc info, otherwise supply [z,y,x] """ if ld is None: - leftDown = N.zeros((3,), N.int) + leftDown = N.zeros((3,), int) debug=""" if hasattr(self, 'pic_ny'): if self.dims in ((1,2), (1,0)): # XY, ZY diff --git a/Chromagnon/test_compareReg.py b/Chromagnon/test_compareReg.py old mode 100755 new mode 100644 diff --git a/Chromagnon/testfuncs.py b/Chromagnon/testfuncs.py old mode 100755 new mode 100644 diff --git a/Chromagnon/threads.py b/Chromagnon/threads.py old mode 100755 new mode 100644 index 0d4035d..a8e61f2 --- a/Chromagnon/threads.py +++ b/Chromagnon/threads.py @@ -607,7 +607,7 @@ def OnClosePage(self, evt): self.panel.aui.imEditWindows.DeletePage(evt.data[0]) def OnProgress(self, evt): - self.panel.progress.SetValue(evt.data[0]) + self.panel.progress.SetValue(int(round(evt.data[0]))) class ThreadFlat(ThreadWithExc): diff --git a/Chromagnon/version.py b/Chromagnon/version.py old mode 100755 new mode 100644 index 2373c18..8509aac --- a/Chromagnon/version.py +++ b/Chromagnon/version.py @@ -1 +1 @@ -version = '0.92' +version = '0.93' diff --git a/LICENSE.md b/LICENSE.md old mode 100755 new mode 100644 diff --git a/README.md b/README.md old mode 100755 new mode 100644 index f2e4b9e..d5880f6 --- a/README.md +++ b/README.md @@ -50,7 +50,8 @@ Dependencies: * `wxPython` (optional for GUI) * `PyOpenGL` (optional for GUI) * `pyFFTW` (optional for fast Fourier transform) -* `tifffile <= 2021.7.2` (optional for reading/writing tiff) +* `tifffile <= 2021.7.2` (optional for reading/writing tiff) +* `nd2` (optional for reading Nikon nd2 file) * `javabridge` (optional for reading more image file formats) * `python-bioformats` (optional for reading more image file formats) * `lxml` (optional for reading more image file formats) diff --git a/setup.py b/setup.py old mode 100755 new mode 100644