猫も杓子も構造化

発達障害、特別支援などについて書いています。最近は心理学関係の内容が多めです。

Python版OpenCVによるpng画像と透過処理について

必要があって画像処理の技術に入門している。その覚書。

f:id:nekomosyakushimo:20171115120827p:plain
(出所:いらすとや)

このタヌキが今回の実験台である。かわいい。

opencvを使って画像を読み込む際はimreadを使う。

# coding:utf-8

import cv2
import numpy as np

src = "test.png"

img = cv2.imread(src, 0)

第1引数にファイル、第2引数には読み込みのオプションを指定する。0だとグレースケール、1だとカラー、-1だとアルファチャンネル等の追加のチャンネルを含んだまま読み込むらしい。読み込まれた画像はndarray、つまりN次元の配列データとして扱われる。

各次元の要素数を調べるためにnumpy.shapeのメソッドを使うと読み込みによって得られる配列の違いが分かる。

f:id:nekomosyakushimo:20171115121303p:plain

グレースケールだと2次元の配列として行列が得られる。カラーだと3次元の配列として色情報次元のデータもRGBで得られるが、追加チャンネルを含んだ読み込みだとこの次元の要素数が一つ増える。この場合では、不透明度を決定する4つ目の要素が得られる。(RGBA)

このような形で配列情報を得ると特定の画素値にアクセスすることも可能である。例えば、上から100ピクセル目、左から100ピクセル目の画素値へアクセスすると以下のようになる。

f:id:nekomosyakushimo:20171115122239p:plain

グレースケールでは、画素値は0を最小値、255を最大値とする値が一つ格納されている、カラーだとRGBの画素値が、追加チャンネル込みだと、そこにアルファチャンネルを追加した値が得られる。

ちなみにアルファチャンネルは0から255までの値をとりうるようで、0だと透明、255だと完全な不透明になる。ここの数字を変えると透明度合いをいじることができる。例えば、先ほどの画像を半透明にするには、すべての画素のアルファチャンネルに真ん中ぐらいの128の数字を代入すれば良い。

newimg = img3
newimg[:, :, 3] = 128

結果の画像は次の通り。

f:id:nekomosyakushimo:20171115134840p:plain

もともと透明だった背景の黒が透明じゃなくなり、透明でなかったタヌキが半透明化している。

今度は、タヌキの部分だけ半透明にしたい場合を考える。タヌキの部分の画素のインデックスを取得することができれば、今と同じ手順でアルファチャンネルをいじることができる。そこで、条件を満たす要素のインデックスを取得するnp.whereというものを使って、中身のインデックスを取得して、中身のアルファチャンネルのみ変更をする。

newimg2 = img3
hutomei = np.where(img3[:, :, 3] != 0)  #アルファチャンネルが透明でないインデックスを取得
three = np.ones((len(hutomei[1])), dtype="int8") * 3  # 3だけからなる配列を作成
index = tuple((hutomei[0], hutomei[1], three))  #合わせたインデックスを作成 
newimg2[index] = 128

なんか無駄が多いコードな気がするのだが、とりあえず試行錯誤の結果うまくいったのでよしとする。結果の出力画像は以下の通り。

f:id:nekomosyakushimo:20171115143940p:plain

背景は完全に透明なまま、中身のタヌキだけが無事に半透明化した。背景は画面上では白く見えているかもしれないが、画素値にアクセスするとさきほどまでの画像と同じように黒である。

【参考】
https://amzn.to/2Pp2qYQ

OpenCVによる画像処理入門 改訂第2版 (KS情報科学専門書)

OpenCVによる画像処理入門 改訂第2版 (KS情報科学専門書)