Returning arrays as objects

java
#1

Hi, Sorry for this probably extremely basic question, but it is driving me nuts…
Trying to follow Albert Cardona’s old plugin guidelines I call an external (xy) plugin’s exec method that should return one int variable and two int arrays as objects:

This is what the xy plugin returns:

return new Object[ ]{ count, xstart, ystart};

(count is the int value which is the size of the arrays, xstart and y ystart are the int arrays.

Then I get the result when calling the xy plugin with:

Object[] result = xy.exec(imp1, true, false);

and I can get the “count” value with:

int regions = (Integer) result[0];

but I am struggling to get the two object arrays into int arrays.

The following does not produce a compiling error (Java 1.6)

int [] xs = new int [regions];
int [] ys = new int [regions];
xs =  (int[]) result[1];
ys =  (int[]) result[2];

but it generates this runtime error:

java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [I

My question is how do I cast the arrays returned in result[1] and result[2] into the int arrays xs and ys?
I am sure I am missing something very obvious here, but cannot see what it is.
Many thanks.

Gabriel

0 Likes

#2

Hi @gabriel,

What you’re doing should work, e.g. the code below produces what you would expect.

Do you know where the source for that plugin lives?

John

P.s. One comment: you don’t actually need to make new int[]'s
I.e.

int[] xs = (int[]) result[1];
int[] ys = (int[]) result[2];

should work.

0 Likes

#3

Thanks @bogovicj that is indeed puzzling…
I tried your suggestion in java 1.8 and I still get:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [I

I can get the first object, but not the other two arrays.

Yes, the source code lives where the other plugin lives too.

0 Likes

#4

@gabriel

What plugin is it?

0 Likes

#5

Something I am testing where I want to return only the XStart YStart coordinates of every region in a binary image:
Here is the exec method:

	 public Object[] exec(ImagePlus imp1, boolean whiteParticles, boolean connect4) {

		int width  = imp1.getWidth();
		int height = imp1.getHeight();
		int size = width * height;
		ImageProcessor  ip3;
		ImagePlus imp3;

		int x, y, offset, pointer, labelColour=0;
		byte foreground = (byte) 255;//, background = 0;

		IJ.showStatus("Labelling...");

 		imp3 = new ImagePlus("_labeller",imp1.getProcessor().duplicate());
		ip3 = imp3.getProcessor();

		if (!whiteParticles) 
			ip3.invert();

		byte [] pixel =(byte []) ip3.getPixels();
		Vector vx = new Vector();
		Vector vy = new Vector();
		
		
		FloodFiller ff = new FloodFiller(ip3);
		ip3.setColor(0);
		if (connect4){
			for(y=0; y<height; y++) {
				offset=y*width;
				for(x=0; x<width; x++){
					pointer = offset + x;
					if ( pixel[pointer]  == foreground) {
						ff.fill(x, y);
						vx.add(new Integer(x));
						vy.add(new Integer(y));
						labelColour++;
					}
				}
			}
		}
		else { //8
			for(y=0; y<height; y++) {
				offset=y*width;
				for(x=0; x<width; x++){
					pointer = offset + x;
					if ( pixel[pointer]  == foreground) {
						vx.add(new Integer(x));
						vy.add(new Integer(y));
						labelColour++;
						ff.fill8(x, y);
					}
				}
			}
		}
		Object xstart = vx.toArray();
		Object ystart = vy.toArray();
		imp3.close();
		
		return new Object[]{labelColour, xstart, ystart};
        }
}
0 Likes

#6

Vector.toArray returns an Object[] array, not an int[] array, which are two different classes. If you wonder why Object[] cannot be cast to int[], I suggest you read up on the difference between primitive types and objects in Java, maybe these are good starting poitns


https://en.wikibooks.org/wiki/Java_Programming/Primitive_Types
https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html

Also quoting the Vector doc:

Vector is synchronized. If a thread-safe implementation is not needed, it is recommended to use ArrayList in place of Vector .

Since you are working with primitive types (int) and arrays thereof (int[]) I would not even recommend using an ArrayList but a primitive type collection like the trove TIntArrayList instead.

3 Likes

#7

@gabriel If you’re working with Java 8 or later and want to avoid the external dependency on Trove, you may convert your Vector or (preferably) ArrayList to int[] via a stream:

int[] b = a.stream().mapToInt(i -> i).toArray();

Otherwise, you can convert to Integer[] with:

Integer[] b = a.toArray(new Integer[0]);

Both of these still incur all the overhead of boxing/unboxing the primitives so are likely to be very inefficient compared to @hanslovsky’s suggestion, but this may not matter a lot if you don’t expect to be adding a lot of integers.

(Alternatively, you could of course store the values directly in a preallocated int[] array at the cost of extra code needed to keep a record of how much of the array is occupied and expanding its length as needed.)

A tiny additional point is that new Integer(i) is discouraged in favor of Integer.valueOf(i) in the javadocs.

1 Like

#8

Thank you all. You’ve given me a few ideas to try.

0 Likes

#9

Hi Gabriel,

since the issue seems to be solved already, just for the record as I did not read a clear solution:
int is not an Object, so it cant go into an Object[] array. You need an Integer Object, e.g.
new Integer(count).
Alternatively, have the integer ‘count’ in a third one-element array, and return as 2D array
new int[][] {new int[]{count}, xstart, ystart}
As a third option (maybe the cleanest), define a class containing the two int arrays and the integer number count. Note that the java.awt.polygon class could be used if count is never larger than the arr size.
– Michael

1 Like

#10

Thanks Michael. To give a bit more of background I was trying to get the XStart YStart coordinates of binary regions directly from an image in the quickest possible way (e.g. avoiding region labelling and avoiding writing to- and reading from- the Results Table).
So I was exploring to raster scan the binary image, then for each new found pixel record its coordinates (that is the XStart, YStart pixel), then flood fill it with the background colour (which deletes the region) and carry on scanning for the next region.

That seems to be about 3 to 4 times quicker than using the Particles8 plugin and the built-in Analyze Particles (run with only the “Record starts” option).
I wanted those coordinates to be returned in an pair of arrays directly (to avoid the Results table), but one does not know in advance how many regions an image has.
As mentioned by Pete, an int array would have to be resized accordingly and so I thought that a Vector or ArrayList would be convenient, but as mentioned earlier they are not as efficient.

Thanks again for all the suggestions.
Gabriel

1 Like