- 画像処理にはPillow(PIL)使います。
インストール
Python
MacははじめからPythonが入っています。python --version
brew install python
brewが入っていることが前提ですが。。。
pipのインストール
ライブラリのインストールを楽にするためにPythonのパッケージ管理ツールであるpipをインストールします。easy_install pip
モジュール(ライブラリ)のインストール
今回は、numpyとPillow(PIL)を使います。numpyは計算に便利なツール類が、Pillowは画像処理関係のツール類が含まれます。pip install numpy
pip install pillow
画像を操作してみる
とりあえず、画像を読み込んで表示してみます。from PIL import Image
im = Image.open("./achan.jpg")
im.show()
from PIL import Image
im = Image.open("./achan.jpg")
im.rotate(30).show()
実務では、これでいいのですが、内部的にどのような処理をしているのかはわかりません。なので、ここでは、1個1個のピクセルを処理して回転させるプログラムを書いてみたいと思います。
ピクセルを操作してみる
では、個々のピクセルを操作してみます。ネガポジ反転してみる
では、代表的かつ簡単なピクセル操作処理であるネガポジ反転処理を行ってみます。#coding:utf-8
from PIL import Image
#画像の読み込み
im = Image.open("./achan.jpg")
#RGBに変換
rgb_im = im.convert('RGB')
#画像サイズを取得
size = rgb_im.size
#取得したサイズと同じ空のイメージを新規に作成
im2 = Image.new('RGBA',size)
#loop
#x
for x in range(size[0]):
#y
for y in range(size[1]):
#ピクセルを取得
r,g,b = rgb_im.getpixel((x,y))
#反転処理
r = 255 - r
g = 255 - g
b = 255 - b
#set pixel
im2.putpixel((x,y),(r,g,b,0))
#show
im2.show()
グレースケール
グレースケールは、r,g,bが同じ値を持つことでグレーに見えます。ただ、そのようなルールで同じ値にするかはケースバイケースです。ここではr,g,bの平均値を取得し、その値にしてみます(以下、ピクセル操作のみ抜粋)。#loop
#x
for x in range(size[0]):
#y
for y in range(size[1]):
#ピクセルを取得
r,g,b = rgb_im.getpixel((x,y))
#平均化
g = (r + g + b)/3
#set pixel
im2.putpixel((x,y),(g,g,g,0))
#show
im2.show()
回転させてみる
画像を回転させる方法はいくつかありますが、最も原始的な方法は、回転行列を利用することです。回転行列
回転行列は、下記の式で表されます。θに回転させたい角度をラジアンで与えると、その角度だけ回転した座表(x2,y2)を得ることができます。m_matrix = np.matrix([
[np.cos(rad),-1*np.sin(rad)],
[np.sin(rad),np.cos(rad)]
])
ここではmatrixを使っていますが、特に理由がない場合、arrayを使ったほうがいいらしい。
コードを書く
では、回転行列を利用して回転するコードを書いてみます。なお、下記のコードでは画像中心ではなく、左上が回転軸として処理されます。さらにintしか扱えないputpixelを利用してるので、画像にムラが発生します(が、ここではわかり易さ重視でこの方法にしています)。#coding:UTF-8
from PIL import Image
import numpy as np
#画像の読み込み
im = Image.open("./achan.jpg")
#RGBに変換
rgb_im = im.convert('RGB')
#画像サイズを取得
size = rgb_im.size
#取得したサイズと同じ空のイメージを新規に作成
im2 = Image.new('RGBA',size)
#loop
#x
for x in range(size[0]):
#y
for y in range(size[1]):
#ピクセルを取得
r,g,b = rgb_im.getpixel((x,y))
#処理
#30度
rad = np.pi/6
#回転行列
m_matrix = np.matrix([
[np.cos(rad),-1*np.sin(rad)],
[np.sin(rad),np.cos(rad)]
])
#適用座標(元の座標)
p_matrix = np.matrix([
[x],
[y]
])
#行列演算
p2_matrix = m_matrix * p_matrix
#移動後の作業を取得(intしかputできないのでintに変換)
x2 = int(p2_matrix[0,0])
y2 = int(p2_matrix[1,0])
#画像サイズの内であれば
if 0 < x2 < size[0] and 0 < y2 < size[1]:
#移動後の座標に元RGBを指定
im2.putpixel((x2,y2),(r,g,b,0))
#show
im2.show()
反転させてみる
x軸、y軸、任意軸に対する反転等も行列で行えます。高校数学の1次変換です。例えば、y軸反転は、下記で与えられます。
反転行列(y軸)
#y軸対象
y_matrix = np.matrix([
[-1,0],
[0,1]
])
コードを書いてみる
では、コードを書いてみます。なお、普通にy軸反転すると、全ての点が、マイナス方向にずれるため描画されません。そこで、画像の横幅(x軸)分だけ、平行移動させます。#coding:UTF-8
from PIL import Image
import numpy as np
#画像の読み込み
im = Image.open("./achan.jpg")
#RGBに変換
rgb_im = im.convert('RGB')
#画像サイズを取得
size = rgb_im.size
#取得したサイズと同じ空のイメージを新規に作成
im2 = Image.new('RGBA',size)
#loop
#x
for x in range(size[0]):
#y
for y in range(size[1]):
#ピクセルを取得
r,g,b = rgb_im.getpixel((x,y))
#処理
#y軸対象
y_matrix = np.matrix([
[-1,0],
[0,1]
])
#適用座標(元の座標)
p_matrix = np.matrix([
[x],
[y]
])
#行列演算
p2_matrix = y_matrix * p_matrix
#移動後の作業を取得(intしかputできないのでintに変換)
x2 = int(p2_matrix[0,0]) + size[0] #加増の横サイズだけx座標を平行移動
y2 = int(p2_matrix[1,0])
#画像サイズの内であれば
if 0 < x2 < size[0] and 0 < y2 < size[1]:
#移動後の座標に元RGBを指定
im2.putpixel((x2,y2),(r,g,b,0))
#show
im2.show()
反転しました。いわゆる左右反転。
なんか1ピクセル余計にずれてる感じもしますが。。。ここでは気にしません。
近傍処理
行列演算と同様、画像処理では必須処理となる近傍処理をやってみます。近傍処理は「ぼかし処理」や「輪郭抽出」等で活躍します。ぼかし
ここでは、比較的簡単な「ぼかし処理」をやってみます。いろいろなアルゴリズムがありますが、ここでは最も単純な8近傍の平均値を取得し、セットするという方法をためしてみます。8近傍とは、基準座標を取り囲む8つのエリアのことです。
それそれの座標のr,g,b値を取得して、平均化します。
1つ注意点があるのは、画像の辺縁部の座標に置いては、x-1が存在しなかったり、x+1が座標からはみ出たりしますので、その処理が必要になります。では、コードを書いてみます。
#coding:utf-8
from PIL import Image
#画像の読み込み
im = Image.open("./achan.jpg")
#RGBに変換
rgb_im = im.convert('RGB')
#画像サイズを取得
size = rgb_im.size
#取得したサイズと同じ空のイメージを新規に作成
im2 = Image.new('RGBA',size)
#loop
#x
for x in range(size[0]):
#y
for y in range(size[1]):
#対象座標のピクセルを取得
r0,g0,b0 = rgb_im.getpixel((x,y))
#初期化(とりあえず、全ての近傍値に現座標値をセット)
r1 = r2 = r3 = r4 = r5 = r6 = r7 = r8 = r0;
g1 = g2 = g3 = g4 = g5 = g6 = g7 = g8 = g0;
b1 = b2 = b3 = b4 = b5 = b6 = b7 = b8 = b0;
#近傍座標の値を取得
#1
if x-1 > 0 and y+1 < size[1]:
r1,g1,b1 = rgb_im.getpixel((x-1,y+1))
#2
if y+1 < size[1]:
r2,g2,b2 = rgb_im.getpixel((x,y+1))
#3
if x+1 < size[0] and y+1 < size[1]:
r3,g3,b3 = rgb_im.getpixel((x+1,y+1))
#4
if x-1 > 0:
r4,g4,b4 = rgb_im.getpixel((x-1,y))
#5
if x+1 < size[0]:
r5,g5,b5 = rgb_im.getpixel((x+1,y))
#6
if x-1 > 0 and y-1 > 0:
r6,g6,b6 = rgb_im.getpixel((x-1,y-1))
#7
if y-1 > 0:
r7,g7,b7 = rgb_im.getpixel((x,y-1))
#8
if x+1 < size[0] and y-1 > 0:
r8,g8,b8 = rgb_im.getpixel((x+1,y-1))
#近傍のRGBを平均化
r = (r0 + r1 + r2 + r3 + r4 + r5 + r6 + r7 + r8)/9
g = (g0 + g1 + g2 + g3 + g4 + g5 + g6 + g7 + g8)/9
b = (b0 + b1 + b2 + b3 + b4 + b5 + b6 + b7 + b8)/9
#描画
im2.putpixel((x,y),(r,g,b,0))
#show
im2.show()
ピクセルの補正など、実務に耐えうる状態にするにはまだまだですが、とりあえず、基本的な内容はここまで。
しや保存、各種画像処理(クリッピング、リサイズそして回転など)の方法についてまとめます。
環境
- OS: Ubuntu16.04LTS
- Python 3.7.0
- Pillow 5.3.0
インストール
Pillowは3rdパーティ製のモジュールです。自分の環境に無いときはインストールしましょう。公式リファレンスに記載の通りpipコマンドでできます。Anaconda環境の場合は以下でインストール可能です。
使い方
Pillowモジュールのインポート
PillowはPIL(Python Imaging Library)から派生して作られた経緯があり、PILとの互換性を保つため、慣例的に以下のように呼び出すそうです。画像データの読み出し
open()メソッドを使います。画像ファイルまたはファイルオブジェクトを引数に設定します。戻りはImageオブジェクトです。また、Imageオブジェクトは様々なプロパティを持っており、サイズやフォーマットを確認することができます。
読みだした画像データを表示
show()メソッドを使います。ただ、公式リファレンスによると、このメソッドは非効率なのでデバック目的にしか使わないほうが良いよ、とのこと。
画像を保存する
保存ファイル名をsave()メソッドの引数に設定します。拡張子を変更することで、自動的にフォーマットを変換することもできます。これは便利そうですね。また、ファイル名の他にオプション引数を記述すると、画質の調整など細かな設定ができますが、細かくなるのでここでは割愛します。詳細は公式リファレンス:Image file formatsをご参照ください。
新しいImageオブジェクトを生成
new()メソッドを使います。まずは、Lime色の幅200px, 高さ150pxの矩形を表示してみます。
第一引数はカラーモードを表しており、上記例で設定した”RGBA”の他に”RGB”や”CMYK”などいろいろ設定できます。
<カラーモードの例>
- RGBA:赤(Red),緑(Green), 青(Blue)、α値(不透明度)
α : 0 – 255の値(0:透明 ⇔ 255:不透明)
- RGB: 赤(Red),緑(Green), 青(Blue)
- CMYK:シアン(Cyan)、マゼンタ(Magenta)、イエロー(Yellow)、キー・プレート(Key plate:黒)
※この他のカラーコードは公式リファレンスをご参照ください。
第二引数は、描画する矩形の幅、高さを表します。
第三引数は、カラーを表します。デフォルトは黒です。RBGなら3つ、RGBAなら4つのの整数値を要素としたタプルを設定します。また、上記例のようにImageColorモジュールによって定義された文字列で表すこともできます。他にも例を示すと、、、
カラー:赤 RGBA値=(255, 0, 0, 255)の場合
カラー:紫 RGBA値=(128, 0, 128, 255)の場合
画像の切り抜き
crop()メソッドを用います。切り取るエリアを座標で表して引数として渡します。ここで、Pillowにで使われる座標系について簡単におさらいします。
- 座標系: 画面左上が原点(0, 0)、x軸は画面横方向、y軸は画面縦方向
- 座標の表し方:
- 点 : (x, y)の2つタプル
- 四角形: (左上のx, 左上のy, 右下のx, 右下のy)の4つのタプル
塗りつぶした矩形だと面白くないので、具体的な例を以下に示します。
→
画像のペースト
paste()メソッドを用います。第一引数にペーストしたい画像、第二引数にペーストする座標を設定します。このメソッドは、元の画像(Imageオブジェクト)を直接変更します。元の画像を変更したくない場合はcopy()メソッドで画像を複製しておきます。
この他に、maskパラメータを使ってペーストする画像の境界線をぼやかすなどエフェクトを付けることも可能ですが、ここでは割愛します。
画像のリサイズ
resize()メソッドを用います。以下は元画像の1/4のサイズにする場合の例です。
画像の回転
rotate()メソッドを使います。引数は、半時計回りの回転角度(degree)です。90°回転の場合の例
この設定だと、画像のサイズが変わらないまま回転してしまうので、画像がはみ出てしまっています。
元の画像全部が表示されるように回転させるには、オプション引数expand=Trueを設定します。
まとめ
PillowモジュールのImageオブジェクトを使った画像処理についてまとめました。- 画像データの呼び出し・保存
- 画像のクリッピング、ペースト、回転といった基本的な操作
0 件のコメント:
コメントを投稿