Các hàm trong opencv python

Tải hình ảnh y tế lên Python

Trong bài này, chúng ta sẽ làm việc với các phần của chụp cắt lớp vi tính (CT) từ Kho lưu trữ hình ảnh ung thư . CT sử dụng một ống tia X quay để tạo ra hình ảnh 3D trên vùng mục tiêu của cơ thể bệnh nhân.

Hình ảnh thực tế phụ thuộc vào thiết bị được sử dụng: hình ảnh đo ánh sáng nhìn thấy, tia X và CT đo độ hấp thụ bức xạ, và máy quét MRI đo từ trường.

Hãy sử dụng imageio để tải một hình ảnh DICOM từ ổ đĩa cần quét và kiểm tra một vài thuộc tính của nó.

Các hàm trong opencv python
Các Toolbox dùng cho việc xử lý hình ảnh y sinh.
Các hàm trong opencv python
im = imageio.imread('./dataset/tcia-chest-ct-sample/chest-220.dcm')

# Print image attributes
print('Image type:', type(im))
print('Shape of image array:', im.shape)

imageiolà một hàm đa năng. Nó có thể đọc nhiều loại dữ liệu hình ảnh, bao gồm JPEG, PNG và TIFF. Nhưng nó đặc biệt hữu ích cho khả năng xử lý các tệp DICOM.

Image type: 
Shape of image array: (512, 512)

Metadata

ImageIO:đọc dữ liệu dưới dạng đối tượng Image. Đây là các mảng NumPy tiêu chuẩn với nhiều siêu dữ liệu.Siêu dữ liệu có thể khá phong phú trong hình ảnh y tế và có thể bao gồm:

+Nhân khẩu học của bệnh nhân: tên, tuổi, giới tính, thông tin lâm sàng.

+Thông tin thu nhận: hình dạng hình ảnh, tỷ lệ lấy mẫu, loại dữ liệu, phương thức (chẳng hạn như X-Ray, CT hoặc MRI).

Các hàm trong opencv python

Plot images:

imshow():Chức năng của Matplotlib cung cấp cho bạn một cách đơn giản để thực hiện việc này. Biết một vài thao tác code đơn giản sẽ giúp:

+cmap: kiểm soát ánh xạ màu cho từng giá trị. Bản đồ màu « xám » là phổ biến, nhưng có nhiều bản đồ khác.

+vminvà vmax:kiểm soát độ tương phản màu giữa các giá trị. Thay đổi các giá trị này có thể làm giảm ảnh hưởng của các giá trị extreme .

plt.axis('off') loại bỏ trục và đánh dấu nhãn khỏi hình ảnh.

Các hàm trong opencv python
Các hàm trong opencv python
fig, ax = plt.subplots(1, 3, figsize=(15, 10))
# Draw the image in grayscale
ax[0].imshow(im, cmap='gray');

# Draw the image with greater contrast
ax[1].imshow(im, cmap='gray', vmin=-200, vmax=200);

# Remove axis ticks and labels
ax[2].imshow(im, cmap='gray', vmin=-200, vmax=200);
ax[2].axis('off');
Các hàm trong opencv python
Các hàm trong opencv python
Các hàm trong opencv python
Hình ảnh y tế N chiều

+Hình dạng, lấy mẫu và trường xem (field of view).

+Hình dạng hình ảnh (Image shape): số phần tử dọc theo mỗi trục.

+Tốc độ lấy mẫu: không gian vật lý được bao phủ bởi từng phần tử.

+Trường nhìn: không gian vật lý được bao phủ dọc theo mỗi trục.

Các hàm trong opencv python

Xếp chồng hình ảnh (Stack images)

Các « ngăn xếp » (stacks) hình ảnh là một phương pháp hữu ích để hiểu dữ liệu đa chiều. Mỗi chiều cao hơn là một chồng các mảng có chiều thấp hơn.

