package plugins.nherve.toolbox.image.feature.lbp; import icy.image.IcyBufferedImage; import icy.type.TypeUtil; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import plugins.nherve.toolbox.Algorithm; import plugins.nherve.toolbox.image.BinaryIcyBufferedImage; import plugins.nherve.toolbox.image.feature.FeatureException; import plugins.nherve.toolbox.image.feature.IcySupportRegion; import plugins.nherve.toolbox.image.feature.SegmentableIcyBufferedImage; import plugins.nherve.toolbox.image.feature.SupportRegion; import plugins.nherve.toolbox.image.feature.clustering.ClusteringException; import plugins.nherve.toolbox.image.feature.com.CooccurenceMatrixFactory; import plugins.nherve.toolbox.image.feature.com.KernelFactory; import plugins.nherve.toolbox.image.feature.fuzzy.FuzzyClusteringAlgorithm; import plugins.nherve.toolbox.image.feature.fuzzy.FuzzyClusteringProcessor; import plugins.nherve.toolbox.image.feature.region.IcyPixel; import plugins.nherve.toolbox.image.feature.region.SupportRegionException; import plugins.nherve.toolbox.image.feature.signature.SignatureException; import plugins.nherve.toolbox.image.feature.signature.DefaultVectorSignature; import plugins.nherve.toolbox.image.mask.Mask; import plugins.nherve.toolbox.image.mask.MaskException; import plugins.nherve.toolbox.image.mask.MaskStack; import plugins.nherve.toolbox.image.toolboxes.SomeImageTools; public class LBPToolbox extends Algorithm { public final static int FUZZY_FUNCTION_STANDARD = 1; public final static int FUZZY_FUNCTION_TANH = 2; public final static int FUZZY_FUNCTION_STEP = 3; public interface FuzzyFunction { double apply(double x); } public class StandardFuzzyFunction implements FuzzyFunction { public StandardFuzzyFunction(double fuzzifier) { super(); this.fuzzifier = fuzzifier; } private double fuzzifier; @Override public double apply(double x) { if (x < -fuzzifier) { return 0d; } else if (x > fuzzifier) { return 1d; } else { return (1d + (x / fuzzifier)) / 2d; } } } public class TanhFuzzyFunction implements FuzzyFunction { public TanhFuzzyFunction(double alpha) { super(); this.alpha = alpha; } private double alpha; @Override public double apply(double x) { return Math.tanh(alpha * x); } } public class StepFuzzyFunction implements FuzzyFunction { public StepFuzzyFunction(double alpha) { super(); } @Override public double apply(double x) { return (x >= 0) ? 1 : 0; } } private int P; private long maxLBPIndex; private double R; private double[][] nb; private boolean ri; private boolean uniform; private int v; private int encoding; private double ternaryThreshold; // private double fuzzifier; private HashMap<Long, Long> rilut; private HashMap<Long, Long> rilutid; private boolean useIntensity; private FuzzyClusteringAlgorithm fca; private FuzzyClusteringProcessor proc; private FuzzyFunction ff; private List<IcyPixel> intKernel; public LBPToolbox(int p, double r, boolean ri, boolean uniform, int v, int encoding, boolean in, boolean display) { super(display); P = p; R = r; this.ri = ri; this.uniform = uniform; this.v = v; this.encoding = encoding; this.useIntensity = in; this.ternaryThreshold = 20d; maxLBPIndex = (long) Math.pow(2, P); if (ri) { initRILUT(); } initNeighbours(); log("LocalBinaryPattern signature size = " + getSignatureSize() + " - (" + maxLBPIndex + " * " + v + " * " + encoding + ")"); } public long circularRightShift(long x) { return (x >> 1) | ((x & 1) << (P - 1)); } public long uniformity(long x) { long u = 0; long xor = x ^ circularRightShift(x); for (long p = 0; p < P; p++) { u += xor & 1; xor = circularRightShift(xor); } return u; } public IcyBufferedImage computeLBP(final Mask m) { return computeLBP(m, m); } public IcyBufferedImage computeLBP(final Mask m1, final Mask m2) { final int w = m1.getWidth(); final int h = m1.getHeight(); IcyBufferedImage lbp = new IcyBufferedImage(w, h, 1, TypeUtil.TYPE_INT); int[] lbpdata = lbp.getDataXYAsInt(0); final byte[] bin1 = m1.getBinaryData().getRawData(); final byte[] bin2 = m2.getBinaryData().getRawData(); int idx = 0; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { if (bin1[idx] == BinaryIcyBufferedImage.TRUE) { long lbpidx = 0; long p2 = 1; for (IcyPixel px : intKernel) { int nx = x + (int) px.x; int ny = y + (int) px.y; if ((nx >= 0) && (nx < w) && (ny >= 0) && (ny < h) && (bin2[nx + w * ny] == BinaryIcyBufferedImage.TRUE)) { lbpidx += p2; } p2 = p2 << 1; } if (ri) { lbpidx = getRI(lbpidx); } lbpdata[idx] = (int) lbpidx; } else { lbpdata[idx] = -1; } idx++; } } lbp.dataChanged(); return lbp; } /* * public IcyBufferedImage[] computeFuzzy(IcyBufferedImage gray) { int w = * gray.getWidth(); int h = gray.getHeight(); * * IcyBufferedImage[] flbp = new IcyBufferedImage[(int) maxLBPIndex]; * double[][] id = new double[(int) maxLBPIndex][]; for (int i = 0; i < * maxLBPIndex; i++) { flbp[i] = new IcyBufferedImage(w, h, 1, * TypeUtil.TYPE_DOUBLE); id[i] = flbp[i].getDataXYAsDouble(0); } * * for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { double[] data * = getFLBP(gray, x, y); int idx = x + y * w; * * for (int i = 0; i < maxLBPIndex; i++) { id[i][idx] = data[i]; } } } * * for (int i = 0; i < maxLBPIndex; i++) { flbp[i].dataChanged(); } * * return flbp; } */ public double[] computeFuzzyFullImage(IcyBufferedImage gray) { return computeFuzzyFullImage(gray, gray); } public double[] computeFuzzyFullImage(IcyBufferedImage center, IcyBufferedImage neighbours) { int w = center.getWidth(); int h = center.getHeight(); double[] flbpsum = new double[(int) maxLBPIndex]; Arrays.fill(flbpsum, 0d); for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { double[] data = getFLBP(center, neighbours, x, y); for (int i = 0; i < maxLBPIndex; i++) { flbpsum[i] += data[i]; } } } return flbpsum; } public double[] computeFuzzyRegion(IcyBufferedImage center, IcyBufferedImage neighbours, SupportRegion<IcyPixel> reg) { double[] flbpsum = new double[(int) maxLBPIndex]; Arrays.fill(flbpsum, 0d); Rectangle2D bb = reg.getBoundingBox(); for (int x = (int)Math.floor(bb.getMinX()); x < (int)Math.floor(bb.getMaxX()); x++) { for (int y = (int)Math.floor(bb.getMinY()); y < (int)Math.floor(bb.getMaxY()); y++) { if (reg.contains(x, y)) { double[] data = getFLBP(center, neighbours, x, y); for (int i = 0; i < maxLBPIndex; i++) { flbpsum[i] += data[i]; } } } } return flbpsum; } private long varoff(IcyBufferedImage gray, int x, int y) { int ivar = 0; if (v > 1) { if (useIntensity) { double intens = SomeImageTools.getBilinearInterpolatedValue(gray, 0, x, y); ivar = (int) Math.floor(intens * v / 256.0); } else { double var = getVAR(gray, x, y); ivar = (int) Math.floor(var * v / 16384.0); } if (ivar == v) { ivar--; } } return ivar * maxLBPIndex; } public IcyBufferedImage[] compute(IcyBufferedImage gray) { int w = gray.getWidth(); int h = gray.getHeight(); IcyBufferedImage[] lbp = new IcyBufferedImage[encoding]; int[][] id = new int[encoding][]; for (int i = 0; i < encoding; i++) { lbp[i] = new IcyBufferedImage(w, h, 1, TypeUtil.TYPE_INT); id[i] = lbp[i].getDataXYAsInt(0); } for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { long varoff = varoff(gray, x, y); switch (encoding) { case LocalBinaryPattern.BINARY_ENCODING: id[0][x + y * w] = (int) (varoff + getLBP(gray, x, y)); break; case LocalBinaryPattern.TERNARY_ENCODING: long[] ids = getLTP(gray, x, y); for (int i = 0; i < encoding; i++) { id[i][x + y * w] = (int) (varoff + ids[i]); } break; } // System.out.println("idx " + idx); } } for (int i = 0; i < encoding; i++) { lbp[i].dataChanged(); } return lbp; } public long getLBP(IcyBufferedImage gray, int x, int y) { long lbp = 0; double gc = SomeImageTools.getBilinearInterpolatedValue(gray, 0, x, y); double gp, diff; long p2 = 1; for (int p = 0; p < P; p++) { gp = SomeImageTools.getBilinearInterpolatedValue(gray, 0, x + nb[p][0], y + nb[p][1]); diff = gp - gc; if (diff >= 0) { lbp += p2; } p2 = p2 << 1; } if (ri) { lbp = getRI(lbp); } return lbp; } public long[] getLTP(IcyBufferedImage gray, int x, int y) { long lbp[] = new long[2]; lbp[0] = 0; lbp[1] = 0; double gc = SomeImageTools.getBilinearInterpolatedValue(gray, 0, x, y); double gp, diff; long p2 = 1; for (int p = 0; p < P; p++) { gp = SomeImageTools.getBilinearInterpolatedValue(gray, 0, x + nb[p][0], y + nb[p][1]); diff = gp - gc; // System.out.println(diff); if (diff >= ternaryThreshold) { lbp[0] += p2; } else if (diff <= (-ternaryThreshold)) { lbp[1] += p2; } p2 = p2 << 1; } if (ri) { lbp[0] = getRI(lbp[0]); lbp[1] = getRI(lbp[1]); } return lbp; } public double[] getFLBP(IcyBufferedImage gray, int x, int y) { return getFLBP(gray, gray, x, y); } public double[] getFLBP(IcyBufferedImage center, IcyBufferedImage neighbours, int x, int y) { // double check = 0; double[] res = new double[(int) maxLBPIndex]; double gc = SomeImageTools.getBilinearInterpolatedValue(center, 0, x, y); double[] f = new double[P]; for (int p = 0; p < P; p++) { double diff = SomeImageTools.getBilinearInterpolatedValue(neighbours, 0, x + nb[p][0], y + nb[p][1]) - gc; f[p] = ff.apply(diff); } for (long i = 0; i < maxLBPIndex; i++) { double v = 1; long p2 = 1; for (int p = 0; p < P; p++) { if ((i & p2) == p2) { v *= f[p]; } else { v *= 1d - f[p]; } if (v == 0) { break; } p2 = p2 << 1; } res[(int) i] = v; // check += v; } // System.out.println("("+x+", "+y+") : " + check); return res; } public int getP() { return P; } public double getR() { return R; } public int getSignatureSize() { return (int) (v * encoding * maxLBPIndex); } public int getTernarySingleSignatureSize() { return (int) (v * maxLBPIndex); } public double getVAR(IcyBufferedImage gray, int x, int y) { double sum = 0; double mean; double varsum = 0; double var; List<Double> gps = new ArrayList<Double>(); for (int p = 0; p < P; p++) { double gp = SomeImageTools.getBilinearInterpolatedValue(gray, 0, x + nb[p][0], y + nb[p][1]); sum += gp; gps.add(gp); } mean = sum / (double) P; for (double gp : gps) { varsum += Math.pow((gp - mean), 2d); } var = varsum / (double) P; // System.out.println(var); return var; } public void initNeighbours() { nb = new double[P][2]; double x, y, c; final double cst = 2 * Math.PI / P; for (int p = 0; p < P; p++) { c = p * cst; x = -R * Math.sin(c); y = R * Math.cos(c); if (Math.abs(x) < 0.0000001) { x = 0; } if (Math.abs(y) < 0.0000001) { y = 0; } nb[p][0] = x; nb[p][1] = y; } intKernel = KernelFactory.getKernel(P, R, false); } public long getRI(long lbp) { return rilut.get(lbp); } public void initRILUT() { rilut = new HashMap<Long, Long>(); rilutid = new HashMap<Long, Long>(); long id = 0; for (long i = 0; i < maxLBPIndex; i++) { long min = i; long nv = i; for (int j = 1; j < P; j++) { nv = circularRightShift(nv); if (nv < min) { min = nv; } } if (!rilutid.containsKey(min)) { if (uniform && (uniformity(min) > 2)) { rilutid.put(min, (long) P); } else { rilutid.put(min, id++); } } min = rilutid.get(min); rilut.put(i, min); } maxLBPIndex = id; // if (uniform) { // for (long i = 0; i < originalmaxLBPIndex; i++) { // System.out.println(i + " - " + toBin(i) + " -> [" + rilut.get(i) + // "]"); // } // } // for (long idl : rilutid.keySet()) { // System.out.println(idl + " - id " + toBin(idl) + " - " + // rilutid.get(idl) + " - " + uniformity(idl)); // } } public String toBin(long i) { String r = ""; int p2 = 1; for (int p = 0; p < P; p++) { if ((i & p2) == 0) { r = "0" + r; } else { r = "1" + r; } p2 = p2 << 1; } return r; } @Override public String toString() { return getP() + ", " + getR() + (ri ? " (RI)" : ""); } public FuzzyClusteringAlgorithm getFca() { return fca; } public void setFca(FuzzyClusteringAlgorithm sm) { this.fca = sm; } public IcyBufferedImage getIndexed(SegmentableIcyBufferedImage simg) throws SignatureException { try { MaskStack seg = getSegmented(simg); IcyBufferedImage indexed = CooccurenceMatrixFactory.getIndexedImage(seg); return indexed; } catch (SupportRegionException e) { throw new SignatureException(e); } catch (MaskException e) { throw new SignatureException(e); } catch (ClusteringException e) { throw new SignatureException(e); } catch (FeatureException e) { throw new SignatureException(e); } } public MaskStack getSegmented(SegmentableIcyBufferedImage simg) throws SignatureException { try { IcySupportRegion[] regions = proc.getRegions(simg); DefaultVectorSignature[] sigs = proc.getSignatures(simg, regions); MaskStack seg = new MaskStack(simg.getWidth(), simg.getHeight()); proc.addToMaskStack(fca, simg.getImage(), seg, regions, sigs); return seg; } catch (SupportRegionException e) { throw new SignatureException(e); } catch (MaskException e) { throw new SignatureException(e); } catch (ClusteringException e) { throw new SignatureException(e); } catch (FeatureException e) { throw new SignatureException(e); } } public IcyBufferedImage[] getMembershipImages(SegmentableIcyBufferedImage simg) throws SignatureException { try { IcyBufferedImage[] res = new IcyBufferedImage[fca.getNbClasses()]; IcySupportRegion[] regions = proc.getRegions(simg); DefaultVectorSignature[] sigs = proc.getSignatures(simg, regions); for (int i = 0; i < fca.getNbClasses(); i++) { double[] mb = fca.getMemberships(sigs, i); res[i] = proc.getAsImage(mb, regions, simg.getWidth(), simg.getHeight()); } return res; } catch (SupportRegionException e) { throw new SignatureException(e); } catch (ClusteringException e) { throw new SignatureException(e); } catch (FeatureException e) { throw new SignatureException(e); } } public IcyBufferedImage[] getMembershipImages(SegmentableIcyBufferedImage simg, List<Integer> workOnCanals) throws SignatureException { try { IcyBufferedImage[] res = new IcyBufferedImage[fca.getNbClasses()]; IcySupportRegion[] regions = proc.getRegions(simg); DefaultVectorSignature[] sigs = proc.getSignatures(simg, regions); for (int c : workOnCanals) { double[] mb = fca.getMemberships(sigs, c, workOnCanals); res[c] = proc.getAsImage(mb, regions, simg.getWidth(), simg.getHeight()); } return res; } catch (SupportRegionException e) { throw new SignatureException(e); } catch (ClusteringException e) { throw new SignatureException(e); } catch (FeatureException e) { throw new SignatureException(e); } } public IcyBufferedImage getMembershipImage(SegmentableIcyBufferedImage simg, int canal) throws SignatureException { try { IcySupportRegion[] regions = proc.getRegions(simg); DefaultVectorSignature[] sigs = proc.getSignatures(simg, regions); double[] mb = fca.getMemberships(sigs, canal); return proc.getAsImage(mb, regions, simg.getWidth(), simg.getHeight()); } catch (SupportRegionException e) { throw new SignatureException(e); } catch (ClusteringException e) { throw new SignatureException(e); } catch (FeatureException e) { throw new SignatureException(e); } } public int getEncoding() { return encoding; } public double getTernaryThreshold() { return ternaryThreshold; } public void setTernaryThreshold(double ternaryThreshold) { this.ternaryThreshold = ternaryThreshold; } public void setFuzzyFunction(int type, double param) { switch (type) { case FUZZY_FUNCTION_STANDARD: ff = new StandardFuzzyFunction(param); break; case FUZZY_FUNCTION_TANH: ff = new TanhFuzzyFunction(param); break; case FUZZY_FUNCTION_STEP: ff = new StepFuzzyFunction(param); break; } } public FuzzyClusteringProcessor getFuzzyClusteringProcessor() { return proc; } public void setFuzzyClusteringProcessor(FuzzyClusteringProcessor proc) { this.proc = proc; } public boolean isRotationInvariant() { return ri; } }