/*
* Copyright (C) 2009-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 org.bytedeco.javacpp.Loader;
import static org.bytedeco.javacpp.opencv_core.*;
import static org.bytedeco.javacpp.opencv_imgproc.*;
import static org.bytedeco.javacpp.opencv_videoio.*;
/**
*
* @author Samuel Audet
* @author Lloyd (github.com/lloydmeta)
*/
public class OpenCVFrameGrabber extends FrameGrabber {
public static String[] getDeviceDescriptions() throws Exception {
tryLoad();
throw new UnsupportedOperationException("Device enumeration not support by OpenCV.");
}
public static OpenCVFrameGrabber createDefault(File deviceFile) throws Exception { return new OpenCVFrameGrabber(deviceFile); }
public static OpenCVFrameGrabber createDefault(String devicePath) throws Exception { return new OpenCVFrameGrabber(devicePath); }
public static OpenCVFrameGrabber createDefault(int deviceNumber) throws Exception { return new OpenCVFrameGrabber(deviceNumber); }
private static Exception loadingException = null;
public static void tryLoad() throws Exception {
if (loadingException != null) {
throw loadingException;
} else {
try {
Loader.load(org.bytedeco.javacpp.opencv_highgui.class);
} catch (Throwable t) {
throw loadingException = new Exception("Failed to load " + OpenCVFrameGrabber.class, t);
}
}
}
public OpenCVFrameGrabber(int deviceNumber) {
this.deviceNumber = deviceNumber;
}
public OpenCVFrameGrabber(File file) {
this(file.getAbsolutePath());
}
public OpenCVFrameGrabber(String filename) {
this.filename = filename;
}
public void release() throws Exception {
stop();
}
@Override protected void finalize() throws Throwable {
super.finalize();
release();
}
private int deviceNumber = 0;
private String filename = null;
private VideoCapture capture = null;
private Mat returnMatrix = null;
private final OpenCVFrameConverter converter = new OpenCVFrameConverter.ToMat();
private final Mat mat = new Mat();
@Override public double getGamma() {
// default to a gamma of 2.2 for cheap Webcams, DV cameras, etc.
if (gamma == 0.0) {
return 2.2;
} else {
return gamma;
}
}
@Override public String getFormat() {
if (capture == null) {
return super.getFormat();
} else {
int fourcc = (int)capture.get(CV_CAP_PROP_FOURCC);
return "" + (char)( fourcc & 0xFF) +
(char)((fourcc >> 8) & 0xFF) +
(char)((fourcc >> 16) & 0xFF) +
(char)((fourcc >> 24) & 0xFF);
}
}
@Override public int getImageWidth() {
if (returnMatrix != null) {
return returnMatrix.cols();
} else {
return capture == null ? super.getImageWidth() : (int)capture.get(CV_CAP_PROP_FRAME_WIDTH);
}
}
@Override public int getImageHeight() {
if (returnMatrix != null) {
return returnMatrix.rows();
} else {
return capture == null ? super.getImageHeight() : (int)capture.get(CV_CAP_PROP_FRAME_HEIGHT);
}
}
@Override public int getPixelFormat() {
return capture == null ? super.getPixelFormat() : (int)capture.get(CV_CAP_PROP_CONVERT_RGB);
}
@Override public double getFrameRate() {
return capture == null ? super.getFrameRate() : (int)capture.get(CV_CAP_PROP_FPS);
}
@Override public void setImageMode(ImageMode imageMode) {
if (imageMode != this.imageMode) {
returnMatrix = null;
}
super.setImageMode(imageMode);
}
@Override public int getFrameNumber() {
return capture == null ? super.getFrameNumber() :
(int)capture.get(CV_CAP_PROP_POS_FRAMES);
}
@Override public void setFrameNumber(int frameNumber) throws Exception {
if (capture == null) {
super.setFrameNumber(frameNumber);
} else {
if (!capture.set(CV_CAP_PROP_POS_FRAMES, frameNumber)) {
throw new Exception("set() Error: Could not set CV_CAP_PROP_POS_FRAMES to " + frameNumber + ".");
}
}
}
@Override public long getTimestamp() {
return capture == null ? super.getTimestamp() :
Math.round(capture.get(CV_CAP_PROP_POS_MSEC)*1000);
}
@Override public void setTimestamp(long timestamp) throws Exception {
if (capture == null) {
super.setTimestamp(timestamp);
} else {
if (!capture.set(CV_CAP_PROP_POS_MSEC, timestamp/1000.0)) {
throw new Exception("set() Error: Could not set CV_CAP_PROP_POS_MSEC to " + timestamp/1000.0 + ".");
}
}
}
@Override public int getLengthInFrames() {
return capture == null ? super.getLengthInFrames() :
(int)capture.get(CV_CAP_PROP_FRAME_COUNT);
}
@Override public long getLengthInTime() {
return Math.round(getLengthInFrames() * 1000000L / getFrameRate());
}
public void start() throws Exception {
if (filename != null && filename.length() > 0) {
capture = new VideoCapture(filename);
} else {
capture = new VideoCapture(deviceNumber);
}
if (format != null && format.length() >= 4) {
format = format.toUpperCase();
byte cc0 = (byte)format.charAt(0);
byte cc1 = (byte)format.charAt(1);
byte cc2 = (byte)format.charAt(2);
byte cc3 = (byte)format.charAt(3);
capture.set(CV_CAP_PROP_FOURCC, CV_FOURCC(cc0, cc1, cc2, cc3));
}
if (imageWidth > 0) {
if (!capture.set(CV_CAP_PROP_FRAME_WIDTH, imageWidth)) {
capture.set(CV_CAP_PROP_MODE, imageWidth); // ??
}
}
if (imageHeight > 0) {
if (!capture.set(CV_CAP_PROP_FRAME_HEIGHT, imageHeight)) {
capture.set(CV_CAP_PROP_MODE, imageHeight); // ??
}
}
if (frameRate > 0) {
capture.set(CV_CAP_PROP_FPS, frameRate);
}
if (bpp > 0) {
capture.set(CV_CAP_PROP_FORMAT, bpp); // ??
}
capture.set(CV_CAP_PROP_CONVERT_RGB, imageMode == ImageMode.COLOR ? 1 : 0);
Mat mat = new Mat();
try {
// Before retrieve() starts returning something else then null
// QTKit sometimes requires some "warm-up" time for some reason...
// The first frame on Linux is sometimes null as well,
// so it's probably a good idea to run this for all platforms... ?
int count = 0;
while (count++ < 100 && !capture.read(mat)) {
Thread.sleep(100);
}
} catch (InterruptedException ex) {
// reset interrupt to be nice
Thread.currentThread().interrupt();
}
if (!capture.read(mat)) {
throw new Exception("read() Error: Could not read frame in start().");
}
if (!triggerMode) {
if (!capture.grab()) {
throw new Exception("grab() Error: Could not grab frame. (Has start() been called?)");
}
}
}
public void stop() throws Exception {
if (capture != null) {
capture.release();
capture = null;
}
}
public void trigger() throws Exception {
Mat mat = new Mat();
for (int i = 0; i < numBuffers+1; i++) {
capture.read(mat);
}
if (!capture.grab()) {
throw new Exception("grab() Error: Could not grab frame. (Has start() been called?)");
}
}
public Frame grab() throws Exception {
if (!capture.retrieve(mat)) {
throw new Exception("retrieve() Error: Could not retrieve frame. (Has start() been called?)");
}
if (!triggerMode) {
if (!capture.grab()) {
throw new Exception("grab() Error: Could not grab frame. (Has start() been called?)");
}
}
if (imageMode == ImageMode.GRAY && mat.channels() > 1) {
if (returnMatrix == null) {
returnMatrix = new Mat(mat.rows(), mat.cols(), mat.depth(), 1);
}
cvtColor(mat, returnMatrix, CV_BGR2GRAY);
} else if (imageMode == ImageMode.COLOR && mat.channels() == 1) {
if (returnMatrix == null) {
returnMatrix = new Mat(mat.rows(), mat.cols(), mat.depth(), 3);
}
cvtColor(mat, returnMatrix, CV_GRAY2BGR);
} else {
returnMatrix = mat;
}
return converter.convert(returnMatrix);
}
}