I am trying to draw an .obj file offscreen under Android.
The image should then be passed to OpenCV for further use as a matrix.
In 2D the whole thing already works without problems.
public class ObjDrawer extends PApplet {
PGraphics buffer;
PShape obj;
int width;
int height;
public ObjDrawer(int width, int height) {
this.width = width;
this.height = height;
setup();
}
#Override
public void setup() {
buffer = createGraphics(this.width, this.height, P3D);
//obj = loadShape("Wuerfel_40mm.obj");
noLoop();
}
public Mat testDraw() {
buffer.beginDraw();
buffer.background(255);
buffer.ellipse(200, 200, 100, 100);
//buffer.shape(obj);
buffer.endDraw();
return toMat(buffer.get());
}
Mat toMat(PImage image) {
//source: https://gist.github.com/Spaxe/3543f0005e9f8f3c4dc5
int w = image.width;
int h = image.height;
Mat mat = new Mat(h, w, CvType.CV_8UC4);
byte[] data8 = new byte[w * h * 4];
int[] data32 = new int[w * h];
arrayCopy(image.pixels, data32);
ByteBuffer bBuf = ByteBuffer.allocate(w * h * 4);
IntBuffer iBuf = bBuf.asIntBuffer();
iBuf.put(data32);
bBuf.get(data8);
mat.put(0, 0, data8);
return mat;
}
}
When using 3D, I now get the following error message:
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean processing.core.PGraphics.isGL()' on a null object reference
at processing.core.PApplet.makeGraphics(PApplet.java:1584)
at processing.core.PApplet.createGraphics(PApplet.java:1568)
at Analyzer.ObjDrawer.setup(ObjDrawer.java:52)
at Analyzer.ObjDrawer.<init>(ObjDrawer.java:45)
at com.quickbirdstudios.opencvexample.MainActivity$cvCameraViewListener$1.onCameraFrame(MainActivity.kt:75)
at org.opencv.android.CameraBridgeViewBase.deliverAndDrawFrame(CameraBridgeViewBase.java:392)
at org.opencv.android.JavaCameraView$CameraWorker.run(JavaCameraView.java:373)
at java.lang.Thread.run(Thread.java:920)
In my research I read about a3d, this is missing for me. Since all the stuff I found regarding a3d was about 10 years old: has anything changed in this regard or has it been replaced or do I need to include this separately?
I have included Processing as in the tutorial on https://android.processing.org/tutorials/android_studio/index.html
integrated. I use Android Mode for Processing 3 4.3.0, because the mode manager in Processing 4 is currently broken.
many thanks in advance!
Related
I need to store HCURSOR in BufferedImage with its real size and color.
I have found similar questions 1 and 2 which work fine with standard 32x32 cursor, but if I change color or size then BufferedImage becomes invalid, giving me a result like this:
Firstly, my problem was to get a real cursor size. But then I found the way to get it via JNA from the registry.
Then I need to save it to BufferedImage. I tried to use code snippets getImageByHICON() and getIcon() from the first link above, but there's an error somewhere -- the image is still incorrect or broken. Maybe I don't understand how to use it correctly because I am not much familiar with BufferedImage creation.
How can I save HCURSOR to BufferedImage if I have cursors real size and CURSORINFO?
Here is my full code:
import com.sun.jna.Memory;
import com.sun.jna.platform.win32.*;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
class CursorExtractor {
public static void main(String[] args) {
BufferedImage image = getCursor();
JLabel icon = new JLabel();
icon.setIcon(new ImageIcon(image));
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(icon);
frame.pack();
frame.setVisible(true);
Toolkit toolkit = Toolkit.getDefaultToolkit();
Point pointerPos = new Point(1, 1);
Cursor c = toolkit.createCustomCursor(image, pointerPos, "cursorname");
frame.setCursor(c);
}
public static BufferedImage getCursor() {
// Read an int (& 0xFFFFFFFFL for large unsigned int)
int baseSize = Advapi32Util.registryGetIntValue(
WinReg.HKEY_CURRENT_USER, "Control Panel\\Cursors", "CursorBaseSize");
final User32.CURSORINFO cursorinfo = new User32.CURSORINFO();
User32.INSTANCE.GetCursorInfo(cursorinfo);
WinDef.HCURSOR hCursor = cursorinfo.hCursor;
return getImageByHICON(baseSize, baseSize, hCursor);
}
public static BufferedImage getImageByHICON(final int width, final int height, final WinDef.HICON hicon) {
final WinGDI.ICONINFO iconinfo = new WinGDI.ICONINFO();
try {
// get icon information
if (!User32.INSTANCE.GetIconInfo(hicon, iconinfo)) {
return null;
}
final WinDef.HWND hwdn = new WinDef.HWND();
final WinDef.HDC dc = User32.INSTANCE.GetDC(hwdn);
if (dc == null) {
return null;
}
try {
final int nBits = width * height * 4;
// final BitmapInfo bmi = new BitmapInfo(1);
final Memory colorBitsMem = new Memory(nBits);
// // Extract the color bitmap
final WinGDI.BITMAPINFO bmi = new WinGDI.BITMAPINFO();
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = WinGDI.BI_RGB;
GDI32.INSTANCE.GetDIBits(dc, iconinfo.hbmColor, 0, height, colorBitsMem, bmi, WinGDI.DIB_RGB_COLORS);
// g32.GetDIBits(dc, iconinfo.hbmColor, 0, size, colorBitsMem,
// bmi,
// GDI32.DIB_RGB_COLORS);
final int[] colorBits = colorBitsMem.getIntArray(0, width * height);
final BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
bi.setRGB(0, 0, width, height, colorBits, 0, height);
return bi;
} finally {
com.sun.jna.platform.win32.User32.INSTANCE.ReleaseDC(hwdn, dc);
}
} finally {
User32.INSTANCE.DestroyIcon(new WinDef.HICON(hicon.getPointer()));
GDI32.INSTANCE.DeleteObject(iconinfo.hbmColor);
GDI32.INSTANCE.DeleteObject(iconinfo.hbmMask);
}
}
}
I originally answered this question suggesting that you use the GetSystemMetrics() function, using the constant SM_CXCURSOR (13) for the width of the cursor in pixels, and SM_CYCURSOR (14) for the height. The linked documentation states "The system cannot create cursors of other sizes."
But then I see you posted a similar question here, and stated that those values don't change from 32x32. What happens there, as noted in this answer, is that the cursor is still actually that size, but only the smaller image is displayed on the screen; the rest of the pixels are simply "invisible". The same appears to be true for "larger" images, in that internally the "icon" associated with the cursor is still the same 32x32 size, but the screen displays something else.
Interestingly, the icon when hovering over the Swing window is always 32x32. Your choice to use Cursor c = toolkit.createCustomCursor(image, pointerPos, "cursorname"); is scaling down the bitmap image to a new (smaller) cursor in the window. I can keep the default cursor with a simple:
Cursor c = Cursor.getDefaultCursor();
I made the following changes to your code to get an ugly pixellated version at the right size:
changed method arguments width and height to w and h: getImageByHICON(final int w, final int h, final WinDef.HICON hicon)
at the start of the try block, set int width = 32 and int height = 32.
after fetching the colorBitsMem from GetDIBits() inserted the following:
final int[] colorBitsBase = colorBitsMem.getIntArray(0, width * height);
final int[] colorBits = new int[w * h];
for (int row = 0; row < h; row++) {
for (int col = 0; col < w; col++) {
int r = row * 32 / h;
int c = col * 32 / w;
colorBits[row * w + col] = colorBitsBase[r * 32 + c];
}
}
So with a 64x64 system icon, I see this in the swing window:
That size matches my mouse cursor. The pixels, notsomuch.
Another option, inspired by this answer is to use a better bitmap scaling than my simple integer math with pixels. In your getCursor() method, do:
BufferedImage before = getImageByHICON(32, 32, hCursor);
int w = before.getWidth();
int h = before.getHeight();
BufferedImage after = new BufferedImage(baseSize, baseSize, BufferedImage.TYPE_INT_ARGB);
AffineTransform at = new AffineTransform();
at.scale(baseSize / 32d, baseSize / 32d);
AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
after = scaleOp.filter(before, after);
return after;
Which is giving me this:
Yet another option, in your getCursor() class is CopyImage().
WinDef.HCURSOR hCursor = cursorinfo.hCursor;
HANDLE foo = User32.INSTANCE.CopyImage(hCursor, 2, baseSize, baseSize, 0x00004000);
return getImageByHICON(baseSize, baseSize, new WinDef.HCURSOR(foo.getPointer()));
Gives this:
public class Loader {
public static byte[] loadImage(String path) {
Image image;
try {
image = new Image(new FileInputStream(path));
int width = (int) image.getWidth();
int height = (int) image.getHeight();
byte[] data = new byte[width * height * 4];
image.getPixelReader().getPixels(0, 0, width, height, PixelFormat.getByteBgraPreInstance(), data, 0, width * 4);
return data;
}catch(IOException e) {
e.printStackTrace();
System.exit(-1);
}
return null;
}
}
#Override
public void render(GraphicsContext gc) {
gc.clearRect(0, 0, width, height);
gc.getPixelWriter().setPixels(x++, 0, 819 ,720, PixelFormat.getByteBgraPreInstance(), data, 0, 819*4);
gc.getPixelWriter().setPixels(400, 0,819 ,720, PixelFormat.getByteBgraPreInstance(), data, 0, 819*4);
}
Hello, at the moment i have a Problem with the PixelWriter/PixelReader from JavaFx. I try to read the Pixel from an Image and store it in a Buffer, after that I want to render it to the Screen. But the Image contains now no Alpha Value, so there are nor transparent Pixels. I searched for a few hours on the Internet but I can't find an answer. Maybe there is a problem with the Format.
Thanks in advance.
I haven't tried it (because you did not provide a complete and executable example) but I think your problem is that you use a pixel format with pre-multiplied alpha. Try again with one of the simple rgb formats without pre-multiplication.
I've got a byte array storing 16-bit pixel data from an already-deconstructed DICOM file. What I need to do now is convert/export that pixel data somehow into a TIFF file format. I'm using the imageio-tiff-3.3.2.jar plugin to handle the tiff conversion/header data. But now I need to pack that image data array into a BufferedImage of the original image dimensions so it can be exported to TIFF. But it seems that BufferedImage doesn't support 16-bit images. Is there a way around this problem, such as an external library? Is there another way I can pack that image data into a TIFF image of the original DICOM dimensions? Keep in mind, this process has to be completely lossless. I've looked around and tried out some things for the last few days, but so far nothing has worked for me.
Let me know if you have any questions or if there's anything I can do to clear up any confusion.
EDIT: Intended and Current image
Given your input data of a raw byte array, containing unsigned 16 bit image data, here's two ways to create a BufferedImage.
The first one will be slower, as it involves copying the byte array into a short array. It will also need twice the amount of memory. The upside is that it creates a standard TYPE_USHORT_GRAY BufferedImage, which may be faster to display and may be more compatible.
private static BufferedImage createCopyUsingByteBuffer(int w, int h, byte[] rawBytes) {
short[] rawShorts = new short[rawBytes.length / 2];
ByteBuffer.wrap(rawBytes)
// .order(ByteOrder.LITTLE_ENDIAN) // Depending on the data's endianness
.asShortBuffer()
.get(rawShorts);
DataBuffer dataBuffer = new DataBufferUShort(rawShorts, rawShorts.length);
int stride = 1;
WritableRaster raster = Raster.createInterleavedRaster(dataBuffer, w, h, w * stride, stride, new int[] {0}, null);
ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);
return new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
}
A variant that is much faster (previous version takes 4-5x more time) to create, but results in a TYPE_CUSTOM image, that might be slower to display (it does seem to perform reasonable though, in my tests). It's much faster, and uses very little extra memory, as it does no copying/conversion of the input data at creation time.
Instead, it uses a custom sample model, that has DataBuffer.TYPE_USHORT as transfer type, but uses DataBufferByte as data buffer.
private static BufferedImage createNoCopy(int w, int h, byte[] rawBytes) {
DataBuffer dataBuffer = new DataBufferByte(rawBytes, rawBytes.length);
int stride = 2;
SampleModel sampleModel = new MyComponentSampleModel(w, h, stride);
WritableRaster raster = Raster.createWritableRaster(sampleModel, dataBuffer, null);
ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);
return new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
}
private static class MyComponentSampleModel extends ComponentSampleModel {
public MyComponentSampleModel(int w, int h, int stride) {
super(DataBuffer.TYPE_USHORT, w, h, stride, w * stride, new int[] {0});
}
#Override
public Object getDataElements(int x, int y, Object obj, DataBuffer data) {
if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
throw new ArrayIndexOutOfBoundsException("Coordinate out of bounds!");
}
// Simplified, as we only support TYPE_USHORT
int numDataElems = getNumDataElements();
int pixelOffset = y * scanlineStride + x * pixelStride;
short[] sdata;
if (obj == null) {
sdata = new short[numDataElems];
}
else {
sdata = (short[]) obj;
}
for (int i = 0; i < numDataElems; i++) {
sdata[i] = (short) (data.getElem(0, pixelOffset) << 8 | data.getElem(0, pixelOffset + 1));
// If little endian, swap the element order, like this:
// sdata[i] = (short) (data.getElem(0, pixelOffset + 1) << 8 | data.getElem(0, pixelOffset));
}
return sdata;
}
}
If your image looks strange after this conversion, try flipping the endianness, as commented in the code.
And finally, some code to exercise the above:
public static void main(String[] args) {
int w = 1760;
int h = 2140;
byte[] rawBytes = new byte[w * h * 2]; // This will be your input array, 7532800 bytes
ShortBuffer buffer = ByteBuffer.wrap(rawBytes)
// .order(ByteOrder.LITTLE_ENDIAN) // Try swapping the byte order to see sharp edges
.asShortBuffer();
// Let's make a simple gradient, from black UL to white BR
int max = 65535; // Unsigned short max value
for (int y = 0; y < h; y++) {
double v = max * y / (double) h;
for (int x = 0; x < w; x++) {
buffer.put((short) Math.round((v + max * x / (double) w) / 2.0));
}
}
final BufferedImage image = createNoCopy(w, h, rawBytes);
// final BufferedImage image = createCopyUsingByteBuffer(w, h, rawBytes);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(new JScrollPane(new JLabel(new ImageIcon(image))));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
Here's what the output should look like (scaled down to 1/10th):
The easiest thing to do is to create a BufferedImage of type TYPE_USHORT_GRAY, which is type to use for 16 bits encoding.
public BufferedImage Convert(short[] array, final int width, final int height)
{
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_GRAY) ;
short[] sb = ((DataBufferUShort) image.getRaster().getDataBuffer()).getData() ;
System.arraycopy(array, 0, sb, 0, array.length) ;
return image ;
}
Then you can use Java.imageio to save your image as a TIFF or a PNG. I think that the Twelve Monkey Project allows a better TIFF support for imageio, but you have to check first.
[EDIT] In your case because you deal with huge DICOM images that cannot be stored into a regular BufferedImage, you have to create your own type using the Unsafe class to allocated the DataBuffer.
Create a new class DataBufferLongShort that will allocate the needed array/DataBuffer using the Unsafe class. Then you can use Long indexes instead of Integer
Create a new class DataBuffer that extends the classical DataBuffer in order to add a type TYPE_LONG_USHORT
Then you can create the ColorModel with the new DataBuffer.
I am beginner in programming i show this class
http://www.magicandlove.com/blog/2014/03/06/people-detection-in-processing-with-opencv/
& i want to run it in net beans but main method is missed & some errors appears like can not find PImage also , size ,background
can you help me how to run it & what should classes must be have.
PImage small;
HOGDescriptor hog;
byte [] bArray;
int [] iArray;
int pixCnt1, pixCnt2;
int w, h;
float ratio;
void setup() {
size(640, 480);
ratio = 0.5;
w = int(width*ratio);
h = int(height*ratio);
background(0);
// Define and initialise the default capture device.
cap = new Capture(this, width, height);
cap.start();
// Load the OpenCV native library.
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
println(Core.VERSION);
pixCnt1 = w*h*4;
pixCnt2 = w*h;
bArray = new byte[pixCnt1];
iArray = new int[pixCnt2];
small = createImage(w, h, ARGB);
hog = new HOGDescriptor();
hog.setSVMDetector(HOGDescriptor.getDefaultPeopleDetector());
noFill();
stroke(255, 255, 0);
}
void draw() {
if (cap.available()) {
cap.read();
}
else {
return;
}
image(cap, 0, 0);
You need to copy everything from that page including the import, i'm thinking those objects you're trying to create are from the things that were suppose to be imported so that might solve your problem with that.
I've got some code that initializes OpenGL to render to a java.awt.Canvas.
The problem is, I can't figure out how I can get the buffer of the canvas and turn it into a BufferedImage.
I've tried overriding getGraphics(), cloning the Raster, and replacing the CanvasPeer with a custom one.
I'm guessing OpenGL doesn't use java graphics in any way then, so how can I get OpenGL's buffer and convert it into a BufferedImage?
I am using LWJGL's code for setting parent:
Display.setParent(display_parent);
Display.create();
You need to copy data from OpenGL buffer. I was using this method:
FloatBuffer grabScreen(GL gl)
{
int w = SCREENWITDH;
int h = SCREENHEIGHT;
FloatBuffer bufor = FloatBuffer.allocate(w*h*4); // 4 = rgba
gl.glReadBuffer(GL.GL_FRONT);
gl.glReadPixels(0, 0, w, h, GL.GL_RGBA, GL.GL_FLOAT, bufor); //Copy the image to the array imageData
return bufor;
}
You need to use something similar according to your OpenGL wrapper. This is JOGL example.
And here for LWJGL wrapper:
private static synchronized byte[] grabScreen()
{
int w = screenWidth;
int h = screenHeight;
ByteBuffer bufor = BufferUtils.createByteBuffer(w * h * 3);
GL11.glReadPixels(0, 0, w, h, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, bufor); //Copy the image to the array imageData
byte[] byteimg = new byte[w * h * 3];
bufor.get(byteimg, 0, byteimg.length);
return byteimg;
}
EDIT
This may be useful also (it's not fully mine, should be tuned too):
BufferedImage toImage(byte[] data, int w, int h)
{
if (data.length == 0)
return null;
DataBuffer buffer = new DataBufferByte(data, w * h);
int pixelStride = 3; //assuming r, g, b, skip, r, g, b, skip...
int scanlineStride = 3 * w; //no extra padding
int[] bandOffsets = { 0, 1, 2 }; //r, g, b
WritableRaster raster = Raster.createInterleavedRaster(buffer, w, h, scanlineStride, pixelStride, bandOffsets,
null);
ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
boolean hasAlpha = false;
boolean isAlphaPremultiplied = true;
int transparency = Transparency.TRANSLUCENT;
int transferType = DataBuffer.TYPE_BYTE;
ColorModel colorModel = new ComponentColorModel(colorSpace, hasAlpha, isAlphaPremultiplied, transparency,
transferType);
BufferedImage image = new BufferedImage(colorModel, raster, isAlphaPremultiplied, null);
AffineTransform flip;
AffineTransformOp op;
flip = AffineTransform.getScaleInstance(1, -1);
flip.translate(0, -image.getHeight());
op = new AffineTransformOp(flip, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
image = op.filter(image, null);
return image;
}
I don't think this is possible for your situation, and here's why:
LWJGL doesn't draw directly to the canvas (at least not in Windows). The canvas is only used to obtain a window handle to provide as the parent window to OpenGL. As such, the canvas is never directly drawn to. To capture the contents, you'll probably have to resort to a screen capture.