Review Phương pháp sử dụng cho xử lý ảnh ✅

Thủ Thuật Hướng dẫn Phương pháp sử dụng cho xử lý ảnh Chi Tiết

Gan Feng Du đang tìm kiếm từ khóa Phương pháp sử dụng cho xử lý ảnh được Update vào lúc : 2022-09-29 18:48:22 . Với phương châm chia sẻ Bí quyết về trong nội dung bài viết một cách Chi Tiết Mới Nhất. Nếu sau khi đọc nội dung bài viết vẫn ko hiểu thì hoàn toàn có thể lại Comments ở cuối bài để Tác giả lý giải và hướng dẫn lại nha.

Bài 21 - Tiền xử lý ảnh OpenCV

06 Jan 2022 - phamdinhkhanh

Nội dung chính
    Bài 21 - Tiền xử lý ảnh OpenCV1. Vai trò của tiền xử lý ảnh2. Tiền xử lý ảnh2.1. Các biến hóa hình học.2.1.1. Phóng đại ảnh (Scale ảnh)2.1.2. Dịch chuyển ảnh (Translation)2.1.2. Xoay ảnh (Rotation)2.1.3. Biến đổi Affine2.1.4. Biến đổi phối cảnh (Perspective Transform)2.2. Làm mịn ảnh (smoothing images)2.2.1. Bộ lọc tích chập 2D (2D convolution)2.2.2. Làm mờ ảnh (Image blurring)2.3. Phương pháp Canny phát hiện edge2.4. Contour2.4.1. Xác định những contour2.4.2. Các đặc trưng của contour2.4.3. Bounding box2.4.5. Các thuộc tính của contour2.4.6. Tìm bounding box trong Object detection3. Kết luận4. Tài liệu

1. Vai trò của tiền xử lý ảnh

Khi phát triển một thuật toán phân loại ảnh tất cả chúng ta hoàn toàn có thể gặp phải một số trong những trường hợp không mong đợi như: Kết quả huấn luyện có độ đúng chuẩn rất cao trên cả tập huấn luyện (train dataset) và tập phát triển (dev dataset). Nhưng khi áp dụng vào thực tiễn lại cho độ đúng chuẩn thấp. Có rất nhiều những nguyên nhân dẫn tới điều này và một trong số đó là:

    Các tấm hình được huấn luyện khác xa so với những tấm hình được người tiêu dùng upload về những khía cạnh: độ phân giải, cường độ sắc tố, chất lượng ảnh, độ to nhỏ của vật thể, chiều, hướng và tư thế của vật thể bên trong ảnh.

    Có thể những tấm hình được người tiêu dùng upload lên tuy nhiên cùng nhãn nhưng khác về tính chất so với những tấm hình đã huấn luyện. Ví dụ trong một thuật toán phân loại dog and cat, tập huấn luyện chỉ gồm có những con mèo trưởng thành nhưng thực tế người tiêu dùng lại upload lên rất nhiều hình ảnh của mèo con hoàn toàn có thể dẫn tới thuật toán bị nhầm lẫn.

    Đối với một số trong những tác vụ phân loại ảnh khó, đòi hỏi Chuyên Viên gán nhãn, rất dễ mắc sai lầm như chuẩn đoán bệnh nhãn cầu. Một số ít những ảnh trong tập huấn luyện hoàn toàn có thể bị gán sai nhãn. Do đó ảnh hưởng đến kĩ năng dự báo của thuật toán.

    Bộ tài liệu huấn luyện có kích thước quá nhỏ và không đại diện cho toàn bộ những class được huấn luyện.

    Phân phối của tập huấn luyện khác xa so với thực tế. Chẳng hạn tập huấn luyện chứa ảnh chó và mèo theo tỷ lệ 50:50 nhưng số lượng tấm hình người tiêu dùng upload lên ảnh chó chiếm đa số theo tỷ lệ 90:10. …

Và rất nhiều những nguyên nhân khác dẫn tới thuật toán hoạt động và sinh hoạt giải trí không được như kì vọng.

Khi đối mặt với trường hợp trên tất cả chúng ta nên phải tìm ra nguyên nhân thực sự là gì để từ đó đưa ra phương án thích hợp khắc phục những lỗi quy mô.

Các kĩ thuật để xử lý và xử lý những trường hợp lỗi như vậy đã được tổng hợp rất kĩ trong cuốn Khao khát học máy - Andrew Ng. Hiện tại sách đã được nhóm anh Tiệp và hiệp hội dịch ra Tiếng Việt khá đầy đủ. Bạn đọc hoàn toàn có thể đọc từ chương 1 đến 16 để tìm hiểu phương pháp xây dựng và phát triển những quy mô thông qua kinh nghiệm tay nghề được tổng kết của tác giả.

Trong cuốn sách tác giả nêu ra nhiều hướng giải quyết như: Thay đổi tập tài liệu huấn luyện và tập tài liệu phát triển, thống kê lỗi và tìm cách xử lý và xử lý những lỗi chính mang lại cải tổ lớn, xác định tập huấn luyện/phát triển và phép đo đơn trị thích hợp ngay từ đầu cho bài toán, áp dụng những phương pháp và kiến trúc quy mô rất khác nhau,…

Trong trường hợp tài liệu không đủ lớn, tài liệu gán nhãn với ngân sách cao (như chuẩn đoán bệnh qua hình ảnh, phải tìm được bệnh nhân gặp đúng bệnh đó và bác sĩ chuyên khoa để chuẩn đoán), việc thay đổi tập tài liệu là khá tốn ngân sách. Có một phương pháp mà mình nghĩ rằng sẽ giúp ngày càng tăng số lượng ảnh đầu vào. Đó đó đó là học tăng cường (data augumentation) sử dụng những biến hóa tiền xử lý hình ảnh đầu vào. Đây là một phương pháp hiệu suất cao nhằm mục đích thay đổi tập tài liệu huấn luyện và từ đó giúp cải tổ hiệu suất cao dự báo.

Vậy tiền xử lý tài liệu ảnh là gì và có những phương pháp nào để thực hiện tiền xử lý ảnh, nội dung bài viết này sẽ ra mắt tới những bạn một series những phương pháp tiếp cận rất khác nhau của tiền xử lý ảnh trên cả hai phương diện lý thuyết phối hợp thực hành. Các code sẽ được thực hiện trên opencv, một package khá tiện ích trong xử lý ảnh. Chúng ta khởi đầu nhé.

2. Tiền xử lý ảnh

2.1. Các biến hóa hình học.

Đây là tập hợp những phép biến hóa hình ảnh từ một hình dạng này sang một hình dạng khác thông qua việc làm thay đổi phương, chiều, góc, cạnh mà không làm thay đổi nội dung chính của tấm hình. Về mặt lý thuyết toán học một phép biến hóa được định nghĩa như sau:

Định nghĩa:

Mỗi một phép biến hóa hình học sẽ được xác định bởi một ma trận dịch chuyển (translation matrix) $mathbfM$. Khi đó bất kì 1 điểm có tọa độ $(x, y)$ trên ảnh gốc thông qua phép biến hóa $T$ sẽ có tọa độ trong không khí mới sau dịch chuyển là $T(x, y)$ theo công thức:

[T(x, y) = mathbfM beginbmatrix x \ yendbmatrix = beginbmatrix a_11 & a_12 \ a_21 & a_22endbmatrixbeginbmatrix x \ yendbmatrix = beginbmatrix a_11 x + a_12 y \ a_21 x + a_22 yendbmatrix]

Một hàm số $T: mathbbR^n rightarrow mathbbR^n$ được xem là một phép biến hóa tuyến tính nếu nó thỏa mãn 2 tính chất sau:

    Tính chất cộng tính: (T(vecu + vecv) = T(vecu) + T(vecv))Tính chất nhân tính: (T(lambda vecx) = lambda T(vecx))

Phương pháp sử dụng cho xử lý ảnh

Hình 1: Tính chất cộng tính của phép biến hóa tuyến tính. Ta nhận thấy tính chất này hoàn toàn hoàn toàn có thể được suy ra trực tiếp từ phép nhân ma trận $mathbfM(mathbfA+mathbfB) = mathbfMmathbfA+mathbfMmathbfB$. Trong số đó $mathbfM$ là ma trận biến hóa và $mathbfA, mathbfB$ là những tọa độ điểm.

Như vậy tổng kết lại, để xác định một phép biến hóa hình học ta sẽ nên phải xác định được ma trận dịch chuyển của nó là gì? Các dạng biển đổi sẽ được trình bày phía dưới sẽ được đặc trưng bởi những dạng ma trận dịch chuyển rất khác nhau.

2.1.1. Phóng đại ảnh (Scale ảnh)

