Tuần 2: Phép toán với điểm - Điểu chỉnh độ tương phản

Ôn lại bài tuần 1

Ta sẽ nhắc lại các kiến thức đã học ở bài tuần 1:

  • Ảnh số là một hàm với mảng 2 chiều f(x,y)f(x,y)
  • Có nhiều loại ảnh số: chụp dùng cảm ứng ánh sáng, hoặc các loại sóng, hoặc hàm tùy chỉnh
  • Và chúng ta cũng biết cài đặt thư viện: OpenCV, Python, Jupyter Notebook

Số hoá ảnh

  • Định nghĩa: số hóa ảnh là biến đổi một ảnh (hay một hàm) liên tục trong không gian cũng như theo giá trị thành dạng số rời rạc.
  • Có 2 bước:
    • Lấy mẫu (sampling): đo giá trị trong các khoảng không gian
    • Lượng tử hóa (quantization): ánh xạ cường độ (hoặc giá trị) đo được thành một số hữu hạn các mức rời rạc
      Để cho trực quan hơn, ta có thể xem ví dụ dưới đây:

Hình 2.16 a là hình ảnh một đối tượng ảnh liên tục mà ta muốn chuyển đổi sang dạng ảnh số gọi là ảnh gốc.

Hình 2.16 b thể hiện các giá trị độ lớn của ảnh gốc dọc theo đoạn thẳng AB. Để lấy mẫu hàm này, ta chia đoạnh AB thành các khoảng bằng nhau như hình 2.16 c. Trong đó các vị trí lấy mẫu được đánh dấu bởi một đoạn thẳng nhỏ ở phía dưới hình và giá trị của mỗi mẫu được thể hiện là hình tròn nhỏ trên hàm. Tập hợp các vị trí rời rạc đó cho ta một hàm đã được lấy mẫu.

Hình bên trái của 2.16 c là cho biết miền giá trị của cường độ chia thành 8 khoảng rời rạc. Lượng tử hóa bằng các thực hiện gaasnmooxi giá trị cường độ vào 1 trong 8 khoảng rời rạc đó.

Sau khi thực hiện lấy mẫu và lượng tử hóa, ta có kết quả như sau:

Mức độ lượng tử hóa: Số lượng mức xám được yêu cầu trên một bức ảnh số tương ứng với số lượng bits trên mỗi pixel Ảnh số thường được lượng tử hoá thành 256 mức xám

Các biến đổi trên ảnh

Nhận biết bước xám (Tonal level perception)

Nếu mức xám dùng 8 bit nó sẽ là dải liên tục.

Biến đổi gamma

vout=vinγv_{out} = v_{in}^\gamma

vinv_{in} : độ sáng thực tế (actual luminance value)

voutv_{out}: độ sáng cảm nhận (output luminance value)

Ta sẽ có các ví dụ sau:

Cho dải màu xám 16 mức, với gamma = 1 thì nó sẽ giữ nguyên như sau:

Link code : here

# Không điều chỉnh gamma (no adjustment) 
draw_quantization_img(
    adjust_gamma(gray_16, gamma=1),
    height=2
)

Với gamma > 1, ta thấy nếu giá trị đầu vào là tối (thấp) thì đẩu ra là tối hơn, ví dụ đầu vào là giá trị 17 thì đầu ra là giá trị 0:

Link code: here

#with gamma >1, the levels are shifted toward the the darker end of the spectrum
draw_quantization_img(
        adjust_gamma(gray_16, gamma=2.2),
        height=2
)

Với gamma < 1, các giá trị đầu vào là thấp thì đẩu ra sáng hơn, ví dụ đầu vào là 17 đầu ra là giá trị 74:

Link code: here

#with gamma < 1, lighter
draw_quantization_img(
        adjust_gamma(gray_16, gamma=1/2.2, debug=True),
        height=2
)

Sử dụng gamma để điều chỉnh độ tương phản

1. Độ tương phản thấp (Low exposure)

Ví dụ ảnh vào là một ảnh tối như sau: Thì ta cần chỉnh độ sáng lên thì cần gamma < 1:

def adjust_image_gamma(image, gamma = 1.0):
  image = np.power(image, gamma)
  max_val = np.max(image.ravel())
  image = image/max_val * 255
  image = image.astype(np.uint8)
  return image

Link: here

Ta thử với 1 số giá trị. Với gamma = 0.45

low_adjusted = adjust_image_gamma(low, 0.45)
plt.imshow(low_adjusted[:,:,::-1])

Kết quả sẽ như sau, nhìn trông tốt hơn trước 😀:

Nếu như gamma quá nhỏ thì sao?

Link code: here

# what if gamma is too low?
low_adjusted = adjust_image_gamma(low, 0.1)
plt.imshow(low_adjusted[:,:,::-1])

