I am implementing a simple eye tracker, which requires fast screenshoting of what is happening on the screen simultaneously with capturing the video from webcam.
The thing is that the way of doing it with Robot, mentioned here: https://stackoverflow.com/questions/2475303/java-library-for-capturing-active-window-screenshot is extremely slow.
By the way, retrieving the video from a webcam works much faster and returns the byte array, which is very fast to be processed.
Does anybody know a faster solution? C++ libraries, which can be linked to Java for doing this may help as well.
Thank you!
UPDATE: Decided to switch to OpenCV, now looking for the way to make screenshot with it :)
The Print Screen button The easiest way to take a screenshot on Windows 10 or Windows 11 is with the Print Screen (PrtScn) key. To capture your entire screen, simply press PrtScn on the upper-right side of your keyboard. In Windows 10, the screenshot will be copied to your clipboard.
Answer: When we want to take multiple screenshots in Windows, we can use a combination of some keys on the keyboard. Press Ctrl key + Alt key together and the PrtScn key.
Here's a Windows-specific version using JNA that I am using in one of my projects.
I have found it to be an order-of-magnitude faster than Robot
, even with the native call overhead.
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferUShort;
import java.awt.image.DirectColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.W32API;
import com.sun.jna.win32.W32APIOptions;
public class JNAScreenShot {
public static BufferedImage getScreenshot(Rectangle bounds) {
W32API.HDC windowDC = GDI.GetDC(USER.GetDesktopWindow());
W32API.HBITMAP outputBitmap =
GDI.CreateCompatibleBitmap(windowDC,
bounds.width, bounds.height);
try {
W32API.HDC blitDC = GDI.CreateCompatibleDC(windowDC);
try {
W32API.HANDLE oldBitmap =
GDI.SelectObject(blitDC, outputBitmap);
try {
GDI.BitBlt(blitDC,
0, 0, bounds.width, bounds.height,
windowDC,
bounds.x, bounds.y,
GDI32.SRCCOPY);
} finally {
GDI.SelectObject(blitDC, oldBitmap);
}
GDI32.BITMAPINFO bi = new GDI32.BITMAPINFO(40);
bi.bmiHeader.biSize = 40;
boolean ok =
GDI.GetDIBits(blitDC, outputBitmap, 0, bounds.height,
(byte[]) null, bi, GDI32.DIB_RGB_COLORS);
if (ok) {
GDI32.BITMAPINFOHEADER bih = bi.bmiHeader;
bih.biHeight = - Math.abs(bih.biHeight);
bi.bmiHeader.biCompression = 0;
return bufferedImageFromBitmap(blitDC, outputBitmap, bi);
} else {
return null;
}
} finally {
GDI.DeleteObject(blitDC);
}
} finally {
GDI.DeleteObject(outputBitmap);
}
}
private static BufferedImage
bufferedImageFromBitmap(GDI32.HDC blitDC,
GDI32.HBITMAP outputBitmap,
GDI32.BITMAPINFO bi) {
GDI32.BITMAPINFOHEADER bih = bi.bmiHeader;
int height = Math.abs(bih.biHeight);
final ColorModel cm;
final DataBuffer buffer;
final WritableRaster raster;
int strideBits =
(bih.biWidth * bih.biBitCount);
int strideBytesAligned =
(((strideBits - 1) | 0x1F) + 1) >> 3;
final int strideElementsAligned;
switch (bih.biBitCount) {
case 16:
strideElementsAligned = strideBytesAligned / 2;
cm = new DirectColorModel(16, 0x7C00, 0x3E0, 0x1F);
buffer =
new DataBufferUShort(strideElementsAligned * height);
raster =
Raster.createPackedRaster(buffer,
bih.biWidth, height,
strideElementsAligned,
((DirectColorModel) cm).getMasks(),
null);
break;
case 32:
strideElementsAligned = strideBytesAligned / 4;
cm = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF);
buffer =
new DataBufferInt(strideElementsAligned * height);
raster =
Raster.createPackedRaster(buffer,
bih.biWidth, height,
strideElementsAligned,
((DirectColorModel) cm).getMasks(),
null);
break;
default:
throw new IllegalArgumentException("Unsupported bit count: " + bih.biBitCount);
}
final boolean ok;
switch (buffer.getDataType()) {
case DataBuffer.TYPE_INT:
{
int[] pixels = ((DataBufferInt) buffer).getData();
ok = GDI.GetDIBits(blitDC, outputBitmap, 0, raster.getHeight(), pixels, bi, 0);
}
break;
case DataBuffer.TYPE_USHORT:
{
short[] pixels = ((DataBufferUShort) buffer).getData();
ok = GDI.GetDIBits(blitDC, outputBitmap, 0, raster.getHeight(), pixels, bi, 0);
}
break;
default:
throw new AssertionError("Unexpected buffer element type: " + buffer.getDataType());
}
if (ok) {
return new BufferedImage(cm, raster, false, null);
} else {
return null;
}
}
private static final User32 USER = User32.INSTANCE;
private static final GDI32 GDI = GDI32.INSTANCE;
}
interface GDI32 extends com.sun.jna.platform.win32.GDI32 {
GDI32 INSTANCE =
(GDI32) Native.loadLibrary(GDI32.class);
boolean BitBlt(HDC hdcDest,
int nXDest,
int nYDest,
int nWidth,
int nHeight,
HDC hdcSrc,
int nXSrc,
int nYSrc,
int dwRop);
HDC GetDC(HWND hWnd);
boolean GetDIBits(HDC dc, HBITMAP bmp, int startScan, int scanLines,
byte[] pixels, BITMAPINFO bi, int usage);
boolean GetDIBits(HDC dc, HBITMAP bmp, int startScan, int scanLines,
short[] pixels, BITMAPINFO bi, int usage);
boolean GetDIBits(HDC dc, HBITMAP bmp, int startScan, int scanLines,
int[] pixels, BITMAPINFO bi, int usage);
int SRCCOPY = 0xCC0020;
}
interface User32 extends com.sun.jna.platform.win32.User32 {
User32 INSTANCE =
(User32) Native.loadLibrary(User32.class,
W32APIOptions.UNICODE_OPTIONS);
HWND GetDesktopWindow();
}
The robot.createScreenCapture(captureSize);
call takes about 20ms for me.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With