Skip to content

Instantly share code, notes, and snippets.

@puhitaku
Created April 7, 2020 05:42
Show Gist options
  • Save puhitaku/6886dccfc2f1bc6e2ed9f8fd70ca973e to your computer and use it in GitHub Desktop.
Save puhitaku/6886dccfc2f1bc6e2ed9f8fd70ca973e to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
# atsumorizer - resize and posterize square image into ACNH-compatible one
import json
import click
from more_itertools import collapse
from PIL import Image, ImageDraw, ImageFont
@click.command()
@click.option('--dither/--no-dither', default=True)
@click.argument('image', type=click.Path(exists=True))
def main(dither, image):
with open('palettes.json', 'r') as f:
j = json.load(f)
palettes = []
for raw in j:
p = list(collapse([c['r'], c['g'], c['b']] for c in raw))
palettes.append(p + [p[0], p[1], p[2]] * 241)
im = Image.open(image)
if im.size[0] != im.size[1]:
print(f'The image {image} is not square. Aborting.')
return
if im.size != (32, 32):
im = im.resize((32, 32), Image.LANCZOS).convert('RGBA')
bg = Image.new('RGBA', im.size, (255,255,255))
im = Image.alpha_composite(bg, im).convert('RGB')
im.save('orig.png')
summary = Image.new('RGB', (32 * 7, 32 * 5))
draw = ImageDraw.Draw(summary)
font = ImageFont.truetype('Arial', size=16)
for (i, pal) in enumerate(palettes):
nop = Image.new(mode="P", size=(1, 1))
nop.putpalette(pal)
dither = Image.FLOYDSTEINBERG if dither else Image.NONE
q = im.quantize(colors=15, method=3, dither=dither, palette=nop)
q.save(f'out{i:02d}.png')
text_pos = (i%7) * 32 + 6, i//7 * 96 + 8
draw.text(text_pos, '{:02d}'.format(i+1), font=font)
summary.paste(q.convert('RGB'), ((i%7) * 32, 32 + i//7 * 96))
draw.text((8, 78), 'Original >>', font=font)
summary.paste(im, (32 * 3, 32 * 2 + 6))
summary = summary.resize((summary.size[0]*5, summary.size[1]*5), Image.NEAREST)
summary.save('summary.png')
if __name__ == '__main__':
main()
[
[
{
"r": 229,
"g": 45,
"b": 45
},
{
"r": 229,
"g": 119,
"b": 44
},
{
"r": 229,
"g": 230,
"b": 30
},
{
"r": 119,
"g": 229,
"b": 44
},
{
"r": 27,
"g": 139,
"b": 27
},
{
"r": 56,
"g": 183,
"b": 215
},
{
"r": 41,
"g": 146,
"b": 214
},
{
"r": 60,
"g": 61,
"b": 229
},
{
"r": 139,
"g": 76,
"b": 225
},
{
"r": 226,
"g": 93,
"b": 200
},
{
"r": 228,
"g": 169,
"b": 151
},
{
"r": 138,
"g": 81,
"b": 62
},
{
"r": 229,
"g": 229,
"b": 229
},
{
"r": 110,
"g": 110,
"b": 110
},
{
"r": 20,
"g": 20,
"b": 20
}
],
[
{
"r": 110,
"g": 22,
"b": 21
},
{
"r": 110,
"g": 52,
"b": 14
},
{
"r": 110,
"g": 110,
"b": 12
},
{
"r": 63,
"g": 124,
"b": 21
},
{
"r": 12,
"g": 65,
"b": 13
},
{
"r": 15,
"g": 103,
"b": 123
},
{
"r": 14,
"g": 81,
"b": 124
},
{
"r": 22,
"g": 22,
"b": 110
},
{
"r": 69,
"g": 33,
"b": 120
},
{
"r": 121,
"g": 36,
"b": 101
},
{
"r": 108,
"g": 68,
"b": 58
},
{
"r": 50,
"g": 27,
"b": 19
},
{
"r": 110,
"g": 110,
"b": 110
},
{
"r": 50,
"g": 50,
"b": 50
},
{
"r": 20,
"g": 20,
"b": 20
}
],
[
{
"r": 230,
"g": 122,
"b": 122
},
{
"r": 229,
"g": 155,
"b": 106
},
{
"r": 230,
"g": 229,
"b": 121
},
{
"r": 156,
"g": 230,
"b": 107
},
{
"r": 87,
"g": 185,
"b": 86
},
{
"r": 136,
"g": 212,
"b": 228
},
{
"r": 107,
"g": 181,
"b": 230
},
{
"r": 121,
"g": 122,
"b": 230
},
{
"r": 158,
"g": 106,
"b": 230
},
{
"r": 229,
"g": 138,
"b": 205
},
{
"r": 230,
"g": 192,
"b": 183
},
{
"r": 212,
"g": 147,
"b": 129
},
{
"r": 229,
"g": 229,
"b": 229
},
{
"r": 169,
"g": 169,
"b": 169
},
{
"r": 124,
"g": 124,
"b": 124
}
],
[
{
"r": 230,
"g": 132,
"b": 107
},
{
"r": 229,
"g": 165,
"b": 121
},
{
"r": 229,
"g": 193,
"b": 167
},
{
"r": 229,
"g": 138,
"b": 137
},
{
"r": 230,
"g": 108,
"b": 107
},
{
"r": 171,
"g": 215,
"b": 188
},
{
"r": 146,
"g": 199,
"b": 145
},
{
"r": 71,
"g": 155,
"b": 121
},
{
"r": 160,
"g": 198,
"b": 95
},
{
"r": 227,
"g": 209,
"b": 123
},
{
"r": 231,
"g": 194,
"b": 61
},
{
"r": 195,
"g": 153,
"b": 81
},
{
"r": 229,
"g": 215,
"b": 214
},
{
"r": 169,
"g": 123,
"b": 133
},
{
"r": 110,
"g": 66,
"b": 83
}
],
[
{
"r": 70,
"g": 71,
"b": 215
},
{
"r": 107,
"g": 131,
"b": 229
},
{
"r": 114,
"g": 215,
"b": 175
},
{
"r": 61,
"g": 185,
"b": 135
},
{
"r": 41,
"g": 124,
"b": 108
},
{
"r": 229,
"g": 183,
"b": 220
},
{
"r": 230,
"g": 137,
"b": 192
},
{
"r": 169,
"g": 56,
"b": 148
},
{
"r": 212,
"g": 73,
"b": 96
},
{
"r": 230,
"g": 162,
"b": 65
},
{
"r": 226,
"g": 230,
"b": 81
},
{
"r": 169,
"g": 169,
"b": 45
},
{
"r": 214,
"g": 223,
"b": 228
},
{
"r": 113,
"g": 146,
"b": 153
},
{
"r": 13,
"g": 62,
"b": 95
}
],
[
{
"r": 199,
"g": 94,
"b": 65
},
{
"r": 214,
"g": 145,
"b": 41
},
{
"r": 229,
"g": 181,
"b": 107
},
{
"r": 169,
"g": 115,
"b": 79
},
{
"r": 110,
"g": 67,
"b": 58
},
{
"r": 163,
"g": 200,
"b": 105
},
{
"r": 104,
"g": 154,
"b": 91
},
{
"r": 67,
"g": 110,
"b": 57
},
{
"r": 91,
"g": 123,
"b": 112
},
{
"r": 148,
"g": 183,
"b": 177
},
{
"r": 134,
"g": 137,
"b": 182
},
{
"r": 88,
"g": 91,
"b": 172
},
{
"r": 229,
"g": 221,
"b": 184
},
{
"r": 139,
"g": 110,
"b": 102
},
{
"r": 80,
"g": 57,
"b": 41
}
],
[
{
"r": 109,
"g": 43,
"b": 44
},
{
"r": 199,
"g": 93,
"b": 93
},
{
"r": 229,
"g": 230,
"b": 92
},
{
"r": 153,
"g": 154,
"b": 60
},
{
"r": 95,
"g": 95,
"b": 25
},
{
"r": 57,
"g": 85,
"b": 124
},
{
"r": 78,
"g": 37,
"b": 139
},
{
"r": 36,
"g": 44,
"b": 80
},
{
"r": 48,
"g": 79,
"b": 45
},
{
"r": 78,
"g": 122,
"b": 69
},
{
"r": 112,
"g": 155,
"b": 136
},
{
"r": 82,
"g": 110,
"b": 98
},
{
"r": 200,
"g": 204,
"b": 215
},
{
"r": 79,
"g": 104,
"b": 109
},
{
"r": 26,
"g": 36,
"b": 48
}
],
[
{
"r": 230,
"g": 229,
"b": 59
},
{
"r": 229,
"g": 217,
"b": 167
},
{
"r": 229,
"g": 187,
"b": 15
},
{
"r": 229,
"g": 168,
"b": 75
},
{
"r": 230,
"g": 143,
"b": 14
},
{
"r": 91,
"g": 119,
"b": 229
},
{
"r": 60,
"g": 61,
"b": 229
},
{
"r": 26,
"g": 26,
"b": 138
},
{
"r": 24,
"g": 84,
"b": 120
},
{
"r": 225,
"g": 154,
"b": 214
},
{
"r": 225,
"g": 108,
"b": 160
},
{
"r": 227,
"g": 78,
"b": 74
},
{
"r": 229,
"g": 229,
"b": 229
},
{
"r": 123,
"g": 124,
"b": 170
},
{
"r": 21,
"g": 22,
"b": 79
}
],
[
{
"r": 139,
"g": 17,
"b": 90
},
{
"r": 214,
"g": 71,
"b": 213
},
{
"r": 174,
"g": 114,
"b": 214
},
{
"r": 128,
"g": 71,
"b": 214
},
{
"r": 71,
"g": 39,
"b": 200
},
{
"r": 229,
"g": 230,
"b": 14
},
{
"r": 229,
"g": 187,
"b": 15
},
{
"r": 230,
"g": 149,
"b": 31
},
{
"r": 226,
"g": 120,
"b": 42
},
{
"r": 194,
"g": 228,
"b": 46
},
{
"r": 118,
"g": 199,
"b": 68
},
{
"r": 45,
"g": 151,
"b": 85
},
{
"r": 229,
"g": 230,
"b": 214
},
{
"r": 78,
"g": 123,
"b": 66
},
{
"r": 12,
"g": 65,
"b": 13
}
],
[
{
"r": 229,
"g": 230,
"b": 152
},
{
"r": 229,
"g": 230,
"b": 14
},
{
"r": 229,
"g": 187,
"b": 15
},
{
"r": 230,
"g": 143,
"b": 14
},
{
"r": 230,
"g": 100,
"b": 14
},
{
"r": 215,
"g": 85,
"b": 85
},
{
"r": 184,
"g": 36,
"b": 36
},
{
"r": 124,
"g": 14,
"b": 13
},
{
"r": 156,
"g": 10,
"b": 95
},
{
"r": 215,
"g": 41,
"b": 112
},
{
"r": 228,
"g": 123,
"b": 140
},
{
"r": 228,
"g": 168,
"b": 167
},
{
"r": 229,
"g": 215,
"b": 214
},
{
"r": 153,
"g": 92,
"b": 91
},
{
"r": 65,
"g": 27,
"b": 26
}
],
[
{
"r": 152,
"g": 229,
"b": 183
},
{
"r": 56,
"g": 215,
"b": 151
},
{
"r": 33,
"g": 170,
"b": 142
},
{
"r": 61,
"g": 195,
"b": 230
},
{
"r": 30,
"g": 149,
"b": 229
},
{
"r": 28,
"g": 101,
"b": 214
},
{
"r": 27,
"g": 60,
"b": 199
},
{
"r": 20,
"g": 20,
"b": 154
},
{
"r": 75,
"g": 50,
"b": 180
},
{
"r": 118,
"g": 57,
"b": 210
},
{
"r": 78,
"g": 16,
"b": 127
},
{
"r": 52,
"g": 9,
"b": 81
},
{
"r": 215,
"g": 226,
"b": 228
},
{
"r": 82,
"g": 97,
"b": 154
},
{
"r": 21,
"g": 22,
"b": 79
}
],
[
{
"r": 229,
"g": 220,
"b": 213
},
{
"r": 229,
"g": 193,
"b": 167
},
{
"r": 229,
"g": 173,
"b": 136
},
{
"r": 229,
"g": 155,
"b": 106
},
{
"r": 199,
"g": 127,
"b": 79
},
{
"r": 229,
"g": 138,
"b": 137
},
{
"r": 230,
"g": 108,
"b": 107
},
{
"r": 199,
"g": 93,
"b": 93
},
{
"r": 184,
"g": 97,
"b": 69
},
{
"r": 135,
"g": 83,
"b": 62
},
{
"r": 136,
"g": 97,
"b": 68
},
{
"r": 110,
"g": 74,
"b": 48
},
{
"r": 79,
"g": 53,
"b": 36
},
{
"r": 50,
"g": 32,
"b": 20
},
{
"r": 20,
"g": 12,
"b": 9
}
],
[
{
"r": 229,
"g": 220,
"b": 213
},
{
"r": 229,
"g": 205,
"b": 107
},
{
"r": 229,
"g": 187,
"b": 15
},
{
"r": 229,
"g": 168,
"b": 75
},
{
"r": 230,
"g": 143,
"b": 14
},
{
"r": 229,
"g": 156,
"b": 137
},
{
"r": 229,
"g": 120,
"b": 91
},
{
"r": 215,
"g": 88,
"b": 56
},
{
"r": 183,
"g": 56,
"b": 23
},
{
"r": 137,
"g": 43,
"b": 15
},
{
"r": 166,
"g": 96,
"b": 47
},
{
"r": 152,
"g": 75,
"b": 21
},
{
"r": 110,
"g": 52,
"b": 14
},
{
"r": 80,
"g": 40,
"b": 14
},
{
"r": 50,
"g": 27,
"b": 19
}
],
[
{
"r": 229,
"g": 229,
"b": 229
},
{
"r": 214,
"g": 214,
"b": 214
},
{
"r": 199,
"g": 199,
"b": 199
},
{
"r": 184,
"g": 184,
"b": 184
},
{
"r": 169,
"g": 169,
"b": 169
},
{
"r": 154,
"g": 154,
"b": 154
},
{
"r": 139,
"g": 139,
"b": 139
},
{
"r": 124,
"g": 124,
"b": 124
},
{
"r": 110,
"g": 110,
"b": 110
},
{
"r": 95,
"g": 95,
"b": 95
},
{
"r": 80,
"g": 80,
"b": 80
},
{
"r": 65,
"g": 65,
"b": 65
},
{
"r": 50,
"g": 50,
"b": 50
},
{
"r": 35,
"g": 35,
"b": 35
},
{
"r": 20,
"g": 20,
"b": 20
}
]
]
click
more_itertools
pillow
#!/usr/bin/env python3
# textify - run-length encode atsumorized images to help players to copy it
import click
from more_itertools import sliced, run_length
from PIL import Image
@click.command()
@click.argument('image', type=click.Path(exists=True))
def main(image):
im = Image.open(image)
pal = list(sliced(im.getpalette(), 3))
for y in range(im.size[1]):
l = []
for x in range(im.size[0]):
l.append('{:X}'.format(im.getpixel((x, y))))
rle = [f'{p}x{n}' for p, n in run_length.encode(l)]
print(f'[{y:02d}] ' + ' '.join(rle))
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment