Package com.alibaba.security.simpleimage.analyze.sift.scala

Source Code of com.alibaba.security.simpleimage.analyze.sift.scala.DScaleSpace$RefCheckMark

/*
* Copyright 2013 Alibaba.com All right reserved. This software is the
* confidential and proprietary information of Alibaba.com ("Confidential
* Information"). You shall not disclose such Confidential Information and shall
* use it only in accordance with the terms of the license agreement you entered
* into with Alibaba.com.
*/
package com.alibaba.security.simpleimage.analyze.sift.scala;

import java.util.ArrayList;

import com.alibaba.security.simpleimage.analyze.reftype.RefDouble;
import com.alibaba.security.simpleimage.analyze.sift.ImageMap;
import com.alibaba.security.simpleimage.analyze.sift.conv.GaussianConvolution;
import com.alibaba.security.simpleimage.analyze.sift.matrix.SimpleMatrix;

/**
* 类DScaleSpace.java的实现描述:TODO 类实现描述
*
* @author axman 2013-3-25 上午11:12:20
*/
public class DScaleSpace {

    boolean     verbose = System.getProperty("_verbose") == null ? false : true;
    DScaleSpace down    = null;
    DScaleSpace up      = null;

    public void setVerbose(boolean verbose) {
        this.verbose = verbose;
    }

    public DScaleSpace getDown() {
        return this.down;
    }

    public void setDown(DScaleSpace down) {
        this.down = down;
    }

    public DScaleSpace getUp() {
        return this.up;
    }

    public void setUp(DScaleSpace up) {
        this.up = up;
    }

    ImageMap   baseImg;
    double     basePixScale;

    ImageMap[] imgScaled;

    public ImageMap getGaussianMap(int idx) {
        return this.imgScaled[idx];
    }

    private ImageMap[] magnitudes;
    private ImageMap[] directions;

    public ImageMap getLastGaussianMap() {
        if (this.imgScaled.length < 2) {
            throw new java.lang.IllegalArgumentException("err: too few gaussian maps.");
        }
        return (this.imgScaled[this.imgScaled.length - 2]);
    }

    public ImageMap[] spaces;

    public int getCount() {
        return this.spaces.length;
    }

    public ArrayList<ScalePoint> findPeaks(double dogThresh) {
        if (verbose) System.out.printf("FindPeaks: scale %9.2f, testing %d levels\r\n", basePixScale,
                                       this.spaces.length - 2);

        ArrayList<ScalePoint> peaks = new ArrayList<ScalePoint>();

        ImageMap current, above, below;

        // Search the D(k * sigma) to D(2 * sigma) spaces
        for (int level = 1; level < (this.spaces.length - 1); ++level) {
            current = this.spaces[level];
            below = this.spaces[level - 1];
            above = this.spaces[level + 1];

            //System.out.printf("peak-search at level %d\r\n", level);
            /*
             * Console.WriteLine ("below/current/above: {0} {1} {2}", below == null ? "-" : "X", current == null ? "-" :
             * "X", above == null ? "-" : "X"); Console.WriteLine ("peak-search at level {0}", level);
             */

            peaks.addAll(findPeaksThreeLevel(below, current, above, level, dogThresh));
            below = current;
        }

        return (peaks);
    }

    public ArrayList<KeyPoint> generateKeyPoints(ArrayList<ScalePoint> localizedPeaks, int scaleCount,
                                                 double octaveSigma) {
        ArrayList<KeyPoint> keypoints = new ArrayList<KeyPoint>();
        for (ScalePoint sp : localizedPeaks) {
            ArrayList<KeyPoint> thisPointKeys = generateKeyPointSingle(this.basePixScale, sp, 36, 0.8, scaleCount,
                                                                       octaveSigma);
            thisPointKeys = createDescriptors(thisPointKeys, magnitudes[sp.getLevel()], directions[sp.getLevel()], 2.0,
                                              4, 8, 0.2);
            for (KeyPoint kp : thisPointKeys) {
                if (kp.hasFV == false) {
                    throw new java.lang.IllegalStateException("should not happen");
                }

                kp.x *= kp.imgScale;
                kp.y *= kp.imgScale;
                kp.scale *= kp.imgScale;
                keypoints.add(kp);
            }
        }
        return keypoints;
    }

