Android基于OpenCV實(shí)現(xiàn)霍夫直線檢測(cè)
霍夫直線檢測(cè)
點(diǎn)和線的對(duì)偶性
- 圖像空間中的點(diǎn),對(duì)應(yīng)霍夫空間中的直線
- 圖像空間中的直線,對(duì)應(yīng)霍夫空間中的點(diǎn)
- 共點(diǎn)的直線,在霍夫空間中對(duì)應(yīng)的點(diǎn)在一條直線上
- 共線的點(diǎn),在霍夫空間中對(duì)應(yīng)的直線交于一點(diǎn)

極坐標(biāo)參數(shù)方程
對(duì)于平面中的一條直線,在笛卡爾坐標(biāo)中,常見的有點(diǎn)斜式,兩點(diǎn)式兩種表示方法。然而在霍夫變換中,考慮的是另外一種表示方式:使用(r, theta)來(lái)表示一條直線。其中r為該直線到原點(diǎn)的距離,theta為該直線的垂線與x軸的夾角。如下圖所示:

根據(jù)霍夫變換原理,利用極坐標(biāo)形式表示直線時(shí),在圖像空間中經(jīng)過某一點(diǎn)的所有直線映射到參數(shù)空間中是一個(gè)正弦曲線。圖像空間中直線上的兩個(gè)點(diǎn)在參數(shù)空間中映射的兩條正弦曲線相交于一點(diǎn)。

通過上述的變換過程,將圖像中的直線檢測(cè)轉(zhuǎn)換成了在參數(shù)空間中尋找某個(gè)點(diǎn) 通過的正線曲線最多的問題。由于在參數(shù)空間內(nèi)的曲線是連續(xù)的,而在實(shí)際情況中圖像的像素是離散的,因此我們需要將參數(shù)空間的坐標(biāo)軸進(jìn)行離散化,用離散后的方格表示每一條正弦曲線。首先尋找符合條件的網(wǎng)格,之后尋找該網(wǎng)格對(duì)應(yīng)的圖像空間中所有的點(diǎn),這些點(diǎn)共同組成了原圖像中的直線。
由此可見,霍夫變換算法檢測(cè)圖像中的直線主要分為4個(gè)步驟
- 將參數(shù)空間的坐標(biāo)軸離散化,例如theta=0,10,20……, r=0.1,0.2,0.3……
- 將圖像中每個(gè)非0像素通過映射關(guān)系求取在參數(shù)空間通過的方格。
- 統(tǒng)計(jì)參數(shù)空間內(nèi)每個(gè)方格出現(xiàn)的次數(shù),選取次數(shù)大于某一閾值的方格作為表示直線的方格。
- 將參數(shù)空間中表示直線的方格的參數(shù)作為圖像中直線的參數(shù)。
霍夫檢測(cè)具有抗干擾能力強(qiáng),對(duì)圖像中直線的殘缺部分、噪聲以及其它共存的非直線結(jié)構(gòu)不敏感,能容忍特征邊界描述中的間隙,并且相對(duì)不受圖像噪聲影響等優(yōu)點(diǎn),但是霍夫變換的時(shí)間復(fù)雜度和空間復(fù)雜度都很高,并且檢測(cè)精度受參數(shù)離散間隔制約。離散間隔較大時(shí)會(huì)降低檢測(cè)精度,離散間隔較小時(shí)雖然能提高精度,但是會(huì)增加計(jì)算負(fù)擔(dān),導(dǎo)致計(jì)算時(shí)間邊長(zhǎng)
API
public static void HoughLines(Mat image, Mat lines, double rho, double theta, int threshold, double srn, double stn, double min_theta)
- 參數(shù)一:image,待檢測(cè)直線的原圖像,必須是CV_8U的單通道圖像.
- 參數(shù)二:lines,霍夫變換檢測(cè)到的直線輸出量,每一條直線都由兩個(gè)或者三個(gè)參數(shù)表示。第一個(gè)表示直線距離坐標(biāo)原點(diǎn)的距離 ,第二個(gè)表示坐標(biāo)原點(diǎn)到直線的垂線與x軸的夾角,若有第三個(gè),則表示累加器的數(shù)值。

