Python + Pillowで画像をグレースケール化してみる(中間値法、RGBからの輝度計算)

  • 14 May 2017

ちょっとした練習で、Pythonで画像処理をやってみました。のちのちのHOG特徴量抽出の前座作業です。

環境

  • Windows10
  • Python3.5.2
  • PIL (Pillow)

Pillow インストール

デフォルトで入ってるかもしれませんが。

$ pip install pillow

実装内容

下の画像にグレースケール変換を行います。

Lenna

実装内容は3ステップ。

  1. 画像の読み込み
  2. 1ピクセルごとにRGBを取得してグレースケール計算
  3. 新しい画像を生成

2のグレースケール計算ではこちらの二つの方法を試します。

  1. 中間値法
  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')

結果

結果はこちら。

result

一番左が、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.

次は各ピクセルの輝度勾配も求めてみようと思います。

以上です。

参考