Scale ảnh là việc tất cả chúng ta thay đổi kích thước dài, rộng của ảnh mà không làm thay đổi tính chất song song của những đoạn thẳng trên ảnh gốc so với những trục tọa độ X và Y. Trong opencv, tất cả chúng ta sẽ thay đổi kích thước của hình ảnh bằng hàm cv2.resize().

Theo định nghĩa về phép biến hóa hình học thì một biến hóa phóng đại những chiều (x, y) theo hệ số (a_1, a_2) sẽ có ma trận dịch chuyển M là ma trận đường chéo. Tức là ma trận vuông có đường chéo đó đó là $[a_1, a_2]$ và những phần tử còn sót lại bằng 0. Khi đó phép dịch chuyển sẽ là:

[T(x, y) = mathbfM beginbmatrix x \ yendbmatrix = beginbmatrix a_1 & 0 \ 0 & a_2endbmatrix beginbmatrix x \ yendbmatrix = beginbmatrix a_1 x \ a_2 yendbmatrix]

Hàm _downloadImage() sẽ có tác dụng tải và convert ảnh sang numpy array từ đầu vào là link url của ảnh. Bạn đọc lưu ý, hàm này sẽ được sử dụng xuyên suốt bài hướng dẫn.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import cv2 import numpy as np from PIL import Image import requests from io import BytesIO import matplotlib.pyplot as plt url = 'https://i.imgur.com/1vzDG2J.jpg' def _downloadImage(url): resp = requests.get(url) img = np.asarray(bytearray(resp.content), dtype="uint8") img = cv2.imdecode(img, cv2.IMREAD_COLOR) return img img = _downloadImage(url) print('origin image shape: '.format(img.shape)) # Scale image bằng phương pháp gấp hai width and height h, w = img.shape[:2] imgScale = cv2.resize(img, (int(w*2), int(h*2)), interpolation = cv2.INTER_LINEAR) print('scale image shape: '.format(imgScale.shape)) plt.subplot(121),plt.imshow(imgScale),plt.title('Origin Image') plt.subplot(122),plt.imshow(imgScale),plt.title('Scale Image')

1 2 origin image shape: (709, 621, 3) scale image shape: (1418, 1242, 3)

Phương pháp sử dụng cho xử lý ảnh

Như vậy tấm hình đã được resize về một kích thước gấp hai cả về chiều width và height nhưng không làm nội dung ảnh thay đổi. Resize ảnh rất thường xuyên được sử dụng trong những quy mô deep learning phân loại ảnh vì mỗi một quy mô đều có một kích thước đầu vào tiêu chuẩn. Chẳng hạn như Lenet là kích thước 32x32x3 và Alexnet là 224x224x3.

2.1.2. Dịch chuyển ảnh (Translation)

Dịch chuyển ảnh thường được thực hiện trong trường hợp bạn muốn dịch chuyển ảnh đến những vị trí rất khác nhau. Ví dụ tới những góc trái, phải, ở giữa, phía trên, phía dưới. Phép dịch chuyển sẽ không thay đổi tính chất song song của những đoạn thẳng sau dịch chuyển đối với những trục X hoặc Y nếu trước dịch chuyển chúng cũng song song với một trong hai trục này. Để dịch chuyển hình ảnh tất cả chúng ta phải xác định được $(t_x, t_y)$ là những giá trị di tán ảnh theo trục $x$ và $y$. Ma trận dịch chuyển $mathbfM$ sẽ có dạng như phía dưới:

[mathbfM = beginbmatrix 1 & 0 & t_x \ 0 & 1 & t_yendbmatrix]

Thật vậy. Giả sử mọi điểm ảnh đều nằm trên không khí 2 chiều. Khi đó ta coi chiều thứ 3 là một hằng số, ví dụ điển hình $z=1$. Khi đó phép biến hóa $(x, y)$ bất kì theo ma trận dịch chuyển $mathbfM$ sẽ là:

[T(x, y) = mathbfM beginbmatrix x \ yendbmatrix = beginbmatrix 1 & 0 & t_x \ 0 & 1 & t_yendbmatrix beginbmatrix x \ y \ 1endbmatrix = beginbmatrix x + t_x \ y + t_yendbmatrix]

Như vậy mỗi điểm tọa độ $(x, y)$ đã được dịch chuyển tới một tọa độ mới là $(x+t_x, y+t_y)$

Trong opencv, Áp dụng hàm cv2.warpAffine() với đầu vào là ma trận dịch chuyển $mathbfM$ và tấm hình gốc ta thu được kết quả là ảnh sau dịch chuyển.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 rows, cols = img.shape[:2] # Dịch chuyển hình ảnh xuống góc dưới bên phải tx, ty = (200, 200) M1 = np.array([[1, 0, tx], [0, 1, ty]], dtype=np.float32) tran1 = cv2.warpAffine(img, M1, (cols, rows)) # Dịch chuyển hình ảnh xuống góc dưới bên trái M2 = np.array([[1, 0, -tx], [0, 1, ty]], dtype=np.float32) tran2 = cv2.warpAffine(img, M2, (cols, rows)) # Dịch chuyển hình ảnh xuống góc dưới bên trái M3 = np.array([[1, 0, tx], [0, 1, -ty]], dtype=np.float32) tran3 = cv2.warpAffine(img, M3, (cols, rows)) # Dịch chuyển hình ảnh xuống góc dưới bên trái M4 = np.array([[1, 0, -tx], [0, 1, -ty]], dtype=np.float32) tran4 = cv2.warpAffine(img, M4, (cols, rows)) plt.figure(figsize=(16, 4)) plt.subplot(151),plt.imshow(img),plt.title('Origin Image') plt.subplot(152),plt.imshow(tran1),plt.title('Translate to Bottom Right') plt.subplot(153),plt.imshow(tran2),plt.title('Translate to Bottom Left') plt.subplot(154),plt.imshow(tran3),plt.title('Translate to Up Right') plt.subplot(155),plt.imshow(tran4),plt.title('Translate to Up Left')

Phương pháp sử dụng cho xử lý ảnh

2.1.2. Xoay ảnh (Rotation)

Xoay ảnh được hiểu là ta quay một tấm hình theo một góc xác định quanh một điểm nào đó. Phép xoay sẽ không đảm bảo tính chất song song với những trục X hoặc Y như phép dịch chuyển nhưng nó sẽ bảo toàn độ lớn góc. Nếu 3 điểm bất kì tại ảnh gốc tạo thành một tam giác thì khi biến hóa qua phép xoay ảnh, chúng sẽ tạo thành một tam giác đồng dạng với tam giác ban đầu. Phép xoay của một hình ảnh tương ứng với một góc $theta$ đạt được bằng một ma trận dịch chuyển $mathbfM$ như sau:

[mathbfM = beginbmatrix cos(theta) & -sin(theta) \ sin(theta) & cos(theta)endbmatrix]

Ngoài ra OpenCV tương hỗ một phép xoay phóng đại (scaled rotation) với kĩ năng vừa biến hóa ảnh theo phép xoay theo tâm xác định và điều chỉnh lại kích thước ảnh sau xoay. Như vậy bạn hoàn toàn có thể xoay theo bất kì vùng nào mà bạn ưa chuộng hơn. Phép dịch chuyển ma trận được đưa ra như sau:

[beginbmatrix alpha & beta & (1-alpha)c_x-beta c_y \ -beta & alpha & beta c_x+(1-alpha)c_yendbmatrix]

trong đó:

[alpha = scale.cos(theta) \ beta = scale.sin(theta)]

$(c_x, c_y)$ là tọa độ tâm của phép xoay và $scale$ là độ phóng đại.

Để tìm ra ma trận transform hoàn toàn có thể sử dụng hàm cv2.getRotationMatrix2D() của OpenCV. Trong hàm này tất cả chúng ta cần xác định tâm của phép xoay (đối số center), góc xoay (angle) và tham số phóng đại kích thước ảnh (scale).

Bên dưới là một ví dụ xoay ảnh kích thước 45 độ tại tâm của ảnh.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 # Xoay ảnh kích thước 45 độ tại tâm của ảnh, độ phóng đại ảnh không đổi. M5 = cv2.getRotationMatrix2D(center = (cols/2,rows/2), angle=-45, scale=1) tran5 = cv2.warpAffine(img, M5, (cols,rows)) # Xoay ảnh kích thước 45 độ tại tâm của ảnh và độ phóng đại giảm 1/2 M6 = cv2.getRotationMatrix2D(center = (cols/2,rows/2), angle=-45, scale=0.5) tran6 = cv2.warpAffine(img, M6, (cols,rows)) # Xoay ảnh kích thước -45 độ tại tâm của ảnh M7 = cv2.getRotationMatrix2D(center = (cols/2,rows/2), angle=45, scale=1) tran7 = cv2.warpAffine(img, M7, (cols,rows)) # Xoay ảnh kích thước 20 độ tại góc trên bên trái M8 = cv2.getRotationMatrix2D(center = (0, 0), angle=-20, scale=1) tran8 = cv2.warpAffine(img, M8, (cols,rows)) # Xoay ảnh kích thước 20 độ tại góc dưới bên phải M9 = cv2.getRotationMatrix2D(center = (cols,rows), angle=-20, scale=1) tran9 = cv2.warpAffine(img, M9, (cols,rows)) plt.figure(figsize=(12, 8)) plt.subplot(231),plt.imshow(img),plt.title('Origin Image') plt.subplot(232),plt.imshow(tran5),plt.title('Rotate 45 centroid') plt.subplot(233),plt.imshow(tran6),plt.title('Rotate 45 resize 0.5') plt.subplot(234),plt.imshow(tran7),plt.title('Rotate -45 centroid') plt.subplot(235),plt.imshow(tran8),plt.title('Rotate 20 upper left corner') plt.subplot(236),plt.imshow(tran9),plt.title('Rotate 20 bottom right corner')

