Computer Vision

Histograms

Trong Image Processing, histogram được dùng để biểu diễn sự phân bố cường độ điểm ảnh trong một bức ảnh.

Để tính histogram, chúng ta sử dụng function:

cv2.calcHist([images], [channels], mask, [histSize], [ranges])

  • [images] – một list các hình ảnh cần tính histogram.
  • [channels] – một list các index tương ứng với các channel cần tính histogram, đánh số từ 0.
  • mask – calculate histogram for masked pixels only.
  • [histSize] – một list các số khoảng dùng để tính histogram. Nếu histSize = 256 thì mỗi giá trị cường độ (trong khoảng [0,255]) sẽ tương ứng với một khoảng, tuy nhiên nếu histSize = 2 thì chúng ta chỉ có 2 khoảng giá trị là [0,128) và [128,255]. Do đó, histSize càng lớn thì histogram càng chính xác. histSize của từng channel không nhất thiết phải giống nhau.
  • [ranges] – khoảng giá trị của cường độ điểm ảnh, thường là [0,256] cho mỗi channel trong hệ BGR.

GRAYSCALE HISTOGRAM

Example 1: grayscale histograms

from matplotlib import pyplot as plt
import cv2

image = cv2.imread("image_path")
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imshow("Original", image)

hist = cv2.calcHist([image], [0], None, [256], [0, 256])

plt.figure()
plt.title("Grayscale Histogram")
plt.xlabel("Bins")
plt.ylabel("# of Pixels")
plt.plot(hist)
plt.xlim([0, 256])
plt.show()
cv2.waitKey(0)

Output:

grayscalehist

Với kết quả trên ta thấy rằng, đa số các pixel có cường độ ở khoảng từ 60 đến 120 và rất ít pixel trong khoảng 200-255. Điều này có nghĩa rất it pixel có màu trắng trong bức ảnh này.


COLOR HISTOGRAMS

Example 2: color histograms

from matplotlib import pyplot as plt
import numpy as np
import cv2

image = cv2.imread("image_path")
cv2.imshow("Original", image)
chans = cv2.split(image)
colors = ("b", "g", "r")
plt.figure()
plt.title("'Flattened' Color Histogram")
plt.xlabel("Bins")
plt.ylabel("# of Pixels")

for (chan, color) in zip(chans, colors):
    hist = cv2.calcHist([chan], [0], None, [256], [0, 256])
    plt.plot(hist, color = color)
    plt.xlim([0, 256])
plt.show()

Output:

colorhist

Với kết quả trên, ta nhận thấy rằng cường độ của màu đỏ trong bức ảnh khá thấp, tập trung chủ yếu ở khoảng 30 đến 70. Cường độ của màu xanh lá tập trung ở khoảng 80 đến 120, đây là màu xanh lá đậm của cây cối trong bức ảnh. Cường độ của màu xanh dương tập trung chủ yếu ở khoảng 170 đến 220 (màu xanh da trời) và khoảng 25 đến 50 (màu xanh đậm của nước biển).

Bây giờ chúng ta sẽ nghiên cứu multi-dimensional histogram. Multi-dimensional histogram sẽ trả lời cho câu hỏi: ví dụ “Có bao nhiêu pixel có cường độ RED là 100 và cường độ GREEN là 200?”.

Example 3: multi-dimensional histogram

from matplotlib import pyplot as plt
import numpy as np
import cv2

image = cv2.imread("image_path")
cv2.imshow("Original", image)
chans = cv2.split(image)

fig = plt.figure()
ax = fig.add_subplot(131)
hist = cv2.calcHist([chans[0],chans[1]], [0,1], None, [32,32], [0,256,0,256])
# or use
# hist = cv2.calcHist([image], [0,1], None, [32,32], [0,256,0,256])
p = ax.imshow(hist, interpolation = "nearest")
ax.set_title("G and B")
plt.ylabel("Blue")
plt.xlabel("Green")
plt.colorbar(p)

ax = fig.add_subplot(132)
hist = cv2.calcHist([chans[1],chans[2]], [0,1], None, [32,32], [0,256,0,256])
# or use
# hist = cv2.calcHist([image], [1,2], None, [32,32], [0,256,0,256])
p = ax.imshow(hist, interpolation = "nearest")
ax.set_title("G and R")
plt.ylabel("Green")
plt.xlabel("Red")

ax = fig.add_subplot(133)
hist = cv2.calcHist([chans[2],chans[0]], [0,1], None, [32,32], [0,256,0,256])
# or use
# hist = cv2.calcHist([image], [2,0], None, [32,32], [0,256,0,256])
p = ax.imshow(hist, interpolation = "nearest")
ax.set_title("B and R")
plt.ylabel("Red")
plt.xlabel("Blue")
plt.colorbar(p)

print("2D histogram shape: {}, with {} values".format(hist.shape, hist.flatten().shape[0]))

plt.show()

Output:
multihist

Ở example 3, chúng ta sử dụng histSize = 32 thay vì 256 như các ví dụ trước. Phần tử đầu tiên của list [image] trong function cv2.calcHist() sẽ được biểu diễn trên trục y, phần tử còn lại trên trục x. Dựa vào cột màu bên cạnh từng đồ thị (pixel count), ta có thể dễ dàng trả lời câu hỏi đã đưa ra ở trên.

