Skip to content

Commit 32b948d

Browse files
committed
RESOLVED - #419 Aggregation method Merge only supports 2D to 3D, not 3D to 4D
- Merge Image aggregation operator now supports nD images (however, it's most efficient in the 2D case)
1 parent f68f9af commit 32b948d

1 file changed

Lines changed: 116 additions & 64 deletions

File tree

org.knime.knip.base/src/org/knime/knip/base/data/aggregation/ImgMergeOperator.java

Lines changed: 116 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,11 @@
5252
import net.imagej.axis.CalibratedAxis;
5353
import net.imagej.axis.DefaultLinearAxis;
5454
import net.imagej.space.DefaultCalibratedSpace;
55+
import net.imglib2.Cursor;
5556
import net.imglib2.img.Img;
5657
import net.imglib2.img.ImgView;
5758
import net.imglib2.img.array.ArrayImg;
59+
import net.imglib2.img.array.ArrayImgFactory;
5860
import net.imglib2.img.basictypeaccess.array.ArrayDataAccess;
5961
import net.imglib2.img.basictypeaccess.array.ByteArray;
6062
import net.imglib2.img.basictypeaccess.array.DoubleArray;
@@ -74,6 +76,7 @@
7476
import net.imglib2.type.numeric.real.DoubleType;
7577
import net.imglib2.type.numeric.real.FloatType;
7678
import net.imglib2.util.Fraction;
79+
import net.imglib2.view.Views;
7780

7881
/**
7982
* Aggregation operator which merges images.
@@ -467,7 +470,7 @@ private static SettingsModelString createAxisLabelModel() {
467470

468471
private String m_axisLabel;
469472

470-
/* aggregated pixel data */
473+
/* aggregated pixel data (in case of a 2D input)*/
471474
private ArrayList<A> m_data = null;
472475

473476
// dialog components
@@ -484,6 +487,18 @@ private static SettingsModelString createAxisLabelModel() {
484487

485488
private RealTypeHandler<T, A, ADA> m_typeHandler;
486489

490+
/**
491+
* Required to determine the size of the result image for the nD case (n > 2)
492+
* and keep track of the current position in the result image
493+
*/
494+
private long m_rowCount;
495+
private long m_rowIdx;
496+
497+
/**
498+
* Result image in case of an input >2D
499+
*/
500+
private Img<T> m_resImg;
501+
487502
public ImgMergeOperator() {
488503
super("Merge Image", "Merge Image", "Merge Image");
489504
}
@@ -498,7 +513,7 @@ public ImgMergeOperator(final GlobalSettings globalSettings, final String axisLa
498513
m_smAxisLabel.setStringValue(axisLabel);
499514
}
500515
m_axisLabel = m_smAxisLabel.getStringValue();
501-
516+
m_rowCount = globalSettings.getNoOfRows();
502517
}
503518

504519
/**
@@ -522,59 +537,70 @@ protected boolean computeInternal(final DataRow row, final DataCell cell) {
522537
}
523538

524539
if (m_dims != null) {
525-
for (int i = 0; i < 2; i++) {
540+
for (int i = 0; i < m_dims.length - 1; i++) {
526541
if (imgPlus.dimension(i) != m_dims[i]) {
527542
throw new IllegalArgumentException(
528543
"Image " + imgPlus.getName() + " not compatible with first-row image. Different dimension!");
529544
}
530545
}
531-
532546
}
533547

534-
if (m_data == null) {
535-
m_type = imgPlus.firstElement().createVariable();
536-
if (m_type instanceof ByteType) {
537-
m_typeHandler = (RealTypeHandler<T, A, ADA>)new ByteTypeHandler();
538-
} else if (m_type instanceof UnsignedByteType) {
539-
m_typeHandler = (RealTypeHandler<T, A, ADA>)new UnsignedByteTypeHandler();
540-
} else if (m_type instanceof UnsignedShortType) {
541-
m_typeHandler = (RealTypeHandler<T, A, ADA>)new UnsignedShortTypeHandler();
542-
} else if (m_type instanceof ShortType) {
543-
m_typeHandler = (RealTypeHandler<T, A, ADA>)new ShortTypeHandler();
544-
} else if (m_type instanceof IntType) {
545-
m_typeHandler = (RealTypeHandler<T, A, ADA>)new IntTypeHandler();
546-
} else if (m_type instanceof LongType) {
547-
m_typeHandler = (RealTypeHandler<T, A, ADA>)new LongTypeHandler();
548-
} else if (m_type instanceof FloatType) {
549-
m_typeHandler = (RealTypeHandler<T, A, ADA>)new FloatTypeHandler();
550-
} else if (m_type instanceof DoubleType) {
551-
m_typeHandler = (RealTypeHandler<T, A, ADA>)new DoubleTypeHandler();
552-
} else {
553-
throw new IllegalArgumentException(
554-
"Pixel type " + m_type.getClass().getSimpleName() + " not supported for merging.");
555-
}
556-
m_data = new ArrayList<A>();
548+
int numHyperPlanes = -1;
549+
int hyperPlaneSize = -1;
550+
if (m_data == null && m_resImg == null) {
551+
//first iteration -> create data structures
557552

558553
final CalibratedAxis[] axes = new CalibratedAxis[imgPlus.numDimensions()];
559554
imgPlus.axes(axes);
560-
final CalibratedAxis[] newAxes = new CalibratedAxis[3];
561-
for (int i = 0; i < 2; i++) {
555+
final CalibratedAxis[] newAxes = new CalibratedAxis[axes.length + 1];
556+
for (int i = 0; i < axes.length; i++) {
562557
newAxes[i] = axes[i];
563558
}
564559

565560
//TODO: How to support different types of calibrates spaces/axis.
566-
newAxes[2] = new DefaultLinearAxis(Axes.get(m_axisLabel));
561+
newAxes[newAxes.length - 1] = new DefaultLinearAxis(Axes.get(m_axisLabel));
567562
m_metadata = new DefaultImgMetadata(new DefaultCalibratedSpace(newAxes), imgPlus, imgPlus, imgPlus);
568-
m_dims = new long[3];
569-
m_dims[0] = imgPlus.dimension(0);
570-
m_dims[1] = imgPlus.dimension(1);
563+
m_dims = new long[newAxes.length];
564+
imgPlus.dimensions(m_dims);
571565

566+
m_type = imgPlus.firstElement().createVariable();
567+
if (imgPlus.numDimensions() == 2) {
568+
hyperPlaneSize = 1;
569+
for (int i = 0; i < imgPlus.numDimensions(); i++) {
570+
hyperPlaneSize *=imgPlus.dimension(i);
571+
}
572+
numHyperPlanes = (int)(imgPlus.size() / hyperPlaneSize);
573+
m_dims[m_dims.length - 1] += numHyperPlanes;
574+
575+
if (m_type instanceof ByteType) {
576+
m_typeHandler = (RealTypeHandler<T, A, ADA>)new ByteTypeHandler();
577+
} else if (m_type instanceof UnsignedByteType) {
578+
m_typeHandler = (RealTypeHandler<T, A, ADA>)new UnsignedByteTypeHandler();
579+
} else if (m_type instanceof UnsignedShortType) {
580+
m_typeHandler = (RealTypeHandler<T, A, ADA>)new UnsignedShortTypeHandler();
581+
} else if (m_type instanceof ShortType) {
582+
m_typeHandler = (RealTypeHandler<T, A, ADA>)new ShortTypeHandler();
583+
} else if (m_type instanceof IntType) {
584+
m_typeHandler = (RealTypeHandler<T, A, ADA>)new IntTypeHandler();
585+
} else if (m_type instanceof LongType) {
586+
m_typeHandler = (RealTypeHandler<T, A, ADA>)new LongTypeHandler();
587+
} else if (m_type instanceof FloatType) {
588+
m_typeHandler = (RealTypeHandler<T, A, ADA>)new FloatTypeHandler();
589+
} else if (m_type instanceof DoubleType) {
590+
m_typeHandler = (RealTypeHandler<T, A, ADA>)new DoubleTypeHandler();
591+
} else {
592+
throw new IllegalArgumentException(
593+
"Pixel type " + m_type.getClass().getSimpleName() + " not supported for merging.");
594+
}
595+
m_data = new ArrayList<A>();
596+
} else {
597+
//create res img
598+
m_dims[m_dims.length - 1] = m_rowCount;
599+
m_resImg = new ArrayImgFactory().create(m_dims, m_type);
600+
m_rowIdx = 0;
601+
}
572602
}
573603

574-
final int planeSize = (int)(imgPlus.dimension(0) * imgPlus.dimension(1));
575-
final int numPlanes = (int)(imgPlus.size() / planeSize);
576-
m_dims[2] += numPlanes;
577-
578604
//in case of a previous virtual operation (img is wrapped in a ImgView), first execute the operation in order to get the 'physical' pixel data
579605
Img<T> img;
580606
if(imgPlus.getImg() instanceof ImgView) {
@@ -583,27 +609,41 @@ protected boolean computeInternal(final DataRow row, final DataCell cell) {
583609
img = imgPlus.getImg();
584610
}
585611

586-
// copy data
587-
if (img instanceof ArrayImg) {
588-
for (int i = 0; i < numPlanes; i++) {
589-
final A plane = m_typeHandler.createArray(planeSize);
590-
m_typeHandler.copyData((A)((ArrayDataAccess<A>)((ArrayImg)img).update(null))
591-
.getCurrentStorageArray(), plane, 0);
592-
m_data.add(plane);
593-
}
612+
if (imgPlus.numDimensions() == 2) {
613+
//in case of 2D we can efficiently copy the data arrays
614+
// copy data
615+
if (img instanceof ArrayImg) {
616+
for (int i = 0; i < numHyperPlanes; i++) {
617+
final A plane = m_typeHandler.createArray(hyperPlaneSize);
618+
m_typeHandler
619+
.copyData((A)((ArrayDataAccess<A>)((ArrayImg)img).update(null)).getCurrentStorageArray(),
620+
plane, 0);
621+
m_data.add(plane);
622+
}
594623

595-
} else if (imgPlus.getImg() instanceof PlanarImg) {
624+
} else if (imgPlus.getImg() instanceof PlanarImg) {
596625

597-
for (int i = 0; i < ((PlanarImg)img).numSlices(); i++) {
598-
final A plane = m_typeHandler.createArray(planeSize);
599-
m_typeHandler.copyData((A)((ArrayDataAccess<A>)((PlanarImg)img).getPlane(i))
600-
.getCurrentStorageArray(), plane, 0);
601-
m_data.add(plane);
626+
for (int i = 0; i < ((PlanarImg)img).numSlices(); i++) {
627+
final A plane = m_typeHandler.createArray(hyperPlaneSize);
628+
m_typeHandler
629+
.copyData((A)((ArrayDataAccess<A>)((PlanarImg)img).getPlane(i)).getCurrentStorageArray(),
630+
plane, 0);
631+
m_data.add(plane);
632+
}
633+
} else {
634+
if (imgPlus.numDimensions() != (m_dims.length - 1)) {
635+
throw new IllegalArgumentException("Image type not supported, yet.");
636+
}
602637
}
603638
} else {
604-
if (imgPlus.numDimensions() != (m_dims.length - 1)) {
605-
throw new IllegalArgumentException("Image type not supported, yet.");
639+
//just transfer the pixel values to the already created result image
640+
//TODO guarantee same iteration order
641+
Cursor<T> dest = Views.hyperSlice(m_resImg, m_resImg.numDimensions() - 1, m_rowIdx).cursor();
642+
for(T t : imgPlus) {
643+
dest.fwd();
644+
dest.get().set(t);
606645
}
646+
m_rowIdx++;
607647
}
608648

609649
return false;
@@ -637,24 +677,36 @@ protected DataType getDataType(final DataType origType) {
637677
*/
638678
@Override
639679
public String getDescription() {
640-
return "Merges the n-dimension to one (n+1) dimensional image object. The images to be merged must have the same X and Y dimensions and same pixel type as the first image in the group. If not, they will be skipped.";
680+
return "Merges the n-dimension to one (n+1) dimensional image object. The images to be merged must have exactly the same dimensions and same pixel type as the first image in the group. If not, they will be skipped.";
641681
}
642682

643683
/**
644684
* {@inheritDoc}
645685
*/
646686
@Override
647687
protected DataCell getResultInternal() {
648-
final ArrayList<ADA> mirror = new ArrayList<ADA>(m_data.size());
649-
for (int i = 0; i < m_data.size(); i++) {
650-
mirror.add(m_typeHandler.wrap(m_data.get(i)));
651-
}
652-
final CustomPlanarImg img = new CustomPlanarImg(mirror, m_dims, new Fraction(1, 1));
653-
img.setLinkedType(m_typeHandler.createLinkedType(img));
654-
try {
655-
return getImgPlusCellFactory().createCell(new ImgPlus(img, m_metadata));
656-
} catch (final IOException e) {
657-
throw new RuntimeException(e);
688+
if (m_data != null) {
689+
//in case 2D images have been merged
690+
final ArrayList<ADA> mirror = new ArrayList<ADA>(m_data.size());
691+
for (int i = 0; i < m_data.size(); i++) {
692+
mirror.add(m_typeHandler.wrap(m_data.get(i)));
693+
}
694+
final CustomPlanarImg img = new CustomPlanarImg(mirror, m_dims, new Fraction(1, 1));
695+
img.setLinkedType(m_typeHandler.createLinkedType(img));
696+
try {
697+
return getImgPlusCellFactory().createCell(new ImgPlus(img, m_metadata));
698+
} catch (final IOException e) {
699+
//TODO better error handling
700+
throw new RuntimeException(e);
701+
}
702+
} else {
703+
assert m_resImg != null;
704+
try {
705+
return getImgPlusCellFactory().createCell(new ImgPlus(m_resImg, m_metadata));
706+
} catch (IOException e) {
707+
//TODO better error handling
708+
throw new RuntimeException(e);
709+
}
658710
}
659711
}
660712

0 commit comments

Comments
 (0)