読者です 読者をやめる 読者になる 読者になる

はてブロ@ama-ch

https://twitter.com/ama_ch

Python Challenge Level7

Python Challenge

※ネタバレ有り

考え方?

今回はちょっと反則をしてしまいました。
PILについて調べていた時に、今回の問題の断片的な情報も色んなところで見てしまい、前もってやることは把握してしまいました。
画像のグレースケールの部分はRGBが同じ値になっているので、その値を抽出して文字にすれば良いみたいです。


・・・とはいうものの、PILの使い方が全然分からないので手探りで頑張ります。
まずはPILでRGBの値を取得しないといけないので、画像を保存し、FirefoxのカラーピッカーアドオンColorZillaをインストールしました。


PILの使い方を知るために、
Python Imaging Library Handbook
PILチュートリアル ― nagosui.org
などを参考にインタラクティブシェルで動作を確認しました。

>>> from PIL import Image
>>> im = Image.open("oxygen.png")
>>> print im.format
PNG
>>> im.size
(629, 95)
>>> im.mode
'RGBA'
>>> im.show()  # 画像を表示
>>> im.getpixel((1,1))
(78, 93, 26, 255)

getpixel関数で、指定座標のRGBの値が得られるようです。


それではグレースケール部分のRGBを確認・・・したいんだけど座標がわからない><
ここで先ほどインストールしたColorZillaの出番です。
画像のグレー部分の1番左側をこいつで調べると、R:115, G:115, B:115だとわかりました。
あとは調べるy座標を変更してあげて、RGBが115になっているピクセルを探します。

>>> im.getpixel((0,47))
(115, 115, 115, 255)

im.sizeで調べた画像の大きさは(629, 95)だったので、とりあえず真ん中からと思って95の半分、47を指定したら見事ピッタリ!
y座標はこれで固定して、x軸を見ていこう。

>>> for cnt, x in enumerate(range(im.size[0])):
...     print "%3d" % cnt, im.getpixel((x, 47))
... 
  0 (115, 115, 115, 255)
  1 (115, 115, 115, 255)
  2 (115, 115, 115, 255)
  3 (115, 115, 115, 255)
  4 (115, 115, 115, 255)
  5 (109, 109, 109, 255)
  6 (109, 109, 109, 255)
--- 中略 ---
623 (95, 83, 45, 255)
624 (98, 86, 48, 255)
625 (97, 84, 49, 255)
626 (95, 82, 47, 255)
627 (97, 83, 46, 255)
628 (99, 85, 46, 255)

うーん、何ピクセルで変化するんだろう?
最初の115は5ピクセルで、あとは7ピクセルずつ存在してるように見える。最後の方(画像右端)はグレースケールでなくなってるので、値も色々に変化しています。


7ピクセルでよさげなので、7ピクセルずつ表示してみます。

>>> for cnt, x in enumerate(range(0, im.size[0], 7)):
...     print "%3d" % cnt, im.getpixel((x, 47))
... 
  0 (115, 115, 115, 255)
  1 (109, 109, 109, 255)
  2 (97, 97, 97, 255)
  3 (114, 114, 114, 255)
  4 (116, 116, 116, 255)
  5 (32, 32, 32, 255)
  6 (103, 103, 103, 255)
--- 中略 ---
 84 (50, 50, 50, 255)
 85 (49, 49, 49, 255)
 86 (93, 93, 93, 255)
 87 (112, 110, 69, 255)
 88 (101, 92, 51, 255)
 89 (95, 83, 45, 255)

このままじゃどうしようもないから、chr()で文字にしてみます!

>>> for cnt, x in enumerate(range(0, im.size[0], 7)):
...     print chr(im.getpixel((x, 47))[0]),
... 
s m a r t   g u y ,   y o u   m a d e   i t .   t h e   n e x t   l e v e l   i s   [ 1 0 5 ,   1 1 0 ,   1 1 6 ,   1 0 1 ,   1 0 3 ,   1 1 4 ,   1 0 5 ,   1 1 6 ,   1 2 1 ] p e _

おおお、なんか出てきた!最後のpe_はグレースケールじゃないとこが入り込んじゃったみたい。
この値をまたchr()で出力すれば答えになりそうです。

>>> next = [105, 110, 116, 101, 103, 114, 105, 116, 121]
>>> import sys
>>> for char in next:
...     sys.stdout.write(chr(char))
... 
integrity

おお、答え出た・・・


インタラクティブシェルだけで終わりだとつまらないので、ここまでの過程をひとつのソースにまとめてみました。

回答

#!/usr/bin/env python
#! -*- coding: utf-8 -*-
""" level 7
http://www.pythonchallenge.com/pc/def/oxygen.html
"""
from PIL import Image
import re
import sys

im = Image.open("oxygen.png")
next = []

for x in range(0, im.size[0], 7):
    r, g, b = [im.getpixel((x, 47))[i] for i in range(3)]
    if r == g == b:
        next.append(chr(r))
print "".join(next)

for char in re.findall("\d+", "".join(next)):
    sys.stdout.write(chr(int(char)))
print ""

実行結果

$ python level7.py
smart guy, you made it. the next level is [105, 110, 116, 101, 103, 114, 105, 116, 121]
integrity

感想

PILを使うと聞いていたから、もっとゴリゴリ画像処理するのかと思ったらRGB取得するだけでした。
今回は手順がほぼわかっていたため、そこで悩まなかったので短時間で解けてしまいました。