Phương pháp sử dụng cho xử lý ảnh

2.1.3. Biến đổi Affine

Trong biến hóa affine, toàn bộ những đường thẳng song song trong tấm hình gốc không thay đổi tính chất song song ở ảnh đầu ra. Để tìm ma trận chuyển vị, tất cả chúng ta cần xác định ra 3 điểm từ ảnh đầu vào và tọa độ tương ứng của chúng trong hình ảnh đầu ra. Hàm cv2.getAffineTransform sẽ tạo ra được một ma trận 2x3 được truyền vào hàm cv2.warpAffine.

Kiểm tra ví dụ phía dưới, tất cả chúng ta cùng nhìn vào những điểm mà tôi lựa chọn (được đánh dấu x, white color).

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 rows,cols,ch = img.shape pts1 = np.float32([[50,50], [200,50], [50,200]]) # pts2 = np.float32([[50,100], [200,50], [50,200]]) pts2 = np.float32([[50,300], [200,150], [150, 400]]) M = cv2.getAffineTransform(pts1,pts2) imageAffine = cv2.warpAffine(img,M,(cols,rows)) # Hiển thị hình ảnh gốc và 3 điểm ban đầu trên ảnh plt.subplot(121),plt.imshow(img),plt.title('Input') for (x, y) in pts1: plt.scatter(x, y, s=50, c="white", marker="x") # Hiển thị hình ảnh sau dịch chuyển và 3 điểm tiềm năng của phép dịch chuyển. plt.subplot(122),plt.imshow(imageAffine),plt.title('Output') for (x, y) in pts2: plt.scatter(x, y, s=50, c="white", marker="x")

Phương pháp sử dụng cho xử lý ảnh

Như vậy sử dụng phép biến hóa Affine hoàn toàn có thể giúp ta tạo thành nhiều biến thể, tư thế rất khác nhau cho cùng một vật thể. Thường được áp dụng trong Data Augumentation để làm giàu tài liệu trong trường hợp số lượng ảnh không nhiều nếu không muốn nói là rất ít. Chẳng hạn với ảnh mặt người đang chụp nghiêng ta hoàn toàn có thể xoay theo những chiều sao cho từ mặt nghiêng trở thành mặt chính diện hoặc như trong hình trên, hình ảnh messi đang chạy dáng nghiêng đã được chuyển sang chạy dáng thẳng đứng và nghiêng với những độ lớn góc rất khác nhau.

Một số nghiên cứu và phân tích chỉ ra rằng khi áp dụng phương pháp Data Augumentation thì độ đúng chuẩn của thuật toán với số lượng ảnh huấn luyện nhỏ hoàn toàn có thể không thua kém những thuật toán được huấn luyện trên nhiều tài liệu hơn về độ đúng chuẩn.

2.1.4. Biến đổi phối cảnh (Perspective Transform)

Để biến hóa phối cảnh thì tất cả chúng ta cần một ma trận biến hóa 3x3. Đường thẳng sẽ không thay đổi là đường thẳng sau biến hóa. Để tìm ra ma trận biến hóa này, tất cả chúng ta cần tìm ra 4 điểm trong ảnh đầu vào tương ứng với những điểm trong ảnh đầu ra. Trong số 4 điểm này, không còn bất kì 3 điểm nào thẳng hàng. Sau đó ma trận biến hóa hoàn toàn có thể được thiết lập thông qua hàm số cv2.getPerspectiveTransform. Và áp dụng cv2.warpPerspective với ma trận biến hóa 3x3.

Xem code phía dưới.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 pts1 = np.float32([[50,50],[350,50],[50,350],[350,350]]) pts2 = np.float32([[0,0],[200,50],[50,300],[300,300]]) M = cv2.getPerspectiveTransform(pts1,pts2) dst = cv2.warpPerspective(img,M,(300,300)) plt.subplot(121),plt.imshow(img),plt.title('Input') for (x, y) in pts1: plt.scatter(x, y, s=50, c="white", marker="x") plt.subplot(122),plt.imshow(dst),plt.title('Perspective Transform') for (x, y) in pts2: plt.scatter(x, y, s=50, c="white", marker="x") plt.show()

Phương pháp sử dụng cho xử lý ảnh

Như vậy phép biến hóa này cũng gần in như phép biến hóa Affine. Khác biệt đó là nó chỉ trả ra tấm hình là biến hóa trên vùng ảnh bị số lượng giới hạn trong tọa độ của 4 điểm gốc thay vì biến hóa trên toàn bộ tấm hình ban đầu như phép biến hóa Affine. Trong trường hợp muốn crop ảnh ta sẽ xác định trước tọa độ của 4 góc và sử dụng phép biến hóa phối cảnh giữa 4 điểm với chính những điểm đó.

1 2 3 4 5 6 7 8 9 10 11 pts1 = np.float32([[50,50],[350,50],[50,350],[350,350]]) M = cv2.getPerspectiveTransform(pts1,pts1) dst = cv2.warpPerspective(img,M,(300,300)) plt.subplot(121),plt.imshow(img),plt.title('Input') for (x, y) in pts1: plt.scatter(x, y, s=50, c="white", marker="x") plt.subplot(122),plt.imshow(dst),plt.title('Crop Image') plt.show()

Phương pháp sử dụng cho xử lý ảnh

2.2. Làm mịn ảnh (smoothing images)

Chúng ta hoàn toàn có thể lọc nhiễu cho ảnh bằng bộ lọc tích chập 2 chiều (2D convolution), hoặc làm mờ ảnh bằng bộ lọc trung bình hoặc Gaussian Filtering.

2.2.1. Bộ lọc tích chập 2D (2D convolution)

Như đối với tín hiệu 1 chiều, những hình ảnh cũng khá được lọc với đa dạng những bộ lọc truyền dẫn thấp (low-pass filters LPF), bộ lọc truyền dẫn cao (high-pass fiters HPF). Một HPF sẽ giúp ta tìm ra những cạnh trong một hình ảnh, còn LPF sẽ lọc nhiễu cho ảnh và làm mờ ảnh.

OpenCV đưa ra một hàm cv2.filter2D() để tích chập một bộ lọc (kernel) với một hình ảnh. Chẳng hạn như phía dưới tất cả chúng ta sẽ thử lọc trung bình trên một tấm hình. thông qua phép nhân tích chập với một bộ lọc trung bình kích thước 5x5 như phía dưới.

[K = frac125 beginbmatrix 1 & 1 & 1 & 1 & 1 \ 1 & 1 & 1 & 1 & 1 \ 1 & 1 & 1 & 1 & 1 \ 1 & 1 & 1 & 1 & 1 \ 1 & 1 & 1 & 1 & 1 \ endbmatrix]

Khi đó mỗi một vùng ảnh cục bộ (local region) kích thước 5x5 trên ảnh gốc, những pixels sẽ được lấy giá trị bằng nhau và bằng trung bình của toàn bộ những pixels trên vùng ảnh. Dịch chuyển bộ lọc $K$ trên toàn bộ những vùng ảnh gốc như một phép tích chập 2 chiều thông thường ta sẽ được ảnh smoothing. Cụ thể như code phía dưới.

1 2 3 4 5 6 7 8 9 10 11 import cv2 import numpy as np kernel = np.ones((5,5),np.float32)/25 imgSmooth = cv2.filter2D(img,-1,kernel) plt.subplot(121),plt.imshow(img),plt.title('Original') plt.xticks([]), plt.yticks([]) plt.subplot(122),plt.imshow(imgSmooth),plt.title('Averaging') plt.xticks([]), plt.yticks([]) plt.show()

Phương pháp sử dụng cho xử lý ảnh

Ta thấy ảnh bên trái sắc nét, ảnh bên trái mờ hơn do đã lọc nhiễu.

2.2.2. Làm mờ ảnh (Image blurring)

