Python/OpenCVで任意色を透過させたpng画像に変換

  • このエントリーをはてなブックマークに追加

画像内任意色の画素を透過させるくらいの処理はPythonとOpenCVを使えばあっという間に可能です。ここでは透過を意味するRGBA形式の説明と、Pythonによる透過png画像作成コードを紹介します。

こんにちは。wat(@watlablog)です。ここではPython/OpenCVによる透過png画像作成方法を紹介します

画像透過処理に必要な知識とメリット

RGBA形式

最もポピュラーな画像形式はRGB(Red Green Blue)形式の画像で、3原色のみを使って色を表現したカラー画像です。

しかしこの形式の画像のままでは透過処理をすることができないので、RGBA形式の画像にする必要があります。

RGBA(Red Green Blue Alpha)形式の画像とは、赤・緑・青の3原色の色強度の他に透明度Alphaを追加した画像形式のことを意味します。

RGBA形式の説明画像

これらそれぞれの値は8bitのデータ容量を持ち、0から255までの256階調で値を指定することが可能です。

透明度Alpha(Alphaチャンネルと呼ばれることもある)は0が透過度100%で、255が不透明(つまりRGB色そのまま)となります。

透過を表現できる画像形式にはpnggifがあります(jpegbmpは透過に対応していません)。

透過画像にする事のメリット

透過処理のためにRGBA形式の画像にすることがわかりました。
ここでは画像の容量を増やしてまで透過処理を行うメリットをいくつか紹介します。

今回説明に使用するサンプル画像は以下のピンポン玉画像(オートシェイプで適当に作成)です。

サンプル画像

このピンポン玉画像はRGB形式の画像で作成されており、下図のように二つ以上重ねてしまうと背景の白色部分で裏の画像が隠れてしまいます。

RGB画像だと重ねると隠れてしまうの図

透過処理を施した画像であれば下図のように二つの画像を重ねても背景部分で裏の画像はしっかりと表示されます。

このように複数画像を組み合わせて1つの表現をしたい場合、透過画像は圧倒的に表現力が増すというメリットがあります

RGBA画像だと重ねても隠れてしまわないの図

プログラムで透過画像を作成するメリット

透過画像は確かにクリエイターにとって大変有用ですが、大半の方はPhotoshop等の有料ソフトやGIMP等の無料ソフトで処理していると思います。

今回はPythonというプログラミング言語で透過画像を作成する方法を紹介しますが、プログラムの長所は反復計算なので、大量生産が簡単というメリットがあります

透過処理程度だったらプログラムでも簡単に書けるため、是非退屈な作業はPythonに任せてしまいましょう。

例えば、LINEスタンプを作成する時に40枚の画像をいちいち1枚ずつ汎用ソフトに読み込ませて、輪郭を抽出して…という作業をするとそれこそ1日作業になりかねません。

色付きの背景を透明にするアイデア

マスク画像を使って背景を操作する

背景がいつも白色であれば、白色を検索してAlphaチャンネルを書き換える操作で十分です。

しかし、背景色が青、もしくはちょっとノイズが乗っているなんとなく同じような色が付いた部分をまとめて透過させたい時は工夫が必要です。

以下の図は画像処理で特定の領域を抽出するマスク処理というものの例です。(a)元画像に対し、青色部分を白く、それ以外の部分を黒く変換したものが(b)マスク画像です。

上記元画像と、元画像をマスクした画像で論理演算(ブーリアン演算)をすると、以下の結果を得ます。

画像の論理演算

今回は青色部分のみを透過させたいので、not演算をすれば良いとわかります

逆に、青色部分だけを残したい場合はandやorを使うことで実現できます。
この論理演算はAlphaチャンネルも併せて計算されるため、任意色だけを透過する処理の目標となります。

任意色を透過処理するPythonコード

OpenCVの準備

今回はPythonと共に、有名な画像処理ライブラリであるOpenCVを使います。

コードを紹介する前に、まだOpenCVをインストールしていない方は「Pythonで画像処理!OpenCVのメリットとインストール法」に詳細を記載しましたので、必要に応じて是非参照頂ければと思います。

白色を透明化するPythonコード

以下のコードは、
①RGB画像をAlphaチャンネルを追加した状態で読み込む。
②白色(画素の全てが255)を検索し、Trueなら0(透明)、Falseなら255(不透明)にする。
③画像を保存する。
という内容です。

以下が実行結果の例です。
元画像(左)は一見背景が無いように見えますが、重ねてみると一目瞭然です。これで透過が出来ました。

