/*-
* #%L
* Fiji distribution of ImageJ for the life sciences.
* %%
* Copyright (C) 2007 - 2017 Fiji developers.
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-2.0.html>.
* #L%
*/
package mpicbg.spim.registration.threshold;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import spim.vecmath.Point3d;
import mpicbg.imglib.cursor.LocalizableByDimCursor3D;
import mpicbg.imglib.image.Image;
import mpicbg.imglib.type.numeric.integer.IntType;
import mpicbg.imglib.type.numeric.real.FloatType;
import mpicbg.spim.io.IOFunctions;
public class ConnectedComponent
{
// stores label groups which can contain more than one label
// each label is stored only once in this list
public ArrayList <int[]> distinctLabels = new ArrayList<int[]>();
// stores properties of the respective components (which have a label)
private ArrayList <ComponentProperties> components = null;
private HashMap<Integer, Integer> labelGroups = new HashMap<Integer, Integer>();
// equalizes the labels and counts the number of pixels well as min and max coordinates
public void equalizeLabels( final Image<IntType> connectedComponents )
{
final int w = connectedComponents.getDimension( 0 );
final int h = connectedComponents.getDimension( 1 );
final int d = connectedComponents.getDimension( 2 );
components = new ArrayList<ComponentProperties>();
for (int i = 0; i < distinctLabels.size(); i++)
components.add(new ComponentProperties());
final LocalizableByDimCursor3D<IntType> cursor = (LocalizableByDimCursor3D<IntType>) connectedComponents.createLocalizableByDimCursor();
for (int z = 0; z < d; z++)
{
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++)
{
cursor.setPosition( x, y, z );
final int pixel = cursor.getType().get();
if ( pixel > 0 )
{
cursor.getType().set( getLabelGroup(pixel) + 1 ); // starts with 0 which is the background per definition
cursor.setPosition( x, y, z );
ComponentProperties compProp = components.get( cursor.getType().get() - 1 );
compProp.size++;
compProp.label = cursor.getType().get();
if (x < compProp.minX) compProp.minX = x;
if (y < compProp.minY) compProp.minY = y;
if (z < compProp.minZ) compProp.minZ = z;
if (x > compProp.maxX) compProp.maxX = x;
if (y > compProp.maxY) compProp.maxY = y;
if (z > compProp.maxZ) compProp.maxZ = z;
}
}
}
cursor.close();
//PrintWriter out = fileAccess.openFileWrite("components.txt");
//out.println("size" + "\t" + "sizeX" + "\t" + "sizeY" + "\t" + "sizeZ");
for (Iterator <ComponentProperties>i = components.iterator(); i.hasNext(); )
{
ComponentProperties compProp = i.next();
compProp.sizeX = compProp.maxX - compProp.minX + 1;
compProp.sizeY = compProp.maxY - compProp.minY + 1;
compProp.sizeZ = compProp.maxZ - compProp.minZ + 1;
//out.println(compProp.size + "\t" + compProp.sizeX + "\t" + compProp.sizeY + "\t" + compProp.sizeZ);
}
//out.close();
}
public ArrayList<ComponentProperties> getBeads( final Image<IntType> connectedComponents, final Image<FloatType> img,
final int minSize, final int maxSize, final int minBlackBorder,
final boolean useCenterOfMass, final double circularityFactor)
{
final int w = connectedComponents.getDimension( 0 );
final int h = connectedComponents.getDimension( 1 );
final int d = connectedComponents.getDimension( 2 );
//OldFloatArray3D spheres = new OldFloatArray3D(connectedComponents.width, connectedComponents.height, connectedComponents.depth);
//OldFloatArray3D psf = new OldFloatArray3D(21, 21, 21);
//OldFloatArray3D count = new OldFloatArray3D(21, 21, 21);
// Remove regions that are do not match the criteria
for (int i = 0; i < components.size();)
{
ComponentProperties compProp = components.get(i);
//double minVolume = (4.0/3.0) * Math.PI * (compProp.sizeX/2.0) * (compProp.sizeY/2.0) * (compProp.sizeZ/2.0);
// to small or too large or Volume too small relative to the bounding box
if (compProp.size < minSize || compProp.size > maxSize)// || compProp.size < minVolume*circularityFactor)
{
components.remove(i);
}
else
{
boolean isIsolated = true;
// should not touch the image edges
if (compProp.minX - minBlackBorder < 0 || compProp.maxX + minBlackBorder >= w ||
compProp.minY - minBlackBorder < 0 || compProp.maxY + minBlackBorder >= h ||
compProp.minZ - minBlackBorder < 0 || compProp.maxZ + minBlackBorder >= d )
{
isIsolated = false;
}
float countX = 0;
float countY = 0;
float countZ = 0;
compProp.center = new Point3d(0,0,0);
final LocalizableByDimCursor3D<FloatType> cursor = (LocalizableByDimCursor3D<FloatType>) img.createLocalizableByDimCursor();
float maxIntensity = -Float.MAX_VALUE;
Point3d maxCenter = new Point3d();
// and it furthermore has to be isolated from other components
for (int z = compProp.minZ - minBlackBorder; z <= compProp.maxZ + minBlackBorder && isIsolated; z++)
for (int y = compProp.minY - minBlackBorder; y <= compProp.maxY + minBlackBorder; y++)
for (int x = compProp.minX - minBlackBorder; x <= compProp.maxX + minBlackBorder; x++)
{
/*int label = connectedComponents.get(x, y, z);
if (label != compProp.label && label != 0)
{
isIsolated = false;
break;
}
else*/
{
cursor.setPosition(x, y, z);
float value = cursor.getType().get();
if (useCenterOfMass)
{
compProp.center.x += x * value;
compProp.center.y += y * value;
compProp.center.z += z * value;
countX += value;
countY += value;
countZ += value;
}
else
{
// select pixel with maximum brightness
if (value > maxIntensity)
{
maxIntensity = value;
maxCenter.x = x;
maxCenter.y = y;
maxCenter.z = z;
}
}
}
}
cursor.close();
if (!isIsolated)
{
components.remove(i);
}
else
{
if (useCenterOfMass)
{
compProp.center.x /= countX;//(compProp.maxX - compProp.minX)/2.0f + compProp.minX;
compProp.center.y /= countY;//(compProp.maxY - compProp.minY)/2.0f + compProp.minY; //(float)countY;
compProp.center.z /= countZ;//(compProp.maxZ - compProp.minZ)/2.0f + compProp.minZ; //(float)countZ;
}
else
{
compProp.center.x = maxCenter.x;
compProp.center.y = maxCenter.y;
compProp.center.z = maxCenter.z;
// create the PSF
/*int minX = compProp.minX - 7;
int maxX = compProp.maxX + 7 + 1;
int minY = compProp.minY - 7;
int maxY = compProp.maxY + 7 + 1;
int minZ = compProp.minZ - 7;
int maxZ = compProp.maxZ + 7 + 1;
IOFunctions.println( minX + " " + maxX );
IOFunctions.println( minY + " " + maxY );
IOFunctions.println( minZ + " " + maxZ );
if (maxX - minX >= psf.width || maxY - minY >= psf.height || maxZ - minZ >= psf.height)
break;
IOFunctions.println( compProp.minX + " " + compProp.maxX );
IOFunctions.println( compProp.minY + " " + compProp.maxY );
IOFunctions.println( compProp.minZ + " " + compProp.maxZ );
for (int z = compProp.minZ - 7; z <= compProp.maxZ + 7; z++)
for (int y = compProp.minY - 7; y <= compProp.maxY + 7; y++)
for (int x = compProp.minX - 7; x <= compProp.maxX + 7; x++)
{
int xs = psf.width/2 + (x - Math.round((float)compProp.center.x));
int ys = psf.height/2 + (y - Math.round((float)compProp.center.y));
int zs = psf.depth/2 + (z - Math.round((float)compProp.center.z));
try
{
psf.set(psf.get(xs, ys, zs) + img.get(x, y, z), xs, ys, zs);
count.set(count.get(xs, ys, zs) + 1, xs, ys, zs);
}
catch (Exception e)
{
IOFunctions.println(xs + " " + ys + " " + zs);
IOFunctions.println(x + " " + y + " " + z);
System.exit(0);
}
}
System.exit(0);*/
}
//IOFunctions.println(compProp.label + ": " + compProp.center);
//ImageFilter.addGaussianSphere(spheres, null, 1.0f, (float)compProp.center.x, (float)compProp.center.y, (float)compProp.center.z, 4.0f, 10, false);
i++;
}
}
}
/*for (int i = 0; i < psf.data.length; i++)
if (count.data[i] > 1)
psf.data[i] /= (float)(count.data[i]);
ImageArrayConverter.FloatArrayToStack(psf, "psf", 0, 0).show();
int a = 1;
do
{
try {
Thread.sleep( 1000 );
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
while(1 == a);*/
//FloatArrayToStack(spheres, "segemented beads", 0, 0).show();
return components;
}
public synchronized void addLabel(int label)
{
Iterator <int[]>i = distinctLabels.iterator();
while (i.hasNext())
{
int[] labelGroup = i.next();
for (int knownLabel : labelGroup)
if (knownLabel == label)
return;
}
// we have a new group of labels which contains one label
distinctLabels.add(new int[]{label});
// this label is in group "distinctLabels.size() - 1"
labelGroups.put(label, distinctLabels.size() - 1);
}
// this method cannot be called twice at a time, it would mess up the order in distinctlabels
public synchronized void addEqualLabels(int label1, int label2)
{
// get the label group for both labels
int group1 = getLabelGroup(label1);
int group2 = getLabelGroup(label2);
// if they are not in the same group already we merge both groups
if (group1 != group2)
{
int[] labelGroup1 = distinctLabels.get(group1);
int[] labelGroup2 = distinctLabels.get(group2);
int[] newGroup = new int[labelGroup1.length + labelGroup2.length];
for (int i = 0; i < labelGroup1.length; i++)
newGroup[i] = labelGroup1[i];
for (int i = 0; i < labelGroup2.length; i++)
newGroup[i + labelGroup1.length] = labelGroup2[i];
if (group2 > group1)
{
distinctLabels.remove(group2);
distinctLabels.remove(group1);
}
else
{
distinctLabels.remove(group1);
distinctLabels.remove(group2);
}
distinctLabels.add(newGroup);
// labelGroups has to know all new positions of the labels (order in arraylist changed!)
Iterator <int[]>i = distinctLabels.iterator();
int group = 0;
while (i.hasNext())
{
int[] labelGroup = i.next();
for (int label : labelGroup)
labelGroups.put(label, group);
group++;
}
}
}
private int getLabelGroup(int label)
{
/*Iterator <int[]>i = distinctLabels.iterator();
int count = 0;
int result = -1;
while (i.hasNext() && result == -1)
{
int[] labelGroup = i.next();
for (int knownLabel : labelGroup)
if (knownLabel == label)
result = count;
count++;
}
//IOFunctions.printErr("EqualLabel.getLabelGroup(): Label " + label + " not found!");
//return -1;
*/
Integer group = labelGroups.get(label);
if (group == null)
{
IOFunctions.printErr("EqualLabel.getLabelGroup(): Label " + label + " not found!");
return -1;
}
else
{
return group;
}
}
}