- 參數(shù)三:rho,距離分辨率,以像素為單位,距離離散化時(shí)的單位長(zhǎng)度
- 參數(shù)四:theta,角度分辨率,以弧度為單位,夾角離散化時(shí)的單位角度。
- 參數(shù)五:threshold,累加器的閾值,即參數(shù)空間中離散化后每個(gè)方格被通過的累計(jì)次數(shù)大于該閾值時(shí)將被識(shí)別為直線,否則不被識(shí)別為直線。
- 參數(shù)六:srn,對(duì)于多尺度霍夫變換算法中,該參數(shù)表示距離分辨率的除數(shù),粗略的累加器距離分辨率是第三個(gè)參數(shù)rho,精確的累加器分辨率是rho/srn。這個(gè)參數(shù)必須是非負(fù)數(shù),默認(rèn)參數(shù)為0。
- 參數(shù)七:stn,對(duì)于多尺度霍夫變換算法中,該參數(shù)表示角度分辨率的除數(shù),粗略的累加器距離分辨率是第四個(gè)參數(shù)rho,精確的累加器分辨率是rho/stn。這個(gè)參數(shù)必須是非負(fù)數(shù),默認(rèn)參數(shù)為0。當(dāng)這個(gè)參數(shù)與第六個(gè)參數(shù)srn同時(shí)為0時(shí),此函數(shù)表示的是標(biāo)準(zhǔn)霍夫變換。
- 參數(shù)八:min_theta,檢測(cè)直線的最小角度,默認(rèn)參數(shù)為0。
- 參數(shù)九:max_theta,檢測(cè)直線的最大角度,默認(rèn)參數(shù)為CV_PI,是OpenCV 4中的默認(rèn)數(shù)值具體為3.1415926535897932384626433832795。
使用標(biāo)準(zhǔn)霍夫變換和多尺度霍夫變換函數(shù)HoughLins()提取直線時(shí)無(wú)法準(zhǔn)確知道圖像中直線或者線段的長(zhǎng)度,只能得到圖像中是否存在符合要求的直線以及直線的極坐標(biāo)解析式。如果需要準(zhǔn)確的定位圖像中線段的位置,HoughLins()函數(shù)便無(wú)法滿足需求。但是OpenCV 4提供的漸進(jìn)概率式霍夫變換函數(shù)HoughLinesP()可以得到圖像中滿足條件的直線或者線段兩個(gè)端點(diǎn)的坐標(biāo),進(jìn)而確定直線或者線段的位置。
public static void HoughLinesP(Mat image, Mat lines, double rho, double theta, int threshold, double minLineLength, double maxLineGap)
參數(shù)一:image,待檢測(cè)直線的原圖像,必須是CV_8U的單通道圖像.
參數(shù)二:lines,輸出線段。每條線由4元素表示。如下,分別代表每個(gè)線段的兩個(gè)端點(diǎn)

- 參數(shù)三:rho,距離分辨率,以像素為單位,距離離散化時(shí)的單位長(zhǎng)度
- 參數(shù)四:theta,角度分辨率,以弧度為單位,夾角離散化時(shí)的單位角度。
- 參數(shù)五:threshold,累加器的閾值,即參數(shù)空間中離散化后每個(gè)方格被通過的累計(jì)次數(shù)大于該閾值時(shí)將被識(shí)別為直線,否則不被識(shí)別為直線。該累積數(shù)越大,則得到的直線可能就越長(zhǎng)。
- 參數(shù)六:minLineLength,表示可以檢測(cè)的最小線段長(zhǎng)度,根據(jù)實(shí)際需要進(jìn)行設(shè)置。
- 參數(shù)七:maxLineGap,表示線段之間的最大間隔像素,假設(shè)5表示小于5個(gè)像素的兩個(gè)相鄰線段可以連接起來(lái)。
操作
package cn.onlyloveyd.demo.ui
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import cn.onlyloveyd.demo.R
import cn.onlyloveyd.demo.databinding.ActivityHoughLineBinding
import cn.onlyloveyd.demo.ext.showMat
import org.opencv.android.Utils
import org.opencv.core.Mat
import org.opencv.core.Point
import org.opencv.core.Scalar
import org.opencv.imgproc.Imgproc
import kotlin.math.cos
import kotlin.math.roundToInt
import kotlin.math.sin
/**
* 霍夫直線檢測(cè)
* author: yidong
* 2020/7/18
*/
class HoughLineDetectActivity : AppCompatActivity() {
private lateinit var mBinding: ActivityHoughLineBinding
private lateinit var mGray: Mat
private lateinit var mEdge: Mat
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_hough_line)
mBinding.presenter = this
mGray = Mat()
mEdge = Mat()
val bgr = Utils.loadResource(this, R.drawable.book)
Imgproc.cvtColor(bgr, mGray, Imgproc.COLOR_BGR2GRAY)
mBinding.ivLena.showMat(mGray)
Imgproc.Canny(mGray, mEdge, 80.0, 150.0, 3, false)
}
override fun onDestroy() {
mGray.release()
mEdge.release()
super.onDestroy()
}
fun doHoughLineDetect() {
title = "HoughLine"
val lines = Mat()
Imgproc.HoughLines(mEdge, lines, 1.0, Math.PI / 180.0, 150)
val out = Mat.zeros(mGray.size(), mGray.type())
val data = FloatArray(2)
for (i in 0 until lines.rows()) {
lines.get(i, 0, data)
val rho = data[0] // 直線距離坐標(biāo)原點(diǎn)的距離
val theta = data[1] // 直線過坐標(biāo)原點(diǎn)垂線與x軸夾角
val a = cos(theta.toDouble()) //夾角的余弦值
val b = sin(theta.toDouble()) //夾角的正弦值
val x0 = a * rho //直線與過坐標(biāo)原點(diǎn)的垂線的交點(diǎn)
val y0 = b * rho
val pt1 = Point()
val pt2 = Point()
pt1.x = (x0 + 1000 * (-b)).roundToInt().toDouble()
pt1.y = (y0 + 1000 * (a)).roundToInt().toDouble()
pt2.x = (x0 - 1000 * (-b)).roundToInt().toDouble()
pt2.y = (y0 - 1000 * (a)).roundToInt().toDouble()
Imgproc.line(out, pt1, pt2, Scalar(255.0, 255.0, 255.0), 2, Imgproc.LINE_AA, 0)
}
mBinding.ivResult.showMat(out)
out.release()
lines.release()
}
fun doHoughLinePDetect() {
title = "HoughLineP"
val lines = Mat()
Imgproc.HoughLinesP(mEdge, lines, 1.0, Math.PI / 180.0, 100, 50.0, 10.0)
val out = Mat.zeros(mGray.size(), mGray.type())
for (i in 0 until lines.rows()) {
val data = IntArray(4)
lines.get(i, 0, data)
val pt1 = Point(data[0].toDouble(), data[1].toDouble())
val pt2 = Point(data[2].toDouble(), data[3].toDouble())
Imgproc.line(out, pt1, pt2, Scalar(255.0, 255.0, 255.0), 2, Imgproc.LINE_AA, 0)
}
mBinding.ivResult.showMat(out)
out.release()
lines.release()
}
}
效果


