Warning: Undefined variable $position in /home/pystyles/pystyle.info/public_html/wp/wp-content/themes/lionblog/functions.php on line 4897

OpenCV – cv2.LUT の使い方、ガンマ補正、ネガポジ反転、ポスタリゼーション

OpenCV – cv2.LUT の使い方、ガンマ補正、ネガポジ反転、ポスタリゼーション

概要

OpenCV でネガポジ反転やポスタリゼーションなどの階調変換を画像に適用する方法を紹介します。

Advertisement

階調変換

画素値の入力を $x \in [0, 255]$ としたとき、$f(x) = y \in [0, 255]$ という関数で変換することを階調変換 (gray level transformation)、階調変換を規定する関数を階調変換関数 (gray level transformation function) といいます。 実装上は、画素値は $[0, 255]$ の256種類なので、各値の変換後の値を格納した長さが256の1次元配列である LUT (Look Up Table) を用意して階調変換を行います。

cv2.LUT

OpenCV では、cv2.LUT() で階調変換が行えます。

dst = cv2.LUT(src, lut[, dst])
引数
名前 デフォルト値
src ndarray
入力画像
lut sequence of int
Look Up Table の略。[0, 255] の値の変換後の値を (256,) の1次元配列で表す。例えば、lut[10] は入力10の変換後の値を表す。
返り値
名前 説明
dst 変換後の画像

sample.jpg

In [1]:
import cv2
import numpy as np
from IPython.display import Image, display


def imshow(img):
    """ndarray 配列をインラインで Notebook 上に表示する。
    """
    ret, encoded = cv2.imencode(".jpg", img)
    display(Image(encoded))


# 画像を読み込む。
img = cv2.imread("sample.jpg", cv2.IMREAD_GRAYSCALE)
imshow(img)

ネガポジ反転

画素値を反転させる階調変換をネガポジ反転といいます。

$f(x) = 255 – x$

In [2]:
def plot_grayscale_conversion(src, dst):
    fig = plt.figure(figsize=(10, 6))
    ax1 = plt.subplot2grid((3, 2), (0, 0), rowspan=2)
    ax2 = plt.subplot2grid((3, 2), (0, 1), rowspan=2)
    ax3 = plt.subplot2grid((3, 2), (2, 0))
    ax4 = plt.subplot2grid((3, 2), (2, 1))
    # 入力画像を描画する。
    ax1.set_title("Input")
    ax1.imshow(src, cmap="gray", vmin=0, vmax=255)
    ax1.set_axis_off()
    # 出力画像を描画する。
    ax2.set_title("Output")
    ax2.imshow(dst, cmap="gray", vmin=0, vmax=255)
    ax2.set_axis_off()
    # 入力画像のヒストグラムを描画する。
    ax3.hist(src.ravel(), bins=256, range=(0, 255), color="k")
    ax3.grid()
    ax3.set_xticks([0, 255])
    ax3.set_yticks([])
    # 出力画像のヒストグラムを描画する。
    ax4.hist(dst.ravel(), bins=256, range=(0, 255), color="k")
    ax4.set_xticks([0, 255])
    ax4.set_yticks([])
    ax4.grid()


# ネガポジ反転
x = np.arange(256)
y = 255 - x

# 変換する。
dst = cv2.LUT(img, y)
plot_grayscale_conversion(img, dst)
Advertisement

2値化

閾値などなんらかの条件で入力値を2段階にする階調変換を2値化 (binarization) といいます。

$$ f(x) = \begin{cases} 0 & x \le \mathrm{threshold} \\ 255 & その他の場合 \end{cases} $$
In [3]:
# 2値化
x = np.arange(256)
y = np.where(x <= 100, 0, 255)

# 変換する。
dst = cv2.LUT(img, y)
plot_grayscale_conversion(img, dst)

ポスタリゼーション

色を $n$ 段階にする階調変換をポスタリゼーション (postarization) といいます。 $[0, 255]$ の範囲に $n$ 個の区間を用意して、各値がどの区間に属するかを numpy.digitize() で調べて、LUT を作成します。

In [4]:
# ポスタリゼーション
x = np.arange(256)

n = 7  # 画素値を何段階で表現するか
bins = np.linspace(0, 255, n + 1)
y = np.array([bins[i - 1] for i in np.digitize(x, bins)]).astype(int)

# 変換する。
dst = cv2.LUT(img, y)
plot_grayscale_conversion(img, dst)

ソラリゼーション

過度の露光で画像の一部が反転する現象を ソラリゼーション (solarization) といいます。 以下の関数でソラリゼーションを擬似的に再現できます。

In [5]:
# ソラリゼーション
x = np.arange(256)
y = (np.sin(x * 2 * np.pi / 255) + 1) * 255 / 2

# 変換する。
dst = cv2.LUT(img, y)
plot_grayscale_conversion(img, dst)
Advertisement

折れ線型トーンカーブ

折れ線で表される階調変換関数を折れ線型トーンカーブといいます。

コントラストを上げる (1)

$$ f(x) = \begin{cases} \frac{255}{t} & x \le t \\ 255 & x > t \end{cases} = \text{clip}(\frac{255}{t}, 0, 255) $$

sample2.jpg

In [6]:
# 画像を読み込む。
img = cv2.imread("sample2.jpg", cv2.IMREAD_GRAYSCALE)

# コントラストを上げる折れ線型トーンカーブ
t = 155
x = np.arange(256)
y = np.clip(255 / t * x, 0, 255)

# 変換する。
dst = cv2.LUT(img, y)
plot_grayscale_conversion(img, dst)

コントラストを上げる (2)

In [7]:
# コントラストを上げる折れ線型トーンカーブ
t = 55
x = np.arange(256)
y = (255 - t) / 255 * x + t

# 変換する。
dst = cv2.LUT(img, y)
plot_grayscale_conversion(img, dst)
Advertisement

コントラストを下げる (1)

$$ f(x) = \begin{cases} 0 & x \le t \\ \frac{255 + b}{255}x – b & x > t \end{cases} = \text{clip}(\frac{255 + b}{255}x – b, 0, 255) $$

ただし、$b$ は次を解いて得られる値です。

$$ \frac{255 + b}{255}t – b = 0 $$
In [8]:
# コントラストを下げる折れ線型トーンカーブ
t = 55
x = np.arange(256)
y = np.clip(255 / (255 - t) * x - 255 / (255 - t) * t, 0, 255)

# 変換する。
dst = cv2.LUT(img, y)
plot_grayscale_conversion(img, dst)

コントラストを下げる (2)

$$ f(x) = \frac{t}{255} $$
In [9]:
# コントラストを下げる折れ線型トーンカーブ
t = 200
x = np.arange(256)
y = t / 255 * x

# 変換する。
dst = cv2.LUT(img, y)
plot_grayscale_conversion(img, dst)

明るさ、コントラストの補正

$$ f(x) = \text{clip}(\alpha x + \beta , 0, 255) $$

$\alpha$ はゲイン (gain) またはコントラスト (contrast)、$\beta$ はバイアス (bias) または明るさ (brightness) といいます。

In [10]:
# 明るさ、コントラストの補正
alpha = 2
beta = 30
x = np.arange(256)
y = np.clip(alpha * x + beta, 0, 255)

# 変換する。
dst = cv2.LUT(img, y)
plot_grayscale_conversion(img, dst)
Advertisement

ガンマ補正

次の関数で行う階調変換をガンマ変換 (gamma transformation) またはガンマ補正 (gamma correction) といいます。

$$ f(x) = \left(\frac{x}{255}\right)^\gamma \times 255 $$

$\gamma < 1$ で黒く、$\gamma > 1$ で明るくなるように補正できます。

In [11]:
gamma = 1 / 3
x = np.arange(256)
y = (x / 255) ** gamma * 255

# 変換する。
dst = cv2.LUT(img, y)
plot_grayscale_conversion(img, dst)
In [12]:
gamma = 3
x = np.arange(256)
y = (x / 255) ** gamma * 255

# 変換する。
dst = cv2.LUT(img, y)
plot_grayscale_conversion(img, dst)

S 字型トーンカーブ

S 字型のトーンカーブで階調変換すると、暗い部分は更に暗く、明るい部分は更に明るくすることができます。

以下の例では、$\arctan(x), x \in [-5, 5]$ の値を $[0, 255]$ にスケールすることで S 字型のトーンカーブを作成しました。

In [13]:
def scale(x):
    """x の範囲を [0, 255] にスケールする。
    """
    return 255 / (x.max() - x.min()) * (x - x.max()) + 255


x = np.arange(256)
y = np.arctan(np.linspace(-5, 5, 256))
y = scale(y)  # [atan(-5), atan(5)] -> [0, 255] にスケール

# 変換する。
dst = cv2.LUT(img, y)
plot_grayscale_conversion(img, dst)