Hough變換

Gaowaly發表於2024-07-28

python實現廣義Hough變換演算法、Hough變換演算法

      • 1.廣義Hough變換演算法詳解
        • 演算法步驟
        • Python 實現
        • 詳細解釋
        • 優缺點
      • 2.Hough變換演算法詳解
        • 演算法步驟
        • Python 實現
        • 詳細解釋
        • 優缺點

實現廣義Hough變換演算法(Generalized Hough Transform)可以用於檢測任意形狀的物件,不限於標準的直線或圓形。這裡將詳細介紹廣義Hough變換的原理及其Python實現。

1.廣義Hough變換演算法詳解

廣義Hough變換的基本原理是將目標形狀(通常用邊緣檢測後的二值影像表示)的每個畫素點轉化為其可能的引數空間(例如,中心點、旋轉角度等)中的投票。透過累積這些投票,可以找到在影像中存在的目標形狀的位置和方向。

演算法步驟
  1. 預處理:獲取目標形狀的邊緣影像。

  2. 定義引數空間:根據目標形狀的特徵,定義引數空間(通常是多維的),例如中心點座標、角度、比例等。

  3. 填充累加器:對於每個目標形狀的畫素點,計算其在引數空間中的可能位置,並在累加器中進行累加。

  4. 確定目標位置:在累加器中找到最高投票的位置,該位置即為目標形狀在原始影像中的位置和屬性。

Python 實現

以下是廣義Hough變換的Python實現示例,用於檢測影像中的任意形狀。這裡以檢測矩形為例子。

import cv2
import numpy as np
import matplotlib.pyplot as plt

def generalized_hough_transform(image, template):
    """
    廣義Hough變換演算法實現,用於檢測任意形狀的物件

    引數:
    image (numpy.ndarray): 輸入的灰度影像
    template (numpy.ndarray): 目標形狀的二值化模板影像

    返回:
    tuple: 目標形狀在原始影像中的位置和屬性資訊
    """
    # 提取目標形狀的邊緣
    edges = cv2.Canny(template, 50, 150)

    # 計算目標形狀的引數空間
    accumulator = np.zeros_like(image, dtype=np.uint64)

    rows, cols = np.nonzero(edges)
    for i in range(len(rows)):
        y = rows[i]
        x = cols[i]
        for theta in range(0, 180):  # 假設角度範圍
            rho = x * np.cos(np.deg2rad(theta)) + y * np.sin(np.deg2rad(theta))
            rho = int(rho)
            accumulator[rho, theta] += 1

    # 找到最高投票的位置
    rho_idx, theta_idx = np.unravel_index(np.argmax(accumulator), accumulator.shape)
    rho = rho_idx - accumulator.shape[0] // 2  # 轉換為原始座標系中的rho
    theta = np.deg2rad(theta_idx)  # 轉換為角度

    return rho, theta

# 示例用法
if __name__ == "__main__":
    # 讀取影像
    image = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE)
    template = cv2.imread('template.png', cv2.IMREAD_GRAYSCALE)

    # 呼叫廣義Hough變換演算法
    rho, theta = generalized_hough_transform(image, template)

    # 顯示原始影像和檢測結果
    plt.figure(figsize=(12, 6))
    plt.subplot(1, 2, 1)
    plt.title('Original Image')
    plt.imshow(image, cmap='gray')
    plt.axis('off')

    plt.subplot(1, 2, 2)
    plt.title('Detected Shape')
    plt.imshow(cv2.cvtColor(template, cv2.COLOR_GRAY2RGB))
    plt.axis('off')

    # 繪製檢測到的形狀
    h, w = template.shape
    cos_theta = np.cos(theta)
    sin_theta = np.sin(theta)
    x0 = cos_theta * rho
    y0 = sin_theta * rho
    x1 = int(x0 + 1000 * (-sin_theta))
    y1 = int(y0 + 1000 * (cos_theta))
    x2 = int(x0 - 1000 * (-sin_theta))
    y2 = int(y0 - 1000 * (cos_theta))
    plt.plot([x1, x2], [y1, y2], 'r-', lw=2)

    plt.show()
詳細解釋

1. 提取目標形狀的邊緣:

edges = cv2.Canny(template, 50, 150)

使用Canny邊緣檢測演算法獲取目標形狀的二值化邊緣影像。

2. 計算目標形狀的引數空間:

accumulator = np.zeros_like(image, dtype=np.uint64)

建立一個累加器陣列,用於累計目標形狀的投票。

3. 填充累加器:

