note.nkmk.me

Python, NumPyで画像処理(読み込み、演算、保存)

Date: 2015-08-22 / Modified: 2018-02-08 / tags: Python, NumPy, 画像処理
このエントリーをはてなブックマークに追加

Pillow(PIL)で読み込んだ画像をNumPyの配列ndarrayとして格納すると、NumPyの機能を使って様々な画像処理を行うことができる。

画素値の取得や書き換え、スライスでのトリミング、結合などndarrayの操作がそのまま使えるので、NumPyに慣れている人は、OpenCVなどのライブラリを使わなくても結構いろいろなことができる。

OpenCVを使う場合も、PythonのOpenCVでは画像データをndarrayとして扱うので、NumPy(ndarray)での処理を覚えておくと何かと便利。

基本的な画像の読み書きとして、

  • 画像ファイルをNumPy配列ndarrayとして読み込む方法
  • NumPy配列ndarrayを画像ファイルとして保存する方法

と、NumPy(ndarray)での画像処理の例、

  • 単色化と画像の結合(連結)
  • ネガポジ反転(画素値の逆転)
  • 減色処理
  • 二値化処理
  • ガンマ補正
  • スライスでトリミング

について説明する。

ここではPillowを使った画像ファイルの読み書きについて述べる。OpenCVでの画像ファイルの読み込みと保存については以下の記事参照。

Pillowについては以下の記事も参照。

スポンサーリンク

画像ファイルをNumPy配列ndarrayとして読み込む方法

np.array()PIL.Image.open()で読み込んだ画像データを渡すとndarrayが得られる。

RGB画像は行(高さ) x 列(幅) x 色(3)の三次元のndarray、白黒画像は行(高さ) x 列(幅)の二次元のndarrayになる。

from PIL import Image
import numpy as np

im = np.array(Image.open('data/src/lena_square.png'))

print(im.dtype)  # データ型
# uint8

print(im.ndim)  # 次元数
# 3

print(im.shape)  # サイズ(高さ x 幅 x 色数)
# (512, 512, 3)

なお、PillowのPIL.Imageからndarrayに変換した場合、色の並びはRGB(赤、緑、青)となる。OpenCVで読み込んだ場合(BGR)と異なるので注意。

画像データに対して乗算・除算など小数点を伴う計算をしたい場合は、floatで読み込んでおく。

im_f = np.array(Image.open('data/src/lena_square.png'), 'f')

print(im_f.dtype)  # データ型
# float32

ndarrayなのでデータアクセスも簡単。もちろんmin()max()などのメソッドもそのまま使える。

print(im[256, 256])  # 指定した座標の画素値(R, G, B) / 原点は左上
# [180  65  72]

print(im[:, :, 0].min())  # Redの最小値
# 54

具体的な処理の例は後述。

NumPy配列ndarrayを画像ファイルとして保存する方法

Image.fromarray()ndarrayを渡すとPIL.Imageが得られる。

Pillowのメソッドで画像処理を行ったり、save()で画像として保存するなどの操作が可能になる。

pil_img = Image.fromarray(im)
pil_img.save('data/dst/lena_square_save.png')

保存するだけなら一行で書いてもいい。

Image.fromarray(im).save('data/dst/lena_square_save.png')

ndarraydtypefloatなどuint8でない場合は、JPGやPNGで保存するためにuint8に変換する。

pil_img_f = Image.fromarray(np.uint8(im_f))
pil_img_f.save('data/dst/lena_square_save.png')

単色化と画像の結合(連結)

他の色の値を0にして単色画像を生成する。さらに横に並べて結合する。

結合についての詳細は以下の記事参照。

from PIL import Image
import numpy as np

im = np.array(Image.open('data/src/lena_square.png'))

im_R = im.copy()
im_R[:, :, (1, 2)] = 0
im_G = im.copy()
im_G[:, :, (0, 2)] = 0
im_B = im.copy()
im_B[:, :, (0, 1)] = 0

# 横に並べて結合(どれでもよい)
im_RGB = np.concatenate((im_R, im_G, im_B), axis=1)
# im_RGB = np.hstack((im_R, im_G, im_B))
# im_RGB = np.c_['1', im_R, im_G, im_B]

pil_img = Image.fromarray(im_RGB)
pil_img.save('data/dst/lena_numpy_split_color.jpg')
NumPy image processing split color

ネガポジ反転(画素値を逆転)

画素値を計算して処理するのも簡単。

Max値(uint8の場合は255)から画素値を引くとネガポジ反転した画像が取得できる、

import numpy as np
from PIL import Image

im = np.array(Image.open('data/src/lena_square.png').resize((256, 256)))

im_i = 255 - im

Image.fromarray(im_i).save('data/dst/lena_numpy_inverse.jpg')

Python NumPy inverse

減色処理

//で割り算の余りを切り捨てて再度掛け算すると画素値が飛び飛びの値になり、色数を減らせる。

import numpy as np
from PIL import Image

im = np.array(Image.open('data/src/lena_square.png').resize((256, 256)))

im_32 = im // 32 * 32
im_128 = im // 128 * 128

im_dec = np.concatenate((im, im_32, im_128), axis=1)

Image.fromarray(im_dec).save('data/dst/lena_numpy_dec_color.png')

Python NumPy decrease color

二値化処理

しきい値に応じて白黒に振り分けることも可能。

詳細は以下の記事を参照。

Python NumPy OpenCV binarization

ガンマ補正

掛け算、割り算、累乗、なんでもできる。

from PIL import Image
import numpy as np

im = np.array(Image.open('data/src/lena_square.png'), 'f')  # floatで読み込み

im_1_22 = 255.0 * (im / 255.0)**(1 / 2.2)
im_22 = 255.0 * (im / 255.0)**2.2

im_gamma = np.concatenate((im_1_22, im, im_22), axis=1)

pil_img = Image.fromarray(np.uint8(im_gamma))  # floatをuintに変換
pil_img.save('data/dst/lena_numpy_gamma.jpg')
NumPy image processing split gamma

スライスでトリミング

スライスで領域を指定すると、矩形にトリミングすることもできる。

from PIL import Image
import numpy as np

im = np.array(Image.open('data/src/lena_square.png'))

print(im.shape)
# (512, 512, 3)

im_trim1 = im[128:384, 128:384]
print(im_trim1.shape)
# (256, 256, 3)

Image.fromarray(im_trim1).save('data/dst/lena_numpy_trim.jpg')

numpy image trimming 1

左上の座標とトリミングする領域の幅・高さで指定する関数を用意しておくと便利かもしれない。

def trim(array, x, y, width, height):
    return array[y:y + height, x:x+width]

im_trim2 = trim(im, 128, 192, 256, 128)
print(im_trim2.shape)
# (128, 256, 3)

Image.fromarray(im_trim2).save('data/dst/lena_numpy_trim2.jpg')

numpy image trimming 2

画像のサイズ外を指定すると無視される。

im_trim3 = trim(im, 128, 192, 512, 128)
print(im_trim3.shape)
# (128, 384, 3)

Image.fromarray(im_trim3).save('data/dst/lena_numpy_trim3.jpg')

numpy image trimming 3

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事