使用OpenCV進行圖像二值化與灰度化
相關概念
「二值圖像」(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中,可以使用不同的閾值處理方法來實現圖像的二值化和灰度化。