墨卡托投影地图转换为球形钩针图解

2024-05-31 22:00:00   技术   编织 python PIL

很久以前我就种草了一个钩针作品:钩地球。

小红书上有个比较好的图解:【🌎钩针地球摆件玩偶(附图解) - 董阿萌】

成品图比较好看的有

手心地球🌍饰品/摆件【附图解】

成品图来源: 手心地球🌍饰品/摆件【附图解】

工程量真不小,我一直没有开工。但是我一直在想,如果地球可以钩,是不是八(九)大行星都可以钩呢?

于是稍微研究了一下,大概的成品如下:

月球效果

准备工作

现有图(文字)解解构

原有的文字解如下:

原文字解

来源水印已保留,具体还是来自于【🌎钩针地球摆件玩偶(附图解) - 董阿萌】

因为个人钩织的习惯问题,这个看了头还是很痛。我比较喜欢图解,而且在加针/减针的时候换色真的太折磨了,所以用Excel弄了个图解如下,加了个特别丑的水印,可点击放大,连接在一起的色块代表减针: 整理后的图解

整理后得知,这个图解大概是这样的:

  1. 整个球纵向分为上中下3个部分
    1. 上面部分8行,进行了加针的操作,加针规律是第一行6短针,后面每行加1针
    2. 中间部分10行,不加不减
    3. 下面的部分8行,进行了减针的操作,减针规律是除了第4行,每行减1针
  2. 整个球横向平均分为6个部分,每部分最宽8针,最窄1针