Các ảnh mờ hoàn toàn có thể thu được thông qua phép tích chập hình ảnh với những bộ lọc LPF. Đây là những bộ lọc rất hữu ích trong vô hiệu nhiễu. Trên thực tế nó vô hiệu những nội dung tần số cao (ví dụ như nhiễu, những cạnh) khỏi hình ảnh dẫn đến những cạnh bị làm mờ khi bộ lọc được áp dụng. Có rất nhiều kĩ thuật làm mờ ảnh mà không làm mờ những cạnh. OpenCV đáp ứng 4 kĩ thuật làm mờ đa phần.

1. Trung bình (Average)

Tương tự như tích chập 2 chiều, chúng cũng sử dụng một ma trận vuông 2 chiều gồm toàn giá trị 1 để lấy trung bình trên những vùng cục bộ. Chúng ta hoàn toàn có thể thực hiện thông qua những hàm cv2.blur() và cv2.boxFilter(). Chẳng hạn như ta làm như phía dưới:

1 2 3 4 5 6 7 8 9 10 url="https://photo-2-baomoi.zadn/w1000_r1/2019_05_10_351_30668071/06856d2daf6c46321f7d.jpg" img = _downloadImage(url) blur = cv2.blur(img,(5,5)) plt.subplot(121),plt.imshow(img),plt.title('Original') plt.xticks([]), plt.yticks([]) plt.subplot(122),plt.imshow(blur),plt.title('Blurred') plt.xticks([]), plt.yticks([]) plt.show()

Phương pháp sử dụng cho xử lý ảnh

2. Bộ lọc Gausian

Bộ lọc gaussian được khởi tạo thông qua hàm cv2.GaussianBlur(). Chúng ta cần xác định độ lệch chuẩn theo 2 phương X và Y là $sigma_x$ và $sigma_y$. Bộ lọc Gaussian rất hiệu suất cao trong việc xóa bỏ noise khỏi hình ảnh.

1 2 3 4 5 6 7 gaussian_img = cv2.GaussianBlur(img, (5, 5), 0) plt.subplot(121),plt.imshow(img),plt.title('Original') plt.xticks([]), plt.yticks([]) plt.subplot(122),plt.imshow(gaussian_img),plt.title('Gaussian Blurred') plt.xticks([]), plt.yticks([]) plt.show()

Phương pháp sử dụng cho xử lý ảnh

Ngoài ra ta còn tồn tại những bộ lọc:

    median: Được khởi tạo thông qua hàm cv2.medianBlur(). Cũng tương tự như bộ lọc trung bình, nhưng thay vì tính mean thì ta tính toán những median của toàn bộ những pixels trên một vùng cục bộ và thay thế những điểm ảnh trên vùng cục bộ bằng median.

    bilateral: Được khởi tạo thông qua hàm cv2.bilateralFilter(). Các bộ lọc trình bày trước đó đã cho tất cả chúng ta biết có xu hướng làm mờ cạnh. Tuy nhiên bộ lọc nó lại chỉ remove nhiễu mà vẫn bảo tồn được những cạnh.

Bên dưới là kết quả áp dụng những bộ lọc này:

1 2 3 4 5 6 7 8 9 10 median_img = cv2.medianBlur(img, 5, 0) bilateral_img = cv2.bilateralFilter(img, 9, 75, 75) plt.subplot(131),plt.imshow(img),plt.title('Original') plt.xticks([]), plt.yticks([]) plt.subplot(132),plt.imshow(median_img),plt.title('Median Blurred') plt.xticks([]), plt.yticks([]) plt.subplot(133),plt.imshow(bilateral_img),plt.title('Bilateral Blurred') plt.xticks([]), plt.yticks([]) plt.show()

Phương pháp sử dụng cho xử lý ảnh

2.3. Phương pháp Canny phát hiện edge

Canny là một phương pháp phát hiện edge phổ biến được phát triển bởi John F.Canny vào năm 1986. Nó là một phương pháp được thực hiện qua nhiều bước như sau:

1. Giảm nhiễu ảnh

Bởi vì phát hiện edge dễ gây ra nhiễu trong ảnh nên bước đầu tiên giảm nhiễu cho ảnh bằng bộ lọc Gausian kích thước 5x5. Về bộ lọc Gaussian xem lại mục 2.2. làm mịn ảnh.

2. Tìm kiếm cường độ gradient của tấm hình

Trước khi đọc phần này, những bạn vẫn còn nhớ mục 2.1.1 - tính toán gradient descent ở thuật toán HOG chứ? Ảnh được làm mịn sau đó được lọc qua bộ lọc Sobel theo cả hai chiều dọc và ngang để thu được đạo hàm bậc 1 theo 2 phương x và y lần lượt là $G_x$ và $G_y$. Từ những hình ảnh này, tất cả chúng ta hoàn toàn có thể tìm được độ dài cạnh gradient và phương cho từng pixel như phía dưới:

[|G| = sqrtG_x^2 + G_y^2] [theta = arctan(fracG_yG_x)]

Phương gradient luôn luôn vuông góc với những cạnh. Nó được làm tròn thành một trong bốn góc màn biểu diễn trục dọc, ngang và 2 phương của đường chéo.

3. Triệt tiêu Phi tối đa (Non-maximum Suppression)

Triệt tiêu phi tối đa (Non-maximum Suppression) hiểu đơn giản là một thuật toán vô hiệu nếu như không phải là giá trị lớn số 1. Trong object detection, để phát hiện những bounding box thì ta sẽ tìm ra trong tập hợp những bounding box mà tỷ lệ overlap lên anchor box to hơn 0.5 một bounding box có kích thước lớn số 1 và xóa những bounding box còn sót lại.

Đối với tìm edge cũng vậy. Sau khi nhận được độ lớn của gradient và phương, một lượt quét được thực hiện trên những tấm hình để vô hiệu bất kì pixels nào không tạo thành cạnh. Để thực hiện điều đó, tại mỗi pixel, pixel được kiểm tra xem liệu nó có là một cực lớn cục bộ trong số những lân cận của nó theo phương của gradient. Như hình ảnh phía dưới.

Phương pháp sử dụng cho xử lý ảnh

Hình 2: Phương pháp xác định edge của thuật toán canny.

Điểm A nằm trên cạnh (theo chiều dọc). Phương gradient là norm chuẩn của cạnh. Điểm B và C là vấn đề nằm trên phương gradient. Nếu điểm A được kiểm tra với điểm B và C để xem liệu nó có là một cực lớn cục bộ. Nếu như vậy, nó được xem xét giữ lại cho bước tiếp theo, trái lại thì nó sẽ bị triệt tiêu (bằng phương pháp thiết lập bằng 0).

4. Ngưỡng độ trễ (Hysteresis Thresholding)

Ở bước này ta sẽ quyết định xem toàn bộ những cạnh có những cạnh nào là thực sự và những cạnh nào không thông qua việc thiết lập ngưỡng gồm 2 giá trị, minVal và maxVal. Một cạnh bất kì có cường độ gradient to hơn maxVal thì chắc như đinh là những cạnh và những cạnh có cường độ gradient nhỏ hơn sẽ không được xem là cạnh. Nếu một cạnh mà có những điểm ảnh nằm trong ngưỡng này thì hoàn toàn có thể được xem xét thuộc cùng một cạnh hoặc không thuộc nhờ vào sự link của những điểm với nhau. Cụ thể như hình phía dưới.

Phương pháp sử dụng cho xử lý ảnh

Hình 3: Phương pháp triệt tiêu cạnh theo ngưỡng cường độ gradient.

Trên hình vẽ là 2 cạnh A red color và B màu xanh. Cạnh A do có những điểm có cường độ gradient nằm trên maxVal nên được xem là những cạnh đạt tiêu chuẩn. Mặc dù trên cạnh A có những điểm nằm trong ngưỡng từ minVal tới maxVal. Cạnh B được xem là không đạt tiêu chuẩn vì toàn bộ những điểm nằm trên cạnh B đều nằm trong ngưỡng minVal và maxVal. Như vậy cạnh A sẽ được giữ lại và cạnh B sẽ được xóa bỏ.

Bước này tất cả chúng ta cũng vô hiệu những điểm pixels là nhiễu (thực chất là những cạnh nhưng quá ngắn) nhờ vào giả định rằng những cạnh là những đường dài.

Như vậy những bạn đã tưởng tượng được nguyên tắc hoạt động và sinh hoạt giải trí của thuật toán Canny để tìm kiếm những cạnh rồi chứ. Vậy openCV tương hỗ thuật toán Canny để phát hiện cạnh ra làm sao. Cùng thực hiện như phía dưới.

Thực hành thuật toán Canny trên openCV

