啊,半年来跟sift和surf的纠结终于告一段落了

#define xm
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ImageProcessing.Base.Imaging;
using ImageProcessing.Graph;
using ImageProcessing.Generic;
using System.Drawing;
using ImageProcessing.Base;
namespace Surf.Base.Net.FastSurf
{
    /// <summary>
    /// Surf金字塔
    /// </summary>
    public class SurfPyramid
    {
        public static bool InterpolateKeypoint(float[,] mat, int dx, int dy, int dscale, SurfFeature feature)
        {
            bool solve_ok = true;
            Matrix A = new Matrix(3, 3);
            Matrix X = new Matrix(3, 1);
            X[0, 0] = -(mat[1, 5] - mat[1, 3]) / 2;  /* Negative 1st deriv with respect to x */
            X[1, 0] = -(mat[1, 7] - mat[1, 1]) / 2;  /* Negative 1st deriv with respect to y */
            X[2, 0] = -(mat[2, 4] - mat[0, 4]) / 2;  /* Negative 1st deriv with respect to s */
            A[0, 0] = mat[1, 3] - 2 * mat[1, 4] + mat[1, 5];            /* 2nd deriv x, x */
            A[0, 1] = (mat[1, 8] - mat[1, 6] - mat[1, 2] + mat[1, 0]) / 4; /* 2nd deriv x, y */
            A[0, 2] = (mat[2, 5] - mat[2, 3] - mat[0, 5] + mat[0, 3]) / 4; /* 2nd deriv x, s */
            A[1, 0] = A[0, 1];                                    /* 2nd deriv y, x */
            A[1, 1] = mat[1, 1] - 2 * mat[1, 4] + mat[1, 7];            /* 2nd deriv y, y */
            A[1, 2] = (mat[2, 7] - mat[2, 1] - mat[0, 7] + mat[0, 1]) / 4; /* 2nd deriv y, s */
            A[2, 0] = A[0, 2];                                    /* 2nd deriv s, x */
            A[2, 1] = A[1, 2];                                    /* 2nd deriv s, y */
            A[2, 2] = mat[0, 4] - 2 * mat[1, 4] + mat[2, 4];            /* 2nd deriv s, s */
            A.SolveLinear(X);
            if (X[0, 0] > 1 || X[0, 0] < -1 || X[1, 0] > 1 || X[1, 0] < -1 || X[2, 0] > 1 || X[2, 0] < -1)
                solve_ok = false;
            else
            {
                feature.pt.X += X[0, 0] * dx;
                feature.pt.Y += X[1, 0] * dy;
                feature.size = (int)Math.Round(feature.size + X[2, 0] * dscale);
            }
            return solve_ok;
        }
        public const int haar_size0 = 9;
        public const int haar_size_inc = 6;
        public static int[,] dx_s = { { 0, 2, 3, 7, 1 }, { 3, 2, 6, 7, -2 }, { 6, 2, 9, 7, 1 } };
        public static int[,] dy_s = { { 2, 0, 7, 3, 1 }, { 2, 3, 7, 6, -2 }, { 2, 6, 7, 9, 1 } };
        public static int[,] dxy_s = { { 1, 1, 4, 4, 1 }, { 5, 1, 8, 4, -1 }, { 1, 5, 4, 8, -1 }, { 5, 5, 8, 8, 1 } };
        public static List<SurfFeature> GetKeypoints(ImageMapF iimg, int octaves, int layers, float thresh)
        {
            List<SurfFeature> fts = new List<SurfFeature>();
            int margin, size, size0, size1;
            SurfBox[][] xBoxes, yBoxes, xyBoxes;
            SurfFeature ft;
            int step = 1;
            ImageMapF[][] spaces = new ImageMapF[octaves][];
            for (int i = 0; i < octaves; i++)
            {
                spaces[i] = new ImageMapF[layers + 2];
                for (int j = 0; j < layers + 2; j++)
                    spaces[i][j] = new ImageMapF(iimg.Width / step, iimg.Height / step, -1f);
                step *= 2;
            }
            step = 1;
            for (int octave = 0; octave < octaves; octave++)
            {
                for (int layer = 0; layer < layers + 2; layer++)
                {
                    if (layer != 0 && layer != layers + 1)
                    {
                        size = (haar_size0 + haar_size_inc * layer) << octave;
                        size0 = (haar_size0 + haar_size_inc * (layer - 1)) << octave;
                        size1 = (haar_size0 + haar_size_inc * (layer + 1)) << octave;
                        margin = (((step + size1 / 2) + 1) / 2) * 2; //取偶数
                        xBoxes = SurfBox.Boxes(dx_s, haar_size0, iimg.Width, iimg.Height, size0, size, size1);
                        yBoxes = SurfBox.Boxes(dy_s, haar_size0, iimg.Width, iimg.Height, size0, size, size1);
                        xyBoxes = SurfBox.Boxes(dxy_s, haar_size0, iimg.Width, iimg.Height, size0, size, size1);
                        for (int y = margin; y < iimg.Height - 1 - margin; y += step)
                            for (int x = margin; x < iimg.Width - 1 - margin; x += step)
                                if (TryGetFeature(iimg, spaces, octave, layer, step, x, y, thresh, xBoxes, yBoxes, xyBoxes, out ft, size0, size, size1))
                                    fts.Add(ft);
                    }
                }
                step *= 2;
            }
            return fts;
        }
        /// <summary>
        /// 判断(x,y)是否是脚点
        /// </summary>
        static bool TryGetFeature(ImageMapF iimg,ImageMapF[][] spaces, int octave, int layer, int step, int x, int y,
            float thresh, SurfBox[][] xBoxes, SurfBox[][] yBoxes, SurfBox[][] xyBoxes, out SurfFeature result, params int[] sizes)
        {
            result = null;
            float dx, dy, dxy;
            float det, db;
            byte trc;
            int x0 = x / step;
            int y0 = y / step;
            int idx = (x - sizes[1] / 2 + (y - sizes[1] / 2) * iimg.Width);
            dx = SurfBox.Hears(iimg, idx, xBoxes[1]); //获取中心窗口的权值
            dy = SurfBox.Hears(iimg, idx, yBoxes[1]);
            dxy = SurfBox.Hears(iimg, idx, xyBoxes[1]);
            det = dx * dy - 0.81f * dxy * dxy;
            if (det >= thresh) //如果大于阈值,判断和邻域两个layer之间的大小关系,在该层上超过阈值再进行其他计算,防止多余的计算
            {
                trc = (byte)((dx + dy) >= 0 ? 1: 0);
                float[,] mat = new float[3, 9];
                bool ex = true;
                for (int i = -1; i <= 1 && ex; i++) //layer
                {
                    int size = sizes[i + 1]; //获取窗口尺寸
                    int rd = size / 2;       //窗口半径
                    int nx, ny;
                    for (int j = -1; j <= 1 && ex; j++) //Y
                    {
                        ny = y + j * step - rd;
                        for (int k = -1; k <= 1 && ex; k++) //X
                        {
                            if (spaces[octave][layer + i][y0 + j, x0 + k] == -1) //如果此位置未被计算过,计算此位置,防止重复的计算
                            {
                                nx = x + k * step - rd;
                                idx = nx + ny * iimg.Width;
                                dx = SurfBox.Hears(iimg, idx, xBoxes[i + 1]);
                                dy = SurfBox.Hears(iimg, idx, yBoxes[i + 1]);
                                dxy = SurfBox.Hears(iimg, idx, xyBoxes[i + 1]);
                                db = dx * dy - 0.81f * dxy * dxy;
                                spaces[octave][layer + i][y0 + j, x0 + k] = db;
                            }
                            else                                                //如果已经被计算过,那么直接赋值
                                db = spaces[octave][layer + i][y0 + j, x0 + k];
                            if (i != 0 && j != 0 && k != 0)
                                if (db >= det) ex = false;
                            mat[i + 1, 4 + j * 3 + k] = db;
                        }
                    }
                }
                if (ex)
                {
                    SurfFeature ft = new SurfFeature(new PointD(x, y), trc, sizes[1], 0, det, null);
                    if (InterpolateKeypoint(mat, step, step, sizes[1] - sizes[0], ft))
                    {
                        result = ft;
                        return true;
                    }
                }
            }
            return false;
        }
        /// <summary>
        /// 角度归纳中扇形区域的总角度
        /// </summary>
        public const int ori_win = 60;
        /// <summary>
        /// 角度扫描增量
        /// </summary>
        public const int ori_search_inc = 5;
        /// <summary>
        /// 角度扫描半径
        /// </summary>
        public const int ori_radius = 6;
        /// <summary>
        /// 角度扫描尺寸
        /// </summary>
        public const int ori_size = (ori_radius * 2 + 1);
        /// <summary>
        /// 角度扫描窗口面积
        /// </summary>
        public const int ori_count = ori_size * ori_size;
        /// <summary>
        /// 描述子窗口尺寸
        /// </summary>
        public const int patch_sz = 20;
        /// <summary>
        /// haar_x窗口
        /// </summary>
        public static int[,] ori_dx_s = { { 0, 0, 2, 4, -1 }, { 2, 0, 4, 4, 1 } };
        /// <summary>
        /// haar_y窗口
        /// </summary>
        public static int[,] ori_dy_s = { { 0, 0, 4, 2, 1 }, { 0, 2, 4, 4, -1 } };
        /// <summary>
        /// 角度扫描模糊矩阵
        /// </summary>
        public static GaussainMatrix oriGauss = new GaussainMatrix(2.5, ori_size);
        /// <summary>
        /// 描述子窗口模糊矩阵
        /// </summary>
        public static GaussainMatrix desGauss = new GaussainMatrix(3.3, patch_sz);
        /// <summary>
        /// 角度扫描点
        /// </summary>
        public static Point[] ori_points = GetAnglePoints();
        /// <summary>
        /// 角度模糊系数
        /// </summary>
        public static float[] ori_weights = GetAngleWeights();
        static float[] GetAngleWeights()
        {
            float[] res = new float[ori_count];
            int index = 0;
            for (int y = 0; y < ori_size; y++)
                for (int x = 0; x < ori_size; x++)
                    res[index++] = (float)oriGauss[x, y];
            return res;
        }
        static Point[] GetAnglePoints()
        {
            Point[] res = new Point[ori_count];
            int index = 0;
            for (int y = -ori_radius; y <= ori_radius; y++)
                for (int x = -ori_radius; x <= ori_radius; x++)
                    res[index++] = new Point(x, y);
            return res;
        }
        public static void CreateDescriptors(ImageMapF sum, GrayImage img, SurfFeature feature)
        {
            int i, j, x, y;
            int size = feature.size;
            float s = (float)size * 1.2f / 9.0f; //单位窗口尺寸
            int grad_wav_size = 2 * (int)Math.Round(2 * s); //角度扫描单位窗口尺寸
            int win_size = (int)((patch_sz + 1) * s);
            SurfPositionBox[] xBoxes = SurfPositionBox.Boxes(ori_dx_s, 4, grad_wav_size); //X方向haar窗口
            SurfPositionBox[] yBoxes = SurfPositionBox.Boxes(ori_dy_s, 4, grad_wav_size);
            PointD center = feature.pt;
            float[] X = new float[ori_count]; //X坐标序列
            float[] Y = new float[ori_count];
            int nangle = 0;
            List<float> angles = new List<float>();
            for (int kk = 0; kk < ori_count; kk++) //获取角度描述子向量
            {
                float vx, vy;
                x = (int)Math.Round(center.X + ori_points[kk].X * s - (float)(grad_wav_size - 1) / 2); //当前位置
                y = (int)Math.Round(center.Y + ori_points[kk].Y * s - (float)(grad_wav_size - 1) / 2);
                if (y >= (sum.Height - grad_wav_size) || x >= (sum.Width - grad_wav_size))
                    continue;
                vx = SurfPositionBox.Haars(sum, x, y, xBoxes); //求像素值
                vy = SurfPositionBox.Haars(sum, x, y, yBoxes);
                X[nangle] = vx * ori_weights[kk]; //边缘衰弱
                Y[nangle] = vy * ori_weights[kk];
                angles.Add((float)new PointD(vx, vy).Angle); //记录角度
                nangle++;
            }
            if (nangle == 0)
            {
                feature.size = -1;
                return;
            }
            float bestx = 0, besty = 0, descriptor_mod = 0;
            for (i = 0; i < 360; i += ori_search_inc) //扫描最优的角度
            {
                float sumx = 0, sumy = 0, temp_mod;
                for (j = 0; j < nangle; j++) //遍历计算出的角度
                {
                    int d = (int)Math.Abs(Math.Round(angles[j]) - i);
                    if (d < ori_win / 2 || d > 360 - ori_win / 2)
                    //判断是否在当前角度的邻域范围内,就是在当前角度的[-ori_win / 2,ori_win / 2]范围内,ori_win为60度
                    {
                        sumx += X[j]; //向量相加,得出扇形区域的主方向
                        sumy += Y[j];
                    }
                }
                temp_mod = sumx * sumx + sumy * sumy; //计算向量的模
                if (temp_mod > descriptor_mod) //记录模最大的向量
                {
                    descriptor_mod = temp_mod;
                    bestx = sumx;
                    besty = sumy;
                }
            }
            float descriptor_dir = (float)new PointD(besty, bestx).Angle;
            feature.dir = descriptor_dir;
#if !xm
            descriptor_dir = (float)(descriptor_dir * Math.PI / 180); //将角度转化为PI
            float sin_dir = (float)Math.Sin(descriptor_dir);
            float cos_dir = (float)Math.Cos(descriptor_dir);
            float win_offset = -(float)(win_size - 1) / 2;
            float start_x = center.x + win_offset * cos_dir + win_offset * sin_dir; //左上角旋转到主角度方向
            float start_y = center.y - win_offset * sin_dir + win_offset * cos_dir;
            ImageMap win = new ImageMap(win_size, win_size);
            for (i = 0; i < win_size; i++, start_x += sin_dir, start_y += cos_dir) //将描述子窗口旋转为标准窗口!
            {
                float pixel_x = start_x;
                float pixel_y = start_y;
                for (j = 0; j < win_size; j++, pixel_x += cos_dir, pixel_y -= sin_dir) //Y要反向?实在不行就用像素插值
                {
                    x = Math.Min(Math.Max(Math.Round(pixel_x), 0), img.Width - 1);
                    y = Math.Min(Math.Max(Math.Round(pixel_y), 0), img.Height - 1);
                    win[i * win_size + j] = img[x, y];
                }
            }
#else
            PointD pd;
            ImageMap win = new ImageMap(patch_sz + 1, patch_sz + 21); //要多出一个像素,保证每个像素都可以计算dx 和 dy
            ImageTransform2D trans = new ImageTransform2D  //逆向插值的旋转-缩放-平移变换
            {
                Origin = new PointD(patch_sz / 2d, patch_sz / 2d), //中心点,描述子窗口中心点
                Rotate = descriptor_dir,                            //旋转角度,由0度到描述子方向
                Scale = (double)win_size / (patch_sz + 1d),                //缩放,由描述子矩阵放大到实际图形
                Translation = center - new PointD(patch_sz / 2d, patch_sz / 2d) //平移,由描述子矩阵到特征点位置
            };
            for (y = 0; y <= patch_sz; y++)
            {
                for (x = 0; x <= patch_sz; x++)
                {
                    pd = trans.Transform(new PointD(x, y)); //求得插值位置
                    win[x + y * win_size] = GraphHelper.box_gray_pixel(img.Buffers, img.Width, img.Height, pd.X, pd.Y); //像素插值
                }
            }
#endif
            ImageMapF DX = new ImageMapF(patch_sz, patch_sz); //记录x梯度变化
            ImageMapF DY = new ImageMapF(patch_sz, patch_sz); //记录y梯度变化
            for (i = 0; i < patch_sz; i++) //计算xy方向梯度
            {
                for (j = 0; j < patch_sz; j++)
                {
                    float dw = (float)desGauss[j, i];
                    float vx = (win[i, j + 1] - win[i, j] + win[i + 1, j + 1] - win[i + 1, j]) * dw;         //边缘衰弱
                    float vy = (win[i + 1, j] - win[i, j] + win[i + 1, j + 1] - win[i, j + 1]) * dw;
                    DX[i, j] = vx;
                    DY[i, j] = vy;
                }
            }
            float[] vec = new float[64];
            int vec_ptr = 0;
            float square_mag = 0f; //记录64个描述子的平方的总和
            for (i = 0; i < 4; i++) //计算描述子 (dx,dy,|dx|,|dy|)*4*4
            {
                for (j = 0; j < 4; j++) //就是把20个像素高宽的描述子矩阵分成16个5X5的单位窗口
                {
                    for (y = i * 5; y < i * 5 + 5; y++)             //计算单位窗口内的梯度变化总和
                    {
                        for (x = j * 5; x < j * 5 + 5; x++)
                        {
                            float tx = DX[y, x], ty = DY[y, x];
                            vec[vec_ptr + 0] += tx;                 // dx
                            vec[vec_ptr + 1] += ty;                 // dy
                            vec[vec_ptr + 2] += (float)Math.Abs(tx);//|dx|
                            vec[vec_ptr + 3] += (float)Math.Abs(ty);//|dy|
                        }
                    }
                    for (int kk = 0; kk < 4; kk++)
                        square_mag += vec[vec_ptr + kk] * vec[vec_ptr + kk]; //累加
                    vec_ptr += 4; //偏移
                }
            }
            float scale = 1f / ((float)Math.Sqrt(square_mag) + float.Epsilon);
            for (i = 0; i < 64; i++)
                vec[i] = vec[i] * scale;
            feature.discriptors = vec;
        }
        public static void GetOritation(ImageMapF sum, SurfFeature feature)
        {
            int size = feature.size;
            float s = (float)size * 1.2f / 9.0f; //单位窗口尺寸
            int grad_wav_size = 2 * (int)Math.Round(2 * s); //角度扫描单位窗口尺寸
            int win_size = (int)((patch_sz + 1) * s);
            SurfPositionBox[] xBoxes = SurfPositionBox.Boxes(ori_dx_s, 4, grad_wav_size); //X方向haar窗口
            SurfPositionBox[] yBoxes = SurfPositionBox.Boxes(ori_dy_s, 4, grad_wav_size);
            PointD center = feature.pt;
            int x, y;
            float[] X = new float[ori_count]; //X坐标序列
            float[] Y = new float[ori_count];
            int nangle = 0;
            List<float> angles = new List<float>();
            for (int kk = 0; kk < ori_count; kk++) //获取角度描述子向量
            {
                float vx, vy;
                x = (int)Math.Round(center.X + ori_points[kk].X * s - (float)(grad_wav_size - 1) / 2); //当前位置
                y = (int)Math.Round(center.Y + ori_points[kk].Y * s - (float)(grad_wav_size - 1) / 2);
                if (y >= (sum.Height - grad_wav_size) || x >= (sum.Width - grad_wav_size))
                    continue;
                vx = SurfPositionBox.Haars(sum, x, y, xBoxes); //求像素值
                vy = SurfPositionBox.Haars(sum, x, y, yBoxes);
                X[nangle] = vx * ori_weights[kk]; //边缘衰弱
                Y[nangle] = vy * ori_weights[kk];
                angles.Add((float)new PointD(vx, vy).Angle); //记录角度
                nangle++;
            }
            if (nangle == 0)
            {
                feature.size = -1;
                return;
            }
            float bestx = 0, besty = 0, descriptor_mod = 0;
            for (int i = 0; i < 360; i += ori_search_inc) //扫描最优的角度
            {
                float sumx = 0, sumy = 0, temp_mod;
                for (int j = 0; j < nangle; j++) //遍历计算出的角度
                {
                    int d = (int)Math.Abs(Math.Round(angles[j]) - i);
                    if (d < ori_win / 2 || d > 360 - ori_win / 2)
                    //判断是否在当前角度的邻域范围内,就是在当前角度的[-ori_win / 2,ori_win / 2]范围内,ori_win为60度
                    {
                        sumx += X[j]; //向量相加,得出扇形区域的主方向
                        sumy += Y[j];
                    }
                }
                temp_mod = sumx * sumx + sumy * sumy; //计算向量的模
                if (temp_mod > descriptor_mod) //记录模最大的向量
                {
                    descriptor_mod = temp_mod;
                    bestx = sumx;
                    besty = sumy;
                }
            }
            float descriptor_dir = (float)new PointD(besty, bestx).Angle;     //... ... ... ... ...
            feature.dir = descriptor_dir;
        }
        public static ImageMapF SquareWindow(ImageMapF sum, SurfFeature feature)
        {
            int size = feature.size;
            float s = (float)size * 1.2f / 9.0f;
            int grad_wav_size = 2 * (int)Math.Round(2 * s);
            int win_size = (int)((patch_sz + 1) * s);
            return SquareWindow(sum, (int)Math.Round(feature.pt.X), (int)Math.Round(feature.pt.Y), win_size, patch_sz);
        }
        public static ImageMapF SquareWindow(ImageMapF sum, int x, int y, int size, int newSize)
        {
            ImageMapF res = new ImageMapF(newSize, newSize);
            float scale = size / newSize;
            int nx, ny;
            int top = y - size / 2;
            int left = x - size / 2;
            int wndSize = (int)Math.Round(scale);
            int area = wndSize * wndSize;
            for (int ty = 0; ty < newSize; ty++)
            {
                ny = top + (int)Math.Round(ty * scale);
                for (int tx = 0; tx < newSize; tx++)
                {
                    nx = left + (int)Math.Round(tx * scale);
                    res[ty, tx] = sum.BoxIntegral(nx, ny, wndSize, wndSize) / area;
                }
            }
            return res;
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值