Android OpenCV(三十六):轮廓面积与周长

轮廓面积与周长

轮廓面积和轮廓周长都是轮廓的重要统计特征。轮廓面积是指每个轮廓中所有像素点围成区域的面积,单位为像素。轮廓周长是指每个轮廓中所有像素点围成区域的周长,单位同样为像素。通过分析轮廓面积和轮廓周长,我们可以区分物体的大小,识别物体的不同,同时还能分析出一些其他内容,例如,正方形区域的周长和面积是有固定关系的,圆形区域的周长和面积是有固定关系的。通过计算轮廓面积和周长,再结合这些固定关系,我们是可以得到一些结论的。

API

轮廓面积

public static double contourArea(Mat contour, boolean oriented)
  • 参数一:contour,轮廓的像素点。
  • 参数二:oriented,区域面积是否有方向性的标志位。true表示有方向性,false表示不具有方向性。轮廓顶点顺时针轮廓面积和逆时针轮廓面积互为相反数。此参数默认为false。

轮廓周长

public static double arcLength(MatOfPoint2f curve, boolean closed) 
  • 参数一:curve,轮廓的像素点。
  • 参数二:closed,轮廓是否闭合的标志位,true表示闭合,false表示不闭合。例如,计算三个点A,B,C的距离,若closed=true,则周长=AB+BC+CA,若closed=false,则周长=AB+BC。

操作

结合上篇Android OpenCV(三十五):轮廓发现与绘制示例,计算各轮廓的面积和周长。

/**
 * 轮廓发现、绘制、面积、周长
 * author: yidong
 * 2020/9/19
 */
class FindContoursActivity : AppCompatActivity() {
    private lateinit var mBinding: ActivityFindContoursBinding
    private lateinit var mSource: Mat
    private var level = 1
        set(value) {
            field = value
            find()
            mBinding.level.text = level.toString()
        }

    private var mFlag = Imgproc.RETR_TREE
        set(value) {
            field = value
            find()
        }

    private var ignoreLevel = true
        set(value) {
            field = value
            find()
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_find_contours)
        mBinding.ignoreLevel.setOnCheckedChangeListener { _, isChecked ->
            ignoreLevel = isChecked
        }
        val bgr = Utils.loadResource(this, R.drawable.hierarchy)
        mSource = Mat()
        Imgproc.cvtColor(bgr, mSource, Imgproc.COLOR_BGR2RGB)
        mBinding.ivLena.showMat(mSource)
        title = "RETR_TREE"
    }

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.menu_contours, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        title = item.title
        mFlag = when (item.itemId) {
            R.id.retr_external -> Imgproc.RETR_EXTERNAL
            R.id.retr_list -> Imgproc.RETR_LIST
            R.id.retr_ccomp -> Imgproc.RETR_CCOMP
            else -> Imgproc.RETR_TREE
        }
        return true
    }

    private fun find() {
        val tmp = mSource.clone()
        val gray = Mat()
        Imgproc.cvtColor(mSource, gray, Imgproc.COLOR_BGR2GRAY)
        Imgproc.GaussianBlur(gray, gray, Size(13.0, 13.0), 4.0, 4.0)
        val binary = Mat()
        Imgproc.threshold(gray, binary, 170.0, 255.0, Imgproc.THRESH_BINARY and Imgproc.THRESH_OTSU)

        val contours = mutableListOf<MatOfPoint>()
        val hierarchy = Mat()
        Imgproc.findContours(
            binary,
            contours,
            hierarchy,
            mFlag,
            Imgproc.CHAIN_APPROX_SIMPLE
        )

        for (i in 0 until contours.size) {
            val area = Imgproc.contourArea(contours[i])
            val source = MatOfPoint2f()
            source.fromList(contours[i].toList())
            val length = Imgproc.arcLength(source, true)
            Log.d(App.TAG, "轮廓${i}面积:${area}; 周长:${length}")
        }

        if (ignoreLevel) {
            Imgproc.drawContours(
                tmp,
                contours,
                -1,
                Scalar(255.0, 0.0, 0.0),
                2,
                Imgproc.LINE_AA
            )
        } else {
            Imgproc.drawContours(
                tmp,
                contours,
                -1,
                Scalar(255.0, 0.0, 0.0),
                2,
                Imgproc.LINE_AA,
                hierarchy,
                level
            )
        }
        mBinding.ivResult.showMat(tmp)
        Log.d(App.TAG, "hierarchy: ${hierarchy.dump()}")
        gray.release()
        binary.release()
        hierarchy.release()
        tmp.release()
    }


    fun increase(v: View) {
        level += 1
    }

    fun decrease(v: View) {
        level -= 1
    }

    override fun onDestroy() {
        mSource.release()
        super.onDestroy()
    }
}

效果

2020-10-02 13:57:57.801 6127-6127/cn.onlyloveyd.demo D/LearningAndroidOpenCV: 轮廓0面积:471385.0; 周长:2967.313708305359
2020-10-02 13:57:57.801 6127-6127/cn.onlyloveyd.demo D/LearningAndroidOpenCV: 轮廓1面积:434496.0; 周长:2861.9411249160767
2020-10-02 13:57:57.802 6127-6127/cn.onlyloveyd.demo D/LearningAndroidOpenCV: 轮廓2面积:277124.0; 周长:2381.313708305359
2020-10-02 13:57:57.802 6127-6127/cn.onlyloveyd.demo D/LearningAndroidOpenCV: 轮廓3面积:252129.0; 周长:2285.9411249160767
2020-10-02 13:57:57.802 6127-6127/cn.onlyloveyd.demo D/LearningAndroidOpenCV: 轮廓4面积:25732.0; 周长:649.3137083053589
2020-10-02 13:57:57.802 6127-6127/cn.onlyloveyd.demo D/LearningAndroidOpenCV: 轮廓5面积:48772.0; 周长:897.3137083053589
2020-10-02 13:57:57.802 6127-6127/cn.onlyloveyd.demo D/LearningAndroidOpenCV: 轮廓6面积:10312.0; 周长:1401.313708305359
2020-10-02 13:57:57.802 6127-6127/cn.onlyloveyd.demo D/LearningAndroidOpenCV: 轮廓7面积:2588.0; 周长:489.3137083053589

源码

https://github.com/onlyloveyd/LearningAndroidOpenCV

onlyloveyd CSDN认证博客专家 Android Kotlin OpenCV
个人公众号【OpenCV or Android】,热爱Android、Kotlin、Flutter和OpenCV。毕业于华中科技大学计算机专业,曾就职于华为武汉研究所。目前在三线小城市生活,专注技术与研发。
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页