// 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;
        }
        // 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;
        }