作为一个我的世界资深玩家,从一个地方到另外的一个地方也得徒步旅行,哪怕是开船,骑马也是很慢的,通过下界还可以,远点也要跑好久。
我是服务器的 op 角色可以直接使用 tp 命令到达某个地方,依然有个很头疼的地方,不知道要去的地方乍样,坐标如何。
于是我就想着做一个相册,然后通过 OCR 来识别坐标,然后点击一下图片就能到那一个地方。
我们来开一下几个 tp 命令吧
execute in world tp user x y z
我们需要把截图的世界类型和坐标识别出来,我试了一些现成的 OCR 工具不能很好的识别出来,于是自己用 python 写了一个。
![](https://img.haomeiwen.com/i4845727/7b9ddda18b61b741.png)
分析截图,我们发现坐标和世界类型位置比较固定,我们使用 graphicsmagick 将坐标和世界类型截下来。
gm convert -crop 600x30+0+260 ${image} world.jpg # 切出世界类型
gm convert -crop 800x30+90+330 ${image} position.jpg # 切出坐标图
初始化,并对图片进行预处理
import numpy as np
from PIL import Image
读取一张图片转成 numpy 数组
data = np.array(Image.open('world.jpg').convert("L"))
图片进行二值化
def normal_image(data, revert = False):
# 获取矩阵(图像)的长宽
data = np.copy(data)
rows, cols = data.shape
for i in range(rows):
for j in range(cols):
# 与阈值比较
if data[i, j] <= 220:
# 设为灰度最小值
if revert:
data[i, j] = 0
else:
data[i, j] = 1
else:
# 设为灰度最大值
if revert:
data[i, j] = 1
else:
data[i, j] = 0
return data
data = normal_image(data)
切除图片的空白边框
def strip_row(data):
# 每行最小值
row_min = np.min(data, axis=1)
# 找到第一个有图像的行
row_start = np.argmin(row_min)
# 找到最后一个有图像的行
row_end = np.argmin(np.flip(row_min))
# 只取有图像的行
if row_end > 0:
data = data[row_start:-row_end, :]
else:
data = data[row_start:, :]
return data
def strip_col(data):
# 每行最小值
col_min = np.min(data, axis=0)
# 找到第一个有图像的行
col_start = np.argmin(col_min)
# 找到最后一个有图像的行
col_end = np.argmin(np.flip(col_min))
# 只取有图像的行
if col_end > 0:
data = data[:, col_start:-col_end]
else:
data = data[:, col_start:]
return data
data = strip_row(data)
data = strip_col(data)
识别世界类型
我的世界类型在原版的只有三种类型,minecraft:overworld
, minecraft:the_nether
和 minecraft:the_end
。
我们只要比对三张图片是否一致就可以。
分别找到三个世界类型的图片,分别预处理后保存成图片,作为世界类型库。
def to_image_array(data):
data = np.copy(data)
# 获取矩阵(图像)的长宽
rows, cols = data.shape
for i in range(rows):
for j in range(cols):
# 与阈值比较
if data[i, j] == 0:
# 设为灰度最小值
data[i, j] = 0
else:
# 设为灰度最大值
data[i, j] = 255
return data
data = to_image_array(data)
img = Image.fromarray(data)
img.save('datasets/overworld.jpg') # 保存成黑白图
# img.save('datasets/the_nether.jpg')
# img.save('datasets/the_end.jpg')
读取世界类型库
datasets = []
dataset = np.array(Image.open('dataset/overworld.jpg').convert("L"))
dataset = normal_image(dataset, True)
datasets.append(dataset)
对比两个世界类型是不是一样(识别)
def recog_one(data, datasets):
x = data.flatten()
got = -1
score = -1
for num, dataset in datasets:
try:
new_score = np.sum(x ^ dataset)
if score == -1:
score = new_score
got = num
if score > new_score:
score = new_score
got = num
except Exception:
pass
return got, score
recog_one(data, datasets)
到这里我们已经把世界类型识别给搞定了。
识别坐标
识别坐标与识别世界类型相识,我们需要把数字切出来,然后手动做字库,最后比对数字,拼成坐标字符就可以搞定。
找出数字的坐标
def find_num_poses(data):
poses = [0]
for pos in range(4, data.shape[1]):
img_matrix_spited = data[:, :pos]
col_min = np.min(img_matrix_spited, axis=0)
col_end = np.argmin(np.flip(col_min))
if col_end > 0:
rel_pos = pos - col_end
if rel_pos not in poses:
poses.append(rel_pos)
return poses
poses = find_num_poses(data)
将数字切出来并保存成文件
poses_start = poses[0:-1]
poses_end = poses[1:]
got_nums = []
for col_start, col_end in zip(poses_start, poses_end):
img_matrix_spited = img_matrix[:, col_start:col_end]
img_matrix_spited = strip_col(img_matrix_spited)
save_image(img_matrix_spited, 'position-{}-{}'.format(col_start, col_end))
手工制作字体库
同样的方法识别每一个数字,最后形成一个坐标。
总结
我们到此位置已经完成了我的世界截图的坐标识别和世界类型识别。
相关代码详见
网友评论