Biến đổi Hough trong Xử lý ảnh

Biến đổi Hough Tổng quát

Biến đổi Hough tổng quát (Generalized Hough Transform - GHT) là một mở rộng của biến đổi Hough cơ bản, cho phép phát hiện các đối tượng có hình dạng tùy ý, không chỉ giới hạn ở các đường thẳng hay hình tròn. Nguyên lý cốt lõi của GHT là chuyển đổi mỗi điểm biên của một hình dạng mục tiêu (thường là một ảnh nhị phân sau khi phát hiện biên) thành các phiếu bầu trong không gian tham số (ví dụ: tọa độ trung tâm, góc quay, tỷ lệ,...). Bằng cách tích lũy các phiếu bầu này, chúng ta có thể xác định vị trí và hướng của hình dạng mục tiêu trong ảnh gốc.

Các bước của thuật toán

  1. Tiền xử lý: Lấy ảnh biên của hình dạng mục tiêu.
  2. Xác định không gian tham số: Dựa trên đặc điểm của hình dạng, xác định không gian tham số (thường là đa chiều), ví dụ: tọa độ trung tâm, góc quay, tỷ lệ,...
  3. Lấp đầy bộ tích lũy: Với mỗi điểm pixel của hình dạng mục tiêu, tính toán vị trí có thể của nó trong không gian tham số và tăng giá trị trong bộ tích lũy.
  4. Xác định vị trí mục tiêu: Tìm vị trí có số phiếu bầu cao nhất trong bộ tích lũy, vị trí này chính là vị trí và thuộc tính của hình dạng mục tiêu trong ảnh gốc.

Cài đặt Python

Dưới đây là ví dụ về cài đặt Python cho biến đổi Hough thông thường (để phát hiện đường thẳng), một dạng cơ bản của thuật toán. Mã nguồn này sử dụng thư viện OpenCV và NumPy.


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

def bien_doi_hough(image, anh_lan_can, nguong):
    """
    Thực hiện biến đổi Hough để phát hiện các đường thẳng trong ảnh.

    Tham số:
    image (numpy.ndarray): Ảnh xám đầu vào
    anh_lan_can (numpy.ndarray): Ảnh biên đã được phát hiện
    nguong (int): Ngưỡng bộ tích lũy để xác định độ nhạy của việc phát hiện

    Trả về:
    list: Danh sách các tham số của đường thẳng đã phát hiện [(rho1, theta1), (rho2, theta2), ...]
    """
    # Kích thước ảnh
    chieu_cao, chieu_rong = anh_lan_can.shape

    # Bước nhảy của các tham số rho và theta trong không gian cực
    buoc_theta = 1
    buoc_rho = 1

    # Xác định phạm vi của không gian tham số
    gia_tri_rho_lon_nhat = int(np.sqrt(chieu_cao**2 + chieu_rong**2))
    pham_vi_theta = np.arange(0, 180, buoc_theta)
    pham_vi_rho = np.arange(-gia_tri_rho_lon_nhat, gia_tri_rho_lon_nhat, buoc_rho)

    # Xây dựng bộ tích lũy
    mang_tich_luy = np.zeros((len(pham_vi_rho), len(pham_vi_theta)), dtype=np.uint64)

    # Lấy các điểm không phải zero trong ảnh biên
    diem_lan_can = np.argwhere(anh_lan_can > 0)

    # Duyệt qua từng điểm biên
    for y, x in diem_lan_can:
        for chi_so_theta, theta in enumerate(np.deg2rad(pham_vi_theta)):
            rho = int(x * np.cos(theta) + y * np.sin(theta))
            chi_so_rho = np.argmin(np.abs(pham_vi_rho - rho))
            mang_tich_luy[chi_so_rho, chi_so_theta] += 1

    # Lấy các tham số của đường thẳng có số phiếu bầu cao
    duong_thang_phat_hien = []
    for chi_so_rho in range(len(pham_vi_rho)):
        for chi_so_theta in range(len(pham_vi_theta)):
            if mang_tich_luy[chi_so_rho, chi_so_theta] > nguong:
                rho = pham_vi_rho[chi_so_rho]
                theta = np.deg2rad(pham_vi_theta[chi_so_theta])
                duong_thang_phat_hien.append((rho, theta))

    return duong_thang_phat_hien

