diff --git a/python/PBMotionAnalyzer/MAxyPlot.py b/python/PBMotionAnalyzer/MAxyPlot.py index 4f24402..807a681 100644 --- a/python/PBMotionAnalyzer/MAxyPlot.py +++ b/python/PBMotionAnalyzer/MAxyPlot.py @@ -30,6 +30,26 @@ import pylab as plt #used for the colormaps #The source of the DraggableColorbar is from: #http://www.ster.kuleuven.be/~pieterd/python/html/plotting/interactive_colorbar.html +class MouseData: #data structure for mouse events on image + usageStr='unknown mode. available m:move t: transform' + def __init__(self): + self.mode=None + + def addTrfPt(self,p1): + try: + p0=self.btnPress + del self.btnPress + except AttributeError: return + try: + trf=self.trfPts + except AttributeError: + trf=self.trfPts=[] + + p=p0+p1 + print('add trf: {}'.format(p)) + trf.append(p) + + class MPLCanvasImg(FigureCanvas): def __init__(self,parent,SetStatusCB=None): if SetStatusCB: @@ -44,12 +64,12 @@ class MPLCanvasImg(FigureCanvas): self.mpl_connect('scroll_event', self.OnBtnScroll) self.mpl_connect('key_press_event',self.OnKeyPress) #self.mpl_connect('pick_event',self.OnPick) #works but locks the screed if debugging - self.fig=fig self.ax=ax + self.mouseData=MouseData() def InitChild(self,doc): - fig=self.fig + fig=self.figure ax=self.ax pts=doc.fh['pts'] rec=doc.fh['rec'] @@ -80,6 +100,33 @@ class MPLCanvasImg(FigureCanvas): hl+=ax.plot(rec[idxTrigger,1],rec[idxTrigger,0],'rx',label='trig',markeredgewidth=1.,markersize=6.) #hl+=ax.plot(recPts[:,1],recPts[:,0],'g.',label='recDot') + fn=doc.fh.fid.name[:-4]+'.jpg' + try: + img=mpl.image.imread(fn) + try: + self.imgTrf=trf=doc.fh['imgTrf'] + except KeyError: + mn=pts.min(0) + mx=pts.max(0) + ext=(mn[0], mx[0], mn[1], mx[1]) + skew=None + else: + xmin=trf[0, 2] + ymin=trf[1, 2] + xmax=trf[0, :].sum() + ymax=trf[1, :].sum() + xskew=trf[0, 2]+trf[0, 0] + yskew=trf[1, 2]+trf[1, 0] + ext=(xmin, xmax, ymin, ymax) + skew=(xskew, yskew) + + #interpolation must be none to allow skew image + himg=ax.imshow(img,extent=ext,interpolation='none') + himg._image_skew_coordinate=skew + + pass + except IOError as e: + print(e) ax.xaxis.set_label_text('x-pos um') ax.yaxis.set_label_text('y-pos um') ax.axis('equal') @@ -99,8 +146,26 @@ class MPLCanvasImg(FigureCanvas): def OnMotion(self,event): #print event,event.x,event.y,event.inaxes,event.xdata,event.ydata - if event.inaxes==self.ax: + md=self.mouseData + if md.mode==2 and event.button==1 and self.toolbar.mode=='': # move + try: + himg=self.ax.get_images()[0] + except IndexError: + print('no image to transform') + return + trf=self.imgTrf + xOfs=event.xdata-md.btnPress[0] + yOfs=event.ydata-md.btnPress[1] + xmin=trf[0, 2]+xOfs + ymin=trf[1, 2]+yOfs + xmax=trf[0, :].sum()+xOfs + ymax=trf[1, :].sum()+yOfs + xskew=trf[0, 2]+trf[0, 0]+xOfs + yskew=trf[1, 2]+trf[1, 0]+yOfs + himg.set_extent((xmin, xmax, ymin, ymax)) + himg._image_skew_coordinate=(xskew, yskew) + self.figure.canvas.draw() x=int(round(event.xdata)) y=int(round(event.ydata)) @@ -108,8 +173,8 @@ class MPLCanvasImg(FigureCanvas): for h in self.ax.get_lines(): # to get also the ellipses: self.ax.get_children() c=h.contains(event) if c[0]: - #s = str(h)+str(c[1]) - #print "over %s" % s + s = str(h)+str(c[1]) + #print("over %s" % s) if type(h)==mpl.lines.Line2D: lbl=h.get_label() arr=h.get_data() @@ -117,9 +182,7 @@ class MPLCanvasImg(FigureCanvas): s += '%s[%d] = [%.2f, %.2f]'%(lbl,idx,arr[0][idx],arr[1][idx]) else: s += str(h)+str(c[1]) - self.SetStatusCB(self.Parent, 0, (x, y, s)) - #try: # #v=self.img.get_array()[y,x] # v=0; @@ -142,14 +205,32 @@ class MPLCanvasImg(FigureCanvas): def OnBtnPress(self, event): """on button press we will see if the mouse is over us and store some data""" - #print(dir(event.guiEvent)) - return - if event.inaxes == self.colBar.ax: + if self.toolbar.mode!='': + print(self.toolbar.mode) + return + if event.inaxes==self.ax: + md=self.mouseData + md.mode + if md.mode==1:#transform + if event.guiEvent.RightDown()==True: + self.doTransform() + if event.guiEvent.LeftDown()==True: + p=(event.xdata,event.ydata) + md.btnPress=p + elif md.mode==2:#move + if event.guiEvent.LeftDown()==True: + p=(event.xdata,event.ydata) + md.btnPress=p + else: + print(md.usageStr) + + #print('self.btnPress1', p) + #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) + #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) @@ -157,10 +238,19 @@ class MPLCanvasImg(FigureCanvas): def OnBtnRelease(self, event): """on release we reset the press data""" - #self.OnMouse(event) + print('OnBtnRelease') + md=self.mouseData + if md.mode==1: #transform + p1=(event.xdata, event.ydata) + md.addTrfPt(p1) + elif md.mode==2:#move + trf=self.imgTrf + xOfs=event.xdata-md.btnPress[0] + yOfs=event.ydata-md.btnPress[1] + #trf[0,2]+=xOfs + #trf[1,2]+=yOfs + #trf[1,2]+=yOfs return - try: del self.colBarPressed - except AttributeError: pass def OnBtnScroll(self, event): return @@ -183,20 +273,27 @@ class MPLCanvasImg(FigureCanvas): colBar.patch.figure.canvas.draw() def OnKeyPress(self, event): + #self.trfPts=[(-1831.2063808574276, 1349.6011964107677, -1966.6001994017945, 1433.3499501495512), (-1830.4810087443084, 917.64916303133487, -1958.3629895701151, 870.93212944552363), (-1561.5041487047886, 917.17727380319548, -1445.8912878106089, 873.29157558622114)] + #self.trfPts=[(-1960., 872., -1960., 872.), (-1450., 872, -1450., 872), (-1960., 1432.,-1960., 1432.)] + #self.trfPts=[(-1960., 872., -1760., 872.), (-1450., 872, -1250., 872), (-1960., 1432.,-1760., 1432.)] + #self.doTransform() + md=self.mouseData + if event.key=='t': + md.mode=1;print('transform image mode (drag drop to add point, right mopuse to execute)') + elif event.key=='x': + self.doTransform() + elif event.key=='m': + md.mode=2;print('move image mode') + elif event.key=='s': + self.saveTransform() + else: + try: + print('unknown mode. available m:move t: transform') + md.mode=None + except AttributeError: + pass return - 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): return @@ -204,6 +301,84 @@ class MPLCanvasImg(FigureCanvas): if k[0]!='_': print(k,getattr(event,k)) + def doTransform(self): + #print('doTransform') + try: + trfPts=self.mouseData.trfPts + del self.mouseData.trfPts + except AttributeError: + print('no transformation points') + return + print(trfPts) + trfPts=np.array(trfPts) + + try: + himg=self.ax.get_images()[0] + except IndexError: + print('no image to transform') + return + xmin,xmax,ymin,ymax=himg.get_extent() + #himg.set_extent((xmin,xmax+10,ymin,ymax)) + skew=himg._image_skew_coordinate + if skew is None: + xskew=xmax + yskew=ymin + else: + (xskew, yskew)=skew + + trf0=np.array([[xskew-xmin,xmax-xskew,xmin], + [yskew-ymin,ymax-yskew,ymin], + [0,0,1]]) + + + #calculate least square transformation matrix of the transformation points + n=trfPts.shape[0] + if n<3: + print('tot enough points (needs at least 3)') + return + + M=np.mat(trf0).I + trfPtsImgI=M*np.vstack((trfPts[:,(0,1)].T,np.ones((1,n)))) + trfPtsImgO=M*np.vstack((trfPts[:,(02,3)].T,np.ones((1,n)))) + trfPtsImg=np.hstack((trfPtsImgI[0:2,:].T,trfPtsImgO[0:2,:].T)) + + A=np.zeros((n*2, 6)) + A[0:n, (0, 1)]=trfPtsImg[:,(0,1)] + A[0:n, 2]=1 + A[n:n*2, (3, 4)]=trfPtsImg[:,(0,1)] + A[n:n*2, 5]=1 + A=np.mat(A) + B=trfPtsImg[:,(2,3)].T.reshape(-1, 1) + + # (A'*A)^-1*A'*B + r=(A.T*A).I*A.T*B + trf1=np.hstack((r.T, [[0, 0, 1]])).reshape(3, -1) + + trf=np.mat(trf0)*trf1 + xmin=trf[0, 2] + ymin=trf[1, 2] + xmax=trf[0, :].sum() + ymax=trf[1, :].sum() + xskew=trf[0, 2]+trf[0, 0] + yskew=trf[1, 2]+trf[1, 0] + self.imgTrf=trf + print(trf) + himg.set_extent((xmin,xmax,ymin,ymax)) + himg._image_skew_coordinate=(xskew, yskew) + self.figure.canvas.draw() + + def saveTransform(self): + fhr=self.Parent.doc.fh + fn_npz=fhr.fid.name + fn_npzOrig=fn_npz+'.orig' + d=dict(fhr.items()) + fhr.close() + if not os.path.exists(fn_npzOrig): + os.rename(fn_npz, fn_npzOrig) + d['imgTrf']=self.imgTrf + np.savez_compressed(fn_npz,**d) + self.Parent.doc.fh=np.load(fn_npz) + class MAxyPlotFrame(wx.Frame): def __del__(self): self.doc.view.remove(self) diff --git a/python/PBMotionAnalyzer/PBMotionAnalyzer.py b/python/PBMotionAnalyzer/PBMotionAnalyzer.py index 102c04c..13dee84 100755 --- a/python/PBMotionAnalyzer/PBMotionAnalyzer.py +++ b/python/PBMotionAnalyzer/PBMotionAnalyzer.py @@ -374,10 +374,10 @@ if __name__ == '__main__': f1 = MAxyPlotFrame(frame, doc) f1.Show(True) - f2= MAErrorFrame(frame, doc) - f2.Show(True) - f3= MAVelocityFrame(frame, doc) - f3.Show(True) + #f2= MAErrorFrame(frame, doc) + #f2.Show(True) + #f3= MAVelocityFrame(frame, doc) + #f3.Show(True) self.SetTopWindow(frame) return True