towards fiducial detection

This commit is contained in:
2022-09-15 14:46:30 +02:00
parent e4b3ad0b5f
commit 7534eec921
5 changed files with 319 additions and 101 deletions

View File

@@ -248,7 +248,7 @@ class geometry:
_log.debug('least square data:\nK:{}\nAA:{}'.format(K, AA)) _log.debug('least square data:\nK:{}\nAA:{}'.format(K, AA))
@staticmethod @staticmethod
def autofocus(cam,mot,rng=(-1,1),n=30): def autofocus(cam,mot,rng=(-1,1),n=30,saveImg=False):
# cam camera object # cam camera object
# mot motor object # mot motor object
# rng region (min max relative to current position) to seek # rng region (min max relative to current position) to seek
@@ -256,33 +256,11 @@ class geometry:
# roi region of interrest to calculate sharpness # roi region of interrest to calculate sharpness
# mode mode to calculate sharpness (sum/max-min/hist? of edge detection in roi) # mode mode to calculate sharpness (sum/max-min/hist? of edge detection in roi)
import PIL.Image import PIL.Image
from scipy import ndimage if mot is not None:
v=np.ndarray(shape=(len(cam),2))
if type(cam) == list:
for i, fn in enumerate(cam):
img=PIL.Image.open(fn)
img=np.asarray(img)
s=ndimage.sobel(img)
v[i,0]=s.sum()
v[i,1]=s.std()
#fft=np.log(np.abs(np.fft.fft2(img)))
#fft=np.fft.fftshift(fft)
#s=np.array(fft.shape,dtype=np.uint16)/2
#fft[300:700,400:800]=0
#v[i,1]=fft.sum()
#if i&0x3==0:
# plt.figure()
# plt.imshow(fft)
fig, ax=plt.subplots()
mx=v.max(0)
mn=v.min(0)
v=(v-mn)/(mx-mn)
#ax.plot(v[:,0])
ax.plot(v)
plt.show()
pass
else:
p0=mot.get_rbv() p0=mot.get_rbv()
else:
p0=0
if saveImg:
for i,p in enumerate(np.linspace(p0+rng[0],p0+rng[1],n)): for i,p in enumerate(np.linspace(p0+rng[0],p0+rng[1],n)):
mot.move_abs(p,wait=True) mot.move_abs(p,wait=True)
pic=cam._pic# get_image() pic=cam._pic# get_image()
@@ -292,11 +270,55 @@ class geometry:
pic=np.array(pic/scl, dtype=np.uint8) pic=np.array(pic/scl, dtype=np.uint8)
elif pic.dtype!=np.uint8: elif pic.dtype!=np.uint8:
pic=np.array(pic, dtype=np.uint8) pic=np.array(pic, dtype=np.uint8)
img=PIL.Image.fromarray(pic) img=PIL.Image.fromarray(pic)
fn=f'/tmp/image{i:03d}.png' fn=f'/tmp/image{i:03d}.png'
img.save(fn) img.save(fn)
_log.debug(f'{fn} {pic.dtype} {pic.min()} {pic.max()}') _log.debug(f'{fn} {pic.dtype} {pic.min()} {pic.max()}')
mot.move_abs(p0)
return p0
else:
from scipy import ndimage, signal
if type(cam) == list:
imgLst=cam
n=len(imgLst)
mtr=np.ndarray(shape=(n,))
posLst=np.linspace(p0+rng[0], p0+rng[1], n)
for i,p in enumerate(posLst):
if type(cam)==list:
img=PIL.Image.open(imgLst[i])
img=np.asarray(img)
else:
mot.move_abs(p, wait=True)
img=cam._pic # get_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()
_log.debug(f'{i}/{p:.4g} -> {mtr[i]:.4g}')
mx=mtr.argmax()
_log.debug(f'best focus at idx:{mx}= pos:{posLst[mx]} = metric:{mtr[mx]:.6g}')
if mx>0 and mx <len(posLst):
#fit parabola and interpolate:
# y=ax2+bx+c, at positions x=-1, 0, 1, y'= 2a+b == 0 (top of parabola)
# calc a,b,c:
# y(-1)=a-b+c
# y( 0)= +c
# y( 1)=a+b+c
# c=y(0)
# b=(y(1)-y(-1))/2
# a=(y(1)+y(-1))/2-y(0)
# x=-b/2a=(y(-1)-y(1))/2(y(-1)+y(1)-2y(0))
u,v,w=mtr[mx-1:mx+2]
d=posLst[1]-posLst[0]
p=posLst[mx]+d*.5*(u-w)/(u+w-2*v)
else:
p=posLst[mx]
if mot is not None:
mot.move_abs(p)
return p
pass pass
def pix2pos(self, p, zoom=None): def pix2pos(self, p, zoom=None):
@@ -603,7 +625,7 @@ if __name__=="__main__":
if args.mode&0x08: if args.mode&0x08:
import glob import glob
imgLst=sorted(glob.glob("scratch/image*.png")) imgLst=sorted(glob.glob("scratch/autofocus2/image*.png"))
geometry.autofocus(imgLst,None) geometry.autofocus(imgLst,None)

View File

@@ -6,7 +6,7 @@ if hostname=='ganymede':
else: else:
sys.path.insert(0, os.path.expanduser('/sf/cristallina/applications/mx/zamofing_t/PBTools/')) sys.path.insert(0, os.path.expanduser('/sf/cristallina/applications/mx/zamofing_t/PBTools/'))
sys.path.insert(0, os.path.expanduser("/photonics/home/gac-cristall/Documents/swissmx_cristallina/slic/")) sys.path.insert(0, os.path.expanduser("/photonics/home/gac-cristall/Documents/swissmx_cristallina/slic/"))
from slic.core.acquisition import SFAcquisition #from slic.core.acquisition import SFAcquisition
import logging import logging

View File

@@ -492,11 +492,14 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
action.triggered.connect(self._OLD_escape_goToTellMountPosition) action.triggered.connect(self._OLD_escape_goToTellMountPosition)
self.toolBar.addAction(action) self.toolBar.addAction(action)
action = QAction(icon, "Auto\nFocus", self)
action.triggered.connect(self.cb_autofocus)
self.toolBar.addAction(action)
action = QAction(icon, "Test\nCode", self) action = QAction(icon, "Test\nCode", self)
action.triggered.connect(self.cb_testcode) action.triggered.connect(self.cb_testcode)
self.toolBar.addAction(action) self.toolBar.addAction(action)
self.toolBar.addWidget(qutilities.horiz_spacer()) self.toolBar.addWidget(qutilities.horiz_spacer())
icon = qtawesome.icon("material.sync") icon = qtawesome.icon("material.sync")
@@ -1359,6 +1362,8 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
if pic.max()>255: if pic.max()>255:
scl=2**int(round(np.log2(mx)-8)) scl=2**int(round(np.log2(mx)-8))
pic=np.array(pic/scl,dtype=np.uint8) pic=np.array(pic/scl,dtype=np.uint8)
elif pic.dtype!=np.uint8:
pic=np.array(pic, dtype=np.uint8)
except AttributeError: except AttributeError:
sim=app._camera._sim sim=app._camera._sim
pic=cam._sim['imgSeq'][sim['imgIdx']] pic=cam._sim['imgSeq'][sim['imgIdx']]
@@ -1582,6 +1587,11 @@ Author Thierry Zamofing (thierry.zamofing@psi.ch)
QMessageBox.about(self, "SwissMX", txt) QMessageBox.about(self, "SwissMX", txt)
pass pass
def cb_autofocus(self):
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)
def cb_testcode(self): def cb_testcode(self):
try: try:
@@ -1598,18 +1608,13 @@ Author Thierry Zamofing (thierry.zamofing@psi.ch)
plt.stem(x, y) plt.stem(x, y)
plt.show(block=False) plt.show(block=False)
step=2 step=5
if step==0: if step==0:
vb=self.vb vb=self.vb
vb.autoRange(items=(self._goImg,)) vb.autoRange(items=(self._goImg,))
elif step==1: elif step==1:
testMatplotlib() testMatplotlib()
elif step==2: elif step==2:
app=QApplication.instance()
cfg=app._cfg
geo=app._geometry
geo.autofocus(app._camera, self.tweakers['base_z'])
elif step==3:
grp=pg.ItemGroup() grp=pg.ItemGroup()
vb.addItem(grp) vb.addItem(grp)
obj=UsrGO.Marker((100, 100), (100, 100), mode=1) obj=UsrGO.Marker((100, 100), (100, 100), mode=1)
@@ -1620,7 +1625,7 @@ Author Thierry Zamofing (thierry.zamofing@psi.ch)
grp.addItem(obj) grp.addItem(obj)
tc['grp']=grp tc['grp']=grp
vb.autoRange(items=(obj,)) vb.autoRange(items=(obj,))
elif step==4: elif step==3:
grp=tc['grp'] grp=tc['grp']
tr=grp.transform() tr=grp.transform()
# UsrGO.obj_info(tr) # UsrGO.obj_info(tr)
@@ -1628,7 +1633,16 @@ Author Thierry Zamofing (thierry.zamofing@psi.ch)
-.2, 1, 0, -.2, 1, 0,
0, 0, 1) 0, 0, 1)
grp.setTransform(tr) grp.setTransform(tr)
elif step==4:
app=QApplication.instance()
cfg=app._cfg
geo=app._geometry
geo.autofocus(app._camera, self.tweakers['base_z'],rng=(-1, 1), n=30,saveImg=True)
elif step==5:
app=QApplication.instance()
cfg=app._cfg
geo=app._geometry
geo.autofocus(app._camera, self.tweakers['base_z'],rng=(-1, 1), n=10)
#print(vb.childGroup.childItems()) #print(vb.childGroup.childItems())
pass pass

