/*
* Copyright 2010, 2011 Institut Pasteur.
*
* This file is part of NHerve Main Toolbox, which is an ICY plugin.
*
* NHerve Main Toolbox 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 3 of the License, or
* (at your option) any later version.
*
* NHerve Main Toolbox 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 NHerve Main Toolbox. If not, see <http://www.gnu.org/licenses/>.
*/
package plugins.nherve.toolbox.image.segmentation;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import plugins.nherve.toolbox.image.BinaryIcyBufferedImage;
import plugins.nherve.toolbox.image.My2DConnectedComponent;
import plugins.nherve.toolbox.image.mask.Mask;
import plugins.nherve.toolbox.image.mask.MaskException;
import plugins.nherve.toolbox.image.toolboxes.SomeImageTools;
/**
* The Class SegmentationComparison.
*
* @author Nicolas HERVE - nicolas.herve@pasteur.fr
*/
public class SegmentationComparison {
/**
* Intersection.
*
* @param m1
* the m1
* @param m2
* the m2
* @return the double
*/
public static double intersection(Mask m1, Mask m2) {
return BinaryIcyBufferedImage.intersection(m1.getBinaryData(), m2.getBinaryData());
}
/**
* Clean segmentation.
*
* @param seg
* the seg
* @param maskPrefix
* the mask prefix
* @throws MaskException
* the mask exception
*/
private static void cleanSegmentation(Segmentation seg, String maskPrefix) throws MaskException {
List<Mask> trm = new ArrayList<Mask>();
for (Mask m : seg) {
if (!m.getLabel().toUpperCase().startsWith(maskPrefix.toUpperCase())) {
trm.add(m);
}
}
for (Mask m : trm) {
seg.remove(m);
}
}
/**
* Prepare for lre score.
*
* @param seg1
* the seg1
* @param seg2
* the seg2
* @param maskPrefix
* the mask prefix
* @return the binary icy buffered image
* @throws MaskException
* the mask exception
*/
private static BinaryIcyBufferedImage prepareForLREScore(Segmentation seg1, Segmentation seg2, String maskPrefix) throws MaskException {
cleanSegmentation(seg1, maskPrefix);
cleanSegmentation(seg2, maskPrefix);
BinaryIcyBufferedImage rt = new BinaryIcyBufferedImage(seg1.getWidth(), seg1.getHeight());
for (Mask m : seg1) {
rt.add(m.getBinaryData());
}
for (Mask m : seg2) {
rt.add(m.getBinaryData());
}
BinaryIcyBufferedImage rt1 = rt.getCopy();
for (Mask m : seg1) {
rt1.remove(m.getBinaryData());
}
Mask rt1m = seg1.createNewMask("RestrictTo background", false, Color.WHITE, 0);
rt1m.setBinaryData(rt1);
BinaryIcyBufferedImage rt2 = rt.getCopy();
for (Mask m : seg2) {
rt2.remove(m.getBinaryData());
}
Mask rt2m = seg2.createNewMask("RestrictTo background", false, Color.WHITE, 0);
rt2m.setBinaryData(rt2);
seg1.createBackgroundMask("Unused", Color.WHITE);
seg1.createIndex();
seg2.createBackgroundMask("Unused", Color.WHITE);
seg2.createIndex();
return rt;
}
/**
* Restricted gce.
*
* @param gt
* the gt
* @param at
* the at
* @param restrict
* the restrict
* @return the double
* @throws MaskException
* the mask exception
*/
public static double restrictedGce(Segmentation gt, Segmentation at, String restrict) throws MaskException {
try {
Segmentation gtb = gt.clone();
Segmentation atb = at.clone();
BinaryIcyBufferedImage rt = prepareForLREScore(gtb, atb, restrict);
// XXX debug
// try {
// OptimizedMaskPersistenceImpl p = new
// OptimizedMaskPersistenceImpl();
// p.save(gtb, new
// File("/Users/nherve/Travail/glomdetect/glomdb/debug_pool/gtb" +
// p.getMaskFileExtension()));
// p.save(atb, new
// File("/Users/nherve/Travail/glomdetect/glomdb/debug_pool/atb" +
// p.getMaskFileExtension()));
// try {
// Saver.saveImage(rt, new
// File("/Users/nherve/Travail/glomdetect/glomdb/debug_pool/rt2.tif"),
// true);
// } catch (FormatException e) {
// e.printStackTrace();
// } catch (IOException e) {
// e.printStackTrace();
// } catch (ServiceException e) {
// e.printStackTrace();
// }
// } catch (PersistenceException e) {
// e.printStackTrace();
// }
// ---
return gce(gtb, atb, rt);
} catch (CloneNotSupportedException e) {
throw new MaskException(e);
}
}
/**
* Restricted lce.
*
* @param gt
* the gt
* @param at
* the at
* @param restrict
* the restrict
* @return the double
* @throws MaskException
* the mask exception
*/
public static double restrictedLce(Segmentation gt, Segmentation at, String restrict) throws MaskException {
try {
Segmentation gtb = gt.clone();
Segmentation atb = at.clone();
BinaryIcyBufferedImage rt = prepareForLREScore(gtb, atb, restrict);
return lce(gtb, atb, rt);
} catch (CloneNotSupportedException e) {
throw new MaskException(e);
}
}
/**
* Mccc.
*
* @param gt
* the gt
* @param at
* the at
* @return the double
* @throws MaskException
* the mask exception
*/
private static double mccc2(Segmentation gt, Segmentation at, String restrict) throws MaskException {
int w = gt.getWidth();
int h = gt.getHeight();
int gts = gt.getMaxId() + 1;
int ats = at.getMaxId() + 1;
double[] lreCache = new double[gts * ats];
Arrays.fill(lreCache, -1d);
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
Mask gtm = gt.getMask(x, y);
int gtmid = gtm.getId();
Mask atm = at.getMask(x, y);
int atmid = atm.getId();
if (lreCache[gtmid + atmid * gts] == -1) {
lreCache[gtmid + atmid * gts] = lre(gtm, atm) + lre(atm, gtm);
}
}
}
double[] gtc = new double[gts];
Arrays.fill(gtc, 0);
double[] atc = new double[ats];
Arrays.fill(atc, 0);
for (Mask gtm : gt) {
gtc[gtm.getId()] = 1;
}
for (Mask atm : at) {
atc[atm.getId()] = 1;
}
for (Mask gtm : gt) {
if (gtm.getLabel().toUpperCase().startsWith(restrict.toUpperCase())) {
int gtmid = gtm.getId();
double min = Double.MAX_VALUE;
int minId = -1;
for (Mask atm : at) {
if (atm.getLabel().toUpperCase().startsWith(restrict.toUpperCase())) {
int atmid = atm.getId();
if (atc[atmid] > 0) {
double v = lreCache[gtmid + atmid * gts];
if ((v >= 0) && (v < min)) {
min = v;
minId = atmid;
}
}
}
}
if (minId >= 0) {
gtc[gtmid] = 0;
atc[minId] = 0;
// System.out.println("MCCC association : " +
// gt.getById(gtmid) + " - " + at.getById(minId));
}
}
}
double sum = 0;
for (int i = 0; i < gts; i++) {
if (gtc[i] != 0) {
// System.out.println("MCCC (1) unassociated : " +
// gt.getById(i));
sum += gtc[i];
}
}
for (int i = 0; i < ats; i++) {
if (atc[i] != 0) {
// System.out.println("MCCC (2) unassociated : " +
// at.getById(i));
sum += atc[i];
}
}
return sum / (double) gt.size();
}
/**
* Mccc.
*
* @param gt
* the gt
* @param at
* the at
* @param restrict
* the restrict
* @return the double
* @throws MaskException
* the mask exception
*/
public static double mccc(Segmentation gt, Segmentation at, String restrict) throws MaskException {
try {
Segmentation gtb = gt.clone();
Segmentation atb = at.clone();
cleanSegmentation(gtb, restrict);
cleanSegmentation(atb, restrict);
gtb.createBackgroundMask("Unused", Color.WHITE);
gtb.createIndex();
atb.createBackgroundMask("Unused", Color.WHITE);
atb.createIndex();
// prepareForLREScore(gtb, atb, restrict);
return mccc2(gtb, atb, restrict);
} catch (CloneNotSupportedException e) {
throw new MaskException(e);
}
}
/**
* Gce.
*
* @param gt
* the gt
* @param at
* the at
* @return the double
* @throws MaskException
* the mask exception
*/
public static double gce(Segmentation gt, Segmentation at) throws MaskException {
return gce(gt, at, null);
}
/**
* Gce.
*
* @param gt
* the gt
* @param at
* the at
* @param restrictTo
* the restrict to
* @return the double
* @throws MaskException
* the mask exception
*/
public static double gce(Segmentation gt, Segmentation at, BinaryIcyBufferedImage restrictTo) throws MaskException {
int w = gt.getWidth();
int h = gt.getHeight();
int s = 0;
int gts = gt.getMaxId() + 1;
int ats = at.getMaxId() + 1;
double[] lre12Cache = new double[gts * ats];
Arrays.fill(lre12Cache, -1d);
double[] lre21Cache = new double[gts * ats];
Arrays.fill(lre21Cache, -1d);
double lre12 = 0;
double lre21 = 0;
byte[] rt = null;
if (restrictTo != null) {
rt = restrictTo.getRawData();
}
int idx = 0;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
if ((rt == null) || (rt[idx] == BinaryIcyBufferedImage.TRUE)) {
Mask gtm = gt.getMask(x, y);
int gtmid = gtm.getId();
Mask atm = at.getMask(x, y);
int atmid = atm.getId();
if (lre12Cache[gtmid + atmid * gts] == -1) {
lre12Cache[gtmid + atmid * gts] = lre(gtm, atm);
lre21Cache[gtmid + atmid * gts] = lre(atm, gtm);
}
lre12 += lre12Cache[gtmid + atmid * gts];
lre21 += lre21Cache[gtmid + atmid * gts];
s++;
}
idx++;
}
}
if (s > 0) {
lre12 /= s;
lre21 /= s;
}
return Math.min(lre12, lre21);
}
/**
* Lce.
*
* @param gt
* the gt
* @param at
* the at
* @return the double
* @throws MaskException
* the mask exception
*/
public static double lce(Segmentation gt, Segmentation at) throws MaskException {
return lce(gt, at, null);
}
/**
* Lce.
*
* @param gt
* the gt
* @param at
* the at
* @param restrictTo
* the restrict to
* @return the double
* @throws MaskException
* the mask exception
*/
public static double lce(Segmentation gt, Segmentation at, BinaryIcyBufferedImage restrictTo) throws MaskException {
int w = gt.getWidth();
int h = gt.getHeight();
int s = 0;
int gts = gt.getMaxId() + 1;
int ats = at.getMaxId() + 1;
double[] lre12Cache = new double[gts * ats];
Arrays.fill(lre12Cache, -1d);
double[] lre21Cache = new double[gts * ats];
Arrays.fill(lre21Cache, -1d);
double lce = 0;
byte[] rt = null;
if (restrictTo != null) {
rt = restrictTo.getRawData();
}
int idx = 0;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
if ((rt == null) || (rt[idx] == BinaryIcyBufferedImage.TRUE)) {
Mask gtm = gt.getMask(x, y);
int gtmid = gtm.getId();
Mask atm = at.getMask(x, y);
int atmid = atm.getId();
if (lre12Cache[gtmid + atmid * gts] == -1) {
lre12Cache[gtmid + atmid * gts] = lre(gtm, atm);
lre21Cache[gtmid + atmid * gts] = lre(atm, gtm);
}
lce += Math.min(lre12Cache[gtmid + atmid * gts], lre21Cache[gtmid + atmid * gts]);
s++;
}
idx++;
}
}
if (s > 0) {
lce /= s;
}
return lce;
}
/**
* Lre.
*
* @param cc1
* the cc1
* @param cc2
* the cc2
* @return the double
*/
public static double lre(Mask cc1, Mask cc2) {
BinaryIcyBufferedImage b1 = cc1.getBinaryData().getCopy();
double b1Card = b1.getSurface();
BinaryIcyBufferedImage b2 = cc2.getBinaryData();
b1.remove(b2);
double b1minb2Card = b1.getSurface();
double lre = b1minb2Card / b1Card;
// System.out.println(cc1.getId() + " - " + cc2.getId() + " : " + lre);
return lre;
}
/**
* Nhd.
*
* @param gt
* the gt
* @param attempt
* the attempt
* @return the double
*/
public static double nhd(BinaryIcyBufferedImage gt, BinaryIcyBufferedImage attempt) {
return score1(gt, attempt) / Mask.getSurface(gt);
}
/**
* Nhd.
*
* @param gt
* the gt
* @param attempt
* the attempt
* @return the double
*/
public static double nhd(Mask gt, Mask attempt) {
return nhd(gt.getBinaryData(), attempt.getBinaryData());
}
/**
* Ncc.
*
* @param gt
* the gt
* @param attempt
* the attempt
* @return the double
*/
public static double ncc(Mask gt, Mask attempt) {
return ncc(gt.getBinaryData(), attempt.getBinaryData());
}
/**
* Ncc.
*
* @param gt
* the gt
* @param attempt
* the attempt
* @return the double
*/
public static double ncc(BinaryIcyBufferedImage gt, BinaryIcyBufferedImage attempt) {
List<My2DConnectedComponent> gtCc = SomeImageTools.findConnectedComponents(gt);
List<My2DConnectedComponent> attemptCc = SomeImageTools.findConnectedComponents(attempt);
return ((double) (gtCc.size() - attemptCc.size())) / (double) (gtCc.size());
}
/**
* Score.
*
* @param type
* the type
* @param gt
* the gt
* @param attempt
* the attempt
* @return the double
*/
public static double score(int type, BinaryIcyBufferedImage gt, BinaryIcyBufferedImage attempt) {
switch (type) {
case 1:
return score1(gt, attempt);
case 2:
return score2(gt, attempt);
case 3:
return score3(gt, attempt);
case 4:
return score4(gt, attempt);
case 5:
return score5(gt, attempt);
default:
return Double.MAX_VALUE;
}
}
/**
* Score.
*
* @param type
* the type
* @param gt
* the gt
* @param attempt
* the attempt
* @return the double
*/
public static double score(int type, Mask gt, Mask attempt) {
return score(type, gt.getBinaryData(), attempt.getBinaryData());
}
/**
* Score1.
*
* @param gt
* the gt
* @param attempt
* the attempt
* @return the double
*/
public static double score1(BinaryIcyBufferedImage gt, BinaryIcyBufferedImage attempt) {
return BinaryIcyBufferedImage.union(gt, attempt) - BinaryIcyBufferedImage.intersection(gt, attempt);
}
/**
* Score1.
*
* @param gt
* the gt
* @param attempt
* the attempt
* @return the double
*/
public static double score1(Mask gt, Mask attempt) {
return score1(gt.getBinaryData(), attempt.getBinaryData());
}
/**
* Score2.
*
* @param gt
* the gt
* @param attempt
* the attempt
* @return the double
*/
public static double score2(BinaryIcyBufferedImage gt, BinaryIcyBufferedImage attempt) {
double u = BinaryIcyBufferedImage.union(gt, attempt);
double i = BinaryIcyBufferedImage.intersection(gt, attempt);
return (u - i) / u;
}
/**
* Score2.
*
* @param gt
* the gt
* @param attempt
* the attempt
* @return the double
*/
public static double score2(Mask gt, Mask attempt) {
return score2(gt.getBinaryData(), attempt.getBinaryData());
}
/**
* Score3.
*
* @param gt
* the gt
* @param attempt
* the attempt
* @return the double
*/
public static double score3(BinaryIcyBufferedImage gt, BinaryIcyBufferedImage attempt) {
return (1 + BinaryIcyBufferedImage.union(gt, attempt)) / (1 + BinaryIcyBufferedImage.intersection(gt, attempt));
}
/**
* Score3.
*
* @param gt
* the gt
* @param attempt
* the attempt
* @return the double
*/
public static double score3(Mask gt, Mask attempt) {
return score3(gt.getBinaryData(), attempt.getBinaryData());
}
/**
* Score4.
*
* @param gt
* the gt
* @param attempt
* the attempt
* @return the double
*/
public static double score4(BinaryIcyBufferedImage gt, BinaryIcyBufferedImage attempt) {
return Math.abs(Mask.getSurface(gt) - Mask.getSurface(attempt));
}
/**
* Score4.
*
* @param gt
* the gt
* @param attempt
* the attempt
* @return the double
*/
public static double score4(Mask gt, Mask attempt) {
return score4(gt.getBinaryData(), attempt.getBinaryData());
}
/**
* Score5.
*
* @param gt
* the gt
* @param attempt
* the attempt
* @return the double
*/
public static double score5(BinaryIcyBufferedImage gt, BinaryIcyBufferedImage attempt) {
return Math.abs(Mask.getSurface(gt) - Mask.getSurface(attempt));
}
/**
* Score5.
*
* @param gt
* the gt
* @param attempt
* the attempt
* @return the double
*/
public static double score5(Mask gt, Mask attempt) {
return score5(gt.getBinaryData(), attempt.getBinaryData());
}
/**
* Union.
*
* @param m1
* the m1
* @param m2
* the m2
* @return the double
*/
public static double union(Mask m1, Mask m2) {
return BinaryIcyBufferedImage.union(m1.getBinaryData(), m2.getBinaryData());
}
}