前几篇我们大致了解了如何运用OpenCV在Android上进行图片但简单处理,并运用这些知识做了一个文字提取的应用。
Android OpenCV应用篇一:环境搭建,高斯差分
Android OpenCV应用篇二:图片处理
Android OpenCV应用篇三:提取图片中的文字
下面我们继续进行了解OpenCV在Android如何进去一些其他方面的应用:图片特征检测
前言
开始之前,我们需要知道一些在计算机视觉和图像处理 的语境中所谓的图像特征包括哪些:
- 边缘
- 直线
- 圆
- 椭圆
- 色块或轮廓
- 用户定义形状
- 角点
- …
OpenCV中给我们提供来丰富的图像特征信息提取的算法,接下来我们来看一下在Android中如何运用。
边缘和角点检测
边缘检测和角点检测是最为基本而且最为常用的两中特征检测算法,经常用于找出图像中目标图像的边界、角点,或者分析一幅图像的旋转情况,目标在图像序列(视频)中的移动情况等。
边缘检测之高斯差分技术
高斯差分技术我们在Android OpenCV应用篇一:环境搭建,高斯差分篇中有做介绍,这里就不再重复。
边缘检测之Canny边缘检测器
Canny边缘检测器在计算机视觉中被广泛采用,并被认为是边缘检测最优的算法
算法步骤如下:
- 平滑图像
- 计算图像的梯度
- 非最大值抑制
- 用滞后阈值化选择边缘
我们可以直接使用OpenCV提供的方法进行Canny边缘检测:
Canny(Mat image,
Mat edges,
double threshold1, // 低阈值
double threshold2 // 高阈值
)
例:
/**
* Canny边缘检测
*/
fun canny() {
imageBitmap?.apply {
val originalMat = Mat(height, width, CvType.CV_8UC4)
val grayMat = Mat()
val cannyMat = Mat()
Utils.bitmapToMat(imageBitmap, originalMat)
Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY)
Imgproc.Canny(grayMat, cannyMat, 10.0, 100.0)
val resultBitmap = Bitmap.createBitmap(cannyMat.cols(), cannyMat.rows(), Bitmap.Config.ARGB_8888)
Utils.matToBitmap(cannyMat, resultBitmap)
imageResult.setImageBitmap(resultBitmap)
}
}
效果图:
边缘检测之Sobel算子
Sobel算子边缘检测与Canny类似,去计算像素的灰度梯度,只不过是换用另一种方式:详情查阅 (百度百科:Sobel算子)
Sobel算子步骤:
- 将图像转换成灰度
- 计算水平方向灰度梯度绝对值
- 计算垂直方向灰度梯度绝对值
- 计算最终梯度
/**
* Sobel滤波
*/
fun sobel() {
imageBitmap?.apply {
val originalMat = Mat(height, width, CvType.CV_8UC4)
val grayMat = Mat()
Utils.bitmapToMat(imageBitmap, originalMat)
Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGRA2GRAY)
val sobelMat = Mat()
val gradX = Mat()
val absGradX = Mat()
val gradY = Mat()
val absGradY = Mat()
// 计算水平方向梯度
Imgproc.Sobel(grayMat, gradX, CvType.CV_16S, 1, 0, 3, 1.0, 0.0)
// 计算竖直方向梯度
Imgproc.Sobel(grayMat, gradY, CvType.CV_16S, 0, 1, 3, 1.0, 0.0)
// 计算两个方向上的绝对梯度
Core.convertScaleAbs(gradX, absGradX)
Core.convertScaleAbs(gradY, absGradY)
// 计算结果梯度
Core.addWeighted(absGradX, 0.5, absGradY, 0.5, 1.0, sobelMat)
val resultBitmap = Bitmap.createBitmap(sobelMat.cols(), sobelMat.rows(), Bitmap.Config.ARGB_8888)
Utils.matToBitmap(sobelMat, resultBitmap)
imageResult.setImageBitmap(resultBitmap)
}
}
效果图:
角点检测
角点 一般是指两条边缘的交点或者在局部领域中有多个显著边缘方向的点,通常用来当作图像中的兴趣点。
下面我们来用Harris角点检测来检测图像的角点。
/**
* harris 角点检测
*/
fun harris() {
imageBitmap?.apply {
val originalMat = Mat(height, width, CvType.CV_8UC4)
val grayMat = Mat()
val cornersMat = Mat()
Utils.bitmapToMat(this, originalMat)
Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY)
val tempDstMat = Mat()
// 找出角点
Imgproc.cornerHarris(grayMat, tempDstMat, 2, 3, 0.04)
// 归一化harris角点输出
val tempDstNorMat = Mat()
Core.normalize(tempDstMat, tempDstNorMat, 0.0, 255.0, Core.NORM_MINMAX)
Core.convertScaleAbs(tempDstNorMat, cornersMat)
// 在新图像上绘制角点
for (i in 0 until tempDstNorMat.cols()) {
for (j in 0 until tempDstNorMat.rows()) {
val value: DoubleArray? = tempDstNorMat.get(i, j)
value?.apply {
if (value[0] > 150) {
cornersMat.get(i, j)
Imgproc.circle(cornersMat, Point(i.toDouble(), j.toDouble()), 5, Scalar(255.0),2)
}
}
}
}
val resultBitmap = Bitmap.createBitmap(cornersMat.cols(), cornersMat.rows(), Bitmap.Config.ARGB_8888)
Utils.matToBitmap(cornersMat, resultBitmap)
imageResult.setImageBitmap(resultBitmap)
}
}
效果:
霍夫变化
通常,我们会使用霍夫变换来检测形状(百度百科:霍夫变换)
霍夫直线
霍夫直线检测是霍夫变换中最为简单的一种用途
/**
* 霍夫直线检测
*/
fun houghLines() {
imageBitmap?.apply {
val originalMat = Mat(height, width, CvType.CV_8UC4)
val grayMat = Mat()
Utils.bitmapToMat(this, originalMat)
// 转换为灰度图像
Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGRA2GRAY)
val cannyMat = Mat()
// 进行边缘检测
Imgproc.Canny(grayMat, cannyMat, 400.0, 500.0, 5, false)
val linesMat = Mat()
// 计算霍夫直线
Imgproc.HoughLinesP(cannyMat, linesMat, 1.0, Math.PI / 180, 50, 0.0, 0.0)
val houghLinesMat = Mat()
// 这里我们将检测到到直线绘制到图像上,以方便展示
houghLinesMat.create(cannyMat.size(), CvType.CV_8UC1)
for (i in 0 until linesMat.rows()) {
val points = linesMat.get(i, 0)
val x1 = points[0]
val y1 = points[1]
val x2 = points[2]
val y2 = points[3]
val ponit1 = Point(x1, y1)
val point2 = Point(x2, y2)
Imgproc.line(houghLinesMat, ponit1, point2, Scalar(255.0, 0.0, 0.0), 1, Imgproc.LINE_4, 0)
}
val resultBitmap = Bitmap.createBitmap(houghLinesMat.cols(), houghLinesMat.rows(), Bitmap.Config.ARGB_8888)
Utils.matToBitmap(houghLinesMat, resultBitmap)
imageResult.setImageBitmap(resultBitmap)
}
}
上面到操作如下:
- 将图像转换成灰度图像
- 进行边缘检测,得到边缘轮廓图像
- 进行霍夫直线检测
- 将霍夫直线绘制到图像上并展示出来
效果如下:
霍夫圆到操作与霍夫直线类似,我们用OpenCV提供到HoughCircles方法可以进行霍夫圆的检测。
轮廓检测
利用轮廓检测,我们可以得到图像中的图元(连通部分),通常以图片中的边缘来计算。
/**
* 轮廓检测
*/
fun contouurs() {
imageBitmap?.apply {
val originalMat = Mat(height, width, CvType.CV_8UC4)
Utils.bitmapToMat(imageBitmap, originalMat)
val grayMat = Mat()
val cannyEdges = Mat()
val hierarchy = Mat()
val contourList = ArrayList<MatOfPoint>()
// 转换为灰度图像
Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY)
// 找出边缘
Imgproc.Canny(grayMat, cannyEdges, 10.0, 100.0)
// 找出轮廓
Imgproc.findContours(cannyEdges, contourList, hierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE)
// 在新图上绘制轮廓
val contours = Mat()
contours.create(cannyEdges.rows(), cannyEdges.cols(), CvType.CV_8UC3)
val r = Random
for (i in 0 until contourList.size) {
Imgproc.drawContours(
contours,
contourList,
i,
Scalar(r.nextDouble(255.0), r.nextDouble(255.0), r.nextDouble(255.0)),
-1
)
}
showResult(contours, imageResult)
}
}
上述代码我们进行的操作主要有:
- 将图像转换成灰度图像
- 边缘检测
- 轮廓检测
- 在新图上用色块填充轮廓
效果如下: