OpenCV4Android学习之图像基本特征检测

来源:互联网 发布:c语言流程图怎么画 编辑:程序博客网 时间:2024/06/10 19:07

图像中的信息包括边缘、直线、椭圆、色块或轮廓、角点等形式,这些信息在计算机视觉和图像处理语境中通常被称为特征。下面就来了解一些结合OpenCV在Android平台上的常规的特征检测算法,这里使用AndroidStudio开发平台,当然Eclipse也一样。如果对在这两个IDE上部署OpenCV不熟悉的话,可以参考我之前写的这篇文章:Eclipse与AndroidStudio关于OpenCV4Android库的部署


1.边缘和焦点检测
边缘检测和角点检测时两种最基本也是非常有用的特征检测算法,这些算法包括高斯差分、Canny边缘检测器、Sobel算子和Harris角点。比如我们想在一幅图中找到不同目标的边界或者角点,用于分析目标在图像中的旋转或移动等情况,这时候就需要知道边缘和角点的信息了。

  • 高斯差分
    这里我们先了解下什么是边缘和高斯模糊方法,因为下面要用到边缘这个性质和对图像进行高斯模糊来计算边缘点,即边缘点。

    • 简单的说,边缘就是图像中像素亮度变化明显的点。
    • 高斯(Gaussian)模糊是最常用的模糊方法,是将指定像素变换为其与周边像素加权平均后的值,权重就是高斯分布函数计算出来的值。OpenCV提供了GaussianBlur()的内置函数,我们可以在应用中使用它执行高斯模糊。这里是一篇关于高斯模糊算法的介绍:高斯模糊算法

    形如:

    Mat src;
    Imgproc.GaussianBlur(src,src,new Size(6,6),0);

    让我们回到高斯差分技术,其算法分成三步:

    • 将图像转换为弧度图像
    • 用两个不同的模糊半径对灰色图像执行高斯模糊
    • 对上面两幅图像相减,得到一副只包含边缘点的结果图像

    在应用中,创建一个用于给定图像计算边缘的函数:

    /**     * 高斯差分     */    private void DifferenceOfGaussian() {        Mat grayMat = new Mat();        Mat blur1 = new Mat();        Mat blur2 = new Mat();        //将图像转换为灰度        Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);        //以两个不同的模糊半径对图像做模糊处理,前两个参数分别是输入和输出图像,第三个参数指定应用滤波器时所用核的尺寸,最后一个参数指定高斯函数中的标准差数值        Imgproc.GaussianBlur(grayMat, blur1, new Size(15, 15), 5);        Imgproc.GaussianBlur(grayMat, blur2, new Size(21, 21), 5);        //将两幅模糊后的图像相减        Mat DoG = new Mat();        Core.absdiff(blur1, blur2, DoG);        //反转二值阈值化        Core.multiply(DoG, new Scalar(100), DoG);        Imgproc.threshold(DoG, DoG, 50, 255, Imgproc.THRESH_BINARY_INV);        //将Mat转换为位图        Utils.matToBitmap(DoG, currentBitmap);        loadImageToImageView();    }    /**     * 设置图像     */    private void loadImageToImageView() {        ImageView imgView = (ImageView) findViewById(R.id.image_view);        imgView.setImageBitmap(currentBitmap);    }

处理结果图:

高斯差分

  • Canny边缘检测器
    Canny边缘检测器算法使用了多向灰度梯度和滞后阈值化等复杂技巧,它是边缘检测的最优方法。
    算法分为四个步骤:
    • 平滑图:使用合适的模糊半径执行高斯模糊来减少图像内的噪声
    • 计算图像梯度:计算图像梯度,并将其分为垂直、水平和斜对角
    • 非最大值:由图像梯度计算得到梯度方向,检查某一像素在梯度正方向和负方向的局部最大值,然后抑制该像素
    • 用滞后阈值化选择边缘:检查某一条边缘是否明显到足以作为最终输出,最后取出所有不够明显的边缘

下面是OpenCV对该算法在Android上的实现代码:

    /**     * Canny边缘检测器     */    private void Canny() {        Mat grayMat = new Mat();        Mat cannyEdges = new Mat();        //将图像转换为灰度        Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_RGB2GRAY);        Imgproc.Canny(grayMat, cannyEdges, 10, 100);        //将Mat转换回位图        Utils.matToBitmap(cannyEdges, currentBitmap);        loadImageToImageView();    }    /**     * 设置图像     */    private void loadImageToImageView() {        ImageView imgView = (ImageView) findViewById(R.id.image_view);        imgView.setImageBitmap(currentBitmap);    }

处理结果图:

Canny边缘检测器

  • Sobel算子
    Sobel算子和Canny边缘检测一样,计算像素灰度梯度。
    算法分为四个步骤:
    • 将图像转换为灰度图像
    • 计算水平方向灰度梯度绝对值
    • 计算垂直方向灰度梯度绝对值
    • 计算最终梯度

下面是OpenCV对该算法在Android上的实现代码:

    /**     * Sobel算子     */    private void Sobel() {        Mat grayMat = new Mat();        //用来保存结果的Mat        Mat sobel = new Mat();         //分别用于保存梯度和绝对梯度的Mat        Mat grad_x = new Mat();        Mat abs_grad_x = new Mat();        Mat grad_y = new Mat();        Mat abs_grad_y = new Mat();        //将图像转换为灰度        Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);        //计算水平方向梯度        Imgproc.Sobel(grayMat, grad_x, CvType.CV_16S, 1, 0, 3, 1, 0);        //计算垂直方向梯度        Imgproc.Sobel(grayMat, grad_y, CvType.CV_16S, 0, 1, 3, 1, 0);        //计算两个方向上梯度绝对值        Core.convertScaleAbs(grad_x, abs_grad_x);        Core.convertScaleAbs(grad_y, abs_grad_y);        //计算结果梯度        Core.addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 1, sobel);        //将Mat转换为位图        Utils.matToBitmap(sobel, currentBitmap);        loadImageToImageView();    }     /**     * 设置图像     */    private void loadImageToImageView() {        ImageView imgView = (ImageView) findViewById(R.id.image_view);        imgView.setImageBitmap(currentBitmap);    }

处理结果图:

Sobel算子

  • Harris角点检测
    Harris焦点检测器是一种在角点检测中最常用的技术,在图像上使用滑动窗口计算亮度的变化。
    下面是OpenCV对该算法在Android上的实现代码:
    /**     * Harris角点     */    private void HarrisCorner() {        Mat grayMat = new Mat();        Mat corners = new Mat();        //将图像转换成灰度        Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);        Mat tempDst = new Mat();        //找出角点        Imgproc.cornerHarris(grayMat, tempDst, 2, 3, 0.04);        //在新的图像上绘制角点        Mat tempDstNorm = new Mat();        Core.normalize(tempDst, tempDstNorm, 0, 255, Core.NORM_MINMAX);        Core.convertScaleAbs(tempDstNorm, corners);        //将Mat转换为位图        Random r = new Random();        for (int i = 0; i < tempDstNorm.cols(); i++) {            for (int j = 0; j < tempDstNorm.rows(); j++) {                double[] value = tempDstNorm.get(j, i);                if (value[0] > 150)                    Core.circle(corners, new Point(i, j), 5, new Scalar(r.nextInt(255)), 2);            }        }        //将Mat转换为位图        Utils.matToBitmap(corners, currentBitmap);        loadImageToImageView();    }    /**     * 设置图像     */    private void loadImageToImageView() {        ImageView imgView = (ImageView) findViewById(R.id.image_view);        imgView.setImageBitmap(currentBitmap);    }

处理结果图:

Harris角点检测


2.霍夫变换
对于图像分析来说,除了边缘和角点外,还需要检测如直线、圆、椭圆等其他相关形状。例如我们想在一张桌子的图像中检测签字笔等,对于这种情况,一般采用霍夫变换这种技术,它被广泛采用的利用数学等式的参数形式在图像中检测形状的技术。通常我们考虑二维形状的霍夫变换,比如直线和圆,它相对于球体等复杂形状而言更为简单。

  • 霍夫直线
    我们对于霍夫变化一般不会直接对图像执行算法,因为图像中任何一条明显的直线都一定是边缘,反之则不然。OpenCV提供了两种实现霍夫直线的方式:标准霍夫直线和概率霍夫直线。它们的主要区别在于概率霍夫直线选取经过随机采样的边缘点子集,而不是所有的边缘点。因为这种方式处理的点更少,所以这使得算法的执行速度更快,而不会降低算法的效果。
    下面是OpenCV对该算法在Android上的实现代码:

    /** * 霍夫直线 */private void HoughLines() {    Mat grayMat = new Mat();    Mat cannyEdges = new Mat();    Mat lines = new Mat();    //将图像转换成灰度    Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);    //这里使用Canny边缘检测技术计算图像中的边缘,使用其他的也可以    Imgproc.Canny(grayMat, cannyEdges, 10, 100);    Imgproc.HoughLinesP(cannyEdges, lines, 1, Math.PI / 180, 50, 20, 20);    Mat houghLines = new Mat();    houghLines.create(cannyEdges.rows(), cannyEdges.cols(), CvType.CV_8UC1);    //在图像上绘制直线    for (int i = 0; i < lines.cols(); i++) {        double[] points = lines.get(0, i);        double x1, y1, x2, y2;        x1 = points[0];        y1 = points[1];        x2 = points[2];        y2 = points[3];        Point pt1 = new Point(x1, y1);        Point pt2 = new Point(x2, y2);        //在一副图像上绘制直线        Core.line(houghLines, pt1, pt2, new Scalar(255, 0, 0), 1);    }    //将Mat转换为位图    Utils.matToBitmap(houghLines, currentBitmap);    loadImageToImageView();}/** * 设置图像 */private void loadImageToImageView() {    ImageView imgView = (ImageView) findViewById(R.id.image_view);    imgView.setImageBitmap(currentBitmap);}

    处理结果图:

    霍夫直线检测

  • 霍夫圆

    与霍夫直线类似,步骤也一样。
    下面是OpenCV对该算法在Android上的实现代码:

    /**     *霍夫圆     */    private void HoughCircles() {        Mat grayMat = new Mat();        Mat cannyEdges = new Mat();        Mat circles = new Mat();        //将图像转换成灰度        Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);       //这里使用Canny边缘检测技术计算图像中的边缘,使用其他的也可以        Imgproc.Canny(grayMat, cannyEdges, 10, 100);        Imgproc.HoughCircles(cannyEdges, circles, Imgproc.CV_HOUGH_GRADIENT, 1, cannyEdges.rows() / 15);        Mat houghCircles = new Mat();        houghCircles.create(cannyEdges.rows(), cannyEdges.cols(), CvType.CV_8UC1);        //在图像上绘制圆形        for (int i = 0; i < circles.cols(); i++) {            double[] parameters = circles.get(0, i);            double x, y;            int r;            x = parameters[0];            y = parameters[1];            r = (int) parameters[2];            Point center = new Point(x, y);            //在一副图像上绘制圆形            Core.circle(houghCircles, center, r, new Scalar(255, 0, 0), 1);        }        //将Mat转换为位图        Utils.matToBitmap(houghCircles, currentBitmap);        loadImageToImageView();    }     /**     * 设置图像     */    private void loadImageToImageView() {        ImageView imgView = (ImageView) findViewById(R.id.image_view);        imgView.setImageBitmap(currentBitmap);    }

处理结果图:

霍夫圆检测


3.轮廓
有时候我们只想关注于感兴趣的目标,这就需要将图像分解成更小的片元,比如我们在一角、五角和一元硬币中分析五角的情况。我们有两种实现途径,一种是使用霍夫圆,另一种就是利用轮廓检测将图像分割为更小的部分,每个部分代表一个特定的硬币。轮廓通常以图像中的边缘来计算,边缘与轮廓的区别在于轮廓是闭合的,边缘可以是任意的。
下面是OpenCV对该算法在Android上的实现代码:

    /**     * 轮廓检测     */    private void Contours() {        Mat grayMat = new Mat();        Mat cannyEdges = new Mat();        Mat hierarchy = new Mat();        List<MatOfPoint> contourList = new ArrayList<MatOfPoint>(); //A list to store all the contours        //保存轮廓列表        Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);         //这里使用Canny边缘检测技术计算图像中的边缘,使用其他的也可以        Imgproc.Canny(originalMat, cannyEdges, 10, 100);        //找出轮廓        Imgproc.findContours(cannyEdges, contourList, hierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);        //在新的图像上绘制轮廓        Mat contours = new Mat();        contours.create(cannyEdges.rows(), cannyEdges.cols(), CvType.CV_8UC3);        Random r = new Random();        for (int i = 0; i < contourList.size(); i++) {            Imgproc.drawContours(contours, contourList, i, new Scalar(r.nextInt(255), r.nextInt(255), r.nextInt(255)), -1);        }        //将Mat转换为位图        Utils.matToBitmap(contours, currentBitmap);        loadImageToImageView();    }    /**     * 设置图像     */    private void loadImageToImageView() {        ImageView imgView = (ImageView) findViewById(R.id.image_view);        imgView.setImageBitmap(currentBitmap);    }

处理结果图:

轮廓检测


以上就是图像的基本特征在Android设备上实现的不同算法,也是我们在Android相关应用开发中的基础。

1 0
原创粉丝点击