0%

Python Challenge (Level 33)

第33关 用户名kohsamui 密码thailand

Finally,the last one!

图片的名称是beer1.jpg,显然要尝试beer2.jpg,得到提示no, png,得到beer2.png

一张杂乱无章的图片,根据之前页面Page Source里的提示:

1
2
3
4
If you are blinded by the light,
remove its power, with its might.
Then from the ashes, fair and square,
another truth at you will glare.

首先,我们要把较亮的像素移除,然后用剩下的像素(from the ashes)重新组成一副图片。那要移除多少像素呢,应该是要使剩下的像素点的个数为平方数(fair and square).

我们可以统计一下像素频数,再用numpy的cumsum来观察每次要移除那些像素。

1
2
3
4
im = Image.open('data/beer2.png')
im_data = np.array(list(im.getdata()))
im_data_stat = itemfreq(im_data)
pprint(im_data_stat)

得到的结果为

1
2
3
4
5
6
7
8
9
10
array([[   1, 1532],
[ 2, 232],
[ 7, 963],
[ 8, 189],
[ 13, 724],
[ 14, 329],
[ 19, 549],
[ 20, 243],
[ 25, 144],
[ 26, 424] ...

其中,第一列为像素值,第二列为相应的个数。cumsum观察要移除多少个较亮的像素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pprint([np.sqrt(i) for i in np.cumsum(im_data_stat[:, 1])])

[39.14077158156185,
42.0,
52.220685556587632,
54.0,
60.332412515993425,
63.0,
67.216069507224233,
69.0,
70.035705179572517,
73.0,
73.810568348983736,
76.0 ...
]

可以看到,每次移除最亮的两个值,剩下的像素个数刚好是平方数。将剩下的像素中,最大值的为亮部,非最大值的为暗部,画二值图像。完整的代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from PIL import Image
import numpy as np
from scipy.stats import itemfreq
from pprint import pprint

im = Image.open('data/beer2.png')
im_data = np.array(list(im.getdata()))
im_data_stat = itemfreq(im_data)
pprint(im_data_stat)
pprint([np.sqrt(i) for i in np.cumsum(im_data_stat[:, 1])])

for i in range(im_data_stat.shape[0] - 2, 0, -2):
newIm_data = im_data[np.where(im_data < im_data_stat[i, 0])]
idx_0 = np.where(newIm_data == newIm_data.max())
idx_1 = np.where(newIm_data != newIm_data.max())
newIm_data[idx_0] = 0
newIm_data[idx_1] = 1
size = int(np.sqrt(len(newIm_data)))
newIm = Image.new('1', (size, size))
newIm.putdata(newIm_data)
newIm.save('data/level33_%i.png' % i)

最后,得到许多张图片。图片上是一个个字母,其中有一些字母带框框,这些字母组成通关密码gremlins

http://www.pythonchallenge.com/pc/rock/gremlins.html