有三张图片:
a.png 、k.png 是数量相等的一串麻将,题目.png是两行麻将,像是麻将种类的列举
解题思路:“题目”作为一个坐标轴,其中第一行是坐标轴的y轴,第二行是坐标轴的x轴,“a”和“k”是两两对应的坐标点,使用脚本匹配,将其坐标点画出来,获得flag。
将题目中的图片进行切片,脚本:
from PIL import Image
pic = Image.open('E:\\题目.png')
x = 0
y = 0
for i in range(0, 42):
# m=Image.new('RGB',(431,73))
n = pic.crop((x, y, x + 53, y + 73))
#将从pic对象中裁剪出一个大小为53x73的矩形图像,并将结果保存在变量n中。
n.save(f'E:\\cuted\\{i}.png')
x += 53
这里记得手动创建一个保存图片切片的目录cuted,否则将找不到目录进而报错。
成功后得到如下一堆图片:
然后根据a和k图片进行匹配,匹配出坐标点,画出坐标点,脚本:
import cv2
import numpy as np
import os
import turtle as t
images = []
x = [0 for i in range(343)]
y = [0 for i in range(343)]
# 获取文件夹内的图片名称
for root, dirs, filenames in os.walk('E:\\cuted'):
for filename in filenames:
f = int(filename.split(".")[0]) # 将文件名转换为数字
images.append(f)
images.sort() # 对文件名进行排序
# 读取识别图像
img_rgb = cv2.imread('E:\\a.png')
if img_rgb is None:
raise Exception("Failed to load image: a.png")
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
# 使用模板匹配进行识别
for i in range(len(images)):
template = cv2.imread(f'E:\\cuted\\{images[i]}.png', 0)
if template is None:
raise Exception(f"Failed to load template: {images[i]}.png")
h, w = template.shape[:2]
result = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
threshold = 0.9
loc = np.where(result >= threshold)
for pt in zip(*loc[::-1]):
m = pt[0] // 53
x[m] = images[i]
bottom_right = (pt[0] + w, pt[1] + h)
cv2.rectangle(img_rgb, pt, bottom_right, (0, 0, 255), 2)
cv2.imwrite("E:\\x.jpg", img_rgb)
# 读取第二张识别图像
img_rgb = cv2.imread('E:\\k.png')
if img_rgb is None:
raise Exception("Failed to load image: k.png")
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
# 使用模板匹配进行识别
for i in range(len(images)):
template = cv2.imread(f'E:\\cuted\\{images[i]}.png', 0)
if template is None:
raise Exception(f"Failed to load template: {images[i]}.png")
h, w = template.shape[:2]
result = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
threshold = 0.9
loc = np.where(result >= threshold)
for pt in zip(*loc[::-1]):
m = pt[0] // 53
y[m] = images[i]
bottom_right = (pt[0] + w, pt[1] + h)
cv2.rectangle(img_rgb, pt, bottom_right, (0, 0, 255), 2)
cv2.imwrite("E:\\y.jpg", img_rgb)
# 打印坐标
for i in range(0, 343):
print(f'{x[i]} {y[i]}')
# 绘制模块
t.speed(100)
t.penup()
for i in range(0, 343):
t.goto(y[i] * 5, -x[i] * 5)
t.pendown()
t.circle(1, 360)
t.penup()
t.done()
运行成功即可获取到flag:
下面再看一个“F61d”战队的脚本:
他们是先获取题目图片尺寸,保存为一个字节组,然后对a.png和k.png进行切片,再拿去对着字节组进行匹配,脚本如下:
import numpy as np # 导入NumPy库,用于数值计算和数组操作
from PIL import Image # 导入PIL库中的Image模块,用于图像处理
class test():
def __init__(self):
self.a = np.array(Image.open("./a.png")) # 通过Image.open()打开并读取名为"a.png"的图像,然后转换为NumPy数组
self.k = np.array(Image.open('./k.png')) # 通过Image.open()打开并读取名为"k.png"的图像,然后转换为NumPy数组
self.img = np.array(Image.open('./题目.png')) # 通过Image.open()打开并读取名为"题目.png"的图像,然后转换为NumPy数组
self.img_make() # 调用img_make()方法,用于处理图像尺寸
self.dic() # 调用dic()方法,用于创建字典映射
def img_make(self):
lst_img = self.img.shape # 获取self.img的形状(尺寸)
self.part = (lst_img[0] // 2, lst_img[1] // 43, lst_img[2]) # 计算图像尺寸的一部分,用于后续的图像分割
def dic(self):
dic = {} # 创建一个空字典,用于存储图像片段与索引的映射关系
for n, x in enumerate(range(self.part[1], self.img.shape[1], self.part[1])):
# 遍历图像中的特定区域,每次从x=self.part[1]开始,到x=self.img.shape[1]为止,以self.part[1]为步长
# 这个循环将图像分割成若干个小片段,并将每个片段的像素值转换为字节序列,并作为键存储到字典dic中,值为n
dic[self.img[0:self.part[0], x:x + self.part[1], :self.part[2] - 1].tobytes()] = n
# 使用字典dic将self.a和self.k中的每个像素值转换为对应的索引值
self.a = [dic[self.a[:, x:x + self.part[1]].tobytes()] for x in range(0, self.a.shape[1], self.part[1])]
self.k = [dic[self.k[:, x:x + self.part[1]].tobytes()] for x in range(0, self.k.shape[1], self.part[1])]
result = np.zeros((max(self.a), max(self.k)), dtype=np.uint8) # 创建一个二维的全零数组,大小由self.a和self.k的最大值确定
result[np.array(self.a) - 1, np.array(self.k) - 1] = 1 # 将数组result中对应索引位置的元素设为1
resimg = Image.fromarray(result * 255) # 将二维数组result转换为PIL图像
resimg.show() # 显示图像
if __name__ == "__main__":
test() # 创建test类的实例,并执行其中的代码
运行成功后可得到flag,如图: