使用OpenCV进行图像二值化与灰度化

人工智能
图像二值化是将图像中的像素点设置为0或255,从而实现黑白效果的过程。通过适当的阈值选取,可以将灰度图像中的像素分为两类,一类被认为是前景(目标),另一类被认为是背景。这样可以使图像数据量减小,同时凸显出目标的轮廓。

相关概念

「二值图像」(Binary Image)是一种只包含两种颜色(通常是黑色和白色)的图像。在二值图像中,每个像素要么是0(表示黑色),要么是255(表示白色),没有中间灰度级别。

二值图像主要用于简化图像处理和分析过程,因为它只包含两种颜色,使得图像处理算法更加简单、快速。例如,在文字识别、条形码读取等应用中,二值图像可以大大简化图像处理和识别的过程。

二值图像可以通过多种方法生成,包括阈值分割、迭代阈值分割、Otsu阈值法等。这些方法通常将原始图像中的像素值与某个阈值进行比较,根据比较结果将像素设置为黑色或白色。

二值图像的生成通常用于计算机视觉和图像处理领域,以简化图像处理和分析的过程。

「灰度图像」(Grayscale Image)是每个像素只有一个采样颜色的图像,通常显示为从最暗的黑色到最亮的白色的灰度。灰度图像与二值图像不同,在计算机图像领域中,二值图像只有黑色与白色两种颜色,而灰度图像在黑色与白色之间还有许多级的颜色深度。

灰度图像通常是测量每个像素的亮度得到的,用于显示的灰度图像通常用每个采样像素8位的非线性尺度来保存,这样可以有256级灰度。

灰度图像在图像处理中常用于简化图像处理和分析过程,因为它只包含一种颜色通道,使得算法更加简单和快速。

「彩色图像」(Color Image)彩色图像是一种能够显示颜色信息的图像,通常由红、绿、蓝三个颜色通道组合而成。每个通道的颜色强度范围从0到255,其中0表示该颜色完全缺失,255表示该颜色完全饱和。通过组合不同强度的三个通道,可以得到几乎所有的颜色。

彩色图像是数字图像处理和计算机视觉领域中的常见形式,广泛应用于摄影、视频、动画和网页设计等领域。在数字图像处理中,彩色图像的处理和分析通常比灰度图像更加复杂,因为需要同时处理三个颜色通道。

彩色图像的优点是可以显示颜色信息,更加真实地反映现实世界。然而,由于需要更多的存储空间和处理时间,彩色图像的处理速度通常比灰度图像慢。

「图像灰度化」(Image Grayscale) 是将彩色图像转换为灰度图像的过程。在灰度图像中,每个像素只包含一个灰度值,而不是彩色图像中的红、绿和蓝三个通道。灰度图像通常用于简化图像处理和分析,因为它们只包含亮度信息,而没有颜色信息。

灰度化的好处是相较于彩色图像灰度图像占内存更小,运行速度更快;灰度图像后可以在视觉上增加对比,突出目标区域。灰度化的应用包括图像处理、计算机视觉、模式识别等领域。

灰度图像是二值图像的一种特例,二值图像通过比较阈值将像素设置为黑色或白色。灰度化处理有三种常用方法:最大值法、平均值法和加权平均法。最大值法是直接取R、B、G三个分量中数值最大的分量的数值(0视为最小,255视为最大);平均值法是取R、B、G三个分量中数值的均值;加权平均法则是根据人眼对不同颜色的敏感度不同,给不同的颜色通道赋予不同的权重。

「图像二值化」(Image Binarization)是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的黑白效果的过程。二值化图像中数据量大为减少,从而能凸显出目标的轮廓。要得到二值化图像,首先要把图像灰度化,然后将256个亮度等级的灰度图像通过适当的阈值选取而获得仍然可以反映图像整体和局部特征的二值化图像。所有灰度大于或等于阈值的像素被判定为属于特定物体,其灰度值为255,否则这些像素点被排除在物体区域以外,灰度值为0,表示背景或者例外的物体区域。

比较常用的二值化方法有:简单二值法,平均值法,双峰法和OTSU法等。

二值化是图像分割的一种最简单的方法,广泛应用于计算机视觉领域。

灰度化方法

  • 最大值法:将彩色图像中的三分量亮度的最大值作为灰度图的灰度值。

图片图片

  • 平均值法:将彩色图像中的三分量亮度求平均得到一个灰度值。