Chúng ta sẽ sử dụng hàm stack() ở NumPy để kết hợp một số mảng 2D thành một khối 3D. Theo quy ước, thể tích dữ liệu nên được xếp chồng lên nhau dọc theo chiều hướng thứ nhất: vol[plane, row, col].Lưu ý: thực hiện bất kỳ thao tác nào trên Imageđối tượng ImageIO sẽ chuyển đổi nó thành một numpy.ndarray.

im1 = imageio.imread('./dataset/tcia-chest-ct-sample/chest-220.dcm')
im2 = imageio.imread('./dataset/tcia-chest-ct-sample/chest-221.dcm')
im3 = imageio.imread('./dataset/tcia-chest-ct-sample/chest-222.dcm')

# Stack images into a volume
vol = np.stack([im1, im2, im3], axis=0)
print('Volume dimensions:', vol.shape)
Volume dimensions: (3, 512, 512)
Các hàm trong opencv python

Load volumes:

volread():Chức năng của hàm này nằm nằm trong ImageIO là có thể tải bộ dữ liệu đa chiều và tạo khối lượng ảnh 3D từ một thư mục hình ảnh. Nó cũng có thể tổng hợp siêu dữ liệu trên nhiều hình ảnh này.

Đối với ví dụ này, hãy đọc toàn bộ khối lượng dữ liệu não bộ từ thư mục « ./dataset/tcia-chest-ct-sample », chứa 5 hình ảnh DICOM.

vol = imageio.volread('./dataset/tcia-chest-ct-sample/')

# Print image attributes
print('Available metadata:', vol.meta.keys())
print('Shape of image array:', vol.shape)
Các hàm trong opencv python

Field of view

Khoảng không gian vật lý được bao phủ bởi một hình ảnh là trường nhìn của nó, được tính toán từ hai thuộc tính:

+Hình dạng mảng, số phần tử dữ liệu trên mỗi trục. Có thể được truy cập bằng thuộc tính shape.

+Độ phân giải lấy mẫu, lượng không gian vật lý được bao phủ bởi mỗi pixel. Đôi khi có sẵn trong siêu dữ liệu (ví dụ meta['sampling']:).

Các hàm trong opencv python

Để vẽ dữ liệu N chiều, hãy cắt nó!

Chế độ xem không chuẩn

+Trục (Plain)

+Coronal (Hàng)

+sagittal (Col)

Các hàm trong opencv python

Tạo các ô phụ (cắt hình ảnh thành các ô):

Bạn có thể tạo nhiều hình ảnh trong một hình để khám phá dữ liệu nhanh chóng. Sử dụng plt.subplots()để tạo một mảng các ô con.

Ví dụ:

fig, axes = plt.subplots(nrows=2, ncols=2)

Để vẽ hình ảnh trên subplot, hãy gọi phương thức vẽ đồ thị trực tiếp từ đối tượng subplot chứ không phải thông qua PyPlot: axes[0,0].imshow(im)hơn là plt.imshow(im).

fig, axes = plt.subplots(nrows=1, ncols=2)

# Draw an images on each subplot
axes[0].imshow(im1, cmap='gray');
axes[1].imshow(im2, cmap='gray');

# Remove ticks/labels and render
axes[0].axis('off');
axes[1].axis('off');
Các hàm trong opencv python
Các hàm trong opencv python

Cắt hình ảnh 3D trong y tế

Cách đơn giản nhất để vẽ các hình ảnh 3D và 4D là làm bằng cách cắt chúng thành nhiều khung hình 2D. Việc vẽ nhiều lát một cách liên tiếp có thể tạo ra hiệu ứng « fly-through » giúp bạn hiểu toàn bộ hình ảnh.

Để chọn khung 2D, hãy chọn khung cho trục đầu tiên và chọn tất cả dữ liệu từ hai khung còn lại: vol[0, :, :]

Ví dụ:

fig, axes = plt.subplots(1, 5, figsize=(15, 10))

# Loop through subplots and draw image
for ii in range(5):
    im = vol[ii, :, :]
    axes[ii].imshow(im, cmap='gray', vmin=-200, vmax=200)
    axes[ii].axis('off')
Các hàm trong opencv python

Vẽ các chế độ xem khác

Bất kỳ hai kích thước nào của một mảng đều có thể tạo thành một hình ảnh và việc cắt dọc theo các trục khác nhau có thể cung cấp một góc nhìn hữu ích cho việc phân tích ảnh y tế. Tuy nhiên, tỷ lệ lấy mẫu không đồng đều có thể tạo ra hình ảnh bị méo.

Các hàm trong opencv python

Thay đổi tỷ lệ khung hình có thể giải quyết vấn đề này bằng cách tăng chiều rộng của một trong các kích thước.

Đối với ví dụ này, vẽ các hình ảnh cắt dọc theo chiều thứ hai và thứ ba của dữ liệu. Đặt tỷ lệ co (Explicitly set) một cách rõ ràng để tạo hình ảnh không bị biến dạng.

im1 = vol[:, 256, :]
im2 = vol[:, :, 256]

# Compute aspect ratios
d0, d1, d2 = vol.meta['sampling']
asp1 = d0 / d2
asp2 = d0 / d1

# Plot the images on a subplots array
fig, axes = plt.subplots(2, 1, figsize=(15, 8))
axes[0].imshow(im1, cmap='gray', aspect=asp1);
axes[1].imshow(im2, cmap='gray', aspect=asp2);
Các hàm trong opencv python
Các hàm trong opencv python
Các hàm trong opencv python

Cắt hình ảnh để xử lý đến xương bằng cách biến đổi hình ảnh X quang. Bạn sẽ được hướng dẫn cách khai thác các mẫu với các cường độ khác nhau để chọn được các mảng con của một mảng và sẽ sử dụng các bộ lọc tích hợp để phát hiện các tính năng thú vị. Bạn cũng sẽ sử dụng mô-đun ndimage của SciPy, chứa một kho tàng các công cụ xử lý hình ảnh.

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

plt.rcParams['figure.figsize'] = (10, 8)

Pixels và voxels

+Pixel: Yếu tố hình ảnh 2D

+Voxel: Phần tử của ảnh 3D

Biểu đồ (Histograms)

+Đếm số lượng pixel ở mỗi giá trị cường độ (each intensity value)

Cân bằng (Equalization)

+Sự phân bố thường lệch về cường độ thấp (giá trị nền) tức low intensity (background values)

+Phân bố lại các giá trị để tối ưu hóa toàn dải cường độ.

Cường độ ảnh y tế

Trong phần này, chúng ta sẽ làm việc với một bức ảnh chụp X quang tay từ một cuộc thi của Hiệp hội X quang Bắc Mỹ năm 2017 . Sự hấp thụ tia X cao nhất ở mô dày đặc như xương nên cường độ kết quả phải cao. Do đó, những hình ảnh như thế này có thể được sử dụng để dự đoán « tuổi xương » (« bone age ») ở trẻ em.

Để bắt đầu, hãy tải hình ảnh lên Python và kiểm tra phạm vi cường độ của nó.

Kiểu dữ liệu hình ảnh xác định phạm vi của các cường độ có thể có: ví dụ: số nguyên không dấu 8 bit ( uint8) có thể nhận các giá trị trong phạm vi từ 0 đến 255. Thanh màu (colorbar) có thể dùng để kết nối các giá trị này với hình ảnh được hiển thị.

def format_and_render_plot():
    '''Custom function to simplify common formatting operations for exercises. Operations include: 
    1. Turning off axis grids.
    2. Calling `plt.tight_layout` to improve subplot spacing.
    3. Calling `plt.show()` to render plot.'''
    fig = plt.gcf()
    for ax in fig.axes:
        ax.axis('off')    
    plt.tight_layout()
    plt.show()
im = imageio.imread('./dataset/hand.png')
im = im.astype('float64')
print('Data type:', im.dtype)
print('Min value:', im.min())
print('Max value:', im.max())

# Plot the grayscale image
plt.imshow(im, cmap='gray', vmin=0, vmax=255);
plt.colorbar();
Data type: float64
Min value: 3.0
Max value: 224.0
Các hàm trong opencv python

Histograms:

Biểu đồ (Histograms) hiển thị sự phân bố các giá trị trong hình ảnh của bạn bằng cách phân loại từng phần tử theo cường độ của nó, sau đó đo kích thước của mỗi ngăn.

Vùng dưới biểu đồ được gọi là hàm phân phối tích lũy (viết tắt là CDF-cumulative distribution function ). Nó đo tần suất xảy ra ở một phạm vi cường độ pixel nhất định.

Đối với ví dụ này, chúng ta biểu diễn sự phân bố cường độ trong im bằng cách tính toán biểu đồ (Histograms)  và hàm phân bố tích lũy và hiển thị chúng cùng nhau.

def format_and_render_plot():
    '''Custom function to simplify common formatting operations for exercises. Operations include: 
    1. Turning off axis grids.
    2. Calling `plt.tight_layout` to improve subplot spacing.
    3. Calling `plt.show()` to render plot.'''
    fig = plt.gcf()
    for ax in fig.axes:
        ax.legend(loc='center right')
    plt.show()
import scipy.ndimage as ndi

# Create a histogram, binned at each possible value
hist = ndi.histogram(im, min=0, max=255, bins=256)

# Create a cumulative distribution function
cdf = hist.cumsum() / hist.sum()

# Plot the histogram and CDF
fig, axes = plt.subplots(2, 1, sharex=True)
axes[0].plot(hist, label='Histogram');
axes[1].plot(cdf, label='CDF');
format_and_render_plot();
Các hàm trong opencv python

Bạn có thể thấy dữ liệu được tập hợp thành một số bản phân phối (separate distributions) riêng biệt, bao gồm sự nhiễu xung quanh, da, xương và artifacts. Đôi khi, chúng ta có thể tách biệt chúng với các ngưỡng toàn cục (tiền cảnh / hậu cảnh); sự phân bố chồng lên nhau khá nhiều (da / xương).

Mặt nạ (Masks)

Tạo mặt nạ

Mặt nạ (Masks) là phương pháp chính để loại bỏ hoặc chọn các phần cụ thể của hình ảnh. Chúng là các mảng nhị phân cho biết liệu một giá trị có nên được đưa vào phân tích hay không. Thông thường, mặt nạ được tạo bằng cách áp dụng một hoặc nhiều phép toán logic ( logical operations) cho một hình ảnh.

Đối với ví dụ này,chúng ta sẽ cố gắng sử dụng một ngưỡng cường độ đơn giản để phân biệt giữa da và xương trên phim chụp X quang bằng tay (the hand radiograph).

def format_and_render_plot():
    '''Custom function to simplify common formatting operations for exercises. Operations include: 
    1. Turning off axis grids.
    2. Calling `plt.tight_layout` to improve subplot spacing.
    3. Calling `plt.show()` to render plot.'''
    fig = plt.gcf()
    for ax in fig.axes:
        ax.axis('off')    
    plt.tight_layout()
    plt.show()
mask_bone = im >= 75
mask_skin = (im >= 35) & (im < 75)

# Plot the skin (0) and bone (1) masks
fig, axes = plt.subplots(1, 2)
axes[0].imshow(mask_skin, cmap='gray')
axes[1].imshow(mask_bone, cmap='gray')
format_and_render_plot()
Các hàm trong opencv python

ứng dụng của một mask:

Mặc dù mặt nạ là nhị phân, chúng có thể được áp dụng vào hình ảnh để lọc ra các pixel ở các vị trí của mặt nạ False.

where():Chức năng của NumPy là một hàm linh hoạt để ứng dụng 1 mặt nạ ảnh. Nó có ba đối số:

np.where(condition, x, y)

conditionxVà ycó thể là mảng hoặc giá trị duy nhất (single values). Điều này cho phép bạn chuyển qua các giá trị hình ảnh gốc trong khi đặt các giá trị bị che thành 0.

