diff --git a/src/main/java/net/imagej/ops/image/ascii/DefaultASCII.java b/src/main/java/net/imagej/ops/image/ascii/DefaultASCII.java
index dcf247105..0e7a979ce 100644
--- a/src/main/java/net/imagej/ops/image/ascii/DefaultASCII.java
+++ b/src/main/java/net/imagej/ops/image/ascii/DefaultASCII.java
@@ -36,6 +36,7 @@
import net.imglib2.Cursor;
import net.imglib2.IterableInterval;
import net.imglib2.type.numeric.RealType;
+import net.imglib2.type.numeric.real.DoubleType;
import net.imglib2.util.Pair;
import org.scijava.plugin.Parameter;
@@ -46,7 +47,7 @@
*
* Only the first two dimensions of the image are considered.
*
- *
+ *
* @author Curtis Rueden
*/
@Plugin(type = Ops.Image.ASCII.class)
@@ -79,24 +80,28 @@ public String calculate(final IterableInterval input) {
if (min == null) min = minMax.getA();
if (max == null) max = minMax.getB();
}
- return ascii(input, min, max);
+
+ DoubleType minSource = new DoubleType(min.getRealDouble());
+ DoubleType maxSource = new DoubleType(max.getRealDouble());
+ DoubleType minTarget = new DoubleType(0);
+ DoubleType maxTarget = new DoubleType(CHARS.length());
+
+ IterableInterval converted = ops().convert().float64(input);
+ IterableInterval normalized = ops().image().normalize(converted,
+ minSource, maxSource, minTarget, maxTarget);
+
+ return ascii(normalized);
}
// -- Utility methods --
- public static > String ascii(
- final IterableInterval image, final T min, final T max)
- {
+ public static String ascii(final IterableInterval image) {
final long dim0 = image.dimension(0);
final long dim1 = image.dimension(1);
- // TODO: Check bounds.
+
final int w = (int) (dim0 + 1);
final int h = (int) dim1;
- // span = max - min
- final T span = max.copy();
- span.sub(min);
-
// allocate ASCII character array
final char[] c = new char[w * h];
for (int y = 1; y <= h; y++) {
@@ -104,22 +109,21 @@ public static > String ascii(
}
// loop over all available positions
- final Cursor cursor = image.localizingCursor();
+ final Cursor cursor = image.localizingCursor();
final int[] pos = new int[image.numDimensions()];
- final T tmp = image.firstElement().copy();
while (cursor.hasNext()) {
cursor.fwd();
cursor.localize(pos);
final int index = w * pos[1] + pos[0];
- // normalized = (value - min) / (max - min)
- tmp.set(cursor.get());
- tmp.sub(min);
- final double normalized = tmp.getRealDouble() / span.getRealDouble();
-
- final int charLen = CHARS.length();
- final int charIndex = (int) (charLen * normalized);
- c[index] = CHARS.charAt(charIndex < charLen ? charIndex : charLen - 1);
+ // grab the value from the normalized image, convert it to an ASCII char.
+ // N.B. if the original value was at the max for the type range it will be
+ // equal to the length of the char array after normalization. Thus to
+ // prevent an exception when converting to ASCII we subtract one when the
+ // normalized image value is equal to the length.
+ int val = (int) cursor.get().getRealDouble();
+ if (val == CHARS.length()) val--;
+ c[index] = CHARS.charAt(val);
}
return new String(c);
diff --git a/src/test/java/net/imagej/ops/image/ascii/ASCIITest.java b/src/test/java/net/imagej/ops/image/ascii/ASCIITest.java
index 3ceb8f2f2..467e54add 100644
--- a/src/test/java/net/imagej/ops/image/ascii/ASCIITest.java
+++ b/src/test/java/net/imagej/ops/image/ascii/ASCIITest.java
@@ -29,7 +29,7 @@
package net.imagej.ops.image.ascii;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
import net.imagej.ops.AbstractOpTest;
import net.imglib2.img.Img;
@@ -40,8 +40,9 @@
/**
* Tests {@link net.imagej.ops.Ops.Image.ASCII}.
- *
+ *
* @author Leon Yang
+ * @author Gabe Selzer
*/
public class ASCIITest extends AbstractOpTest {
@@ -63,9 +64,41 @@ public void testDefaultASCII() {
final String ascii = (String) ops.run(DefaultASCII.class, img);
for (int i = 0; i < len; i++) {
for (int j = 0; j < width; j++) {
- assertTrue(ascii.charAt(i * (width + 1) + j) == CHARS.charAt(i));
+ assertEquals(ascii.charAt(i * (width + 1) + j), CHARS.charAt(i));
}
- assertTrue(ascii.charAt(i * (width + 1) + width) == '\n');
+ assertEquals(ascii.charAt(i * (width + 1) + width), '\n');
}
}
+
+ @Test
+ public void testASCIIMinMax() {
+ // character set used in DefaultASCII, could be updated if necessary
+ final String CHARS = "#O*o+-,. ";
+ final int len = CHARS.length();
+ final int width = 10;
+ final byte[] array = new byte[width * len];
+ for (int i = 0; i < len; i++) {
+ for (int j = 0; j < width; j++) {
+ array[i * width + j] = (byte) (i * width + j);
+ }
+ }
+ final UnsignedByteType min = new UnsignedByteType(0);
+ final UnsignedByteType max = new UnsignedByteType(90);
+ final Img img = ArrayImgs.unsignedBytes(array, width,
+ len);
+ final String ascii = (String) ops.run(DefaultASCII.class, img, min, max);
+ for (int i = 0; i < len; i++) {
+ for (int j = 0; j < width; j++) {
+ assertEquals(ascii.charAt(i * (width + 1) + j), CHARS.charAt(i));
+ }
+ assertEquals(ascii.charAt(i * (width + 1) + width), '\n');
+ }
+
+ // make sure that the values of the min/max ascii are the same as the
+ // unclamped version (which will set the minimum and maximum to those of the
+ // data, which are the same as the ones that we set).
+ final String asciiUnclamped = (String) ops.run(DefaultASCII.class, img);
+ assertEquals(asciiUnclamped, ascii);
+
+ }
}