Program: 22717
Project: 22717
Buggy source:
/** 
 * Calculate the Rand index between to 3D clusters, as described by William M. Rand \cite{Rand71}, but pruning out the zero component of the ground truth, which leads to an asymmetric index. The input images  must be 16-bit. Note: this method is based on the N_choose_2 normalization. BibTeX: <pre> &#64;article{Rand71, author    = {William M. Rand}, title     = {Objective criteria for the evaluation of clustering methods}, journal   = {Journal of the American Statistical Association}, year      = {1971}, volume    = {66}, number    = {336}, pages     = {846--850}, doi       = {10.2307/2284239) } </pre>
 * @param originalLabels ground truth, 3D segmented image (objects are labeled with different numbers) 
 * @param proposedLabels prediction, 3D segmented image (objects are labeled with different numbers)
 * @return adapted Rand index value and prediction statistics
 */
public static ClassificationStatistics adaptedRandIndexStats3D(ImagePlus originalLabels,ImagePlus proposedLabels){
  if (originalLabels.getImageStack().getProcessor(1) instanceof ShortProcessor == false || proposedLabels.getImageStack().getProcessor(1) instanceof ShortProcessor == false)   return null;
  int nSlices=originalLabels.getImageStackSize();
  int maxIDGroundTruth=0;
  int maxIDProposal=0;
  for (int slice=1; slice <= nSlices; slice++) {
    ImageProcessor gt=originalLabels.getImageStack().getProcessor(slice);
    gt.resetMinAndMax();
    if (maxIDGroundTruth < gt.getMax())     maxIDGroundTruth=(int)gt.getMax();
    ImageProcessor proposal=proposedLabels.getImageStack().getProcessor(slice);
    proposal.resetMinAndMax();
    if (maxIDProposal < proposal.getMax())     maxIDProposal=(int)proposal.getMax();
  }
  double agreements=0;
  long[][] cont=new long[maxIDGroundTruth + 1][maxIDProposal + 1];
  double[] ni=new double[cont.length];
  double[] nj=new double[cont[0].length];
  double n=0;
  for (int slice=1; slice <= nSlices; slice++) {
    ShortProcessor cluster1=(ShortProcessor)originalLabels.getImageStack().getProcessor(slice);
    ShortProcessor cluster2=(ShortProcessor)proposedLabels.getImageStack().getProcessor(slice);
    final short[] pixels1=(short[])cluster1.getPixels();
    final short[] pixels2=(short[])cluster2.getPixels();
    double nPixels=pixels1.length;
    for (int i=0; i < nPixels; i++) {
      cont[pixels1[i]][pixels2[i]]++;
      if (pixels1[i] > 0)       n++;
    }
  }
  for (int i=1; i < cont.length; i++)   for (int j=0; j < cont[0].length; j++) {
    ni[i]+=cont[i][j];
  }
  for (int j=1; j < cont[0].length; j++)   for (int i=1; i < cont.length; i++) {
    nj[j]+=cont[i][j];
  }
  double truePositives=0;
  for (int j=1; j < cont[0].length; j++)   for (int i=1; i < cont.length; i++)   truePositives+=cont[i][j] * (cont[i][j] - 1.0);
  double nPairsTotal=n * (n - 1.0);
  double nPosTrue=0;
  for (int k=0; k < ni.length; k++)   nPosTrue+=ni[k] * (ni[k] - 1.0);
  double nPosActual=0;
  for (int k=0; k < nj.length; k++)   nPosActual+=nj[k] * (nj[k] - 1.0);
  double trueNegatives=nPairsTotal + truePositives - nPosTrue - nPosActual;
  agreements+=truePositives + trueNegatives;
  double falsePositives=nPosActual - truePositives;
  double nNegActual=nPairsTotal - nPosActual;
  double falseNegatives=nNegActual - trueNegatives;
  truePositives/=2.0;
  trueNegatives/=2.0;
  falsePositives/=2.0;
  falseNegatives/=2.0;
  double randIndex=agreements / nPairsTotal;
  return new ClassificationStatistics(truePositives,trueNegatives,falsePositives,falseNegatives,randIndex);
}