(这样打出来就有一种傻乎乎的感觉:satisfied:

素材图片准备

素材图片

大多数的图片我都是从 Views of the Solar System这个网站上下载的

关于地球的图片我还参考了Planetary maps这个网站,不过最后还是没有使用

原来考虑直接从NASA上下载,结果反而太麻烦了

图片纵横比都是1:2,毕竟是墨卡托投影嘛。大多数大小是1440*720,天王星和海王星是720*360,也没什么大问题

代码逻辑思考

其实代码的最终需求是,把原图缩略成只有4个颜色的图并马赛克化,在两极的位置进行一下图片的缩放

所以本质上还是比较清晰的。大概就是这些模块:

  1. 把原图拆分为26*48的方格
  2. 上面8行和下面8行要根据加针和减针的规律,把每行的色块分成不同的部分
  3. 中间的10行每一格独立成一个部分
  4. 把各个部分的颜色全部混杂成一个颜色,用这个唯一的颜色填充这个部分
  5. 然后把整个图片颜色调整为整个图片只有4个颜色
  6. 输出需要的颜色,绘制图解

为了便于理解,我还画了这个测试图片,5个颜色,横向12个色块,纵向13个色块:

色块图片

调整之后会变成这样的效果:

色块填充图片

可以看到因为混色效果,黄色变得更绿,整个图片变得暗沉沉的

所以在后面的代码里面,我还调整了一下亮度和对比度让颜色能更接近原图

色块填充图片高亮

随手填了2个数值,大概是这样的效果,不过我觉得这个也只是在视觉上让自己好受一点,具体生成图解之后,还会受到毛线色号的制约

PIL函数准备

在这个“面向AI编程”的时代,写代码真的是越来越简单了:satisfied:。我在Copilet里面输入“我想用python把一张图片马赛克化,并且只用4个颜色,代码怎么写?”Copilet就噼噼啪啪回复一堆乱七八糟但是挺好用的代码

不过我还是整理了一下,主要用到的函数:

from PIL import Image, ImageDraw, ImageEnhance, ImageFont
  • Image.open(fp, mode=’r’)

    打开图片

  • Image.save(fp, format=None, **params)

    保存图片

  • Image.convert(mode=None, matrix=None, dither=None, palette=0, colors=256)

    图像格式转换,主要用来缩色

  • Image.resize(size, resample=0)

    调整图像(图块)大小

  • Image.crop((x1, y1, x2, y2))

    截取图块

  • Image.getpixel(self, xy)

    获取图像的像素点,主要是后面用来取色

  • obj = ImageEnhance.Brightness(image)

    obj.enhance(factor)

    调整图像的亮度

  • obj = ImageEnhance.Contrast(image)

    obj.enhance(factor)

    调整图像的对比度

  • ImageDraw.Draw(img)

    准备在图像上乱涂乱画(bushi

  • ImageDraw.Draw.rectangle(xy, fill=None, outline=None)

    在图像上绘制实心矩形或者矩形框

  • ImageDraw.Draw.text(xy, text, fill=None, font=None, anchor=None, spacing=0, align=’left’)

    在图像上写字

代码

原图解构

from PIL import Image, ImageDraw, ImageEnhance, ImageFont
import math
import sys

input_image_path = "00-color.png" # 输入素材图路径
num_colors = 4 # 锁色为4色
brightness_e = 1.7 # 亮度调整
contrast_e = 1.7 # 对比度调整
outline = True # 是否画出外框

image = Image.open(input_image_path)
image = image.convert("RGB")
width, height = image.size

# 计算纵向切片高度
double_piece_height = height/(8 + 10 + 8)

拉伸绘图

# 把指定色块给压缩成1*1实现混色效果,然后用这个颜色填满这个色块
def fill_color(image, x1, y1, x2, y2):
    crop_image = image.crop((x1, y1, x2, y2))
    img_small = crop_image.resize((1, 1), Image.LANCZOS)
    img_rgb = img_small.convert("RGB")
    r, g, b = img_rgb.getpixel((0, 0))
    draw = ImageDraw.Draw(image)
    draw.rectangle([(x1, y1), (x2, y2)], fill=(r, g, b))
    return image

# 上 8行 48 列
# 加针 1 2 3 4 5 6 7 8
for y in range(8):
    # 计算当前切片宽度
    double_piece_width = width / 6 / (y + 1.0)
    for x in range(6 * (y + 1)):
        image = fill_color(image,
                           math.ceil(double_piece_width * x),
                           math.ceil(double_piece_height * y),
                           math.ceil(double_piece_width * (x + 1)),
                           math.ceil(double_piece_height * (y + 1))
                           )

# 中 10行 48 列
for y in range(10):
    double_piece_width = width / 48.0
    for x in range(48):
        image = fill_color(image,
                           math.ceil(double_piece_width * x),
                           math.ceil(double_piece_height * (y + 8)),
                           math.ceil(double_piece_width * (x + 1)),
                           math.ceil(double_piece_height * (y + 9))
                           )

# 下 8行 6*48列
# 减针 7 6 5 5 4 3 2 1
for y in range(8):
    # 第四行不减针的特殊操作
    real_y = y
    if y < 3:
        real_y += 1
    double_piece_width = width / 6 / (8.0 - real_y)
    for x in range(6 * (8 - real_y)):
        image = fill_color(image,
                           math.ceil(double_piece_width * x),
                           math.ceil(double_piece_height * (y + 18)),
                           math.ceil(double_piece_width * (x + 1)),
                           math.ceil(double_piece_height * (y + 19))
                           )

缩色

image = image.convert("P", palette=Image.ADAPTIVE, colors=num_colors)
image = image.convert("RGB")

调整亮度和对比度

brightness_enhancer = ImageEnhance.Brightness(image)
image = brightness_enhancer.enhance(brightness_e)
contrast_enhancer = ImageEnhance.Contrast(image)
image = contrast_enhancer.enhance(contrast_e)

取色获取文字解

color_array = []
color_line = []

# 上 8行 48 列
for y in range(8):
    color_line = []
    double_piece_width = width / 6 / (y + 1.0)
    for x in range(6 * (y + 1)):
        r, g, b = image.getpixel((
            math.ceil(double_piece_width * x),
            math.ceil(double_piece_height * y))
        )
        color_line.append((r, g, b))
    color_array.append(color_line)

# 中 10行 48 列
for y in range(10):
    color_line = []
    double_piece_width = width / 48.0
    for x in range(48):
        r, g, b = image.getpixel((
            math.ceil(double_piece_width * x),
            math.ceil(double_piece_height * (y + 8)))
        )
        color_line.append((r, g, b))
    color_array.append(color_line)

# 下 8行 6*48列
for y in range(8):
    color_line = []
    double_piece_width = width / 6 / (8.0 - y)
    for x in range(6 * (8 - y)):
        r, g, b = image.getpixel((
            math.ceil(double_piece_width * x),
            math.ceil(double_piece_height * (y + 18)))
        )
        color_line.append((r, g, b))
    color_array.append(color_line)

for i in range(len(color_array)):
    print("[R%s] " % (i+1), end='')
    color_line = color_array[i]
    for color in color_line:
        r, g, b = color
        print('#{:02x}{:02x}{:02x}, '.format(r, g, b), end='')
    print()

这时候获取的文字解是这样的格式,显然对人类非常不友好:dizzy_face:

[R1] #ff0801, #00ffff, #ff0801, #ffff00, #2d67ff, #ff0801, 
[R2] #ff0801, #ffff00, #ffff00, #00ffff, #2d67ff, #ff0801, #ffff00, #ffff00, #00ffff, #2d67ff, #ff0801, #ffff00, 
[R3] #2d67ff, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #00ffff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ffff00, #ffff00, #00ffff, #00ffff, #2d67ff, #ff0801, #ff0801, 
[R4] #2d67ff, #2d67ff, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #2d67ff, #2d67ff, #ff0801, #ff0801, 
[R5] #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, 
[R6] #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, 
[R7] #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, 
[R8] #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, 
[R9] #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, 
[R10] #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, 
[R11] #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, 
[R12] #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, 
[R13] #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, 
[R14] #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, 
[R15] #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, 
[R16] #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, 
[R17] #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, 
[R18] #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, 
[R19] #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, 
[R20] #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, 
[R21] #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, 
[R22] #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #00ffff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #2d67ff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ff0801, #ffff00, #ffff00, 
[R23] #2d67ff, #2d67ff, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #ffff00, #00ffff, #00ffff, #2d67ff, #2d67ff, #ff0801, #ff0801, 
[R24] #2d67ff, #ff0801, #ff0801, #ffff00, #ffff00, #ffff00, #00ffff, #2d67ff, #2d67ff, #ff0801, #ff0801, #ffff00, #ffff00, #00ffff, #00ffff, #2d67ff, #ff0801, #ff0801, 
[R25] #00ffff, #2d67ff, #ff0801, #ffff00, #ffff00, #00ffff, #2d67ff, #ff0801, #ffff00, #ffff00, #00ffff, #2d67ff, 
[R26] #2d67ff, #ff0801, #00ffff, #ff0801, #ffff00, #2d67ff, 
[Finished in 1.1s]

不管怎么说,我是想要上面那个Excel差不多格式的图解对吧

调试用外框绘制

如果不绘制外框,这里一个中间产物就会是在下面这样的效果

色块填充图片无外框

感觉对人类不太友好,所以我画了点框

如果这里用行星的图片,会有一种莫名的教堂花窗玻璃的感觉

木星彩绘玻璃

好像还挺好看:sweat_smile:

def fill_outline(image, x1, y1, x2, y2):
    draw = ImageDraw.Draw(image)
    draw.rectangle([(x1, y1), (x2, y2)], fill=None, outline="black", width=2)
    return image

if outline:
    # 上 8行 48 列
    for y in range(8):
        double_piece_width = width / 6 / (y + 1.0)
        for x in range(6 * (y + 1)):
            image = fill_outline(image,
                                 math.ceil(double_piece_width * x),
                                 math.ceil(double_piece_height * y),
                                 math.ceil(double_piece_width * (x + 1)),
                                 math.ceil(double_piece_height * (y + 1))
                                 )

    # 中 10行 48 列
    for y in range(10):
        double_piece_width = width / 48.0
        for x in range(48):
            image = fill_outline(image,
                                 math.ceil(double_piece_width * x),
                                 math.ceil(double_piece_height * (y + 8)),
                                 math.ceil(double_piece_width * (x + 1)),
                                 math.ceil(double_piece_height * (y + 9))
                                 )

    # 下 8行 6*48列
    for y in range(8):
    # 减针特殊算法
        real_y = y
        if y < 3:
            real_y += 1
        double_piece_width = width / 6 / (8.0 - real_y)
        for x in range(6 * (8 - real_y)):
            image = fill_outline(image,
                                 math.ceil(double_piece_width * x),
                                 math.ceil(double_piece_height * (y + 18)),
                                 math.ceil(double_piece_width * (x + 1)),
                                 math.ceil(double_piece_height * (y + 19))
                                 )

image.save("filled_"+input_image_path)

设置图解长度和宽度

text_width = 50
offset = 30
max_length = 0
color_block_width = 30
font_size = 20
font = ImageFont.truetype("arial.ttf", font_size)

# 计算最大换色次数
for color_line in color_array:
    color_count = 0
    color_point = None
    for color in color_line:
        if color_point == color:
            pass
        else:
            color_point = color
            color_count +=1
    if color_count > max_length:
        max_length = color_count

image_diagram = Image.new(mode="RGB", 
                          size=(offset + color_block_width * 48 + offset + text_width + text_width * max_length + offset,
                                offset + color_block_width * 26 + offset), 
                          color="white")
draw_diagram = ImageDraw.Draw(image_diagram)

绘制图解

pattern_table = [
    "X       ",
    "XX      ",
    "XX   X  ",
    "XXX  X  ",
    "XXX  XX ",
    "XXXX XX ",
    "XXXX XXX",
    "XXXXXXXX",
    "X"*8,
    "X"*8,
    "X"*8,
    "X"*8,
    "X"*8,
    "X"*8,
    "X"*8,
    "X"*8,
    "X"*8,
    "X"*8,
    "XXXA XXX",
    "XXXX XA ",
    "XXA  XX ",
    "XXX  XX ",
    "XXX  A  ",
    "XA   X  ",
    "XA      ",
    "A       "
]

for y in range(0, len(pattern_table)):
    flag = 0
    color_index = 0
    for x in range(0, len(pattern_table[y]*6)):
        if not pattern_table[y][x % 8].isspace():
            w = 1
            # 减针2倍宽,反正右边没东西了
            if pattern_table[y][x % 8] == "A":
                w = 2
            draw_diagram.rectangle(
                [(x * color_block_width + offset, y * color_block_width + offset),
                ((x + w) * color_block_width + offset, (y + 1) * color_block_width + offset)],
                fill=color_array[y][flag], outline="black", width=1)
            flag += 1
    color_count = 0
    color_point = None
    index = 1

    # 画文字解
    # 行号
    draw_diagram.rectangle([
            (offset + (x + 1) * color_block_width + offset + text_width * color_index,
            y * color_block_width + offset),
            (offset + (x + 1) * color_block_width + offset + text_width * (color_index + 1),
            y * color_block_width + offset + offset)
        ],
        fill=None,
        outline="black",
        width=1)
    draw_diagram.text(
        xy=(offset + (x + 1) * color_block_width + offset + text_width * color_index + font_size/4,
            y * color_block_width + offset + font_size/4),
        text = 'R'+str(y+1), 
        fill = "black", 
        font = font)

    # 文字和框
    for i in range(0, len(color_array[y])):
        color = color_array[y][i]
        if color_point:
            if color == color_point:
                color_count += 1
            else:
                draw_diagram.rectangle(
                    [
                        (offset + (x + 1) * color_block_width + offset + text_width * (color_index + 1),
                         y * color_block_width + offset),
                        (offset + (x + 1) * color_block_width + offset + text_width * (color_index + 2),
                         y * color_block_width + offset + color_block_width)
                    ],
                    fill=None,
                    outline="black",
                    width=1)
                draw_diagram.text(
                    xy=(offset + (x + 1) * color_block_width + offset + text_width * (color_index + 1)  + font_size/4,
                        y * color_block_width + offset + font_size/4),
                    text=str(color_count)+'X',
                    fill=color,
                    font=font
                )
                color_point = color
                color_count = 1
                color_index += 1
        else:
            color_point = color
            color_count += 1
    draw_diagram.rectangle(
                    [
                        (offset + (x + 1) * color_block_width + offset + text_width * (color_index + 1),
                         y * color_block_width + offset),
                        (offset + (x + 1) * color_block_width + offset + text_width * (color_index + 2),
                         y * color_block_width + offset + color_block_width)
                    ],
                    fill=None,
                    outline="black",
                    width=1)
    draw_diagram.text(
                    xy=(offset + (x + 1) * color_block_width + offset + text_width * (color_index + 1)  + font_size/4,
                        y * color_block_width + offset + font_size/4),
                    text=str(color_count)+'X',
                    fill=color,
                    font=font
                )
image_diagram.save("diagram_" + input_image_path)

图解成品

我根据自己的喜好调整了一下亮度和对比度

至于水印已经懒得上了,因为正常人看了这个图解也钩不出来,颜色太接近了,肉眼太难看清楚了,必须要和文字版配合才行

如果有感兴趣的人可以自己用代码跑了试试结果

五色色块图(用来调试)

五色色块图图解

亮度=1.7

对比度=1.7

太阳

太阳图解

亮度=1.3

对比度=1.3

月球

月球图解

亮度=1.1

对比度=1.3

水星

水星图解

亮度=1

对比度=1

金星

金星图解

亮度=1

对比度=1

地球(无冰盖)

地球无冰盖图解

亮度=0.7

对比度=3

火星

火星图解

亮度=0.9

对比度=1.5

土星

土星图解

亮度=1

对比度=1

木星

木星图解

亮度=1.3

对比度=1

天王星

天王星图解

亮度=1

对比度=1

海王星

海王星图解

亮度=0.9

对比度=1.1

:satisfied: 图解生成好了,我几乎完全没有钩出来的欲望了。哦天王星和海王星好像挺简单的,说不定哪天可以动手试一试……

评论共