这是我参与更文挑战的第 24 天,活动详情查看: 更文挑战
Shi-Tomasi角点检测
在Harris角点检测中,Harris角点评价系数R表示为:
λ1 和 λ2 是矩阵M的特征值, k是一个经验常数,在范围 (0.04, 0.06) 之间。由于 Harris 角点检测算法的稳定性和 k 值有关,而 k 是个经验值,不好设定最佳值,且角点的稳定性与矩阵 M 的较小特征值有关,于是直接用较小的那个特征值作为评价系数。所以在Shi-Tomasi算法中,评价系数R表示为:
如果某个像素点R值大于阈值,则像素点是为角点。把它绘制到 λ1 ~ λ2 空间中,就会得到下图:
API
public static void goodFeaturesToTrack(Mat image, MatOfPoint corners, int maxCorners, double qualityLevel, double minDistance, Mat mask, int blockSize, boolean useHarrisDetector, double k)
- 参数一:image,输入源图像。必须是单通道8U或者32F类型。
- 参数二:corners,检测到的角点输出量。
- 参数三:maxCorners,返回角点数目最大值。如果实际检测到的角点大于返回数目最大值,则返回最强的maxCorners个角点。若此参数设置为0,则无此限制。
- 参数四:qualityLevel,表示图像角点的最低可接受水平。比如最强角点评价系数为1500,并且此参数为0.01,那么所有评价系数低于15的将都会被排除在外,不是角点,即15就是检测角点的阈值。
- 参数五:minDistance,角点之间的最小欧几里得距离。
- 参数六:mask,掩码矩阵,表示检测角点的区域。如果参数不为空,则必须是和源图像大小相同的单通道8U图像。
- 参数七:blockSize,计算梯度协方差矩阵的尺寸。
- 参数八:useHarrisDetector,是否使用Harris角点检测。默认为false,使用Shi-Tomasi算法。
- 参数九:k,Harris角点检测过程中的权重系数。
操作
/**
* Shi-Tomas角点检测
* author: yidong
* 2021/1/7
*/
class ShiTomasiActivity : AppCompatActivity() {
private val gray by lazy {
getBgrFromResId(R.drawable.lena).toGray()
}
private val rgb by lazy {
getBgrFromResId(R.drawable.lena).toRgb()
}
private val mBinding: ActivityShiTomasBinding by lazy {
ActivityShiTomasBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(mBinding.root)
mBinding.ivLena.showMat(gray)
wrapCoroutine({ showLoading() }, { doShiTomas() }, { hideLoading() })
}
private fun doShiTomas() {
val corners = MatOfPoint()
val maxCorners = 100;
val qualityLevel = 0.1
val minDistance = 0.04
Imgproc.goodFeaturesToTrack(
gray,
corners,
maxCorners,
qualityLevel,
minDistance,
Mat(),
3,
false
)
val points = corners.toList()
val result = rgb.clone()
GlobalScope.launch(Dispatchers.Main) {
for (point in points) {
Imgproc.circle(result, point, 10, Scalar(0.0, 255.0, 0.0), 2, Imgproc.LINE_8)
}
mBinding.ivResult.showMat(result)
}
}
private fun showLoading() {
mBinding.isLoading = true
}
private fun hideLoading() {
mBinding.isLoading = false
}
override fun onDestroy() {
super.onDestroy()
gray.release()
rgb.release()
}
}