图片图片

  • 加权平均法:根据人眼对R,G,B三通道的敏感度,按照一定权值进行加权平均得到灰度值。

图片图片

  • 浮点灰度法:将红、绿、蓝三个颜色通道乘以不同的浮点数权重,其中 RGB 的权重总和为 1,得到一个灰度值。

图片图片

  • 整数灰度法:避免浮点数运算使用整数算法,其中 RGB 的权重总和为 100,得到一个灰度值。

图片图片

  • 移位灰度法:移位计算比整数灰度法处理速度更快。

图片图片

  • 单通道法:仅取绿色作为灰度值。

图片图片

灰度方法示例:

//进行灰度
mBitmap?.run {
    val bitmap = Bitmap.createBitmap(this.width, this.height, Bitmap.Config.ARGB_8888)
    val srcMat = Mat()
    val dstMat = Mat()
    Utils.bitmapToMat(this, srcMat)
    Imgproc.cvtColor(srcMat, dstMat, Imgproc.COLOR_BGRA2GRAY)
    Utils.matToBitmap(dstMat, bitmap)
    runOnUiThread { mBinding.ivImage.setImageBitmap(bitmap) }
    srcMat.release()
    dstMat.release()
}

图片图片

二值化方法

  • 全局阈值法:该方法假设在整个图像范围内,亮度高于某个阈值的像素应被标记成前景(白色),而亮度低于该阈值的像素则应被标记成背景(黑色)。
  • otsu阈值法:这是一种根据直方图的自适应阈值选择算法,使用的阈值可使目标与背景之间的差异达到最大。
  • 局部阈值法:该方法不像全局阈值法将整张图像划分为前景和背景,而是根据每个像素周围邻域亮度变化来确定其属于前景还是背景。这种方法通常用于具有光照不均、噪声较多、纹理粗糙等情况下的图像二值化。
  • 自适应阈值法:该方法将每个像素的阈值设置为与其邻域内像素的平均值或加权平均值相关的值。这种方法通常用于灰度图像中需要提取一些特殊特征的情况下。
  • 基于形态学操作的局部二值化法:该方法是在阈值法的基础上,对图像的不同区域设定不同的阈值,以更好地反映图像的局部特征。
  • 基于聚类分析的二值化法:该方法是将像素点的灰度值分为两个簇,然后分别计算两个簇的均值,将均值作为阈值来进行处理。
  • 基于边缘检测的二值化法:该方法是在图像进行边缘检测之后,将边缘像素设为白色,其他像素设为黑色,以实现图像的二值化。

在 OpenCV 中,通过使用阈值分割 threshold() 函数、彩色图像分割 inRange() 函数以及边缘检测 Canny() 函数都可以实现图像二值化。

OpenCV中最简单的实现方式,先把图像灰度化,然后对灰度图像中的每个像素进行遍历,根据它的像素值是否大于一个固定的阈值,对输出图像对应位置的像素赋予不同的值。公式如下:

图片图片

二值化方法示例:

mBitmap?.run {
    val bitmap = Bitmap.createBitmap(this.width, this.height, Bitmap.Config.ARGB_8888)
    //先灰度
    val srcMat = Mat()
    val dstMat = Mat()
    Utils.bitmapToMat(this, srcMat)
    Imgproc.cvtColor(srcMat, dstMat, Imgproc.COLOR_BGRA2GRAY)
    val resultMat = Mat()
    Imgproc.threshold(dstMat, resultMat, 100.0, 255.0, Imgproc.THRESH_BINARY)
    Utils.matToBitmap(resultMat, bitmap)
    runOnUiThread { mBinding.ivImage.setImageBitmap(bitmap) }
    srcMat.release()
    dstMat.release()
    resultMat.release()
}

图片图片

完整示例

<?xml versinotallow="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".activity.TestActivity">

    <ImageView
        android:id="@+id/iv_image"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:scaleType="centerCrop" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btn_load"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="加载图片"
            android:textSize="16sp" />

        <Button
            android:id="@+id/btn_gray"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_weight="1"
            android:text="图片灰度化"
            android:textSize="16sp" />

        <Button
            android:id="@+id/btn_binarization"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_weight="1"
            android:text="图片二值化"
            android:textSize="16sp" />
    </LinearLayout>
</LinearLayout>
class TestActivity : AppCompatActivity() {

    private val TAG = MainActivity::class.java.simpleName
    private lateinit var mBinding: ActivityTestBinding
    private var mBitmap: Bitmap? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mBinding = ActivityTestBinding.inflate(layoutInflater)
        setContentView(mBinding.root)

