Android OpenCV(二十二):边缘检测

Stella981
• 阅读 967

边缘检测

什么是图像的边缘?

图像的边缘是图像最基本的特征之一。所谓边缘(或边沿)是指周围像素灰度有跳跃性变化或“屋顶”变化的那些像素的集合。边缘是图像局部强度变化最明显的地方,它主要存在于目标与目标、目标与背景、区域与区域之间,因此它是图像分割依赖的重要特征。从本质上说,图像边缘是图像局部特性不连续性(灰度突变、颜色突变、纹理结构突变等)的反应,它标志着一个区域的终结和另一个区域的开始。

检测出的边缘并不等同于实际目标的真实边缘。由于图像数据是二维的,而实际物体是三维的,从三维到二维的投影必然会造成信息的丢失,再加上成像过程中的光照不均和噪声等因素的影响,使得有边缘的地方不一定能被检测出来,而检测出的边缘也不一定代表实际边缘。

图像的边缘有方向和幅度两个属性,沿边缘方向像素变化平缓,垂直于边缘方向像素变化剧烈。边缘上的这种变化可以用微分算子检测出来,通常用一阶或两阶导数来检测边缘。一阶导数认为最大值对应边缘位置,而二阶导数则以过零点对应边缘位置。

Android OpenCV(二十二):边缘检测

检测原理

由于图像是离散的信号,我们可以用临近的两个像素差值来表示像素灰度值函数的导数,如下:

Android OpenCV(二十二):边缘检测

一阶求导

这种X轴方向求导方式对应于滤波器为[1, -1],对于Y轴方向则是[1, -1]的转置矩阵。

但是这种求导方式的计算结果最接近于两个像素中间位置的梯度,而两个像素之间是不存在像素的,因此,我们应该选取像素点的前后像素的差值来进行计算,如下:

Android OpenCV(二十二):边缘检测

优化后的计算方式

此时,对应的X轴方向的滤波器为[0.5, 0, -0.5],对应的Y轴方向的滤波器则是其转置矩阵。根据这种方式,我们也可以得到计算45度方向的梯度对应的滤波器。
Android OpenCV(二十二):边缘检测

Android OpenCV(二十二):边缘检测

API

图像卷积

public static void filter2D(Mat src, Mat dst, int ddepth, Mat kernel, Point anchor, double delta, int borderType) 
  • 参数一:src,输入图像。

  • 参数二:dst,输出图像,与输入图像具有相同的尺寸和通道数。

  • 参数三:ddepth,输出图像的数据类型(深度),根据输入图像的数据类型不同拥有不同的取值范围。当赋值为-1时,输出图像的数据类型自动选择。

  • 参数四:kernel,卷积核,CV_32FC1类型的矩阵。

  • 参数五:anchor,内核的基准点(锚点),默认值(-1,-1)代表内核基准点位于kernel的中心位置。基准点即卷积核中与进行处理的像素点重合的点,其位置必须在卷积核的内部。

  • 参数六:delta,偏值,在计算结果中加上偏值。

  • 参数七:borderType,像素外推法选择标志。默认参数为BORDER_DEFAULT,表示不包含边界值倒序填充。

绝对值

图像的边缘有可能是由高像素值变为低像素值,也有可能是由低像素值变成高像素值,通过卷积计算得到的正数值表示需要像素值突然由低变高,得到的负数值表示像素值由高到低,这两种都是图像的边缘,因此为了在图像中同时表示出这两种边缘信息,需要将计算的结果求取绝对值。

public static void convertScaleAbs(Mat src, Mat dst, double alpha, double beta) 
  • 参数一:src,输入图像。

  • 参数二:dst,计算绝对值后输入矩阵。

  • 参数三:缩放因子,默认参数为只求取绝对值不进行缩放。

  • 参数四:在原始数据上添加的偏值,默认参数表示不增加偏值。

该函数可以求取矩阵中所有数据的绝对值。函数前两个参数分别为输入、输出矩阵,两个参数可以是相同的变量。函数第三个和第四个参数为对绝对值的缩放和原始数据上的偏移。函数的计算原理如式:
Android OpenCV(二十二):边缘检测

操作