    private ArrayList<KeyPoint> generateKeyPointSingle(double imgScale, ScalePoint point, int binCount,
                                                       double peakRelThresh, int scaleCount, double octaveSigma) {

        // The relative estimated keypoint scale. The actual absolute keypoint
        // scale to the original image is yielded by the product of imgScale.
        // But as we operate in the current octave, the size relative to the
        // anchoring images is missing the imgScale factor.
        double kpScale = octaveSigma * Math.pow(2.0, (point.level + point.local.scaleAdjust) / scaleCount);

        // Lowe03, "A gaussian-weighted circular window with a \sigma three
        // times that of the scale of the keypoint".
        //
        // With sigma = 3.0 * kpScale, the square dimension we have to
        // consider is (3 * sigma) (until the weight becomes very small).
        double sigma = 3.0 * kpScale;
        int radius = (int) (3.0 * sigma / 2.0 + 0.5);
        int radiusSq = radius * radius;

        ImageMap magnitude = magnitudes[point.level];
        ImageMap direction = directions[point.level];

        // As the point may lie near the border, build the rectangle
        // coordinates we can still reach, minus the border pixels, for which
        // we do not have gradient information available.
        int xMin = Math.max(point.x - radius, 1);
        int xMax = Math.min(point.x + radius, magnitude.xDim - 1);
        int yMin = Math.max(point.y - radius, 1);
        int yMax = Math.min(point.y + radius, magnitude.yDim - 1);

        // Precompute 1D gaussian divisor (2 \sigma^2) in:
        // G(r) = e^{-\frac{r^2}{2 \sigma^2}}
        double gaussianSigmaFactor = 2.0 * sigma * sigma;

        double[] bins = new double[binCount];

        // Build the direction histogram
        for (int y = yMin; y < yMax; ++y) {
            for (int x = xMin; x < xMax; ++x) {
                // Only consider pixels in the circle, else we might skew the
                // orientation histogram by considering more pixels into the
                // corner directions
                int relX = x - point.x;// 求半径
                int relY = y - point.y;// 求半径
                if (isInCircle(relX, relY, radiusSq) == false) continue;

                // The gaussian weight factor.
                double gaussianWeight = Math.exp(-((relX * relX + relY * relY) / gaussianSigmaFactor));

                // find the closest bin and add the direction
                int binIdx = findClosestRotationBin(binCount, direction.valArr[y][x]);
                bins[binIdx] += magnitude.valArr[y][x] * gaussianWeight;
            }
        }

        // As there may be succeeding histogram entries like this:
        // ( ..., 0.4, 0.3, 0.4, ... ) where the real peak is located at the
        // middle of this three entries, we can improve the distinctiveness of
        // the bins by applying an averaging pass.
        //
        // is this really the best method? (we also loose a bit of
        // information. Maybe there is a one-step method that conserves more)
        averageWeakBins(bins, binCount);

        // find the maximum peak in gradient orientation
        double maxGrad = 0.0;
        int maxBin = 0;
        for (int b = 0; b < binCount; ++b) {
            if (bins[b] > maxGrad) {
                maxGrad = bins[b];
                maxBin = b;
            }
        }

        // First determine the real interpolated peak high at the maximum bin
        // position, which is guaranteed to be an absolute peak.
        //
        // XXX: should we use the estimated peak value as reference for the
        // 0.8 check or the original bin-value?

        RefPVAndDC ref1 = new RefPVAndDC();
        InterpolateOrientation(bins[maxBin == 0 ? (binCount - 1) : (maxBin - 1)], bins[maxBin], bins[(maxBin + 1)
                                                                                                     % binCount], ref1);

        // Now that we know the maximum peak value, we can find other keypoint
        // orientations, which have to fulfill two criterias:
        // 这样找到的不止是两个最大的方向 看
        // paper pager 13
        // Therefore,for locations with multiple peaks of similar magnitude
        // ,there will be multiple keypoints created at the same location and
        // scale but different orientations 2006.03.8
        //
        // 1. They must be a local peak themselves. Else we might add a very
        // similar keypoint orientation twice (imagine for example the
        // values: 0.4 1.0 0.8, if 1.0 is maximum peak, 0.8 is still added
        // with the default threshhold, but the maximum peak orientation
        // was already added).
        // 2. They must have at least peakRelThresh times the maximum peak
        // value.
        boolean[] binIsKeypoint = new boolean[binCount];
        for (int b = 0; b < binCount; ++b) {
            binIsKeypoint[b] = false;

            // The maximum peak of course is
            if (b == maxBin) {
                binIsKeypoint[b] = true;
                continue;
            }

            // Local peaks are, too, in case they fulfill the threshhold
            if (bins[b] < (peakRelThresh * ref1.peakValue)) continue;

            int leftI = (b == 0) ? (binCount - 1) : (b - 1);
            int rightI = (b + 1) % binCount;
            if (bins[b] <= bins[leftI] || bins[b] <= bins[rightI]) continue; // no local peak

            binIsKeypoint[b] = true;
        }

        // All the valid keypoint bins are now marked in binIsKeypoint, now
        // build them.
        ArrayList<KeyPoint> keypoints = new ArrayList<KeyPoint>();

        // find other possible locations
        double oneBinRad = (2.0 * Math.PI) / binCount;

        for (int b = 0; b < binCount; ++b) {
            if (binIsKeypoint[b] == false) continue;

            int bLeft = (b == 0) ? (binCount - 1) : (b - 1);
            int bRight = (b + 1) % binCount;

            // Get an interpolated peak direction and value guess.
            // double peakValue;
            // double degreeCorrection;
            RefPVAndDC ref2 = new RefPVAndDC();

            if (InterpolateOrientation(bins[bLeft], bins[b], bins[bRight], ref2) == false) {
                throw (new java.lang.IllegalStateException("BUG: Parabola fitting broken"));
            }

            // [-1.0 ; 1.0] -> [0 ; binrange], and add the fixed absolute bin
            // position.
            // We subtract PI because bin 0 refers to 0, binCount-1 bin refers
            // to a bin just below 2PI, so -> [-PI ; PI]. Note that at this
            // point we determine the canonical descriptor anchor angle. It
            // does not matter where we set it relative to the peak degree,
            // but it has to be constant. Also, if the output of this
            // implementation is to be matched with other implementations it
            // must be the same constant angle (here: -PI).
            double degree = (b + ref2.degreeCorrection) * oneBinRad - Math.PI;
            // 完全化在 -180 到 +180 之间
            if (degree < -Math.PI) degree += 2.0 * Math.PI;
            else if (degree > Math.PI) degree -= 2.0 * Math.PI;

            KeyPoint kp = new KeyPoint(imgScaled[point.level], point.x + point.local.fineX,
                                       point.y + point.local.fineY, imgScale, kpScale, degree);
            keypoints.add(kp);
        }

        return (keypoints);
    }

