diff --git a/app_config.py b/app_config.py index 6b843b0..2036939 100644 --- a/app_config.py +++ b/app_config.py @@ -263,10 +263,10 @@ verbose bits: 0x01 basic info 0x02 plot sorting steps 0x04 list program - 0x04 upload progress - 0x08 plot gather path - 0x10 plot pvt trajectory (before motion) - 0x20 print sync details + 0x08 upload progress + 0x10 plot gather path + 0x20 plot pvt trajectory (before motion) + 0x40 print sync details ''' params=[ @@ -330,12 +330,12 @@ verbose bits: {'name':'Delta Tau Parameters', 'type':'group','expanded':False, 'children':[ {'name':AppCfg.DT_HOST ,'title':'host name (host[:port:port_gather])','value':dt_host ,'type':'str'} , {'name':AppCfg.DT_MISC, 'title':'miscellaneous', 'type':'group', 'children':[ - {'name':'show plots after collection', 'value':dt_misc['show_plots'], 'type':'bool','tip':"This is a checkbox"}, - {'name':'velocity_scale', 'value':dt_misc['vel_scl'], 'type':'float', 'limits':(0, 1), 'step':0.1, 'tip':"This is a checkbox"}, - {'name':'pt2pt_time', 'value':dt_misc['pt2pt_time'],'type':'float', 'step':0.1,'tip':"This is a checkbox"}, - {'name':'sync_mode', 'value':dt_misc['sync_mode'], 'type':'int', 'tip':tip_sync_mode}, - {'name':'sync_flag', 'value':dt_misc['sync_flag'], 'type':'int', 'tip':tip_sync_flag}, - {'name':'verbose', 'value':dt_misc['verbose'], 'type':'int', 'tip':tip_verbose}, + {'name':'show_plots', 'value':dt_misc['show_plots'], 'type':'bool', 'title':'show plots after collection'}, + {'name':'vel_scl', 'value':dt_misc['vel_scl'], 'type':'float', 'limits':(0, 1), 'step':0.1, 'title':'velocity_scale', 'tip':"scale between smooth(1.0) and stop and go(0.0)"}, + {'name':'pt2pt_time', 'value':dt_misc['pt2pt_time'], 'type':'float', 'step':0.1,'tip':"time in ms from point to point"}, + {'name':'sync_mode', 'value':dt_misc['sync_mode'], 'type':'int', 'tip':tip_sync_mode}, + {'name':'sync_flag', 'value':dt_misc['sync_flag'], 'type':'int', 'tip':tip_sync_flag}, + {'name':'verbose', 'value':dt_misc['verbose'], 'type':'int', 'tip':tip_verbose}, ]}, ]}, #{'name':'Save/Restore functionality', 'type':'group','expanded':False, 'children':[ diff --git a/camera.py b/camera.py index 874395f..fa2b9a3 100755 --- a/camera.py +++ b/camera.py @@ -109,35 +109,28 @@ class epics_cam(object): def get_image(self): _log.warning('this can be very slow. Use callbacks if possible.') try: - pv_pic=self.getPv('FPICTURE') - except AttributeError: + imgSeq=self._sim['imgSeq'] + except AttributeError: pass + else: imgSeq=self._sim['imgSeq'] idx=self._sim['imgIdx'] self._sim['imgIdx']=(idx + 1) % imgSeq.shape[0] #_log.debug('simulated idx:{}'.format(idx)) - self.pic=pic=imgSeq[idx] + self._pic=pic=imgSeq[idx] return pic try: - sz=self._sz - pic = pv_pic.get(count=sz[0]*sz[1], as_numpy=True).reshape(sz[::-1]) - epics_cam.set_fiducial(pic,255) - except AttributeError as e: - _log.warning("failed to fetch image") - else: - if pic.dtype==np.int16: - pic.dtype=np.uint16 - try: - trf=self._transformation - except AttributeError as e: - pass - else: - if trf[1,0]==0: - pic=pic[::trf[0,0],::trf[1,1]] - else: - pic=pic[::trf[0,1],::trf[1,0]].T - - self.pic=pic + pic=self._pic return pic + except AttributeError as e: + pass + pv_pic=self.getPv('FPICTURE') + sz=self._sz + pic = pv_pic.get(count=sz[0]*sz[1], as_numpy=True).reshape(sz[::-1]) + epics_cam.set_fiducial(pic,255) + if pic.dtype==np.int16: + pic.dtype=np.uint16 + self._pic=pic + return pic def stop(self,v=CameraStatus.IDLE): if 'pic' in self._pv: diff --git a/geometry.py b/geometry.py index db4c2be..317d4b0 100755 --- a/geometry.py +++ b/geometry.py @@ -256,13 +256,13 @@ class geometry: _log.debug('least square data:\nK:{}\nAA:{}'.format(K, AA)) @staticmethod - def autofocus(cam,mot,rng=(-1,1),n=30,saveImg=False): + def autofocus(cam,mot,rng=(-1,1),n=30,progressDlg=None,saveImg=False): # cam camera object - # mot motor object + # mot motor object (e.g. SimMotorTweak) # rng region (min max relative to current position) to seek # n number of images to take in region - # roi region of interrest to calculate sharpness - # mode mode to calculate sharpness (sum/max-min/hist? of edge detection in roi) + # progressDlg a pg.ProgressDialog to allow to abort the function call + if mot is not None: p0=mot.get_rbv() else: @@ -295,13 +295,19 @@ class geometry: img=np.asarray(img) else: mot.move_abs(p, wait=True) - img=cam._pic # get_image() + img=cam.get_image() # get latest image img16=np.array(img, np.int16) msk=np.array(((1, 0, -1), (2, 0, -2), (1, 0, -1)), np.int16) sb1=signal.convolve2d(img16, msk, mode='same', boundary='fill', fillvalue=0) sb2=signal.convolve2d(img16, msk.T, mode='same', boundary='fill', fillvalue=0) sb=np.abs(sb1)+np.abs(sb2) mtr[i]=sb.sum() + try: + progressDlg+=1 + if progressDlg.wasCanceled(): + return + except TypeError as e: + pass _log.debug(f'{i}/{p:.4g} -> {mtr[i]:.4g}') mx=mtr.argmax() @@ -333,8 +339,7 @@ class geometry: img=PIL.Image.open(cam) img=np.asarray(img) else: - img=cam._pic # get_image() - img16=np.array(img, np.int16) + img=cam.get_image() # get latest image fid=np.ones((sz[1]+2*brd[1],sz[0]+2*brd[0]),dtype=np.uint8)*255 fid[brd[1]:sz[1]+brd[1],brd[0]:sz[0]+brd[0]]=0 diff --git a/psi_device.py b/psi_device.py index 0364c20..1a2bc89 100644 --- a/psi_device.py +++ b/psi_device.py @@ -15,7 +15,7 @@ _log=logging.getLogger(__name__) from PyQt5.QtWidgets import (QApplication,) from app_config import AppCfg #settings, option, toggle_option - +import epics from pbtools.misc.pp_comm import PPComm from pbtools.misc.gather import Gather import shapepath @@ -25,6 +25,7 @@ try: except ImportError as e: _log.warning(e) + class Deltatau: def __init__(self): app=QApplication.instance() @@ -83,7 +84,14 @@ class Jungfrau: except NameError as e: _log.warning(f'Jungfrau not connected: {e}') self._daq=None + self._pv_pulse_id=epics.PV('SAR-EXPMX-EVR0:RX-PULSEID') + def acquire(self,run_name, n_pulses, wait=False): if self._daq is not None: + self._pulse_id_start=self._pv_pulse_id.value self._daq.acquire( run_name, n_pulses=n_pulses, wait=False) + + def gather_upload(self): + self._pulse_id_end=self._pv_pulse_id.value + diff --git a/swissmx.py b/swissmx.py index 0d906b1..4724058 100755 --- a/swissmx.py +++ b/swissmx.py @@ -30,6 +30,7 @@ logging.getLogger('matplotlib').setLevel(logging.INFO) logging.getLogger('PIL').setLevel(logging.INFO) logging.getLogger('illumination').setLevel(logging.INFO) logging.getLogger('zoom').setLevel(logging.INFO) +logging.getLogger('pbtools.misc.pp_comm').setLevel(logging.INFO) _log = logging.getLogger("swissmx") #_log.setLevel(logging.INFO) @@ -47,6 +48,8 @@ import sys, os, time import json, re import random, signal, subprocess import matplotlib as mpl +import matplotlib.pyplot as plt + mpl.use('Qt5Agg') # needed to avoid blocking of ui ! TASK_JUNGFRAU_SETTINGS = "jungfrau_settings" @@ -1595,7 +1598,10 @@ Author Thierry Zamofing (thierry.zamofing@psi.ch) app=QApplication.instance() geo=app._geometry #geo.autofocus(app._camera, self.tweakers['base_z'],rng=(-1, 1), n=30,saveImg=True) - geo.autofocus(app._camera, self.tweakers['base_z'],rng=(-1, 1), n=10) + n=10 + rng=(-1, 1) + with pg.ProgressDialog('Progress', 0, n) as dlg: + geo.autofocus(app._camera, self.tweakers['base_z'],rng, n,dlg) def cb_find_fiducial(self): app=QApplication.instance() @@ -2059,33 +2065,53 @@ Author Thierry Zamofing (thierry.zamofing@psi.ch) sp.verbose=dt_misc['verbose'] sp.points=kwargs['points'] sp.meta['pt2pt_time']=dt_misc['pt2pt_time'] - sp.setup_gather() - sp.setup_sync(verbose=sp.verbose&0x20, timeOfs=0.05) - try: - p=geo._fitPlane - sp.setup_coord_trf(cz=f'{p[0]:+.18g}X{p[1]:+.18g}Y{p[2]:+.18g}') # reset to shape path system - except AttributeError: - _log.warning('no plane fitting done. z does not move') - sp.setup_coord_trf() # reset to shape path system - # sp.meta['pt2pt_time']=10 #put between setup_sync and setup_motion to have more motion points than FEL syncs - sp.setup_motion(fnPrg=fn+'.prg', mode=3, scale=1., dwell=10) - sp.homing() # homing if needed - sp.run() # start motion program - sp.wait_armed() # wait until motors are at first position - sp.trigger(0.5) # send a start trigger (if needed) after given time - jf.acquire('filename.tmp',sp.points.shape[0]) - if not dt._comm is None: - while True: - p=sp.progress() - if p<0: break - _log.info(f'progress {p}/{sp.points.shape[0]}') - time.sleep(.1) - sp.gather_upload(fnRec=fn+'.npz') - if dt_misc['show_plots']: - dp=deltatau.shapepath.DebugPlot(sp) - dp.plot_gather(mode=11) - plt.show(block=False) - #plt.show(block=True) + with pg.ProgressDialog('Progress', 0, 100) as dlg: + dlg.setWindowModality(Qt.WindowModal) + #dlg.setRange(0, 0) + #dlg.setCancelButtonText("Abort Acquisition") + #dlg.canceled.connect(self.complete_daq) + #dlg.setAutoClose(True) + #dlg.show() + + dlg.setLabelText("Setup Gather/Sync");dlg+=5 + sp.setup_gather() + sp.setup_sync(verbose=sp.verbose&0x40, timeOfs=0.05) + try: + p=geo._fitPlane + sp.setup_coord_trf(cz=f'{p[0]:+.18g}X{p[1]:+.18g}Y{p[2]:+.18g}') # reset to shape path system + except AttributeError: + _log.warning('no plane fitting done. z does not move') + sp.setup_coord_trf() # reset to shape path system + # sp.meta['pt2pt_time']=10 #put between setup_sync and setup_motion to have more motion points than FEL syncs + dlg.setLabelText("Download motion program");dlg+=5 + sp.setup_motion(fnPrg=fn+'.prg', mode=3, scale=1., dwell=10) + dlg.setLabelText("Homing and get ready");dlg+=35 + sp.homing() # homing if needed + sp.run() # start motion program + sp.wait_armed() # wait until motors are at first position + sp.trigger(0.5) # send a start trigger (if needed) after given time + #PV with the current pulse + # [sf-lc7a ~]$ caget + jf.acquire('filename.tmp',sp.points.shape[0]) + if not dt._comm is None: + dlg.setLabelText("run motion/acquisition") + dlg.setMaximum(sp.points.shape[0]) + while True: + p=sp.progress() + if p<0 or dlg.wasCanceled(): + break + #_log.info(f'progress {p}/{sp.points.shape[0]}') + dlg.setValue(p) + time.sleep(.1) + if not dlg.wasCanceled(): + jf.gather_upload() + dlg.setLabelText("upload gather data") + sp.gather_upload(fnRec=fn+'.npz') + if dt_misc['show_plots']: + dp=psi_device.shapepath.DebugPlot(sp) + dp.plot_gather(mode=11) + plt.show(block=False) + #plt.show(block=True) def esc_run_steps(self, steps, title, esc_state): self._esc_state ="busy" diff --git a/workbench/autofocus.py b/workbench/autofocus.py index c903fa1..a1ed2f9 100644 --- a/workbench/autofocus.py +++ b/workbench/autofocus.py @@ -107,9 +107,9 @@ class autofocus(QtGui.QMainWindow): # fft[300:700,400:800]=0 # v[i,1]=fft.sum() - self._imv1.setImage(sb,autoRange=auto,autoLevels=auto,autoHistogramRange=auto) - #self._imv2.setImage(sb,autoRange=auto,autoLevels=auto,autoHistogramRange=auto) - self._imv2.setImage(sbLut,autoRange=auto,autoLevels=auto,autoHistogramRange=auto) + self._imv1.setImage(img,autoRange=auto,autoLevels=auto,autoHistogramRange=auto) + self._imv2.setImage(sb,autoRange=auto,autoLevels=auto,autoHistogramRange=auto) + #self._imv2.setImage(sbLut,autoRange=auto,autoLevels=auto,autoHistogramRange=auto) mtr=self._metrics mtr[i, 0]=img.std() diff --git a/workbench/fiducialDetection.py b/workbench/fiducialDetection.py index 653747c..f9e8e4e 100644 --- a/workbench/fiducialDetection.py +++ b/workbench/fiducialDetection.py @@ -69,13 +69,26 @@ class fiducial(QtGui.QMainWindow): self._imgLst=imgLst=sorted(glob.glob("../scratch/fiducial/*.png")) self._metrics=mtr=np.ndarray(shape=(len(imgLst), 5)) mtr[:]=0 - self._sld=sld=QtGui.QSlider(QtCore.Qt.Horizontal) - sld.setMinimum(0) - sld.setMaximum(len(imgLst)-1) - sld.setValue(0) - sld.setTickPosition(QtGui.QSlider.TicksBelow) - sld.setTickInterval(1) - sld.valueChanged.connect(self.cb_sld_change) + self._sldImgIdx=sld1=QtGui.QSlider(QtCore.Qt.Horizontal) + sld1.setMinimum(0) + sld1.setMaximum(len(imgLst)-1) + sld1.setValue(0) + sld1.setTickPosition(QtGui.QSlider.TicksBelow) + sld1.setTickInterval(1) + sld1.valueChanged.connect(self.cb_sld_change) + + self._sldTplSz=sld2=QtGui.QSlider(QtCore.Qt.Horizontal) + sld2.setMinimum(0) + sld2.setMaximum(300) + sld2.setValue(90) + sld2.valueChanged.connect(self.cb_sld_change) + + self._sldTplBrd=sld3=QtGui.QSlider(QtCore.Qt.Horizontal) + sld3.setMinimum(3) + sld3.setMaximum(50) + sld3.setValue(20) + sld3.valueChanged.connect(self.cb_sld_change) + self._imv1=imv1=pg.ImageView() self._imv2=imv2=pg.ImageView() @@ -91,10 +104,12 @@ class fiducial(QtGui.QMainWindow): pw.resize(100,100) pw.setMaximumSize(2000,200) - l.addWidget(sld, 0, 0) - l.addWidget(imv1, 1, 0) - l.addWidget(imv2, 2, 0) - l.addWidget(pw, 3, 0) + l.addWidget(sld1, 0, 0) + l.addWidget(sld2, 1, 0) + l.addWidget(sld3, 2, 0) + l.addWidget(imv1, 3, 0) + l.addWidget(imv2, 4, 0) + l.addWidget(pw, 5, 0) ## Display the data self.cb_sld_change(0,True) @@ -108,7 +123,7 @@ class fiducial(QtGui.QMainWindow): self._imv2.setLevels(0, 1) def cb_sld_change(self,val,auto=False): - i=self._sld.value() + i=self._sldImgIdx.value() _log.debug(f'{i}') fn= self._imgLst[i] img=PIL.Image.open(fn) @@ -134,15 +149,17 @@ class fiducial(QtGui.QMainWindow): #fid=np.ones((250,250),dtype=np.uint8)*255 #fid[20:230,20:230]=0 - sz=(90, 90); brd=(20, 20) # zoom 001 - #sz=(130, 130); brd=(20, 20) # zoom 200 - #sz=(210, 210); brd=(20, 20) # zoom 400 + #sz=( 84, 84); brd=( 7, 7) # zoom 001 + #sz=(128, 128); brd=( 9, 9) # zoom 200 + #sz=(200, 200); brd=(13, 13) # zoom 400 + v=self._sldTplSz.value(); sz=(v,v) + v=self._sldTplBrd.value(); brd=(v,v) + fid=np.ones((sz[1]+2*brd[1],sz[0]+2*brd[0]),dtype=np.uint8)*255 fid[brd[1]:sz[1]+brd[1],brd[0]:sz[0]+brd[0]]=0 mask=np.ones((sz[1]+2*brd[1],sz[0]+2*brd[0]),dtype=np.uint8)*255 mask[2*brd[1]:sz[1],2*brd[0]:sz[0]]=0 - #https://docs.opencv.org/4.5.2/d4/dc6/tutorial_py_template_matching.html #res = cv.matchTemplate(img,fid,cv.TM_CCORR_NORMED ) res = cv.matchTemplate(img,fid,cv.TM_CCORR_NORMED,mask=mask) @@ -177,6 +194,11 @@ class fiducial(QtGui.QMainWindow): self._imv1.setImage(img,autoRange=auto,autoLevels=auto,autoHistogramRange=auto) self._imv2.setImage(res,autoRange=auto,autoLevels=auto,autoHistogramRange=auto) pos=mtr.mean(0)[::-1]+(fw2,fh2) + print(fid) + print(mask) + print(corr) + print(mtr) + print(f'TplSz:{sz} TplBrd:{brd}') _log.debug(f'position: {pos} correlation:{corr.mean()}')