图像检测_图像检测和识别的区别 - CSDN
  • 图像检测

    千次阅读 2018-03-04 19:16:59
    图像检测1.算法描述这里我用到三种处理方法:直接相关运算,归一化相关系数,以及像素点个数统计。(1)直接作相关运算:轮胎图像直接作为模板,和汽车图像直接做相关运算,即用公式:得到的矩阵不在像素值表示范围...

    图像检测

    1.算法描述

    这里我用到三种处理方法:直接相关运算,归一化相关系数,以及像素点个数统计。

    (1)直接作相关运算:

    轮胎图像直接作为模板,和汽车图像直接做相关运算,即用公式:

    得到的矩阵不在像素值表示范围内,所以还要除以一个系数(height * width * 255)来确保能输出图像。

    但这种方法得到的相关系数矩阵转化而来的图像效果并不明显,而且如果一片与检测图像无关的区域比较亮,也会使得该区域的相关系数值较大。

    (2)改进:归一化相关系数:

    使用以下归一化相关系数矩阵的计算公式:

    这样能得到一个元素范围在[-1, 1]的双精度浮点型矩阵,可以直接输出为图像。这次的效果明显好于上一次。

    作归一化相关系数运算和直接作相关运算的对比:

    (3)补充:自定义的基于像素个数统计的相关系数:

    前两种方法还不能找出我们需要的4个轮胎的坐标,而我希望能在对应于每个轮胎的范围内能唯一地识别出一个像素点,于是我自定义了一个函数CountingCorrelation。

    假设当前遍历到原图的(i, j)处,如果模板上的一个像素点的值和对应的原图位置上的像素值相差不超过一定范围(例如我选定的是5,这个数经多次选择,觉得5较合适),那么就在给新的矩阵中的(i, j)处的值加1。

    这样,和模板的像素值越接近的原图中的一片等大的区域的核心对应在新矩阵中的值就越大。然后用一定方法将新矩阵的每个元素取值都映射到[0, 255]中(这里我用的方法是cor(i, j) = exp(cor(i, j) / 8),用指数函数能使得大值与小值之间的差变得更大,更有利于使匹配的轮胎的位置更亮更明显,系数8则是经过多次选择觉得较合适的数)。

    最后经过这样处理,恰巧能使4个像素的值为255,经过分析它们大致分别对于于原图4个轮胎的中心位置。

    2.Matlab代码

    (1)函数Correlation:

    Correlation.m

    function correlation = Correlation(img, template)
        [h1,w1] = size(img);
        [h2,w2] = size(template);
        half_h = floor(h2 / 2);
        half_w = floor(w2 / 2);
        expand_img = zeros(h1 + h2 - 1, w1 + w2 - 1);
        expand_img((half_h + 1):(h1 + half_h), (half_w + 1):(w1 + half_w)) = img;
        expand_img = uint8(expand_img);
        correlation = zeros(h1 + h2 - 1, w1 + w2 - 1);
        for i = half_h + 1 : h1 + half_h
            for j = half_w + 1 : w1 + half_w
                for u = -half_h : half_h
                    for v = -half_w : half_w
                        correlation(i, j) = correlation(i, j) + double(expand_img(i + u, j + v)) * double(template(half_h + 1 + u, half_w + 1 + v));
                    end
                end  
            end
        end
    
        correlation = correlation(half_h + 1 : h1 + half_h, half_w + 1 : w1 + half_w);

    (2)函数NormalizedCorrelation:

    NormalizedCorrelation.m

    function ncorr = NormalizedCorrelation(img, template)
        [h1,w1] = size(img);
        [h2,w2] = size(template);
    
        avrg_template = mean2(template(:));
        half_h = floor(h2 / 2);
        half_w = floor(w2 / 2);
        expand_img = zeros(h1 + h2 - 1, w1 + w2 - 1);
        expand_img((half_h + 1):(h1 + half_h), (half_w + 1):(w1 + half_w)) = img;
        expand_img = double(expand_img);
        template = double(template);
        ncorr = zeros(h1 + h2 - 1, w1 + w2 - 1);
        for i = half_h + 1 : h1 + half_h
            for j = half_w + 1 : w1 + half_w
                numerator = 0;
                denominator1 = 0;
                denominator2 = 0;
                avrg_img = mean2(expand_img(i - half_h : i + half_h, j - half_w : j + half_w));
                for u = -half_h : half_h
                    for v = -half_w : half_w
                        numerator = numerator + (expand_img(i + u, j + v) - avrg_img) * (template(half_h + 1 + u, half_w + 1 + v) - avrg_template);
                        denominator1 = denominator1 + (template(half_h + 1 + u, half_w + 1 + v) - avrg_template) * (template(half_h + 1 + u, half_w + 1 + v) - avrg_template);
                        denominator2 = denominator2 + (expand_img(i + u, j + v) - avrg_img) * (expand_img(i + u, j + v) - avrg_img);
                    end
                end
                ncorr(i, j) = numerator / sqrt(denominator1 * denominator2);
            end
        end
    
        ncorr = ncorr(half_h + 1 : h1 + half_h, half_w + 1 : w1 + half_w);

    (3)函数CountingCorrelation:

    CountingCorrelation.m

    function cor = CountingCorrelation(img, template)
        [h1,w1] = size(img);
        [h2,w2] = size(template);
        half_h = floor(h2 / 2);
        half_w = floor(w2 / 2);
    
        expand_img = zeros(h1 + h2 - 1, w1 + w2 - 1);
        expand_img((half_h + 1):(h1 + half_h), (half_w + 1):(w1 + half_w)) = img;
        expand_img = uint8(expand_img);
    
        cor = zeros(h1, w1);
        for i = 1 : h1
            for j = 1 : w1
                for u = -half_h : half_h
                    for v = -half_w : half_w
                        if i + u < 1 || j + v < 1 || i + u > h1 || j + v > w1
                            continue;
                        elseif abs(int16(img(i + u, j + v)) - int16(template(half_h + 1 + u, half_w + 1 + v))) < 5
                            cor(i, j) = cor(i, j) + 1;
                        end
                    end
                end
                cor(i, j) = exp(cor(i, j) / 8);
            end
        end
        cor = uint8(cor);

    (4)调用函数的脚本Task1:

    Task1.m

    img = imread('car.png');
    [h1,w1] = size(img);
    template = imread('wheel.png');
    [h2,w2] = size(template);
    
    figure();
    subplot(2, 2, 1);
    imshow(img);
    title('Original Image');
    
    subplot(2, 2, 2);
    ncorr = NormalizedCorrelation(img, template);
    imshow(ncorr);
    title('Normalized Correlation Image');
    
    ccorr = CountingCorrelation(img, template);
    subplot(2, 2, 3);
    imshow(ccorr);
    title('Counting Correlation');
    
    correlation = Correlation(img, template);
    correlation = correlation / (h2 * w2 * 255);
    correlation = uint8(correlation);
    subplot(2, 2, 4);
    imshow(correlation);
    title('Correlation Image');
    
    num = 1;
    lightest = zeros(4,2);
    for i = 1 : h1
        for j = 1 : w1
            if ccorr(i, j) == 255
                lightest(num,1) = i;
                lightest(num,2) = j;
                num = num + 1;
            end
        end
    end

    3.处理结果

    下图为图像检测效果图对比,

    这里写图片描述

    检测到的符合的像素点坐标:

    这里写图片描述

    展开全文
  • 深度学习与图像识别 图像检测

    万次阅读 多人点赞 2016-12-02 16:02:27
    主要是看了些关于深度学习图像识别和图像检测的论文,介绍了深度学习处理图像的优势
    
    
    主要做了基于深度学习的图像识别与检测的研究,下面是一些整理内容

    1、深度学习的优势


    (1)从统计,计算的角度看,DL特别适合处理大数据

           a、用较为复杂的模型降低模型偏差

           b、用大数据提升统计估计的准确度

           c、用可扩展的梯度下降算法求解大规模优化问题

    这个大数据是除了数量上的大,还有更重要的是维度的大,很多算法本身是无法处理高纬度数据的,例如Kernel学习机相关的算法, 虽然理论上是先将数据向高维空间映射,然后在高维空间进行线性的求解,实际上在处理的时候还是回到原空间处理。传统的BP算法针对高维的数据也是效果不佳。

     

    CNN等为什么对图像领域更加有效,因为其不但关注了全局特征,更是利用了图像识别领域非常重要的局部特征,应该是将局部特征抽取的算法融入到了神经网络中。图像本身的局部数据存在关联性,而这种局部关联性的特征是其他算法无法提取的。深度学习很重要的是对全局和局部特征的综合把握


    (2)深度学习不是一个黑箱系统。它像概率模型一样,提供一套丰富的、基于联接主义的建模语言。利用这套语言系统,我们可以表达数据内在的丰富关系和结构。比如用卷积处理图像中的二维空间结构,用递归神经网络(Recurrent Neural Network)处理自然语言等数据中的时序结构


    (3)深度学习几乎是唯一的端到端的学习系统

    它直接作用于原始数据,自动逐层进行特征学习,整个过程直接优化目标函数。


    2、深度学习在图像识别中的发展趋势


    (1)模型层次不断加深

           2012Alex 获得ImageNet 冠军,其所用的AlexNet5个卷积层 3个pool层 和2个全连接层

           2014年获得ImageNet的GoogleNet,使用了59个卷积层,16个pool层和2个全连接层。

           2016年微软的ResNet深度残差网络,用了152层的架构


    (2)模型结构日趋复杂

           传统的卷积神经网络都是简单的 conv-pool-FC

           后来NIN 用mlpconv 代替传统的 conv层(mlp 实际上是卷积加传统的多层感知器 )。这样做一方面降低过拟合程度提高模型的推广能力,另一方面为大规模并行训练提供非常有利的条件


    (3)海量的标注数据和适当的数据扰动

           DL需要大量的数据,现有的图像数据不能满足需求,结合图像数据的特点,通过平移、水平翻转、旋转、缩放等数据扰动方式可以产生更多的有效数据,普遍提高识别模型的推广能力。


    3、如何应用深度学习


    (1)  将ImageNet上训练得到的模型作为起点,利用目标训练集和反向传播对其进行继续训练,将模型适应到特定的应用

    (2)  如果目标训练集不够大,可以将底层的网络参数固定,沿用ImageNet上的训练集结果,只对上层进行更新。

    (3)  直接采用ImageNet上训练得到的模型,把最高的隐含层的输出作为特征表达,代替常用的手工设计的特征。

     

    4、卷积神经网络


    (1)什么是卷积神经网络

         

     卷积神经网络是一种为了处理二维输入数据而特殊设计的多层人工神经网络。网络中的每层都由多个二维平面组成,而每个平面由多个独立的神经元组成。相邻两层的神经元之间互相连接。

    A、       卷积特征提取(局部连接,权值共享

    从图像中随机选取一小块局域作为训练样本,从该样本中学习到一些特征,然后将这些特征作为滤波器,与原始整个图像作卷积运算,从而得到原始图像中任意位置上的不同特征的激活值(见动图)


    B、       池化

    通过将卷积层提取的特征输入至分类器中进行训练,可以实现输出最终的分类结果。理论上可以直接输出,然而这将需要非常大的计算开销,特别是对于大尺寸高分辨率图像

    由于图像具有一种“静态性”的属性,在图像的一个局部区域得到的特征极有可能在另一个局部区域同样适用。因此,对图像的一个局部区域中不同位置的特征进行聚合统计操作,这种操作统称为池化


    (2)卷积神经网络的发展


    A、 1990年,LeCun等在研究手写体数字识别问题时,首先提出来使用梯度反向传播算法训练的卷积神经网络模型,并在MNIST手写数字数据集上表现出了好的性能。

    B、 2012年ImageNet比赛中 AlexKrizhevsky等提出的AlexNet首次将深度学习应用到大规模图像分类,并获得了冠军。

    AlexNet用了5层卷积层和3层全连接层,最后用softmax进行分类。

    改进点:

    a、       采用dropout训练策略,在训练过程中将输入层和中间层的一些神经元随机置零。这模拟了噪声对输入数据的各种干扰使一些神经元对一些视觉模式产生漏检的情况。使得训练过程收敛过慢,但得到的网络模型更加鲁棒。

    b、       采用ReLU(修正线性单元)作为激励函数,降低了计算的复杂度

    c、        通过对训练样本镜像映射和加入随机平移扰动,产生了更多的训练样本,减少了过拟合

    C、 2013 ImageNet的获胜队伍Clarifai 提出了卷积神经网络的可视化方法,运用反卷积网络对AlexNet的每个卷积层进行可视化,以此来分析每一层所学习到的特征从而加深了对于卷积神经网络为什么能在图像分类上取得好的效果的理解,并据此改进了该模型。


    D、 2014 ImageNet Google团队


    网络有22 层,受到赫布学习规则的启发,同时基于多尺度处理的方法对卷积神经网络作出改进。该文基于Network in Network思想提出了Inception模块。Inception 模块的结构如图所示,它的主要思想是想办法找出图像的最优局部稀疏结构,并将其近似地用稠密组件替代。这样做一方面可以实现有效的降维,从而能够在计算资源同等的情况下增加网络的宽度与深度;另一方面也可以减少需要训练的参数,从而减轻过拟合问题,提高模型的推广能力。


    E、 2015年微软亚洲研究院所提出的152层的深度残差网络以绝对的优势获得图像检测、图像分类和图像定位3个项目的冠军

     

    5、物体检测

    (1)物体分类与检测的难点与挑战

    物体分类与检测是视觉研究中的基本问题,也是一个非常具有挑战性的问题.物体分类与检测的难点与挑战在本文中分为3个层次:实例层次、类别

    层次和语义层次,如图所示

     

     

     

    a)       实例层次

    针对单个物体实例而言,通常由于图像采集过程中光照条件、拍摄视角、距离的不同、物体自身的非刚体形变以及其他物体的部分遮挡,使得物体实例的表观特征产生很大的变化,给视觉识别算法带来了极大的困难

    b)       类别层次

    困难与挑战通常来自3个方面,

    类内差大,也即属于同一类的物体表观特征差别比较大,其原因有前面提到的各种实例层次的变化,但这里更强调的是类内不同实例的差别,例如图(a)所示

    类间模糊性,即不同类的物体实例具有一定的相似性,如图(b)背景的干扰在实际场景下,物体不可能出现在一个非常干净的背景下,往往相反,背景可能是非常复杂的、对我们感兴趣的物体存在干扰的,这使得识别问题的难度大大增加

    c)        语义层次.

    困难和挑战与图像的视觉语义相关,这个层次的困难往往非常难处理,特别是对现在的计算机视觉理论水平而言,一个典型的问题称为多重稳定性.如图(C)左边既可以看成是两个面对面的人,也可以看成是一个燃烧的蜡烛;右边则同时可以解释为兔子或者小鸭.同样的图像,不同的解释,这既与人的观察视角、关注点等物理条件有关,也与人的性格、经历等有关,而这恰恰是视觉识别系统难以处理的部分



    (2)物体检测的发展(详细算法介绍见后续)

           较有影响力的工作包括:

    A、       RCNN (2013)

    B、       Fast RCNN

    C、       Faster RCNN

    D、      R-FCN

    E、       YOLO

    F、       SSD


     

    参考文献


    【1】  基于深度学习的图像识别进展:百度的若干实践 (2015 百度公司)

    【2】  图像识别中的深度学习 (2015 香港中文大学 王晓刚)

    【3】  图像无图分类与检测算法综述 (2014 中国科学院自动化研究所模式识别国家重点实验室智能感知与计算研究中心)

    【4】  深度卷积神经网络在计算机视觉中的应用研究综述 (2016 上海交通大学)

    【5】  Rich feature hierarchies foraccurate object detection and semantic segmentation (2014)

    【6】  Fast R-CNN (2015)

    【7】  Faster R-CNN :Towards Real-Time Object Detection with Region Proposal Networks (2016)

    【8】  Detection(网址上面描述各种检测算法)

    展开全文
  • 图像特征检测(Image Feature Detection)

    千次阅读 2016-07-27 10:01:24
    图像特征检测(Image Feature Detection) 作者:王先荣 前言  图像特征提取是计算机视觉和图像处理中的一个概念。它指的是使用计算机提取图像信息,决定每个图像的点是否属于一个图像特征。本文主要探讨如何...

    图像特征检测(Image Feature Detection)

    作者:王先荣
    前言
        图像特征提取是计算机视觉和图像处理中的一个概念。它指的是使用计算机提取图像信息,决定每个图像的点是否属于一个图像特征。本文主要探讨如何提取图像中的“角点”这一特征,及其相关的内容。而诸如直方图、边缘、区域等内容在前文中有所提及,请查看相关文章。OpenCv(EmguCv)中实现了多种角点特征的提取方法,包括:Harris角点、ShiTomasi角点、亚像素级角点、SURF角点、Star关键点、FAST关键点、Lepetit关键点等等,本文将逐一介绍如何检测这些角点。在此之前将会先介绍跟角点检测密切相关的一些变换,包括Sobel算子、拉普拉斯算子、Canny算子、霍夫变换。另外,还会介绍一种广泛使用而OpenCv中并未实现的SIFT角点检测,以及最近在OpenCv中实现的MSER区域检测。所要讲述的内容会很多,我这里尽量写一些需要注意的地方及实现代码,而参考手册及书本中有的内容将一笔带过或者不会提及。

    Sobel算子
        Sobel算子用多项式计算来拟合导数计算,可以用OpenCv中的cvSobel函数或者EmguCv中的Image<TColor,TDepth>.Sobel方法来进行计算。需要注意的是,xorder和yorder中必须且只能有一个为非零值,即只能计算x方向或者y反向的导数;如果将方形滤波器的宽度设置为特殊值CV_SCHARR(-1),将使用Scharr滤波器代替Sobel滤波器。
        使用Sobel滤波器的示例代码如下:

    复制代码
    //Sobel算子 private string SobelFeatureDetect() { //获取参数 int xOrder = int.Parse((string)cmbSobelXOrder.SelectedItem); int yOrder = int.Parse((string)cmbSobelYOrder.SelectedItem); int apertureSize = int.Parse((string)cmbSobelApertureSize.SelectedItem); if ((xOrder == 0 && yOrder == 0) || (xOrder != 0 && yOrder != 0)) return "Sobel算子,参数错误:xOrder和yOrder中必须且只能有一个非零。\r\n"; //计算 Stopwatch sw = new Stopwatch(); sw.Start(); Image<Gray, Single> imageDest = imageSourceGrayscale.Sobel(xOrder, yOrder, apertureSize); sw.Stop(); //显示 pbResult.Image = imageDest.Bitmap; //释放资源 imageDest.Dispose(); //返回 return string.Format("·Sobel算子,用时{0:F05}毫秒,参数(x方向求导阶数:{1},y方向求导阶数:{2},方形滤波器宽度:{3})\r\n", sw.Elapsed.TotalMilliseconds, xOrder, yOrder, apertureSize); }
    复制代码

     
    拉普拉斯算子
        拉普拉斯算子可以用作边缘检测;可以用OpenCv中的cvLaplace函数或者EmguCv中的Image<TColor,TDepth>.Laplace方法来进行拉普拉斯变换。需要注意的是:OpenCv的文档有点小错误,apertureSize参数值不能为CV_SCHARR(-1)。
        使用拉普拉斯变换的示例代码如下:

    复制代码
    //拉普拉斯变换 private string LaplaceFeatureDetect() { //获取参数 int apertureSize = int.Parse((string)cmbLaplaceApertureSize.SelectedItem); //计算 Stopwatch sw = new Stopwatch(); sw.Start(); Image<Gray, Single> imageDest = imageSourceGrayscale.Laplace(apertureSize); sw.Stop(); //显示 pbResult.Image = imageDest.Bitmap; //释放资源 imageDest.Dispose(); //返回 return string.Format("·拉普拉斯变换,用时{0:F05}毫秒,参数(方形滤波器宽度:{1})\r\n", sw.Elapsed.TotalMilliseconds, apertureSize); }
    复制代码

     
    Canny算子
        Canny算子也可以用作边缘检测;可以用OpenCv中的cvCanny函数或者EmguCv中的Image<TColor,TDepth>.Canny方法来进行Canny边缘检测。所不同的是,Image<TColor,TDepth>.Canny方法可以用于检测彩色图像的边缘,但是它只能使用apertureSize参数的默认值3;
    而cvCanny只能处理灰度图像,不过可以自定义apertureSize。cvCanny和Canny的方法参数名有点点不同,下面是参数对照表。
    Image<TColor,TDepth>.Canny    CvInvoke.cvCanny
    thresh                                         lowThresh
    threshLinking                               highThresh
    3                                                apertureSize
    值得注意的是,apertureSize只能取3,5或者7,这可以在cvcanny.cpp第87行看到:

    aperture_size &= INT_MAX; if( (aperture_size & 1) == 0 || aperture_size < 3 || aperture_size > 7 ) CV_ERROR( CV_StsBadFlag, "" );


    使用Canny算子的示例代码如下:

    复制代码
    //Canny算子 private string CannyFeatureDetect() { //获取参数 double lowThresh = double.Parse(txtCannyLowThresh.Text); double highThresh = double.Parse(txtCannyHighThresh.Text); int apertureSize = int.Parse((string)cmbCannyApertureSize.SelectedItem); //计算 Stopwatch sw = new Stopwatch(); sw.Start(); Image<Gray, Byte> imageDest = null; Image<Bgr, Byte> imageDest2 = null; if (rbCannyUseCvCanny.Checked) { imageDest = new Image<Gray, byte>(imageSourceGrayscale.Size); CvInvoke.cvCanny(imageSourceGrayscale.Ptr, imageDest.Ptr, lowThresh, highThresh, apertureSize); } else imageDest2 = imageSource.Canny(new Bgr(lowThresh, lowThresh, lowThresh), new Bgr(highThresh, highThresh, highThresh)); sw.Stop(); //显示 pbResult.Image = rbCannyUseCvCanny.Checked ? imageDest.Bitmap : imageDest2.Bitmap; //释放资源 if (imageDest != null) imageDest.Dispose(); if (imageDest2 != null) imageDest2.Dispose(); //返回 return string.Format("·Canny算子,用时{0:F05}毫秒,参数(方式:{1},阀值下限:{2},阀值上限:{3},方形滤波器宽度:{4})\r\n", sw.Elapsed.TotalMilliseconds, rbCannyUseCvCanny.Checked ? "cvCanny" : "Image<TColor, TDepth>.Canny", lowThresh, highThresh, apertureSize); }
    复制代码

     

    另外,在http://www.china-vision.net/blog/user2/15975/archives/2007/804.html有一种自动获取Canny算子高低阀值的方法,作者提供了用C语言实现的代码。我将其改写成了C#版本,代码如下:

    复制代码
    /// <summary> /// 计算图像的自适应Canny算子阀值 /// </summary> /// <param name="imageSrc">源图像,只能是256级灰度图像</param> /// <param name="apertureSize">方形滤波器的宽度</param> /// <param name="lowThresh">阀值下限</param> /// <param name="highThresh">阀值上限</param> unsafe void AdaptiveFindCannyThreshold(Image<Gray, Byte> imageSrc, int apertureSize, out double lowThresh, out double highThresh) { //计算源图像x方向和y方向的1阶Sobel算子 Size size = imageSrc.Size; Image<Gray, Int16> imageDx = new Image<Gray, short>(size); Image<Gray, Int16> imageDy = new Image<Gray, short>(size); CvInvoke.cvSobel(imageSrc.Ptr, imageDx.Ptr, 1, 0, apertureSize); CvInvoke.cvSobel(imageSrc.Ptr, imageDy.Ptr, 0, 1, apertureSize); Image<Gray, Single> image = new Image<Gray, float>(size); int i, j; DenseHistogram hist = null; int hist_size = 255; float[] range_0 = new float[] { 0, 256 }; double PercentOfPixelsNotEdges = 0.7; //计算边缘的强度,并保存于图像中 float maxv = 0; float temp; byte* imageDataDx = (byte*)imageDx.MIplImage.imageData.ToPointer(); byte* imageDataDy = (byte*)imageDy.MIplImage.imageData.ToPointer(); byte* imageData = (byte*)image.MIplImage.imageData.ToPointer(); int widthStepDx = imageDx.MIplImage.widthStep; int widthStepDy = widthStepDx; int widthStep = image.MIplImage.widthStep; for (i = 0; i < size.Height; i++) { short* _dx = (short*)(imageDataDx + widthStepDx * i); short* _dy = (short*)(imageDataDy + widthStepDy * i); float* _image = (float*)(imageData + widthStep * i); for (j = 0; j < size.Width; j++) { temp = (float)(Math.Abs(*(_dx + j)) + Math.Abs(*(_dy + j))); *(_image + j) = temp; if (maxv < temp) maxv = temp; } } //计算直方图 range_0[1] = maxv; hist_size = hist_size > maxv ? (int)maxv : hist_size; hist = new DenseHistogram(hist_size, new RangeF(range_0[0], range_0[1])); hist.Calculate<Single>(new Image<Gray, Single>[] { image }, false, null); int total = (int)(size.Height * size.Width * PercentOfPixelsNotEdges); double sum = 0; int icount = hist.BinDimension[0].Size; for (i = 0; i < icount; i++) { sum += hist[i]; if (sum > total) break; } //计算阀值 highThresh = (i + 1) * maxv / hist_size; lowThresh = highThresh * 0.4; //释放资源 imageDx.Dispose(); imageDy.Dispose(); image.Dispose(); hist.Dispose(); }
    复制代码

     
    霍夫变换
        霍夫变换是一种在图像中寻找直线、圆及其他简单形状的方法,在OpenCv中实现了霍夫线变换和霍夫圆变换。值得注意的地方有以下几点:(1)HoughLines2需要先计算Canny边缘,然后再检测直线;(2)HoughLines2计算结果的获取随获取方式的不同而不同;(3)HoughCircles检测结果似乎不正确。
        使用霍夫变换的示例代码如下所示:

    复制代码
    //霍夫线变换 private string HoughLinesFeatureDetect() { //获取参数 HOUGH_TYPE method = rbHoughLinesSHT.Checked ? HOUGH_TYPE.CV_HOUGH_STANDARD : (rbHoughLinesPPHT.Checked ? HOUGH_TYPE.CV_HOUGH_PROBABILISTIC : HOUGH_TYPE.CV_HOUGH_MULTI_SCALE); double rho = double.Parse(txtHoughLinesRho.Text); double theta = double.Parse(txtHoughLinesTheta.Text); int threshold = int.Parse(txtHoughLinesThreshold.Text); double param1 = double.Parse(txtHoughLinesParam1.Text); double param2 = double.Parse(txtHoughLinesParam2.Text); MemStorage storage = new MemStorage(); int linesCount = 0; StringBuilder sbResult = new StringBuilder(); //计算,先运行Canny边缘检测(参数来自Canny算子属性页),然后再用计算霍夫线变换 double lowThresh = double.Parse(txtCannyLowThresh.Text); double highThresh = double.Parse(txtCannyHighThresh.Text); int apertureSize = int.Parse((string)cmbCannyApertureSize.SelectedItem); Image<Gray, Byte> imageCanny = new Image<Gray, byte>(imageSourceGrayscale.Size); CvInvoke.cvCanny(imageSourceGrayscale.Ptr, imageCanny.Ptr, lowThresh, highThresh, apertureSize); Stopwatch sw = new Stopwatch(); sw.Start(); IntPtr ptrLines = CvInvoke.cvHoughLines2(imageCanny.Ptr, storage.Ptr, method, rho, theta, threshold, param1, param2); Seq<LineSegment2D> linesSeq = null; Seq<PointF> linesSeq2 = null; if (method == HOUGH_TYPE.CV_HOUGH_PROBABILISTIC) linesSeq = new Seq<LineSegment2D>(ptrLines, storage); else linesSeq2 = new Seq<PointF>(ptrLines, storage); sw.Stop(); //显示 Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>(); if (linesSeq != null) { linesCount = linesSeq.Total; foreach (LineSegment2D line in linesSeq) { imageResult.Draw(line, new Bgr(255d, 0d, 0d), 4); sbResult.AppendFormat("{0}-{1},", line.P1, line.P2); } } else { linesCount = linesSeq2.Total; foreach (PointF line in linesSeq2) { float r = line.X; float t = line.Y; double a = Math.Cos(t), b = Math.Sin(t); double x0 = a * r, y0 = b * r; int x1 = (int)(x0 + 1000 * (-b)); int y1 = (int)(y0 + 1000 * (a)); int x2 = (int)(x0 - 1000 * (-b)); int y2 = (int)(y0 - 1000 * (a)); Point pt1 = new Point(x1, y1); Point pt2 = new Point(x2, y2); imageResult.Draw(new LineSegment2D(pt1, pt2), new Bgr(255d, 0d, 0d), 4); sbResult.AppendFormat("{0}-{1},", pt1, pt2); } } pbResult.Image = imageResult.Bitmap; //释放资源 imageCanny.Dispose(); imageResult.Dispose(); storage.Dispose(); //返回 return string.Format("·霍夫线变换,用时{0:F05}毫秒,参数(变换方式:{1},距离精度:{2},弧度精度:{3},阀值:{4},参数1:{5},参数2:{6}),找到{7}条直线\r\n{8}", sw.Elapsed.TotalMilliseconds, method.ToString("G"), rho, theta, threshold, param1, param2, linesCount, linesCount != 0 ? (sbResult.ToString() + "\r\n") : ""); } //霍夫圆变换 private string HoughCirclesFeatureDetect() { //获取参数 double dp = double.Parse(txtHoughCirclesDp.Text); double minDist = double.Parse(txtHoughCirclesMinDist.Text); double param1 = double.Parse(txtHoughCirclesParam1.Text); double param2 = double.Parse(txtHoughCirclesParam2.Text); int minRadius = int.Parse(txtHoughCirclesMinRadius.Text); int maxRadius = int.Parse(txtHoughCirclesMaxRadius.Text); StringBuilder sbResult = new StringBuilder(); //计算 Stopwatch sw = new Stopwatch(); sw.Start(); CircleF[][] circles = imageSourceGrayscale.HoughCircles(new Gray(param1), new Gray(param2), dp, minDist, minRadius, maxRadius); sw.Stop(); //显示 Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>(); int circlesCount = 0; foreach (CircleF[] cs in circles) { foreach (CircleF circle in cs) { imageResult.Draw(circle, new Bgr(255d, 0d, 0d), 4); sbResult.AppendFormat("圆心{0}半径{1},", circle.Center, circle.Radius); circlesCount++; } } pbResult.Image = imageResult.Bitmap; //释放资源 imageResult.Dispose(); //返回 return string.Format("·霍夫圆变换,用时{0:F05}毫秒,参数(累加器图像的最小分辨率:{1},不同圆之间的最小距离:{2},边缘阀值:{3},累加器阀值:{4},最小圆半径:{5},最大圆半径:{6}),找到{7}个圆\r\n{8}", sw.Elapsed.TotalMilliseconds, dp, minDist, param1, param2, minRadius, maxRadius, circlesCount, sbResult.Length > 0 ? (sbResult.ToString() + "\r\n") : ""); }
    复制代码

     

    Harris角点
        cvCornerHarris函数检测的结果实际上是一幅包含Harris角点的浮点型单通道图像,可以使用类似下面的代码来计算包含Harris角点的图像:

    复制代码
    //Harris角点 private string CornerHarrisFeatureDetect() { //获取参数 int blockSize = int.Parse(txtCornerHarrisBlockSize.Text); int apertureSize = int.Parse(txtCornerHarrisApertureSize.Text); double k = double.Parse(txtCornerHarrisK.Text); //计算 Image<Gray, Single> imageDest = new Image<Gray, float>(imageSourceGrayscale.Size); Stopwatch sw = new Stopwatch(); sw.Start(); CvInvoke.cvCornerHarris(imageSourceGrayscale.Ptr, imageDest.Ptr, blockSize, apertureSize, k); sw.Stop(); //显示 pbResult.Image = imageDest.Bitmap; //释放资源 imageDest.Dispose(); //返回 return string.Format("·Harris角点,用时{0:F05}毫秒,参数(邻域大小:{1},方形滤波器宽度:{2},权重系数:{3})\r\n", sw.Elapsed.TotalMilliseconds, blockSize, apertureSize, k); }
    复制代码

        如果要计算Harris角点列表,需要使用cvGoodFeatureToTrack函数,并传递适当的参数。

    ShiTomasi角点
        在默认情况下,cvGoodFeatureToTrack函数计算ShiTomasi角点;不过如果将参数use_harris设置为非0值,那么它会计算harris角点。
    使用cvGoodFeatureToTrack函数的示例代码如下:

    复制代码
    //ShiTomasi角点 private string CornerShiTomasiFeatureDetect() { //获取参数 int cornerCount = int.Parse(txtGoodFeaturesCornerCount.Text); double qualityLevel = double.Parse(txtGoodFeaturesQualityLevel.Text); double minDistance = double.Parse(txtGoodFeaturesMinDistance.Text); int blockSize = int.Parse(txtGoodFeaturesBlockSize.Text); bool useHarris = cbGoodFeaturesUseHarris.Checked; double k = double.Parse(txtGoodFeaturesK.Text); //计算 Stopwatch sw = new Stopwatch(); sw.Start(); PointF[][] corners = imageSourceGrayscale.GoodFeaturesToTrack(cornerCount, qualityLevel, minDistance, blockSize, useHarris, k); sw.Stop(); //显示 Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>(); int cornerCount2 = 0; StringBuilder sbResult = new StringBuilder(); int radius = (int)(minDistance / 2) + 1; int thickness = (int)(minDistance / 4) + 1; foreach (PointF[] cs in corners) { foreach (PointF p in cs) { imageResult.Draw(new CircleF(p, radius), new Bgr(255d, 0d, 0d), thickness); cornerCount2++; sbResult.AppendFormat("{0},", p); } } pbResult.Image = imageResult.Bitmap; //释放资源 imageResult.Dispose(); //返回 return string.Format("·ShiTomasi角点,用时{0:F05}毫秒,参数(最大角点数目:{1},最小特征值:{2},角点间的最小距离:{3},邻域大小:{4},角点类型:{5},权重系数:{6}),检测到{7}个角点\r\n{8}", sw.Elapsed.TotalMilliseconds, cornerCount, qualityLevel, minDistance, blockSize, useHarris ? "Harris" : "ShiTomasi", k, cornerCount2, cornerCount2 > 0 ? (sbResult.ToString() + "\r\n") : ""); }
    复制代码

     

    亚像素级角点
        在检测亚像素级角点前,需要提供角点的初始为止,这些初始位置可以用本文给出的其他的角点检测方式来获取,不过使用GoodFeaturesToTrack得到的结果最方便直接使用。
        亚像素级角点检测的示例代码如下:

    复制代码
    //亚像素级角点 private string CornerSubPixFeatureDetect() { //获取参数 int winWidth = int.Parse(txtCornerSubPixWinWidth.Text); int winHeight = int.Parse(txtCornerSubPixWinHeight.Text); Size win = new Size(winWidth, winHeight); int zeroZoneWidth = int.Parse(txtCornerSubPixZeroZoneWidth.Text); int zeroZoneHeight = int.Parse(txtCornerSubPixZeroZoneHeight.Text); Size zeroZone = new Size(zeroZoneWidth, zeroZoneHeight); int maxIter=int.Parse(txtCornerSubPixMaxIter.Text); double epsilon=double.Parse(txtCornerSubPixEpsilon.Text); MCvTermCriteria criteria = new MCvTermCriteria(maxIter, epsilon); //先计算得到易于跟踪的点(ShiTomasi角点) int cornerCount = int.Parse(txtGoodFeaturesCornerCount.Text); double qualityLevel = double.Parse(txtGoodFeaturesQualityLevel.Text); double minDistance = double.Parse(txtGoodFeaturesMinDistance.Text); int blockSize = int.Parse(txtGoodFeaturesBlockSize.Text); bool useHarris = cbGoodFeaturesUseHarris.Checked; double k = double.Parse(txtGoodFeaturesK.Text); PointF[][] corners = imageSourceGrayscale.GoodFeaturesToTrack(cornerCount, qualityLevel, minDistance, blockSize, useHarris, k); //计算 Stopwatch sw = new Stopwatch(); sw.Start(); imageSourceGrayscale.FindCornerSubPix(corners, win, zeroZone, criteria); sw.Stop(); //显示 Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>(); int cornerCount2 = 0; StringBuilder sbResult = new StringBuilder(); int radius = (int)(minDistance / 2) + 1; int thickness = (int)(minDistance / 4) + 1; foreach (PointF[] cs in corners) { foreach (PointF p in cs) { imageResult.Draw(new CircleF(p, radius), new Bgr(255d, 0d, 0d), thickness); cornerCount2++; sbResult.AppendFormat("{0},", p); } } pbResult.Image = imageResult.Bitmap; //释放资源 imageResult.Dispose(); //返回 return string.Format("·亚像素级角点,用时{0:F05}毫秒,参数(搜索窗口:{1},死区:{2},最大迭代次数:{3},亚像素值的精度:{4}),检测到{5}个角点\r\n{6}", sw.Elapsed.TotalMilliseconds, win, zeroZone, maxIter, epsilon, cornerCount2, cornerCount2 > 0 ? (sbResult.ToString() + "\r\n") : ""); }
    复制代码

     

    SURF角点
        OpenCv中的cvExtractSURF函数和EmguCv中的Image<TColor,TDepth>.ExtractSURF方法用于检测SURF角点。
        SURF角点检测的示例代码如下:

    复制代码
    //SURF角点 private string SurfFeatureDetect() { //获取参数 bool getDescriptors = cbSurfGetDescriptors.Checked; MCvSURFParams surfParam = new MCvSURFParams(); surfParam.extended=rbSurfBasicDescriptor.Checked ? 0 : 1; surfParam.hessianThreshold=double.Parse(txtSurfHessianThreshold.Text); surfParam.nOctaves=int.Parse(txtSurfNumberOfOctaves.Text); surfParam.nOctaveLayers=int.Parse(txtSurfNumberOfOctaveLayers.Text); //计算 SURFFeature[] features = null; MKeyPoint[] keyPoints = null; Stopwatch sw = new Stopwatch(); sw.Start(); if (getDescriptors) features = imageSourceGrayscale.ExtractSURF(ref surfParam); else keyPoints = surfParam.DetectKeyPoints(imageSourceGrayscale, null); sw.Stop(); //显示 bool showDetail = cbSurfShowDetail.Checked; Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>(); StringBuilder sbResult = new StringBuilder(); int idx = 0; if (getDescriptors) { foreach (SURFFeature feature in features) { imageResult.Draw(new CircleF(feature.Point.pt, 5), new Bgr(255d, 0d, 0d), 2); if (showDetail) { sbResult.AppendFormat("第{0}点(坐标:{1},尺寸:{2},方向:{3}°,hessian值:{4},拉普拉斯标志:{5},描述:[", idx, feature.Point.pt, feature.Point.size, feature.Point.dir, feature.Point.hessian, feature.Point.laplacian); foreach (float d in feature.Descriptor) sbResult.AppendFormat("{0},", d); sbResult.Append("]),"); } idx++; } } else { foreach (MKeyPoint keypoint in keyPoints) { imageResult.Draw(new CircleF(keypoint.Point, 5), new Bgr(255d, 0d, 0d), 2); if (showDetail) sbResult.AppendFormat("第{0}点(坐标:{1},尺寸:{2},方向:{3}°,响应:{4},octave:{5}),", idx, keypoint.Point, keypoint.Size, keypoint.Angle, keypoint.Response, keypoint.Octave); idx++; } } pbResult.Image = imageResult.Bitmap; //释放资源 imageResult.Dispose(); //返回 return string.Format("·SURF角点,用时{0:F05}毫秒,参数(描述:{1},hessian阀值:{2},octave数目:{3},每个octave的层数:{4},检测到{5}个角点\r\n{6}", sw.Elapsed.TotalMilliseconds, getDescriptors ? (surfParam.extended == 0 ? "获取基本描述" : "获取扩展描述") : "不获取描述", surfParam.hessianThreshold, surfParam.nOctaves, surfParam.nOctaveLayers, getDescriptors ? features.Length : keyPoints.Length, showDetail ? sbResult.ToString() + "\r\n" : ""); }
    复制代码

     

    Star关键点
        OpenCv中的cvGetStarKeypoints函数和EmguCv中的Image<TColor,TDepth>.GetStarKeypoints方法用于检测“星型”附近的点。
        Star关键点检测的示例代码如下:

    复制代码
    //Star关键点 private string StarKeyPointFeatureDetect() { //获取参数 StarDetector starParam = new StarDetector(); starParam.MaxSize = int.Parse((string)cmbStarMaxSize.SelectedItem); starParam.ResponseThreshold = int.Parse(txtStarResponseThreshold.Text); starParam.LineThresholdProjected = int.Parse(txtStarLineThresholdProjected.Text); starParam.LineThresholdBinarized = int.Parse(txtStarLineThresholdBinarized.Text); starParam.SuppressNonmaxSize = int.Parse(txtStarSuppressNonmaxSize.Text); //计算 Stopwatch sw = new Stopwatch(); sw.Start(); MCvStarKeypoint[] keyPoints = imageSourceGrayscale.GetStarKeypoints(ref starParam); sw.Stop(); //显示 Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>(); StringBuilder sbResult = new StringBuilder(); int idx = 0; foreach (MCvStarKeypoint keypoint in keyPoints) { imageResult.Draw(new CircleF(new PointF(keypoint.pt.X, keypoint.pt.Y), keypoint.size / 2), new Bgr(255d, 0d, 0d), keypoint.size / 4); sbResult.AppendFormat("第{0}点(坐标:{1},尺寸:{2},强度:{3}),", idx, keypoint.pt, keypoint.size, keypoint.response); idx++; } pbResult.Image = imageResult.Bitmap; //释放资源 imageResult.Dispose(); //返回 return string.Format("·Star关键点,用时{0:F05}毫秒,参数(MaxSize:{1},ResponseThreshold:{2},LineThresholdProjected:{3},LineThresholdBinarized:{4},SuppressNonmaxSize:{5}),检测到{6}个关键点\r\n{7}", sw.Elapsed.TotalMilliseconds, starParam.MaxSize, starParam.ResponseThreshold, starParam.LineThresholdProjected, starParam.LineThresholdBinarized, starParam.SuppressNonmaxSize, keyPoints.Length, keyPoints.Length > 0 ? (sbResult.ToString() + "\r\n") : ""); }
    复制代码

     
    FAST角点检测
        FAST角点由E. Rosten教授提出,相比其他检测手段,这种方法的速度正如其名,相当的快。值得关注的是他所研究的理论都是属于实用类的,都很快。Rosten教授实现了FAST角点检测,并将其提供给了OpenCv,相当的有爱呀;不过OpenCv中的函数和Rosten教授的实现似乎有点点不太一样。遗憾的是,OpenCv中目前还没有FAST角点检测的文档。下面是我从Rosten的代码中找到的函数声明,可以看到粗略的参数说明。
    /*
    The references are:

     * Machine learning for high-speed corner detection,
     
       E. Rosten and T. Drummond, ECCV 2006
     * Faster and better: A machine learning approach to corner detection

       E. Rosten, R. Porter and T. Drummond, PAMI, 2009

    */
    void cvCornerFast( const CvArr* image, int threshold, int N,

                       int nonmax_suppression, int* ret_number_of_corners,
                       CvPoint** ret_corners);


    image:      OpenCV image in which to detect corners. Must be 8 bit unsigned.

    threshold:  Threshold for detection (higher is fewer corners). 0--255

    N:          Arc length of detector, 9, 10, 11 or 12. 9 is usually best.

    nonmax_suppression: Whether to perform nonmaximal suppression.

    ret_number_of_corners: The number of detected corners is returned here.

    ret_corners: The corners are returned here.
    EmguCv中的Image<TColor,TDepth>.GetFASTKeypoints方法也实现了FAST角点检测,不过参数少了一些,只有threshold和nonmaxSupression,其中N我估计取的默认值9,但是返回的角点数目我不知道是怎么设置的。
    使用FAST角点检测的示例代码如下:

    复制代码
    //FAST关键点 private string FASTKeyPointFeatureDetect() { //获取参数 int threshold = int.Parse(txtFASTThreshold.Text); bool nonmaxSuppression = cbFASTNonmaxSuppression.Checked; bool showDetail = cbFASTShowDetail.Checked; //计算 Stopwatch sw = new Stopwatch(); sw.Start(); MKeyPoint[] keyPoints = imageSourceGrayscale.GetFASTKeypoints(threshold, nonmaxSuppression); sw.Stop(); //显示 Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>(); StringBuilder sbResult = new StringBuilder(); int idx = 0; foreach (MKeyPoint keypoint in keyPoints) { imageResult.Draw(new CircleF(keypoint.Point, (int)(keypoint.Size / 2)), new Bgr(255d, 0d, 0d), (int)(keypoint.Size / 4)); if (showDetail) sbResult.AppendFormat("第{0}点(坐标:{1},尺寸:{2},方向:{3}°,响应:{4},octave:{5}),", idx, keypoint.Point, keypoint.Size, keypoint.Angle, keypoint.Response, keypoint.Octave); idx++; } pbResult.Image = imageResult.Bitmap; //释放资源 imageResult.Dispose(); //返回 return string.Format("·FAST关键点,用时{0:F05}毫秒,参数(阀值:{1},nonmaxSupression:{2}),检测到{3}个关键点\r\n{4}", sw.Elapsed.TotalMilliseconds, threshold, nonmaxSuppression, keyPoints.Length, showDetail ? (sbResult.ToString() + "\r\n") : ""); }
    复制代码

     

    Lepetit关键点
        Lepetit关键点由Vincent Lepetit提出,可以在他的网站(http://cvlab.epfl.ch/~vlepetit/)上看到相关的论文等资料。EmguCv中的类LDetector实现了Lepetit关键点的检测。
        使用Lepetit关键点检测的示例代码如下:

    复制代码
    //Lepetit关键点 private string LepetitKeyPointFeatureDetect() { //获取参数 LDetector lepetitDetector = new LDetector(); lepetitDetector.BaseFeatureSize = double.Parse(txtLepetitBaseFeatureSize.Text); lepetitDetector.ClusteringDistance = double.Parse(txtLepetitClasteringDistance.Text); lepetitDetector.NOctaves = int.Parse(txtLepetitNumberOfOctaves.Text); lepetitDetector.NViews = int.Parse(txtLepetitNumberOfViews.Text); lepetitDetector.Radius = int.Parse(txtLepetitRadius.Text); lepetitDetector.Threshold = int.Parse(txtLepetitThreshold.Text); lepetitDetector.Verbose = cbLepetitVerbose.Checked; int maxCount = int.Parse(txtLepetitMaxCount.Text); bool scaleCoords = cbLepetitScaleCoords.Checked; bool showDetail = cbLepetitShowDetail.Checked; //计算 Stopwatch sw = new Stopwatch(); sw.Start(); MKeyPoint[] keyPoints = lepetitDetector.DetectKeyPoints(imageSourceGrayscale, maxCount, scaleCoords); sw.Stop(); //显示 Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>(); StringBuilder sbResult = new StringBuilder(); int idx = 0; foreach (MKeyPoint keypoint in keyPoints) { //imageResult.Draw(new CircleF(keypoint.Point, (int)(keypoint.Size / 2)), new Bgr(255d, 0d, 0d), (int)(keypoint.Size / 4)); imageResult.Draw(new CircleF(keypoint.Point, 4), new Bgr(255d, 0d, 0d), 2); if (showDetail) sbResult.AppendFormat("第{0}点(坐标:{1},尺寸:{2},方向:{3}°,响应:{4},octave:{5}),", idx, keypoint.Point, keypoint.Size, keypoint.Angle, keypoint.Response, keypoint.Octave); idx++; } pbResult.Image = imageResult.Bitmap; //释放资源 imageResult.Dispose(); //返回 return string.Format("·Lepetit关键点,用时{0:F05}毫秒,参数(基础特征尺寸:{1},集群距离:{2},阶数:{3},视图数:{4},半径:{5},阀值:{6},计算详细结果:{7},最大关键点数目:{8},缩放坐标:{9}),检测到{10}个关键点\r\n{11}", sw.Elapsed.TotalMilliseconds, lepetitDetector.BaseFeatureSize, lepetitDetector.ClusteringDistance, lepetitDetector.NOctaves, lepetitDetector.NViews, lepetitDetector.Radius, lepetitDetector.Threshold, lepetitDetector.Verbose, maxCount, scaleCoords, keyPoints.Length, showDetail ? (sbResult.ToString() + "\r\n") : ""); }
    复制代码


    SIFT角点
        SIFT角点是一种广泛使用的图像特征,可用于物体跟踪、图像匹配、图像拼接等领域,然而奇怪的是它并未被OpenCv实现。提出SIFT角点的David Lowe教授已经用C和matlab实现了SIFT角点的检测,并开放了源代码,不过他的实现不方便直接使用。您可以在http://www.cs.ubc.ca/~lowe/keypoints/看到SIFT的介绍、相关论文及David Lowe教授的实现代码。下面我要介绍由Andrea Vedaldi和Brian Fulkerson先生创建的vlfeat开源图像处理库,vlfeat库有C和matlab两种实现,其中包含了SIFT检测。您可以在http://www.vlfeat.org/下载到vlfeat库的代码、文档及可执行文件。
        使用vlfeat检测SIFT角点需要以下步骤:
        (1)用函数vl_sift_new()初始化SIFT过滤器对象,该过滤器对象可以反复用于多幅尺寸相同的图像;
        (2)用函数vl_sift_first_octave()及vl_sift_process_next()遍历缩放空间的每一阶,直到返回VL_ERR_EOF为止;
        (3)对于缩放空间的每一阶,用函数vl_sift_detect()来获取关键点;
        (4)对每个关键点,用函数vl_sift_calc_keypoint_orientations()来获取该点的方向;
        (5)对关键点的每个方向,用函数vl_sift_calc_keypoint_descriptor()来获取该方向的描述;
        (6)使用完之后,用函数vl_sift_delete()来释放资源;
        (7)如果要计算某个自定义关键点的描述,可以使用函数vl_sift_calc_raw_descriptor()。
        直接使用vlfeat中的SIFT角点检测示例代码如下:

    复制代码
    //通过P/Invoke调用vlfeat函数来进行SIFT检测 unsafe private string SiftFeatureDetectByPinvoke(int noctaves, int nlevels, int o_min, bool showDetail) { StringBuilder sbResult = new StringBuilder(); //初始化 IntPtr ptrSiftFilt = VlFeatInvoke.vl_sift_new(imageSource.Width, imageSource.Height, noctaves, nlevels, o_min); if (ptrSiftFilt == IntPtr.Zero) return "Sift特征检测:初始化失败。"; //处理 Image<Gray, Single> imageSourceSingle = imageSourceGrayscale.ConvertScale<Single>(1d, 0d); Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>(); int pointCount = 0; int idx = 0; //依次遍历每一组 if (VlFeatInvoke.vl_sift_process_first_octave(ptrSiftFilt, imageSourceSingle.MIplImage.imageData) != VlFeatInvoke.VL_ERR_EOF) { while (true) { //计算每组中的关键点 VlFeatInvoke.vl_sift_detect(ptrSiftFilt); //遍历并绘制每个点 VlSiftFilt siftFilt = (VlSiftFilt)Marshal.PtrToStructure(ptrSiftFilt, typeof(VlSiftFilt)); pointCount += siftFilt.nkeys; VlSiftKeypoint* pKeyPoints = (VlSiftKeypoint*)siftFilt.keys.ToPointer(); for (int i = 0; i < siftFilt.nkeys; i++) { VlSiftKeypoint keyPoint = *pKeyPoints; pKeyPoints++; imageResult.Draw(new CircleF(new PointF(keyPoint.x, keyPoint.y), keyPoint.sigma / 2), new Bgr(255d, 0d, 0d), 2); if (showDetail) sbResult.AppendFormat("第{0}点,坐标:({1},{2}),阶:{3},缩放:{4},s:{5},", idx, keyPoint.x, keyPoint.y, keyPoint.o, keyPoint.sigma, keyPoint.s); idx++; //计算并遍历每个点的方向 double[] angles = new double[4]; int angleCount = VlFeatInvoke.vl_sift_calc_keypoint_orientations(ptrSiftFilt, angles, ref keyPoint); if (showDetail) sbResult.AppendFormat("共{0}个方向,", angleCount); for (int j = 0; j < angleCount; j++) { double angle = angles[j]; if (showDetail) sbResult.AppendFormat("【方向:{0},描述:", angle); //计算每个方向的描述 IntPtr ptrDescriptors = Marshal.AllocHGlobal(128 * sizeof(float)); VlFeatInvoke.vl_sift_calc_keypoint_descriptor(ptrSiftFilt, ptrDescriptors, ref keyPoint, angle); float* pDescriptors = (float*)ptrDescriptors.ToPointer(); for (int k = 0; k < 128; k++) { float descriptor = *pDescriptors; pDescriptors++; if (showDetail) sbResult.AppendFormat("{0},", descriptor); } sbResult.Append("】,"); Marshal.FreeHGlobal(ptrDescriptors); } } //下一阶 if (VlFeatInvoke.vl_sift_process_next_octave(ptrSiftFilt) == VlFeatInvoke.VL_ERR_EOF) break; } } //显示 pbResult.Image = imageResult.Bitmap; //释放资源 VlFeatInvoke.vl_sift_delete(ptrSiftFilt); imageSourceSingle.Dispose(); imageResult.Dispose(); //返回 return string.Format("·SIFT特征检测(P/Invoke),用时:未统计,参数(阶数:{0},每阶层数:{1},最小阶索引:{2}),{3}个关键点\r\n{4}", noctaves, nlevels, o_min, pointCount, showDetail ? (sbResult.ToString() + "\r\n") : ""); }
    复制代码


        要在.net中使用vlfeat还是不够方便,为此我对vlfeat中的SIFT角点检测部分进行了封装,将相关操作放到了类SiftDetector中。
        使用SiftDetector需要两至三步:
        (1)用构造函数初始化SiftDetector对象;
        (2)用Process方法计算特征;
        (3)视需要调用Dispose方法释放资源,或者等待垃圾回收器来自动释放资源。
        使用SiftDetector的示例代码如下:

    复制代码
    //通过dotnet封装的SiftDetector类来进行SIFT检测 private string SiftFeatureDetectByDotNet(int noctaves, int nlevels, int o_min, bool showDetail) { //初始化对象 SiftDetector siftDetector = new SiftDetector(imageSource.Size, noctaves, nlevels, o_min); //计算 Image<Gray, Single> imageSourceSingle = imageSourceGrayscale.Convert<Gray, Single>(); Stopwatch sw = new Stopwatch(); sw.Start(); List<SiftFeature> features = siftDetector.Process(imageSourceSingle, showDetail ? SiftDetectorResultType.Extended : SiftDetectorResultType.Basic); sw.Stop(); //显示结果 Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>(); StringBuilder sbResult = new StringBuilder(); int idx=0; foreach (SiftFeature feature in features) { imageResult.Draw(new CircleF(new PointF(feature.keypoint.x, feature.keypoint.y), feature.keypoint.sigma / 2), new Bgr(255d, 0d, 0d), 2); if (showDetail) { sbResult.AppendFormat("第{0}点,坐标:({1},{2}),阶:{3},缩放:{4},s:{5},", idx, feature.keypoint.x, feature.keypoint.y, feature.keypoint.o, feature.keypoint.sigma, feature.keypoint.s); sbResult.AppendFormat("共{0}个方向,", feature.keypointOrientations != null ? feature.keypointOrientations.Length : 0); if (feature.keypointOrientations != null) { foreach (SiftKeyPointOrientation orientation in feature.keypointOrientations) { if (orientation.descriptors != null) { sbResult.AppendFormat("【方向:{0},描述:", orientation.angle); foreach (float descriptor in orientation.descriptors) sbResult.AppendFormat("{0},", descriptor); } else sbResult.AppendFormat("【方向:{0},", orientation.angle); sbResult.Append("】,"); } } } } pbResult.Image = imageResult.Bitmap; //释放资源 siftDetector.Dispose(); imageSourceSingle.Dispose(); imageResult.Dispose(); //返回 return string.Format("·SIFT特征检测(.net),用时:{0:F05}毫秒,参数(阶数:{1},每阶层数:{2},最小阶索引:{3}),{4}个关键点\r\n{5}", sw.Elapsed.TotalMilliseconds, noctaves, nlevels, o_min, features.Count, showDetail ? (sbResult.ToString() + "\r\n") : ""); }
    复制代码


        对vlfeat库中的SIFT部分封装代码如下所示:

    定义SiftDetector类


    MSER区域
        OpenCv中的函数cvExtractMSER以及EmguCv中的Image<TColor,TDepth>.ExtractMSER方法实现了MSER区域的检测。由于OpenCv的文档中目前还没有cvExtractMSER这一部分,大家如果要看文档的话,可以先去看EmguCv的文档。
        需要注意的是MSER区域的检测结果是区域中所有的点序列。例如检测到3个区域,其中一个区域是从(0,0)到(2,1)的矩形,那么结果点序列为:(0,0),(1,0),(2,0),(2,1),(1,1),(0,1)。
        MSER区域检测的示例代码如下:

    复制代码
    //MSER(区域)特征检测 private string MserFeatureDetect() { //获取参数 MCvMSERParams mserParam = new MCvMSERParams(); mserParam.delta = int.Parse(txtMserDelta.Text); mserParam.maxArea = int.Parse(txtMserMaxArea.Text); mserParam.minArea = int.Parse(txtMserMinArea.Text); mserParam.maxVariation = float.Parse(txtMserMaxVariation.Text); mserParam.minDiversity = float.Parse(txtMserMinDiversity.Text); mserParam.maxEvolution = int.Parse(txtMserMaxEvolution.Text); mserParam.areaThreshold = double.Parse(txtMserAreaThreshold.Text); mserParam.minMargin = double.Parse(txtMserMinMargin.Text); mserParam.edgeBlurSize = int.Parse(txtMserEdgeBlurSize.Text); bool showDetail = cbMserShowDetail.Checked; //计算 Stopwatch sw = new Stopwatch(); sw.Start(); MemStorage storage = new MemStorage(); Seq<Point>[] regions = imageSource.ExtractMSER(null, ref mserParam, storage); sw.Stop(); //显示 Image<Bgr, Byte> imageResult = imageSourceGrayscale.Convert<Bgr, Byte>(); StringBuilder sbResult = new StringBuilder(); int idx = 0; foreach (Seq<Point> region in regions) { imageResult.DrawPolyline(region.ToArray(), true, new Bgr(255d, 0d, 0d), 2); if (showDetail) { sbResult.AppendFormat("第{0}区域,包含{1}个顶点(", idx, region.Total); foreach (Point pt in region) sbResult.AppendFormat("{0},", pt); sbResult.Append(")\r\n"); } idx++; } pbResult.Image = imageResult.Bitmap; //释放资源 imageResult.Dispose(); storage.Dispose(); //返回 return string.Format("·MSER区域,用时{0:F05}毫秒,参数(delta:{1},maxArea:{2},minArea:{3},maxVariation:{4},minDiversity:{5},maxEvolution:{6},areaThreshold:{7},minMargin:{8},edgeBlurSize:{9}),检测到{10}个区域\r\n{11}", sw.Elapsed.TotalMilliseconds, mserParam.delta, mserParam.maxArea, mserParam.minArea, mserParam.maxVariation, mserParam.minDiversity, mserParam.maxEvolution, mserParam.areaThreshold, mserParam.minMargin, mserParam.edgeBlurSize, regions.Length, showDetail ? sbResult.ToString() : ""); }
    复制代码

     

    各种特征检测方法性能对比
        上面介绍了这么多的特征检测方法,那么它们的性能到底如何呢?因为它们的参数设置对处理时间及结果的影响很大,我们在这里基本都使用默认参数处理同一幅图像。在我机器上的处理结果见下表:

    特征 用时(毫秒) 特征数目
    Sobel算子 5.99420 n/a
    拉普拉斯算子 3.13440 n/a
    Canny算子 3.41160 n/a
    霍夫线变换 13.70790 10
    霍夫圆变换 78.07720 0
    Harris角点 9.41750 n/a
    ShiTomasi角点 16.98390 18
    亚像素级角点 3.63360 18
    SURF角点 266.27000 151
    Star关键点 14.82800 56
    FAST角点 31.29670 159
    SIFT角点 287.52310 54
    MSER区域 40.62970 2

    (图片尺寸:583x301,处理器:AMD ATHLON IIx2 240,内存:DDR3 4G,显卡:GeForce 9500GT,操作系统:Windows 7)

     

    感谢您耐心看完本文,希望对您有所帮助。

    下一篇文章我们将一起看看如何来跟踪本文讲到的特征点(角点)。

    另外,如果需要本文的源代码,请点击这里下载

    展开全文
  • 图像分类与检测概述

    千次阅读 2018-10-11 14:13:34
    图像分类与检测是现今计算机视觉处理中最为常见的两项任务,本文尽量综述一下传统的图像分类与检测与现今的图像分类与检测技术。以下是要讲的几个方面: 图像分类与检测概述 传统的图像分类与检测方法 现今的图像...

    图像分类与检测是现今计算机视觉处理中最为常见的两项任务,本文尽量综述一下传统的图像分类与检测与现今的图像分类与检测技术。以下是要讲的几个方面:

    图像分类与检测概述
    传统的图像分类与检测方法
    现今的图像分类与检测方法
    

    1 图像分类与检测概述

    当我们面对一张图片的时候,最基础的任务就是这张图片是什么,是风景图还是人物图、是描写建筑物的还是关于食物的,这就是分类。分类作为一个较为笼统的目标,还是较为好达成的。当知道了图像的类别的时候,进一步的就是检测了,例如我知道这个图像是关于人脸的,那么这个人脸在哪里,能不能把它框出来。检测作为一个较为精细的目标,达成的难度可以说是远大于分类的。

    1.1 图像分类与检测的难点

    不只是图像分类与检测,几乎所有的关于机器学习的难点,都是特征提取这一步,一旦找到好的特征,分类与检测就变的很容易了。所谓的特征提取就是指构建一种提取算法,提取出图像里目标对象的特征,例如人脸的边缘特征、皮肤的颜色特征等,这个特征需要尽可能的将目标物体与其他物体区分开来,例如需要区分的物体是黑猫和白猫,那么毫无疑问颜色特征是一个很好的特征。但是,生活中遇到的难题往往都是很难去提取特征的,例如在嘈杂的街道上检测行人与车辆,这种任务对于检测算法的正确率要求很高,因为一不小心漏检或错检一个人可能就会带来一场车祸。

    1.2 图像分类与检测的评价指标

    图像分类的指标比较常见,就是分类的正确率,例如猫狗分类,100张中99张分类正确,那么正确率就是99%的正确率。对于目标检测来说,评价指标就多了一些,主要是检错率、漏检率以及检测meaniou,检错率是指一张图片上有两只猫一只狗,而你的模型检测出了三只猫,那么那第三只猫就是检错的,检错率就是33%;漏检的意思是如果你将上面的图片只检测出一只猫和一只狗的话,那就是漏检了一只猫,漏检率33%;mean_iou指的是你检测出来的目标物体的框和真实的框之间的交并比,如下图:
    2 传统的图像分类与检测方法

    传统的图像分类与检测的步骤大致是两步:特征提取-》训练分类器

    在特征提取阶段,设计者会尝试各种通用特征或者自己设计的特征对图像进行特征提取,我们以人脸检测为例,通用的特征就是HOG、SURF、LBP等特征,而对于人脸效果比较好的特征有鼎鼎大名的haar特征。在选定了这些备用特征之后,设计者会进行尝试特征与权衡利弊,因为对于计算机来说,特征的唯一性、通用性越好,所意味的计算量就越大,设计者必须在保证速度的情况下选择尽量好的特征。

    当选择了计算量适中同时能达到要求的准确率的特征例如LBP和haar特征之后,使用传统的机器学习方法例如朴素贝叶斯、adaboost、随机森林等建模方法,训练出一个分类模型,使用这个分类模型来进行图像分类或检测。模型的选择通常要比特征选择简单的多,只需要把备选的模型都试一遍,挑效果最好的模型进行调参,调到极致之后,一个人脸检测模型就做出来的。

    走这么一套传统图像检测方法的流程,需要很长的时间,并且每一步都需要做好,最终的模型才会有较好的效果,一旦中间的一步出现错误就会牵一发而动全身。所以使用传统方法做图像处理是需要很大代价的。
    3 现今的图像分类和检测方法

    自从2015年深度学习占领各大图像处理比赛榜首之后,现在的图像处理大部分使用的方法都是深度学习,也就是神经网络。神经网络通过很多的神经元构建成一层一层的网络,通过激活层来使得模型有很强的非线性拟合的能力。设计者只需要将图像输入,然后告诉模型需要的结果是什么,模型便会自动的学习特征提取与结果映射。

    通过深度学习,剩下设计者在传统图像处理时最为费时费力的特征提取的那一部分。设计者只需要设计网络结果,使得网络自动提取的特征越好,效果就会也好,正确率越高。

    神经网络本质上是矩阵相乘与非线性的组合,通过很多很多的滤波核,来过滤对结果最为有用的特征而抑制对结果没有用的特征,来进行学习与分类。

    现在在工程中最为常用的还是vgg、resnet、inception这几种结构,设计者通常会先直接套用原版的模型对数据进行训练一次,然后选择效果较为好的模型进行微调与模型缩减。因为工程上使用的模型必须在精度高的同时速度要快。常用的模型缩减的方法是减少卷积和个数与减少resnet的模块数。

    现在常用的检测模型,还是FRCNN、Mask-RCNN、YOLO、SSD等网络模型,一方面精度确实是高,另一方面速度现在进过优化也可以做到实时了。像上面的人脸检测的功能,只需要准备好人脸图片及对应的框标注文件,便可直接跑模型,得到一个还不错的检测模型。
    4 总结

    深度学习对于图像处理非常有用,但同时也有一些弊端,例如需要大量数据、调参很依靠经验,需要的计算能力很高等,适合处理很复杂的现实生活场景。传统的图像处理对于特定场景下简单的任务例如文本文档的检测、矫正等,还是非常有用且高效的。

    第一次写文章,这是从工程角度写的综述,不足之处恳请批评指正。以后会不定期写写图像处理的一些小项目,欢迎交流。

    展开全文
  • 计算机视觉领域目标检测必看论文,包括ResNet,R-CNN,SDD,YOLO等
  • 图像处理---目标检测二十年

    万次阅读 多人点赞 2018-04-17 11:11:07
    “目标检测是当前计算机视觉和机器学习领域的研究热点。从Viola-Jones Detector、DPM等冷兵器时代的智慧到当今RCNN、YOLO等深度学习土壤孕育下的GPU暴力美学...从过去的十多年来看,自然图像的目标检测算法大体上可以分
  • matlab图像检测处理——图形检测

    万次阅读 2018-09-03 08:49:06
    本博客仅仅是因为自己经常忘记,记笔记而已 常用操作: 1、二值化 clc A=imread('a.jpg'); %读取到一张图片 ... %对图像二值化 figure(1) imshow(A) %显示二值化之前的图片 figure(2) ims...
  • 图像检测指标

    2019-12-25 10:12:35
    图像检测指标分类指标精确率(Precision), 召回率(Recall)混淆矩阵(Confusion Matrix)准确率(Accuracy)F-scoreP-R曲线 分类指标 主要有准确率(accuracy),精确率/查准率(precision),召回率/查全率(recall),PR...
  • 1、图像分类图像分类主要是基于图像的内容对图像进行标记,通常会有一组固定的标签,而你的模型必须预测出最适合图像的标签。这个问题对于机器来说相当困难的,因为它看到的只是图像中的一组数字流。 上图片来自于...
  • 图像检测技术的研究现状

    万次阅读 2016-09-08 13:29:08
    图像检测技术的研究现状 技术检测 图像处理知识库 · 2016-01-08 19:59 图像检测技术的研究现状 所谓图像检测,就是通过图像对感兴趣的特征区域(检测目标)进行提取的过程,其中图像是承载检测...
  • 医学图像检测论文汇总

    千次阅读 2019-02-21 15:28:50
    医学图像检测与自然图像检测具有一些差异,导致自然图像检测方法迁移到医学图像检测时存在困难。   自然图像的检测目标通常较明显,且类别较多。常用检测网络包括:(1) ResNet、FastRCNN、FasterRCNN(具有更深...
  • 历经5年研发,内置完整的商业级的机器视觉算法,经过上百个行业实例验证,国内自主产权的智能CCD图像检测系统,正式向大家开放了,大家可以免费试用,评估了。 康恩士智能CCD图像检测系统是一个基于数据流的、为...
  • CV之FD之HOG:图像检测之基于HOG算法、简介、代码实现(计算图像相似度)之详细攻略 目录 基于HOG的人脸识别算法简介 1、主要思想 2、实现方法 3、性能提高 4、HOG特征的优点 5、HOG特征的提取与计算步骤 ...
  • Opencv 图像增强算法 图像检测结果

    万次阅读 多人点赞 2012-03-28 14:17:05
    将图像灰度阈值拉伸到0-255,图像检测结果见底部 Keywords: 图像增强 增强对比度 直方图变换 int ImageStretchByHistogram(IplImage *src1,IplImage *dst1) /*************************************************...
  • 用CNN识别CT图像检测肺癌

    万次阅读 2018-07-29 14:50:46
    用CNN识别CT图像检测肺癌原文:2nd place solution for the 2017 national datascience bowl 翻译参考:知乎用户王小新 Kaggle百万美元大赛优胜者:用CNN识别CT图像检测肺癌概要本文为2017年由Kaggle举办的数据科学...
  • 缺陷检测之高纹理图像检测

    千次阅读 2018-12-11 21:28:49
    * 该例程展示了如何在高纹理图像检测mura缺陷 * dev_close_window () dev_update_off () Path := 'lcd/mura_defects_texture_' read_image (Image, Path + '01') get_image_size (Image, Width, Height) ...
  • YOLT遥感图像检测算法详解

    千次阅读 2019-11-10 23:26:31
    目录1、需求解读2、遥感图像处理和普通图像处理的区别与联系3、YOLT检测算法分析3.1 遥感图像中的视觉挑战3.2 YOLT检测算法简介4、YOLT检测算法实现详解4.1 YOLT检测算法网络架构简介4.2 YOLT检测算法如...
  • 医学图像和自然图像区别

    千次阅读 2018-04-19 17:49:16
    医学图像检测与自然图像检测差别还是比较大的。自然图像的目标检测由于需要检测的类别非常多,现在的目标是更深的网络(提高目标的特征表示能力,resnet),更快的检测速度(最终需要商用,要有较好的实时性,yolo,...
  • 基于Faster RCNN的医学图像检测(肺结节检测)

    万次阅读 多人点赞 2016-10-27 10:04:43
    Faster-R-CNN算法由两大模块组成:1.PRN候选框提取模块 2.Fast R-CNN检测模块。其中,RPN是全卷积神经网络,用于提取候选框;Fast R-CNN基于RPN提取的proposal检测并识别proposal中的目标。 在 学习Faster-RCNN (py-...
  • 项目实例完整代码可以下载Examples:https://pan.baidu.com/s/1dGn9nSP 密码:zo3r有一种水表的塑料...技术指标是:检测速度每分钟不少于200个;检测准确率达于99.9%;支持的齿轮种类包含白色、红色、黑色及不同尺寸...
1 2 3 4 5 ... 20
收藏数 236,921
精华内容 94,768
关键字:

图像检测