    private ArrayList<KeyPoint> createDescriptors(ArrayList<KeyPoint> keypoints, ImageMap magnitude,
                                                  ImageMap direction, double considerScaleFactor, int descDim,
                                                  int directionCount, double fvGradHicap) {
        if (keypoints.size() <= 0) return (keypoints);
        // 通过尺度因子找到周围所包含的像素
        considerScaleFactor *= keypoints.get(0).scale;
        double dDim05 = ((double) descDim) / 2.0;

        // Now calculate the radius: We consider pixels in a square with
        // dimension 'descDim' plus 0.5 in each direction. As the feature
        // vector elements at the diagonal borders are most distant from the
        // center pixel we have scale up with sqrt(2).
        int radius = (int) (((descDim + 1.0) / 2) * Math.sqrt(2.0) * considerScaleFactor + 0.5);

        // Instead of modifying the original list, we just copy the keypoints
        // that received a descriptor.
        ArrayList<KeyPoint> survivors = new ArrayList<KeyPoint>();

        // Precompute the sigma for the "center-most, border-less" gaussian
        // weighting.
        // (We are operating to dDim05, CV(computer vision) book tells us G(x), x > 3 * sigma
        // negligible, but this range seems much shorter!?)
        //
        // In Lowe03, page 15 it says "A Gaussian weighting function with
        // sigma equal to one half the width of the descriptor window is
        // used", so we just use his advice.它指的是描述器窗口的一半
        double sigma2Sq = 2.0 * dDim05 * dDim05;// 2 * sigma ^2是高斯函数e指数上的分母上的数

        for (KeyPoint kp : keypoints) {
            // The angle to rotate with: negate the orientation.
            double angle = -kp.orientation;// 旋转-angle拉到水平方向上来

            kp.createVector(descDim, descDim, directionCount);
            // Console.WriteLine ("  FV allocated");
            // 旋转angle度的坐标
            for (int y = -radius; y < radius; ++y) {
                for (int x = -radius; x < radius; ++x) {
                    // Rotate and scale
                    double yR = Math.sin(angle) * x + Math.cos(angle) * y;
                    double xR = Math.cos(angle) * x - Math.sin(angle) * y;

                    // 使他定义在描述器的纬度之内
                    yR /= considerScaleFactor; // yR = yR / considerScaleFactor
                    xR /= considerScaleFactor; // yR = yR / considerScaleFactor

                    // Now consider all (xR, yR) that are anchored within
                    // (- descDim/2 - 0.5 ; -descDim/2 - 0.5) to
                    // (descDim/2 + 0.5 ; descDim/2 + 0.5),
                    // as only those can influence the FV.
                    // 使该点不超出描述器的范围
                    if (yR >= (dDim05 + 0.5) || xR >= (dDim05 + 0.5) || xR <= -(dDim05 + 0.5) || yR <= -(dDim05 + 0.5)) continue;
                    // 计算关键点和加权的点的具体x位置
                    int currentX = (int) (x + kp.x + 0.5);
                    // 计算关键点和加权的点的具体y位置
                    int currentY = (int) (y + kp.y + 0.5);
                    // 这保证它在范围之内部出去
                    if (currentX < 1 || currentX >= (magnitude.xDim - 1) || currentY < 1
                        || currentY >= (magnitude.yDim - 1)) continue;

                    /*
                     * Console.WriteLine ("    ({0},{1}) by angle {2} -> ({3},{4})", x, y, angle, xR, yR);
                     */

                    // Weight the magnitude relative to the center of the
                    // whole FV. We do not need a normalizing factor now, as
                    // we normalize the whole FV later anyway (see below).
                    // xR, yR are each in -(dDim05 + 0.5) to (dDim05 + 0.5)
                    // range
                    // 高斯权重的计算
                    double magW = Math.exp(-(xR * xR + yR * yR) / sigma2Sq) * magnitude.valArr[currentY][currentX];

                    // Anchor to (-1.0, -1.0)-(dDim + 1.0, dDim + 1.0), where
                    // the FV points are located at ( x , y )
                    yR += dDim05 - 0.5;
                    xR += dDim05 - 0.5;

                    // 记住在两个点之间有阶跃的时候都可以用插值

                    // Build linear interpolation weights:
                    // A B
                    // C D
                    //
                    // The keypoint is located between A, B, C and D.
                    int[] xIdx = new int[2];
                    int[] yIdx = new int[2];
                    int[] dirIdx = new int[2]; // 每个点的坐标的orientation索引 [0] 方向的值 [1]是那个方向
                    double[] xWeight = new double[2];
                    double[] yWeight = new double[2];
                    double[] dirWeight = new double[2];// 方向上
                    // 可能在做插值
                    if (xR >= 0) {
                        xIdx[0] = (int) xR;
                        xWeight[0] = (1.0 - (xR - xIdx[0]));
                    }
                    if (yR >= 0) {
                        yIdx[0] = (int) yR;
                        yWeight[0] = (1.0 - (yR - yIdx[0]));
                    }

                    if (xR < (descDim - 1)) {
                        xIdx[1] = (int) (xR + 1.0);
                        xWeight[1] = xR - xIdx[1] + 1.0;
                    }
                    if (yR < (descDim - 1)) {
                        yIdx[1] = (int) (yR + 1.0);
                        yWeight[1] = yR - yIdx[1] + 1.0;
                    }
                    // end 可能在做插值

                    // Rotate the gradient direction by the keypoint
                    // orientation, then normalize to [-pi ; pi] range.
                    // 旋转角度到keypoint的坐标下来,并且用[ -pi : pi ] 来表示
                    double dir = direction.valArr[currentY][currentX] - kp.orientation;
                    if (dir <= -Math.PI) dir += Math.PI;
                    if (dir > Math.PI) dir -= Math.PI;
                    // 统一在分着个八个方向上来
                    double idxDir = (dir * directionCount) / (2.0 * Math.PI);// directionCount/8为每一个度数有几个方向,然后 *
                                                                             // dir就统一到一至的方向上来了
                    if (idxDir < 0.0) idxDir += directionCount;

                    dirIdx[0] = (int) idxDir;
                    dirIdx[1] = (dirIdx[0] + 1) % directionCount; // 下一个方向
                    dirWeight[0] = 1.0 - (idxDir - dirIdx[0]); // 和下一个方向所差的值
                    dirWeight[1] = idxDir - dirIdx[0]; // 和所在方向所差的值

                    /*
                     * Console.WriteLine ("    ({0},{1}) yields:", xR, yR); Console.WriteLine
                     * ("      x<{0},{1}>*({2},{3})", xIdx[0], xIdx[1], xWeight[0], xWeight[1]); Console.WriteLine
                     * ("      y<{0},{1}>*({2},{3})", yIdx[0], yIdx[1], yWeight[0], yWeight[1]); Console.WriteLine
                     * ("      dir<{0},{1}>*({2},{3})", dirIdx[0], dirIdx[1], dirWeight[0], dirWeight[1]);
                     * Console.WriteLine ("    weighting m * w: {0} * {1}", magnitude[currentX, currentY], Math.Exp
                     * (-(xR * xR + yR * yR) / sigma2Sq));
                     */
                    for (int iy = 0; iy < 2; ++iy) {
                        for (int ix = 0; ix < 2; ++ix) {
                            for (int id = 0; id < 2; ++id) {
                                kp.featureVectorSet(xIdx[ix], yIdx[iy], dirIdx[id],
                                                    kp.featureVectorGet(xIdx[ix], yIdx[iy], dirIdx[id]) + xWeight[ix]
                                                            * yWeight[iy] * dirWeight[id] * magW);
                            }
                        }
                    }
                }
            }

            // Normalize and hicap the feature vector, as recommended on page
            // 16 in Lowe03.
            capAndNormalizeFV(kp, fvGradHicap);

            survivors.add(kp);
        }

        return (survivors);
    }

    private boolean isInCircle(int rX, int rY, int radiusSq) {
        rX *= rX;
        rY *= rY;
        if ((rX + rY) <= radiusSq) return (true);

        return (false);
    }

    private ArrayList<ScalePoint> findPeaksThreeLevel(ImageMap below, ImageMap current, ImageMap above, int curLev,
                                                      double dogThresh) {
        ArrayList<ScalePoint> peaks = new ArrayList<ScalePoint>();

        for (int y = 1; y < (current.yDim - 1); ++y) {
            for (int x = 1; x < (current.xDim - 1); ++x) {
                RefCheckMark ref = new RefCheckMark();
                ref.isMin = true;
                ref.isMax = true;

                double c = current.valArr[y][x]; // Center value

                /*
                 * If the magnitude is below the threshhold, skip it early.
                 */
                if (Math.abs(c) <= dogThresh) continue;

                checkMinMax(current, c, x, y, ref, true);
                checkMinMax(below, c, x, y, ref, false);
                checkMinMax(above, c, x, y, ref, false);
                if (ref.isMin == false && ref.isMax == false) continue;

                // Console.WriteLine ("{0} {1} {2} # DOG", y, x, c);

                /*
                 * Add the peak that survived the first checks, to the peak list.
                 */
                peaks.add(new ScalePoint(x, y, curLev));
            }
        }

        return (peaks);
    }