View File

@@ -4,91 +4,135 @@ Demonstrate a simple data-slicing task: given 3D data (displayed at top), select
a 2D plane and interpolate data along that plane to generate a slice image a 2D plane and interpolate data along that plane to generate a slice image
(displayed at bottom). (displayed at bottom).
https://www.hindawi.com/journals/js/2021/5643054/
https://www.hindawi.com/journals/mpe/2021/8243072/
""" """
## Add path to library (just for examples; you do not need this) import logging
import numpy as np
_log=logging.getLogger(__name__)
import numpy as np import numpy as np
import PIL.Image
from scipy import ndimage,signal
import glob
from pyqtgraph.Qt import QtCore, QtGui from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg import pyqtgraph as pg
app=QtGui.QApplication([])
## Create window with two ImageView widgets class autofocus(QtGui.QMainWindow):
win=QtGui.QMainWindow() def __init__(self, parent = None):
win.resize(800, 800) super(autofocus, self).__init__(parent)
win.setWindowTitle('pyqtgraph example: DataSlicing') self.resize(800, 1500)
self.setWindowTitle('pyqtgraph example: DataSlicing')
cw=QtGui.QWidget() cw=QtGui.QWidget()
win.setCentralWidget(cw) self.setCentralWidget(cw)
l=QtGui.QGridLayout() l=QtGui.QGridLayout()
cw.setLayout(l) cw.setLayout(l)
imv1=pg.ImageView()
imv2=pg.ImageView() #self._imgLst=imgLst=sorted(glob.glob("../scratch/autofocus1/image*.png"))
l.addWidget(imv1, 0, 0) self._imgLst=imgLst=sorted(glob.glob("../scratch/autofocus2/image*.png"))
l.addWidget(imv2, 1, 0) self._metrics=mtr=np.ndarray(shape=(len(imgLst), 5))
sld=QtGui.QSlider(QtCore.Qt.Horizontal) mtr[:]=0
sld.setMinimum(10) self._sld=sld=QtGui.QSlider(QtCore.Qt.Horizontal)
sld.setMaximum(30) sld.setMinimum(0)
sld.setValue(20) sld.setMaximum(len(imgLst)-1)
sld.setValue(0)
sld.setTickPosition(QtGui.QSlider.TicksBelow) sld.setTickPosition(QtGui.QSlider.TicksBelow)
sld.setTickInterval(5) sld.setTickInterval(1)
sld.valueChanged.connect(self.cb_sld_change)
l.addWidget(sld, 2, 0) self._imv1=imv1=pg.ImageView()
win.show() self._imv2=imv2=pg.ImageView()
roi=pg.LineSegmentROI([[10, 64], [120, 64]], pen='r') #spl=QtGui.QSplitter(QtCore.Qt.Horizontal)
imv1.addItem(roi) self._pw=pw=pg.PlotWidget(name='Plot1') ## giving the plots names allows us to link
x1=np.linspace(-30, 10, 128)[:, np.newaxis, np.newaxis] self._plt=plt=[]
x2=np.linspace(-20, 20, 128)[:, np.newaxis, np.newaxis] for c in ('rgbcy'):
y=np.linspace(-30, 10, 128)[np.newaxis, :, np.newaxis] plt.append(pw.plot(pen=c))
z=np.linspace(-20, 20, 128)[np.newaxis, np.newaxis, :]
d1=np.sqrt(x1**2+y**2+z**2)
d2=2*np.sqrt(x1[::-1]**2+y**2+z**2)
d3=4*np.sqrt(x2**2+y[:, ::-1]**2+z**2)
data=(np.sin(d1)/d1**2)+(np.sin(d2)/d2**2)+(np.sin(d3)/d3**2)
import PIL.Image
from scipy import ndimage
import glob
imgLst=sorted(glob.glob("image*.png"))
v=np.ndarray(shape=(len(imgLst), 2))
#for i, fn in enumerate(imgLst):
# img=PIL.Image.open(fn)
# img=np.asarray(img)
# s=ndimage.sobel(img)
# v[i, 0]=s.sum()
# v[i, 1]=s.std()
#fig, ax=plt.subplots()
#mx=v.max(0)
#mn=v.min(0)
#v=(v-mn)/(mx-mn)
# ax.plot(v[:,0])
#ax.plot(v)
#plt.show()
# pass
def update():
global data, imv1, imv2, imgLst
d2=roi.getArrayRegion(data, imv1.imageItem, axes=(1, 2))
imv2.setImage(d2)
pw.resize(100,100)
roi.sigRegionChanged.connect(update) pw.setMaximumSize(2000,200)
l.addWidget(sld, 0, 0)
l.addWidget(imv1, 1, 0)
l.addWidget(imv2, 2, 0)
l.addWidget(pw, 3, 0)
## Display the data ## Display the data
imv1.setImage(data) self.cb_sld_change(0,True)
imv1.setHistogramRange(-0.01, 0.01) mtr[1:,:]=mtr[0,:]
imv1.setLevels(-0.003, 0.003)
update() self._imv1.setHistogramRange(0, 100)
self._imv1.setLevels(0, 40)
self._imv2.setHistogramRange(0, 100)
self._imv2.setLevels(0, 40)
def cb_sld_change(self,val,auto=False):
i=self._sld.value()
_log.debug(f'{i}')
fn= self._imgLst[i]
img=PIL.Image.open(fn)
img=np.asarray(img)
#slb=ndimage.sobel(img)
img16=np.array(img,np.int16)
s1=np.array(((1,0,-1),(2,0,-2),(1,0,-1)),np.int16)
sb1=signal.convolve2d(img16, s1, mode='same', boundary='fill', fillvalue=0)
sb2=signal.convolve2d(img16, s1.T, mode='same', boundary='fill', fillvalue=0)
sb=np.abs(sb1)+np.abs(sb2)
#remove irrelevant low values
idx=sb[:]<20
sbLut=sb*1
sbLut[idx]=0
#import numpy as np
#import matplotlib.pyplot as plt
mx=sb.max()+1
lut=(np.sin(np.arange(mx)/mx*np.pi-np.pi/2)+1)*128
#lut=((np.arcsin(np.arange(mx)/(mx-1)*2-1)/np.pi)+.5)*255
#lut=np.array(lut*255,np.uint16)
#sbLut=lut[sb]
#import matplotlib.pyplot as plt
#plt.plot(lut);plt.show()
#img2=ndimage.grey_dilation(sb,size=(5,5)) #, size=None, footprint=None, structure=None, output=None, mode='reflect', cval=0.0, origin=0)
img2=ndimage.grey_closing(sb,size=(25,25)) #, size=None, footprint=None, structure=None, output=None, mode='reflect', cval=0.0, origin=0)
#fft=np.log(np.abs(np.fft.fft2(sb)))
#fft=np.fft.fftshift(fft)
# 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)
mtr=self._metrics
mtr[i, 0]=img.std()
mtr[i, 1]=sb.sum()
mtr[i, 2]=sb.std()
mtr[i, 3]=sbLut.sum()#sb.std()
mtr[i, 4]=sbLut.std()#img2.sum()
mx=mtr.max(0)
mn=mtr.min(0)
mtr=(mtr-mn)/(mx-mn)
_log.debug(f'{i} {mtr[i,:]}')
for i in range(mtr.shape[1]):
self._plt[i].setData(mtr[:,i])
## Start Qt event loop unless running in interactive mode. ## Start Qt event loop unless running in interactive mode.
if __name__=='__main__': if __name__=='__main__':
import sys import sys
logging.basicConfig(level=logging.DEBUG, format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ')
app=QtGui.QApplication([])
af=autofocus()
af.show()
if (sys.flags.interactive!=1) or not hasattr(QtCore, 'PYQT_VERSION'): if (sys.flags.interactive!=1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_() QtGui.QApplication.instance().exec_()

View File

@@ -0,0 +1,138 @@
# -*- coding: utf-8 -*-
"""
Demonstrate a simple data-slicing task: given 3D data (displayed at top), select
a 2D plane and interpolate data along that plane to generate a slice image
(displayed at bottom).
https://www.hindawi.com/journals/js/2021/5643054/
https://www.hindawi.com/journals/mpe/2021/8243072/
"""
import logging
import numpy as np
_log=logging.getLogger(__name__)
import numpy as np
import PIL.Image
from scipy import ndimage,signal
import glob
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
class fiducial(QtGui.QMainWindow):
def __init__(self, parent = None):
super(autofocus, self).__init__(parent)
self.resize(800, 1500)
self.setWindowTitle('pyqtgraph example: DataSlicing')
cw=QtGui.QWidget()
self.setCentralWidget(cw)
l=QtGui.QGridLayout()
cw.setLayout(l)
#self._imgLst=imgLst=sorted(glob.glob("../scratch/autofocus1/image*.png"))
self._imgLst=imgLst=sorted(glob.glob("../scratch/autofocus2/image*.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._imv1=imv1=pg.ImageView()
self._imv2=imv2=pg.ImageView()
#spl=QtGui.QSplitter(QtCore.Qt.Horizontal)
self._pw=pw=pg.PlotWidget(name='Plot1') ## giving the plots names allows us to link
self._plt=plt=[]
for c in ('rgbcy'):
plt.append(pw.plot(pen=c))
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)
## Display the data
self.cb_sld_change(0,True)
mtr[1:,:]=mtr[0,:]
self._imv1.setHistogramRange(0, 100)
self._imv1.setLevels(0, 40)
self._imv2.setHistogramRange(0, 100)
self._imv2.setLevels(0, 40)
def cb_sld_change(self,val,auto=False):
i=self._sld.value()
_log.debug(f'{i}')
fn= self._imgLst[i]
img=PIL.Image.open(fn)
img=np.asarray(img)
#slb=ndimage.sobel(img)
img16=np.array(img,np.int16)
s1=np.array(((1,0,-1),(2,0,-2),(1,0,-1)),np.int16)
sb1=signal.convolve2d(img16, s1, mode='same', boundary='fill', fillvalue=0)
sb2=signal.convolve2d(img16, s1.T, mode='same', boundary='fill', fillvalue=0)
sb=np.abs(sb1)+np.abs(sb2)
#remove irrelevant low values
idx=sb[:]<20
sbLut=sb*1
sbLut[idx]=0
#import numpy as np
#import matplotlib.pyplot as plt
mx=sb.max()+1
lut=(np.sin(np.arange(mx)/mx*np.pi-np.pi/2)+1)*128
#lut=((np.arcsin(np.arange(mx)/(mx-1)*2-1)/np.pi)+.5)*255
#lut=np.array(lut*255,np.uint16)
#sbLut=lut[sb]
#import matplotlib.pyplot as plt
#plt.plot(lut);plt.show()
#img2=ndimage.grey_dilation(sb,size=(5,5)) #, size=None, footprint=None, structure=None, output=None, mode='reflect', cval=0.0, origin=0)
img2=ndimage.grey_closing(sb,size=(25,25)) #, size=None, footprint=None, structure=None, output=None, mode='reflect', cval=0.0, origin=0)
#fft=np.log(np.abs(np.fft.fft2(sb)))
#fft=np.fft.fftshift(fft)
# 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)
mtr=self._metrics
mtr[i, 0]=img.std()
mtr[i, 1]=sb.sum()
mtr[i, 2]=sb.std()
mtr[i, 3]=sbLut.sum()#sb.std()
mtr[i, 4]=sbLut.std()#img2.sum()
mx=mtr.max(0)
mn=mtr.min(0)
mtr=(mtr-mn)/(mx-mn)
_log.debug(f'{i} {mtr[i,:]}')
for i in range(mtr.shape[1]):
self._plt[i].setData(mtr[:,i])
## Start Qt event loop unless running in interactive mode.
if __name__=='__main__':
import sys
logging.basicConfig(level=logging.DEBUG, format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ')
app=QtGui.QApplication([])
fd=fiducial()
fd.show()
if (sys.flags.interactive!=1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()