引言
最近永夜星河
火了之后,带动了一些周边,有一些手办{叫什么拼豆}我也没看明白怎么玩的,像是十字绣那种感觉,DIY的话,需要买一些原料,但是原料还凑不齐,参考好几家也找不到,找到他们有的颜色,和模板上对应,后期可以参考得到的颜色,看看对不对,色差大不大。
知识点
颜色距离:欧几里得距离
编程语言:Python 以及 数学扩展
前期整理
图像虚化,这样能减少取色难度,但是也扩大了误差
from PIL import Image, ImageFilter, ImageDraw
s17_17 = [] # 本想自动获取
image_path = "test.png"
image = Image.open(image_path)
image = image.filter(ImageFilter.GaussianBlur(radius=2))
image.save("s1.png")
draw = ImageDraw.Draw(image)
image.show()
本想根据坐标直接获取颜色值,但是这个图片还需要进一步处理,OCR也没有完全提取出他的编号,这里就手工做吧。(感觉手工和代码时间成本接近,甚至手工更高)
后期比对代码
import tkinter as tk
import numpy as np
import struct
# 计算两个颜色之间的欧几里得距离
def color_distance(c1, c2):
return np.linalg.norm(np.array(c1) - np.array(c2))
def get_declare_color(color):
if type(color) == str:
color = struct.unpack('BBB', bytes.fromhex("ff2f2f"))
return tuple([255, 255, 255]) if color_distance(color, [255, 255, 255]) > color_distance(color, [0, 0, 0]) else tuple(
[ 0, 0, 0])
# 找到最接近的颜色
def find_closest_color(color, color_list):
distances = [color_distance(color, c) for c in color_list]
return color_list[np.argmin(distances)], min(distances)
def find_id(color, color_list):
for i in color_list:
if color_list[i].replace(" ", '') == f"""{color[0]},{color[1]},{color[2]}""":
return i
def render_msg(cur, text, ids, dist):
status.config(text=f"""当前颜色值:{struct.unpack('BBB', bytes.fromhex(cur[1:]))}
目标颜色值:{text}
标号:{ids}
距离:{dist}
点我重置""")
# 处理左侧颜色块点击事件
def on_left_click(event, right_colors):
clicked_color = event.widget.cget("bg") # 获取点击块的背景色
clicked_color_rgb = [int(clicked_color[i:i + 2], 16) for i in (1, 3, 5)] # 转换为RGB格式
closest_color, dist = find_closest_color(clicked_color_rgb, right_colors)
id1 = find_id(closest_color, colors2)
left_frame.config(bg=("#%02x%02x%02x" % closest_color))
right_frame.config(bg=("#%02x%02x%02x" % closest_color))
render_msg(clicked_color, closest_color, id1, dist)
def on_right_click(event, left_colors):
clicked_color = event.widget.cget("bg") # 获取点击块的背景色
clicked_color_rgb = [int(clicked_color[i:i + 2], 16) for i in (1, 3, 5)] # 转换为RGB格式
closest_color, dist = find_closest_color(clicked_color_rgb, left_colors)
id2 = find_id(closest_color, colors1)
left_frame.config(bg=("#%02x%02x%02x" % closest_color))
right_frame.config(bg=("#%02x%02x%02x" % closest_color))
render_msg(clicked_color, closest_color, id2, dist)
# 解析颜色字符串为 RGB 格式
def parse_color(color_str):
return tuple(map(int, color_str.split(',')))
# 左侧颜色数据
colors1 = {
"A1": "234, 132, 68",
"A2": "224, 196, 65",
"C3": "245, 163, 72",
"D4": "244, 108, 8",
"A5": "120, 150, 200",
"B6": "45, 90, 180",
"C7": "245, 220, 60",
"D8": "90, 200, 255",
"E9": "255, 165, 0",
"F10": "200, 0, 100",
"G11": "100, 250, 150",
"H12": "30, 30, 30",
"I13": "10, 10, 10",
"J14": "0, 0, 0",
}
colors2 = {
"_A1": "34, 132, 68",
"_A2": "24, 196, 65",
"_C3": "245, 63, 72",
"_D4": "244, 18, 8",
"_A5": "120, 150, 20",
"_B6": "45, 90, 80",
"_C7": "215, 20, 60",
"_D8": "80, 220, 255",
"_E9": "211, 175, 0",
"_F10": "200, 50, 100",
"_G11": "100, 210, 150",
}
def reset():
left_frame.config(bg="#f0f0f0")
right_frame.config(bg="#f0f0f0")
status.config(text="已重置")
# 将颜色字典转换为 RGB 格式的列表
left_colors = [parse_color(value) for value in colors1.values()]
color_keys = list(colors1.keys())
# 右侧颜色数据:每个 RGB 值都加 1
right_colors = [parse_color(value) for value in colors2.values()]
color_keys2 = list(colors2.keys())
# 创建Tkinter窗口
root = tk.Tk()
root.title("颜色匹配")
# 创建状态标签,放置在最底部
status = tk.Label(root, text="状态:", anchor="w", padx=10, justify="left")
status.pack(side='bottom', fill='x', pady=5) # 使状态标签横向填充
status.bind('<Button-1>', lambda event: reset())
# 创建左侧颜色展示区域
left_frame = tk.Frame(root)
left_frame.pack(side="left", padx=10, pady=10)
# 创建右侧颜色展示区域
right_frame = tk.Frame(root)
right_frame.pack(side="right", padx=10, pady=10)
# 设置每行最多显示6个颜色块的行数
colors_per_row = 6
# 创建左侧颜色块并绑定点击事件
left_color_blocks = []
for i, color in enumerate(left_colors):
fs = get_declare_color(color)
# 创建每个颜色块
block = tk.Label(left_frame, bg=("#%02x%02x%02x" % color), width=12, height=2,
text=f"""{color_keys[i]}\n[{colors1[color_keys[i]]}]""",
foreground=("#%02x%02x%02x" % fs))
block.grid(row=i // colors_per_row, column=i % colors_per_row, padx=5, pady=5) # 使用grid进行布局
block.bind("<Button-1>", lambda event, right_colors=right_colors: on_left_click(event, right_colors))
left_color_blocks.append(block)
# 创建右侧颜色块
right_color_blocks = []
for i, color in enumerate(right_colors):
fs = get_declare_color(color)
block = tk.Label(right_frame, bg=("#%02x%02x%02x" % color), width=12, height=2,
text=f"""{color_keys2[i]}\n[{colors2[color_keys2[i]]}]""",
foreground=("#%02x%02x%02x" % fs))
block.grid(row=i // colors_per_row, column=i % colors_per_row, padx=5, pady=5) # 使用grid进行布局
block.bind("<Button-1>",
lambda event, right_colors=right_colors: on_right_click(event, left_colors))
right_color_blocks.append(block)
# 进入主循环
root.mainloop()
比如D4对应的是右侧的_E9