    static class RefCheckMark {

        /**
         * 用于传递引用的数据结构
         */
        boolean isMin;
        boolean isMax;
    }

    static class RefPVAndDC {

        /**
         * 用于传递引用的数据结构
         */
        double peakValue;
        double degreeCorrection;
    }

    private void checkMinMax(ImageMap layer, double c, int x, int y, RefCheckMark ref, boolean cLayer) {
        if (layer == null) return;

        if (ref.isMin) {
            if (layer.valArr[y - 1][x - 1] <= c || layer.valArr[y][x - 1] <= c || layer.valArr[y + 1][x - 1] <= c
                || layer.valArr[y - 1][x] <= c
                ||
                // note here its just < instead of <=
                (cLayer ? false : (layer.valArr[y][x] < c)) || layer.valArr[y + 1][x] <= c
                || layer.valArr[y - 1][x + 1] <= c || layer.valArr[y][x + 1] <= c || layer.valArr[y + 1][x + 1] <= c) ref.isMin = false;
        }
        if (ref.isMax) {
            if (layer.valArr[y - 1][x - 1] >= c || layer.valArr[y][x - 1] >= c || layer.valArr[y + 1][x - 1] >= c
                || layer.valArr[y - 1][x] >= c
                ||
                // note here its just > instead of >=
                (cLayer ? false : (layer.valArr[y][x] > c)) || layer.valArr[y + 1][x] >= c
                || layer.valArr[y - 1][x + 1] >= c || layer.valArr[y][x + 1] >= c || layer.valArr[y + 1][x + 1] >= c) ref.isMax = false;
        }
    }

    private int findClosestRotationBin(int binCount, double angle) {
        angle += Math.PI;
        angle /= 2.0 * Math.PI;
        // calculate the aligned bin
        angle *= binCount;

        int idx = (int) angle;
        if (idx == binCount) idx = 0;
        return (idx);
    }

    private void averageWeakBins(double[] bins, int binCount) {
        // ( 0.4, 0.4, 0.3, 0.4, 0.4 ))
        // 每三个做一个平均直至完成
        for (int sn = 0; sn < 4; ++sn) {
            double firstE = bins[0];
            double last = bins[binCount - 1];

            for (int sw = 0; sw < binCount; ++sw) {
                double cur = bins[sw];
                double next = (sw == (binCount - 1)) ? firstE : bins[(sw + 1) % binCount];

                bins[sw] = (last + cur + next) / 3.0;
                last = cur;
            }
        }
    }

    private boolean InterpolateOrientation(double left, double middle, double right, RefPVAndDC ref) {
        double a = ((left + right) - 2.0 * middle) / 2.0;
        ref.degreeCorrection = ref.peakValue = Double.NaN;

        // Not a parabol
        if (a == 0.0) return (false);

        double c = (((left - middle) / a) - 1.0) / 2.0;
        double b = middle - c * c * a;

        if (c < -0.5 || c > 0.5) throw (new IllegalStateException("InterpolateOrientation: off peak ]-0.5 ; 0.5["));

        ref.degreeCorrection = c;
        ref.peakValue = b;

        return true;
    }

