Kiến trúc mạng Unet

Một phần của tài liệu 1622190092-baocaodoan5-duongquockhanh-hoangngocphuong (Trang 29)

Mỗi một thanh chữ nhật màu xanh là một feature map đa kênh. Kích thước width x height được kí hiệu góc trái bên dưới của thanh chữ nhật và số lượng channels được kí hiệu trên đỉnh của feature map. Các thanh chữ nhật màu trắng bên nhánh phải của hình chữ U được copy từ nhánh bên trái và concatenate vào nhánh bên phải. Mỗi một mũi tên có màu sắc khác nhau tương ứng với một phép biến đổi khác nhau như chúng ta có thể thấy trong mơ tả của mạng.

Mạng Unet bao gồm 2 nhánh đối xứng nhau hình chữ U nên được gọi là Unet.

Kiến trúc mạng Unet bao gồm 2 phần là phần thu hẹp (contraction) ở bên trái và phần

mở rộng (expansion) ở bên phải. Mỗi phần sẽ thực hiện một nhiệm vụ riêng như sau:

 Phần thu hẹp: Làm nhiệm vụ trích lọc đặc trưng để tìm ra bối cảnh của hình ảnh. Vai trị của phần thu hẹp tương tự như một Encoder. Một mạng Deep CNN sẽ đóng vai trị trích lọc đặc trưng. Lý do nhánh được gọi là thu hẹp vì kích thước dài và rộng của các layers giảm dần. Từ input kích thước 572x572 chỉ cịn 32x32. Đồng thời độ sâu cũng tăng dần từ 3 lên 512.

 Phần mở rộng: Gồm các layer đối xứng tương ứng với các layer của nhánh thu hẹp. Q trình Upsampling được áp dụng giúp cho kích thước layer tăng dần lên. Sau cùng ta thu được một ảnh mask đánh dấu nhãn dự báo của từng pixel.

Đặc trưng riêng trong cấu trúc của Unet đó là áp dụng kết nối tắt đối xứng giữa layer bên trái với layer bên phải.

Chương 2 XÂY DỰNG CHƯƠNG TRÌNH XỬ LÝ ẢNH TÍCH HỢP TRÍ TUỆ NHÂN TẠO

2.1 Tổng quan

Cơng nghệ xử lý hình ảnh có tiềm năng lớn trong việc áp dụng rộng rãi trong các ngành công nghiệp khác nhau. Để chứng minh rằng công nghệ này đang tồn tại khắp thế giới, chúng ta hãy xem xét các số liệu thống kê gần đây. Các nhà nghiên cứu dự đoán rằng thị trường tồn cầu của cơng nghệ xử lý hình ảnh sẽ đạt 38,92 tỷ USD vào năm 2021. Đó là một con số rất lớn! Vì vậy, khơng có thắc mắc rằng ngày càng nhiều cái gọi là ứng dụng tận dụng xử lý hình ảnh cho các mục đích khác nhau và trong cả kinh doanh.

Điều gì giúp mở rộng cơng nghệ xử lý hình ảnh ngày nay? Đó là các cơng cụ mã nguồn mở giúp lập trình dễ dàng hơn, trong khi tính tốn giá cả phải chăng hơn. Các khung công tác và thư viện nguồn mở ngày nay làm cho các cơng ty có thể hưởng lợi từ cơng nghệ nhận dạng hình ảnh theo cấp số nhân.

2.2 Mục tiêu

Chương trình xử lý hình ảnh tích hợp trí tuệ nhân tạo được xây dựng với các mục tiêu chính như sau:

 Tạo ra chương trình xử lý ảnh với các chức năng như: Làm sắc nét, làm mịn, tăng giảm độ sáng, tăng tương phản, âm bản, …

 Tích hợp trí tuệ nhân tạo vào chương trình với chức năng: tìm kiếm vật thể chính, xóa phơng

2.3 Cơng cụ hỗ trợ 2.3.1 Ngơn ngữ lập trình

Trong phạm vi đồ án này, chúng em sử dụng ngôn ngữ lập trình Python phiên bản 3.7.

2.3.2 Các thư viện hỗ trợ:

Chương 3 Opencv (Open Computer Vision): là một thư viện mã nguồn mở hàng

đầu cho xử lý về thị giác máy tính, machine learning, xử lý ảnh. OpenCV đươc viết bằng C/C++, vì vậy có tốc độ tính tốn rất nhanh, có thể sử dụng với các ứng dụng liên quan đến thời gian thực. Opencv có các interface cho C/C++, Python Java vì vậy hỗ trợ được cho Window, Linux, MacOs lẫn Android, IOS.

Ở trong phạm vi đồ án này, chúng em sử dụng các hàm ở trong thư viện OpenCV để hỗ trợ việc đọc, ghi hình ảnh từ bộ nhớ, xử lý ảnh nhanh hơn, dễ dàng tiếp cận hơn.

Numpy: là một thư viện lõi phục vụ cho khoa học máy tính của Python, hỗ trợ cho việc

tính tốn các mảng nhiều chiều, có kích thước lớn với các hàm đã được tối ưu áp dụng lên các mảng nhiều chiều đó.

Vì ảnh là ma trận (mảng) của tập hợp các điểm ảnh nên việc xử lý ảnh là thực hiện các tính tốn ở trên ma trận. Thơng thường thì việc tính tốn ở trên ma trận bắt buộc phải duyệt đến từng phần tử của ma trận đó, với mỗi tấm ảnh khoảng 1 megapixel thì số lượng phần tử ở trong ma trận là rất lớn, cho nên cách duyệt từng phần tử trong ma trận làm cho chương trình chạy rất chậm, ảnh hưởng đến trải nghiệm của người dùng. Sử dụng thư viện numpy sẽ tăng tốc được quá trình xử lý ảnh, đồng thời giúp chương trình cần ít câu lệnh hơn giúp cho việc bảo trì sau này dễ dàng hơn.

Tensorflow: là thư viện mã nguồn mở cho machine learning nổi tiếng nhất thế giới,

được phát triển bởi các nhà nghiên cứu từ Google. Việc hỗ trợ mạnh mẽ các phép tốn học để tính tốn trong machine learning và deep learning đã giúp việc tiếp cận các bài tốn trở nên đơn giản, nhanh chóng và tiện lợi hơn nhiều.

Keras: là một open source cho Neural Network được viết bởi ngôn ngữ Python. Nó là

một library được phát triển vào năm 205 bởi Francois Chollet, là một kỹ sư nghiên cứu Deep Learning. Phần backend của keras thường là Tensorflow

Ở trong phạm vi đồ án này, chúng em sử dụng keras từ tensorflow nhằm phục vụ cho việc học máy.

Matplotlib: Sử dụng để vẽ biểu đồ histogram.

PyQt5: Giao diện chính của chương trình được xây dựng bằng thư viện PyQt5 của

python. PyQt5 là một thư viện của Python cho phép tạo GUI với framework Qt5, cho phép xây dựn giao diện desktop app trên python, có thể tinh chỉnh các thuộc tính bằng stylesheet tương tự như css ở trên web.

3.1 Các bước thực hiện 3.1.1 Xây dựng giao diện

Giao diện chính của chương trình được xây dựng bằng thư viện PyQt5 của python Giao diện chương trình bào gồm 3 phần:

 Phần hiển thị ảnh gốc và ảnh qua chỉnh sửa

 Phía bên phải là phần hiển thị các chức năng: bao gồm các Slider, các Check box, các Radio button, Combobox và biểu đồ histogram nằm phía trên

 Phía trên phần hiển thị hình ảnh chứa các nút cơng cụ như mở hình ảnh, lưu hình ảnh, ẩn/ hiện biểu đồ histogram, cắt ảnh , vẽ, và phần chỉnh sửa tích hợp trí tuệ nhân tạo

Hình 2.30: Giao diện chính 3.1.2 Xây dựng chức năng xử lý ảnh

Hình 31: Mơ hình thao tác xử lý ảnh

Bộ lọc là các thao tác xử lý trong miền không gian, cụ thể ở đồ án này sử dụng 3 phương pháp biến đổi:

 Toán tử điểm ảnh (lý thuyết được trình bày tại mục 1.2.2)

 Lọc trong miền khơng gian (lý thuyết được trình bày tại mục 1.2.3)

 Biến đổi bằng hệ màu HSV (lý thuyết được trình bày tại mục 1.4.1)

3.1.2.1 Các chức năng xử lý bằng toán tử điểm ảnh

Chương 4 Chức năng đảo ngược mức sáng (âm bản)

Lý thuyết xem lại mục 1.2.2.2