Tất cả tiến trình trên được openCV gói gọn trong một hàm số là cv2.Canny(). Trong hàm số này tất cả chúng ta sẽ khai báo tham số đầu tiên là hình ảnh đầu vào, tham số thứ 2 và thứ 3 lần lượt là ngưỡng minVal và maxVal.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import cv2 import numpy as np import requests from matplotlib import pyplot as plt url = 'https://i.pinimg.com/736x/6d/9c/e0/6d9ce08209b81b28c6ea64012e070003.jpg' img = _downloadImage(url) edges = cv2.Canny(img, 100, 200) plt.subplot(121),plt.imshow(img,cmap = 'gray') plt.title('Original Image'), plt.xticks([]), plt.yticks([]) plt.subplot(122),plt.imshow(edges,cmap = 'gray') plt.title('Edge Image'), plt.xticks([]), plt.yticks([]) plt.show()

Phương pháp sử dụng cho xử lý ảnh

Như vậy tất cả chúng ta thấy toàn bộ những đường nét chính của nhân vật truyện tranh Conan đã được thuật toán Canny giữ lại.

Tìm hiểu thêm về:

Thuật toán Canny - wikipediaHướng dẫn thuật toán Canny trong phát hiện cạnh - Bill Green

2.4. Contour

2.4.1. Xác định những contour

Contour được hiểu đơn giản là một đường cong link toàn bộ những điểm liên tục (dọc theo đường biên) mà có cùng sắc tố hoặc giá trị cường độ. Contour rất hữu ích trong phân tích hình dạng, phát hiện vật thể và nhận diện vật thể. Một số lưu ý khi sử dụng contour.

    Để độ đúng chuẩn cao hơn thì nên sử dụng hình ảnh nhị phân (chỉ gồm 2 màu đen và trắng). Do đó trước khi phát hiện contour thì nên áp dụng threshold hoặc thuật toán canny để chuyển sang ảnh nhị phân.

    Hàm findContour và drawContour sẽ thay đổi hình ảnh gốc. Do đó nếu bạn muốn hình ảnh gốc sau khi tìm được contour, hãy lưu nó vào một biến khác.

    Trong openCV, tìm những contours như thể tìm những vật thể white color từ nền màu đen. Do đó hãy nhớ rằng, object cần tìm nên là white color và background nên là màu đen.

Bây giờ ta sẽ cùng tìm những contours cho một ảnh nhị phân.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import cv2 import numpy as np import requests from matplotlib import pyplot as plt # Đọc hình ảnh # url="https://imgur.com/7J223M7.png" url = 'https://c4.wallpaperflare.com/wallpaper/279/111/762/close-up-pile-pipes-round-wallpaper-preview.jpg' resp = requests.get(url) img = np.asarray(bytearray(resp.content), dtype="uint8") img = cv2.imdecode(img, cv2.IMREAD_COLOR) # Lọc ảnh nhị phân bằng thuật toán canny imgCanny = cv2.Canny(img, 100, 200) contours, hierarchy = cv2.findContours(imgCanny, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

Có 3 tham số trong hàm cv2.findContours(), đầu tiên là hình ảnh gốc, thứ 2 là phương pháp trích xuất contours, thứ 3 là phương pháp xấp xỉ contour. Kết quả trả ra là hình ảnh và contours. Trong số đó contours là một list của toàn bộ những contours xác định trong hình ảnh. Mỗi một contour là một numpy array của những tọa độ $(x, y)$ của những điểm biên trong object.

Lưu ý rằng ở một số trong những version openCV thì cách trích xuất những contours sẽ hoàn toàn có thể sẽ thêm/bớt một vài biến output. Trong trường hợp gặp lỗi bạn đọc hoàn toàn có thể tìm hiểu fix lỗi sử dụng hàm findContours ở những version openCV - stackoverflow

Sau khi trích xuất được những contour thì tất cả chúng ta sẽ vẽ những contour đó thông qua hàm cv2.drawContours(). Nó hoàn toàn có thể được sử dụng để vẽ bất kì hình dạng nào mà bạn có những tọa độ điểm biên của nó. Các tham số chính của hàm số này:

    Tham số đầu tiên: Hình ảnh gốc.Tham số thứ 2: List những contours cần vẽ. Mỗi một phần tử của list là array tọa độ những điểm biên của một contour.Tham số thứ 3: Index của contour. Chẳng hạn tất cả chúng ta chỉ muốn lựa lựa chọn ra một contour ở những index nhất định thuộc list contours thì khai báo index tại đây. Muốn vẽ toàn bộ những contours thì thiết lập giá trị cho tham số này bằng -1.

Thành phần còn sót lại sẽ đó đó là tham số về sắc tố, độ dày của contour.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # Vì drawContours sẽ thay đổi ảnh gốc nên cần lưu ảnh sang một biến mới. imgOrigin = img.copy() img1 = img.copy() img2 = img.copy() # Vẽ toàn bộ contours trên hình ảnh gốc cv2.drawContours(img1, contours, -1, (0, 255, 0), 3) # Vẽ chỉ contour thứ 4 trên hình ảnh gốc cv2.drawContours(img2, contours, 100, (0, 255, 0), 3) plt.figure(figsize = (12, 3)) plt.subplot(141),plt.imshow(imgOrigin),plt.title('Original') plt.xticks([]), plt.yticks([]) plt.subplot(142),plt.imshow(imgCanny),plt.title('Canny Binary Image') plt.xticks([]), plt.yticks([]) plt.subplot(143),plt.imshow(img1),plt.title('All Contours') plt.xticks([]), plt.yticks([]) plt.subplot(144),plt.imshow(img2),plt.title('Contour 4') plt.xticks([]), plt.yticks([])

Phương pháp sử dụng cho xử lý ảnh

2.4.2. Các đặc trưng của contour

Ở trên tất cả chúng ta đã biết những trích suất ra những contour của một hình ảnh. Tiếp theo tất cả chúng ta sẽ tìm hiểu xem những đặc trưng như kích thước dài, rộng, diện tích s quy hoạnh, tâm,… của một contour là gì và hoàn toàn có thể tính toán chúng trên openCV ra sao.

Moment

Các moments của hình ảnh sẽ giúp ta tính toán tâm của vật thể và diện tích s quy hoạnh của vật thể. Có thể xem thêm tại Image moment - wikipedia.

Hàm cv2.moments() sẽ tương hỗ ta thực hiện tính toán những moments. Kết quả trả về là một dictionary như sau:

1 2 3 4 5 6 7 8 9 10 11 12 import cv2 import numpy as np url = 'https://image.flaticon.com/icons/png/512/130/130188.png' img = _downloadImage(url) # Khởi tạo ảnh nhị phân canny imgCanny = cv2.Canny(img, 100, 255) plt.imshow(imgCanny) # Tìm kiếm contours trên ảnh nhị phân từ bộ lọc canny contours, hierarchy = cv2.findContours(imgCanny, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

Phương pháp sử dụng cho xử lý ảnh

1 2 3 4 5 6 7 8 9 # Vẽ contour 0 trên hình ảnh gốc img1 = img.copy() cv2.drawContours(img1, contours, 0, (0, 255, 0), 5) plt.imshow(img1) # Lấy ra contour 0 cnt = contours[0] M = cv2.moments(cnt) print('Moment values of contour 0: '.format(M))

1 Moment values of contour 0: 'm00': 129.5, 'm10': 9821.833333333332, 'm01': 35877.166666666664, 'm20': 1204998.5833333333, 'm11': 3155542.208333333, 'm02': 10432159.25, 'm30': 218048987.95000002, 'm21': 443324957.01666665, 'm12': 1071073460.4833333, 'm03': 3214254682.55, 'mu20': 460068.77616902627, 'mu11': 434464.5907872161, 'mu02': 492614.1687044203, 'mu30': 56869583.044915825, 'mu21': 43584319.12291357, 'mu12': 39122522.03416099, 'mu03': 51138718.292099, 'nu20': 27.43362658094103, 'nu11': 25.90686428569736, 'nu02': 29.374288916648254, 'nu30': 297.99269854672315, 'nu21': 228.3788305516631, 'nu12': 204.9993210906005, 'nu03': 267.963361926664

Phương pháp sử dụng cho xử lý ảnh

Từ đây tất cả chúng ta hoàn toàn có thể tính ra được tâm của contour 0 theo công thức:

[c_x = fracM_10M_00, c_y = fracM_01M_00]

1 2 3 4 5 6 7 8 # Tính toán tâm của contour 0 cx = int(M['m10']/M['m00']) cy = int(M['m01']/M['m00']) print('Centroid position (, )'.format(cx, cy)) # Vẽ biểu đồ tâm contour 0 và contour 0 plt.imshow(img1) plt.scatter(cx, cy, s=50, c="red", marker="o")

1 Centroid position (75, 277)

Phương pháp sử dụng cho xử lý ảnh

Để biết diện tích s quy hoạnh của contour ta dùng hàm cv2.contourArea() hoặc phần tử M[‘m00’]