    private void capAndNormalizeFV(KeyPoint kp, double fvGradHicap) {
        // Straight normalization
        double norm = 0.0;
        for (int n = 0; n < kp.getFVLinearDim(); ++n)
            norm += Math.pow(kp.featureVectorLinearGet(n), 2.0);// 所有的值平方

        norm = Math.sqrt(norm);// feature vector的模
        if (norm == 0.0) throw (new java.lang.IllegalStateException(
                                                                    "CapAndNormalizeFV cannot normalize with norm = 0.0"));

        for (int n = 0; n < kp.getFVLinearDim(); ++n)
            kp.featureVectorLinearSet(n, kp.featureVectorLinearGet(n) / norm);

        // Hicap after normalization
        for (int n = 0; n < kp.getFVLinearDim(); ++n) {
            if (kp.featureVectorLinearGet(n) > fvGradHicap) {
                kp.featureVectorLinearSet(n, fvGradHicap);
            }
        }

        // Renormalize again
        norm = 0.0;
        for (int n = 0; n < kp.getFVLinearDim(); ++n)
            norm += Math.pow(kp.featureVectorLinearGet(n), 2.0);

        norm = Math.sqrt(norm);

        for (int n = 0; n < kp.getFVLinearDim(); ++n)
            kp.featureVectorLinearSet(n, kp.featureVectorLinearGet(n) / norm);
    }

    /**
     * @param peaks
     * @param maximumEdgeRatio
     * @param dValueLowThresh
     * @param scaleAdjustThresh
     * @param relocationMaximum
     * @return
     */
    public ArrayList<ScalePoint> filterAndLocalizePeaks(ArrayList<ScalePoint> peaks, double edgeRatio,
                                                        double dValueLoThresh, double scaleAdjustThresh,
                                                        int relocationMaximum) {
        ArrayList<ScalePoint> filtered = new ArrayList<ScalePoint>();

        int[][] processedMap = new int[spaces[0].xDim][spaces[0].yDim];

        for (ScalePoint peak : peaks) {
            if (isTooEdgelike(spaces[peak.level], peak.x, peak.y, edgeRatio)) continue;

            // When the localization hits some problem, i.e. while moving the
            // point a border is reached, then skip this point.

            if (localizeIsWeak(peak, relocationMaximum, processedMap)) continue;

            /*
             * Console.WriteLine ("peak.Local.ScaleAdjust = {0}", peak.Local.ScaleAdjust);
             */
            if (Math.abs(peak.local.scaleAdjust) > scaleAdjustThresh) continue;

            // Additional local pixel information is now available, threshhold
            // the D(^x)
            // Console.WriteLine ("{0} {1} {2} # == DVALUE", peak.Y, peak.X, peak.Local.DValue);

            if (Math.abs(peak.local.dValue) <= dValueLoThresh) continue;

            /*
             * Console.WriteLine ("{0} {1} {2} {3} # FILTERLOCALIZE", peak.Y, peak.X, peak.Local.ScaleAdjust,
             * peak.Local.DValue);
             */

            // its edgy enough, add it
            filtered.add(peak);

        }
        return (filtered);
    }

    public void generateMagnitudeAndDirectionMaps() {
        // We leave the first entry to null, and ommit the last. This way, the
        // magnitudes and directions maps have the same index as the
        // imgScaled maps they below to.
        magnitudes = new ImageMap[this.getCount() - 1];// 图像的数组
        directions = new ImageMap[this.getCount() - 1];// 图像的数组

        // Build the maps, omitting the border pixels, as we cannot build
        // gradient information there.
        // top left border corner -> * * *
        // * [*] *
        // * * *
        //
        for (int s = 1; s < (this.getCount() - 1); ++s) {
            magnitudes[s] = new ImageMap(imgScaled[s].xDim, imgScaled[s].yDim);
            directions[s] = new ImageMap(imgScaled[s].xDim, imgScaled[s].yDim);

            for (int y = 1; y < (imgScaled[s].yDim - 1); ++y) {
                for (int x = 1; x < (imgScaled[s].xDim - 1); ++x) {
                    // gradient magnitude m
                    magnitudes[s].valArr[y][x] = Math.sqrt(Math.pow(imgScaled[s].valArr[y][x + 1]
                                                                    - imgScaled[s].valArr[y][x - 1], 2.0)
                                                           + Math.pow(imgScaled[s].valArr[y + 1][x]
                                                                      - imgScaled[s].valArr[y - 1][x], 2.0));

                    // gradient direction theta
                    directions[s].valArr[y][x] = Math.atan2(imgScaled[s].valArr[y + 1][x]
                                                                    - imgScaled[s].valArr[y - 1][x],
                                                            imgScaled[s].valArr[y][x + 1]
                                                                    - imgScaled[s].valArr[y][x - 1]);
                }
            }
        }
    }

    /**
     *
     */
    public void clearMagnitudeAndDirectionMaps() {
        for (int i = 0; i < this.magnitudes.length; i++)
            this.magnitudes[i] = null;
        for (int i = 0; i < this.directions.length; i++)
            this.directions[i] = null;
        magnitudes = directions = null;
    }