/** * 边缘检测 * author: yidong * 2020/5/2 */class EdgeDetectionActivity : AppCompatActivity() {    private lateinit var mBinding: ActivityEdgeDetectionBinding    private lateinit var mRgb: Mat    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_edge_detection)        val bgr = Utils.loadResource(this, R.drawable.lena)        mRgb = Mat()        Imgproc.cvtColor(bgr, mRgb, Imgproc.COLOR_BGR2RGB)        showMat(mBinding.ivLena, mRgb)    }    private fun showMat(view: ImageView, source: Mat) {        val bitmap = Bitmap.createBitmap(source.width(), source.height(), Bitmap.Config.ARGB_8888)        bitmap.density = 360        Utils.matToBitmap(source, bitmap)        view.setImageBitmap(bitmap)    }    override fun onCreateOptionsMenu(menu: Menu?): Boolean {        menuInflater.inflate(R.menu.menu_edge_detection, menu)        return true    }    override fun onOptionsItemSelected(item: MenuItem): Boolean {        when (item.itemId) {            R.id.edge_detection_x -> {                edgeDetectionX()            }            R.id.edge_detection_y -> {                edgeDetectionY()            }            R.id.edge_detection_x_y -> {                edgeDetectionXAndY()            }            R.id.edge_detection_xy -> {                edgeDetectionXY()            }            R.id.edge_detection_yx -> {                edgeDetectionYX()            }        }        return true    }    private fun edgeDetectionX() {        title = "X轴方向边缘检测"        // X轴方向边缘检测        val kernelX = Mat(1, 3, CvType.CV_16S)        val arrayX = shortArrayOf(-1, 0, 1)        kernelX.put(0, 0, arrayX)        val resultKernelX = Mat()        Imgproc.filter2D(mRgb, resultKernelX, CvType.CV_16S, kernelX)        Core.convertScaleAbs(resultKernelX, resultKernelX)        showMat(mBinding.ivResult, resultKernelX)        kernelX.release()        resultKernelX.release()    }    private fun edgeDetectionY() {        title = "Y轴方向边缘检测"        // Y轴方向边缘检测        val kernelY = Mat(3, 1, CvType.CV_16S)        val arrayY = shortArrayOf(-1, 0, 1)        kernelY.put(0, 0, arrayY)        val resultKernelY = Mat()        Imgproc.filter2D(mRgb, resultKernelY, CvType.CV_16S, kernelY)        Core.convertScaleAbs(resultKernelY, resultKernelY)        showMat(mBinding.ivResult, resultKernelY)        kernelY.release()        resultKernelY.release()    }    private fun edgeDetectionXAndY() {        title = "X和Y轴方向边缘检测"        // X轴方向边缘检测        val kernelX = Mat(1, 3, CvType.CV_16S)        val arrayX = shortArrayOf(-1, 0, 1)        kernelX.put(0, 0, arrayX)        val resultKernelX = Mat()        Imgproc.filter2D(mRgb, resultKernelX, CvType.CV_16S, kernelX)        Core.convertScaleAbs(resultKernelX, resultKernelX)        // Y轴方向边缘检测        val kernelY = Mat(3, 1, CvType.CV_16S)        val arrayY = shortArrayOf(-1, 0, 1)        kernelY.put(0, 0, arrayY)        val resultKernelY = Mat()        Imgproc.filter2D(mRgb, resultKernelY, CvType.CV_16S, kernelY)        Core.convertScaleAbs(resultKernelY, resultKernelY)        // X,Y轴方向合并        val resultXY = Mat()        Core.add(resultKernelX, resultKernelY, resultXY)        showMat(mBinding.ivResult, resultXY)        kernelX.release()        resultKernelX.release()        kernelY.release()        resultKernelY.release()        resultXY.release()    }    private fun edgeDetectionXY() {        title = "由左上到右下方向边缘检测"        //由左上到右下方向边缘检测        val kernelXY = Mat(2, 2, CvType.CV_16S)        val arrayXY = shortArrayOf(1, 0, 0, -1)        kernelXY.put(0, 0, arrayXY)        val resultKernelXY = Mat()        Imgproc.filter2D(mRgb, resultKernelXY, CvType.CV_16S, kernelXY)        Core.convertScaleAbs(resultKernelXY, resultKernelXY)        showMat(mBinding.ivResult, resultKernelXY)        kernelXY.release()        resultKernelXY.release()    }    private fun edgeDetectionYX() {        title = "由右上到左下方向边缘检测"        //由右上到左下方向边缘检测        val kernelYX = Mat(2, 2, CvType.CV_16S)        val arrayYX = shortArrayOf(0, -1, 1, 0)        kernelYX.put(0, 0, arrayYX)        val resultKernelYX = Mat()        Imgproc.filter2D(mRgb, resultKernelYX, CvType.CV_16S, kernelYX)        Core.convertScaleAbs(resultKernelYX, resultKernelYX)        showMat(mBinding.ivResult, resultKernelYX)        kernelYX.release()        resultKernelYX.release()    }}

效果

Android OpenCV(二十二):边缘检测

X轴方向边缘检测

Android OpenCV(二十二):边缘检测

Y轴方向边缘检测

Android OpenCV(二十二):边缘检测

X轴Y轴方向边缘检测

Android OpenCV(二十二):边缘检测

从左上到右下边缘检测

Android OpenCV(二十二):边缘检测

从右上往左下边缘检测

源码

https://github.com/onlyloveyd/LearningAndroidOpenCV

本文分享自微信公众号 - 微卡智享(VaccaeShare)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Karen110 Karen110
3年前
Python-OpenCV获取图像轮廓的图像处理方法
一、引言在《OpenCV阈值处理函数threshold处理32位彩色图像的案例》介绍了threshold函数,但threshold的图像阈值处理对于某些光照不均的图像,这种全局阈值分割的方法并不能得到好的效果。图像阈值化操作中,我们更关心的是从二值化图像中分离目标区域和背景区域,仅仅通过固定阈值很难达到理想的分割效果。在图片中的灰度是不均匀的,所以通常情
Wesley13 Wesley13
3年前
10、图像的几何变换——平移、镜像、缩放、旋转、仿射变换
1.几何变换的基本概念  图像几何变换又称为图像空间变换,它将一副图像中的坐标位置映射到另一幅图像中的新坐标位置。我们学习几何变换就是确定这种空间映射关系,以及映射过程中的变化参数。图像的几何变换改变了像素的空间位置,建立一种原图像像素与变换后图像像素之间的映射关系,通过这种映射关系能够实现下面两种计算:1.原图像任意像素
Stella981 Stella981
3年前
Python实现图像边缘检测算法
实现效果!(http://p1f1jwe7c.bkt.clouddn.com/18612/5650040.jpg)!(http://p1f1jwe7c.bkt.clouddn.com/18612/48501449.jpg)代码!/usr/bin/envpython3coding
可莉 可莉
3年前
14、OpenCV实现图像的空间滤波——图像锐化及边缘检测
1、图像锐化理论基础1、锐化的概念   图像锐化的目的是使模糊的图像变得清晰起来,主要用于增强图像的灰度跳变部分,这一点与图像平滑对灰度跳变的抑制正好相反。而且从算子可以看出来,平滑是基于对图像领域的加权求和或者说积分运算的,而锐化则是通过其逆运算导数(梯度)或者说有限差分来实现的。2、图像的一阶微分和二阶
Stella981 Stella981
3年前
C++ OpenCV特征提取之积分图计算
前言什么是积分图像积分图像的定义:取图像左上侧的全部像素计算累加和,并用这个累加和替换图像中的每一个像素,使用这种方式得到的图像称为积分图像。为什么要用积分图像直方图的计算方法为遍历图像的全部像素并累计每个强度值在图像中出现的次数。有时仅需要计算图像中某个特定区域的直方图,而
Stella981 Stella981
3年前
Dicom关键概念
数据格式    DICOM将信息分组到datasets中,这意味着胸部X射线图像的文件实际上包含文件中的患者ID,因此图像永远不会被错误地与该信息分开。类似的,JPEG等图像格式也可以使用嵌入式tags中。    DICOM数据对象由许多属性组成,包括名字,ID等,还有一个包含图像像素数据的特殊属性。单个DICOM对象只能包含一个包含像素数
Stella981 Stella981
3年前
Python+OpenCV图像处理(十三)—— Canny边缘检测
简介:1.Canny边缘检测(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fbaike.baidu.com%2Fitem%2F%25E8%25BE%25B9%25E7%25BC%2598%25E6%25A3%2580%25E6%25B5%258B)算子是JohnF.Canny于
Stella981 Stella981
3年前
Python+OpenCV图像处理(九)—— 模板匹配
百度百科:模板匹配是一种最原始、最基本的模式识别方法,研究某一特定对象物的图案位于图像的什么地方,进而识别对象物,这就是一个匹配问题。它是图像处理中最基本、最常用的匹配方法。模板匹配具有自身的局限性,主要表现在它只能进行平行移动,若原图像中的匹配目标发生旋转或大小变化,该算法无效。简单来说,模板匹配就是在整个图像区域发现与给定子图像匹配的小块区域。工
Stella981 Stella981
3年前
Python OpenCV实例:图像直方图均衡化(数学公式简单实现)
coding:utf8'''直方图均衡化作用:通常用来增加图像局部对比度,尤其在图像的有用数据的对比度相当接近时,通过直方图均衡化,图像的亮度可以更好地在直方图上分布基本思想:把原始图像的直方图变换为均匀分布的形式,增加了像素灰度值的动态范围,从而增强图像的整