        //初始化OpenCV
        val initState = OpenCVLoader.initLocal()
        Log.d(TAG, "onCreate: OpenCV初始化$initState")

        mBinding.btnLoad.setOnClickListener {
            val intent = Intent()
            intent.setType("image/*")

            intent.setAction(Intent.ACTION_GET_CONTENT)
            startActivityForResult(intent, 20240104)
        }
        mBinding.btnGray.setOnClickListener {
            if (mBitmap == null) {
                return@setOnClickListener
            }
            //进行灰度
            mBitmap?.run {
                val bitmap = Bitmap.createBitmap(this.width, this.height, Bitmap.Config.ARGB_8888)
                val srcMat = Mat()
                val dstMat = Mat()
                Utils.bitmapToMat(this, srcMat)
                Imgproc.cvtColor(srcMat, dstMat, Imgproc.COLOR_BGRA2GRAY)
                Utils.matToBitmap(dstMat, bitmap)
                runOnUiThread { mBinding.ivImage.setImageBitmap(bitmap) }
                srcMat.release()
                dstMat.release()
            }
        }
        mBinding.btnBinarization.setOnClickListener {
            if (mBitmap == null) {
                return@setOnClickListener
            }
            mBitmap?.run {
                val bitmap = Bitmap.createBitmap(this.width, this.height, Bitmap.Config.ARGB_8888)
                //先灰度
                val srcMat = Mat()
                val dstMat = Mat()
                Utils.bitmapToMat(this, srcMat)
                Imgproc.cvtColor(srcMat, dstMat, Imgproc.COLOR_BGRA2GRAY)
                val resultMat = Mat()
                Imgproc.threshold(dstMat, resultMat, 100.0, 255.0, Imgproc.THRESH_BINARY)
                Utils.matToBitmap(resultMat, bitmap)
                runOnUiThread { mBinding.ivImage.setImageBitmap(bitmap) }
                srcMat.release()
                dstMat.release()
                resultMat.release()
            }

        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == 20240104 && resultCode == RESULT_OK && data != null) {
            data.data?.run {
                mBitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(this))
            }
            mBitmap?.run {
                mBinding.ivImage.setImageBitmap(this)
            }
        }
    }
}

总结

图像二值化是将图像中的像素点设置为0或255,从而实现黑白效果的过程。通过适当的阈值选取,可以将灰度图像中的像素分为两类,一类被认为是前景(目标),另一类被认为是背景。这样可以使图像数据量减小,同时凸显出目标的轮廓。

灰度化是将彩色图像转换为灰度图像的过程。在灰度图像中,每个像素只包含一个灰度值,而不是彩色图像中的红、绿和蓝三个通道。灰度图像通常用于简化图像处理和分析,因为它们只包含亮度信息,而没有颜色信息。

灰度图像的每个像素可以用8位来表示,因此有0-255个灰度值。而二值图像中,像素只有两种状态:黑色(0)和白色(255)。

二值化是将灰度图像转换为黑白二值图像的过程,而灰度化是将彩色图像转换为灰度图像的过程。在OpenCV中,可以使用不同的阈值处理方法来实现图像的二值化和灰度化。

责任编辑:武晓燕 来源: 沐雨花飞蝶
相关推荐

2011-12-31 15:57:21

Java

2017-09-25 15:43:24

图像模板Python+Open

2016-10-26 22:16:48

macaca自动化测试javascript

2019-09-02 10:51:59

Python脚本语言程序员

2024-11-21 15:24:49

2013-03-22 15:15:28

自动化管理部署虚拟化

2019-09-02 09:11:58

MySQLDocker数据

2022-12-05 10:47:08

RocketMQ灰度消息

2011-12-02 09:57:50

存储虚拟化存储虚拟化

2019-08-22 16:26:02

LinuxKVM虚拟化

2011-12-05 14:07:17

虚拟化本地存储桌面虚拟化

2023-10-12 09:21:41

Java图像

2021-02-20 09:14:35

PythonPygal可视化

2023-02-15 08:24:12

数据分析数据可视化

2021-11-09 08:15:18

Grafana 数据可视化运维

2024-02-21 19:02:05

Go模板化方式

2023-11-21 22:48:50

2022-10-19 15:28:32

软件系统应用程序

2021-07-26 16:31:30

网络数据技术

2024-01-02 07:37:52

FlaggerKubernetesIstio
点赞
收藏

51CTO技术栈公众号