/** * Copyright (C) 2013 Colorado School of Mines * * This file is part of the Interface Software Development Kit (SDK). * * The InterfaceSDK is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The InterfaceSDK 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with the InterfaceSDK. If not, see <http://www.gnu.org/licenses/>. */ package edu.mines.acmX.exhibit.input_services.hardware.drivers.openni; import java.awt.Dimension; import java.nio.ByteBuffer; import java.nio.ShortBuffer; import edu.mines.acmX.exhibit.input_services.hardware.devicedata.GestureTrackerInterface; import edu.mines.acmX.exhibit.stdlib.input_processing.receivers.Gesture; import org.OpenNI.ActiveHandEventArgs; import org.OpenNI.Context; import org.OpenNI.DepthGenerator; import org.OpenNI.DepthMap; import org.OpenNI.DepthMetaData; import org.OpenNI.GeneralException; import org.OpenNI.GestureGenerator; import org.OpenNI.GestureRecognizedEventArgs; import org.OpenNI.HandsGenerator; import org.OpenNI.IObservable; import org.OpenNI.IObserver; import org.OpenNI.ImageGenerator; import org.OpenNI.ImageMetaData; import org.OpenNI.InactiveHandEventArgs; import org.OpenNI.StatusException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import edu.mines.acmX.exhibit.input_services.events.EventManager; import edu.mines.acmX.exhibit.input_services.events.EventType; import edu.mines.acmX.exhibit.input_services.events.InputReceiver; import edu.mines.acmX.exhibit.input_services.hardware.HardwareManager; import edu.mines.acmX.exhibit.input_services.hardware.HardwareManagerManifestException; import edu.mines.acmX.exhibit.input_services.hardware.devicedata.DepthImageInterface; import edu.mines.acmX.exhibit.input_services.hardware.devicedata.HandTrackerInterface; import edu.mines.acmX.exhibit.input_services.hardware.devicedata.RGBImageInterface; import edu.mines.acmX.exhibit.input_services.hardware.drivers.DriverInterface; import edu.mines.acmX.exhibit.input_services.hardware.drivers.InvalidConfigurationFileException; import edu.mines.acmX.exhibit.stdlib.graphics.HandPosition; import edu.mines.acmX.exhibit.stdlib.input_processing.tracking.HandTrackingUtilities; import edu.mines.acmX.exhibit.input_services.hardware.drivers.DriverException; import edu.mines.acmX.exhibit.input_services.hardware.drivers.DriverHelper; /** * Kinect driver that provides depth and rgb image functionality. Uses the * openni the library for communication to the kinect device. * * @author Aakash Shah * @author Ryan Stauffer * */ public class KinectOpenNIDriver implements DriverInterface, DepthImageInterface, RGBImageInterface, HandTrackerInterface, GestureTrackerInterface { private static Logger log = LogManager.getLogger(KinectOpenNIDriver.class); public static final EventManager evtMgr = EventManager.getInstance(); private Context context; private boolean loaded; private DepthGenerator depthGen; private ImageGenerator imageGen; private GestureGenerator gestureGen; private HandsGenerator handsGen; private int imageWidth; private int imageHeight; private int depthWidth; private int depthHeight; public KinectOpenNIDriver() { context = null; loaded = false; depthGen = null; imageGen = null; gestureGen = null; handsGen = null; imageWidth = 0; imageHeight = 0; depthWidth = 0; depthHeight = 0; } @Override public void load() throws InvalidConfigurationFileException, DriverException { if(loaded) destroy(); //reset driver loaded = true; try { HardwareManager hm = HardwareManager.getInstance(); String canName = KinectOpenNIDriver.class.getCanonicalName(); if (!hm.hasConfigFile(canName)) { throw new InvalidConfigurationFileException("Missing " + canName + " config file"); } OpenNIContextSingleton.setConfigurationFile(hm.getConfigFile(canName)); context = OpenNIContextSingleton.getContext(); } catch (HardwareManagerManifestException e) { // We should never be here, and it should be caught by the // ModuleManager log.error("HardwareManager Manifest Exception"); } catch (OpenNIConfigurationException e){ throw new InvalidConfigurationFileException("Invalid OpenNI configuration file."); } catch (GeneralException e){ throw new DriverException("Exception in OpenNI"); } try{ gestureGen = GestureGenerator.create(context); gestureGen.addGesture("Wave"); gestureGen.addGesture("Click"); gestureGen.getGestureRecognizedEvent().addObserver( new GestureRecognized()); handsGen = HandsGenerator.create(context); handsGen.getHandCreateEvent().addObserver(new HandCreated()); handsGen.getHandUpdateEvent().addObserver(new HandUpdated()); handsGen.getHandDestroyEvent().addObserver(new HandDestroyed()); depthGen = DepthGenerator.create(context); DepthMetaData depthMD = depthGen.getMetaData(); context.startGeneratingAll(); imageGen = ImageGenerator.create(context); ImageMetaData imageMD = imageGen.getMetaData(); imageWidth = imageMD.getFullXRes(); imageHeight = imageMD.getFullYRes(); depthWidth = depthMD.getFullXRes(); depthHeight = depthMD.getFullYRes(); EventManager.getInstance().fireEvent(EventType.VIEWPORT_DIMENSION, new Dimension(depthWidth, depthHeight)); } catch (GeneralException e){ throw new DriverException("Exception in OpenNI"); } } @Override public boolean loaded(){ return loaded; } //HandTrackerInterface /** * This updates all the nodes being observed by the context for any * available data. This should be called whenever updated information is * desired. */ public void updateDriver() { DriverHelper.checkLoaded(this); try { context.waitAnyUpdateAll(); } catch (StatusException e) { e.printStackTrace(); } } public int getHandTrackingWidth() { DriverHelper.checkLoaded(this); return depthWidth; } public int getHandTrackingHeight() { DriverHelper.checkLoaded(this); return depthHeight; } class GestureRecognized implements IObserver<GestureRecognizedEventArgs> { @Override public void update(IObservable<GestureRecognizedEventArgs> observable, GestureRecognizedEventArgs args) { try { String gestname = args.getGesture(); if(gestname.equals("Wave")) handsGen.StartTracking(args.getEndPosition()); // should we always start tracking? else evtMgr.fireEvent(EventType.GESTURE_RECOGNIZED, new Gesture(args.getGesture(), HandTrackingUtilities.convertOpenNIPoint(depthGen, args.getIdPosition()), HandTrackingUtilities.convertOpenNIPoint(depthGen, args.getEndPosition()))); } catch (StatusException e) { e.printStackTrace(); } } } class HandCreated implements IObserver<ActiveHandEventArgs> { @Override public void update(IObservable<ActiveHandEventArgs> obs, ActiveHandEventArgs e) { HandPosition pos = new HandPosition(e.getId(), HandTrackingUtilities.convertOpenNIPoint(depthGen, e.getPosition())); evtMgr.fireEvent(EventType.HAND_CREATED, pos); } } class HandUpdated implements IObserver<ActiveHandEventArgs> { @Override public void update(IObservable<ActiveHandEventArgs> obs, ActiveHandEventArgs e) { HandPosition pos = new HandPosition(e.getId(), HandTrackingUtilities.convertOpenNIPoint(depthGen, e.getPosition())); evtMgr.fireEvent(EventType.HAND_UPDATED, pos); } } class HandDestroyed implements IObserver<InactiveHandEventArgs> { @Override public void update(IObservable<InactiveHandEventArgs> obs, InactiveHandEventArgs e) { evtMgr.fireEvent(EventType.HAND_DESTROYED, e.getId()); } } // DriverInterface /** * The Kinect openni device is considered available if an openni context can * be created. */ public boolean isAvailable() { boolean ret = true; try { if(!loaded) { load(); } context = OpenNIContextSingleton.getContext(); } catch (GeneralException e) { ret = false; } catch (OpenNIConfigurationException e) { ret = false; } catch (Throwable t){ ret = false; // load threw an exception; } return ret; } /** * Ends tracking on the hand tracker as well ensures that all generators are * stopped on the context singleton. */ public void destroy() { if(loaded) { try { handsGen.StopTrackingAll(); context.stopGeneratingAll(); handsGen.dispose(); String[] strs = gestureGen.enumerateAllGestures(); for(String s: strs){ gestureGen.removeGesture(s); } gestureGen.dispose(); imageGen.dispose(); depthGen.dispose(); OpenNIContextSingleton.destroy(); context = null; handsGen = null; gestureGen = null; imageGen = null; depthGen = null; // Remove all receivers connected to this driver EventManager.getInstance().removeReceivers(EventType.HAND_CREATED); EventManager.getInstance().removeReceivers(EventType.HAND_UPDATED); EventManager.getInstance().removeReceivers(EventType.HAND_DESTROYED); EventManager.getInstance().removeReceivers(EventType.GESTURE_RECOGNIZED); loaded = false; } catch (StatusException e) { e.printStackTrace(); } } } // DepthDataInterface public ShortBuffer getDepthImageData() { DriverHelper.checkLoaded(this); DepthMetaData depthMD = depthGen.getMetaData(); DepthMap dm = depthMD.getData(); ShortBuffer data = dm.createShortBuffer(); data.rewind(); return data; } public int getRGBImageWidth() { DriverHelper.checkLoaded(this); return imageWidth; } public int getRGBImageHeight() { DriverHelper.checkLoaded(this); return imageHeight; } // RGBImageInterface public ByteBuffer getVisualData() { DriverHelper.checkLoaded(this); ImageMetaData imageMD = imageGen.getMetaData(); ByteBuffer rgbBuffer = imageMD.getData().createByteBuffer(); return rgbBuffer; } public int getDepthImageWidth() { DriverHelper.checkLoaded(this); return depthWidth; } public int getDepthImageHeight() { DriverHelper.checkLoaded(this); return depthHeight; } /** * Registers a hand created event given an input receiver * * @param r * the input receiver */ public void registerHandCreated(InputReceiver r) { EventManager.getInstance().registerReceiver(EventType.HAND_CREATED, r); } /** * Registers a hand updated event given an input receiver * * @param r * the input receiver */ public void registerHandUpdated(InputReceiver r) { EventManager.getInstance().registerReceiver(EventType.HAND_UPDATED, r); } /** * Registers a hand destroyed event given an input receiver * * @param r * the input receiver */ public void registerHandDestroyed(InputReceiver r) { EventManager.getInstance() .registerReceiver(EventType.HAND_DESTROYED, r); } public void registerGestureRecognized(InputReceiver r) { EventManager.getInstance() .registerReceiver(EventType.GESTURE_RECOGNIZED, r); } public void registerViewportDimension(InputReceiver r) { EventManager.getInstance().registerReceiver( EventType.VIEWPORT_DIMENSION, r); } @Override public void clearAllHands() {} }