Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
package com.techhounds.imgcv;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.opencv.core.Mat;
import org.opencv.highgui.Highgui;
import org.opencv.highgui.VideoCapture;
/**
* Helper class that runs a background thread to fetch images as quickly as
* possible from the video capture device.
*
* @author Paul Blankenbaker
*/
public class FrameGrabber {
/** Background thread that reads in images from capture device. */
private CaptureThread _CaptureThread;
/** Will be non-null if auto file save has been enabled. */
private File _SaveDir;
/** Counter used while auto save is enabled. */
private int _SaveCnt;
/** How often we save (number of frames to skip). */
private int _HowOften;
/** The end count when we need to disable saving. */
private int _SaveLastCnt;
/**
* Construct a new instance in a a disconnected state (not running until you
* "start").
*/
public FrameGrabber() {
_CaptureThread = null;
}
/**
* @return A string representation of the state of the object.
*/
@Override
public String toString() {
CaptureThread ct = _CaptureThread;
if (ct == null) {
return "No Connection";
}
return ct.toString();
}
/**
* Returns a copy of the last image retrieved (if available).
*
* @return A cloned copy of the last image retrieved (or null if no image
* retrieved yet since the last start).
*/
public Mat getLastImage() {
CaptureThread ct = _CaptureThread;
Mat img = null;
if (ct != null) {
synchronized (ct) {
if (ct._LastImage != null) {
img = ct._LastImage.clone();
}
}
}
return img;
}
/**
* The total number of frames retrieved from the video capture device since
* it was last started.
*
* @return Frame count since start.
*/
public long getFrameCount() {
CaptureThread ct = _CaptureThread;
return (ct == null) ? null : ct._FrameCount;
}
/**
* The frame rate retrieved from the source computed since started.
*
* @return The frames per second we are getting from the source.
*/
public int getFps() {
CaptureThread ct = _CaptureThread;
if (ct == null) {
return 0;
}
return ct.getFps();
}
/**
* Helper method to open a VideoCapture device.
*
* @param url
* A URL of a IP camera device (or null if you want to open a
* local web cam).
* @param devId
* When URL is null, this must be the ID of a local video device
* (web cam).
* @param width
* Pass non-zero value if you want us to try and set the video
* width (not used if URL is non-null).
* @param height
* Pass non-zero value if you want us to try and set the video
* height (not used if URL is non-null).
* @return A VideoCapture device.
*/
public static VideoCapture open(String url, int devId, int width, int height) {
VideoCapture vc = new VideoCapture();
if (url != null) {
vc.open(url);
if (vc.isOpened()) {
System.err.println("Starting IP camera feed from: " + url);
} else {
System.err.println("Failed to start IP camera feed from: " + url);
vc = null;
}
} else {
vc.open(devId);
if (vc.isOpened()) {
if (width > 0 && height > 0) {
vc.set(Highgui.CV_CAP_PROP_FRAME_WIDTH, width);
vc.set(Highgui.CV_CAP_PROP_FRAME_HEIGHT, height);
}
System.err.println("Starting Video Feed using Web Cam: " + devId);
} else {
System.err.println("Failed to start Video Feed using Web Cam: " + devId);
vc = null;
}
}
return vc;
}
/**
* Start a video feed using built-in camera or USB web cam.
*
* @param devId
* The ID of a local video device (web cam). Typically 0 for
* first web cam.
* @param width
* Pass non-zero value if you want us to try and set the video
* width.
* @param height
* Pass non-zero value if you want us to try and set the video
* height.
*/
public void start(int devId, int width, int height) {
stop();
_CaptureThread = new CaptureThread(devId, null, width, height);
_CaptureThread.start();
}
/**
* Start a video feed using an IP based camera.
*
* @param URL
* The URL to connect to for the video feed.
*/
public void start(String url) {
stop();
_CaptureThread = new CaptureThread(-1, url, 0, 0);
_CaptureThread.start();
}
/**
* Stop the video feed connection.
*/
public void stop() {
CaptureThread t = _CaptureThread;
if (t != null) {
// Set flag to let thread shut down nicely
t._Continue = false;
/**
* This is a more drastic way to shut it down more quickly if
* (t.isAlive() && !t.isInterrupted()) { try { t.wait(100); if
* (t.isAlive()) { return; } } catch (InterruptedException e) {
* System.err.println("Capture thread failed to shutdown cleanly");
* } t.interrupt(); try { t.wait(100); } catch (InterruptedException
* e) { System.err.println(
* "Capture thread failed to shutdown cleanly"); } }
*/
}
}
/**
* Indicates whether we think we are still trying to capture images.
*
* @return true If thread is running trying to grab new images.
*/
public boolean isRunning() {
Thread t = _CaptureThread;
return (t != null) && t.isAlive();
}
/**
* Helper class that does the work of connecting to the web cam and grabbing
* images in a background thread.
*
* @author pkb
*/
private class CaptureThread extends Thread {
private static final long MIN_FRAMES_FOR_FPS = 150;
private int _DevId;
private String _Url;
private int _Width;
private int _Height;
private Mat _LastImage;
private long _FrameCount;
private boolean _Continue;
private long _FirstFrameTime;
private long _LastFrameTime;
CaptureThread(int devId, String url, int width, int height) {
_DevId = devId;
_Url = url;
_Width = width;
_Height = height;
_FrameCount = 0;
_LastImage = null;
_Continue = true;
}
/**
* Returns the average FPS after we have at least two frames.
*
* @return Frames Per Second (FPS).
*/
public int getFps() {
int fps = 0;
synchronized (this) {
long dur = _LastFrameTime - _FirstFrameTime;
if ((dur > 0) && (_FrameCount >= MIN_FRAMES_FOR_FPS)) {
fps = (int) ((_FrameCount - MIN_FRAMES_FOR_FPS + 1) * 1000 / dur);
}
}
return fps;
}
@Override
public void run() {
VideoCapture vc = open(_Url, _DevId, _Width, _Height);
if (vc == null) {
return;
}
while (!isInterrupted() && _Continue) {
if (vc.grab()) {
Mat img = new Mat();
synchronized (this) {
if (vc.retrieve(img)) {
_LastImage = img;
_LastFrameTime = System.currentTimeMillis();
if (_FrameCount == MIN_FRAMES_FOR_FPS) {
_FirstFrameTime = _LastFrameTime;
}
_FrameCount++;
// See if we need to save the image
saveCheck(img);
}
}
/*
* if (_FrameCount % 100 == 0) { System.err.println(
* "FPS from camera: " + getFps()); }
*/
}
}
System.err.println("Video Capture thread is stopping");
_FrameCount = 0;
_LastFrameTime = _FirstFrameTime = 0;
vc.release();
}
/**
* Helper method that writes out raw images capture when the
* "enable save" feature is active.
*
* @param img
* The image to write out.
*/
private void saveCheck(Mat img) {
File saveDir = _SaveDir;
if ((saveDir != null) && (_SaveCnt < _SaveLastCnt)) {
if ((_SaveCnt % _HowOften) == 0) {
File imgFile = new File(saveDir, Long.toString(_FrameCount) + ".jpg");
String path = imgFile.getAbsolutePath();
Highgui.imwrite(path, img);
}
_SaveCnt++;
if (_SaveCnt >= _SaveLastCnt) {
_SaveDir = null;
}
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("Frame: ");
sb.append(_FrameCount);
Mat img = _LastImage;
if (img != null) {
sb.append(" (");
sb.append(img.rows());
sb.append(", ");
sb.append(img.cols());
sb.append(", ");
sb.append(img.depth());
sb.append(")");
}
if (_DevId != -1) {
sb.append(" WebCam(");
sb.append(_DevId);
sb.append(')');
}
if (_Url != null) {
sb.append(" URL(");
sb.append(_Url);
sb.append(')');
}
return sb.toString();
}
}
/**
* Helper method which tells the frame grabber to start saving raw images to
* a directory (useful to enable during a robot's autonomous period).
*
* @param dir
* The directory you want images stored under (hint, use
* {@link #createSaveDir(String)}).
* @param total
* The total number of images to capture.
* @param howOften
* How often (1 every frame, 2 every other frame, etc).
*/
public void enableSave(File dir, int total, int howOften) {
_SaveDir = dir.isDirectory() ? dir : null;
_SaveCnt = 0;
_SaveLastCnt = total * howOften;
_HowOften = howOften;
}
/**
* Disables the "auto saving" of images (turns it off early).
*/
public void disableSave() {
_SaveLastCnt = 0;
}
/**
* Creates an output save directory for captured images under:
* $HOME/Desktop/captured-images/PREFIX-YYYYMMDD-HHMMSS.
*
* @param prefix
* The prefix to put in front of the directory name (like "pink",
* "2016-target", etc).
* @return A File object of the created directory or null if the directory
* did not exist and we were unable to create it.
*/
public static File createSaveDir(String prefix) {
File dir = new File(System.getProperty("user.home"), "Desktop");
dir = new File(dir, "captured-images");
SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss");
dir = new File(dir, prefix + "-" + df.format(new Date()));
if (dir.isDirectory() || dir.mkdirs()) {
return dir;
}
return null;
}
}