继续挑战,依然还是很麻烦
第33题地址beer.html
- 网页标题是
33 bottles of beer on the wall
,题目内容为空,源码里面有隐藏内容:<!–
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.
–>
隐藏内容还是相当难理解,不过图片的名字是beer1.jpg
,按套路来看我们要去找下一张。
from io import BytesIO
import requests
from PIL import Image
with requests.Session() as sess:
sess.auth = ('kohsamui', 'thailand')
response = sess.get(
'http://www.pythonchallenge.com/pc/rock/beer2.jpg').content
img = Image.open(BytesIO(response))
img
图片上写着no, png
,显然是要我们去找beer2.png
from io import BytesIO
import requests
from PIL import Image
with requests.Session() as sess:
sess.auth = ('kohsamui', 'thailand')
response = sess.get(
'http://www.pythonchallenge.com/pc/rock/beer2.png').content
img = Image.open(BytesIO(response))
img
from collections import Counter
print(img.size)
c = Counter(img.getdata())
print(sorted(c.items(), key=lambda x: x[0], reverse=True))
(138, 138)
[(194, 272), (193, 1348), (188, 283), (187, 241), (182, 318), (181, 198), (176, 337), (175, 171), (170, 317), (169, 183), (164, 317), (163, 175), (158, 323), (157, 161), (152, 324), (151, 152), (146, 323), (145, 145), (140, 342), (139, 118), (134, 342), (133, 110), (128, 327), (127, 117), (122, 198), (121, 238), (116, 183), (115, 32), (110, 310), (109, 114), (104, 224), (103, 192), (98, 505), (97, 104), (92, 257), (91, 139), (86, 341), (85, 47), (80, 354), (79, 26), (74, 164), (73, 23), (68, 298), (67, 70), (62, 356), (61, 181), (56, 609), (55, 79), (50, 225), (49, 107), (44, 357), (43, 126), (38, 339), (37, 126), (32, 328), (31, 119), (26, 424), (25, 144), (20, 243), (19, 549), (14, 329), (13, 724), (8, 189), (7, 963), (2, 232), (1, 1532)]
我们可以看到像素值是有一定的特点的。我们来尝试理解一下之前题目里的隐藏内容。
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.
- 我们尝试移除最亮的像素值194,还剩
138*138-272=18772
个像素,没什么特点。再观察可以注意到像素值分布是两个连续值为一组的,我们尝试移除最亮的像素值194和193,还剩138*138-272-1348=17424
个像素。咦!正好可以组成132*132(=17424)
的正方形! - 我们简单验证一下。
import math
assert img.width == img.height
count = img.width * img.height
dist = sorted(c.items(), key=lambda x: x[0], reverse=True)
while dist:
print(math.sqrt(count))
count -= dist.pop(0)[1]
count -= dist.pop(0)[1]
138.0
132.0
130.0
128.0
126.0
124.0
122.0
120.0
118.0
116.0
114.0
112.0
110.0
108.0
107.0
105.0
103.0
100.0
98.0
96.0
94.0
93.0
91.0
88.0
84.0
82.0
79.0
76.0
73.0
69.0
63.0
54.0
42.0
果然跟猜想一致。其实再看题目的图片,是一层层的酒瓶,实际上就是要让我们把数据一层层分开来处理。那么我们就把最亮的部分显示出来,然后把剩下的部分重新组成正方形,重复直至用完所有像素。
from collections import Counter
import matplotlib.pyplot as plt
from PIL import Image
data = img.getdata()
c = Counter(data)
pixels = sorted(c.keys(), reverse=True)
num = len(pixels) // 2
num_per_row = math.ceil(math.sqrt(num / (3 / 4))) # Figure 4:3
rows = math.ceil(num / num_per_row)
for i in range(num):
side = int(math.sqrt(len(data)))
img_new = Image.new('L', (side, side))
img_new.putdata([255 if x == pixels[2 * i] else 0 for x in data])
data = list(filter(lambda x: x not in pixels[2 * i:2 * (i + 1)], data))
plt.subplot(rows, num_per_row, i + 1)
plt.axis('off')
plt.imshow(img_new, cmap='gray')
我们可以看到每幅图中间都是一个字母,而其中有一些字母是被框起来的。自然答案就是gremlins.html(为啥多了一个i
?因为这样才是单词小精灵
啊)
- 新页面标题是
Temporary End
,内容是感谢玩耍 看来是到了结尾了!!!
最后再回过头了总结题目里隐藏内容的解析:
If you are blinded by the light,
light
是指最亮的像素,它展示了某些信息(字母),但同时把其他信息遮盖(blinded
)了(移到了其他随机的位置)
remove its power, with its might.
- 所以我们需要移除它本身(
power
),当然还有它伴随的力量(might
),因为像素值个数要构成一个平方数嘛
Then from the ashes, fair and square,
- 然后剩下的散点(
ashes
)自然要重新组成一个正方形(square
)图案,当然还要公平(fair
)地对待所有像素,因此这个操作是一个迭代
another truth at you will glare.
- 你会看到(
glare
)最后的真相(truth
),因为答案隐藏在了被框选的字母里面,还少框了一个i
总结:这一题基本相当于猜谜,工具依然是对图像像素的一些分析和操作,关键在于发现其中的数学规律。
最后,这个挑战也告一段落了,因为作者很久也没再继续出题了。这个项目到此为止了。