for i in range(len(rows)):
    y = rows[i]
    x = cols[i]
    for theta in range(0, 180):  # 假設角度範圍
        rho = x * np.cos(np.deg2rad(theta)) + y * np.sin(np.deg2rad(theta))
        rho = int(rho)
        accumulator[rho, theta] += 1

遍歷目標形狀的邊緣畫素,計算其在引數空間(rho, theta)中的投票,並在累加器中進行累加。

4. 找到最高投票的位置:

rho_idx, theta_idx = np.unravel_index(np.argmax(accumulator), accumulator.shape)
rho = rho_idx - accumulator.shape[0] // 2  # 轉換為原始座標系中的rho
theta = np.deg2rad(theta_idx)  # 轉換為角度

在累加器中找到最高投票的位置,並將其轉換為原始影像座標系中的rho和角度theta。

5. 顯示和儲存檢測結果:

plt.subplot(1, 2, 1)
plt.title('Original Image')
plt.imshow(image, cmap='gray')
plt.axis('off')

plt.subplot(1, 2, 2)
plt.title('Detected Shape')
plt.imshow(cv2.cvtColor(template, cv2.COLOR_GRAY2RGB))
plt.axis('off')

# 繪製檢測到的形狀
h, w = template.shape
cos_theta = np.cos(theta)
sin_theta = np.sin(theta)
x0 = cos_theta * rho
y0 = sin_theta * rho
x1 = int(x0 + 1000 * (-sin_theta))
y1 = int(y0 + 1000 * (cos_theta))
x2 = int(x0 - 1000 * (-sin_theta))
y2 = int(y0 - 1000 * (cos_theta))
plt.plot([x1, x2], [y1, y2], 'r-', lw=2)

plt.show()
  1. 使用Matplotlib庫顯示原始影像和檢測到的形狀,以及在檢測結果影像上繪製檢測到的形狀。

優缺點

優點:

  • 適用性廣泛:能夠檢測任意形狀的物件,不限於直線或圓形。
  • 靈活性:透過定義不同的引數空間,可以適應不同形狀的檢測需求。

缺點:

  • 計算複雜度高:需要對每個畫素點在多維引數空間中進行累加,對計算資源要求高。
  • 對引數敏感:引數的選擇對檢測結果影響較大,需要根據具體應用進行調整。

廣義Hough變換演算法在實際應用中需要根據具體場景和目標形狀進行引數調整和最佳化,以達到最佳的檢測效果。
Hough變換是一種經典的影像處理技術,用於檢測影像中的直線。它透過將影像空間中的畫素點對映到引數空間(Hough空間),並在引數空間中進行累加來檢測直線。以下是Hough變換的詳細介紹和Python實現。

2.Hough變換演算法詳解

演算法步驟
  1. 邊緣檢測:首先對影像進行邊緣檢測,通常使用Canny邊緣檢測器獲取二值化的邊緣影像。

  2. 引數空間初始化:根據影像的尺寸和角度範圍初始化Hough變換的引數空間。對於檢測直線,通常使用極座標參數列示:極徑(r)和極角(θ)。

  3. 累加器填充:遍歷邊緣影像中的每個畫素點,對每個邊緣畫素點計算其可能的直線引數,並在Hough累加器中進行累加。

  4. 找出累加器中的峰值:在累加器中找到高投票的點,這些點表示了可能的直線引數(r, θ)。

  5. 反對映直線到影像空間:將在引數空間中找到的直線引數(r, θ)反對映到原始影像空間中,並繪製檢測到的直線。

Python 實現

以下是使用Python和OpenCV庫實現Hough變換演算法檢測直線的示例程式碼:

import cv2
import numpy as np
import matplotlib.pyplot as plt

def hough_transform(image, edge_image, threshold):
    """
    實現Hough變換檢測影像中的直線

    引數:
    image (numpy.ndarray): 輸入的灰度影像
    edge_image (numpy.ndarray): 邊緣檢測後的二值化影像
    threshold (int): 累加器閾值,用於確定直線檢測的靈敏度

    返回:
    list: 檢測到的直線的引數列表 [(rho1, theta1), (rho2, theta2), ...]
    """
    # 影像尺寸
    height, width = edge_image.shape

    # 極座標引數空間的r和theta的步長
    theta_step = 1
    r_step = 1

    # 極座標引數空間的範圍
    max_rho = int(np.sqrt(height**2 + width**2))  # 極徑的最大值
    theta_range = np.arange(0, 180, theta_step)
    rho_range = np.arange(-max_rho, max_rho, r_step)

    # 構建累加器
    accumulator = np.zeros((len(rho_range), len(theta_range)), dtype=np.uint64)

    # 邊緣影像中的非零畫素點
    edge_points = np.argwhere(edge_image > 0)

    # 遍歷每個邊緣畫素點
    for y, x in edge_points:
        for theta_idx, theta in enumerate(np.deg2rad(theta_range)):
            rho = int(x * np.cos(theta) + y * np.sin(theta))
            rho_idx = np.argmin(np.abs(rho_range - rho))
            accumulator[rho_idx, theta_idx] += 1

    # 獲取高投票的直線引數
    lines = []
    for rho_idx in range(len(rho_range)):
        for theta_idx in range(len(theta_range)):
            if accumulator[rho_idx, theta_idx] > threshold:
                rho = rho_range[rho_idx]
                theta = np.deg2rad(theta_range[theta_idx])
                lines.append((rho, theta))

    return lines

