LoginSignup
1
4

More than 3 years have passed since last update.

Python + Opencvでアルファブレンディング・エンボス

Posted at

概要

急にアルファブレンディングの画像を作りたくなったので、プログラムを制作しました。
Python + Opencvで、アルファブレンディングのプログラムを作成したので紹介をします。
合わせてエンボス処理のプログラムも作成しています。

下のような綺麗な画像を作りたくて、プログラムを組みました。
height_alpha.png

今回使用したコードと画像、処理結果はGithubにも載せています。

アルファブレンディング

まずはアルファブレンディングについてです。

アルファブレンディングは、2枚の入力画像の対応する画素値を $f_{1}$, $f_{2}$としたとき、以下で示す重み付き平均を求めることで出力画像の画素値 $g$を計算できます。

$g = \alpha f_{1} + (1 - \alpha)f_{2}$

ここで、$\alpha(0 \leqq \alpha \leqq 1)$が重みを表します。
$\alpha$の値を画素ごとで指定することによって様々な効果を得ることができます。

まず、今回入力した画像は以下の通りです。img_1.pngimg_2.png

そして、縦方向にアルファブレンディングを施した画像は以下の通りです。(上に載せたのと同じ画像です。)height_alpha.png

めちゃくちゃ綺麗な画像が出力できました。

ついでに横方向のアルファブレンディングを施した画像も載せておきます。width_alpha.png

これも幻想的でいいですよね。

アルファブレンディングのコードは以下の通りです。
処理の箇所だけピックアップして記載します。

    img_1 = cv2.imread("img_1.png") #画像の読み込み
    img_2 = cv2.imread("img_2.png")

    assert img_1.shape == img_2.shape #画像サイズが違う場合はエラーを返す。
        #縦方向のアルファブレンディング
        h = img_1.shape[0]
        alpha = np.linspace(0, 1, h).reshape(-1, 1, 1)  #画素ごとのαの値の生成
        height_alpha_img = img_1 * alpha + img_2 * (1 - alpha) 
        cv2.imwrite("height_alpha.png", height_alpha_img) #画像の保存

        #横方向のアルファブレンディング
        w = img_1.shape[1]
        alpha = np.linspace(0, 1, w).reshape(1, -1, 1) 
        width_alpha_img = img_1 * alpha + img_2 * (1 - alpha)
        cv2.imwrite("width_alpha.png", width_alpha_img)

上のプログラムはOpencvで画像を読み込むプログラムです。画像サイズが違う場合はassertでエラーを返します。

下のプログラムがアルファブレンディングの処理の箇所です。
画素ごとのアルファの値を取得するために、np.linspaceを用いて、縦横の長さに応じて等間隔の数列の値を生み出しているのが特徴です。これによって、画像の各行/列のアルファの値を取得します。
後は、上の式に従って計算しています。ここは、行と列を揃えなくてもPythonのブロードキャスト機能で勝手に揃えてくれます。

エンボス

エンボスはエッジ部分が浮き上がったような画像を生成するための処理です。
言葉だけではイメージが浮かびにくいと思うので、先に処理結果を下に示します。
emboss_img.png

処理の手順は以下の通りです。

  • 入力画像 $f_{1}$の濃淡を反転させた画像(ネガポジ画像)を生成する。
  • 生成した画像を数画素平行移動させ、$f_{2}$を生成する。
  • $f_{1}$と $f_{2}$を用いて、$g = f_{1} + f_{2} - 128$となる画素間演算を行い、出力画像 $g$を得る。

最後に、コードも載せておきます。こちらも処理に関するコードだけ抽出しています。
エンボス処理は1枚の画像のみを使用します。

    img_1 = cv2.imread("img_1.png") #画像の読み込み
    img_2 = cv2.imread("img_2.png")

    assert img_1.shape == img_2.shape #画像サイズが違う場合はエラーを返す。
        #ネガポジ反転
        color_img = cv2.cvtColor(img_1, cv2.COLOR_BGR2YCrCb)
        gray = color_img[:, :, 0]
        h, w = img_1.shape[:2]
        im_invert = cv2.bitwise_not(gray)

        #平行移動(アフィン変換)の処理
        tx, ty = 5, 5
        affine = np.float32([[1, 0, tx],[0, 1, ty]])
        img_afn = cv2.warpAffine(im_invert, affine, (w, h))

        #画像の合成
        emboss_img = gray + img_afn -128

        cv2.imwrite("emboss_img.png", emboss_img)

エンボスは、グレースケール画像を使用するので、Opencvcv2.cvtColorで輝度値のみを抽出しています。
ネガポジ変換には、こちらもOpencvcv2.bitwise_notを使用しています。

平行移動は、アフィン変換を使用しています。
アフィン変換は、任意の線形変換(拡大・縮小など)と平行移動を組み合わせた手法で、式は以下の通りになります。

$\begin{pmatrix}
x' \\
y'
\end{pmatrix}=
\begin{pmatrix}
a & b \\
c & d
\end{pmatrix}
\begin{pmatrix}
x \\
y
\end{pmatrix}+
\begin{pmatrix}
t_{x} \\
t_{y}
\end{pmatrix}$

この行列を展開すると、
$x' = ax + by + t_{x}$
$y' = cx + dy + t_{y}$
となります。

コードのaffineはそれぞれ a, b, $t_{x}$と c, d, $t_{y}$の値を指定しています。
今回は、平行移動のみなので $t_{x}$と $t_{y}$のみの指定で、 x, yの値はそのまま残したいので $a, c = 1$とします。

ちなみに、拡大処理を行いたい場合は a, dのみ値を設定するといいです。
他にも、回転・鏡映・スキューなど複数の処理がアフィン変換で一度にできます。

そして、Opencvcv2.warpAffineでアフィン変換をして、最後に画素値の計算をすることで出力結果が得られます。

終わりに

Adobeがなくても簡単に画像処理できるよ!というお話でした。
最後まで読んでいただきありがとうございました。

1
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4