Kết quả thì nó quá sáng:

Ta thử đo thời gian với hàm adjust_image_gamma:

%timeit low_adjusted = adjust_image_gamma(low, 0.1)
# kq: 1 loop, best of 3: 2.51 s per loop

Nó rất tốn thời gian, có cách nào nhanh hơn không ?. Thật ra là có 😃. Nó sẽ như sau:

Link code: here

#faster way to compute
#reference: https://www.pyimagesearch.com/2015/10/05/opencv-gamma-correction/
def adjust_image_gamma_lookuptable(image, gamma=1.0):
    # build a lookup table mapping the pixel values [0, 255] to
    # their adjusted gamma values
    table = np.array([((i / 255.0) ** gamma) * 255
        for i in np.arange(0, 256)]).astype("uint8")

    # apply gamma correction using the lookup table
    return cv2.LUT(image, table)

Ý tưởng ở đây là dùng bảng chuyển look-up table (LUT) sử dụng hàm cv2.LUT().

Thời gian chạy với hàm này sẽ là :

%timeit adjust_image_gamma_lookuptable(low, 0.45)
#kq: 10 loops, best of 3: 27.6 ms per loop

Tốc độ rất nhanh ( 27.6 ms >> 2.51s)

2. Độ tương phản cao (Overexposure)

Với trường hợp này thì ta cần giảm độ sáng xuống, hay tăng độ tối. Vì vậy, sử dụng gamma > 1:

Cho ảnh đầu vào :

Ở đây sử dụng gamma = 4.

adjusted_high = adjust_image_gamma_lookuptable(high, 4)
plt.imshow(adjusted_high[:,:,::-1])

Kết quả đẩu ra:

Correct using pixel transform

Reference: https://docs.opencv.org/3.4/d3/dc1/tutorial_basic_linear_transform.html

Dùng phép toán nhân và cộng g(x)=αf(x)+βg(x) = \alpha f(x) + \beta

α\alphaβ\beta còn được gọi là tham số gain và bias, hoặc tham số để điều chỉnh contrast (độ tương phản) và brightness (độ sáng)

Với ảnh số: g(i,j)=αf(i,j)+βg(i, j) = \alpha \cdot f(i, j) + \beta

Đầu vào vẫn là ảnh Overexposure. Link code: here

def pixel_transform(image, alpha = 1.0, beta = 0):
 '''
 out[pixel] = alpha * image[pixel] + beta
 '''
 output = np.zeros(image.shape, image.dtype)
 h, w, ch = image.shape
 for y in range(h):
   for x in range(w):
     for c in range(ch):
       output[y,x,c] = np.clip(alpha*image[y,x,c] + beta, 0, 255)

 return output
transformed_high = pixel_transform(high, 0.5, 20)
plt.imshow(transformed_high[:,:,::-1])

Kết quả đầu ra sẽ là :

Có một cách dùng thư viện của opencv để nhanh hơn:

#anyway, a faster 
transformed_high = cv2.convertScaleAbs(high, 20, 0.5)
plt.imshow(transformed_high[:,:,::-1])

Image negatives

Ta có thể chuyển từ ảnh bên trái sang bên phải ( đổi đen thành trắng và trắng thành đen 😃)

Nghĩ có về khó khăn nhưng thật ra rất đơn giản:

negation = 255 - xray
plt.imshow(negation, cmap='gray')

Combining images

1. Combination of different exposures for high-dynamic range imaging

Ta có 4 ảnh với 4 độ sáng khác nhau:

Ta sẽ kết hợp tính giá trị trung bình để tạo ra 1 ảnh mới đẹp hơn.

Link code: here

#Averaging to enhance contrast
hdr = np.mean(stack, axis=0)
hdr = hdr.astype(np.uint8)
plt.rcParams['figure.figsize'] = [8, 8]
plt.imshow(hdr[:,:,::-1])

Kết quả sẽ ra như sau:

2. Phép trừ ảnh:

Để tìm ra sự khác biệt giữa 2 ảnh với nhau. Đâu tiên ta sẽ trừ 2 ảnh sau đó tăng độ tương phản để nhìn rõ sự khac nhau hơn:

3. Video background subtraction

Từ phép trừ ảnh, ta ứng dụng váo video để tìm sự khác biệt khung hình trước và sau:

Tài liệu

Github: here

Colab: Here

Slide: Here

Intensity Transformation Operations on Images

Kết luận

Bài viết đến đây đã dài, nếu có thắc mắc hoặc sai sót gì các bạn có thể comment xuống dưới. Bài Tiếp theo chúng ta tìm hiểu Histogram. Xin chào và hẹn gặp lại các ban.