Skip to content

Commit

Permalink
Ability to specify ImageProvider in WebcamPanel, refs #599
Browse files Browse the repository at this point in the history
  • Loading branch information
sarxos committed Dec 13, 2017
1 parent c43160e commit 727db46
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 44 deletions.
100 changes: 59 additions & 41 deletions webcam-capture/src/main/java/com/github/sarxos/webcam/Webcam.java
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,9 @@ public boolean open() {
}

/**
* Open the webcam in either blocking (synchronous) or non-blocking
* (asynchronous) mode. If the non-blocking mode is enabled the
* DefaultDelayCalculator is used for calculating delay between two image
* fetching.
* Open the webcam in either blocking (synchronous) or non-blocking (asynchronous) mode. If the
* non-blocking mode is enabled the DefaultDelayCalculator is used for calculating delay between
* two image fetching.
*
* @param async true for non-blocking mode, false for blocking
* @return True if webcam has been open, false otherwise
Expand All @@ -250,36 +249,30 @@ public boolean open() {
public boolean open(boolean async) {
return open(async, new DefaultDelayCalculator());
}

/**
* Open the webcam in either blocking (synchronous) or non-blocking
* (asynchronous) mode.The difference between those two modes lies in the
* image acquisition mechanism.<br>
* Open the webcam in either blocking (synchronous) or non-blocking (asynchronous) mode.The
* difference between those two modes lies in the image acquisition mechanism.<br>
* <br>
* In blocking mode, when user calls {@link #getImage()} method, device is
* being queried for new image buffer and user have to wait for it to be
* available.<br>
* In blocking mode, when user calls {@link #getImage()} method, device is being queried for new
* image buffer and user have to wait for it to be available.<br>
* <br>
* In non-blocking mode, there is a special thread running in the background
* which constantly fetch new images and cache them internally for further
* use. This cached instance is returned every time when user request new
* image. Because of that it can be used when timeing is very important,
* because all users calls for new image do not have to wait on device
* response. By using this mode user should be aware of the fact that in
* some cases, when two consecutive calls to get new image are executed more
* often than webcam device can serve them, the same image instance will be
* returned. User should use {@link #isImageNew()} method to distinguish if
* returned image is not the same as the previous one. <br>
* The background thread uses implementation of DelayCalculator interface to
* calculate delay between two image fetching. Custom implementation may be
* specified as parameter of this method. If the non-blocking mode is
* enabled and no DelayCalculator is specified, DefaultDelayCalculator will
* be used.
* In non-blocking mode, there is a special thread running in the background which constantly
* fetch new images and cache them internally for further use. This cached instance is returned
* every time when user request new image. Because of that it can be used when timeing is very
* important, because all users calls for new image do not have to wait on device response. By
* using this mode user should be aware of the fact that in some cases, when two consecutive
* calls to get new image are executed more often than webcam device can serve them, the same
* image instance will be returned. User should use {@link #isImageNew()} method to distinguish
* if returned image is not the same as the previous one. <br>
* The background thread uses implementation of DelayCalculator interface to calculate delay
* between two image fetching. Custom implementation may be specified as parameter of this
* method. If the non-blocking mode is enabled and no DelayCalculator is specified,
* DefaultDelayCalculator will be used.
*
* @param async true for non-blocking mode, false for blocking
* @param delayCalculator responsible for calculating delay between two
* image fetching in non-blocking mode; It's ignored in blocking
* mode.
* @param delayCalculator responsible for calculating delay between two image fetching in
* non-blocking mode; It's ignored in blocking mode.
* @return True if webcam has been open
* @throws WebcamException when something went wrong
*/
Expand Down Expand Up @@ -724,12 +717,25 @@ public ByteBuffer getImageBytes() {
assert driver != null;
assert device != null;

long t1 = 0;
long t2 = 0;

// some devices can support direct image buffers, and for those call
// processor task, and for those which does not support direct image
// buffers, just convert image to RGB byte array

if (device instanceof BufferAccess) {
return new WebcamGetBufferTask(driver, device).getBuffer();
t1 = System.currentTimeMillis();
try {
return new WebcamGetBufferTask(driver, device).getBuffer();
} finally {
t2 = System.currentTimeMillis();
if (device instanceof WebcamDevice.FPSSource) {
fps = ((WebcamDevice.FPSSource) device).getFPS();
} else {
fps = (4 * fps + 1000 / (t2 - t1 + 1)) / 5;
}
}
} else {
throw new IllegalStateException(String.format("Driver %s does not support buffer access", driver.getClass().getName()));
}
Expand All @@ -755,21 +761,34 @@ public void getImageBytes(ByteBuffer target) {
assert driver != null;
assert device != null;

long t1 = 0;
long t2 = 0;

// some devices can support direct image buffers, and for those call
// processor task, and for those which does not support direct image
// buffers, just convert image to RGB byte array

if (device instanceof BufferAccess) {
new WebcamReadBufferTask(driver, device, target).readBuffer();
t1 = System.currentTimeMillis();
try {
new WebcamReadBufferTask(driver, device, target).readBuffer();
} finally {
t2 = System.currentTimeMillis();
if (device instanceof WebcamDevice.FPSSource) {
fps = ((WebcamDevice.FPSSource) device).getFPS();
} else {
fps = (4 * fps + 1000 / (t2 - t1 + 1)) / 5;
}
}
} else {
throw new IllegalStateException(String.format("Driver %s does not support buffer access", driver.getClass().getName()));
}
}

/**
* If the underlying device implements Configurable interface, specified
* parameters are passed to it. May be called before the open method or
* later in dependence of the device implementation.
* If the underlying device implements Configurable interface, specified parameters are passed
* to it. May be called before the open method or later in dependence of the device
* implementation.
*
* @param parameters - Map of parameters changing device defaults
* @see Configurable
Expand All @@ -782,7 +801,7 @@ public void setParameters(Map<String, ?> parameters) {
LOG.debug("Webcam device {} is not configurable", device);
}
}

/**
* Is webcam ready to be read.
*
Expand Down Expand Up @@ -1253,9 +1272,8 @@ public WebcamLock getLock() {
}

/**
* Shutdown webcam framework. This method should be used <b>ONLY</b> when you
* are exiting JVM, but please <b>do not invoke it</b> if you really don't
* need to.
* Shutdown webcam framework. This method should be used <b>ONLY</b> when you are exiting JVM,
* but please <b>do not invoke it</b> if you really don't need to.
*/
protected static void shutdown() {

Expand All @@ -1270,9 +1288,9 @@ protected static void shutdown() {
}

/**
* Return webcam with given name or null if no device with given name has
* been found. Please note that specific webcam name may depend on the order
* it was connected to the USB port (e.g. /dev/video0 vs /dev/video1).
* Return webcam with given name or null if no device with given name has been found. Please
* note that specific webcam name may depend on the order it was connected to the USB port (e.g.
* /dev/video0 vs /dev/video1).
*
* @param name the webcam name
* @return Webcam with given name or null if not found
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,39 @@ public enum DrawMode {
FIT,
}

/**
* This interface can be used to supply {@link BufferedImage} to {@link WebcamPanel}.
*
* @author Bartosz Firyn (sarxos)
*/
public interface ImageSupplier {

/**
* @return {@link BufferedImage} to be displayed in {@link WebcamPanel}
*/
public BufferedImage get();
}

/**
* Default implementation of {@link ImageSupplier} used in {@link WebcamPanel}. It invokes
* {@link Webcam#getImage()} and return {@link BufferedImage}.
*
* @author Bartosz Firyn (sarxos)
*/
private static class DefaultImageSupplier implements ImageSupplier {

private final Webcam webcam;

public DefaultImageSupplier(Webcam webcam) {
this.webcam = webcam;
}

@Override
public BufferedImage get() {
return webcam.getImage();
}
}

/**
* Interface of the painter used to draw image in panel.
*
Expand Down Expand Up @@ -156,9 +189,8 @@ public void paintPanel(WebcamPanel owner, Graphics2D g2) {
g2.drawLine(0, 0, getWidth(), getHeight());
g2.drawLine(0, getHeight(), getWidth(), 0);


String str;

final String strInitDevice = rb.getString("INITIALIZING_DEVICE");
final String strNoImage = rb.getString("NO_IMAGE");
final String strDeviceError = rb.getString("DEVICE_ERROR");
Expand Down Expand Up @@ -562,7 +594,7 @@ private void update() {

// get new image from webcam

BufferedImage tmp = webcam.getImage();
BufferedImage tmp = supplier.get();
boolean repaint = true;

if (tmp != null) {
Expand Down Expand Up @@ -626,6 +658,8 @@ private void update() {
*/
private final Webcam webcam;

private final ImageSupplier supplier;

/**
* Repainter is used to fetch images from camera and force panel repaint when image is ready.
*/
Expand Down Expand Up @@ -715,6 +749,10 @@ public WebcamPanel(Webcam webcam, boolean start) {
* @see WebcamPanel#setFillArea(boolean)
*/
public WebcamPanel(Webcam webcam, Dimension size, boolean start) {
this(webcam, size, start, new DefaultImageSupplier(webcam));
}

public WebcamPanel(Webcam webcam, Dimension size, boolean start, ImageSupplier supplier) {

if (webcam == null) {
throw new IllegalArgumentException(String.format("Webcam argument in %s constructor cannot be null!", getClass().getSimpleName()));
Expand All @@ -723,6 +761,7 @@ public WebcamPanel(Webcam webcam, Dimension size, boolean start) {
this.defaultSize = size;
this.webcam = webcam;
this.updater = new ImageUpdater();
this.supplier = supplier;
this.rb = WebcamUtils.loadRB(WebcamPanel.class, getLocale());

setDoubleBuffered(true);
Expand Down Expand Up @@ -1178,4 +1217,11 @@ public void setMirrored(boolean mirrored) {
public Webcam getWebcam() {
return webcam;
}

/**
* @return {@link BufferedImage} displayed on {@link WebcamPanel}
*/
public BufferedImage getImage() {
return image;
}
}

0 comments on commit 727db46

Please sign in to comment.