// private void ComputeSubpixelHessianSameOctave(
        // float H[9],float b[3],
        // const Image& lap0,const Image& lap1,const Image& lap2,
        // int x,int y)
        private void ComputeSubpixelHessianSameOctave(double[] H, double[] b, LaplacianImage lap0, LaplacianImage lap1, LaplacianImage lap2,
                                                      int x, int y)
        {
            double Dx, Dy, Ds;
            double Dxx, Dyy, Dxy;
            double Dss, Dxs, Dys;

            //assert (x - 1) >= 0 && (x + 1) < lap1.getWidth();
            //assert (y - 1) >= 0 && (y + 1) < lap1.getHeight();
            //assert lap0.getWidth() == lap1.getWidth();
            //assert lap0.getWidth() == lap2.getWidth();
            //assert lap0.getHeight() == lap1.getHeight();
            //assert lap0.getHeight() == lap2.getHeight();

            int lap0_pm1 = lap0.get(y - 1) + x;
            int lap0_p   = lap0.get(y) + x;
            int lap0_pp1 = lap0.get(y + 1) + x;

            int lap1_p   = lap1.get(y) + x;
            int lap2_pm1 = lap2.get(y - 1) + x;
            int lap2_p   = lap2.get(y) + x;
            int lap2_pp1 = lap2.get(y + 1) + x;

            double[] tmp = new double[5];
            // Compute spatial derivatives
            //	 ComputeSubpixelDerivatives(Dx, Dy, Dxx, Dyy, Dxy, lap1, x, y);
            lap1.computeSubpixelDerivatives(x, y, tmp);
            Dx  = tmp[0];
            Dy  = tmp[1];
            Dxx = tmp[2];
            Dyy = tmp[3];
            Dxy = tmp[4];
            double[] lap0buf = (double[])lap0.getBuffer();
            double[] lap1buf = (double[])lap1.getBuffer();
            double[] lap2buf = (double[])lap2.getBuffer();
            // Compute scale derivates
            Ds  = 0.5f * (lap2buf[lap2_p + 0] - lap0buf[lap0_p + 0]);
            Dss = lap0buf[lap0_p + 0] + (-2.0f * lap1buf[lap1_p + 0]) + lap2buf[lap2_p + 0];
            Dxs = 0.25f * ((lap0buf[lap0_p - 1] - lap0buf[lap0_p + 1]) + (-lap2buf[lap2_p - 1] + lap2buf[lap2_p + 1]));
            Dys = 0.25f * ((lap0buf[lap0_pm1 + 0] - lap0buf[lap0_pp1 + 0]) + (-lap2buf[lap2_pm1 + 0] + lap2buf[lap2_pp1 + 0]));

            // H
            H[0] = Dxx;
            H[1] = Dxy;
            H[2] = Dxs;
            H[3] = Dxy;
            H[4] = Dyy;
            H[5] = Dys;
            H[6] = Dxs;
            H[7] = Dys;
            H[8] = Dss;

            // b
            b[0] = -Dx;
            b[1] = -Dy;
            b[2] = -Ds;
        }
        /**
         * Sub-pixel refinement.
         */
        private void findSubpixelLocations(GaussianScaleSpacePyramid pyramid, DogFeaturePointStack i_dog_fp)
        {
            int    num_points;
            double laplacianSqrThreshold;
            double hessianThreshold;

            num_points            = 0;
            laplacianSqrThreshold = (this.mLaplacianThreshold * this.mLaplacianThreshold);
            double te = (mEdgeThreshold + 1);

            hessianThreshold = ((te * te) / mEdgeThreshold);

            for (int i = 0; i < i_dog_fp.getLength(); i++)
            {
                DogFeaturePoint kp = i_dog_fp.getItem(i);
                //assert kp.scale < mLaplacianPyramid.numScalePerOctave();
                // ASSERT(kp.scale < mLaplacianPyramid.numScalePerOctave(),
                // "Feature point scale is out of bounds");
                int lap_index = kp.octave * mLaplacianPyramid.numScalePerOctave() + kp.scale;

                // Get Laplacian images
                LaplacianImage lap0 = mLaplacianPyramid.get(lap_index - 1);
                LaplacianImage lap1 = mLaplacianPyramid.get(lap_index);
                LaplacianImage lap2 = mLaplacianPyramid.get(lap_index + 1);

                // Compute the Hessian
                if (!this.updateLocation(kp, lap0, lap1, lap2))
                {
                    continue;
                }


                if (Math.Abs(kp.edge_score) < hessianThreshold && (kp.score * kp.score) >= laplacianSqrThreshold &&
                    kp.x >= 0 && kp.x < mLaplacianPyramid.get(0).getWidth() && kp.y >= 0 &&
                    kp.y < mLaplacianPyramid.get(0).getHeight())
                {
                    // Update the sigma
                    kp.sigma = pyramid.effectiveSigma(kp.octave, kp.sp_scale);
                    i_dog_fp.swap(i, num_points++);
                }
            }
            i_dog_fp.setLength(num_points);
        }
        // inline void ComputeSubpixelHessianFineOctavePair(
        // float H[9],float b[3],
        // const Image& lap0,const Image& lap1,const Image& lap2,
        // int x,int y)
        private void ComputeSubpixelHessianFineOctavePair(double[] H, double[] b, LaplacianImage lap0, LaplacianImage lap1,
                                                          LaplacianImage lap2, int x, int y)
        {
            double x_div_2, y_div_2;
            double val;
            double Dx, Dy, Ds;
            double Dxx, Dyy, Dxy;
            double Dss, Dxs, Dys;

            //assert (x - 1) >= 0 && (x + 1) < lap1.getWidth();
            //assert (y - 1) >= 0 && (y + 1) < lap1.getHeight();
            //assert lap0.getWidth() == lap1.getWidth();
            //assert (lap0.getWidth() >> 1) == lap2.getWidth();
            //assert lap0.getHeight() == lap1.getHeight();
            //assert (lap0.getHeight() >> 1) == lap2.getHeight();

            int lap0_pm1 = lap0.get(y - 1) + x;
            int lap0_p   = lap0.get(y) + x;
            int lap0_pp1 = lap0.get(y + 1) + x;
            int lap1_p   = lap1.get(y) + x;

            double[] tmp = new double[5];
            bilinear_downsample_point(tmp, x, y, 1);
            x_div_2 = tmp[0];
            y_div_2 = tmp[1];
            //assert x_div_2 - 0.5f >= 0;
            //assert y_div_2 - 0.5f >= 0;
            //assert x_div_2 + 0.5f < lap2.getWidth();
            //assert y_div_2 + 0.5f < lap2.getHeight();

            // Compute spatial derivatives
            // ComputeSubpixelDerivatives(Dx, Dy, Dxx, Dyy, Dxy, lap1, x, y);
            lap1.computeSubpixelDerivatives(x, y, tmp);
            Dx  = tmp[0];
            Dy  = tmp[1];
            Dxx = tmp[2];
            Dyy = tmp[3];
            Dxy = tmp[4];
            // Interpolate the VALUE at the coarser octave
            val = lap2.bilinearInterpolation(x_div_2, y_div_2);


            double[] lap0_buf = (double[])lap0.getBuffer();
            double[] lap1_buf = (double[])lap1.getBuffer();

            Ds  = 0.5f * (val - lap0_buf[lap0_p + 0]);
            Dss = lap0_buf[lap0_p + 0] + (-2.0f * lap1_buf[lap1_p + 0]) + val;
            Dxs = 0.25f * (
                (lap0_buf[lap0_p - 1] + lap2.bilinearInterpolation(x_div_2 + .5f, y_div_2))
                - (lap0_buf[lap0_p + 1] + lap2.bilinearInterpolation(x_div_2 - .5f, y_div_2))
                );
            Dys = 0.25f * (
                (lap0_buf[lap0_pm1 + 0] + lap2.bilinearInterpolation(x_div_2, y_div_2 + .5f))
                - (lap0_buf[lap0_pp1 + 0] + lap2.bilinearInterpolation(x_div_2, y_div_2 - .5f))
                );

            // H
            H[0] = Dxx;
            H[1] = Dxy;
            H[2] = Dxs;
            H[3] = Dxy;
            H[4] = Dyy;
            H[5] = Dys;
            H[6] = Dxs;
            H[7] = Dys;
            H[8] = Dss;

            // b
            b[0] = -Dx;
            b[1] = -Dy;
            b[2] = -Ds;
        }
        // private void ComputeSubpixelHessianCoarseOctavePair(
        // float H[9],float b[3],
        // const Image& lap0,const Image& lap1,const Image& lap2,
        // int x,int y)
        private void ComputeSubpixelHessianCoarseOctavePair(double[] H, double[] b, LaplacianImage lap0, LaplacianImage lap1,
                                                            LaplacianImage lap2, int x, int y)
        {
            double val;
            double x_mul_2, y_mul_2;
            double Dx, Dy, Ds;
            double Dxx, Dyy, Dxy;
            double Dss, Dxs, Dys;

            //assert (x - 1) >= 0 && (x + 1) < lap1.getWidth();
            //assert (y - 1) >= 0 && (y + 1) < lap1.getHeight();
            //assert (lap0.getWidth() >> 1) == lap1.getWidth();
            //assert (lap0.getWidth() >> 1) == lap2.getWidth();
            //assert (lap0.getHeight() >> 1) == lap1.getHeight();
            //assert (lap0.getHeight() >> 1) == lap2.getHeight();


            int lap1_p   = lap1.get(y) + x;
            int lap2_pm1 = lap2.get(y - 1) + x;
            int lap2_p   = lap2.get(y) + x;
            int lap2_pp1 = lap2.get(y + 1) + x;

            double[] tmp = new double[5];
            // Upsample the point to the higher octave
            bilinear_upsample_point(tmp, x, y, 1);
            x_mul_2 = tmp[0];
            y_mul_2 = tmp[1];
            // Compute spatial derivatives
            // ComputeSubpixelDerivatives(Dx, Dy, Dxx, Dyy, Dxy, lap1, x, y);
            lap1.computeSubpixelDerivatives(x, y, tmp);
            Dx  = tmp[0];
            Dy  = tmp[1];
            Dxx = tmp[2];
            Dyy = tmp[3];
            Dxy = tmp[4];
            // Interpolate the VALUE at the finer octave
            val = lap0.bilinearInterpolation(x_mul_2, y_mul_2);
            double[] lap2buf = (double[])lap2.getBuffer();
            double[] lap1buf = (double[])lap1.getBuffer();

            Ds  = 0.5f * (lap2buf[lap2_p + 0] - val);
            Dss = val + (-2.0f * lap1buf[lap1_p + 0]) + lap2buf[lap2_p + 0];
            Dxs = 0.25f * ((lap0.bilinearInterpolation(x_mul_2 - 2, y_mul_2) + lap2buf[lap2_p + 1]) - (lap0.bilinearInterpolation(
                                                                                                           x_mul_2 + 2, y_mul_2) + lap2buf[lap2_p - 1]));
            Dys = 0.25f * ((lap0.bilinearInterpolation(x_mul_2, y_mul_2 - 2) + lap2buf[lap2_pp1 + 0]) - (lap0.bilinearInterpolation(
                                                                                                             x_mul_2, y_mul_2 + 2) + lap2buf[lap2_pm1 + 0]));

            // H
            H[0] = Dxx;
            H[1] = Dxy;
            H[2] = Dxs;
            H[3] = Dxy;
            H[4] = Dyy;
            H[5] = Dys;
            H[6] = Dxs;
            H[7] = Dys;
            H[8] = Dss;

            // b
            b[0] = -Dx;
            b[1] = -Dy;
            b[2] = -Ds;
        }
        // private boolean ComputeSubpixelHessian(
        // float H[9],float b[3],
        // const Image& lap0,const Image& lap1,const Image& lap2,
        // int x,int y)

        private bool updateLocation(DogFeaturePoint kp, LaplacianImage lap0, LaplacianImage lap1, LaplacianImage lap2)
        {
            double[] tmp = new double[2];
            double[] b   = new double[3];


            // Downsample the feature point to the detection octave
            bilinear_downsample_point(tmp, kp.x, kp.y, kp.octave);
            double xp = tmp[0];
            double yp = tmp[1];
            // Compute the discrete pixel location
            int x = (int)(xp + 0.5f);
            int y = (int)(yp + 0.5f);

            double[] H = new double[9];
            if (lap0.getWidth() == lap1.getWidth() && lap1.getWidth() == lap2.getWidth())
            {
                //すべての画像サイズが同じ
                //assert lap0.getHeight() == lap1.getHeight() && lap1.getHeight() == lap2.getHeight();// "Width/height are not consistent");
                ComputeSubpixelHessianSameOctave(H, b, lap0, lap1, lap2, x, y);
            }
            else if ((lap0.getWidth() == lap1.getWidth()) && ((lap1.getWidth() >> 1) == lap2.getWidth()))
            {
                //0,1が同じで2がその半分
                //assert (lap0.getHeight() == lap1.getHeight()) && ((lap1.getHeight() >> 1) == lap2.getHeight());// Width/height are not consistent");
                ComputeSubpixelHessianFineOctavePair(H, b, lap0, lap1, lap2, x, y);
            }
            else if (((lap0.getWidth() >> 1) == lap1.getWidth()) && (lap1.getWidth() == lap2.getWidth()))
            {
                //0の半分が1,2
                //assert ((lap0.getWidth() >> 1) == lap1.getWidth()) && (lap1.getWidth() == lap2.getWidth());// Width/height are not consistent");
                ComputeSubpixelHessianCoarseOctavePair(H, b, lap0, lap1, lap2, x, y);
            }
            else
            {
                // ASSERT(0, "Image sizes are inconsistent");
                return(false);
            }

            // A*u=b	//		if (!SolveSymmetricLinearSystem3x3(u, H, b)) {
            NyARDoubleMatrix33 m = new NyARDoubleMatrix33();

            m.m00 = H[0];
            m.m01 = H[1];
            m.m02 = H[2];
            m.m10 = H[3];
            m.m11 = H[4];
            m.m12 = H[5];
            m.m20 = H[6];
            m.m21 = H[7];
            m.m22 = H[8];
            if (!m.inverse(m))
            {
                return(false);
            }
            double u0 = (m.m00 * b[0] + m.m01 * b[1] + m.m02 * b[2]);
            double u1 = (m.m10 * b[0] + m.m11 * b[1] + m.m12 * b[2]);
            double u2 = (m.m20 * b[0] + m.m21 * b[1] + m.m22 * b[2]);


            // If points move too much in the sub-pixel update, then the point probably unstable.
            if ((u0 * u0) + (u1 * u1) > mMaxSubpixelDistanceSqr)
            {
                return(false);
            }

            // Compute the edge score
            if (!ComputeEdgeScore(tmp, H))
            {
                return(false);
            }
            kp.edge_score = tmp[0];

            // Compute a linear estimate of the intensity
            // ASSERT(kp.score == lap1.get<float>(y)[x],
            // "Score is not consistent with the DoG image");
            double[] lap1_buf = (double[])lap1.getBuffer();
            kp.score = lap1_buf[lap1.get(y) + x] - (b[0] * u0 + b[1] * u1 + b[2] * u2);

            // Update the location:
            // Apply the update on the downsampled location and then upsample
            // the result.
            // bilinear_upsample_point(kp.x, kp.y, xp+u[0], yp+u[1], kp.octave);
            bilinear_upsample_point(tmp, xp + u0, yp + u1, kp.octave);
            kp.x = tmp[0];
            kp.y = tmp[1];

            // Update the scale
            kp.sp_scale = kp.scale + u2;
            kp.sp_scale = ClipScalar(kp.sp_scale, 0, mLaplacianPyramid.numScalePerOctave());
            return(true);
        }
        /**
         * Extract the minima/maxima.
         */
        private void extractFeatures(GaussianScaleSpacePyramid pyramid, DoGPyramid laplacian, DogFeaturePointStack i_dog_fp)
        {
            double laplacianSqrThreshold = (this.mLaplacianThreshold * this.mLaplacianThreshold);

            for (int i = 1; i < mLaplacianPyramid.size() - 1; i++)
            {
                LaplacianImage im0  = laplacian.get(i - 1);
                LaplacianImage im1  = laplacian.get(i);
                LaplacianImage im2  = laplacian.get(i + 1);
                double[]       im0b = (double[])im0.getBuffer();
                double[]       im1b = (double[])im1.getBuffer();
                double[]       im2b = (double[])im2.getBuffer();

                int octave = laplacian.octaveFromIndex((int)i);
                int scale  = laplacian.scaleFromIndex((int)i);

                if (im0.getWidth() == im1.getWidth() && im0.getWidth() == im2.getWidth())
                { // All images are the
                  // same size
                  // ASSERT(im0.height() == im1.height(), "Height is inconsistent");
                  // ASSERT(im0.height() == im2.height(), "Height is inconsistent");

                    int width_minus_1 = im1.getWidth() - 1;
                    int heigh_minus_1 = im1.getHeight() - 1;

                    for (int row = 1; row < heigh_minus_1; row++)
                    {
                        int im0_ym1 = im0.get(row - 1);
                        int im0_y   = im0.get(row);
                        int im0_yp1 = im0.get(row + 1);

                        int im1_ym1 = im1.get(row - 1);
                        int im1_y   = im1.get(row);
                        int im1_yp1 = im1.get(row + 1);

                        int im2_ym1 = im2.get(row - 1);
                        int im2_y   = im2.get(row);
                        int im2_yp1 = im2.get(row + 1);

                        for (int col = 1; col < width_minus_1; col++)
                        {
                            double value = im1b[im1_y + col];

                            // Check laplacian score
                            if ((value * value) < laplacianSqrThreshold)
                            {
                                continue;
                            }
                            bool extrema = false;
                            if (value > im0b[im0_ym1 + col - 1] && value > im0b[im0_ym1 + col] &&
                                value > im0b[im0_ym1 + col + 1] && value > im0b[im0_y + col - 1] &&
                                value > im0b[im0_y + col] && value > im0b[im0_y + col + 1] &&
                                value > im0b[im0_yp1 + col - 1] && value > im0b[im0_yp1 + col] &&
                                value > im0b[im0_yp1 + col + 1] &&
                                /* im1 - 8 evaluations */
                                value > im1b[im1_ym1 + col - 1] && value > im1b[im1_ym1 + col] &&
                                value > im1b[im1_ym1 + col + 1] && value > im1b[im1_y + col - 1] &&
                                value > im1b[im1_y + col + 1] && value > im1b[im1_yp1 + col - 1] &&
                                value > im1b[im1_yp1 + col] && value > im1b[im1_yp1 + col + 1] &&
                                /* im2 - 9 evaluations */
                                value > im2b[im2_ym1 + col - 1] && value > im2b[im2_ym1 + col] &&
                                value > im2b[im2_ym1 + col + 1] && value > im2b[im2_y + col - 1] &&
                                value > im2b[im2_y + col] && value > im2b[im2_y + col + 1] &&
                                value > im2b[im2_yp1 + col - 1] && value > im2b[im2_yp1 + col] &&
                                value > im2b[im2_yp1 + col + 1])
                            {
                                extrema = true;
                            }
                            else if (value < im0b[im0_ym1 + col - 1] && value < im0b[im0_ym1 + col] &&
                                     value < im0b[im0_ym1 + col + 1] && value < im0b[im0_y + col - 1] &&
                                     value < im0b[im0_y + col] && value < im0b[im0_y + col + 1] &&
                                     value < im0b[im0_yp1 + col - 1] && value < im0b[im0_yp1 + col] &&
                                     value < im0b[im0_yp1 + col + 1] &&
                                     /* im1 - 8 evaluations */
                                     value < im1b[im1_ym1 + col - 1] && value < im1b[im1_ym1 + col] &&
                                     value < im1b[im1_ym1 + col + 1] && value < im1b[im1_y + col - 1] &&
                                     value < im1b[im1_y + col + 1] && value < im1b[im1_yp1 + col - 1] &&
                                     value < im1b[im1_yp1 + col] && value < im1b[im1_yp1 + col + 1] &&
                                     /* im2 - 9 evaluations */
                                     value < im2b[im2_ym1 + col - 1] && value < im2b[im2_ym1 + col] &&
                                     value < im2b[im2_ym1 + col + 1] && value < im2b[im2_y + col - 1] &&
                                     value < im2b[im2_y + col] && value < im2b[im2_y + col + 1] &&
                                     value < im2b[im2_yp1 + col - 1] && value < im2b[im2_yp1 + col] &&
                                     value < im2b[im2_yp1 + col + 1])
                            {
                                extrema = true;
                            }

                            if (extrema)
                            {
                                DogFeaturePoint fp = i_dog_fp.prePush();
                                if (fp == null)
                                {
                                    prepush_warning();
                                    break;
                                }
                                fp.octave = octave;
                                fp.scale  = scale;
                                fp.score  = value;
                                fp.sigma  = pyramid.effectiveSigma(octave, scale);
                                double[] tmp = new double[2];
                                bilinear_upsample_point(tmp, col, row, octave);
                                fp.x = tmp[0];
                                fp.y = tmp[1];
                            }
                        }
                    }
                }
                else if (im0.getWidth() == im1.getWidth() && (im1.getWidth() >> 1) == im2.getWidth())
                {
                    int end_x = (int)Math.Floor(((im2.getWidth() - 1) - 0.5f) * 2.0f + 0.5f);
                    int end_y = (int)Math.Floor(((im2.getHeight() - 1) - 0.5f) * 2.0f + 0.5f);

                    for (int row = 2; row < end_y; row++)
                    {
                        int im0_ym1 = im0.get(row - 1);
                        int im0_y   = im0.get(row);
                        int im0_yp1 = im0.get(row + 1);

                        int im1_ym1 = im1.get(row - 1);
                        int im1_y   = im1.get(row);
                        int im1_yp1 = im1.get(row + 1);

                        for (int col = 2; col < end_x; col++)
                        {
                            double value = im1b[im1_y + col];

                            // Check laplacian score
                            if ((value * value) < laplacianSqrThreshold)
                            {
                                continue;
                            }

                            // Compute downsampled point location
                            double ds_x = col * 0.5f - 0.25f;
                            double ds_y = row * 0.5f - 0.25f;

                            bool extrema = false;
                            if (
                                /* im0 - 9 evaluations */
                                value > im0b[im0_ym1 + col - 1] && value > im0b[im0_ym1 + col] &&
                                value > im0b[im0_ym1 + col + 1] && value > im0b[im0_y + col - 1] &&
                                value > im0b[im0_y + col] && value > im0b[im0_y + col + 1] &&
                                value > im0b[im0_yp1 + col - 1] && value > im0b[im0_yp1 + col] &&
                                value > im0b[im0_yp1 + col + 1] &&
                                /* im1 - 8 evaluations */
                                value > im1b[im1_ym1 + col - 1] && value > im1b[im1_ym1 + col] &&
                                value > im1b[im1_ym1 + col + 1] && value > im1b[im1_y + col - 1] &&
                                value > im1b[im1_y + col + 1] && value > im1b[im1_yp1 + col - 1] &&
                                value > im1b[im1_yp1 + col] && value > im1b[im1_yp1 + col + 1] &&
                                /* im2 - 9 evaluations */
                                value > im2.bilinearInterpolation(ds_x - 0.5f, ds_y - 0.5f) &&
                                value > im2.bilinearInterpolation(ds_x, ds_y - 0.5f) &&
                                value > im2.bilinearInterpolation(ds_x + 0.5f, ds_y - 0.5f) &&
                                value > im2.bilinearInterpolation(ds_x - 0.5f, ds_y) &&
                                value > im2.bilinearInterpolation(ds_x, ds_y) &&
                                value > im2.bilinearInterpolation(ds_x + 0.5f, ds_y) &&
                                value > im2.bilinearInterpolation(ds_x - 0.5f, ds_y + 0.5f) &&
                                value > im2.bilinearInterpolation(ds_x, ds_y + 0.5f) &&
                                value > im2.bilinearInterpolation(ds_x + 0.5f, ds_y + 0.5f))
                            {
                                extrema = true;
                            }
                            else if (
                                /* im0 - 9 evaluations */
                                value < im0b[im0_ym1 + col - 1] && value < im0b[im0_ym1 + col] &&
                                value < im0b[im0_ym1 + col + 1] && value < im0b[im0_y + col - 1] &&
                                value < im0b[im0_y + col] && value < im0b[im0_y + col + 1] &&
                                value < im0b[im0_yp1 + col - 1] && value < im0b[im0_yp1 + col] &&
                                value < im0b[im0_yp1 + col + 1] &&
                                /* im1 - 8 evaluations */
                                value < im1b[im1_ym1 + col - 1] && value < im1b[im1_ym1 + col] &&
                                value < im1b[im1_ym1 + col + 1] && value < im1b[im1_y + col - 1] &&
                                value < im1b[im1_y + col + 1] && value < im1b[im1_yp1 + col - 1] &&
                                value < im1b[im1_yp1 + col] && value < im1b[im1_yp1 + col + 1] &&
                                /* im2 - 9 evaluations */
                                value < im2.bilinearInterpolation(ds_x - 0.5f, ds_y - 0.5f) &&
                                value < im2.bilinearInterpolation(ds_x, ds_y - 0.5f) &&
                                value < im2.bilinearInterpolation(ds_x + 0.5f, ds_y - 0.5f) &&
                                value < im2.bilinearInterpolation(ds_x - 0.5f, ds_y) &&
                                value < im2.bilinearInterpolation(ds_x, ds_y) &&
                                value < im2.bilinearInterpolation(ds_x + 0.5f, ds_y) &&
                                value < im2.bilinearInterpolation(ds_x - 0.5f, ds_y + 0.5f) &&
                                value < im2.bilinearInterpolation(ds_x, ds_y + 0.5f) &&
                                value < im2.bilinearInterpolation(ds_x + 0.5f, ds_y + 0.5f))
                            {
                                extrema = true;
                            }

                            if (extrema)
                            {
                                DogFeaturePoint fp = i_dog_fp.prePush();
                                if (fp == null)
                                {
                                    prepush_warning();
                                    break;
                                }
                                fp.octave = octave;
                                fp.scale  = scale;
                                fp.score  = value;
                                fp.sigma  = pyramid.effectiveSigma(octave, scale);
                                double[] tmp = new double[2];
                                bilinear_upsample_point(tmp, col, row, octave);
                                fp.x = tmp[0];
                                fp.y = tmp[1];
                            }
                        }
                    }
                }
                else if ((im0.getWidth() >> 1) == im1.getWidth() && (im0.getWidth() >> 1) == im2.getWidth())
                {
                    int width_minus_1  = im1.getWidth() - 1;
                    int height_minus_1 = im1.getHeight() - 1;

                    for (int row = 1; row < height_minus_1; row++)
                    {
                        int im1_ym1 = im1.get(row - 1);
                        int im1_y   = im1.get(row);
                        int im1_yp1 = im1.get(row + 1);

                        int im2_ym1 = im2.get(row - 1);
                        int im2_y   = im2.get(row);
                        int im2_yp1 = im2.get(row + 1);

                        for (int col = 1; col < width_minus_1; col++)
                        {
                            double value = im1b[im1_y + col];

                            // Check laplacian score
                            if ((value * value) < laplacianSqrThreshold)
                            {
                                continue;
                            }

                            double us_x = (col << 1) + 0.5f;
                            double us_y = (row << 1) + 0.5f;

                            bool extrema = false;
                            if (value > im1b[im1_ym1 + col - 1] && value > im1b[im1_ym1 + col] &&
                                value > im1b[im1_ym1 + col + 1] && value > im1b[im1_y + col - 1] &&
                                value > im1b[im1_y + col + 1] && value > im1b[im1_yp1 + col - 1] &&
                                value > im1b[im1_yp1 + col] && value > im1b[im1_yp1 + col + 1] &&
                                /* im2 - 9 evaluations */
                                value > im2b[im2_ym1 + col - 1] && value > im2b[im2_ym1 + col] &&
                                value > im2b[im2_ym1 + col + 1] && value > im2b[im2_y + col - 1] &&
                                value > im2b[im2_y + col] && value > im2b[im2_y + col + 1] &&
                                value > im2b[im2_yp1 + col - 1] && value > im2b[im2_yp1 + col] &&
                                value > im2b[im2_yp1 + col + 1] &&
                                /* im2 - 9 evaluations */
                                value > im0.bilinearInterpolation(us_x - 2.0f, us_y - 2.0f) &&
                                value > im0.bilinearInterpolation(us_x, us_y - 2.0f) &&
                                value > im0.bilinearInterpolation(us_x + 2.0f, us_y - 2.0f) &&
                                value > im0.bilinearInterpolation(us_x - 2.0f, us_y) &&
                                value > im0.bilinearInterpolation(us_x, us_y) &&
                                value > im0.bilinearInterpolation(us_x + 2.0f, us_y) &&
                                value > im0.bilinearInterpolation(us_x - 2.0f, us_y + 2.0f) &&
                                value > im0.bilinearInterpolation(us_x, us_y + 2.0f) &&
                                value > im0.bilinearInterpolation(us_x + 2.0f, us_y + 2.0f))
                            {
                                extrema = true;
                            }
                            else if (value < im1b[im1_ym1 + col - 1] && value < im1b[im1_ym1 + col] &&
                                     value < im1b[im1_ym1 + col + 1] && value < im1b[im1_y + col - 1] &&
                                     value < im1b[im1_y + col + 1] && value < im1b[im1_yp1 + col - 1] &&
                                     value < im1b[im1_yp1 + col] && value < im1b[im1_yp1 + col + 1] &&
                                     /* im2 - 9 evaluations */
                                     value < im2b[im2_ym1 + col - 1] && value < im2b[im2_ym1 + col] &&
                                     value < im2b[im2_ym1 + col + 1] && value < im2b[im2_y + col - 1] &&
                                     value < im2b[im2_y + col] && value < im2b[im2_y + col + 1] &&
                                     value < im2b[im2_yp1 + col - 1] && value < im2b[im2_yp1 + col] &&
                                     value < im2b[im2_yp1 + col + 1] &&
                                     /* im2 - 9 evaluations */
                                     value < im0.bilinearInterpolation(us_x - 2.0f, us_y - 2.0f) &&
                                     value < im0.bilinearInterpolation(us_x, us_y - 2.0f) &&
                                     value < im0.bilinearInterpolation(us_x + 2.0f, us_y - 2.0f) &&
                                     value < im0.bilinearInterpolation(us_x - 2.0f, us_y) &&
                                     value < im0.bilinearInterpolation(us_x, us_y) &&
                                     value < im0.bilinearInterpolation(us_x + 2.0f, us_y) &&
                                     value < im0.bilinearInterpolation(us_x - 2.0f, us_y + 2.0f) &&
                                     value < im0.bilinearInterpolation(us_x, us_y + 2.0f) &&
                                     value < im0.bilinearInterpolation(us_x + 2.0f, us_y + 2.0f))
                            {
                                extrema = true;
                            }

                            if (extrema)
                            {
                                DogFeaturePoint fp = i_dog_fp.prePush();
                                if (fp == null)
                                {
                                    prepush_warning();
                                    break;
                                }
                                fp.octave = octave;
                                fp.scale  = scale;
                                fp.score  = value;
                                fp.sigma  = pyramid.effectiveSigma(octave, scale);
                                double[] tmp = new double[2];
                                bilinear_upsample_point(tmp, col, row, octave);
                                fp.x = tmp[0];
                                fp.y = tmp[1];
                            }
                        }
                    }
                }
            }
            return;
        }