1 2 area = cv2.contourArea(cnt) print('area of contour 0: '.format(area))

1 area of contour 0: 129.5

2.4.3. Bounding box

Khác với contour là một đường cong nối liền những điểm có cùng cường độ hoặc sắc tố. Bounding box là một đường biên đóng xung quanh vật thể. Bounding box hoàn toàn có thể có nhiều hình dạng rất khác nhau trong đó có những hình dạng chính như: chữ nhật, tam giác, tròn, ellipse. Một bounding box hoàn toàn có thể được xác định thông qua một contour. Bên dưới là những bounding box phổ biến.

1. Bounding box hình chữ nhật

Cái tên nói lên tất cả, bounding box hình chữ nhật sẽ chỉ có một định dạng là hình chữ nhật và tọa độ của nó được xác định thông qua tọa độ của một đỉnh và chiều width, height. Ngoài ra đối với một số trong những hình chữ nhật nằm nghiêng còn tồn tại thêm góc xoay. Có 2 dạng bounding box hình chữ nhật cho một object đó là Bounding box hình chữ nhật đứng (những cạnh song song với những trục tung và trục hoành) và hình chữ nhật xoay (những cạnh không vuông góc với trục tung và trục hoành). Bounding box hình chữ nhật đứng phù phù phù hợp với những vật thể có tư thế thẳng hoặc ngang. Các trường hợp vật thể nằm chéo sẽ phù phù phù hợp với hình chữ nhật xoay.

Hàm cv2.boundingRect() giúp tìm ra Bounding box hình chữ nhật đứng. Hàm cv2minAreaRect() tìm ra Bounding box diện tích s quy hoạnh nhỏ nhất xung quanh một contour xác định. Do đó nó gồm có cả những bounding box hình chữ nhật xoay.

Vẽ bounding box hình chữ nhật đứng

Ví dụ tất cả chúng ta sẽ tìm ra những Bounding box hình chữ nhật đứng đối với một contour như sau:

1 2 3 4 5 6 7 8 img = _downloadImage('https://i0.wp.com/cdn-images-1.medium.com/max/2400/1*LmxW8FDfXZJl5yvESvjP7Q.jpeg?resize=660%2C373&ssl=1') # Chuyển đổi sang ảnh gray img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Chuyển sang ảnh nhị phân _, img = cv2.threshold(img, 100, 255, cv2.THRESH_BINARY) plt.imshow(img) # Tìm kiếm contours trên ảnh nhị phân contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

Phương pháp sử dụng cho xử lý ảnh

Sử dụng hàm cv.contourArea() để tìm ra diện tích s quy hoạnh những contours và sắp xếp thứ tự diện tích s quy hoạnh từ cao xuống thấp.

1 2 3 4 5 # Tìm ra diện tích s quy hoạnh của toàn bộ những contours area_cnt = [cv2.contourArea(cnt) for cnt in contours] area_sort = np.argsort(area_cnt)[::-1] # Top 5 contour có diện tích s quy hoạnh lớn số 1 area_sort[:5]

1 array([45, 78, 89, 93, 63])

Từ contour, ta sẽ xác định tọa độ góc trên bên trái và độ dài cạnh width, height của contour thông qua hàm cv2.boundingRect(). Từ đó vẽ bounding box hình chữ nhật đứng lên hình ảnh gốc bằng phương pháp sử dụng hàm cv.rectangle() như phía dưới:

1 2 3 4 5 6 # Vẽ bounding box cho contours có diện tích s quy hoạnh lớn thứ 2 cnt = contours[area_sort[1]] x,y,w,h = cv2.boundingRect(cnt) print('centroid: (, ), (width, height): (, )'.format(x, y, w, h)) img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2) plt.imshow(img)

1 centroid: (334, 211), (width, height): (73, 74)

Vẽ bounding box hình chữ nhật xoay diện tích s quy hoạnh nhỏ nhất xung quanh một contour

Để vẽ một bounding box có diện tích s quy hoạnh tối thiểu xung quanh một contour xác định tất cả chúng ta sử dụng hàm cv2.minAreaRect(). Hàm này sẽ trả về một cấu trúc Box 2 chiều gồm có những thông số: Tọa độ đỉnh trên cùng bên trái (x, y), (width, height), góc xoay. Nhưng để vẽ hình chữ nhật thì ta cần tọa độ của 4 góc. Thông qua hàm cv2.boxPoint() ta sẽ chuyển những hình dữ nhật thành những box 2D. Cụ thể như sau:

1 2 3 4 5 rect = cv2.minAreaRect(cnt) box = cv2.boxPoints(rect) box = np.int0(box) img = cv2.drawContours(img,[box],0,(100,100,100),2) plt.imshow(img)

Phương pháp sử dụng cho xử lý ảnh

2. Bounding box những hình dạng khác

Ngoài ra opencv cũng tương hỗ vẽ những bouding những hình dạng khác có diện tích s quy hoạnh tối thiểu xung quanh một contour như sau:

    Hình tròn: hàm cv2.minEnclosingCircle()

1 2 3 4 5 (x, y), radius = cv2.minEnclosingCircle(cnt) center = (int(x),int(y)) radius = int(radius) img = cv2.circle(img,center,radius,(0,255,0),2) plt.imshow(img)

Phương pháp sử dụng cho xử lý ảnh
    Hình Ellipse: hàm cv2.fitEllipse()

Do số 8 đã nhiều hình rồi nên để quan sát rõ hơn ta sẽ chuyển qua một số trong những khác.

1 2 3 4 5 # Lấy contour có diện tích s quy hoạnh lớn thứ 3 cnt = contours[area_sort[3]] ellipse = cv2.fitEllipse(cnt) img = cv2.ellipse(img,ellipse,(0,255,0),2) plt.imshow(img)

Phương pháp sử dụng cho xử lý ảnh
    Đường thẳng: Hàm cv2.fitLine()

1 2 3 4 5 6 rows,cols = img.shape[:2] [vx,vy,x,y] = cv2.fitLine(cnt, cv2.DIST_L2,0,0.01,0.01) lefty = int((-x*vy/vx) + y) righty = int(((cols-x)*vy/vx)+y) img = cv2.line(img,(cols-1,righty),(0,lefty),(0,255,0),2) plt.imshow(img)

Phương pháp sử dụng cho xử lý ảnh

2.4.5. Các thuộc tính của contour

Ở đây, tất cả chúng ta sẽ học cách trích xuất một số trong những thuộc tính thường xuyên được sử dụng của một vật thể như độ cô đặc (solidity), đường kính (diameter), ảnh mặt nạ (mask image), trung bình cường độ (mean intensity),….

1. Tỷ lệ cạnh (aspect ratio)

Như tất cả chúng ta đã làm quen ở Bài 13 - Model SSD trong Object Detection. Tỷ lệ hướng (Aspect ratio) là tỷ lệ kích thước chiều dài và chiều rộng theo công thức:

[textAR = fracWH]

với $W, H$ lần lượt là width và height.

Aspect ratio sẽ cho ta biết hình dạng tương đối của bounding box là cao dẹt hay rộng thấp.

1 2 3 x,y,w,h = cv2.boundingRect(cnt) aspect_ratio = float(w)/h print('aspect ratio: '.format(aspect_ratio))

1 aspect ratio: 0.9041095890410958

2. Độ phủ (extent)

Chúng ta muốn biết độ phủ của contour lên bounding box bằng phương pháp tính tỷ lệ diện tích s quy hoạnh giữa chúng theo công thức:

[textExtent = fracS_contourS_area]

Trong số đó $S_contour$ là diện tích s quy hoạnh của contour và $S_area$ là diện tích s quy hoạnh của bounding box.

1 2 3 4 5 6 7 8 # Tính diện tích s quy hoạnh của contour area = cv2.contourArea(cnt) # Tính diện tích s quy hoạnh bouding box x,y,w,h = cv2.boundingRect(cnt) rect_area = w*h # Tính độ phủ extent = float(area)/rect_area print('Extent: '.format(extent))

1 Extent: 0.4712536322125363

3. Độ cô đặc (Solidity)

Cũng gần như thể độ phủ nhưng mẫu số thay vì là bounding box sẽ là một hình đa diện lồi (convex hull area) xung quanh vật thể.

[textSolidity=fracS_contourS_CVhull]

Với $S_contour, S_CVhull$ lần lượt là diện tích s quy hoạnh contour và diện tích s quy hoạnh đa diện lồi.

1 2 3 4 5 6 7 area = cv2.contourArea(cnt) # Khởi tạo một đa diện lồi xung quanh contour hull = cv2.convexHull(cnt) hull_area = cv2.contourArea(hull) # Tính toán độ cô đặc solidity = float(area)/hull_area print('Solidity : '.format(solidity))

1 Solidity : 0.6777611940298508