Hãy thử áp dụng mặt nạ bằng cách chọn các pixel giống như xương từ tia xquang bàn tay (the hand x-ray ) ( im).

def format_and_render_plot():
    '''Custom function to simplify common formatting operations for exercises. Operations include: 
    1. Turning off axis grids.
    2. Calling `plt.tight_layout` to improve subplot spacing.
    3. Calling `plt.show()` to render plot.'''
    fig = plt.gcf()
    fig.axes[0].axis('off')   
    plt.tight_layout()
    plt.show()
mask_bone = im >= 75
im_bone = np.where(mask_bone, im, 0)

# Get the histogram of bone intensity
hist = ndi.histogram(im_bone, min=1, max=255, bins=255)

# Plot masked image and histogram
fig, axes = plt.subplots(2, 1)
axes[0].imshow(im_bone, cmap='gray')
axes[1].plot(hist)
format_and_render_plot()
Các hàm trong opencv python

Chỉnh mặt nạ (Tune a mask):

Mặt nạ không hoàn hảo có thể được điều chỉnh thông qua việc cộng và trừ các pixel. SciPy bao gồm một số tính năng hữu ích để thực hiện những mục tiêu này. Bao gồm các:

+binary_dilation: Thêm pixel dọc theo các cạnh

+binary_erosion: Loại bỏ các pixel dọc theo các cạnh

+binary_opening: Làm mòn (Erode) sau đó mở rộng, « mở » các khu vực gần các cạnh.

+binary_closing: Mở rộng rồi làm mòn (Erode), « lấp đầy » (« filling in ») các lỗ hổng.

Đối với ví dụ này,chúng ta hãy tạo một mặt nạ của xương sau đó điều chỉnh đểó bao gồm các pixel được bổ sung.

mask_bone = im >= 75
mask_dilate = ndi.binary_dilation(mask_bone, iterations=5)
mask_closed = ndi.binary_closing(mask_bone, iterations=5)

# Plot masked images
fig, axes = plt.subplots(1, 3)
axes[0].imshow(mask_bone, cmap='gray')
axes[1].imshow(mask_dilate, cmap='gray')
axes[2].imshow(mask_closed, cmap='gray')
format_and_render_plot()
Các hàm trong opencv python

Bộ lọc

Chuyển đổi với bộ lọc làm sắc nét.

Các hàm trong opencv python

Lọc biến (Filter convolutions):

Bộ lọc là một công cụ thiết yếu trong xử lý ảnh. Chúng cho phép bạn chuyển đổi hình ảnh dựa trên các giá trị cường độ xung quanh một pixel, thay vì toàn bộ.

Các hàm trong opencv python

Đối với ví dụ này, chúng ta hãy làm mịn ảnh X quang bàn chân. Đầu tiên, định rõ trọng số ( weights) sẽ được sử dụng. Chúng được gọi là « dấu chân » và « hạt nhân »(« footprints » and « kernels » ). Sau đó, xoay bộ lọc với im và vẽ biểu đồ kết quả.

def format_and_render_plot():
    '''Custom function to simplify common formatting operations for exercises. Operations include: 
    1. Turning off axis grids.
    2. Calling `plt.tight_layout` to improve subplot spacing.
    3. Calling `plt.show()` to render plot.'''
    fig = plt.gcf()
    for ax in fig.axes:
        ax.axis('off')
    plt.tight_layout()
    plt.show()
weights = [[0.11, 0.11, 0.11],
           [0.11, 0.11, 0.11], 
           [0.11, 0.11, 0.11]]

# Convolve the image with the filter
im_filt = ndi.convolve(im, weights)

# Plot the images
fig, axes = plt.subplots(1, 2)
axes[0].imshow(im, cmap='gray')
axes[1].imshow(im_filt, cmap='gray')
format_and_render_plot()
Các hàm trong opencv python

Làm mịn ảnh y tế

Làm mịn có thể cải thiện tỷ lệ tín hiệu trên nhiễu (viết tắt là SNR) của hình ảnh của bạn bằng cách làm mờ về cường độ của các biến thể nhỏ . Bộ lọc Gaussian là một giải pháp tuyệt vời cho điều này.

