2

I am working with OpenCV in java, but I don't understand part of a class that loads pictures in java:

public class ImageProcessor {
  public BufferedImage toBufferedImage(Mat matrix){
    int type = BufferedImage.TYPE_BYTE_GRAY;
    if ( matrix.channels() > 1 ) {
        type = BufferedImage.TYPE_3BYTE_BGR;
    }
    int bufferSize = matrix.channels()*matrix.cols()*matrix.rows();
    byte [] buffer = new byte[bufferSize];
    matrix.get(0,0,buffer); // get all the pixels
    BufferedImage image = new BufferedImage(matrix.cols(),matrix.rows(),type);                    
    final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
    System.arraycopy(buffer, 0, targetPixels, 0, buffer.length);  
    return image;
  }

Main class sends a Mat object to this class.

The result sends BufferedImage but I don't understand targetPixels because this class doesn't use it somewhere else. But whenever I comment targetPixels and System.arraycopy, result shows black picture.

I want to know what's targetPixels - and what does it do?

GhostCat
  • 137,827
  • 25
  • 176
  • 248
hadis
  • 87
  • 7
  • Slightly off-topic: It seems you could optimize the code a lot, by not creating the `buffer` array and skip the `arraycopy`, and instead invoke `matrix.get(0, 0, targetPixels)` directly. Should have exactly the same effect, but eliminates a potential large array allocation and array copy. – Harald K Apr 05 '17 at 07:29

3 Answers3

2

The point is less about that array, but the methods that get you there.

You start here: getRaster(). That is supposed to return a WritableRaster ... and so on.

That class is using from getDataBuffer() from the Raster class; and there we find:

A class representing a rectangular array of pixels. A Raster encapsulates a DataBuffer that stores the sample values and a SampleModel that describes how to locate a given sample value in a DataBuffer.

What happens in essence here: that Image object, in the end has an array of bytes that are supposed to contain certain information.

That assignment:

final byte[] targetPixels = ...

retrieves a reference to that internal data; and then arrayCopy() is used to copy data into that array.

For the record: that doesn't look like a good approach - as it only works when this copy action really affects the internals of that Image object. But what if that last call getData() would create a copy of the internal data?

In other words: this code tries to gain direct access to internal data of some object; and then manipulate that internal data.

Even if that works today, it is not robust; and might break easily in the future. The other thing: note that this code does a unconditional cast (DataBufferByte). That code throws a RuntimeException if the the buffer doesn't have exactly that type.

Probably that is "all fine"; since it is all related to "AWT" classes which probably exist for ages; and will not change at all any more. But as said; this code has various potential issues.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • *Even if that works today, it is not robust; and might break easily in the future.* No, it won't. This is well-known, documented behavior, and lots of code depends on this. :-) It **is** possible to do it the other way around though (ie. start with the array, create a `DataBuffer` around it, then wrap it in a `WriteableRaster` and finally create a `BufferedImage` from that and a matching `ColorModel`). – Harald K Apr 05 '17 at 07:16
  • Another thing: While probably not obvious, there's a direct relationship between the `BufferedImage.TYPE_BYTE_*` types and `DataBufferByte`. So, the cast is safe, and will always succeed, given the type is always either `TYPE_BYTE_GRAY` or `TYPE_3BYTE_BGR`. – Harald K Apr 05 '17 at 07:22
0

targetPixels is the main image data (i.e. the pixels) of your new image. The actual image is created when the pixeldata is copied from buffer into targetPixels.

Steve Smith
  • 2,244
  • 2
  • 18
  • 22
0

targetPixels is the array of bytes from your newly created BufferedImage, those bytes are empty thus you need to copy the bytes from the buffer to it with System.arraycopy.. :)