# Ví dụ sử dụng
if __name__ == "__main__":
    # Đọc ảnh
    image = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE)

    # Phát hiện biên
    anh_lan_can = cv2.Canny(image, 50, 150)

    # Thực hiện biến đổi Hough
    nguong = 100  # Ngưỡng bộ tích lũy
    duong_thang = bien_doi_hough(image, anh_lan_can, nguong)

    # Vẽ các đường thẳng đã phát hiện
    plt.figure(figsize=(8, 6))
    plt.imshow(cv2.cvtColor(image, cv2.COLOR_GRAY2RGB))
    plt.title('Đường thẳng đã phát hiện')
    plt.axis('off')

    for rho, theta in duong_thang:
        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()

Giải thích chi tiết

  1. Phát hiện biên:
    anh_lan_can = cv2.Canny(image, 50, 150)
    Sử dụng bộ lọc Canny để lấy ảnh biên nhị phân của hình ảnh đầu vào.
  2. Khởi tạo không gian tham số:
    mang_tich_luy = np.zeros((len(pham_vi_rho), len(pham_vi_theta)), dtype=np.uint64)
    Tạo một mảng (bộ tích lũy) để lưu trữ số phiếu bầu cho từng cặp tham số (rho, theta).
  3. Lấp đầy bộ tích lũy:
    for y, x in diem_lan_can:
        for chi_so_theta, theta in enumerate(np.deg2rad(pham_vi_theta)):
            rho = int(x * np.cos(theta) + y * np.sin(theta))
            chi_so_rho = np.argmin(np.abs(pham_vi_rho - rho))
            mang_tich_luy[chi_so_rho, chi_so_theta] += 1
    Duyệt qua từng điểm biên, tính toán các tham số (rho, theta) có thể của đường thẳng đi qua điểm đó, và tăng giá trị tương ứng trong bộ tích lũy.
  4. Tìm các đường thẳng có số phiếu bầu cao:
    if mang_tich_luy[chi_so_rho, chi_so_theta] > nguong:
        rho = pham_vi_rho[chi_so_rho]
        theta = np.deg2rad(pham_vi_theta[chi_so_theta])
        duong_thang_phat_hien.append((rho, theta))
    Tìm các vị trí trong bộ tích lũy có giá trị vượt quá ngưỡng, các vị trí này đại diện cho các tham số của các đường thẳng đã phát hiện.
  5. Vẽ các đường thẳng:
    for rho, theta in duong_thang:
        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)
    Sử dụng thư viện Matplotlib để vẽ các đường thẳng đã phát hiện lên ảnh gốc.

Ưu và nhược điểm

  • Ưu điểm:
    • Phù hợp để phát hiện các đường thẳng, đơn giản và hiệu quả.
    • Không phụ thuộc vào độ dài và vị trí của đường thẳng: Phù hợp để phát hiện các đường thẳng theo mọi hướng.
  • Nhược điểm:
    • Nhạy cảm với việc chọn tham số: Việc chọn các tham số (như ngưỡng) sẽ ảnh hưởng đến kết quả, cần phải điều chỉnh cho phù hợp với từng trường hợp cụ thể.
    • Tính toán phức tạp: Cần duyệt qua từng điểm biên và thực hiện tích lũy trong không gian tham số, yêu cầu tài nguyên tính toán cao.

Biến đổi Hough Thông thường

Biến đổi Hough thông thường (Standard Hough Transform) là một phương pháp cơ bản và hiệu quả để phát hiện các đường thẳng trong ảnh. Kỹ thuật này hoạt động bằng cách ánh xạ các điểm pixel trong không gian ảnh sang không gian tham số (gọi là không gian Hough), sau đó tích lũy các điểm này để tìm ra các đường thẳng có nhiều điểm nhất.