    public void buildDiffMaps() {
        // Generate DoG maps. The maps are organized like this:
        // 0: D(sigma)
        // 1: D(k * sigma)
        // 2: D(k^2 * sigma)
        // ...
        // s: D(k^s * sigma) = D(2 * sigma)
        // s+1: D(k * 2 * sigma)
        //
        // So, we can start peak searching at 1 to s, and have a DoG map into
        // each direction.
        spaces = new ImageMap[imgScaled.length - 1];

        // After the loop completes, we have used (s + 1) maps, yielding s
        // D-gaussian maps in the range of sigma to 2*sigma, as k^s = 2, which
        // is defined as one octave.
        for (int sn = 0; sn < spaces.length; ++sn) {
            // XXX: order correct? It should not really matter as we search
            // for both minimums and maximums, but still, what is recommended?
            // (otherwise maybe the gradient directions are inverted?)
            spaces[sn] = ImageMap.minus(imgScaled[sn + 1], imgScaled[sn]);
        }
    }

    public void buildGaussianMaps(ImageMap first, double firstScale, int scales, double sigma) {
        // We need one more gaussian blurred image than the number of DoG
        // maps. But for the minima/maxima pixel search, we need two more. See
        // BuildDiffMaps.
        imgScaled = new ImageMap[scales + 1 + 1 + 1];
        this.basePixScale = firstScale;
        // Ln1(x, y, k^{p+1}) = G(x, y, \sqrt{k^2-1}) * Ln0(x, y, k^p).
        ImageMap prev = first;
        imgScaled[0] = first;

        double w = sigma;
        double kTerm = Math.sqrt(Math.pow(SToK(scales), 2.0) - 1.0);
        for (int scI = 1; scI < imgScaled.length; ++scI) {
            GaussianConvolution gauss = new GaussianConvolution(w * kTerm);
            prev = imgScaled[scI] = gauss.convolve(prev);
            w *= SToK(scales);
        }
    }

    static public double SToK(int s) {
        return (Math.pow(2.0, 1.0 / s));
    }

    private SimpleMatrix getAdjustment(ScalePoint point, int level, int x, int y, RefDouble ref) {
        /*
         * Console.WriteLine ("GetAdjustment (point, {0}, {1}, {2}, out double dp)", level, x, y);
         */
        ref.val = 0.0;
        if (point.level <= 0 || point.level >= (spaces.length - 1)) throw (new IllegalArgumentException(
                                                                                                        "point.Level is not within [bottom-1;top-1] range"));

        ImageMap below = spaces[level - 1];
        ImageMap current = spaces[level];
        ImageMap above = spaces[level + 1];

        SimpleMatrix H = new SimpleMatrix(3, 3);
        /*
         * 下面是该幅图像尺度空间的三元偏导数,记住是尺度空间上 的二阶自变量为3的偏导数2006.3.1
         */
        H.values[0][0] = below.valArr[y][x] - 2 * current.valArr[y][x] + above.valArr[y][x];
        H.values[0][1] = H.values[1][0] = 0.25 * (above.valArr[y + 1][x] - above.valArr[y - 1][x] - (below.valArr[y + 1][x] - below.valArr[y - 1][x]));
        H.values[0][2] = H.values[2][0] = 0.25 * (above.valArr[y][x + 1] - above.valArr[y][x - 1] - (below.valArr[y][x + 1] - below.valArr[y][x - 1]));
        H.values[1][1] = current.valArr[y - 1][x] - 2 * current.valArr[y][x] + current.valArr[y + 1][x];
        H.values[1][2] = H.values[2][1] = 0.25 * (current.valArr[y + 1][x + 1] - current.valArr[y + 1][x - 1] - (current.valArr[y - 1][x + 1] - current.valArr[y - 1][x - 1]));
        H.values[2][2] = current.valArr[y][x - 1] - 2 * current.valArr[y][x] + current.valArr[y][x + 1];

        SimpleMatrix d = new SimpleMatrix(3, 1);
        /*
         * 下面这个是自变量为3的一阶偏导数2006.3.1
         */
        d.values[0][0] = 0.5 * (above.valArr[y][x] - below.valArr[y][x]);
        d.values[1][0] = 0.5 * (current.valArr[y + 1][x] - current.valArr[y - 1][x]);
        d.values[2][0] = 0.5 * (current.valArr[y][x + 1] - current.valArr[y][x - 1]);

        SimpleMatrix b = (SimpleMatrix) d.clone();
        b.negate();
        // Solve: A x = b
        H.solveLinear(b);
        ref.val = b.dot(d);
        return (b);
    }

