環境
- Windows10
- Python3.5.2
- PIL (Pillow)
Pillow インストール
デフォルトで入ってるかもしれませんが。
$ pip install pillow
実装内容
下の画像にグレースケール変換を行います。
実装内容は3ステップ。
- 画像の読み込み
- 1ピクセルごとにRGBを取得してグレースケール計算
- 新しい画像を生成
2のグレースケール計算ではこちらの二つの方法を試します。
- 中間値法
- NTSC係数による加重平均法
中間値法
対象ピクセルのr,g,bの値のうち最大の値と最小の値を用いて
Y = (min+max) / 2
をグレーの値としてます。すなわち(r,g,b) = (Y,Y,Y)。
NTSC係数による加重平均法
いわゆる「輝度」を求める方法です。輝度はRGBの値をもとに以下の計算で求められます。
L = ( 0.298912 * r + 0.586611 * g + 0.114478 * b )
そのうえで、(r,g,b) = (L,L,L) とすればよいです。
コード
たいしたことないですが。
# -*- coding:utf-8 -*-
from PIL import Image
from itertools import product
img = Image.open("Lenna.jpg")
# 参考までにPILの機能でグレー変換
gray_img = img.convert('L')
gray_img.save('Lenna_gray.png')
# 以下自前グレースケール変換
rgb_img = img.convert('RGB')
w,h = rgb_img.size
my_img = Image.new('RGBA', (w,h)) # 同じ大きさの空画像を作成
# 中間値 : v = (max+min)/2
for x, y in product(range(w), range(h)):
r,g,b = rgb_img.getpixel((x,y))
val = int((min([r,g,b]) + max([r,g,b]))/2)
my_img.putpixel((x,y), (val, val, val))
my_img.save('Lenna_gray_middle_value.png')
# 輝度計算(NTSC係数) : v = 0.299*r + 0.587*g + 0.114*b
for x, y in product(range(w), range(h)):
r,g,b = rgb_img.getpixel((x,y))
val = int(0.299*r + 0.587*g + 0.114*b)
my_img.putpixel((x,y), (val, val, val))
my_img.save('Lenna_gray_luminance.png')
結果
結果はこちら。
一番左が、PILのconvert
メソッドを適用したもの。真ん中が中間値法。右がNTSC係数による加重平均法です。中間値法の結果が若干明るく仕上がり、PILの機能でグレー変換した画像と輝度計算の結果画像が同じようになりました。
実際にPillowのソースコード見てみると、Pillow/PIL/Image.pyのL820に以下のように書いてあり、やはり同じ実装をしていることがうかがえます。
When translating a color image to black and white (mode "L"),
the library uses the ITU-R 601-2 luma transform::
L = R * 299/1000 + G * 587/1000 + B * 114/1000
The default method of converting a greyscale ("L") or "RGB"
image into a bilevel (mode "1") image uses Floyd-Steinberg
dither to approximate the original image luminosity levels. If
dither is NONE, all non-zero values are set to 255 (white). To
use other thresholds, use the :py:meth:`~PIL.Image.Image.point`
method.
次は各ピクセルの輝度勾配も求めてみようと思います。
以上です。