# 示例用法
if __name__ == "__main__":
    # 讀取影像
    image = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE)

    # 邊緣檢測
    edge_image = cv2.Canny(image, 50, 150)

    # 進行Hough變換
    threshold = 100  # 累加器閾值
    detected_lines = hough_transform(image, edge_image, threshold)

    # 繪製檢測到的直線
    plt.figure(figsize=(8, 6))
    plt.imshow(cv2.cvtColor(image, cv2.COLOR_GRAY2RGB))
    plt.title('Detected Lines')
    plt.axis('off')

    for rho, theta in detected_lines:
        a = np.cos(theta)
        b = np.sin(theta)
        x0 = a * rho
        y0 = b * rho
        x1 = int(x0 + 1000 * (-b))
        y1 = int(y0 + 1000 * (a))
        x2 = int(x0 - 1000 * (-b))
        y2 = int(y0 - 1000 * (a))
        plt.plot([x1, x2], [y1, y2], 'r-', lw=2)

    plt.show()
詳細解釋
  1. 邊緣檢測:

    edge_image = cv2.Canny(image, 50, 150)

    使用Canny邊緣檢測演算法獲取影像的二值化邊緣影像。

  2. 初始化引數空間:

    max_rho = int(np.sqrt(height**2 + width**2))  # 極徑的最大值
    theta_range = np.arange(0, 180, theta_step)
    rho_range = np.arange(-max_rho, max_rho, r_step)
    accumulator = np.zeros((len(rho_range), len(theta_range)), dtype=np.uint64)

    初始化極座標引數空間(rho, theta)及其對應的累加器。

  3. 填充累加器:

    for y, x in edge_points:
        for theta_idx, theta in enumerate(np.deg2rad(theta_range)):
            rho = int(x * np.cos(theta) + y * np.sin(theta))
            rho_idx = np.argmin(np.abs(rho_range - rho))
            accumulator[rho_idx, theta_idx] += 1

    遍歷邊緣影像中的每個畫素點,計算其可能的直線引數(rho, theta),並在累加器中進行累加。

  4. 找出高投票的直線引數:

    lines = []
    for rho_idx in range(len(rho_range)):
        for theta_idx in range(len(theta_range)):
            if accumulator[rho_idx, theta_idx] > threshold:
                rho = rho_range[rho_idx]
                theta = np.deg2rad(theta_range[theta_idx])
                lines.append((rho, theta))

    在累加器中找到累計值超過閾值的點,即為檢測到的直線引數(rho, theta)。

  5. 繪製檢測到的直線:

    plt.figure(figsize=(8, 6))
    plt.imshow(cv2.cvtColor(image, cv2.COLOR_GRAY2RGB))
    plt.title('Detected Lines')
    plt.axis('off')
    
    for rho, theta in detected_lines:
        a = np.cos(theta)
        b = np.sin(theta)
        x0 = a * rho
        y0 = b * rho
        x1 = int(x0 + 1000 * (-b))
        y1 = int(y0 + 1000 * (a))
        x2 = int(x0 - 1000 * (-b))
        y2 = int(y0 - 1000 * (a))
        plt.plot([x1, x2], [y1, y2], 'r-', lw=2)
    
    plt.show()

    使用Matplotlib庫在原始影像上繪製檢測到的直線。

優缺點

優點:

  • 適用性廣泛:能夠檢測直線,簡單且有效。
  • 不依賴於直線的長度和位置:適用於檢測任意方向的直線。

缺點:

  • 對引數的選擇敏感:引數的選擇(如閾值)會影響檢測結果,需要根據具體情況調整。
  • 計算複雜度高:需要遍歷邊緣影像中的每個畫素點,並在引數空間中進行累加,計算量較大。

Hough變換是一種經典且重要的影像處理技術,在許多計算機視覺和影像分析應用中都有廣泛應用。

相關文章