4. Đường kính tương đương (Equivalent Diameter)

Đường kính tương đương là đường kính của một hình tròn trụ mà diện tích s quy hoạnh của nó bằng với diện tích s quy hoạnh contour.

[textED = sqrtfrac4times S_contourpi]

1 2 3 area = cv2.contourArea(cnt) equi_diameter = np.sqrt(4*area/np.pi) print('Equivalent Diameter : '.format(equi_diameter))

1 Equivalent Diameter : 53.76700090502712

5. Hướng (Orientation)

Hướng là góc mà đối tượng hướng tới. Phương pháp sau đây sẽ đưa ra những độ dài của những trục chính và trục phụ.

1 2 (x,y),(MA,ma),angle = cv2.fitEllipse(cnt) print('Angle of Orientation: '.format(angle))

1 Angle of Orientation: 149.97474670410156

6. Mặt nạ (Mask) và những điểm pixels

Trong một số trong những trường hợp tất cả chúng ta muốn trích xuất tất cả những điểm pixels tạo thành vật thể. Đây là những điểm có mức giá trị cường độ khác 0.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 img = _downloadImage('https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ8amhxBfrtf4Xcnf3jQOKUn2PhqsxMRj-JbCNpkOze6tv_4oiV&s') imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Find contour contours, hierarchy = cv2.findContours(imgray, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # Tìm ra diện tích s quy hoạnh của toàn bộ những contours area_cnt = [cv2.contourArea(cnt) for cnt in contours] area_sort = np.argsort(area_cnt)[::-1] # Trích xuất contour lớn số 1 cnt = contours[area_sort[0]] x,y,w,h = cv2.boundingRect(cnt) print('centroid: (, ), (width, height): (, )'.format(x, y, w, h)) # Vẽ bounding box img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2) plt.imshow(img)

1 centroid: (13, 9), (width, height): (96, 84)

1 2 3 4 5 6 7 8 9 10 11 # Khởi tạo contour mask zero mask = np.zeros(imgray.shape, np.uint8) # Vẽ contour trên back ground mask zero cv2.drawContours(mask,[cnt],0,255,-1) plt.imshow(mask) # Lấy ra toàn bộ những points có mức giá trị khác 0 pixelpoints = np.transpose(np.nonzero(mask)) print('pixelpoints shape use numpy: '.format(pixelpoints.shape)) # Hoặc cũng hoàn toàn có thể dùng hàm cv2.findNoneZero() pixelpoints = cv2.findNonZero(mask) print('pixelpoints shape use cv2: '.format(pixelpoints.shape))

1 2 pixelpoints shape use numpy: (1865, 2) pixelpoints shape use cv2: (1865, 1, 2)

Phương pháp sử dụng cho xử lý ảnh

7. Trích xuất Min, Max value và vị trí của chúng

Chúng ta hoàn toàn có thể tìm ra những điểm có mức giá trị cường độ ảnh lớn số 1 và nhỏ nhất kèm theo vị trí tọa độ của chúng.

1 2 3 min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(imgray, mask = mask) print('(min, max) values (, )'.format(min_val, max_val)) print('(min, max) location (, )'.format(min_loc, max_loc))

1 2 (min, max) values (11.0, 255.0) (min, max) location ((24, 50), (42, 11))

8. Trích xuất trung bình cường độ sắc tố

Chúng ta hoàn toàn có thể tìm ra trung bình sắc tố của một object thông qua hàm cv2.mean() như sau:

1 mean_val = cv2.mean(img, mask = mask)

9/. Trích xuất những điểm ngoài cùng

Các điểm ngoài cùng là những điểm nằm ở ngoài cùng của object tại những vị trí top, bottom, right, left.

1 2 3 4 5 6 7 8 9 10 leftmost = tuple(cnt[cnt[:,:,0].argmin()][0]) rightmost = tuple(cnt[cnt[:,:,0].argmax()][0]) topmost = tuple(cnt[cnt[:,:,1].argmin()][0]) bottommost = tuple(cnt[cnt[:,:,1].argmax()][0]) plt.imshow(mask) plt.scatter(x = leftmost[0], y = leftmost[1]) plt.scatter(x = rightmost[0], y = rightmost[1]) plt.scatter(x = topmost[0], y = topmost[1]) plt.scatter(x = bottommost[0], y = bottommost[1])

Phương pháp sử dụng cho xử lý ảnh

2.4.6. Tìm bounding box trong Object detection

Trong Object Detection, để huấn luyện những quy mô tất cả chúng ta sẽ nên phải gán nhãn cho những vật thể trong hình và tìm ra tọa độ tâm, kích thước width, height của bounding box xung quanh vật thể. Nếu gãn nhãn bằng tay thủ công sẽ khá mất thời gian và công sức của con người. Có một những nhanh hơn tự động khu vực phạm vi những bounding box của những vật thể trong ảnh trong opencv. Hãy cùng xem.

Gỉa sử tất cả chúng ta có một dãy những chữ số ngẫu nhiên như sau:

1 2 3 4 5 6 7 8 9 10 11 12 img = _downloadImage('https://i0.wp.com/cdn-images-1.medium.com/max/2400/1*LmxW8FDfXZJl5yvESvjP7Q.jpeg?resize=660%2C373&ssl=1') # Chuyển ảnh sang gray image imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Chuyển sang ảnh nhị phân _, imgBi = cv2.threshold(img, 100, 255, cv2.THRESH_BINARY) # Chuyển ảnh sang gray image imgGrayBi = cv2.cvtColor(imgBi, cv2.COLOR_BGR2GRAY) plt.figure(figsize=(16, 4)) plt.subplot(141), plt.imshow(img), plt.title('Origin Image') plt.subplot(142), plt.imshow(imgGray), plt.title('Gray Image') plt.subplot(143), plt.imshow(imgBi), plt.title('Binary Image') plt.subplot(144), plt.imshow(imgGrayBi), plt.title('Gray of Binary Image')

Phương pháp sử dụng cho xử lý ảnh

Tìm ra những contour từ ảnh nhị phân đã được chuyển sang gray

1 2 3 4 5 6 contours, hierarchy = cv2.findContours(imgGrayBi, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # Sắp xếp những contour theo diện tích s quy hoạnh giảm dần: area_cnt = [cv2.contourArea(cnt) for cnt in contours] area_sort = np.argsort(area_cnt)[::-1] # Top 20 contour có diện tích s quy hoạnh lớn số 1 area_sort[:20]

1 2 array([ 45, 78, 89, 93, 63, 55, 83, 53, 95, 110, 74, 60, 70, 113, 99, 106, 76, 117, 56, 91])

Vẽ bounding box cho 25 contours có diện tích s quy hoạnh lớn số 1

1 2 3 4 5 6 7 8 9 10 11 12 def _drawBoundingBox(img, cnt): x,y,w,h = cv2.boundingRect(cnt) img = cv2.rectangle(img, (x,y),(x+w,y+h),(0,255,0),2) return img imgOrigin = img.copy() # Vẽ bounding box cho 25 contours có diện tích s quy hoạnh lớn số 1 for i in area_sort[:25]: cnt = contours[i] imgOrigin = _drawBoundingBox(imgOrigin, cnt) plt.imshow(imgOrigin)

Phương pháp sử dụng cho xử lý ảnh

Như vậy hầu hết những chữ số đã được tìm ra bouding box mà không phải tốn công sức của con người từ người. Việc còn sót lại của tất cả chúng ta chỉ là gán nhãn cho từng bounding box. Theo cách này tất cả chúng ta hoàn toàn có thể tiết kiệm được khoảng chừng 80% thời gian so với xây dựng một bộ tài liệu huấn luyện thủ công.

Ta nhận thấy có một số trong những bounding box overlap nhau như những bounding box quanh số 0, 5, 9. Chúng ta hoàn toàn có thể sử dụng hàm non-max suppression như phía dưới để triệt tiêu những bounding box overlap nhau và chỉ giữ lại những bounding box lớn số 1.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 import numpy as np def non_max_suppression(boxes, overlapThresh): ''' boxes: List những bounding box overlapThresh: Ngưỡng overlapping Một trong những hình ảnh ''' # Nếu không còn bounding boxes thì trả về empty list if len(boxes)==0: return [] # Nếu bounding boxes nguyên thì chuyển sang float. if boxes.dtype.kind == "i": boxes = boxes.astype("float") # Khởi tạo list của index được lựa chọn pick = [] # Lấy ra tọa độ của những bounding boxes x1 = boxes[:,0] y1 = boxes[:,1] x2 = boxes[:,2] y2 = boxes[:,3] # Tính toàn diện tích s quy hoạnh của những bounding boxes và sắp xếp chúng theo thứ tự từ bottom-right, đó đó là tọa độ theo y của bounding box area = (x2 - x1 + 1) * (y2 - y1 + 1) idxs = np.argsort(y2) # Khởi tạo một vòng while loop qua những index xuất hiện trong indexes while len(idxs) > 0: # Lấy ra index ở đầu cuối của list những indexes và thêm giá trị index vào list những indexes được lựa chọn last = len(idxs) - 1 i = idxs[last] pick.append(i) # Tìm cặp tọa độ lớn số 1 (x, y) là vấn đề khởi đầu của bounding box và tọa độ nhỏ nhất (x, y) là vấn đề kết thúc của bounding box xx1 = np.maximum(x1[i], x1[idxs[:last]]) yy1 = np.maximum(y1[i], y1[idxs[:last]]) xx2 = np.minimum(x2[i], x2[idxs[:last]]) yy2 = np.minimum(y2[i], y2[idxs[:last]]) # Tính toán width và height của bounding box w = np.maximum(0, xx2 - xx1 + 1) h = np.maximum(0, yy2 - yy1 + 1) # Tính toán tỷ lệ diện tích s quy hoạnh overlap overlap = (w * h) / area[idxs[:last]] # Xóa index ở đầu cuối và index của bounding box mà tỷ lệ diện tích s quy hoạnh overlap > overlapThreshold idxs = np.delete(idxs, np.concatenate(([last], np.where(overlap > overlapThresh)[0]))) # Trả ra list những index được lựa chọn return boxes[pick].astype("int") boundingBoxes = [cv2.boundingRect(cnt) for cnt in contours] boundingBoxes = np.array([(x,y,x+w,y+h) for (x,y,w,h) in boundingBoxes]) pick = non_max_suppression(boundingBoxes, 0.5)

Áp dụng hàm non_max_suppression() để triệt tiêu những bounding box chồng lấn.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 x,y,w,h = cv2.boundingRect(cnt) # Vẽ bounding box cho 25 contours có diện tích s quy hoạnh lớn số 1 boundingBoxes = [] for i in area_sort[:25]: cnt = contours[i] x,y,w,h = cv2.boundingRect(cnt) x1, y1, x2, y2 = x, y, x+w, y+h boundingBoxes.append((x1, y1, x2, y2)) # Remove đi bounding box parent (đó đó là khung hình bound toàn bộ hình ảnh), nếu không khi áp dụng non max suppression chỉ giữ lại bounding box này boundingBoxes = [box for box in boundingBoxes if box[:2] != (0, 0)] boundingBoxes = np.array(boundingBoxes) pick = non_max_suppression(boundingBoxes, 0.5)

Vẽ những hình ảnh của bounding box sau khi triệt tiêu.

1 2 3 4 imgOrigin = img.copy() for (startX, startY, endX, endY) in pick: imgOrigin = cv2.rectangle(imgOrigin, (startX, startY), (endX, endY), (0, 255, 0), 2) plt.imshow(imgOrigin)

Phương pháp sử dụng cho xử lý ảnh

Như vậy những bounding box overlap nhau ở những vị trí số 0, 5, 9 đã được triệt tiêu để giữ lại 1 bounding box lớn số 1. Chúng ta cũng hoàn toàn có thể crop những ảnh theo những bounding box như sau:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 def _cropImage(x1, y1, x2, y2, img): if np.ndim(img) == 3: crop = img[y1:y2, x1:x2, :] else: crop = img[y1:y2, x1:x2] return crop crop_images = [_cropImage(x1, y1, x2, y2, img) for (x1, y1, x2, y2) in pick] fg, ax = plt.subplots(4,5,figsize=(12, 4)) fg.suptitle('Cropped Images') for i in np.arange(4): for j in np.arange(5): try: ax[i,j].imshow(crop_images[i+j+j*i]) ax[i,j].set_xlabel('Sub Image '+str(i+j+j*i)) except: next

Phương pháp sử dụng cho xử lý ảnh

Sau khi đã tách biệt được từng ảnh của vật thể, ta hoàn toàn có thể đưa chúng vào một quy mô pretrain chuyên nhận diện kí tự chữ số viết tay để gán nhãn tự động. Hướng tới tự động hóa quá trình gán nhãn tài liệu thay vì còn người gán nhãn. Tôi ước tính sẽ giảm thiểu được cho bạn 10% thời gian nữa. Bạn sẽ có dôi ra nhiều thời gian hơn để huấn luyện quy mô, rất hữu ích phải không nào?

3. Kết luận

Như vậy tôi đã ra mắt tới những bạn gần hết những phương pháp xử lý ảnh cơ bản sử dụng opencv. Ngoài opencv thì còn rất nhiều những tools, packages và frameworks khác cũng tương hỗ những hàm tiện ích trong tiền xử lý ảnh như albumentation, keras, pytorch. Ý tưởng chung của chúng đều ra xoay quanh những kĩ thuật chính trong xử lý ảnh đó là:

    Các dịch chuyển hình học như dịch chuyển phải, trái, lên, xuống, xoay ảnh, lật ảnh, biến hóa phối cảnh.Các phương pháp lọc nhiễu, làm mờ thông qua bộ lọc.Phương pháp phát hiện edge sử dụng bộ lọc canny hoặc ngưỡng threshold.Xác định những contours theo cường độ sắc tố tương đồng.Xác định bounding box của vật thể.Sử dụng non-max suppression để triệt tiêu những bounding box chồng lấn.

Tiền xử lý ảnh là một trong những phương pháp rất quan trọng giúp tăng cường tài liệu (data augumentation) cho huấn luyện. Một thuật toán phân loại hoặc phát hiện vật thể hoàn toàn có thể được học đa dạng và tổng quát hơn nếu quá trình tiền xử lý tài liệu tạo ra nhiều ảnh hơn cho nó.

Trên thực tế có nhiều trường hợp quy mô thể hiện trên tài liệu huấn luyện thì rất tốt nhưng khi áp dụng vào thực tế lại không tốt. Nguyên nhân hoàn toàn có thể đến từ khác lạ của hình ảnh huấn luyện và hình ảnh dự báo về cường độ sắc tố ảnh, độ phân giải, chất lượng ảnh, mức độ rõ ràng, mức độ bao quát của ,….

Do đó ứng dụng tiền xử lý tài liệu sẽ giúp ta cover được hầu hết những trường hợp trên và giúp nâng cao độ đúng chuẩn, cải tổ kĩ năng phân loại, nhận diện vật thể.

Cũng nhờ image preprocessing mà tất cả chúng ta tiết kiệm được ngân sách thời gian cho quá trình sẵn sàng sẵn sàng tài liệu cho huấn luyện. Từ đó hoàn toàn có thể tập trung vào việc tinh chỉnh cấu trúc và tunning quy mô để cải tổ chất lượng.

Và còn rất nhiều những quyền lợi khác nữa của tiền xử lý tài liệu ảnh mà tất cả chúng ta hoàn toàn có thể áp dụng.

Hi vọng sau nội dung bài viết này bạn đọc có một chiếc nhìn khái quát hơn về những phương pháp tiền xử lý tài liệu ảnh, trường hợp, và phương pháp áp dụng chúng.

Cuối cùng, không thể thiếu sau mỗi nội dung bài viết của khanh blog là những tài liệu mà tôi đã tham khảo.

4. Tài liệu

opencv documentation pythonNon-Maximum SuppressionImage moment wikiimage preprocessing in diabetic retinopathyAffine Transformation - wikialbumentations package documentTensorflow Keras image preprocessingImprove OCR accuracy by image preprocessingCanny Edge Detector - wikiLinear and affine transformations - youtubeKhao khát học máy bản Tiếng Việt - Tác giả Andrew Ng - Tiep Vu và tập sự dịch Tải thêm tài liệu liên quan đến nội dung bài viết Phương pháp sử dụng cho xử lý ảnh Học Tốt Phương pháp xử lý

Review Phương pháp sử dụng cho xử lý ảnh ?

Bạn vừa Read nội dung bài viết Với Một số hướng dẫn một cách rõ ràng hơn về Review Phương pháp sử dụng cho xử lý ảnh tiên tiến nhất

Share Link Cập nhật Phương pháp sử dụng cho xử lý ảnh miễn phí

Heros đang tìm một số trong những Chia SẻLink Tải Phương pháp sử dụng cho xử lý ảnh miễn phí.

Giải đáp thắc mắc về Phương pháp sử dụng cho xử lý ảnh

Nếu sau khi đọc nội dung bài viết Phương pháp sử dụng cho xử lý ảnh vẫn chưa hiểu thì hoàn toàn có thể lại phản hồi ở cuối bài để Admin lý giải và hướng dẫn lại nha #Phương #pháp #sử #dụng #cho #xử #lý #ảnh - Phương pháp sử dụng cho xử lý ảnh - 2022-09-29 18:48:22
Related posts:

Post a Comment

Previous Post Next Post

Discuss

×Close