diff options
Diffstat (limited to 'src/python/camcalibrator.py')
-rw-r--r-- | src/python/camcalibrator.py | 469 |
1 files changed, 469 insertions, 0 deletions
diff --git a/src/python/camcalibrator.py b/src/python/camcalibrator.py new file mode 100644 index 0000000..08bd308 --- /dev/null +++ b/src/python/camcalibrator.py @@ -0,0 +1,469 @@ +# libavg - Media Playback Engine. +# Copyright (C) 2003-2014 Ulrich von Zadow +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Current versions can be found at www.libavg.de +# +# Original author of this file is igor <igor (at) c-base (dot) org> +# + +import sys, os +from libavg import avg, AVGApp, player + +import coordcalibrator +import apphelpers + +mediadir = os.path.join(os.path.dirname(__file__), 'data') +g_KbManager = apphelpers.KeyboardManager.get() + + +def camera_setup(CameraType): + if CameraType == "Fire-i": + paramList = [ + {'Name':"Brightness", + 'path':"/camera/brightness/@value", + 'min':128, 'max':383, 'increment':1, 'precision':0}, + {'Name':"Exposure", + 'path':"/camera/exposure/@value", + 'min':-1, 'max':511, 'increment':1, 'precision':0}, + {'Name':"Shutter", + 'path':"/camera/shutter/@value", + 'min':0, 'max':7, 'increment':1, 'precision':0}, + {'Name':"Gain", + 'path':"/camera/gain/@value", + 'min':0, 'max':255, 'increment':1, 'precision':0}, + ] + elif CameraType == "FireFly": + paramList = [ + {'Name':"Brightness", + 'path':"/camera/brightness/@value", + 'min':1, 'max':255, 'increment':1, 'precision':0}, + {'Name':"Shutter", + 'path':"/camera/shutter/@value", + 'min':1, 'max':533, 'increment':1, 'precision':0}, + {'Name':"Gain", + 'path':"/camera/gain/@value", + 'min':16, 'max':64, 'increment':1, 'precision':0}, + {'Name':"Gamma", + 'path':"/camera/gamma/@value", + 'min':0, 'max':1, 'increment':1, 'precision':0}, + ] + elif CameraType == "DragonFly": + paramList = [ + {'Name':"Brightness", + 'path':"/camera/brightness/@value", + 'min':1, 'max':255, 'increment':1, 'precision':0}, + {'Name':"Gamma", + 'path':"/camera/gamma/@value", + 'min':512, 'max':4095, 'increment':5, 'precision':0}, + {'Name':"Shutter", + 'path':"/camera/shutter/@value", + 'min':0, 'max':709, 'increment':2, 'precision':0}, + {'Name':"Gain", + 'path':"/camera/gain/@value", + 'min':16, 'max':683, 'increment':2, 'precision':0}, + ] + else: + avg.logger.error("Unknown CameraType %s" % CameraType) + sys.exit() + + paramList.extend([ + # Touch + {'Name':"Threshold", + 'path':"/tracker/touch/threshold/@value", + 'min':1, 'max':255, 'increment':1, 'precision':0}, + {'Name':"Similarity", + 'path':"/tracker/touch/similarity/@value", + 'min':1, 'max':300, 'increment':1, 'precision':1}, + + {'Name':"Min Area", + 'path':"/tracker/touch/areabounds/@min", + 'min':1, 'max':1000000, 'increment':3, 'precision':0}, + {'Name':"Max Area", + 'path':"/tracker/touch/areabounds/@max", + 'min':20, 'max':1000000, 'increment':10, 'precision':0}, + + {'Name':"Ecc. Min", + 'path':"/tracker/touch/eccentricitybounds/@min", + 'min':1, 'max':30, 'increment':1, 'precision':1}, + {'Name':"Ecc. Max", + 'path':"/tracker/touch/eccentricitybounds/@max", + 'min':1, 'max':2000, 'increment':1, 'precision':1}, + + {'Name':"Bandpass Min", + 'path':"/tracker/touch/bandpass/@min", + 'min':0, 'max':15, 'increment':.1, 'precision':1}, + {'Name':"Bandpass Max", + 'path':"/tracker/touch/bandpass/@max", + 'min':0, 'max':15, 'increment':.1, 'precision':1}, + {'Name':"Bandpass Postmult", + 'path':"/tracker/touch/bandpasspostmult/@value", + 'min':0, 'max':30, 'increment':.1, 'precision':1}, + + # Track + {'Name':"Threshold", + 'path':"/tracker/track/threshold/@value", + 'min':1, 'max':255, 'increment':1, 'precision':0}, + + {'Name':"Min Area", + 'path':"/tracker/track/areabounds/@min", + 'min':1, 'max':1000000, 'increment':3, 'precision':0}, + {'Name':"Max Area", + 'path':"/tracker/track/areabounds/@max", + 'min':20, 'max':1000000, 'increment':10, 'precision':0}, + + {'Name':"Ecc. Min", + 'path':"/tracker/track/eccentricitybounds/@min", + 'min':1, 'max':30, 'increment':1, 'precision':1}, + {'Name':"Ecc. Max", + 'path':"/tracker/track/eccentricitybounds/@max", + 'min':1, 'max':2000, 'increment':1, 'precision':1}, + + # Transform + {'Name':"p2", + 'path':"/transform/distortionparams/@p2", + 'min':-3, 'max':3, 'increment':0.001, 'precision':3}, + {'Name':"Trapezoid", + 'path':"/transform/trapezoid/@value", + 'min':-3, 'max':3, 'increment':0.0001, 'precision':4}, + {'Name':"Angle", + 'path':"/transform/angle/@value", + 'min':-3.15, 'max':3.15, 'increment':0.01, 'precision':2}, + {'Name':"Displ. x", + 'path':"/transform/displaydisplacement/@x", + 'min':-5000, 'max':0, 'increment':1, 'precision':0}, + {'Name':"Displ. y", + 'path':"/transform/displaydisplacement/@y", + 'min':-5000, 'max':0, 'increment':1, 'precision':0}, + {'Name':"Scale x", + 'path':"/transform/displayscale/@x", + 'min':-3, 'max':8, 'increment':0.01, 'precision':2}, + {'Name':"Scale y", + 'path':"/transform/displayscale/@y", + 'min':-3, 'max':8, 'increment':0.01, 'precision':2}, + ]) + return paramList + +class Calibrator(AVGApp): + def __init__(self, parentNode, CameraType = "FireFly", appStarter = None): + super(Calibrator, self).__init__(parentNode) + self.paramList = camera_setup(CameraType) + self.parentNode=parentNode + self.appStarter = appStarter + self.mainNode = player.createNode( + """ + <div active="False" opacity="0"> + <image width="1280" height="800" href="black.png"/> + <image id="cal_distorted" x="0" y="0" width="1280" height="800" + sensitive="false" opacity="1"/> + <words id="cal_fps" x="30" y="30" color="00FF00" text=""/> + <words id="cal_notification" x="390" y="390" width="500" fontsize="18" + color="ff3333" alignment="center" /> + <div id="cal_gui" x="30" y="540"> + <image id="cal_shadow" x="0" y="13" width="500" height="150" + href="black.png" opacity="0.6"/> + + <words x="2" y="13" text="camera" fontsize="16" color="00FF00"/> + <image x="2" y="32" href="CamImgBorder.png"/> + <image id="cal_camera" x="4" y="34" width="160" height="120"/> + + <words x="168" y="13" text="nohistory" fontsize="16" color="00FF00"/> + <image x="168" y="32" href="CamImgBorder.png"/> + <image id="cal_nohistory" x="170" y="34" width="160" height="120"/> + + <words x="334" y="13" text="histogram" fontsize="16" color="00FF00"/> + <image x="334" y="32" href="CamImgBorder.png"/> + <image id="cal_histogram" x="336" y="34" width="160" height="120"/> + + <div id="cal_params" y="170" opacity="0.9"> + <image id="cal_shadow2" width="750" height="65" href="black.png" opacity="0.6"/> + <div id="cal_paramdiv0" x="2"> + <words text="camera" y="0" fontsize="10" color="00ff00" /> + <words id="cal_param0" y="12" fontsize="10"/> + <words id="cal_param1" y="24" fontsize="10"/> + <words id="cal_param2" y="36" fontsize="10"/> + <words id="cal_param3" y="48" fontsize="10"/> + </div> + <div id="cal_paramdiv1" x="80"> + <words text="touch" y="0" fontsize="10" color="00ff00" /> + <words id="cal_param4" y="12" fontsize="10"/> + <words id="cal_param5" y="24" fontsize="10"/> + <words id="cal_param6" y="36" fontsize="10"/> + <words id="cal_param7" y="48" fontsize="10"/> + </div> + <div id="cal_paramdiv2" x="200"> + <words id="cal_param8" y="0" fontsize="10"/> + <words id="cal_param9" y="12" fontsize="10"/> + <words id="cal_param10" y="24" fontsize="10"/> + <words id="cal_param11" y="36" fontsize="10"/> + <words id="cal_param12" y="48" fontsize="10"/> + </div> + <div id="cal_paramdiv3" x="350"> + <words text="track" y="0" fontsize="10" color="00ff00" /> + <words id="cal_param13" y="12" fontsize="10"/> + <words id="cal_param14" y="24" fontsize="10"/> + <words id="cal_param15" y="36" fontsize="10"/> + <words id="cal_param16" y="48" fontsize="10"/> + </div> + <div id="cal_paramdiv4" x="500"> + <words id="cal_param17" y="0" fontsize="10"/> + <words text="distort" y="12" fontsize="10" color="00ff00"/> + <words id="cal_param18" y="24" fontsize="10"/> + <words id="cal_param19" y="36" fontsize="10"/> + <words id="cal_param20" y="48" fontsize="10"/> + </div> + <div id="cal_paramdiv5" x="650"> + <words id="cal_param21" y="0" fontsize="10"/> + <words id="cal_param22" y="12" fontsize="10"/> + <words id="cal_param23" y="24" fontsize="10"/> + <words id="cal_param24" y="36" fontsize="10"/> + <words id="cal_param25" y="48" fontsize="10"/> + </div> + </div> + </div> + <div id="cal_coordcalibrator" opacity="0" active="false"> + <image x="0" y="0" width="1280" height="800" href="border.png"/> + <div id="cal_messages" x="100" y="100"/> + <image id="cal_crosshair" href="crosshair.png"/> + <image id="cal_feedback" href="Feedback.png"/> + </div> + </div> + """) + self.mainNode.mediadir=mediadir + parentNode.insertChild(self.mainNode, 0) + + self.coordCal = None + self.tracker = player.getTracker() + self.curParam = 0 + self.saveIndex = 0 + self.hideMainNodeTimeout = None + self.video = [] + self.__guiOpacity = 1 + self.__showBigCamImage = False + self.__notificationTimer = None + self.__onCalibrationSuccess = None + + + + def _enter(self): + + g_KbManager.push() + + g_KbManager.bindKey('d', self.__trackerSetDebugImages, 'tracker set debug images') + g_KbManager.bindKey('b', self.__bigCamImage, 'big cam image') + g_KbManager.bindKey('up', self.__keyFuncUP, 'select parameter up') + g_KbManager.bindKey('down', self.__keyFuncDOWN, 'select parameter down') + g_KbManager.bindKey('left', self.__keyFuncLEFT, 'value up') + g_KbManager.bindKey('right', self.__keyFuncRIGHT, 'value down') + g_KbManager.bindKey('page up', self.__keyFuncPAGEUp, 'value up * 10') + g_KbManager.bindKey('page down', self.__keyFuncPAGEDown, 'value down * 10') + g_KbManager.bindKey('s', self.__trackerSaveConfig, 'save configuration') + g_KbManager.bindKey('g', self.__toggleGUI, 'toggle GUI') + g_KbManager.bindKey('c', self.__startCoordCalibration, + 'start geometry calibration') + g_KbManager.bindKey('w', self.__saveTrackerIMG, 'SAVE trager image') + g_KbManager.bindKey('h', self.appStarter.tracker.resetHistory, 'RESET history') + + self.appStarter.showTrackerImage() + self.mainNode.active=True + self.tracker.setDebugImages(True, True) + avg.fadeIn(self.mainNode, 400, 1) + Bitmap = self.tracker.getImage(avg.IMG_DISTORTED) # Why is this needed? + self.__onFrameID = player.subscribe(player.ON_FRAME, self.__onFrame) + #grandparent = self.parentNode.getParent() + #if grandparent: + # grandparent.reorderChild(grandparent.indexOf(self.parentNode), grandparent.getNumChildren()-1) + self.displayParams() + if self.hideMainNodeTimeout: + player.clearInterval(self.hideMainNodeTimeout) + + def _leave(self): + #unbind all calibrator keys - bind old keys + g_KbManager.pop() + + def hideMainNode(): + self.mainNode.opacity=0 + self.mainNode.active = False + self.appStarter.hideTrackerImage() + #grandparent = self.parentNode.getParent() + #if grandparent: + # grandparent.reorderChild(grandparent.indexOf(self.parentNode), 0) + self.hideMainNodeTimeout = player.setTimeout(400, hideMainNode) + player.clearInterval(self.__onFrameID) + + def reparent(self, newParent): + """reparents the calibrator node; returns the old(!) parent node""" + oldParent = self.mainNode.getParent() + self.mainNode.unlink() + newParent.appendChild(self.mainNode) + return oldParent + + def __deferredRefreshCB(self): + self.displayParams() + self.tracker.resetHistory() + self.setNotification('') + g_KbManager.pop() + player.getElementByID('cal_params').opacity = 0.9 + + def __clearNotification(self): + self.__notificationTimer = None + self.setNotification('') + + def __toggleGUI(self): + self.__guiOpacity = 1 - self.__guiOpacity + player.getElementByID('cal_gui').opacity = self.__guiOpacity + + def __onFrame(self): + def showTrackerImage(trackerImageID, nodeID, size, pos=(0,0)): + bitmap = self.tracker.getImage(trackerImageID) + node = player.getElementByID(nodeID) + node.setBitmap(bitmap) + node.size = size + if pos != (0,0): + node.pos = pos + + # flip: + grid = node.getOrigVertexCoords() + grid = [ [ (1-pos[0], pos[1]) for pos in line ] for line in grid] + node.setWarpedVertexCoords(grid) + + if self.__showBigCamImage: + showTrackerImage(avg.IMG_CAMERA, "cal_distorted", (1280, 960)) + else: + pos = self.tracker.getDisplayROIPos() + size = self.tracker.getDisplayROISize() + showTrackerImage(avg.IMG_DISTORTED, "cal_distorted", pos = pos, size = size) + showTrackerImage(avg.IMG_CAMERA, "cal_camera", (160, 120)) + showTrackerImage(avg.IMG_NOHISTORY, "cal_nohistory", (160, 120)) + showTrackerImage(avg.IMG_HISTOGRAM, "cal_histogram", (160, 120)) + fps = player.getEffectiveFramerate() + player.getElementByID("cal_fps").text = '%(val).2f' % {'val': fps} + + def __trackerSetDebugImages(self): + self.appStarter.toggleTrackerImage() + # toggleTrackerImage() will influence setDebugImages status, so we have to reset it: + self.tracker.setDebugImages(True, True) + + def __bigCamImage(self): + self.__showBigCamImage = not(self.__showBigCamImage) + + def __keyFuncUP(self): + if self.curParam > 0: + self.curParam -= 1 + self.displayParams() + + def __keyFuncDOWN(self): + if self.curParam < len(self.paramList)-1: + self.curParam += 1 + self.displayParams() + + def __keyFuncLEFT(self): + self.changeParam(-1) + self.displayParams() + + def __keyFuncRIGHT(self): + self.changeParam(1) + self.displayParams() + + def __keyFuncPAGEUp(self): + self.changeParam(10) + self.displayParams() + + def __keyFuncPAGEDown(self): + self.changeParam(-10) + self.displayParams() + + def __trackerSaveConfig(self): + self.tracker.saveConfig() + self.setNotification('Tracker configuration saved', 2000) + avg.logger.info("Tracker configuration saved.") + + def __saveTrackerIMG(self): + def saveTrackerImage(id, name): + self.tracker.getImage(id).save("img"+str(self.saveIndex)+"_"+name+".png") + + self.saveIndex += 1 + saveTrackerImage(avg.IMG_CAMERA, "camera") + saveTrackerImage(avg.IMG_DISTORTED, "distorted") + saveTrackerImage(avg.IMG_NOHISTORY, "nohistory") + saveTrackerImage(avg.IMG_HIGHPASS, "highpass") + saveTrackerImage(avg.IMG_FINGERS, "fingers") + saveTrackerImage(avg.IMG_HISTOGRAM, "histogram") + self.setNotification('Tracker images dumped', 2000) + avg.logger.info("Tracker images saved.") + + def __startCoordCalibration(self): + assert(not self.coordCal) + + self.__savedShutter = self.tracker.getParam("/camera/shutter/@value") + self.tracker.setParam("/camera/shutter/@value", "8") + self.__savedGain = self.tracker.getParam("/camera/gain/@value") + self.tracker.setParam("/camera/gain/@value", "16") + self.__savedStrobe = self.tracker.getParam("/camera/strobeduration/@value") + self.tracker.setParam("/camera/strobeduration/@value", "-1") + self.coordCal = coordcalibrator.CoordCalibrator(self.__onCalibrationTerminated) + + def __onCalibrationTerminated(self, isSuccessful): + self.coordCal = None + self.tracker.setParam("/camera/shutter/@value", self.__savedShutter) + self.tracker.setParam("/camera/gain/@value", self.__savedGain) + self.tracker.setParam("/camera/strobeduration/@value", self.__savedStrobe) + self.deferredRefresh() + + def setOnCalibrationSuccess(self, callback): + self.__onCalibrationSuccess = callback + + def deferredRefresh(self): + player.setTimeout(1500, self.__deferredRefreshCB) + self.setNotification('Please wait for settlement') + g_KbManager.push() + player.getElementByID('cal_params').opacity = 0.3 + + def setNotification(self, text, timeout=0): + player.getElementByID('cal_notification').text = text + if timeout: + if self.__notificationTimer is not None: + player.clearInterval(self.__notificationTimer) + + self.__notificationTimer = player.setTimeout(timeout, + self.__clearNotification) + + def displayParams(self): + i = 0 + for Param in self.paramList: + Node = player.getElementByID("cal_param"+str(i)) + Path = Param['path'] + Val = float(self.tracker.getParam(Path)) + Node.text = (Param['Name']+": " + +('%(val).'+str(Param['precision'])+'f') % {'val': Val}) + if self.curParam == i: + Node.color = "FFFFFF" + else: + Node.color = "A0A0FF" + i += 1 + + def changeParam(self, Change): + param = self.paramList[self.curParam] + if param['increment'] >= 1: + Val = int(float(self.tracker.getParam(param['path']))) + else: + Val = float(self.tracker.getParam(param['path'])) + Val += Change*param['increment'] + if Val < param['min']: + Val = param['min'] + if Val > param['max']: + Val = param['max'] + self.tracker.setParam(param['path'], str(Val)) |