opencv4android 常用函数API(一)

openCV4android常用变换(一)

简介

OpenCV的全称是:Open Source Computer Vision Library。OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows和Mac OS操作系统上。它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。–来自百度。

OpenCV4android是opencv在android上的实现,进行一定配置之后就一个在android中通过Java调用opencv中的功能,可以实现很多不同的效果和进行图片分析,处理等。

作者水平有限,并没有专门学过opencv,也不了解具体的算法实现,这篇文章只是简单的介绍一下opencv4android在android中的使用和相关api,如有误,请斧正。

这篇都有哪些内容:主要就是Imgproc中API的使用,核心(core)模块没有多介绍,一些基础的关于opencv的知识可以在http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/tutorials.html学习,作者本人也是在这个网站学的,而且所有文章中的内容都是根据这个网站所提供的信息,在opencv4android中的实现,欢迎交流。

  • 图像平滑处理
  • 腐蚀和膨胀
  • 更多形态学变化
  • 图像金字塔
  • 简单的阈值操作
  • 实现自己的线性滤波器
  • 给图像添加边界
  • Sobel导数

先介绍这么多,以后还会有更多的内容的。这阅读下面的内容之前,请确定自己的Studio已经配置好了opencv4android,不会的可以去看我上一篇文章。先从简单的来。

图像的平滑处理

平滑 也称 模糊, 是一项简单且使用频率很高的图像处理方法。
在opencv中有四种常用处理方式:

  • blur 归一化块滤波器
  • GaussianBlur 高斯滤波器
  • medianBlur 中值滤波器
  • bilateralFilter 双边滤波器

这里先为大家普及两个概念,内核或者说核(Size),锚点(Point)

在我看来,核就是一个物体,用这个物体去扫描图片,使图片产生一系列的变化,锚点就是这个核中额某一个点,在一些特殊需要的情况下可能不在中心位置(我猜的)

具体的每一种滤波器是如何处理图像的我也不会,复制粘贴也不是我的风格,就这样了,直接上代码,看看效果。

private Mat rgbMat,dstMat;
private void changeBitmap(){
    if(rgbMat==null|dstMat==null){
        rgbMat=new Mat();
        Utils.bitmapToMat(mBitmap,rgbMat);
        //双边转换
//            Imgproc.cvtColor(rgbMat,rgbMat,Imgproc.COLOR_BGR2RGB);
        dstMat=new Mat(rgbMat.size(),rgbMat.type());
    }
    Log.d("TAG","rgb:"+rgbMat.type()+"/"+rgbMat.size()+"/"+dstMat.size()+"/"+dstMat.type());
    switch (operation){
        case 0:  Imgproc.blur(rgbMat,dstMat,new Size(2*n+1,2*n+1),new Point(-1,-1));    break;
        case 1:  Imgproc.GaussianBlur(rgbMat,dstMat,new Size(2*n+1,2*n+1),0,0);         break;
        case 2:  Imgproc.medianBlur(rgbMat,dstMat,2*n+1);                               break;
        case 3:  Imgproc.bilateralFilter(rgbMat,dstMat,10,75,75);                       break;
    }
    Utils.matToBitmap(dstMat,dstBitmap);
    dstImage.setImageBitmap(dstBitmap);
}