Cài đặt: Vì ảnh đầu vào là một ma trận của thư viện numpy, thư viện này rất mạnh mẽ trong việc xử lý ma trận, có thể sử dụng như một toán tử để thực hiện cộng trừ nhân chia tương tự tốn tử số học. Vì vậy, chức năng này được thực hiện với một dòng lệnh đơn giản:

image = 255 - image

Chương 5 Chức năng phân ngưỡng: nhằm đưa ảnh về thành ảnh nhị phân (Xem lý thuyết tại mục 1.2.2.3)

Cài đặt: Chức năng này có sẵn trong thư viện OpenCV với hàm cv2.threshold, trong đó tham số thứ nhất là ảnh đầu vào, tham số thứ 2 là ngưỡng, tham số thứ 3 là giá trị mức sáng sao nhất, tham số thứ 4 là hàm phân ngưỡng, thường dung là hàm THRESH_BINARY

image = cv2.threshold(image, threshsold_value, 255, cv2.THRESH_BINARY)

Hình 33: Chức năng cắt ngưỡng

Chương 6 Chức năng điều chỉnh độ tương phản:

Lý thuyết về biến đổi tương phản xem lại mục 1.2.2.4

Để biến đổi độ tương phản ta sử dụng công thức:

g(x) = α f(x) + β với α >0

Nếu α < 1 và β > 0 thì làm giảm tương phản của ảnh Nếu α > 1 và β < 0 thì làm tăng tương phản của ảnh

Cài đặt: Với tính chất của α và β như trên, ta chọn giá trị điều chỉnh độ tương phản (contrast_value) nằm trong khoảng từ -127 đến 127, khi đó:

α = contrast_value/127 + 1 β = -contrast

Khi contrast_value < 0 thì 0 < α < 1, β > 0, độ tương phản giảm Khi contrast_value > 0 thì 1 < α < 2, β < 0, độ tương phản tang

Sau khi thực hiện biến đổi trên, sẽ có các pixel có giá trị nằm ngồi khoảng (0, 255) và ở dạng số thập phân, vì vậy cần thêm một thao tác cắt bỏ các pixel này rồi đưa ảnh về dạng số nguyên.

alpha = contrast_value/127 + 1 beta = - contrast_value

image = image * alpha + beta image = np.clip(image, 0, 255) image = np.uint8(image)

Hình 34: Chức năng tăng giảm độ tương phản 6.1.1.1 Các chức năng xử lý bằng tốn tử khơng gian

Xử lý bằng tốn tử khơng gian là dùng các mặt nạ lọc, kết hợp với các pixel lân cận từ đó tính được kết quả mong muốn (xem cụ thể tại mục 1.2.3). Nếu mà lập trình đơn thuần bằng cách duyệt các pixel trên ảnh thì rất tốn thời gian, vì vậy, trong thư viện OpenCV có hàm filter2D cho phép thực hiện lọc trong miền khơng gian rất nhanh chóng, vì vậy, việc cịn lại là chỉ cần đưa mặt nạ lọc, ảnh đầu vào nữa là được.

Cài đặt: mask là mặt nạ lọc, 1 mảng numpy image = cv2.filter2D(image, -1, mask)

Với một số chức năng mà mặt nạ lọc là cố định và đã có sẵn (làm sắc nét, dị biên) thì chúng ta chỉ cần đưa mặt nạ lọc vào là được. Đối với các chức năng mà mặt nạ lọc thay đổi (làm mờ), thì chúng ta cần cài đặt hàm sinh mặt nạ. Dưới đây là hàm cài đặt để sinh ra mặt nạ lọc trung bình:

def avg_mask(size): # đầu vào x là kích thước của mặt nạ avg = 1/(size*size)

return np.full((size, size), avg)

Hình 35: Chức năng làm mờ

6.1.1.2 Biến đổi bằng không gian màu HSV

Không gian màu HSV rất thường xuyên được sử dụng trong chỉnh sửa ảnh, nó cung cấp 3 kênh giá trị riêng biệt làm cho việc chỉnh sửa ảnh trở nên dễ dàng hơn. Về lý thuyết khơng gian màu HSV có thể xem lại tại mục 1.4.1

Cài đặt: Trước khi biến đổi bằng khơng gian màu HSV thì phải chuyển ảnh về không gian màu này và lấy ra được 3 kênh Hue(h), Saturation(s), Value(v):

hsv_image = cv2.cvtColor(image, cv2.COLOR_RGB2HSV) hsv_image =

cv2.cvtColor(image, cv2.COLOR_RGB2HSV) h, s, v = cv2.split(hsv_image)

Các phép biến đổi trên ba kên h, s, v đều tương tự nhau. Sau khi cộng (trừ) một lượng thay đổi value, trong kênh màu đó sẽ có các giá trị vượt qua mức sáng từ 0 đến 255, vì vậy ta cần thêm một thao tác cắt bỏ các giá trị đó nữa, dưới đây là biến đổi độ sáng của ảnh bằng cách thay dổi trên kênh value(v), các kênh khác cũng thực hiện tương tự:

v = v + value

v = np.clip(v, 0, 255)

Dưới đây là một số hình ảnh biến đổi bằng khơng gian màu hsv

Hình 38: Chức năng thay đổi màu sắc của ảnh

6.1.2 Xây dựng trí tuệ nhân tạo cho chương trình

Trong phần mềm có chức năng gọi là bokeh, tức là làm mờ background để làm nổi bật chủ thể. Vì vậy ta cần một mơ hình học máy để đánh dấu được chủ thể ở trong bức ảnh.

6.1.2.1 Bài toán

Sử dụng Deep learning để tách được chủ thể trong 1 bức ảnh.

Hình 40: Bài tốn đặt ra Dữ liệu đầu vào: Một bức ảnh màu với kích thước bất kỳ.

Dữ liệu đầu ra: Một bức ảnh nhị phân có cùng kích thước với ảnh đầu vào, các pixel ảnh

chỉ có 2 giá trị: 1 với pixel đó là pixel tạo thành chủ thể, 0 với pixel đó là phần background.

Đây là bài tốn phân đoạn ảnh (Segmentation) trong xử lý ảnh, nó khác với các bài toán phân loại ảnh là đầu ra nó chỉ là tên của một lớp, chỉ quan tâm đến sự xuất hiện của vật thể trong ảnh.Vì vậy, cách tiếp cận của bài tốn này khơng giống với các bài tốn phân loại, chúng ta phải tìm cách để phân loại cho từng pixel trong ảnh.

6.1.2.2 Xây dựng mơ hình học máy

Đối với bài toán này, chúng em chọn mơ hình Unet như đã trình bày ở phần lý thuyết vì độ chính xác khá cao và implement khá dễ.

Dưới đây là đoạn code implement của mơ hình Unet, tham khảo tại Unet implement[3]: inputs = tf.keras.layers.Input((256, 256, 3)) s = tf.keras.layers.Lambda(lambda x: x / 255)(inputs) #Contraction path c1 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(s) c1 = tf.keras.layers.Dropout(0.1)(c1) c1 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c1) p1 = tf.keras.layers.MaxPooling2D((2, 2))(c1) c2 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p1) c2 = tf.keras.layers.Dropout(0.1)(c2) c2 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c2) p2 = tf.keras.layers.MaxPooling2D((2, 2))(c2) c3 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p2) c3 = tf.keras.layers.Dropout(0.2)(c3)

c3 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c3) p3 = tf.keras.layers.MaxPooling2D((2, 2))(c3) c4 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p3) c4 = tf.keras.layers.Dropout(0.2)(c4) c4 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c4) p4 = tf.keras.layers.MaxPooling2D(pool_size=(2, 2))(c4) c5 = tf.keras.layers.Conv2D(256, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p4) c5 = tf.keras.layers.Dropout(0.3)(c5) c5 = tf.keras.layers.Conv2D(256, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c5) #Expansive path u6 = tf.keras.layers.Conv2DTranspose(128, (2, 2), strides=(2, 2), padding='same')(c5) u6 = tf.keras.layers.concatenate([u6, c4]) c6 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u6) c6 = tf.keras.layers.Dropout(0.2)(c6) c6 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c6) u7 = tf.keras.layers.Conv2DTranspose(64, (2, 2), strides=(2, 2), padding='same')(c6) u7 = tf.keras.layers.concatenate([u7, c3]) c7 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u7) c7 = tf.keras.layers.Dropout(0.2)(c7) c7 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c7) u8 = tf.keras.layers.Conv2DTranspose(32, (2, 2), strides=(2, 2), padding='same')(c7) u8 = tf.keras.layers.concatenate([u8, c2]) c8 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u8) c8 = tf.keras.layers.Dropout(0.1)(c8) c8 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c8) u9 = tf.keras.layers.Conv2DTranspose(16, (2, 2), strides=(2, 2), padding='same')(c8) u9 = tf.keras.layers.concatenate([u9, c1], axis=3) c9 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u9)