白色を指定して透明化した結果の例

ここではまだマスク処理をしていませんが、白色は全ての値が255なのでものすごくシンプルに書くことが出来ました。

任意色(範囲指定も可)を透明化するPythonコード

続いて、より汎用性を高めた色指定で透過する方法を以下のコードに示します。
ここでは、
①RGB画像をAlphaチャンネルを追加した状態で読み込む。
②color_lowerとcolor_upperで色を範囲付きで指定する(但し、OpenCVの色はBGRAなので順番に注意)。
③マスク画像を作成。
④元画像と元画像にマスクをかけた画像で論理演算をする。
⑤画像を保存する。
というフローになります。

範囲を指定可能ですが、今回は青一色の背景であるため、color_lowerとcolor_upperは同じ値にしています。

実行結果は以下の通りです。色を指定したものも問題無く透過処理がされました。

任意色を指定して透明化した結果の例

まとめ

本記事では透過画像の構造を簡単に説明し、PythonとOpenCVによる画像処理で背景付き画像から透過png画像を作ることが出来ました。

透過の方法は色々ありますが、マスク画像を使って論理演算する方法が最も汎用性があるのではないかと考えられます。

今回は透過処理の記事ですが、マスク処理を覚えた事で様々な画像処理に応用できそうです!
Twitterでも関連情報をつぶやいているので、wat(@watlablog)のフォローお待ちしています!

  • このエントリーをはてなブックマークに追加

SNSでもご購読できます。

コメント

  1. キリノ より:

    すいません、質問なのですが
    mg = cv2.imread(‘sample.png’, -1)
    とあるのですが、もし仮にこの画像がjpegだったとして、jpegファイルの青い部分だけを透明化したい場合はどうすればいいのでしょうか?

    1. wat より:

      ご訪問ありがとうございます。
      透明、という事はアルファチャンネルが必要になり、jpegでは対応できません。
      仮に元画像がjpegの場合は、アルファチャンネルを追加してpngで保存するというのが定石と思います。
      全てNumpyやOpenCVでできると思いますので、ご検討下さい。
      よろしくお願いします。

  2. くま より:

    突然の質問ですみません
    png形式で画像を読み込んだのち、
    img[:, :, 3] = np.where(np.all(img1 == 255, axis=-1), 0, 255) # 白色のみTrueを返し、Alphaを0にする
    を実行したところ
    IndexError: too many indices for array: array is 2-dimensional, but 3 were indexed
    というエラーが出てしまいました
    対処法ございますでしょうか

    1. wat より:

      ご訪問ありがとうございます。
      print(img[:, :, 3].shape)
      を実行してみて、(○, ○)というサイズが返ってきますか?
      それとも(○, ○, 3)といったように異なる数値が返ってくる場合は、元の画像がpng形式になっていないのかも知れません。
      例えば、.jpgファイル等の名前を.pngに変更しただけだと、中身は.jpgのままになっているといったエラー原因はよくお聞きします。
      まずはshapeを確認してみて下さい。
      よろしくお願いします。

  3. ハラ より:

    突然のご質問失礼致します。
    大学の研究室にて、画像の背景を透過させる必要があり、上記プログラムを実行してみたのですが、img[:,:,3] = np.where(np.all(img == 255, axis=-1), 0, 255) この文にて’NoneType’ object does not support item assignment このようなエラーが発生してしまい、上手くいきません。アドバイスを宜しく御願い致します。
    また、フォルダ内の数百枚の画像を一括で背景の透過をさせたく、for文で回してみたのですが上手くできませんでした。プログラムが苦手なこともあり、詰まってしまいましたので、もしお時間に余裕がありましたらフォルダごと一括で透過処理させる方法を教えて頂きたいです。

    1. wat より:

      ご訪問ありがとうございます!
      コメントに気付くのが遅れてしまいました。

      使用している画像は.pngになっていますでしょうか?
      透過処理にはアルファチャンネルが必要になります。
      もし読み込んだ画像が適切なチャンネルを持っているか不明な場合は、
      print(img[:, :, 3].shape)
      等でシェイプを確認する事をおすすめします。

      フォルダ一括処理については、
      「https://watlab-blog.com/2019/07/29/bundle-resize/」
      こちらの記事を参考にして頂けると解決すると思います。
      記事の画像処理内容はリサイズですが、その部分を透過処理に置き換えて頂ければと思います。

ハラ へ返信する コメントをキャンセル

*