saving document, plane and fiducal fitter

This commit is contained in:
2022-08-30 15:46:45 +02:00
parent 011eaa3e31
commit 7deda365c1
5 changed files with 172 additions and 40 deletions

View File

@@ -10,9 +10,25 @@ _log = logging.getLogger(__name__)
from PyQt5.QtCore import QSettings from PyQt5.QtCore import QSettings
from PyQt5.QtWidgets import QApplication, QLineEdit from PyQt5.QtWidgets import QApplication, QLineEdit
import os, json, yaml import json
import numpy as np
import GenericDialog import GenericDialog
class MyJsonEncoder(json.JSONEncoder):
""" Special json encoder for numpy types """
def default(self, obj):
if isinstance(obj, np.integer):
return int(obj)
elif isinstance(obj, np.floating):
return float(obj)
elif isinstance(obj, np.ndarray):
return obj.tolist()
elif type(obj) not in (dict,list,str,int):
_log.error('dont know how to json')
return repr(obj)
return json.JSONEncoder.default(self, obj)
class AppCfg(QSettings): class AppCfg(QSettings):
GEO_OPT_CTR='geometry/opt_ctr' GEO_OPT_CTR='geometry/opt_ctr'
@@ -87,8 +103,7 @@ class AppCfg(QSettings):
self.setValue(AppCfg.GEO_BEAM_POS, [23.4, -54.2]) # beam position relativ to optical center in mm self.setValue(AppCfg.GEO_BEAM_POS, [23.4, -54.2]) # beam position relativ to optical center in mm
if AppCfg.GEO_PIX2POS not in keys: if AppCfg.GEO_PIX2POS not in keys:
_log.warning(f'{AppCfg.GEO_OPT_CTR} not defined. use default') _log.warning(f'{AppCfg.GEO_PIX2POS} not defined. use default')
import numpy as np
lut_pix2pos=(np.array([1., 200., 400., 600., 800., 1000.]), lut_pix2pos=(np.array([1., 200., 400., 600., 800., 1000.]),
np.array([[[ 2.42827273e-03, -9.22117396e-05], np.array([[[ 2.42827273e-03, -9.22117396e-05],
[-1.10489804e-04, -2.42592492e-03]], [-1.10489804e-04, -2.42592492e-03]],
@@ -106,7 +121,6 @@ class AppCfg(QSettings):
if AppCfg.GEO_OPT_CTR not in keys: if AppCfg.GEO_OPT_CTR not in keys:
_log.warning(f'{AppCfg.GEO_OPT_CTR} not defined. use default') _log.warning(f'{AppCfg.GEO_OPT_CTR} not defined. use default')
import numpy as np
opt_ctr=np.array([603.28688025, 520.01112846]) opt_ctr=np.array([603.28688025, 520.01112846])
self.setValue(AppCfg.GEO_OPT_CTR, opt_ctr) self.setValue(AppCfg.GEO_OPT_CTR, opt_ctr)
@@ -129,10 +143,25 @@ class AppCfg(QSettings):
# print(data) # print(data)
def setValue(self, key: str, val): #overload to debug def setValue(self, key: str, val): #overload to debug
# only simple lists, str, int, float can not be serialized nicely
t=type(val)
if key in (AppCfg.GEO_PIX2POS):
val=json.dumps(val, cls=MyJsonEncoder)
elif key in (AppCfg.GEO_OPT_CTR,AppCfg.GEO_BEAM_SZ,AppCfg.GEO_BEAM_POS,):
val=val.tolist()
elif type(val)==tuple:
val=list(val)
return super(AppCfg, self).setValue(key,val) return super(AppCfg, self).setValue(key,val)
def value(self,key,*vargs,**kwargs): #overload to debug def value(self,key,*vargs,**kwargs): #overload to debug
val=super(AppCfg, self).value(key,*vargs,**kwargs) val=super(AppCfg, self).value(key,*vargs,**kwargs)
if key in (AppCfg.GEO_PIX2POS):
val=json.loads(val)#, object_hook=MyJsonDecoder)
val=(np.array(val[0]),np.array(val[1]))
elif key in (AppCfg.GEO_BEAM_SZ,AppCfg.GEO_BEAM_POS,):
val=np.array(tuple(map(float, val)))/1000
elif key in (AppCfg.GEO_OPT_CTR):
val=np.array(tuple(map(float, val)))
return val return val
#@property #@property
#def value(self): #def value(self):

View File

@@ -258,9 +258,15 @@ class epics_cam(object):
imgSeq=np.ndarray(shape=(len(glb),sz[1],sz[0]),dtype=np.uint8) # shape is (n,h,w) imgSeq=np.ndarray(shape=(len(glb),sz[1],sz[0]),dtype=np.uint8) # shape is (n,h,w)
for i,fn in enumerate(glb): for i,fn in enumerate(glb):
img=PIL.Image.open(fn) img=PIL.Image.open(fn)
assert(img.mode=='L') # 8 bit grayscale #assert(img.mode=='L') # 8 bit grayscale
assert(sz==img.size) assert(sz==img.size)
imgSeq[i,:,:]=np.array(img.getdata()).reshape(sz[::-1]) if img.mode in ('L'):
#imgSeq[i, :, :]=np.asarray(img)
imgSeq[i,:,:]=np.array(img.getdata()).reshape(sz[::-1])
elif img.mode=='LA':
imgSeq[i, :, :]=np.asarray(img)[:,:,0]
else:
raise TypeError(f'unsupported image mode format {img.mode}')
pic=imgSeq[i] pic=imgSeq[i]
epics_cam.set_fiducial(pic, 255) epics_cam.set_fiducial(pic, 255)

View File

@@ -268,7 +268,7 @@ class geometry:
pass pass
@staticmethod @staticmethod
def least_square_trf(points): def least_square_trf(points,fid=None,sort=True):
# inputs are 4 points of a parallelogram # inputs are 4 points of a parallelogram
# this function returns the least square transformation # this function returns the least square transformation
#[ px] [a b c ] [ q ] #[ px] [a b c ] [ q ]
@@ -287,35 +287,71 @@ class geometry:
# #
# A *aa = y # A *aa = y
A=np.array(( # A=np.array((
(0,0,1,0,0,0), # (0,0,1,0,0,0),
(0,0,0,0,0,1), # (0,0,0,0,0,1),
(0,1,1,0,0,0), # (0,1,1,0,0,0),
(0,0,0,0,1,1), # (0,0,0,0,1,1),
(1,0,1,0,0,0), # (1,0,1,0,0,0),
(0,0,0,1,0,1), # (0,0,0,1,0,1),
(1,1,1,0,0,0), # (1,1,1,0,0,0),
(0,0,0,1,1,1)), np.float) # (0,0,0,1,1,1)), np.float)
if fid is None:
fid=np.array(((0,0),(0,1),(1,0),(1,1)))
elif type(fid)!=np.ndarray:
fid=np.array(fid)
y=points # sort points p00 p01 p10 p11 y=points # sort points p00 p01 p10 p11
s=y[:,1].argsort() if sort:
y=y[s,:] # p01 ----------- p11
s=y[:2,0].argsort() # | |
y[:2,:]=y[:2,:][s,:] # | |
s=y[2:,0].argsort() # p00-------------p10
y[2:,:]=y[2:,:][s,:] def sort(a):
s=a[:,0].argsort()
a=a[s,:]
s=a[:2,1].argsort()
a[:2,:]=a[:2,:][s,:]
s=a[2:,1].argsort()
a[2:,:]=a[2:,:][s,:]
return a
y=sort(y)
fid=sort(fid)
A=np.ndarray((len(fid)*2,6))
i=0
for q,r in fid:
A[i] =(q,r,1,0,0,0)
A[i+1]=(0,0,0,q,r,1)
i+=2
y=np.asmatrix(y.ravel()).T y=np.asmatrix(y.ravel()).T
A=np.asmatrix(A) A=np.asmatrix(A)
aa=(A.T*A).I*A.T*y aa=(A.T*A).I*A.T*y
aa=aa.reshape((2,3)) aa=aa.reshape((2,3))
return aa return aa
@staticmethod def least_square_plane(self,points):
def least_square_plane(points):
#inputs are multiple (x,y,z) points #inputs are multiple (x,y,z) points
# this function returns the parameters of least square fitted plane x=x,y=y,z=ax+bx+c # this function returns the parameters of least square fitted plane x=x,y=y,z=ax+bx+c
pass
# a b c
# [px1 py1 1 ] [a] [pz1]
# [px2 py2 1 ] [b] [pz2]
# [... ... 1 ]*[c]=[...]
# [pxn pyn 1 ] [pzn]
A=np.ndarray((points.shape[0],3))
y=points[:,2]
A[:,2]=1
A[:,0:2]=points[:,:2]
y=np.asmatrix(y.ravel()).T
A=np.asmatrix(A)
aa=(A.T*A).I*A.T*y
aa=aa.A.ravel()
for p in points:
print(f'{p}->{aa[0]*p[0]+aa[1]*p[1]+aa[2]}')
self._fitPlane=aa
if __name__=="__main__": if __name__=="__main__":
@@ -473,7 +509,7 @@ if __name__=="__main__":
[40, 35], [40, 35],
[10, 35], [10, 35],
[40, 15]]) [40, 15]])
pts=-pts #pts=-pts
for p in pts: for p in pts:

View File

@@ -12,6 +12,8 @@ TODO: Dimension of pyqtgraph is in um not pixel (as now)
''' '''
#import initExample ## Add path to library (just for examples; you do not need this) #import initExample ## Add path to library (just for examples; you do not need this)
import logging
_log=logging.getLogger(__name__)
import pyqtgraph as pg import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui from pyqtgraph.Qt import QtCore, QtGui
@@ -316,7 +318,8 @@ class Path(pg.ROI):
p.drawLines(lh,lv) p.drawLines(lh,lv)
def __repr__(self): def __repr__(self):
s=f'{self.__class__.__name__}:(pos:{itr2str(self.pos())}, size:{itr2str(self.size())}, cnt:{self._cnt}, ficucialScale:{self._fidScl}}}' s=f'{self.__class__.__name__}:(pos:{itr2str(self.pos())}, size:{itr2str(self.size())}, numFid:{self._fiducial.shape[0]}, numPts:{self._path.shape[0]}, ficucialScale:{self._fidScl}}}'
return s return s
def obj2json(self,encoder): def obj2json(self,encoder):
@@ -373,7 +376,15 @@ class FixTargetFrame(pg.ROI):
} }
def __init__( self, pos=(0,0), size=(100,100), tpl='test', dscr=None, **kwargs): def __init__( self, pos=(0,0), size=(100,100), tpl='test', dscr=None, **kwargs):
trf=kwargs.pop('trf',None)
pg.ROI.__init__(self, pos, size, **kwargs) pg.ROI.__init__(self, pos, size, **kwargs)
if trf is not None:
t=self.transform()
t.setMatrix(trf[0][0], trf[0][1], 0,
trf[1][0], trf[1][1], 0,
0, 0, 1)
self.setTransform(t)
#fiducial type 0: 5 squares with pitch 120 um #fiducial type 0: 5 squares with pitch 120 um
if dscr is not None: if dscr is not None:
self._dscr=dscr self._dscr=dscr
@@ -383,6 +394,9 @@ class FixTargetFrame(pg.ROI):
self.addScaleHandle([1, 1], [0, 0]) self.addScaleHandle([1, 1], [0, 0])
self.addScaleHandle([0, 0], [1, 1]) self.addScaleHandle([0, 0], [1, 1])
self.addScaleRotateHandle([1, 0], [0, 0]) self.addScaleRotateHandle([1, 0], [0, 0])
#self.sigHoverEvent.connect(self.hover)
# def hover(self):
# _log.debug(f'hover {self}')
def paint(self, p, *args): def paint(self, p, *args):
#pg.ROI.paint(self, p, *args) #pg.ROI.paint(self, p, *args)
@@ -445,6 +459,11 @@ class FixTargetFrame(pg.ROI):
'size':tuple(self.size()), 'size':tuple(self.size()),
'dscr': self._dscr 'dscr': self._dscr
} }
trf=self.transform()
if not trf.isIdentity():
obj_info(trf)
trf=((trf.m11(),trf.m12()),(trf.m21(),trf.m22()))
jsn['trf']=trf
return jsn return jsn

