見出し画像

Pythonで画像処理1

GWは猫がヘッダーを歩くのを眺めながら過ごしてます。備忘録です。

- Python初心者
- 画像処理初心者(画像認識なんかにも興味があるけどどっちかっていうと視覚的に楽しむ方向)

なので間違ってることがあったら優しく教えてください。


コードはGoogle Colaboratory上で実行しています。
画像ライブラリのPILでイメージを扱い、画像ファイルの輝度データをNumPyで行列要素(array)として弄ってく感じみたいです。


Colabを自分のGoogle Driveと紐づけ

ファイルの読み書きをするため。

from google.colab import drive
drive.mount('/content/drive')

いろいろ言われるのでその通りにする。ディレクトリ指定は'/content/drive/My Drive'以下。


ファイルの読み込み

from PIL import Image
import numpy as np

im1 = Image.open('/content/drive/My Drive/Pyfig1/haruchan.png')
# 画像読み込み

PILライブラリのImage.openを使う。pngとjpgは同じように扱えることを確認した。


解像度変更とファイルの保存

im1.thumbnail((100,100))
im1_resize = im1.resize((100,100))
# thumbnailメゾットは対象を上書きするがresizeメゾットは代入する

im1.save('/content/drive/My Drive/Pyfig1/thumb1.png')
im1_resize.save('/content/drive/My Drive/Pyfig1/resize.png')
# thumbnailは比率を維持するがresizeは任意に変更する。

画像1

解像度の変更には.thumbnailか.resizeを使う。どちらも第一引数はpixel数で第二引数は間引き方。
thumbnailは比率を維持するため、長辺を合わせるのに使えそう。

書き出しは.saveを使う。


.convertを用いた画像の変換

im1_gray = im1.convert('L')
# グレースケールに変換。変換式はL = R * 299/1000 + G * 587/1000 + B * 114/1000。

im1_mask = im1.convert('1')
# 二値化マスクみたいなの。デフォルトだとFloyd-Steinberg Ditheringで変換。
# dither = = 'none'で128を境に変換。

im1_gray.save('/content/drive/My Drive/Pyfig1/gray1.png')
im1_mask.save('/content/drive/My Drive/Pyfig1/mask1.png')


np.array(im1_mask)

# array([[ True,  True,  True, ...,  True,  True, False],
      [ True, False,  True, ..., False,  True, False],
      [ True,  True, False, ...,  True,  True, False],
      ...,
      [False, False, False, ..., False, False, False],
      [ True, False,  True, ..., False,  True, False],
      [False,  True, False, ..., False, False, False]])

.convertでRGBからグレースケールやマスクが作れる。

.convert('L')で変換した輝度(L)はRGBの輝度(R, G, B)を用いて
L = R * 0.299 + G * 0.587 + B * 0.114
で計算される(NTSC加重平均法という規格らしい)。

.convert('1')Floyd-Steinberg detheringという方式で二値化される。形式はBool。出力画像は以下のようなチェッカー状になる。

画像2


画像を行列(array)に変換しRGBチャンネルで分離

im1_arr = np.array(im1)
a, b, c = im1_arr.shape
# arrayに変換。[[[R,G,B],[R,G,B],....]]となっているので、
# im1_arr[height][width][channel]である。

im1_R = np.empty((a, b))
im1_G = np.empty((a, b))
im1_B = np.empty((a, b))
for x in range(a):
 for y in range(b):
   for z in range(c):
     if z == 0:
       im1_R[x][y] = im1_arr[x][y][z]
     elif z == 1:
       im1_G[x][y] = im1_arr[x][y][z]
     elif z == 2:
       im1_B[x][y] = im1_arr[x][y][z]
# RGBに分離

Image.fromarray(im1_R.astype(np.uint8)).save('/content/drive/My Drive/Pyfig1/R1.png')
Image.fromarray(im1_G.astype(np.uint8)).save('/content/drive/My Drive/Pyfig1/G1.png')
Image.fromarray(im1_B.astype(np.uint8)).save('/content/drive/My Drive/Pyfig1/B1.png')
#arrayからimageに変換する。配列の中身がfloat型なので、uint8に変換してから変換する。

画像3

np.arrayで行列に変換するといろいろできる。
目が慣れていないのでarrayの三次元配列がまだわかりづらい、、
奥行(?)方向にR, G, Bの輝度が並んでいるので、RGBごとやグレースケール、マスクに変換すると二次元配列になる。

np.splitで単純に分けることができない気がする。
[[[R],[R],[R],...][[G],[G],[G],...][[B],[B],[B],...]]ってなってしまう。
リスト内包表記に慣れていないのでもっと簡潔なやり方がありそう。

代入操作を行うと形式がfloat型になってしまうのでuint8(0-255までの8bit表記)に変換したものをImage.fromarrayでイメージに戻す。


平均値でグレースケール化

im1_gray2 = np.empty((a, b))
im1_gray2 = (im1_R + im1_G + im1_B) / 3
# RGBの平均値でグレースケール化

画像4

上述の.convert('L')で変換した画像と各チャンネルの平均値を輝度としたグレースケール画像の比較。
まあ違うと言われれば違う。


二値化

im1_arr_gray = np.array(im1_gray)
Th = im1_arr_gray > 128
# 閾値設定。128以上で白に飛ばす

im1_por = [[255 if Th[i][j] else 0 for j in range(b)] for i in range(a)]

画像5

Thの中身はグレースケール画像の輝度が128以上のピクセルはTrue、それ以外はFalseとしたBool型行列。128の数字を変えれば閾値が変わる。

Trueに対応した要素を255(白)に、それ以外を0(黒)にする。


疲れたので残りはまた今度。

この記事が気に入ったらサポートをしてみませんか?