We closed this forum 18 June 2010. It has served us well since 2005 as the ALPHA forum did before it from 2002 to 2005. New discussions are ongoing at the new URL http://forum.processing.org. You'll need to sign up and get a new user account. We're sorry about that inconvenience, but we think it's better in the long run. The content on this forum will remain online.
IndexProgramming Questions & HelpPrograms › Fastest way of getting a "slice" of pixels...
Page Index Toggle Pages: 1
Fastest way of getting a "slice" of pixels... (Read 1061 times)
Fastest way of getting a "slice" of pixels...
Feb 4th, 2010, 8:30pm
 
Hi all, been away from processing and java for some time, and I need some advice.

I want to get horizontal and vertical slices of pixels from a scene. This needs to be as fast as possible because it will run in every frame after loadPixels().

For getting horizontal slices, I've come up with this; suppose my scene dimensions are 640x480 and I want to get the first row of pixels (between 0@0 and 639@0):

Code:

loadPixels();
int[] row = Arrays.copyOfRange(pixels, 0, 639);


My first question: Is there a faster / more efficient way of doing this?

And how should I go for getting vertical columns? Since the pixels array is sorted from left to right - top to bottom, I can't really use copyOfRange. Say I want to get the values into an array between pixels 0@0 and 0@479.

What would the most efficient way of filling an array with this column would be (other than populating it inside a for loop by hand)?

Thanks for any help!
Re: Fastest way of getting a "slice" of pixels...
Reply #1 - Feb 5th, 2010, 2:14am
 
copyOfRange: probably the most efficient way (and most readable), if we suppose those coding the Java API know their business... Hey, they could even do that with native code! (memcpy for example), although it is unlikely (most of Java is coded in Java).

Column: I see no other way than using a for loop. Which is probably, as I wrote, how it is done in copyOfRange anyway.
Re: Fastest way of getting a "slice" of pixels...
Reply #2 - Feb 5th, 2010, 8:06am
 
if you're processing multiple rows, and can reuse the same int[]row storage, then it'll almost certainly be faster to allocate it once yourself then use System.arraycopy instead

ottoh if you're just grabbing that one row there's prolly little difference
Re: Fastest way of getting a "slice" of pixels...
Reply #3 - Feb 6th, 2010, 2:22am
 
Thanks! Much appreciated.
Re: Fastest way of getting a "slice" of pixels...
Reply #4 - Feb 6th, 2010, 12:41pm
 
The answer might really depend on what you intend to do with these "slices".

Perhaps it is better to process each pixel as you read it, rather than grab them all at once and process them later.

Also, do you need to grab every slice, or only a couple of slices If only a few slices, you should not be too concerned about the speed - it should be pretty fast. I expect that there is significantly more to be gained in whatever the process is that you apply to the slices.

Last night I happened to be reading about Buffer classes (whilst inspecting Memo's MSAFluid demo with particles), and Buffers have the potential to give you a speed advantage.

I've never used them before, but the idea is that native I/O operations can be performed with them, as well as bulk transfers. The impression that I get is that you can create a ByteBuffer as your base storage, which can be "direct" (allows native I/O operations), then take an IntBuffer view of the ByteBuffer, which you could then use to read in your row of pixels[] integers (there isn't going to be an equivaent operation for grabbing columns, unfortunately!).

What you do with your buffer (of bytes / ints) after that is going to be the critical part!

From the ByteBuffer docs:
Quote:
Direct vs. non-direct buffers

A byte buffer is either direct or non-direct. Given a direct byte buffer, the Java virtual machine will make a best effort to perform native I/O operations directly upon it. That is, it will attempt to avoid copying the buffer's content to (or from) an intermediate buffer before (or after) each invocation of one of the underlying operating system's native I/O operations.

A direct byte buffer may be created by invoking the allocateDirect factory method of this class. The buffers returned by this method typically have somewhat higher allocation and deallocation costs than non-direct buffers. The contents of direct buffers may reside outside of the normal garbage-collected heap, and so their impact upon the memory footprint of an application might not be obvious. It is therefore recommended that direct buffers be allocated primarily for large, long-lived buffers that are subject to the underlying system's native I/O operations. In general it is best to allocate direct buffers only when they yield a measureable gain in program performance.

A direct byte buffer may also be created by mapping a region of a file directly into memory. An implementation of the Java platform may optionally support the creation of direct byte buffers from native code via JNI. If an instance of one of these kinds of buffers refers to an inaccessible region of memory then an attempt to access that region will not change the buffer's content and will cause an unspecified exception to be thrown either at the time of the access or at some later time.

Whether a byte buffer is direct or non-direct may be determined by invoking its isDirect method. This method is provided so that explicit buffer management can be done in performance-critical code.


*extended pause whilst I go tinker with something...*

Okay, I posted some demo code on OpenProcessing. It doesn't (yet) have a comparison to Arrays.copyOfRange(), nor System.arraycopy(), but at least shows how to get data into the buffers (if not how to exploit the I/O benefits of the buffers!).

Demo direct buffer code: spxlSliceOfPixels

-spxl
Re: Fastest way of getting a "slice" of pixels...
Reply #5 - Feb 6th, 2010, 2:15pm
 
A stripped-down version without debug/timing code, nor extraneous user input processing and so on:

Code:
/**
* spxlSliceOfPixels02
* 2010-02-07 by subpixel
* http://subpixels.com
*
* Demo using direct buffers for taking slices from the
* pixels[] array for further procesing.
*
* See Java docs on Buffers:
* - http://java.sun.com/j2se/1.5.0/docs/api/java/nio/Buffer.html
* - http://java.sun.com/j2se/1.5.0/docs/api/java/nio/ByteBuffer.html
* - http://java.sun.com/j2se/1.5.0/docs/api/java/nio/IntBuffer.html
*/

import java.nio.*;

static final int WIDTH = 640;
static final int HEIGHT = 480;

int totalPixels; // width * height
int maxDimension; // The larger of height and width

// Full buffer - as in full sreen buffer (all pixels)
ByteBuffer FBB;
IntBuffer FIB;

// Slice buffer - buffer containing a single row (or column) of pixels
ByteBuffer SBB;
IntBuffer SIB;

void setup()
{
size(WIDTH, HEIGHT, P3D);

totalPixels = width * height;
maxDimension = max(width, height);

// Magic "direct buffer" allocated OUTSITE of the heap,
// for the entire pixel array,
// and an IntBuffer "view" of the ByteBuffer; same data,
// Note: 4 bytes (A,R,G,B) per pixel
FBB = ByteBuffer.allocateDirect(totalPixels * 4);
FIB = FBB.asIntBuffer();

// Magic "direct buffer" allocated OUTSITE of the heap,
// for a slice of the pixels array (one row or column),
// and an IntBuffer "view" of the ByteBuffer; same data,
// Note: 4 bytes (A,R,G,B) per pixel
SBB = ByteBuffer.allocateDirect(maxDimension * 4);
SIB = SBB.asIntBuffer();

background(0);
}

void draw()
{
putStuffOnScreen();

loadPixels(); // Processing rule to do this before accessing pixels[]

int row = mouseY;
int column = mouseX;

processAllPixels();
processSingleRow(row);
processSingleColumn(column);
simpleProcessAllPixels();
}

void processAllPixels()
{
// Copy all pixels into the full buffer
FIB.clear(); // Set position = 0, limit = capacity, ready for filling
FIB.put(pixels); // Bulk put entire array into buffer
FIB.flip(); // Prepare for processing

// Do something with row slices
// NOTE: the "slice" created IS the data in
// the full buffer, not a copy of the data
{
IntBuffer FIBslice;
for (int i = 0; i < totalPixels; i += width)
{
FIB.position(i); // Position to start of row
FIBslice = FIB.slice();
FIBslice.limit(width); // Don't want to access beyond row's data
processSlice(FIBslice);
}
}

// Do something with column slices
// NOTE: processSingleColumn makes a COPY of the data, since is is
// in a different arrangement than appears in the full buffer
for (int column = 0; column < width; column++)
processSingleColumn(column);
}

void processSingleRow(int row)
{
// Copy single row of pixels into the slice buffer
SIB.clear(); // Reset position and limit ready for filling
SIB.put(pixels, row * width, width); // Bulk put one row into buffer
SIB.flip(); // Prepare for processing
processSlice(SIB);
}

void processSingleColumn(int column)
{
// Copy single column of pixels into the slice buffer
SIB.clear(); // Reset position and limit ready for filling

// Note that totalPixels isn't an "exact" limit here, but
// catches going past the last row perfectly well.
for (int i = column; i < totalPixels; i += width)
SIB.put(pixels[i]); // Put single pixel's int into buffer

SIB.flip(); // Prepare for processing
processSlice(SIB);
}

void processSlice(IntBuffer slice)
{
int pixel;

while (slice.hasRemaining())
{
pixel = slice.get();
processPixel(pixel);
}
}

void simpleProcessAllPixels()
{
// Process rows
for (int base = 0; base < totalPixels; base += width)
processSlice(pixels, base, width, 1);

// Process columns
for (int base = 0; base < width; base++)
processSlice(pixels, base, height, 1);
}

// Process a slice of n=elements items from pixelArray[], starting
// at {base} and moving in increments of {step}
void processSlice(int[] pixelArray, int base, int elements, int step)
{
final int end = base + elements * step;
for (int i = base; i < end; i += step)
processPixel(pixelArray[i]);
}

void processPixel(int pixel)
{
// Pixel processing code needs to go here!
}

void putStuffOnScreen()
{
background(0);

translate(width / 2, height / 2);
rotateZ(PI/6);
rotateX(PI/3);
rotateY(millis() * 0.0001);

stroke(255, 255, 0);
strokeWeight(3);
noFill();
sphereDetail(20);
sphere(250);
}

Re: Fastest way of getting a "slice" of pixels...
Reply #6 - Feb 6th, 2010, 5:13pm
 
Subpixel, thanks a lot. Very insightful and interesting/informative this was.

And you were right with your idea in the beginning, I decided to process the pixel info in place, rather than storing and processing later, and it is working fine. I'm not using every slice on screen so it wouldn't give a noticable performance penalty most probably, but I'm always interested in doing things clean and efficient so that when it really matters, I'll be ready. Smiley

Cheers!
Page Index Toggle Pages: 1