Các bước của thuật toán

  1. Phát hiện biên: Đầu tiên, thực hiện phát hiện biên trên ảnh, thường sử dụng bộ lọc Canny để lấy ảnh biên nhị phân.
  2. Khởi tạo không gian tham số: Dựa trên kích thước ảnh và phạm vi góc, khởi tạo không gian tham số cho biến đổi Hough. Đối với việc phát hiện đường thẳng, thường sử dụng biểu diễn tọa độ cực: bán kính (r) và góc (θ).
  3. Lấp đầy bộ tích lũy: Duyệt qua từng điểm pixel trong ảnh biên, tính toán các tham số đường thẳng có thể cho mỗi điểm biên và tăng giá trị trong bộ tích lũy Hough.
  4. Tìm các đỉnh cao trong bộ tích lũy: Tìm các điểm có giá trị cao trong bộ tích lũy, các điểm này đại diện cho các tham số đường thẳng (r, θ) có thể.
  5. Ánh xạ ngược đường thẳng về không gian ảnh: Chuyển các tham số đường thẳng (r, θ) từ không gian tham số về không gian ảnh gốc và vẽ các đường thẳng đã phát hiện.

Cài đặt Python

Dưới đây là ví dụ về cài đặt Python cho biến đổi Hough thông thường sử dụng thư viện OpenCV.


import cv2
import numpy as np

def bien_doi_hough_opencv(image_path, nguong):
    """
    Sử dụng hàm có sẵn trong OpenCV để phát hiện đường thẳng bằng biến đổi Hough.

    Tham số:
    image_path (str): Đường dẫn đến ảnh đầu vào
    nguong (int): Ngưỡng bộ tích lũy

    Trả về:
    list: Danh sách các đường thẳng đã phát hiện
    """
    # Đọc ảnh và chuyển sang ảnh xám
    img = cv2.imread(image_path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # Phát hiện biên
    edges = cv2.Canny(gray, 50, 150, apertureSize=3)

    # Sử dụng hàm HoughLines của OpenCV
    lines = cv2.HoughLines(edges, 1, np.pi/180, nguong)

    return lines, img, edges

# Ví dụ sử dụng
if __name__ == "__main__":
    # Đường dẫn ảnh
    image_path = 'example.jpg'
    nguong = 150

    # Thực hiện biến đổi Hough
    lines, original_img, canny_edges = bien_doi_hough_opencv(image_path, nguong)

    # Vẽ các đường thẳng đã phát hiện lên ảnh gốc
    if lines is not None:
        for line in lines:
            rho, theta = line[0]
            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))
            cv2.line(original_img, (x1, y1), (x2, y2), (0, 0, 255), 2)

    # Hiển thị kết quả
    cv2.imshow('Original Image with Lines', original_img)
    cv2.imshow('Canny Edges', canny_edges)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

Giải thích chi tiết

  1. Phát hiện biên:
    edges = cv2.Canny(gray, 50, 150, apertureSize=3)
    Sử dụng bộ lọc Canny để lấy ảnh biên nhị phân của ảnh xám.
  2. Sử dụng hàm HoughLines của OpenCV:
    lines = cv2.HoughLines(edges, 1, np.pi/180, nguong)
    Gọi hàm `HoughLines` có sẵn trong OpenCV, truyền vào ảnh biên, độ phân giải của rho (1 pixel), độ phân giải của theta (1 độ), và ngưỡng bộ tích lũy. Hàm này trả về danh sách các đường thẳng.
  3. Vẽ các đường thẳng:
    for line in lines:
        rho, theta = line[0]
        # Tính toán các điểm để vẽ đường thẳng
        cv2.line(original_img, (x1, y1), (x2, y2), (0, 0, 255), 2)
    Duyệt qua danh sách các đường thẳng, tính toán các điểm đầu và cuối của đường thẳng và vẽ chúng lên ảnh gốc bằng hàm `cv2.line`.

Ưu và nhược điểm

  • Ưu điểm:
    • Tích hợp sẵn trong các thư viện xử lý ảnh phổ biến như OpenCV, dễ sử dụng.
    • Hiệu quả cao và đã được tối ưu hóa.
  • Nhược điểm:
    • Ít linh hoạt hơn so với việc triển khai thủ công, khó tùy chỉnh các tham số bên trong.
    • Cần hiểu rõ các tham số đầu vào để đạt được kết quả tốt.

Thẻ: xử lý ảnh opencv python Hough Transform biến đổi Hough

Đăng vào ngày 23 tháng 5 lúc 02:41