Skip to content

Commit a113333

Browse files
committed
Add capture method to CaptureStream to retrieve frames as BufferedImage
1 parent 9d6600c commit a113333

3 files changed

Lines changed: 47 additions & 4 deletions

File tree

src/main/java/io/github/doblon8/openpnp/capture/CaptureDevice.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public CaptureStream openStream(CaptureFormat format) {
4141
if (streamId == -1) {
4242
throw new CaptureException("Error opening capture stream: " + streamId);
4343
}
44-
return new CaptureStream(context, streamId);
44+
return new CaptureStream(context, streamId, format);
4545
}
4646

4747
/**

src/main/java/io/github/doblon8/openpnp/capture/CaptureStream.java

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,57 @@
11
package io.github.doblon8.openpnp.capture;
22

3-
import static io.github.doblon8.openpnp.capture.bindings.openpnp_capture.Cap_closeStream;
4-
import static io.github.doblon8.openpnp.capture.bindings.openpnp_capture.Cap_isOpenStream;
3+
import java.awt.image.BufferedImage;
4+
import java.lang.foreign.Arena;
5+
import java.lang.foreign.MemorySegment;
6+
import java.lang.foreign.ValueLayout;
7+
8+
import static io.github.doblon8.openpnp.capture.bindings.openpnp_capture.*;
59

610
public class CaptureStream implements AutoCloseable {
711
private final CaptureContext context;
812
private final int id;
13+
private final CaptureFormat format;
914

10-
public CaptureStream(CaptureContext context, int id) {
15+
public CaptureStream(CaptureContext context, int id, CaptureFormat format) {
1116
this.context = context;
1217
this.id = id;
18+
this.format = format;
19+
}
20+
21+
/**
22+
* Capture a single frame from the stream and return it as a BufferedImage.
23+
*
24+
* @return a BufferedImage containing the captured frame.
25+
*/
26+
public BufferedImage capture() {
27+
int width = format.info().width();
28+
int height = format.info().height();
29+
byte[] bytes;
30+
31+
try (Arena arena = Arena.ofConfined()) {
32+
int numBytes = width * height * 3; // 3 bytes per pixel for each of R, G, and B channels
33+
MemorySegment bufferPointer = arena.allocate(ValueLayout.JAVA_BYTE, numBytes);
34+
35+
int result = Cap_captureFrame(context.getSegment(), id, bufferPointer, numBytes);
36+
CaptureResult captureResult = CaptureResult.values()[result];
37+
if (captureResult != CaptureResult.OK) {
38+
throw new CaptureException("Error capturing frame: " + result);
39+
}
40+
bytes = bufferPointer.toArray(ValueLayout.JAVA_BYTE);
41+
}
42+
43+
// Note: openpnp-capture documentation says that frames are always 24-bit RGB:
44+
// https://github.com/openpnp/openpnp-capture/blob/32a9bdd3e8e3a31b12cb6573e7c6076208421651/include/openpnp-capture.h#L192-L202
45+
// However, the original JNI-based openpnp-capture-java uses TYPE_3BYTE_BGR:
46+
// https://github.com/openpnp/openpnp-capture-java/blob/06504247a2688d0258f46cb4d7e451cb9715d241/src/main/java/org/openpnp/capture/CaptureStream.java#L102-L109
47+
// noting that the expected R/B swap did not occur, and that the comment's author thinks it's because
48+
// setDataElements converts to the BufferedImage's color model, but I don't think that is the case.
49+
// I just think that the openpnp-capture documentation might be wrong, as the openpnp-capture-java code seems to
50+
// have been working correctly in practice.
51+
// So I'll the same approach as openpnp-capture-java.
52+
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
53+
image.getRaster().setDataElements(0, 0, width, height, bytes);
54+
return image;
1355
}
1456

1557
/**

src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
module io.github.doblon8.openpnp.capture {
2+
requires java.desktop;
23
exports io.github.doblon8.openpnp.capture;
34
}

0 commit comments

Comments
 (0)