说一下流程:

  1. 创建两个矩阵(容器)对象,分别问原图像(rgbMat),输出图像(dstMat);
  2. 将其中rgbMat赋值,使用* Utils.bitmapToMat(mBitmap,rgbMat)*将一张图片变成矩阵对象,赋值到rgbMat中;
  3. dstMat采用rgbMat的尺寸和类型创建。
  4. 四种不同的平滑方式:

    • blur:参数:原图像,输入图像,内核(内核中数值必须为正奇数),锚点(-1,-1代表内核中心)。
    • GaussianBlur:参数:原图像,输入图像,内核(内核中数值必须为正奇数),x方向标准方差,y方向标准方差(0代表使用内核大小计算得到)。
    • medianBlur:参数:原图像,输入图像,内核大小(必须为正奇数)。
    • bilateralFilter:参数:原图像,输入图像,像素的邻域直径,颜色空间的标准方差,坐标空间的标准方差(像素单位))。
      调用Imgproc相对应的平滑方法会执行相应的操作,这里需要重点说一下bilateralFilter这个滤波器,在之前我练习的时候以为都差不多就使用高斯滤波器,跑一下可以了就过了,直到两个小时前在使用bilateralFilter的时候才发现,一直在报错,而且我也不理解是什么意思。

    CvException [org.opencv.core.CvException: cv::Exception: /Volumes/Linux/builds/master_pack-android/opencv/modules/imgproc/src/smooth.cpp:3145: error: (-215) (src.type() == CV_8UC1 || src.type() == CV_8UC3) && src.data != dst.data in function void cv::bilateralFilter_8u(const cv::Mat&, cv::Mat&, int, double, double, int)
    ]

可以看一下错误信息,对于一个从没有学过C++,opencv的人来说这是什么?完全不理解好不好,之后就是上网查资料,看见了这个。

 CV_Assert( (src.type() == CV_8UC1 || src.type() == CV_8UC3) &&  
          src.type() == dst.type() && src.size() == dst.size() &&  
          src.data != dst.data );

这个是赵春江先生/女士在CSDN上一篇博客的内容,是不是特别相似,不过到这我也不明白,所以就百度了下 CV_Assert 这个方法,说是C++中的assert()方法类似,括号内为false会抛一个异常,到这我就知道问题出在哪里了,之后就是解决问题吧,又是坑啊,在opencv4android中并没有明确的CV_8UC1或者CV_8UC3常量,我就是想把type设置成指定类型的我都不知道类型是多少,不过,经过我不断的尝试(就是一个个转换看哪个不报错而已),终于也可以了,可以看见:

Imgproc.cvtColor(rgbMat,rgbMat,Imgproc.COLOR_BGR2RGB);

使用COLOR_BGR2RGB这个转换方式可以将图片转换成CV_8UC3,可能会有人问你怎么知道到RGB就是CV_8UC3,不要嘲笑我,我是真不知道8位无符号3通道就是RGB,不过我有一个灵活的大脑和勤劳的双手,少年看这里:

The input Mat object has to be of the types 'CV_8UC1' (gray-scale), 'CV_8UC3' (RGB) or 'CV_8UC4' (RGBA).

这个是Mat类中倒数第二个方法的注释,可以看到’CV_8UC3’ 就是RGB,而’CV_8UC1’就是灰度图,用android中的数值应该怎么表示那?告诉你:

  • CV_8UC1—–Gray(灰度图)—0
  • CV_8UC3—–RGB(彩色图)—16
  • CV_8UC4—–BGR(彩色图)—24

不要问我怎么知道的,我会告诉你每次转换之后我都打印了一下type了吗?,现在这个问题算是解决了,只要将BGR图片转换成RGB的就可以,至于为什么,我不知道。。。。效果图

这个是正常BGR模式下的效果图,可以看出来在前三种模式下,相同内核大小,高斯滤波器的平滑效果明显要弱于其他两种,具体的不多说了,看下RGB模式下的效果:

可以看出来在初始情况下,图片就已经转换为RGB模式了,颜色上有明显的不同,之后是四种平滑方式的对比实现,可以根据个人喜好去选择不同的平滑方式。

腐蚀和膨胀

腐蚀和膨胀是最基本的形态学操作,之后会学到的其他形态学变化都是基于这两种所实现的,形态学操作,简单来讲,形态学操作就是基于形状的一系列,图像处理操作,通过将结构元素作用于输入图像来产生输出图像。

  • 腐蚀:erode。参数:原图像,输出图像,内核(通过Imgproc.getStructuringElement)指定内核形状和内核大小
  • 膨胀:dilation.参数:原图像,输出图像,内核(通过Imgproc.getStructuringElement)指定内核形状和内核大小

