/*
* Copyright (C) 2011-2012 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 java.io.File;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import org.bytedeco.javacpp.BytePointer;
import org.bytedeco.javacpp.Pointer;
import org.bytedeco.javacpp.Loader;
import static org.bytedeco.javacpp.freenect.*;
import static org.bytedeco.javacpp.opencv_core.*;
import static org.bytedeco.javacpp.opencv_imgproc.*;
/**
*
* @author Samuel Audet
*/
public class OpenKinectFrameGrabber extends FrameGrabber {
public static String[] getDeviceDescriptions() throws Exception {
tryLoad();
freenect_context ctx = new freenect_context(null);
int err = freenect_init(ctx, null);
if (err < 0) {
throw new Exception("freenect_init() Error " + err + ": Failed to init context.");
}
int count = freenect_num_devices(ctx);
if (count < 0) {
throw new Exception("freenect_num_devices() Error " + err + ": Failed to get number of devices.");
}
String[] descriptions = new String[count];
for (int i = 0; i < descriptions.length; i++) {
descriptions[i] = "Kinect #" + i;
}
err = freenect_shutdown(ctx);
if (err < 0) {
throw new Exception("freenect_shutdown() Error " + err + ": Failed to shutdown context.");
}
return descriptions;
}
public static OpenKinectFrameGrabber createDefault(File deviceFile) throws Exception { throw new Exception(OpenKinectFrameGrabber.class + " does not support device files."); }
public static OpenKinectFrameGrabber createDefault(String devicePath) throws Exception { throw new Exception(OpenKinectFrameGrabber.class + " does not support device paths."); }
public static OpenKinectFrameGrabber createDefault(int deviceNumber) throws Exception { return new OpenKinectFrameGrabber(deviceNumber); }
private static Exception loadingException = null;
public static void tryLoad() throws Exception {
if (loadingException != null) {
throw loadingException;
} else {
try {
Loader.load(org.bytedeco.javacpp.freenect.class);
} catch (Throwable t) {
throw loadingException = new Exception("Failed to load " + OpenKinectFrameGrabber.class, t);
}
}
}
public OpenKinectFrameGrabber(int deviceNumber) {
this.deviceNumber = deviceNumber;
}
public void release() throws Exception {
stop();
}
@Override protected void finalize() throws Throwable {
super.finalize();
release();
}
private int deviceNumber = 0;
private boolean depth = false; // default to "video"
private BytePointer rawDepthImageData = new BytePointer((Pointer)null),
rawVideoImageData = new BytePointer((Pointer)null),
rawIRImageData = new BytePointer((Pointer)null);
private IplImage rawDepthImage = null, rawVideoImage = null, rawIRImage = null, returnImage = null;
private FrameConverter converter = new OpenCVFrameConverter.ToIplImage();
private int[] timestamp = { 0 };
private ByteOrder byteOrder = ByteOrder.BIG_ENDIAN;
private int depthFormat = -1;
private int videoFormat = -1;
public ByteOrder getByteOrder() {
return byteOrder;
}
public void setByteOrder(ByteOrder byteOrder) {
this.byteOrder = byteOrder;
}
public int getDepthFormat() {
return depthFormat;
}
public void setDepthFormat(int depthFormat) {
this.depthFormat = depthFormat;
}
public int getVideoFormat() {
return videoFormat;
}
public void setVideoFormat(int videoFormat) {
this.videoFormat = videoFormat;
}
@Override public double getGamma() {
// I guess a default gamma of 2.2 is reasonable...
if (gamma == 0.0) {
return 2.2;
} else {
return gamma;
}
}
@Override public void setImageMode(ImageMode imageMode) {
if (imageMode != this.imageMode) {
returnImage = null;
}
super.setImageMode(imageMode);
}
public void start() throws Exception {
depth = "depth".equalsIgnoreCase(format);
}
public void stop() throws Exception {
freenect_sync_stop();
}
public void trigger() throws Exception {
for (int i = 0; i < numBuffers+1; i++) {
if (depth) {
int fmt = depthFormat < 0 ? bpp : depthFormat; // default bpp == 0 == FREENECT_DEPTH_11BIT
int err = freenect_sync_get_depth(rawDepthImageData, timestamp, deviceNumber, fmt);
if (err != 0) {
throw new Exception("freenect_sync_get_depth() Error " + err + ": Failed to get depth synchronously.");
}
} else {
int fmt = videoFormat < 0 ? bpp : videoFormat; // default bpp == 0 == FREENECT_VIDEO_RGB
int err = freenect_sync_get_video(rawVideoImageData, timestamp, deviceNumber, fmt);
if (err != 0) {
throw new Exception("freenect_sync_get_video() Error " + err + ": Failed to get video synchronously.");
}
}
}
}
public IplImage grabDepth() throws Exception {
int fmt = depthFormat < 0 ? bpp : depthFormat; // default bpp == 0 == FREENECT_DEPTH_11BIT
int iplDepth = IPL_DEPTH_16U, channels = 1;
switch (fmt) {
case FREENECT_DEPTH_11BIT:
case FREENECT_DEPTH_REGISTERED:
case FREENECT_DEPTH_MM:
case FREENECT_DEPTH_10BIT: iplDepth = IPL_DEPTH_16U; channels = 1; break;
case FREENECT_DEPTH_11BIT_PACKED:
case FREENECT_DEPTH_10BIT_PACKED:
default: assert false;
}
int err = freenect_sync_get_depth(rawDepthImageData, timestamp, deviceNumber, fmt);
if (err != 0) {
throw new Exception("freenect_sync_get_depth() Error " + err + ": Failed to get depth synchronously.");
}
int w = 640, h = 480; // how to get the resolution ??
if (rawDepthImage == null || rawDepthImage.width() != w || rawDepthImage.height() != h) {
rawDepthImage = IplImage.createHeader(w, h, iplDepth, channels);
}
cvSetData(rawDepthImage, rawDepthImageData, w*channels*iplDepth/8);
if (iplDepth > 8 && !ByteOrder.nativeOrder().equals(byteOrder)) {
// ack, the camera's endianness doesn't correspond to our machine ...
// swap bytes of 16-bit images
ByteBuffer bb = rawDepthImage.getByteBuffer();
ShortBuffer in = bb.order(ByteOrder.BIG_ENDIAN ).asShortBuffer();
ShortBuffer out = bb.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
out.put(in);
}
super.timestamp = timestamp[0];
return rawDepthImage;
}
public IplImage grabVideo() throws Exception {
int fmt = videoFormat < 0 ? bpp : videoFormat; // default bpp == 0 == FREENECT_VIDEO_RGB
int iplDepth = IPL_DEPTH_8U, channels = 3;
switch (fmt) {
case FREENECT_VIDEO_RGB: iplDepth = IPL_DEPTH_8U; channels = 3; break;
case FREENECT_VIDEO_BAYER:
case FREENECT_VIDEO_IR_8BIT: iplDepth = IPL_DEPTH_8U; channels = 1; break;
case FREENECT_VIDEO_IR_10BIT: iplDepth = IPL_DEPTH_16U; channels = 1; break;
case FREENECT_VIDEO_YUV_RGB: iplDepth = IPL_DEPTH_8U; channels = 3; break;
case FREENECT_VIDEO_YUV_RAW: iplDepth = IPL_DEPTH_8U; channels = 2; break;
case FREENECT_VIDEO_IR_10BIT_PACKED:
default: assert false;
}
int err = freenect_sync_get_video(rawVideoImageData, timestamp, deviceNumber, fmt);
if (err != 0) {
throw new Exception("freenect_sync_get_video() Error " + err + ": Failed to get video synchronously.");
}
int w = 640, h = 480; // how to get the resolution ??
if (rawVideoImage == null || rawVideoImage.width() != w || rawVideoImage.height() != h) {
rawVideoImage = IplImage.createHeader(w, h, iplDepth, channels);
}
cvSetData(rawVideoImage, rawVideoImageData, w*channels*iplDepth/8);
if (iplDepth > 8 && !ByteOrder.nativeOrder().equals(byteOrder)) {
// ack, the camera's endianness doesn't correspond to our machine ...
// swap bytes of 16-bit images
ByteBuffer bb = rawVideoImage.getByteBuffer();
ShortBuffer in = bb.order(ByteOrder.BIG_ENDIAN ).asShortBuffer();
ShortBuffer out = bb.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
out.put(in);
}
if (channels == 3) {
cvCvtColor(rawVideoImage, rawVideoImage, CV_RGB2BGR);
}
super.timestamp = timestamp[0];
return rawVideoImage;
}
public IplImage grabIR() throws Exception {
int iplDepth = IPL_DEPTH_8U, channels = 1;
int err = freenect_sync_get_video(rawIRImageData, timestamp, deviceNumber, FREENECT_VIDEO_IR_8BIT);
if (err != 0) {
throw new Exception("freenect_sync_get_video() Error " + err + ": Failed to get video synchronously.");
}
int w = 640, h = 480; // how to get the resolution ??
if (rawIRImage == null || rawIRImage.width() != w || rawIRImage.height() != h) {
rawIRImage = IplImage.createHeader(w, h, iplDepth, channels);
}
cvSetData(rawIRImage, rawIRImageData, w*channels*iplDepth/8);
super.timestamp = timestamp[0];
return rawIRImage;
}
public Frame grab() throws Exception {
IplImage image = depth ? grabDepth() : grabVideo();
int w = image.width();
int h = image.height();
int iplDepth = image.depth();
int channels = image.nChannels();
if (imageMode == ImageMode.COLOR && channels == 1) {
if (returnImage == null) {
returnImage = IplImage.create(w, h, iplDepth, 3);
}
cvCvtColor(image, returnImage, CV_GRAY2BGR);
return converter.convert(returnImage);
} else if (imageMode == ImageMode.GRAY && channels == 3) {
if (returnImage == null) {
returnImage = IplImage.create(w, h, iplDepth, 1);
}
cvCvtColor(image, returnImage, CV_BGR2GRAY);
return converter.convert(returnImage);
} else {
return converter.convert(image);
}
}
}