    private boolean isTooEdgelike(ImageMap space, int x, int y, double r) {
        double D_xx, D_yy, D_xy;

        // Calculate the Hessian H elements [ D_xx, D_xy ; D_xy , D_yy ]
        /*
         * 感谢徐小明的推导 2006.02.26.于重大数字图像实验室 d_xx = d_f(x+1) - d_f( x );0 d_f(x+1) = f(x+1) - f( x ); 1 d_f(x) = f(x) - f(
         * x-1 );2 将 1, 2式代入0式得 d_xx = f(x+1) + f(x-1) - 2 * f(x); 对于d_xy = ( d_f( x , y+1 ) - d_f( x, y-1 ) ) * 0.5; 0
         * d_f(x,y+1) = (f(x+1,y+1) - f(x-1,y+1)) * 0.5; 1 d_f(x,y-1) = (f(x+1,y-1) - f(x-1,y-1)) * 0.5; 2 将1,2代入 0 式
         * (f(x+1,y+1)+f(x+1,y-1)-f(x-1,y+1)-f(x-1,y-1)) * 0.25
         */
        D_xx = space.valArr[y + 1][x] + space.valArr[y - 1][x] - 2.0 * space.valArr[y][x];
        D_yy = space.valArr[y][x + 1] + space.valArr[y][x - 1] - 2.0 * space.valArr[y][x];
        D_xy = 0.25 * ((space.valArr[y + 1][x + 1] - space.valArr[y + 1][x - 1]) - (space.valArr[y - 1][x + 1] - space.valArr[y - 1][x - 1]));

        // page 13 in Lowe's paper
        double TrHsq = D_xx + D_yy;
        TrHsq *= TrHsq;
        double DetH = D_xx * D_yy - (D_xy * D_xy);

        double r1sq = (r + 1.0);
        r1sq *= r1sq;

        // BUG: this can invert < to >, uhh: if ((TrHsq * r) < (DetH * r1sq))
        if ((TrHsq / DetH) < (r1sq / r)) {
            /*
             * Console.WriteLine ("{0} {1} {2} {3} {4} # EDGETEST", y, x, (TrHsq * r), (DetH * r1sq), (TrHsq / DetH) /
             * (r1sq / r));
             */
            return (false);
        }

        return (true);
    }

    // 1,302,57,4,547,187

    boolean localizeIsWeak(ScalePoint point, int steps, int[][] processed) {
        boolean needToAdjust = true;
        int adjusted = steps;
        while (needToAdjust) {
            int x = point.x;
            int y = point.y;

            // Points we cannot say anything about, as they lie on the border
            // of the scale space
            if (point.level <= 0 || point.level >= (spaces.length - 1)) return (true);

            ImageMap space = spaces[point.level];
            if (x <= 0 || x >= (space.xDim - 1)) return (true);
            if (y <= 0 || y >= (space.yDim - 1)) return (true);

            RefDouble dp = new RefDouble();
            SimpleMatrix adj = getAdjustment(point, point.level, x, y, dp);

            // Get adjustments and check if we require further adjustments due
            // to pixel level moves. If so, turn the adjustments into real
            // changes and continue the loop. Do not adjust the plane, as we
            // are usually quite low on planes in thie space and could not do
            // further adjustments from the top/bottom planes.
            double adjS = adj.values[0][0];
            double adjY = adj.values[1][0];
            double adjX = adj.values[2][0];
            if (Math.abs(adjX) > 0.5 || Math.abs(adjY) > 0.5) {
                // Already adjusted the last time, give up
                if (adjusted == 0) {
                    // Console.WriteLine ("too many adjustments, returning");
                    return (true);
                }

                adjusted -= 1;

                // Check that just one pixel step is needed, otherwise discard
                // the point
                // 用平方做它的偏离程度
                // 亚像素的应用意义2006年3月3日
                double distSq = adjX * adjX + adjY * adjY;
                if (distSq > 2.0) return (true);

                // 如果不满足边缘中心准则:若(adjX,adjY)不在[-0.5,0.5]之间
                // 则以( x + 1 )或 (x - 1) 为新的展开点

                point.x = (int) (point.x + adjX + 0.5);
                point.y = (int) (point.y + adjY + 0.5);
                // point.Level = (int) (point.Level + adjS + 0.5);

                /*
                 * Console.WriteLine ("moved point by ({0},{1}: {2}) to ({3},{4}: {5})", adjX, adjY, adjS, point.X,
                 * point.Y, point.Level);
                 */
                continue;
            }

            if (processed[point.x][point.y] != 0) return (true);

            processed[point.x][point.y] = 1;

            // Save final sub-pixel adjustments.
            PointLocalInformation local = new PointLocalInformation(adjS, adjX, adjY);
            // local.DValue = dp;
            local.dValue = space.valArr[point.y][point.x] + 0.5 * dp.val;
            point.local = local;

            needToAdjust = false;
        }
        return (false);
    }

}
TOP

Related Classes of com.alibaba.security.simpleimage.analyze.sift.scala.DScaleSpace$RefCheckMark

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.