Các hàm trong opencv python

Độ rộng của sự phân bố được kiểm soát bởi đối số sigma, với các giá trị cao hơn dẫn đến hiệu ứng làm mịn lớn hơn.

Đối với ví dụ này, chúng ta hãy kiểm tra tác động của việc áp dụng bộ lọc Gaussian vào phim chụp X-quang bàn chân trước khi tạo mặt nạ với xương.

im_s1 = ndi.gaussian_filter(im, sigma=1)
im_s3 = ndi.gaussian_filter(im, sigma=3)

# Draw bone masks of each image
fig, axes = plt.subplots(1, 3)
axes[0].imshow(im >= 75, cmap='gray')
axes[1].imshow(im_s1 >= 75, cmap='gray')
axes[2].imshow(im_s3 >= 75, cmap='gray')
format_and_render_plot()
Các hàm trong opencv python

Dò tìm các tính năng của ảnh

Phát hiện các cạnh (Detect edges) (1):

Bộ lọc cũng có thể được sử dụng làm « máy dò »(« detectors »). Nếu một phần của hình ảnh phù hợp với mẫu trọng số, giá trị trả về sẽ rất cao (hoặc rất thấp).Trong trường hợp phát hiện cạnh, mẫu đó có sự thay đổi cường độ dọc theo một mặt phẳng. Bộ lọc giúp phát hiện các cạnh ngang có thể trông như thế này:

weights = [[+1, +1, +1],
           [ 0,  0,  0],
           [-1, -1, -1]]

Đối với ví dụ này,chúng ta hãy tạo một bộ dò dọc cạnh và xem nó hoạt động tốt như thế nào trên ảnh X-quang bàn tay (im).

weights = [[1, 0, -1], [1, 0, -1], [1, 0, -1]]

# Convolve "im" with filter weights
edges = ndi.convolve(im, weights)

# Draw the image in color
plt.imshow(edges, cmap='seismic', vmin=-75, vmax=75);
plt.colorbar();
format_and_render_plot();
Các hàm trong opencv python

Phát hiện các cạnh ảnh y tế (2):

Việc phát hiện cạnh có thể được thực hiện dọc theo nhiều trục.Sau đó,chúng được kết hợp thành một giá trị với cạnh duy nhất. Đối với hình ảnh 2D, « bản đồ cạnh » (« edge maps » ) ngang và dọc có thể được kết hợp bằng cách sử dụng định lý Pitago:

Các hàm trong opencv python

Một bộ dò cạnh phổ biến là bộ lọc Sobel . Bộ lọc Sobel cung cấp thêm các trọng số cho các pixel ở trung tâm của bộ dò tìm:

weights = [[ 1,  2,  1], 
           [ 0,  0,  0],
           [-1, -2, -1]]

Đối với ví dụ này, chúng ta hãy cải thiện sự phát hiện trước đó bằng cách hợp nhất kết quả của hai hình ảnh được lọc Sobel thành một bản đồ cạnh tổng hợp (composite edge map).

def format_and_render_plot():
    '''Custom function to simplify common formatting operations for exercises. Operations include: 
    1. Turning off axis grids.
    2. Calling `plt.tight_layout` to improve subplot spacing.
    3. Calling `plt.show()` to render plot.'''
    fig = plt.gcf()
    fig.axes[0].axis('off')
    plt.show()
sobel_ax0 = ndi.sobel(im, axis=0)
sobel_ax1 = ndi.sobel(im, axis=1)

# Calculate edge magnitude 
edges = np.sqrt(np.square(sobel_ax0) + np.square(sobel_ax1))
plt.imshow(edges, cmap='gray', vmax=75)
format_and_render_plot()
Các hàm trong opencv python

Nguồn tham khảo: godboychan

Người viết và tổng hợp:

+Fb: https://www.facebook.com/NguyenCongTrinh213/

+Instagram: https://www.instagram.com/nguyencongtrinh213/