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

图像中的信息包括边缘、直线、椭圆、色块或轮廓、角点等形式,这些信息在计算机视觉和图像处理语境中通常被称为特征。下面就来了解一些结合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相关应用开发中的基础。

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值