Chú ý kỹ chúng ta sẽ nhận ra mối liên hệ giữa single color histogrammulti-dimensional histogram.


HISTOGRAM EQUALIZATION

Histogram equalization giúp cải thiệt độ tương phản của bức ảnh bằng cách kéo giãn sự phân bố của các điểm ảnh. Histogram equalization được áp dụng cho Grayscale image.

Phương pháp này hữu ích khi bức ảnh có background và foreground cùng màu sáng hoặc cùng màu tối. Nó có xu hướng tạo ra các hiệu ứng không thực tế trên bức ảnh, tuy nhiên, thường là hữu ích khi tăng cường sự tương phản trong ảnh vệ tinh hoặc ảnh y tế.

Chúng ta sử dụng function cv2.equalizeHist(grayscaleImage) để thực hiện histogram equalization.

Example 4: Equalization

import numpy as np
import cv2

image = cv2.imread("image_path")
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

eq = cv2.equalizeHist(image)

cv2.imshow("Histogram Equalization", np.hstack([image,eq]))
cv2.waitKey(0)

Output:

equalize


HISTOGRAM NORMALIZATION

Histogram Normalization là sự chuẩn hóa các giá trị của Histogram. Xem lại ví dụ 1 ta thấy rằng giá trị trên trục Y của Histogram là số lượng pixel có màu sắc tương ứng với bin ở trục X. Vậy nếu chúng ta cần biết tỉ lệ phần trăm của các màu sắc trong bức ảnh thì sao? Câu trả lời chính là Histogram Normalization. Có 2 cách để thực hiện Normalization:

  1. hist /= hist.sum() : chia histogram cho tổng các giá trị của nó, tương đương với L1-normalization;
  2. hist = cv2.normalize(src, dst, alpha, beta, norm_type).

Example 5:
Input:
input
Code:

import cv2
import numpy as np
import argparse
from matplotlib import pyplot as plt

ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image")
args = vars(ap.parse_args())

image = cv2.imread(args["image"])
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imshow("Gray", gray)

hist = cv2.calcHist([gray], [0], None, [256], [0,256])

plt.figure()
plt.subplot(311)
plt.xlabel("Bins")
plt.ylabel("# of Pixels")
plt.plot(hist)
plt.xlim([0, 256])

normalizedHist1 = hist / hist.sum()

plt.subplot(312)
plt.xlabel("Bins")
plt.ylabel("% of Pixels")
plt.plot(normalizedHist1)
plt.xlim([0, 256])

normalizedHist2 = cv2.normalize(hist, hist, 0, 255, cv2.NORM_MINMAX)

plt.subplot(313)
plt.xlabel("Bins")
plt.ylabel("Density of Bins")
plt.plot(normalizedHist2)
plt.xlim([0, 256])

plt.tight_layout()
plt.show()
cv2.waitKey(0)

Output:
hist


HISTOGRAMS AND MASKS

Như đã đề cập ở bài “Masking, Splitting & Merging”, chúng ta có thể sử dụng mask để tập trung vào phần hình ảnh cần xử lý và chúng ta cũng có thể tính histogram cho phần hình ảnh này.

Example 6: histogram with mask

from matplotlib import pyplot as plt
import numpy as np
import cv2

def plot_histogram(image, title, mask=None):
    chans = cv2.split(image)
    colors = ("b", "g", "r")
    plt.figure()
    plt.title(title)
    plt.xlabel("Bins")
    plt.ylabel("# of Pixels")

    for (chan,color) in zip(chans, colors):
        hist = cv2.calcHist([chan], [0], mask, [256], [0,256])
        plt.plot(hist, color=color)
        plt.xlim([0,256])

image = cv2.imread("image_path")
cv2.imshow("Original", image)
plot_histogram(image, "Histogram for Original Image")
mask = np.zeros(image.shape[0:2], dtype="uint8")
cv2.rectangle(mask, (15,15), (130,100), 255, -1)
cv2.imshow("Mask", mask)

masked = cv2.bitwise_and(image, image, mask=mask)
cv2.imshow("Applying the Mask", masked)
plot_histogram(image, "Histogram for masked image", mask = mask)

plt.show()

Output:
histogramMask

Có thể thấy rằng 2 histogram nhận được hoàn toàn khác nhau .

Trong phần hình ảnh đã được masked, cường độ màu đỏ khá thấp, nằm ở khoảng 0 đến 80; cường độ màu xanh lá từ 90 đến 140 trung bình, đây là màu xanh lá đậm; cường độ màu xanh dương khá cao, nằm ở khoảng 170 đến 220, vì màu này khá trùng với màu của phần hình ảnh đang được khảo sát.

Histogram khá đơn giản nhưng được áp dụng rất nhiều trong Image Processing và Computer Vision. Hãy nắm vững những kiến thức này, chúng ta sẽ cần dùng nó rất nhiều trong tương lai.

Thân ái.


Reference:
[1] OpenCV normalize function.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s