132 lines
3.9 KiB
Plaintext
132 lines
3.9 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 38,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from PIL import Image, ImageOps\n",
|
|
"from pathlib import Path\n",
|
|
"from typing import Optional\n",
|
|
"from matplotlib import pyplot as plt\n",
|
|
"import logging"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"TILE_SIZE = 1650 # in pixels\n",
|
|
"BORDER_SIZE = 200 # in pixels\n",
|
|
"BORDER_COLOR = (255, 255, 255)\n",
|
|
"BACKGROUND_COLOR = (255, 255, 255)\n",
|
|
"IMAGE_DIR = Path(\"board\")\n",
|
|
"\n",
|
|
"# Define the layout grid (rows of indices, None for empty)\n",
|
|
"# fmt: off\n",
|
|
"layout:list[list[Optional[int]]] = [\n",
|
|
" [None, None, 0, None, None],\n",
|
|
" [None, None, 1, None, None],\n",
|
|
" [None, 5, 2, 4, None],\n",
|
|
" [None, None, 3, None, None],\n",
|
|
"]\n",
|
|
"# fmt: on\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"# charuco_410x410_3x3_s133_m105_face3_no_12_DICT_7X7_1000\n",
|
|
"# xxxxxxx_<phy_size>_<grid_size>_<checker_size>_<marker_space>_face<face_idx>_no_<no_idx>_DICT_<dict_size>\n",
|
|
"# 0 1 2 3 4 5 6 7 8\n",
|
|
"def parse_filename_to_face_idx(filename: str):\n",
|
|
" parts = filename.split(\"_\")\n",
|
|
" return int(parts[5][len(\"face\") :])\n",
|
|
"\n",
|
|
"\n",
|
|
"image_pathes = list(IMAGE_DIR.glob(\"*.png\"))\n",
|
|
"image_indice = map(lambda p: parse_filename_to_face_idx(p.stem), image_pathes)\n",
|
|
"images = {k: v for k, v in zip(image_indice, image_pathes)}\n",
|
|
"display(images)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Create blank canvas\n",
|
|
"rows = len(layout)\n",
|
|
"cols = len(layout[0])\n",
|
|
"canvas = Image.new(\"RGB\", (cols * TILE_SIZE, rows * TILE_SIZE), BACKGROUND_COLOR)\n",
|
|
"\n",
|
|
"# Paste tiles\n",
|
|
"for y, row in enumerate(layout):\n",
|
|
" for x, idx in enumerate(row):\n",
|
|
" if idx is not None:\n",
|
|
" path = images.get(idx)\n",
|
|
" if path is not None:\n",
|
|
" tile = Image.open(path)\n",
|
|
" # for the face index 4, rotate the tile 180 degrees\n",
|
|
" if idx == 4:\n",
|
|
" tile = tile.rotate(180)\n",
|
|
" canvas.paste(tile, (x * TILE_SIZE, y * TILE_SIZE))\n",
|
|
" else:\n",
|
|
" logging.warning(f\"Missing: {idx}\")\n",
|
|
"\n",
|
|
"# Calculate canvas size (before border)\n",
|
|
"canvas_width = cols * TILE_SIZE\n",
|
|
"canvas_height = rows * TILE_SIZE\n",
|
|
"\n",
|
|
"# Determine target size to make it square after padding\n",
|
|
"target_size = max(canvas_width, canvas_height)\n",
|
|
"extra_padding = target_size - canvas_height\n",
|
|
"top_pad = extra_padding // 2\n",
|
|
"bottom_pad = extra_padding - top_pad\n",
|
|
"\n",
|
|
"# First add vertical padding to center the layout\n",
|
|
"canvas_with_border = ImageOps.expand(\n",
|
|
" canvas,\n",
|
|
" border=(0, top_pad, 0, bottom_pad), # (left, top, right, bottom)\n",
|
|
" fill=BACKGROUND_COLOR,\n",
|
|
")\n",
|
|
"\n",
|
|
"plt.imshow(canvas_with_border)\n",
|
|
"plt.show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 41,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"canvas_with_border.save(\"merged_uv_layout.png\")"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "Python 3",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.12.10"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
}
|