Phép Tích Chập trong Xử Lý Ảnh (Convolution)

Convolution là kỹ thuật quan trọng trong Xử Lý Hình ảnh, được tận dụng cốt tử trong những phép toán trên ảnh như: đạo hàm ảnh, làm trơn ảnh, trích xuất biên cạnh trong ảnh. Trong nội dung bài viết này, tôi trình diễn về kỹ thuật tích chập trong nghành xử lý ảnh và cách hiện thực nó tận dụng từ ngữ C++ có tương trợ của thư viện openCV.

Convolution

Khái niệm

Theo toán học, tích chập là phép toán tuyến tính, cho ra thành quả là một hàm bằng việc tính toán dựa trên hai hàm đã được (f và g).

Ví dụ: so với phép lọc ảnh, phép tích chập giữa ma trận lọc và ảnh, cho ra thành quả ảnh đã được xoá nhiễu (làm mờ). Tìm hiểu thêm nội dung bài viết Giới Thiệu Ứng Dụng Của Làm Mờ Hình ảnh (Lọc Nhiễu) Trong Bài Toán Nhận Dạng.

Công thức tích chập giữa hàm ảnh f(x, y) và bộ lọc k(x, y) (kích thước mxn):

Thành phần không thể thiếu của phép tích chập là ma trận kernel (bộ lọc). Điểm neo (anchor point) của kernel sẽ quyết định vùng ma trận tương ứng trên ảnh để tích chập, thường thì anchor point được chọn là tâm của kernel. Giá trị mỗi thành phần trên kernel được xem như thể hệ số tổ phù hợp với thứu tự từng giá trị độ xám của điểm ảnh trong vùng tương ứng với kernel.

Phép tích chập được tưởng tượng triển khai bằng việc dịch chuyển ma trận kernel thứu tự qua tất cả những điểm ảnh trong ảnh, chính thức từ góc bên trái trên của ảnh. Và đặt anchor point tương ứng tại điểm ảnh đang xét. Ở mỗi lần dịch chuyển, triển khai tính toán thành quả mới cho điểm ảnh đang xét bằng công thức tích chập.

Xem minh hoạ triển khai: Hình ảnh minh hoạ theo trật tự từ trái qua phải và từ trên xuống dưới. Hình ảnh sau cùng là thành quả sau khoản thời gian triển khai dịch rời kernel hết toàn bộ ảnh. Ký hiệu: (1) ảnh nguồn, (2) kernel, (3) ảnh thành quả.

Để dễ hiểu, chúng ta có thể xoay ma trận kernel góc 180 độ theo chiều kim đồng hồ đeo tay, tiếp sau đó thành quả tích chập đó là tổng những tích của hai thành phần cùng vị trí nằm trên kernel và trên ảnh.

Một số trong những cách xử lý vùng kernel vượt ra ngoài khỏi ảnh:

  • Bỏ qua, không triển khai tính thành phần đó vào thành quả.
  • Tận dụng một hằng số để tính toán.
  • Duplicate px nằm ở biên của ảnh.

Trong nội dung bài viết này, tôi tận dụng phương án xử lý bỏ qua vùng ở ngoài biên, không cho vào thành quả.

Tính chất

Tích chập được khái niệm là một trong phép toán trên không khí khả tích của những hàm tuyến tính, vì thế nó có tính chất giao hoán, phối kết hợp và phân phối.

  • Giao hoán: f * g = g * f
  • Phối kết hợp: f * g * h = f * (g * h)
  • Phân phối: f * g + f * h = f * (g + h)

Do tính chất phối kết hợp của phép tích chập, khi một phép xử lý ảnh yêu cầu triển khai tích chập liên tục với nhiều bộ lọc (kernel) f * g * h. Ta hoàn toàn có thể tính toán trước ma trận kernel để “giảm độ phức tạp tính toán” k = v * h do kích thước ma trận kernel hầu như rất nhỏ so với ảnh. Lúc này, thay vì triển khai tích chập theo trật tự r = (f * g) * h, ta triển khai r = f * (v * h) = f * k.

Ký hiệu:

  • – f: hàm ảnh
  • – g: bộ lọc thứ nhất
  • – h: bộ lọc thứ hai
  • – r: hàm ảnh thành quả

Tối ưu triển khai

Convolution vẫn còn đó là một kỹ thuật với độ phức tạp tính toán cao. Một số trong những cách sau đây hoàn toàn có thể tối ưu vận tốc của convolution:

  • Mỗi thành phần trong ma trận kernel nên là số nguyên: như trong ví dụ trên, những thành phần trong kernel thực ra là số thực, tuy nhiên, tôi triển khai chuyển sang ma trận số nguyên với số hạng chung cho tất cả những thành phần, thành quả tích chập sẽ nhân cho số hạng chung này.
  • Kernel nên triển khai lưu trong mảng một chiều.
  • Tạo ma trận chỉ số truy vấn nhanh, với cách này hoàn toàn có thể truy vấn nhanh đến px trên ảnh, tương ứng với kernel mà không cần tính toán chỉ số thêm nữa.Ví dụ, với kernel (size: 3×3, anchor point: center)

(-1, -1) (-1, 0) (-1, 1) (0, -1) (0, 0) (0, 1) (1, -1) (1, 0) (1, 1)

Hiện thực hoá với code

Code chỉ triển khai với ảnh xám

void Convolution::doConvolution(Matandamp; sourceImage, Matandamp; destinationImage) { int nr = sourceImage.rows; int nc = sourceImage.cols; // Tạo matrix để lưu giá trị px sau khoản thời gian triển khai tích chập. destinationImage.create(Size(nc, nr), CV_8UC1); // Đi thứu tự từng px của ảnh nguồn. for (int i = 0; i < nr; i ++) { // Lấy địa chỉ dòng của ảnh đích, để lưu thành quả vào. uchar* data = destinationImage.ptrandlt;ucharandgt;(i); for (int j = 0; j < nc; j ++) { // Lưu tổng vốn độ xám của vùng ảnh tương ứng với kernel int g_val = 0; // Duyệt mask, giá trị px đích là tổng hợp tuyến tính của mask với ảnh nguồn. for (int ii = 0; ii < _kernel.size(); ii ++) // Gán giá trị cho matrix đích. data[j] = g_val; } } }

You May Also Like

About the Author: v1000