/* * Copyright (C) 2011-2012 Jiri Masa, Samuel Audet * * Licensed either under the Apache License, Version 2.0, or (at your option) * under the terms of the GNU General Public License as published by * the Free Software Foundation (subject to the "Classpath" exception), * either version 2, or any later version (collectively, the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * http://www.gnu.org/licenses/ * http://www.gnu.org/software/classpath/license.html * * or as provided in the LICENSE.txt file that accompanied this code. * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.bytedeco.javacv; import cl.eye.CLCamera; import java.io.File; import java.lang.reflect.Field; import sun.misc.Unsafe; import static org.bytedeco.javacpp.opencv_core.*; import static org.bytedeco.javacpp.opencv_imgproc.*; /** Minimal Sony PS3 Eye camera grabber implementation. * * It allows grabbing of frames at higher speed than OpenCVFrameGrabber or VideoInputFrameGrabber. * Underlying implementation of last two grabbers is limited to 30 FPS. PS3 allows grabbing * at maximum speed of 75 FPS in VGA and 187 FPS in QVGA modes. * * This code was developed and tested with CLEyeMulticam.dll, version 1.2.0.1008. The dll library * is part of Code Laboratories CL-Eye Platform SDK and is distributed as part of CLEyeMulticam * Redistributable Dynamic Link Library. For license, download and installation see http://www.codelaboratories.com. * * The grab() method returns an internal instance of IplImage image with fresh camera frame. This returned image * have to be considered "read only" and the caller needs to create it's own copy or clone that image. * Calling of release() method for this image shall be avoided. * Based on used resolution the image is in format either 640x480 or 320x240, IPL_DEPTH_8U, 4 channel (color) or 1 channel (gray). * timestamp is set to actual value of System.nanoTime()/1000 obtained after return from the CL driver. * * Typical use case scenario: * create new instance of PS3MiniGrabber * set camera parameters * start() grabber * wait at least 2 frames * grab() in loop * stop() grabber * release() internal resources * * Note: * This code depends on the cl.eye.CLCamera class from Code Laboratories CL-Eye * Platform SDK. It is suggested to download SDK and edit the sample file * ....\cl\eye\CLCamera.java. A few references to processing.core.PApplet class * shall be removed and the file recompiled. The tailored file is not included * here namely because of unclear licence. * * @author jmasa, jmasa@cmail.cz * */ public class PS3EyeFrameGrabber extends FrameGrabber { public static String[] getDeviceDescriptions() throws Exception { tryLoad(); String[] descriptions = new String[CLCamera.cameraCount()]; for (int i = 0; i < descriptions.length; i++) { descriptions[i] = CLCamera.cameraUUID(i); } return descriptions; } public static PS3EyeFrameGrabber createDefault(File deviceFile) throws Exception { throw new Exception(PS3EyeFrameGrabber.class + " does not support device files."); } public static PS3EyeFrameGrabber createDefault(String devicePath) throws Exception { throw new Exception(PS3EyeFrameGrabber.class + " does not support device paths."); } public static PS3EyeFrameGrabber createDefault(int deviceNumber) throws Exception { return new PS3EyeFrameGrabber(deviceNumber); } private static Exception loadingException = null; public static void tryLoad() throws Exception { if (loadingException != null) { throw loadingException; } else { try { CLCamera.IsLibraryLoaded(); } catch (Throwable t) { throw loadingException = new Exception("Failed to load " + PS3EyeFrameGrabber.class, t); } } } CLCamera camera; int cameraIndex = 0; int[] ps3_frame = null; // buffer for PS3 camera frame data byte[] ipl_frame = null; // buffer for RGB-3ch, not allocated unless grab_RGB3() is called IplImage image_4ch = null; IplImage image_1ch = null; FrameConverter converter = new OpenCVFrameConverter.ToIplImage(); String stat; // status of PS3 camera handling - mostly for debugging String uuid; // assigned camera unique key // variables for trigger() implementation // protected enum Triggered {NO_TRIGGER, HAS_FRAME, NO_FRAME}; protected Triggered triggered = Triggered.NO_TRIGGER; /** Default grabber, camera idx = 0, color mode, VGA resolution, 60 FPS frame rate. * */ public PS3EyeFrameGrabber() throws Exception { this(0); } /** Color mode, VGA resolution, 60 FPS frame rate. * @param system wide camera index */ public PS3EyeFrameGrabber(int cameraIndex) throws Exception { this(cameraIndex, 640, 480, 60); } public PS3EyeFrameGrabber(int cameraIndex, int imageWidth, int imageHeight, int framerate) throws Exception { this(cameraIndex, 640, 480, 60, null); } /** Creates grabber, the caller can control basic image and grabbing parameters. * * @param cameraIndex - zero based index of used camera (OS system wide) * @param imageWidth - width of image * @param imageHeight - height of image * @param framerate - frame rate - see CLCamera for allowed frame rates based on resolution * @param applet - PApplet object required by CLCamera * @throws Exception - if parameters don't follow CLCamera definition or camera is not created */ public PS3EyeFrameGrabber(int cameraIndex, int imageWidth, int imageHeight, int framerate, Object applet) throws Exception { camera = null; if (! CLCamera.IsLibraryLoaded()) { throw new Exception("CLEye multicam dll not loaded"); } try { try { // maybe some new version of CLCamera works without a PApplet... camera = CLCamera.class.newInstance(); } catch (Throwable t) { if (applet == null) { // do some really hacky stuff to create an instance // without calling the constructor Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); unsafeField.setAccessible(true); Unsafe unsafe = (Unsafe)unsafeField.get(null); camera = (CLCamera)unsafe.allocateInstance(CLCamera.class); } else { camera = (CLCamera)CLCamera.class.getConstructors()[0].newInstance(applet); } } } catch (Throwable t) { throw new Exception("Failed to construct " + PS3EyeFrameGrabber.class, t); } this.cameraIndex = cameraIndex; stat = "created"; uuid = CLCamera.cameraUUID(cameraIndex); if (((imageWidth == 640) && (imageHeight == 480)) || ((imageWidth == 320) && (imageHeight == 240))) { setImageWidth(imageWidth); setImageHeight(imageHeight); } else throw new Exception("Only 640x480 or 320x240 images supported"); setImageMode(ImageMode.COLOR); setFrameRate((double) framerate); setTimeout(1 + 1000/framerate); setBitsPerPixel(8); setTriggerMode(false); setNumBuffers(4); } /** * @return system wide number of installed/detected Sony PS3 Eye cameras */ public static int getCameraCount() { return CLCamera.cameraCount(); } /** Ask the driver for all installed PS3 cameras. Resulting array is sorted in order of camera index. * Its size is defined by CLCamera.cameraCount(). * * @return array of camera unique uuids or null if there is no PS3 camera */ public static String[] listPS3Cameras() { int no = getCameraCount(); String[] uuids; if (no > 0) { uuids = new String[no]; for (--no; no >=0; no--) { uuids[no] = CLCamera.cameraUUID(no); } return uuids; } return null; } /** Make IplImage form raw int[] frame data * Note: NO array size checks!! * * @param frame int[] image frame data * @return internal IplImage set to frame */ public IplImage makeImage(int[] frame) { image_4ch.getIntBuffer().put(ps3_frame); return image_4ch; } /** Grab one frame and return it as int[] (in the internal camera format RGBA). * Note: use makeImage() to create RGBA, 4-ch image * @return frame as int[] without any processing or null if frame is not available */ public int[] grab_raw() { if (camera.getCameraFrame(ps3_frame, timeout)) { return ps3_frame; } else return null; } public void trigger() throws Exception { for (int i = 0; i < numBuffers+1; i++) { grab_raw(); } if ((ps3_frame = grab_raw()) != null) { triggered = Triggered.HAS_FRAME; timestamp = System.nanoTime()/1000; } else triggered = Triggered.NO_FRAME; } /** Grab and convert one frame, default timeout is (1 + 1000/framerate) [milliseconds]. * Every successful call returns an internal (preallocated) 640x480 or 320x240, IPL_DEPTH_8U, 4-channel image. * The caller shall consider it "read only" and make a copy/clone of it before further processing. * * The call might block for timeout [milliseconds]. * @return the image or null if there is no new image */ public IplImage grab_RGB4() { if (camera.getCameraFrame(ps3_frame, timeout)) { timestamp = System.nanoTime()/1000; image_4ch.getIntBuffer().put(ps3_frame); return image_4ch; } else return null; } /** Grab one frame; * the caller have to make a copy of returned image before processing. * * It will throw null pointer exception if not started before grabbing. * @return "read-only" RGB, 4-channel or GRAY/1-channel image, it throws exception if no image is available */ @Override public Frame grab() throws Exception { IplImage img = null; switch (triggered) { case NO_TRIGGER: img = grab_RGB4(); break; case HAS_FRAME: triggered = Triggered.NO_TRIGGER; img = makeImage(ps3_frame); break; case NO_FRAME: triggered = Triggered.NO_TRIGGER; return null; default: // just schizophrenia - for future enhancement throw new Exception("Int. error - unknown triggering state"); } if ((img != null) && (imageMode == ImageMode.GRAY)) { cvCvtColor(img, image_1ch, CV_RGB2GRAY); img = image_1ch; } return converter.convert(img); } /** Start camera first (before grabbing). * * @return success/failure (true/false) */ public void start() throws Exception { boolean b; if (ps3_frame == null) { ps3_frame = new int[ imageWidth * imageHeight ]; image_4ch = IplImage.create(imageWidth, imageHeight, IPL_DEPTH_8U, 4); image_1ch = IplImage.create(imageWidth, imageHeight, IPL_DEPTH_8U, 1); } b = camera.createCamera( cameraIndex, (imageMode == ImageMode.GRAY) ? CLCamera.CLEYE_MONO_PROCESSED : CLCamera.CLEYE_COLOR_PROCESSED, (imageWidth == 320 && imageHeight == 240) ? CLCamera.CLEYE_QVGA : CLCamera.CLEYE_VGA, (int)frameRate); if (!b) throw new Exception("Low level createCamera() failed"); b = camera.startCamera(); if (!b) throw new Exception("Camera start() failed"); stat = "started"; } /** Stop camera. It can be re-started if needed. * * @return success/failure (true/false) */ public void stop() throws Exception { boolean b = camera.stopCamera(); if (b) stat = "stopped"; else throw new Exception("Camera stop() failed"); } /** Release resources: * - CL driver internal resources binded with camera HW * - internal IplImage * After calling this function, this mini-grabber object instance can not be used anymore. */ public void release() { if (camera != null) { camera.dispose(); camera = null; } if (image_4ch != null) { image_4ch.release(); image_4ch = null; } if (image_1ch != null) { image_1ch.release(); image_1ch = null; } if (ipl_frame != null) ipl_frame = null; if (ps3_frame != null) ps3_frame = null; stat = "released"; } /** Release internal resources, the same as calling release() */ public void dispose() { release(); } @Override protected void finalize() throws Throwable { super.finalize(); release(); } /** Return internal CLCamera object, mainly to set camera parameters, * changing camera parameters must be done on stopped camera and before start() is called. * See CL SDK - setCameraParameter(int param, int val) function. * * @return internal CLCamera instance */ public CLCamera getCamera() { return camera; } public String getUUID() { return uuid; } /** * @return status and camera parameters of the grabber */ @Override public String toString() { return "UUID="+uuid + "; status=" + stat + "; timeout=" + timeout + "; " + ((camera != null) ? camera.toString() : "<no camera>") ; } /** Just for testing - loads the CL CLEyeMulticam.dll file, invokes driver * and lists available cameras. * * @param argv - argv is not used */ public static void main(String[] argv) { String[] uuids = listPS3Cameras(); for (int i = 0; i < uuids.length; i++) System.out.println(i+": "+uuids[i]); } }