massive move and cleanup

This commit is contained in:
2018-10-03 10:18:07 +02:00
parent eefd545289
commit 85364c9a59
46 changed files with 727 additions and 2827 deletions

512
python/imgAnalysis/findxtal.py Executable file
View File

@@ -0,0 +1,512 @@
#!/usr/bin/env python
#*-----------------------------------------------------------------------*
#| |
#| Copyright (c) 2018 by Paul Scherrer Institute (http://www.psi.ch) |
#| |
#| Author Thierry Zamofing (thierry.zamofing@psi.ch) |
#*-----------------------------------------------------------------------*
'''
implements an image alalyser for ESB MX
'''
import os
from scipy import fftpack, ndimage
import scipy.ndimage as ndi
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
import imgStack
def findGrid(image,numPeak=2,minFrq=2,maxFrq=None,debug=0):
d2r=np.pi/180.
s=image.shape
w1=np.hamming(s[0]).reshape((-1,1))
w2=np.hamming(s[1]).reshape((1,-1))
wnd=w1*w2
#plt.figure(num='hamming window')
#plt.imshow(wnd, interpolation="nearest")
image=wnd*image
if debug&1:
plt.figure(num='hamming window*img')
plt.imshow(image, interpolation="nearest")
fft2 = np.fft.fft2(image)
fft2=np.fft.fftshift(fft2)
fa =abs(fft2)
if debug&1:
plt.figure(num='log of fft: hamming wnd*image')
hi=plt.imshow(fa, interpolation="nearest",norm=mpl.colors.LogNorm(vmin=.1, vmax=fa.max()))
#plt.xlim(s[1]/2-50, s[1]/2+50);plt.ylim(s[0]/2-50, s[0]/2+50)
ctr=np.array(image.shape,dtype=np.int16)/2
fa[ctr[0]-minFrq+1:ctr[0]+minFrq, ctr[1]-minFrq+1:ctr[1]+minFrq]=0 # set dc to 0
# limit to maximal frequency
if maxFrq is not None:
fa[:ctr[0]-maxFrq,:]=0;fa[ctr[0]+maxFrq:,:]=0
fa[:,:ctr[1]-maxFrq]=0;fa[:,ctr[1]+maxFrq:]=0
x=np.arange(s[1])/float(s[1])*2.*np.pi
y=np.arange(s[0])/float(s[0])*2.*np.pi
if debug&1:
hi.set_data(fa)
gen = np.zeros(fft2.shape)
#x=np.linspace(0,2*np.pi,s[1],endpoint=False)
#y=np.linspace(0,2*np.pi,s[0],endpoint=False)
xx, yy = np.meshgrid(x, y)
my=int(s[0]/2)
mx=int(s[1]/2)
res=[] #list of tuples (freq_x,freq_y, phase)
for i in range(numPeak):
maxAmpIdx=fa.argmax()
maxAmpPos=np.array(divmod(maxAmpIdx,fa.shape[1]),dtype=np.int16)
peakPos=maxAmpPos-ctr
peak=fft2[maxAmpPos[0] - 1:maxAmpPos[0] + 2, maxAmpPos[1] - 1:maxAmpPos[1] + 2]
if debug&2:
print(peakPos)
print(abs(peak))
(vn, v0, vp)=np.log(fa[maxAmpPos[0], maxAmpPos[1] - 1:maxAmpPos[1] + 2]) #using log for interpolation is more precise
freq_x=peakPos[1]+(vn-vp)/(2.*(vp+vn-2*v0))
(vn, v0, vp)=np.log(fa[maxAmpPos[0]-1:maxAmpPos[0]+2,maxAmpPos[1]])
freq_y=peakPos[0]+(vn-vp)/(2.*(vp+vn-2*v0))
#calculate phase
sumCos=0.
sumSin=0.
sumAmp=0.
n=np.prod(s)
for iy in (-1,0,1):#(0,):
for ix in (-1,0,1):
v=peak[iy+1,ix+1]
fx=peakPos[1]+ix
fy=peakPos[0]+iy
amp=np.abs(v)/n; ang=np.angle(v)
sumAmp+=amp
sumCos+=amp*np.cos(fx*x[mx+ix] + fy*y[my+iy] + ang)
sumSin+=amp*np.sin(fx*x[mx+ix] + fy*y[my+iy] + ang)
if debug&1:
gen+=amp*np.cos(fx*xx + fy*yy + ang)
sumAmp*=2. #double because of conjugate part
sumCos*=2.
sumSin*=2.
#sumAmp=np.abs(peak).sum()/n*2 #double because of conjugate part
w=np.arccos(sumCos/sumAmp)
if sumSin<0: w=-w
phi_= freq_x*x[mx]+freq_y*y[my]-w
phi_%=(np.pi*2)
#have main frequency positive and phase positive (for convinient)
if (freq_x<0 and abs(freq_x)> abs(freq_y)) or \
(freq_y<0 and abs(freq_y)> abs(freq_x)):
freq_x = -freq_x; freq_y = -freq_y; phi_=-phi_
if phi_<0: phi_+=2*np.pi
res.append((freq_x,freq_y,phi_))
fa[maxAmpPos[0]-1:maxAmpPos[0]+2,maxAmpPos[1]-1:maxAmpPos[1]+2]=0 # clear peak
maxAmpPos_=2*ctr-maxAmpPos
fa[maxAmpPos_[0]-1:maxAmpPos_[0]+2,maxAmpPos_[1]-1:maxAmpPos_[1]+2]=0 # clear conjugated peak
if debug&1:
hi.set_data(fa)
if debug&1:
gen*=2. # double because of conjugate part
if debug&2:
for fx,fy,phase in res:
print('fx: %g fy: %g phase: %g deg'%(fx,fy,phase/d2r))
if debug&1:
plt.xlim(s[1]/2-50, s[1]/2+50)
plt.ylim(s[0]/2-50, s[0]/2+50)
plt.figure('image*wnd')
plt.imshow(image,interpolation="nearest")
plt.figure('reconstruct')
plt.imshow(gen,interpolation="nearest")
plt.figure()
x=range(s[1])
y=int(s[0]/2)-1
plt.plot(x,image[y,:],'r')
plt.plot(x,gen[y,:],'g')
return res
def plotGrid(grid,shape):
#x=np.linspace(0,2*np.pi,shape[1],endpoint=False)
#y=np.linspace(0,2*np.pi,shape[0],endpoint=False)
#for (freq_x, freq_y, phase) in grid:
#find points were: np.cos(freq_x*xx + freq_y*yy - phase) is max
#freq_x*xx + freq_y*yy - phase = 0 2pi, 4pi
# grid should have 2 entries
# -> 2 equations to solve
# points for:
# entry1 entry2 = (0 0), (0, 2*pi), (2*pi 0)
(fx0,fy0,p0)=grid[0]
(fx1,fy1,p1)=grid[1]
A=np.array([[fx0,fy0],[fx1,fy1]])
A*=np.array([2*np.pi/shape[1],2*np.pi/shape[0]])
Ai=np.asmatrix(A).I
na=int(max(abs(fx0),abs(fy0)))+1
nb=int(max(abs(fx1),abs(fy1)))+1
p=np.ndarray((na*nb,2))
#p=np.ndarray((3,2))
#i=0
#for x,y in ((0,0),(1,0),(0,1)):
# v = np.array([p0+x*2.*np.pi, p1+y*2.*np.pi]).reshape(-1, 1)
# p[i,:]=(Ai*v).reshape(-1)
# i+=1
i=0
for b in range(nb):
for a in range(na):
v = np.array([p0+a*2.*np.pi, p1+b*2.*np.pi]).reshape(-1, 1)
p[i,:]=(Ai*v).reshape(-1)
i+=1
#plt.plot([400,500],[400,500],'r+',markeredgewidth=2, markersize=10)
plt.plot(p[:,0],p[:,1],'r+',markeredgewidth=2, markersize=10)
plt.axis('image')
pass
def ShowImage(img,title=None,cmap='gray',vmin=None, vmax=None):
plt.figure(title)
plt.imshow(img, interpolation="nearest", cmap=cmap,vmin=vmin,vmax=vmax) # ,vmin=m-3*s, vmax=m+3*s)
plt.colorbar()
def imgEqualize(img,num_bins=256):
# get image histogram
hist, bins = np.histogram(img.flatten(), num_bins, normed=True)
cdf = hist.cumsum() # cumulative distribution function
cdf = 255 * cdf / cdf[-1] # normalize
# use linear interpolation of cdf to find new pixel values
imgEqu=np.interp(img.flatten(), bins[:-1], cdf)
imgEqu=np.uint8(imgEqu.reshape(img.shape))
plt.figure();plt.plot(hist)
h2,b2=np.histogram(imgEqu.flatten(), num_bins, normed=True)
plt.figure();plt.plot(h2)
return imgEqu.reshape(img.shape)#, cdf
def findObj(image,objSize=150,tol=0,debug=0):
#objSiz is the rough diameter of the searched features in pixels
#tol = tolerance in object size (not yet implemented)
#tolShape = roudness tolerance in object roundness (not yet implemented)
from scipy.signal import convolve2d
plt.ion()
image=image[500:2500,1000:2500]
s=image.shape
f=np.array([0.9595264, 0.9600567, 0.9608751, 0.9620137, 0.9634765, 0.9652363, 0.9672352, 0.9693891, 0.9715959, 0.9737464, 0.9757344, 0.9774676, 0.9788761, 0.9799176, 0.9805792, 0.9808776, 0.9808528, 0.9805624, 0.9800734, 0.9794550])
f=f*1000
f=f-f.mean()
m=image.mean();s=image.std()
ShowImage(image,vmin=-.85,vmax=-.99)
ShowImage(image,vmin=m-3*s,vmax=m+3*s)
ieq=imgEqualize(image)
ShowImage(ieq)
m=image.mean();s=image.std()
i2=(image-m)*(256./(3*s))+128.
i2[i2>255.]=255.
i2[i2<0.]=0.
i2=np.uint8(i2)
ShowImage(i2)
image=ndi.filters.gaussian_filter1d(image,sigma=5./3,truncate=3.,axis=0)
image=ndi.filters.gaussian_filter1d(image,sigma=5./3,truncate=3.,axis=1)
ShowImage(image,vmin=-.85,vmax=-.99)
img2=ndi.filters.convolve1d(image,f,0)
ShowImage(img2,vmin=-3,vmax=3)
img3=ndi.filters.convolve1d(image,f,1)
ShowImage(img3,vmin=-3,vmax=3)
img4=np.maximum(abs(img2),abs(img3))
ShowImage(img4,vmin=0,vmax=3)
m=image.mean();s=image.std()
img5=np.zeros(image.shape)
w=np.where(img4>1.5)
img5[w]=1
ShowImage(img5)
#imgStack.Run([image, img2,img3,img4,img5])
#w=np.where(img2>image*1.01)
#img2[:]=0
#img2[w]=1
l=int(objSize/30);l=5
img6=ndi.binary_fill_holes(img5, structure=np.ones((l,l)))
#img6 = ndi.binary_dilation(img5, iterations=2)
#img6 = ndi.binary_erosion(img5, iterations=2)
ShowImage(img6)
l=int(objSize/5)#=int(objSize/10)
if l>=3:
#img5=ndi.binary_opening(img4, structure=np.ones((l,l)))
#img5=ndi.binary_erosion(img4, structure=np.ones((l,l)))
img5=ndi.binary_erosion(img4, iterations=l)
if debug&4:
plt.figure()
plt.imshow(img5, interpolation="nearest", cmap='gray')
#plt.show()
else:
img5=img4
import cv2
#cvi1=np.zeros(shape=img5.shape+(3,), dtype=np.uint8)
#cvi1[:,:,0]=image
#image*np.array([1,1,1]).reshape(-1,1,1)
s=image.shape+(1,)
cvi1=image.reshape(s)*np.ones((1,1,3),dtype=np.uint8)
#cvi1=np.ones((3,1,1),dtype=np.uint8)image
contours, hierarchy=cv2.findContours(np.uint8(img5),cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
#contours,hierarchy=cv2.findContours(np.uint8(img5),1,2)
cv2.drawContours(cvi1, contours, -1, (0,255,0), 3)
plt.figure()
plt.imshow(cvi1 , interpolation="nearest", cmap='gray')
m=cv2.moments(contours[0])
lbl = ndi.label(img5)
if debug&2:
plt.figure()
plt.imshow(lbl[0], interpolation="nearest")
#plt.show()
ctr=ndi.measurements.center_of_mass(image, lbl[0],range(lbl[1]))
ctr=np.array(ctr)
ctr2=np.ndarray(shape=(len(contours),2),dtype=np.uint16)
i=0
for c in contours:
m=cv2.moments(c)
try:
m00=m['m00']
m10=m['m10']
m01=m['m01']
#print m00
if m00>1000 and m00<7000:
ctr2[i,:]=(m10/m00,m01/m00)
i+=1
except ZeroDivisionError:
pass
#ctr2[i, :]=c[0,0]
if debug&1:
plt.figure()
plt.imshow(image, interpolation="nearest", cmap='gray')
plt.plot(ctr[:,1],ctr[:,0],'or',markeredgewidth=2, markersize=10)
plt.plot(ctr2[:i,0],ctr2[:i,1],'+y',markeredgewidth=2, markersize=10)
plt.show()
return ctr
def genImg(shape,*args):
'''args is a list of tuples (freq_x,freq_y, phase) multiple args can be added'''
image=np.ndarray(shape)#,dtype=np.uint8)
x=np.linspace(0,2*np.pi,shape[1],endpoint=False)
y=np.linspace(0,2*np.pi,shape[0],endpoint=False)
#xx, yy = np.meshgrid(x, y, sparse=True)
xx, yy = np.meshgrid(x, y)
dc=0
for i,f in enumerate(args):
(freq_x, freq_y, phase)=f
if i==0:
image = dc+np.cos(freq_x*xx + freq_y*yy - phase)
else:
image += np.cos(freq_x * xx + freq_y * yy - phase)
return image
def phase_retrieval_intensity_tranport(image, mu, delta, I_in, M, pixel_size, R2):
'''calculates the projected thickness as in eq. 12 of Paganin et al, 2002, J. Microscopy,
from an image, the absoption coefficient mu (um-1), the real part of the deviation of the refractive index
from unity delta, the uniform intensity of the incident radiation I_in (ph/s/um2), the magnification of the
image from the point source illumination M, the pixel size (um), the propagation distance R2 (um).'''
F = np.fft.rfft2(image)
k_x = 1/(pixel_size*image.shape[0]) # is there a 2pi factor?
k_y = 1/(pixel_size*image.shape[1]) # is there a 2pi factor?
#print k_x,k_y
k_array = np.zeros((F.shape[0],F.shape[1]))
A = np.zeros((F.shape[0],F.shape[1]), dtype=np.complex128)
for i_k in range(k_array.shape[0]):
for j_k in range(k_array.shape[1]):
k_array[i_k,j_k] = np.sqrt((i_k * k_x)**2 + (j_k * k_y)**2)
A[i_k,j_k] = mu * F[i_k,j_k] / (I_in * (R2 * delta * k_array[i_k,j_k]**2 / M + mu))
Fm1 = np.fft.irfft2(A)
T = np.multiply(-1/mu, np.log(Fm1))
return T
if __name__ == '__main__':
def testfftLoop():
plt.ioff()
for phi in np.arange(0.,180.,10.):
testfft(phi=phi)
plt.show()
pass
def testfft(phi=45.,frq=4.2,amp=1.,n=256.):
#find the main frequency and phase in 1-D
plt.figure(1)
d2r=np.pi/180.
x=np.arange(n)
phi=phi*d2r
y=amp*np.cos(frq*x/n*2.*np.pi-phi)
plt.plot(x,y,'y')
#y[np.where(y<-.5)]=0
#y*=np.hamming(n)
w=np.hanning(n)
plt.plot(x,w,'y')
y*=w
#y*=1.-np.cos(x/(n-1.)*2.*np.pi)
#y=[1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1]
plt.stem(x,y)
fy=np.fft.fft(y)
fya=np.abs(fy)
plt.figure(2)
plt.subplot(211)
plt.stem(x,fya)
plt.subplot(212)
plt.stem(x,np.angle(fy)/d2r)
print(np.angle(fy[frq])/d2r)
fya[0]=0
i=(fya.reshape(2,-1)[0,:]).argmax()
(vn,v0,vp)=fya[i-1:i+2]
frq_=i+(vn-vp)/(2.*(vp+vn-2*v0))
print('freq: %g phase %g %g %g'%((frq_,)+tuple(np.angle(fy[i-1:i+2])/d2r)))
#PHASE CALCULATION
plt.figure(1)
y=np.zeros(x.shape)
z=np.zeros(x.shape)
for ii in (i,i-1,i+1):
y+=np.abs(fy[ii])/n*np.cos(ii*x/n*2.*np.pi+np.angle(fy[ii]))
z+=np.abs(fy[ii])/n*np.sin(ii*x/n*2.*np.pi+np.angle(fy[ii]))
y*=2 #double because of conjugate part
z*=2 #double because of conjugate part
plt.plot(x,y,'r')
amp_=fya[i-1:i+2].sum()/n*2.
t=int(n/2)-1
#->phase: find maximum or where the sin is 0
w=np.arccos(y[t]/amp_)
if(z[t]<0): w=-w
print('amplitude %g, value at middle (%d) cos->%g sin->%g -> acos %g deg'%(amp_,t,y[t],z[t],w/d2r))
#rot angle at middle x=127 =frq_*t/n*2.*np.pi-phi_=w '%(amp_,t,y[t],phi_/d2r))
phi_=frq_*t/n*2.*np.pi-w
y=amp_*np.cos(frq_*x/n*2.*np.pi-phi_)
print('y[%d] %g'%(t,y[t]))
plt.plot(x,y,'g')
pass
def testFindGrid():
plt.ioff()
imggrid=(
('grid_20180409_115332_45deg.png', 2,None),
('grid_20180409_115332.png', 2,None),
('honeycomb.png', 2,25),
('MS01_20180411_110544.png', 10,None),
('MS02_20180411_120354.png', 10,None),
('MS03_20180411_135524.png', 10,None),
('MS04_20180411_143045.png', 10,None),
('MS04_20180411_144239.png', 10,None),
)
for (file,minFrq,maxFrq) in imggrid:
image = ndimage.imread(os.path.join(basePath,file))
image=-image
grid=findGrid(image,minFrq=minFrq,maxFrq=maxFrq,numPeak=2)
plt.figure('grid');plt.imshow(image, interpolation="nearest", cmap='gray')
plotGrid(grid, image.shape)
plt.show()
def testFindObj():
plt.ioff()
imggrid=(
('grid_20180409_115332_45deg.png', 2,None),
)
for (file,p0,p1) in imggrid:
image = ndimage.imread(os.path.join(basePath,file))
image=-image
objPos=findObj(image,debug=0)
plt.figure('findObj');plt.imshow(image, interpolation="nearest", cmap='gray')
plt.plot(objPos[:,1],objPos[:,0],'r+',markeredgewidth=2, markersize=10)
plt.axis('image')
plt.show()
def testPhaseEnhance():
mu=0.001 # um-1, optimal value found is 0.001
phase_shift=-0.4 # rad/um, optimal value found is -1
E=18 # keV
wavelength=12.4/E/10000 # um
delta=-wavelength/(2*np.pi)*phase_shift
delta=4.38560287631e-06
M=1
R2=19*1e4 # um
pixel_size=0.325 # um
flux=1e12 # ph/s
for fn in ('lyso1_scan_18keV_190mm_MosaicJ_noblend_crop.tif',
'PepT2_scan_18keV_190mm_MosaicJ_noblend_crop_FFT40.tif',
'SiN_lysoS1_scan_18keV_190mm_MosaicJ_noblend_crop_FFT40.tif',
'SiN_PepT2_scan_18keV_190mm_MosaicJ_noblend_crop.tif',):
img = ndimage.imread(os.path.join(basePath,fn))
FOV_pix = img.shape
FOV_um = (FOV_pix[0] * pixel_size, FOV_pix[1] * pixel_size)
I_in = flux / (FOV_um[0] * FOV_um[1]) # ph/s/um2
phase_image = phase_retrieval_intensity_tranport(img, mu, delta, I_in, M, pixel_size, R2)
phase_image_flipped = phase_retrieval_intensity_tranport(np.fliplr(img), mu, delta, I_in, M, pixel_size, R2)
added_image = np.add(phase_image,np.fliplr(phase_image_flipped))
m=img.mean();s=img.std()
ShowImage(img, title=fn+' raw', vmin=m-3*s, vmax=m+3*s)
m=added_image.mean();s=added_image.std()
ShowImage(added_image, title=fn+' phase retrival', vmin=m-3*s, vmax=m+3*s)
plt.show()
basePath='/home/zamofing_t/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/python/images/'
#testfftLoop()
#testFindGrid()
testFindObj()
#testPhaseEnhance()
exit(0)
plt.ion()
#image = ndimage.imread(os.path.join(basePath, 'lyso1_scan_18keV_190mm_MosaicJ_noblend_crop.tif'))
image = ndimage.imread(os.path.join(basePath, 'lyso1_scan_18keV_190mm_MosaicJ_noblend_crop.tif'))
#image = ndimage.imread(os.path.join(basePath, 'grid_20180409_115332.png'))
image = -image
#plt.figure('input');
#plt.imshow(image, interpolation="nearest", cmap='gray')
#sbl=ndi.sobel(image)
#plt.figure('sobel');
#plt.imshow(sbl, interpolation="nearest", cmap='gray')
objPos = findObj(image, objSize=50,debug=255)
plt.figure('findObj');
plt.imshow(image, interpolation="nearest", cmap='gray')
plt.plot(objPos[:, 1], objPos[:, 0], 'r+', markeredgewidth=2, markersize=10)
plt.axis('image')
plt.show()
#d2r=np.pi/180.
#image=genImg((600,800),(4.,1.0,10.*d2r))
#image=genImg((600,800),(4.5,-3.2,70.*d2r))
#image=genImg((600,800),(-4.5,3.2,290.*d2r)) #same image
#findGrid(image,numPeak=1)
#findGrid(image,numPeak=1,debug=2)
#for v in np.arange(0,2,.3):
# image=genImg((600,800),(8,.2+v,40.*d2r),(.4,5.2,30.*d2r))
# #image = genImg((600, 800), (.4,5.2,30.))
# plt.figure(10);plt.cla()
# plt.imshow(image)
# grid=findGrid(image,numPeak=2,debug=255)
# plt.figure(1);plt.cla()
# plt.imshow(image, interpolation="nearest", cmap='gray')
# plotGrid(grid,image.shape)
# plt.show()
#image=genImg((600,800),(9.5,.2,0),(.4,5.2,0),(4,8,0))
#findObj(image,viz=1)
#findObj(image,viz=255)
#print(findObj(image))

View File

@@ -0,0 +1,551 @@
#!/usr/bin/env python
#*-----------------------------------------------------------------------*
#| |
#| Copyright (c) 2013 by Paul Scherrer Institute (http://www.psi.ch) |
#| |
#| Author Thierry Zamofing (thierry.zamofing@psi.ch) |
#*-----------------------------------------------------------------------*
'''
implements an image view to show a colored image of a hdf5 dataset.
'''
if __name__ == '__main__':
#Used to guarantee to use at least Wx2.8
import wxversion
wxversion.ensureMinimal('2.8')
import wx
import matplotlib as mpl
if __name__ == '__main__':
mpl.use('WXAgg')
#or mpl.use('WX')
#matplotlib.get_backend()
import os
import numpy as np
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg as NavigationToolbar
import pylab as plt #used for the colormaps
class SliderGroup():
def __init__(self, parent, label, range=(0,100),val=0):
self.sliderLabel = wx.StaticText(parent, label=label)
self.sliderText = wx.TextCtrl(parent, -1, style=wx.TE_PROCESS_ENTER)
self.slider = wx.Slider(parent, -1)
self.slider.SetRange(range[0],range[1])
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self.sliderLabel, 0, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, border=2)
sizer.Add(self.sliderText, 0, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, border=2)
sizer.Add(self.slider, 1, wx.EXPAND)
self.sizer = sizer
self.slider.Bind(wx.EVT_SLIDER, self.sliderHandler)
self.sliderText.Bind(wx.EVT_TEXT_ENTER, self.sliderTextHandler)
self.SetValue(val)
def SetValue(self, value):
self.value = value
self.slider.SetValue(value)
self.sliderText.SetValue(str(value))
def SetCallback(self,funcCB,usrData):
self.cbFuncData=(funcCB,usrData)
def Callback(self,value,msg):
try:
(funcCB,usrData)=self.cbFuncData
except BaseException as e:
pass
else:
funcCB(usrData,value,msg)
def sliderHandler(self, evt):
value = evt.GetInt()
self.sliderText.SetValue(str(value))
self.value=value
self.Callback(value,0)
def sliderTextHandler(self, evt):
value = int(self.sliderText.GetValue())
self.slider.SetValue(value)
value = self.slider.Value
self.sliderText.SetValue(str(value))
self.value=value
self.Callback(value,0)
def AddToolbar(parent,sizer):
toolbar = NavigationToolbar(parent)
toolbar.Realize()
if wx.Platform == '__WXMAC__':
# Mac platform (OSX 10.3, MacPython) does not seem to cope with
# having a toolbar in a sizer. This work-around gets the buttons
# back, but at the expense of having the toolbar at the top
parent.SetToolBar(toolbar)
else:
# On Windows platform, default window size is incorrect, so set
# toolbar width to figure width.
tw, th = toolbar.GetSizeTuple()
fw, fh = parent.GetSizeTuple()
# By adding toolbar in sizer, we are able to put it at the bottom
# of the frame - so appearance is closer to GTK version.
# As noted above, doesn't work for Mac.
toolbar.SetSize(wx.Size(fw, th))
sizer.Add(toolbar, 0, wx.LEFT | wx.EXPAND)
# update the axes menu on the toolbar
toolbar.update()
return toolbar
#class ShiftedLogNorm(mpl.colors.Normalize):
class ShiftedLogNorm(mpl.colors.LogNorm):
#copied and modified from LogNorm
def __call__(self, value, clip=None):
#print value.shape,self.vmin,self.vmax,self.clip,clip
if clip is None:
clip = self.clip
ofs0=1-self.vmin
ofs1=1./(np.log(self.vmax+1-self.vmin))
result=np.log(value+ofs0)*ofs1
result = np.ma.masked_less_equal(result, 0, copy=False)
return result
def inverse(self, value):
if not self.scaled():
raise ValueError("Not invertible until scaled")
vmin, vmax = self.vmin, self.vmax
ofs0=1-vmin
if mpl.cbook.iterable(value):
val = np.ma.asarray(value)
return vmin * np.ma.power((vmax/vmin), val)-ofs0
else:
return vmin * pow((vmax/vmin), value)-ofs0
def autoscale_None(self, A):
if self.vmin is None:
self.vmin = np.ma.min(A)
if self.vmax is None:
self.vmax = np.ma.max(A)
pass
def autoscale(self, A):
pass
class MPLCanvasImg(FigureCanvas):
def __init__(self,parent,SetStatusCB=None):
if SetStatusCB:
self.SetStatusCB=SetStatusCB
fig = mpl.figure.Figure()
ax = fig.add_axes([0.075,0.1,0.75,0.85])
FigureCanvas.__init__(self,parent, -1, fig)
self.mpl_connect('motion_notify_event', self.OnMotion)
self.mpl_connect('button_press_event', self.OnBtnPress)
self.mpl_connect('button_release_event', self.OnBtnRelease)
self.mpl_connect('scroll_event', self.OnBtnScroll)
self.mpl_connect('key_press_event',self.OnKeyPress)
self.fig=fig
self.ax=ax
def InitChild(self,data):
if data.dtype==np.complex128:
self.dataRaw=data
#data=np.angle(data)
data=np.absolute(data)
fig=self.fig
ax=self.ax
msk=~np.isnan(data);msk=data[msk]
avg=np.average(msk); std=np.std(msk)
vmin=np.min(msk);vmax=np.max(msk)
vmin=max(vmin,avg-3*std);vmax=min(vmax,avg+3*std)
if vmin==0:vmin=1
if vmax<=vmin:
vmax=vmin+1
#norm=ShiftedLogNorm()
norm=mpl.colors.Normalize()
#img = ax.imshow(data,interpolation='nearest',cmap=mpl.cm.jet, norm=ShiftedLogNorm(vmin=vmin, vmax=vmax))
img = ax.imshow(data,interpolation='nearest',cmap=mpl.cm.jet, vmin=vmin, vmax=vmax)
colBar=fig.colorbar(img,orientation='vertical',norm=norm)
#colBar.norm=ShiftedLogNorm(vmin=vmin, vmax=vmax)
colBar.norm=mpl.colors.Normalize(vmin=vmin, vmax=vmax)
img.set_norm(colBar.norm)
img.cmap._init();bg=img.cmap._lut[0].copy();bg[:-1]/=4
ax.set_axis_bgcolor(bg)
self.colBar=colBar
self.colCycle = sorted([i for i in dir(plt.cm) if hasattr(getattr(plt.cm,i),'N')])
self.colIndex = self.colCycle.index(colBar.get_cmap().name)
self.img=img
def OnMotion(self,event):
#print event,event.x,event.y,event.inaxes,event.xdata,event.ydata
if event.inaxes==self.ax:
x=int(round(event.xdata))
y=int(round(event.ydata))
try:
v=self.img.get_array()[y,x]
except IndexError as e:
pass
else:
#print x,y,v
self.SetStatusCB(self.Parent,0,(x,y,v))
elif event.inaxes==self.colBar.ax:
colBar=self.colBar
pt=colBar.ax.bbox.get_points()[:,1]
nrm=colBar.norm
vmin,vmax,p0,p1,pS = (nrm.vmin,nrm.vmax,pt[0],pt[1],event.y)
if isinstance(colBar.norm,mpl.colors.LogNorm):#type(colBar.norm)==mpl.colors.LogNorm does not work...
vS=0
else:#scale around point
vS=vmin+(vmax-vmin)/(p1-p0)*(pS-p0)
self.SetStatusCB(self.Parent,1,vS)
try:
vmin,vmax,p0,p1,pS=self.colBarPressed
except AttributeError:
return
#if event.inaxes != self.cbar.ax: return
colBar=self.colBar
#print vmin,vmax,p0,p1,pS,type(colBar.norm)
#print 'x0=%f, xpress=%f, event.xdata=%f, dx=%f, x0+dx=%f'%(x0, xpress, event.xdata, dx, x0+dx)
if isinstance(colBar.norm,mpl.colors.LogNorm):#type(colBar.norm)==mpl.colors.LogNorm does not work...
if event.button==1:
#colBar.norm.vmin=.1
colBar.norm.vmax=vmax*np.exp((pS-event.y)/100)
#scale= np.exp((event.y-pS)/100)
elif event.button==1:#move top,bottom,both
pD = event.y - pS
vD=(vmax-vmin)/(p1-p0)*(pS-event.y)
colBar.norm.vmin = vmin+vD
colBar.norm.vmax = vmax+vD
elif event.button==3:#scale around point
scale= np.exp((pS-event.y)/100)
vS=vmin+(vmax-vmin)/(p1-p0)*(pS-p0)
#print scale,vS
colBar.norm.vmin = vS-scale*(vS-vmin)
colBar.norm.vmax = vS-scale*(vS-vmax)
self.img.set_norm(colBar.norm)#force image to redraw
colBar.patch.figure.canvas.draw()
def OnBtnPress(self, event):
"""on button press we will see if the mouse is over us and store some data"""
#print dir(event.guiEvent)
if event.inaxes == self.colBar.ax:
#if event.guiEvent.LeftDClick()==True:
# print dlg
pt=self.colBar.ax.bbox.get_points()[:,1]
nrm=self.colBar.norm
self.colBarPressed = (nrm.vmin,nrm.vmax,pt[0],pt[1],event.y)
#self.colBarPressed = event.x, event.y
#print self.colBarPressed
#self.OnMouse(event)
pass
def OnBtnRelease(self, event):
"""on release we reset the press data"""
#self.OnMouse(event)
try: del self.colBarPressed
except AttributeError: pass
def OnBtnScroll(self, event):
#self.OnMouse(event)
colBar=self.colBar
if event.inaxes==colBar.ax:
pt=colBar.ax.bbox.get_points()[:,1]
nrm=colBar.norm
vmin,vmax,p0,p1,pS = (nrm.vmin,nrm.vmax,pt[0],pt[1],event.y)
if isinstance(colBar.norm,mpl.colors.LogNorm):#type(colBar.norm)==mpl.colors.LogNorm does not work...
scale= np.exp((-event.step)/10)
colBar.norm.vmax=vmax*scale
else:#scale around point
scale= np.exp((-event.step)/10)
vS=vmin+(vmax-vmin)/(p1-p0)*(pS-p0)
#print scale,vS
colBar.norm.vmin = vS-scale*(vS-vmin)
colBar.norm.vmax = vS-scale*(vS-vmax)
self.img.set_norm(colBar.norm)#force image to redraw
colBar.patch.figure.canvas.draw()
def OnKeyPress(self, event):
colCycle=self.colCycle
colBar=self.colBar
if event.key=='down':
self.colIndex += 1
elif event.key=='up':
self.colIndex -= 1
self.colIndex%=len(colCycle)
cmap = colCycle[self.colIndex]
colBar.set_cmap(cmap)
colBar.draw_all()
self.img.set_cmap(cmap)
self.img.get_axes().set_title(cmap)
colBar.patch.figure.canvas.draw()
def OnMouse(self, event):
for k in dir(event):
if k[0]!='_':
print k,getattr(event,k)
class DlgColBarSetup(wx.Dialog):
def __init__(self,parent):
wx.Dialog.__init__(self,parent,-1,'Colormap Setup')
colBar=parent.canvas.colBar
cmap=colBar.cmap
nrm=colBar.norm
txtVMin=wx.StaticText(self,-1,'vmin')
txtVMax=wx.StaticText(self,-1,'vmax')
txtColMap=wx.StaticText(self,-1,'colormap')
self.edVMin=edVMin=wx.TextCtrl(self,-1,'%g'%nrm.vmin,style=wx.TE_PROCESS_ENTER)
self.edVMax=edVMax=wx.TextCtrl(self,-1,'%g'%nrm.vmax,style=wx.TE_PROCESS_ENTER)
txtTxrFunc=wx.StaticText(self,-1,'function')
self.cbtxrFunc=cbtxrFunc=wx.ComboBox(self, -1, choices=('linear','logarithmic'), style=wx.CB_READONLY)
cbtxrFunc.SetSelection(0 if nrm.__class__==mpl.colors.Normalize else 1)
#colMapLst=('Accent', 'Blues', 'BrBG', 'BuGn', 'BuPu', 'Dark2', 'GnBu', 'Greens', 'Greys', 'OrRd', 'Oranges', 'PRGn', 'Paired',
#'Pastel1', 'Pastel2', 'PiYG', 'PuBu', 'PuBuGn', 'PuOr', 'PuRd', 'Purples', 'RdBu', 'RdGy', 'RdPu', 'RdYlBu', 'RdYlGn', 'Reds',
#'Set1', 'Set2', 'Set3', 'Spectral', 'YlGn', 'YlGnBu', 'YlOrBr', 'YlOrRd', 'afmhot', 'autumn', 'binary', 'bone', 'brg', 'bwr',
#'cool', 'coolwarm', 'copper', 'cubehelix', 'flag', 'gist_earth', 'gist_gray', 'gist_heat', 'gist_ncar', 'gist_rainbow', 'gist_stern',
#'gist_yarg', 'gnuplot', 'gnuplot2', 'gray', 'hot', 'hsv', 'jet', 'ocean', 'pink', 'prism', 'rainbow', 'seismic', 'spectral',
#'spring', 'summer', 'terrain', 'winter')
colMapLst=('hot','spectral','jet','gray','RdYlBu','hsv','gist_stern','gist_ncar','BrBG','RdYlBu','brg','gnuplot2',
'prism','rainbow',)
self.cbColMap=cbColMap=wx.ComboBox(self, -1, choices=colMapLst, style=wx.CB_READONLY)
cbColMap.Value=cmap.name
sizer=wx.BoxSizer(wx.VERTICAL)
fgs=wx.FlexGridSizer(4,2,5,5)
fgs.Add(txtVMin,0,wx.ALIGN_RIGHT)
fgs.Add(edVMin,0,wx.EXPAND)
fgs.Add(txtVMax,0,wx.ALIGN_RIGHT)
fgs.Add(edVMax,0,wx.EXPAND)
fgs.Add(txtTxrFunc,0,wx.ALIGN_RIGHT)
fgs.Add(cbtxrFunc,0,wx.EXPAND)
fgs.Add(txtColMap,0,wx.ALIGN_RIGHT)
fgs.Add(cbColMap,0,wx.EXPAND)
sizer.Add(fgs,0,wx.EXPAND|wx.ALL,5)
edVMin.SetFocus()
btns = self.CreateButtonSizer(wx.OK|wx.CANCEL)
btnApply=wx.Button(self, -1, 'Apply')
btns.Add(btnApply, 0, wx.ALL, 5)
sizer.Add(btns,0,wx.EXPAND|wx.ALL,5)
self.Bind(wx.EVT_BUTTON, self.OnModify, id=wx.ID_OK)
self.Bind(wx.EVT_BUTTON, self.OnModify, btnApply)
#self.Bind(wx.EVT_TEXT_ENTER, self.OnModify, edVMin)
#self.Bind(wx.EVT_TEXT_ENTER, self.OnModify, edVMax)
self.Bind(wx.EVT_TEXT, self.OnModify, edVMin)
self.Bind(wx.EVT_TEXT, self.OnModify, edVMax)
self.Bind(wx.EVT_COMBOBOX, self.OnModify, cbtxrFunc)
self.Bind(wx.EVT_COMBOBOX, self.OnModify, cbColMap)
self.SetSizer(sizer)
sizer.Fit(self)
def OnModify(self, event):
#print 'OnModify'
parent=self.GetParent()
canvas=parent.canvas
colBar=canvas.colBar
cmap=colBar.cmap
nrm=colBar.norm
img=canvas.img
ax=img.get_axes()
data=img.get_array()
v=self.cbColMap.Value
if v!=cmap.name:
cmap=getattr(mpl.cm,v)
colBar.set_cmap(cmap)
colBar.draw_all()
img.set_cmap(cmap)
ax.set_title(cmap.name)
colBar.patch.figure.canvas.draw()
vmin,vmax=(float(self.edVMin.Value),float(self.edVMax.Value))
nrm.vmin=vmin; nrm.vmax=vmax
v=self.cbtxrFunc.GetCurrentSelection()
func=(mpl.colors.Normalize,ShiftedLogNorm)
if nrm.__class__!=func[v]:
if v==0: #linear mapping
colBar.norm = mpl.colors.Normalize(vmin, vmax)
elif v==1: #log mapping
img.cmap._init();bg=img.cmap._lut[0].copy();bg[:-1]/=4
ax.set_axis_bgcolor(bg)
vmin=1
colBar.norm = mpl.colors.LogNorm(vmin,vmax)
img.set_norm(colBar.norm)
colBar.patch.figure.canvas.draw()
parent.Refresh(False)
if event.GetId()==wx.ID_OK:
event.Skip()#do not consume (use event to close the window and sent return code)
class HdfImageFrame(wx.Frame):
def __init__(self, parent,lbl,ims):
wx.Frame.__init__(self, parent, title=lbl, size=wx.Size(850, 650))
canvas = MPLCanvasImg(self,self.SetStatusCB)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
self.SetSizer(sizer)
toolbar=AddToolbar(canvas,sizer)
l=len(ims)
wxAxCtrl=SliderGroup(self, label='Axis:%d'%0,range=(0,l-1))
sizer.Add(wxAxCtrl.sizer, 0, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, border=5)
wxAxCtrl.SetCallback(HdfImageFrame.OnSetView,wxAxCtrl)
idx=wxAxCtrl.value
data=ims[idx]
canvas.InitChild(data)
#self.Fit()
self.Centre()
self.BuildMenu(data.dtype)
self.canvas=canvas
self.sizer=sizer
self.toolbar=toolbar
self.ims=ims
self.wxAxCtrl=wxAxCtrl
def BuildMenu(self,dtype):
mnBar = wx.MenuBar()
#-------- Edit Menu --------
mn = wx.Menu()
mnItem=mn.Append(wx.ID_ANY, 'Setup Colormap', 'Setup the color mapping ');self.Bind(wx.EVT_MENU, self.OnColmapSetup, mnItem)
mnItem=mn.Append(wx.ID_ANY, 'Invert X-Axis', kind=wx.ITEM_CHECK);self.Bind(wx.EVT_MENU, self.OnInvertAxis, mnItem)
self.mnIDxAxis=mnItem.GetId()
mnItem=mn.Append(wx.ID_ANY, 'Invert Y-Axis', kind=wx.ITEM_CHECK);self.Bind(wx.EVT_MENU, self.OnInvertAxis, mnItem)
mnItem=mn.Append(wx.ID_ANY, 'Tomo Normalize', 'Multiplies each pixel with a normalization factor. Assumes there exist an array exchange/data_white', kind=wx.ITEM_CHECK);self.Bind(wx.EVT_MENU, self.OnTomoNormalize, mnItem)
self.mnItemTomoNormalize=mnItem
if dtype==np.complex128:
mnItem=mn.Append(wx.ID_ANY, 'Complex: Phase', kind=wx.ITEM_CHECK);self.Bind(wx.EVT_MENU, self.OnSetComplexData, mnItem)
mnBar.Append(mn, '&Edit')
mn = wx.Menu()
mnItem=mn.Append(wx.ID_ANY, 'Help', 'How to use the image viewer');self.Bind(wx.EVT_MENU, self.OnHelp, mnItem)
mnBar.Append(mn, '&Help')
self.SetMenuBar(mnBar)
self.CreateStatusBar()
def SetIdxXY(self,x,y):
self.idxXY=(x,y)
@staticmethod
def SetStatusCB(obj,mode,v):
if mode==0:
obj.SetStatusText( "x= %d y=%d val=%g"%v,0)
elif mode==1:
obj.SetStatusText( "Colormap Value %d (drag to scale)"%v,0)
else:
raise KeyError('wrong mode')
@staticmethod
def OnSetView(usrData,value,msg):
'called when a slice is selected with the slider controls'
imgFrm=usrData.slider.Parent
#imgFrm.img.set_array(imgFrm.data[usrData.value,...])
idx=imgFrm.wxAxCtrl.value
data=imgFrm.ims[idx]
try:
tomoNorm=imgFrm.tomoNorm
except AttributeError:
imgFrm.canvas.img.set_array(data)
else:
data=data*tomoNorm
imgFrm.canvas.img.set_array(data)
imgFrm.canvas.draw()
pass
def OnTomoNormalize(self,event):
if event.IsChecked():
#try to find white image
#calculate average
#show white normalize factors
white=self.data.parent['data_white']
tomoNorm=white[1,:,:]
#tomoNorm=white[:,:,:].mean(axis=0)
#np.iinfo(tomoNorm.dtype).max
#tomoNorm=float(np.iinfo(tomoNorm.dtype).max/2)/tomoNorm
tomoNorm=tomoNorm.mean()/tomoNorm
#tomoNorm=tomoNorm/float(np.iinfo(tomoNorm.dtype).max)
data=self.canvas.img.get_array()
data*=tomoNorm
#data/=tomoNorm
self.tomoNorm=tomoNorm
self.canvas.img.set_array(data)
else:
tomoNorm=self.tomoNorm
data=self.canvas.img.get_array()
data/=tomoNorm
self.canvas.img.set_array(data)
del self.tomoNorm
self.canvas.draw()
def OnSetComplexData(self, event):
if event.IsChecked():
data=np.angle(self.canvas.dataRaw)
else:
data=np.absolute(self.canvas.dataRaw)
self.canvas.img.set_array(data)
self.canvas.draw()
def OnHelp(self,event):
msg='''to change the image selection:
use the toolbar at the bottom to pan and zoom the image
use the scrollbars at the bottom (if present) to select an other slice
to change the colorscale:
drag with left mouse button to move the colorbar up and down
drag with right mouse button to zoom in/out the colorbar at a given point
use mouse weel to zoom in/out the colorbar at a given point
double click left mouse button to set maximum and minimun colorbar values
use cursor up and down to use a different colormap'''
dlg = wx.MessageDialog(self, msg, 'Help', wx.OK|wx.ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
def OnColmapSetup(self,event):
dlg=DlgColBarSetup(self)
if dlg.ShowModal()==wx.ID_OK:
pass
dlg.Destroy()
def OnInvertAxis(self,event):
ax=self.canvas.ax
#event.Checked()
if self.mnIDxAxis==event.GetId():
ax.invert_xaxis()
else:
ax.invert_yaxis()
self.canvas.draw()
pass
class ImgStackApp(wx.App):
def OnInit(self):
#parser=GetParser(False) # debug with exampleCmd
return True
def OnExit(self):
pass
def Run(ims):
app=ImgStackApp()
frame=HdfImageFrame(None, 'Title', ims)
frame.Show()
app.SetTopWindow(frame)
app.MainLoop()