StevenPZChan
StevenPZChan
5 min read

Categories

Tags

继续挑战


第22题地址copper.html

  • level22.jpg
  • 网页标题是emulate,题目内容为空,源码里面有一行隐藏内容:

    <!– or maybe white.gif would be more bright–>

没什么说的,先把white.gif搞下来看看:

from io import BytesIO
import requests
from PIL import Image

with requests.Session() as sess:
    sess.auth = ('butter', 'fly')
    response = sess.get('http://www.pythonchallenge.com/pc/hex/white.gif').content
    img = Image.open(BytesIO(response))
print(img.info)
{'version': b'GIF89a', 'background': 9, 'duration': 10, 'comment': b'Created with The GIMP', 'extension': (b'NETSCAPE2.0', 75), 'loop': 0}

考虑到这是个GIF动态图片,我们取几帧看看:

import matplotlib.pyplot as plt
from PIL import ImageSequence
%matplotlib inline

imgs = ImageSequence.all_frames(img)
print('frames:', len(imgs))
for i, im in enumerate(imgs[:5]):
    plt.subplot(1, 5, i + 1)
    plt.imshow(im)
    plt.axis('off')
plt.show()
frames: 133

png

都是一团黑。。。
我们开始读题干吧。需要把图片弄得一些,难道除了黑色之外还有别的像素点?结合图片名字white.gif看应该是的:

from PIL import ImageSequence

for im in ImageSequence.all_frames(img)[:10]:
    print(im.getcolors())
[(39999, 0), (1, 8)]
[(39999, 0), (1, 8)]
[(39999, 0), (1, 8)]
[(39999, 0), (1, 8)]
[(39999, 0), (1, 8)]
[(39999, 0), (1, 8)]
[(39999, 0), (1, 8)]
[(39999, 0), (1, 8)]
[(39999, 0), (1, 8)]
[(39999, 0), (1, 8)]

是的!!

所有的133图片帧里面,除了39999个像素是黑的以外,还有孤独的1个像素是第8号颜色:

print(img.getpalette()[8 * 3 : 9 * 3])
[8, 8, 8]

呃,也就是比黑色稍微亮一点点而已。
我们把它弄得明显一点看看:

import numpy as np
from PIL import ImageDraw, ImageSequence

for i, im in enumerate(ImageSequence.all_frames(img)):
    if i % 70 == 0:
        plt.show()
    plt.subplot(7, 10, i % 70 + 1)
    brighter = list(im.getdata()).index(8)
    bpoint = np.array((brighter % im.width, brighter // im.width))
    draw = ImageDraw.Draw(im)
    draw.ellipse((tuple(bpoint - 5), tuple(bpoint + 5)), 255)
    plt.imshow(im)
    plt.axis('off')
plt.show()

png

png

貌似这个亮点基本上都位于图片的正中间。我们还是把坐标提取出来吧。

from collections import Counter
from PIL import ImageSequence

bpoints = []
for im in ImageSequence.Iterator(img):
    brighter = list(im.getdata()).index(8)
    bpoints.append((brighter % im.width, brighter // im.width))
print(Counter(bpoints))
Counter({(100, 102): 31, (102, 100): 28, (98, 100): 25, (100, 98): 14, (102, 102): 10, (102, 98): 9, (98, 102): 6, (100, 100): 5, (98, 98): 5})

总共只有(98, 100, 102)的两两组合共9种情况。
现在可以结合题目的图片是一个控制杆和标题emulate仿真来看,这些坐标点像是控制杆的9个不同的位置,而(100, 100)应该是控制杆的中间位置,其他位置像是在指挥什么东西在移动。
对!我们可以用画笔来进行这个模拟:

import matplotlib.pyplot as plt
from PIL import Image, ImageDraw

num = Counter(bpoints)[(100, 100)]
index = 0
im = None
for point in bpoints:
    if point == (100, 100):
        index += 1
        if im:
            plt.imshow(im)
        plt.subplot(1, num, index)
        plt.axis('off')
        im = Image.new('RGB', (50, 50))
        draw = ImageDraw.Draw(im)
        lpoint = (25, 25)
    else:
        cpoint = tuple(l + p - 100 for l, p in zip(lpoint, point))
        draw.line((lpoint, cpoint), 'white')
        lpoint = cpoint
plt.imshow(im)
plt.show()

png

好了,地址改为bonus.html,来到了下一题。

总结:这一题主要是图像序列和了解和图像处理的进一步应用。画图工具用熟了会很好过。

本题代码地址22_copper.ipynb