参考代码:

 private Mat rgbMat,dstMat;
//腐蚀
private void changeBitmap(){
    if(rgbMat==null|dstMat==null){
        rgbMat=new Mat();
        dstMat=new Mat();
        Utils.bitmapToMat(mBitmap,rgbMat);
    }
    //设置内核形状和内核大小
    Mat kernel=Imgproc.getStructuringElement(shape,new Size(2*n+1,2*n+1),new Point(n,n));
    Imgproc.erode(rgbMat,dstMat,kernel);
    Utils.matToBitmap(dstMat,dstBitmap);
    dstImage.setImageBitmap(dstBitmap);
}
//膨胀
private void grayBitmap(){
    if(rgbMat==null|dstMat==null){
        rgbMat=new Mat();
        dstMat=new Mat();
        Utils.bitmapToMat(mBitmap,rgbMat);
    }
    //设置内核形状和内核大小
    Mat kernel=Imgproc.getStructuringElement(shape,new Size(2*n+1,2*n+1),new Point(n,n));
    Imgproc.dilate(rgbMat,dstMat,kernel);
    Utils.matToBitmap(dstMat,dstBitmap);
    dstImage.setImageBitmap(dstBitmap);
}

可以看到大部分的代码都是相同的就是函数名不一样,也没有什么好解释的,看效果:


关于内核的形状有三种,分别是矩形,交错形和椭圆形,可以通过腐蚀时看出来三种不同的内核对腐蚀效果的影响。

更多形态学变化

在掌握了基础的两种变化之后,可以学习更复杂一点的形态学变化方式:

  • 开运算 (Opening):腐蚀后膨胀
  • 闭运算 (Closing):膨胀后腐蚀
  • 形态梯度 (Morphological Gradient):膨胀和腐蚀之差
  • 顶帽 (Top Hat):原图像和开运算之差
  • 黑帽(Black Hat):原图像和闭运算之差

所使用的函数统一为morphologyEx(Mat src, Mat dst, int op, Mat kernel),其中src为原图像,dst为输出图像,op为操作类型,kernel为内核。
忘了介绍了,在opencv4android中关于内核形状和形态学方法是有常量表示的:

形态学方法

  • MORPH_ERODE = 0,
  • MORPH_DILATE = 1,
  • MORPH_OPEN = 2,
  • MORPH_CLOSE = 3,
  • MORPH_GRADIENT = 4,
  • MORPH_TOPHAT = 5,
  • MORPH_BLACKHAT = 6,

内核形状

  • MORPH_RECT = 0,
  • MORPH_CROSS = 1,
  • MORPH_ELLIPSE = 2,

至于这些内容是从哪找的,可以去参考Imgproc中的常量。上代码~~~~·

private void changeBitmap() {
    if(rgbMat==null|dstMat==null){
    rgbMat=new Mat();
    dstMat=new Mat();
    Utils.bitmapToMat(mBitmap,rgbMat);
    }
    Mat kernel=Imgproc.getStructuringElement(shape,new Size(2*n+1,2*n+1),new Point(n,n));
    Imgproc.morphologyEx(rgbMat,dstMat,operation,kernel);
    Utils.matToBitmap(dstMat,dstBitmap);
    Toast.makeText(MainActivity.this,"OK",Toast.LENGTH_SHORT).show();
    dstImage.setImageBitmap(dstBitmap);
}

效果图如下:

个人比较喜欢黑猴子,不知道大家喜欢哪一个?值得注意就是当采用black加椭圆内核的时候,运行起来有比较严重的延迟,界面被阻塞了,实际应用中应该采用异步的方式去使用,更多的就需要大家去实践了。如果有不理解的可以留言,这篇就讲这么多,剩下的内容我需要去整理一下,先到这里。

你可能感兴趣的:(Android)