View File

@@ -64,6 +64,7 @@ TASK_SETUP_CAMERA = "setup_camera"
TASK_SETUP_ROI = "setup_rois" TASK_SETUP_ROI = "setup_rois"
TASK_SAMPLE_SELECTION = "task_sample_selection" TASK_SAMPLE_SELECTION = "task_sample_selection"
TASK_SCREENING = "screening" TASK_SCREENING = "screening"
TASK_FIX_TARGET = "fix_trg"
TASK_GRID = "grid" TASK_GRID = "grid"
TASK_PRELOCATED = "prelocated" TASK_PRELOCATED = "prelocated"
TASK_HELICAL = "helical" TASK_HELICAL = "helical"
@@ -407,13 +408,14 @@ class SwissMxWnd(QMainWindow, Ui_MainWindow):
self._goGrid=grid=pg.GridItem() # green grid and labels self._goGrid=grid=pg.GridItem() # green grid and labels
grid.opts['pen']=QPen(QColor(0, 255, 0)) grid.opts['pen']=QPen(QColor(0, 255, 0))
grid.opts['textPen']=QPen(QColor(0, 255, 0)) grid.opts['textPen']=QPen(QColor(0, 255, 0))
#tr.reset() tr.reset()
#grid.setTransform(tr) # assign transform #grid.setTransform(tr) # assign transform
vb.addItem(grid) vb.addItem(grid)
#--- beam marker --- #--- beam marker ---
bm_sz=np.array((50, 40)) # it is immidiatly repositioned in zoom_changed_cb bm_sz=np.array((50, 40)) # it is immidiatly repositioned in zoom_changed_cb
self._goBeamMarker=bm=UsrGO.Marker(-opt_ctr-bm_sz/2,bm_sz,mode=0) self._goBeamMarker=bm=UsrGO.Marker(-opt_ctr-bm_sz/2,bm_sz,mode=0)
bm.setTransform(tr) # assign transform
vb.addItem(bm) vb.addItem(bm)
#--- opctical center ---- #--- opctical center ----
@@ -717,8 +719,8 @@ class SwissMxWnd(QMainWindow, Ui_MainWindow):
_log.warning(e) _log.warning(e)
else: else:
opt_ctr=geo._opt_ctr opt_ctr=geo._opt_ctr
bm_sz=np.array(tuple(map(float, cfg.value(AppCfg.GEO_BEAM_SZ))))/1000 bm_sz=cfg.value(AppCfg.GEO_BEAM_SZ)
bm_pos=np.array(tuple(map(float, cfg.value(AppCfg.GEO_BEAM_POS))))/1000 bm_pos=cfg.value(AppCfg.GEO_BEAM_POS)
bm_sz=np.abs(geo.pos2pix(bm_sz)) bm_sz=np.abs(geo.pos2pix(bm_sz))
bm_pos=-opt_ctr-geo.pos2pix(bm_pos)-bm_sz/2 bm_pos=-opt_ctr-geo.pos2pix(bm_pos)-bm_sz/2
bm=self._goBeamMarker bm=self._goBeamMarker
@@ -936,7 +938,7 @@ class SwissMxWnd(QMainWindow, Ui_MainWindow):
#_log.debug(f"vb pos {self.vb.mapFromScene(p)}") #pixel position on the scene (including black frame) #_log.debug(f"vb pos {self.vb.mapFromScene(p)}") #pixel position on the scene (including black frame)
#for o in self.vb.childGroup.childItems(): #for o in self.vb.childGroup.childItems():
# _log.debug(f"{type(o)} pos {o.mapFromScene(p)}") #pixel position on the scene (including black frame) # _log.debug(f"{type(o)} pos {o.mapFromScene(p)}") #pixel position on the scene (including black frame)
_log.debug(f"currentItem:{event.currentItem}")
task = self.active_task() task = self.active_task()
if task==TASK_SETUP_GEOMETRY_CALIB: if task==TASK_SETUP_GEOMETRY_CALIB:
self.mouse_click_event_geometry_calib(event) self.mouse_click_event_geometry_calib(event)
@@ -1235,11 +1237,40 @@ class SwissMxWnd(QMainWindow, Ui_MainWindow):
def execute_collection(self): def execute_collection(self):
app=QApplication.instance() app=QApplication.instance()
geo=app._geometry geo=app._geometry
#zoom=app._zoom.get_val()
task = self.active_task() task = self.active_task()
self._is_aborted = False #self._is_aborted = False
method = self._tabs_daq_methods.currentWidget().accessibleName() #method = self._tabs_daq_methods.currentWidget().accessibleName()
if task == TASK_GRID:
if task == TASK_FIX_TARGET:
#self.re_connect_collect_button(callback=self.collect_abort_grid, accessibleName="grid_abort", label="Abort Grid Scan",)
#self._inspect = self._grid_inspect_area
#self._inspect.setPlainText("")
fast_x=self.tweakers["fast_x"];
fast_y=self.tweakers["fast_y"]
fx=fast_x.get_val()
fy=fast_y.get_val()
opt_ctr=geo._opt_ctr
for go in self._goTracked['objLst']:
points=go.get_points() #points in coordinate system of ROI
# names consists of abrevations
# part 0: po=position sz=size dt=delta
# part 1: px=pixel eu=engineering units (e.g. mm)
po_px=go.pos()
sz_px=go.size()
tr=go.transform() # TODO: this is not yet used
UsrGO.obj_info(tr)
dt_px=-opt_ctr-po_px
dt_eu=geo.pix2pos(dt_px)+(fx,fy)
for i in range(points.shape[0]):
points[i,:]=dt_eu+geo.pix2pos(points[i,:])#*tr
self.daq_collect_points(points, visualizer_method="grid", visualizer_params=None)
elif task == TASK_GRID:
self.re_connect_collect_button( self.re_connect_collect_button(
callback=self.collect_abort_grid, callback=self.collect_abort_grid,
accessibleName="grid_abort", accessibleName="grid_abort",
@@ -1541,6 +1572,7 @@ class SwissMxWnd(QMainWindow, Ui_MainWindow):
self._moduleFixTarget =mft = ModuleFixTarget.WndFixTarget(self,data) self._moduleFixTarget =mft = ModuleFixTarget.WndFixTarget(self,data)
tab = self._tabs_daq_methods.insertTab(0,mft,'Fix Target') tab = self._tabs_daq_methods.insertTab(0,mft,'Fix Target')
mft.setAccessibleName(TASK_FIX_TARGET)
self._tabs_daq_methods.setCurrentWidget(mft) #set this as the active tabs self._tabs_daq_methods.setCurrentWidget(mft) #set this as the active tabs
mft._cbType.addItems(["Fiducial", "FixTarget(12.5x12.5)", "FixTarget(23.0x23.0)", "FixTarget(<param>)", "Grid(<param>)"]) mft._cbType.addItems(["Fiducial", "FixTarget(12.5x12.5)", "FixTarget(23.0x23.0)", "FixTarget(<param>)", "Grid(<param>)"])
@@ -1677,20 +1709,30 @@ class SwissMxWnd(QMainWindow, Ui_MainWindow):
for go in reversed(objLst): for go in reversed(objLst):
if type(go)==UsrGO.FixTargetFrame: if type(go)==UsrGO.FixTargetFrame:
if j==4: if j==4:
trf=geometry.geometry.least_square_trf(ptFitTrf) #fid=np.array(((0.1, 0.1), (0.1, 1.1), (1.1, 0.1), (1.1, 1.1)))
fid=np.array(go._dscr['fiducial']['pos'])
sz=np.array(go._dscr['size'])
fid=fid/sz
trf=geometry.geometry.least_square_trf(ptFitTrf,fid)
tr=go.transform() tr=go.transform()
tr.setMatrix(100, 0, 0, tr.setMatrix(1, trf[1,0]/trf[0,0], 0,
0, 100, 0, trf[0,1]/trf[1,1], 1, 0,
0, 0, 1) 0, 0, 1)
go.setTransform(tr) go.setTransform(tr)
go.setPos(trf[:,2]) go.setPos(trf[:,2])
go.setSize((1,1)) #go.setSize((1,1))
sz=(trf[0,0],trf[1,1])
go.setSize(sz)
j=0 j=0
elif type(go)==UsrGO.Fiducial: elif type(go)==UsrGO.Fiducial:
ptFitTrf[j]=go.pos()+go.size()/2 ptFitTrf[j]=go.pos()+go.size()/2
ptFitPlane[i]=go._xyz ptFitPlane[i]=go._xyz
i+=1;j+=1 i+=1;j+=1
plane=geometry.geometry.least_square_plane(ptFitPlane) app=QApplication.instance()
geo=app._geometry
if n>=3:
geo.least_square_plane(ptFitPlane)
# **************** OBSOLETE AND/OR OLD STUFF **************** # **************** OBSOLETE AND/OR OLD STUFF ****************