Fixed version (ground truth):
/** 
 * Calculate the Rand index between to 3D clusters, as described by William M. Rand \cite{Rand71}, but pruning out the zero component of the ground truth, which leads to an asymmetric index. The input images  must be 16-bit. Note: this method is based on the N_choose_2 normalization. BibTeX: <pre> &#64;article{Rand71, author    = {William M. Rand}, title     = {Objective criteria for the evaluation of clustering methods}, journal   = {Journal of the American Statistical Association}, year      = {1971}, volume    = {66}, number    = {336}, pages     = {846--850}, doi       = {10.2307/2284239) } </pre>
 * @param originalLabels ground truth, 3D segmented image (objects are labeled with different numbers) 
 * @param proposedLabels prediction, 3D segmented image (objects are labeled with different numbers)
 * @return adapted Rand index value and prediction statistics
 */
public static ClassificationStatistics adaptedRandIndexStats3D(ImagePlus originalLabels,ImagePlus proposedLabels){
  if (originalLabels.getImageStack().getProcessor(1) instanceof ShortProcessor == false || proposedLabels.getImageStack().getProcessor(1) instanceof ShortProcessor == false)   return null;
  int nSlices=originalLabels.getImageStackSize();
  int maxIDGroundTruth=0;
  int maxIDProposal=0;
  for (int slice=1; slice <= nSlices; slice++) {
    ImageProcessor gt=originalLabels.getImageStack().getProcessor(slice);
    gt.resetMinAndMax();
    if (maxIDGroundTruth < gt.getMax())     maxIDGroundTruth=(int)gt.getMax();
    ImageProcessor proposal=proposedLabels.getImageStack().getProcessor(slice);
    proposal.resetMinAndMax();
    if (maxIDProposal < proposal.getMax())     maxIDProposal=(int)proposal.getMax();
  }
  double agreements=0;
  long[][] cont=new long[maxIDGroundTruth + 1][maxIDProposal + 1];
  double[] ni=new double[cont.length];
  double[] nj=new double[cont[0].length];
  double n=0;
  for (int slice=1; slice <= nSlices; slice++) {
    ShortProcessor cluster1=(ShortProcessor)originalLabels.getImageStack().getProcessor(slice);
    ShortProcessor cluster2=(ShortProcessor)proposedLabels.getImageStack().getProcessor(slice);
    final short[] pixels1=(short[])cluster1.getPixels();
    final short[] pixels2=(short[])cluster2.getPixels();
    double nPixels=pixels1.length;
    for (int i=0; i < nPixels; i++) {
      cont[pixels1[i] & 0xffff][pixels2[i] & 0xffff]++;
      if (pixels1[i] > 0)       n++;
    }
  }
  for (int i=1; i < cont.length; i++)   for (int j=0; j < cont[0].length; j++) {
    ni[i]+=cont[i][j];
  }
  for (int j=1; j < cont[0].length; j++)   for (int i=1; i < cont.length; i++) {
    nj[j]+=cont[i][j];
  }
  double truePositives=0;
  for (int j=1; j < cont[0].length; j++)   for (int i=1; i < cont.length; i++)   truePositives+=cont[i][j] * (cont[i][j] - 1.0);
  double nPairsTotal=n * (n - 1.0);
  double nPosTrue=0;
  for (int k=0; k < ni.length; k++)   nPosTrue+=ni[k] * (ni[k] - 1.0);
  double nPosActual=0;
  for (int k=0; k < nj.length; k++)   nPosActual+=nj[k] * (nj[k] - 1.0);
  double trueNegatives=nPairsTotal + truePositives - nPosTrue - nPosActual;
  agreements+=truePositives + trueNegatives;
  double falsePositives=nPosActual - truePositives;
  double nNegActual=nPairsTotal - nPosActual;
  double falseNegatives=nNegActual - trueNegatives;
  truePositives/=2.0;
  trueNegatives/=2.0;
  falsePositives/=2.0;
  falseNegatives/=2.0;
  double randIndex=agreements / nPairsTotal;
  return new ClassificationStatistics(truePositives,trueNegatives,falsePositives,falseNegatives,randIndex);
}