Loading... # 引言 最近`永夜星河`火了之后,带动了一些周边,有一些手办{叫什么拼豆}我也没看明白怎么玩的,像是十字绣那种感觉,DIY的话,需要买一些原料,但是原料还凑不齐,参考好几家也找不到,找到他们有的颜色,和模板上对应,后期可以参考得到的颜色,看看对不对,色差大不大。 # 知识点 颜色距离:欧几里得距离 编程语言:Python 以及 数学扩展 # 前期整理 ![test.png](https://www.zunmx.top/usr/uploads/2024/12/1588255310.png) 图像虚化,这样能减少取色难度,但是也扩大了误差 ```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 ![image.png](https://www.zunmx.top/usr/uploads/2024/12/589553325.png) # 结语 最终导入数据后,根据实际情况调整一下样式 ![image.png](https://www.zunmx.top/usr/uploads/2024/12/146434618.png) © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 2 如果觉得我的文章对你有用,请随意赞赏