c9 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c9)

outputs = tf.keras.layers.Conv2D(1, (1, 1), activation='sigmoid')(c9) model = tf.keras.Model(inputs=[inputs], outputs=[outputs])

model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

Dữ liệu đầu vào là ảnh màu với kích thước 256x256, nên phải xử lý ảnh đầu vào để về kích thước của model

Dữ liệu để đào tạo (train): Sử dụng bộ dữ liệu Supervise.ly Filtered Segmentation Person

Dataset[1] gồm 2667 hình ảnh con người với các kích thước, hành động, trang phục khác nhau để đào tạo (train model)

Quá trình train model được xử lý ở trên google colab với hơn 2 tiếng để load dữ liệu và train.

Kết quả quá trình train: Độ chính xác trên tập train và tập test là hơn 95%

Hình 41: Ảnh test model 6.1.2.3 Sử dụng trí tuệ nhân tạo trong phần mềm

Sau khi đã có model, thì việc cịn lại là sử dụng nó như thế nào. Đầu tiên cần lấy được mặt nạ của chủ thể trong hình ảnh.

def get_mask_from_image(image, threshold_value = 0.5, same_size = False): copy_image = cv2.resize(image, IMAGE_SIZE)

copy_image = np.reshape(copy_image, MASK_PREDICT_SHAPE) predict_mask = get_model().predict(copy_image)[0]

_, threshsold_mask = cv2.threshold(predict_mask, threshold_value, 1, cv2.THRESH_BINARY)

mask = cv2.normalize(threshsold_mask, None, 0, 1, cv2.NORM_MINMAX).astype(np.uint8)

mask = np.reshape(mask, (256, 256, 1)) if not same_size:

mask = cv2.resize(mask, (image.shape[1], image.shape[0])) return mask

Sau đó, thực hiện làm mờ hình ảnh gốc để thu được background mới. Duyệt các pixel trong mặt nạ (mask), với các pixel có giá trị bằng 0 trong mặt nạ thì sao chép pixel tương ứng ở background mới đã được làm mờ vào ảnh mới. Với pixel có giá trị bằng 1 ở mặt nạ (mask), sao chép pixel ở vị trí tương ứng của ảnh gốc vào ảnh mới. Kết quả ta thu được ảnh bokeh.

def get_bokeh_image(image, blur_shape = (21,21), blur_sigma = 1.5, threshold_value = 0.5, mask = None):

width = image.shape[1] height = image.shape[0] if len(mask) == 0:

mask = get_mask_from_image(image, threshold_value, False) mask = np.reshape(mask, (height, width, 1))

else:

mask = np.reshape(mask, (height, width, 1))

copy_image = image

blur_image = cv2.GaussianBlur(copy_image, blur_shape, blur_sigma) blur_image = cv2.normalize(blur_image, None, 0, 255,

cv2.NORM_MINMAX).astype(np.uint8)

result = np.where(mask == 0, blur_image, copy_image) return result

Kết quả:

Hình 42: Ảnh kết quả 6.1.3 Thử nghiệm và đánh giá kết quả

6.1.3.1 Thử nghiệm

Giao diện ban đầu bao gồm:

 2 hình ảnh hiển thị trước và sau khi chỉnh sửa

 Phía bên phải gồm các chức năng xử lý ảnh và biểu đồ histogram

 Phía trên là thanh cơng cụ với các công cụ như mở ảnh, lưu ảnh, ẩn hiện histogram, vẽ, cắt ảnh

 Thử nghiệm các chức năng xử lý ảnh

 Thử nghiệm phần tích hợp trí tuệ nhân tạo

Hình 44: Giao diện phần trí tuệ nhân tạo 6.1.3.2 Đánh giá kết quả

Xây dựng thành cơng chương trình xử lý ảnh tích hợp trí tuệ nhân tạo với các chức năng hỗ trợ xử lý hình ảnh.

Một phần của tài liệu 1622190092-baocaodoan5-duongquockhanh-hoangngocphuong (Trang 29)

Tải bản đầy đủ (DOC)

(46 trang)
w