以上就是Android基于OpenCV實(shí)現(xiàn)霍夫直線檢測(cè)的詳細(xì)內(nèi)容,更多關(guān)于Android OpenCV實(shí)現(xiàn)霍夫直線檢測(cè)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Android基于OpenCV實(shí)現(xiàn)圖像脫色
- Android基于opencv實(shí)現(xiàn)多通道分離與合并
- Android基于OpenCV實(shí)現(xiàn)圖像金字塔
- Android基于OpenCV實(shí)現(xiàn)QR二維碼檢測(cè)
- Android基于OpenCV實(shí)現(xiàn)非真實(shí)渲染
- Android基于OpenCV實(shí)現(xiàn)圖像修復(fù)
- Android OpenCv4 繪制多邊形的方法
- Android+OpenCv4實(shí)現(xiàn)邊緣檢測(cè)及輪廓繪制出圖像最大邊緣
- 如何在Android上使用opencv
- Android基于OpenCV實(shí)現(xiàn)Harris角點(diǎn)檢測(cè)
相關(guān)文章
從源碼編譯Android系統(tǒng)的Java類庫(kù)和JNI動(dòng)態(tài)庫(kù)的方法
這篇文章主要介紹了從源碼編譯Android系統(tǒng)的Java類庫(kù)和JNI動(dòng)態(tài)庫(kù)的方法,例子基于Linux系統(tǒng)環(huán)境下來(lái)講,需要的朋友可以參考下2016-02-02
Android使用表格布局設(shè)計(jì)注冊(cè)界面
這篇文章主要為大家詳細(xì)介紹了Android使用表格布局設(shè)計(jì)注冊(cè)界面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-05-05
android bitmap compress(圖片壓縮)代碼
android bitmap compress(圖片壓縮)代碼,需要的朋友可以參考一下2013-06-06
Android開發(fā)中使用WebView控件瀏覽網(wǎng)頁(yè)的方法詳解
這篇文章主要介紹了Android開發(fā)中使用WebView控件瀏覽網(wǎng)頁(yè)的方法,結(jié)合實(shí)例形式較為詳細(xì)的總結(jié)分析了Android WebView控件的功能、布局、設(shè)置、常用方法及相關(guān)操作技巧,需要的朋友可以參考下2017-10-10
Android利用Theme自定義Activity間的切換動(dòng)畫
這篇文章主要為大家詳細(xì)介紹了Android利用Theme自定義Activity間的切換動(dòng)畫,感興趣的小伙伴們可以參考一下2016-09-09
Android UI設(shè)計(jì)系列之自定義ViewGroup打造通用的關(guān)閉鍵盤小控件ImeObserverLayout(9)
這篇文章主要介紹了Android UI設(shè)計(jì)系列之自定義ViewGroup打造通用的關(guān)閉鍵盤小控件ImeObserverLayout,具有一定的實(shí)用性和參考價(jià)值,感興趣的小伙伴們可以參考一下2016-06-06
Android 對(duì)話框 Dialog使用實(shí)例講解
對(duì)話框是在當(dāng)前的頁(yè)面之上彈出的小窗口, 用于顯示一些重要的提示信息, 提示用戶的輸入,確認(rèn)信息,或顯示某種狀態(tài).如 : 顯示進(jìn)度條對(duì)話框, 退出提示.接下來(lái)通過本文給大家介紹android dialog對(duì)話框知識(shí),感興趣的朋友一起看看吧2016-09-09

