Generating UV maps with Python

2026/06/30

I wrote a little script that will generate UV maps of a certain size and with a certain resolution.

Source program

# /// script
# dependencies = [
#     "pillow",
# ]
# ///

from PIL import Image

import random
import argparse

parser = argparse.ArgumentParser()
parser.add_argument("-n", type=int                                             , required=True  , help="resolution")
parser.add_argument("-w", type=int                                             , required=True  , help="pixel size")
parser.add_argument("-r", type=str , choices=["x", "y", "0"] , default="x"     , required=False , help="Red component")
parser.add_argument("-g", type=str , choices=["x", "y", "0"] , default="y"     , required=False , help="Green component")
parser.add_argument("-b", type=str , choices=["x", "y", "0"] , default="0"     , required=False , help="Blue component")
parser.add_argument("-o", type=str                           , default="a.png" , required=False , help="output")
args = parser.parse_args()

img = Image.new("RGB", (args.w, args.w))
pixels = img.load()

cell_size = args.w // args.n

for j in range(args.n):
    for i in range(args.n):
        if args.r == "x": r = i
        elif args.r == "y": r = j
        elif args.r == "0": r = 0

        if args.g == "x": g = i
        elif args.g == "y": g = j
        elif args.g == "0": g = 0

        if args.b == "x": b = i
        elif args.b == "y": b = j
        elif args.b == "0": b = 0

        color = (
            int(r / (args.n - 1) * 255),
            int(g / (args.n - 1) * 255),
            int(b / (args.n - 1) * 255),
        )

        for y in range(j * cell_size, (j + 1) * cell_size):
            for x in range(i * cell_size, (i + 1) * cell_size):
                pixels[x, y] = color

img.save(args.o)
print(f"Generated {args.o}")

Usage

usage: uv_map.py [-h] -n N -w W [-r {x,y,0}] [-g {x,y,0}] [-b {x,y,0}] [-o O]

options:
  -h, --help  show this help message and exit
  -n N        resolution
  -w W        pixel size
  -r {x,y,0}  Red component
  -g {x,y,0}  Green component
  -b {x,y,0}  Blue component
  -o O        output

e.g.

uv run uv_map.py -w 256 -n 16
uv run uv_map.py -w 256 -n 16 -r i -g j -b 0

tip

The images in the following table have been generated with.

for r in ["x", "y", "0"] {
    for g in ["x", "y", "0"] {
        for b in ["x", "y", "0"] {
            for n in [4, 16, 64] {
                uv run /tmp/uv_map.py -w 256 -n $n -r $r -g $g -b $b -o $"uv_map_w256_n($n)_r($r)_g($g)_b($b).png"
            }
        }
    }
}
$$n=4$$ $$n=16$$ $$n=64$$
$$r=x$$ $$g=y$$ $$b=0$$ uv_map_w256_n4_rx_gy_b0.png uv_map_w256_n16_rx_gy_b0.png uv_map_w256_n64_rx_gy_b0.png
$$r=x$$ $$g=y$$ $$b=y$$ uv_map_w256_n4_rx_gy_by.png uv_map_w256_n16_rx_gy_by.png uv_map_w256_n64_rx_gy_by.png
$$r=x$$ $$g=x$$ $$b=x$$ uv_map_w256_n4_rx_gx_bx.png uv_map_w256_n16_rx_gx_bx.png uv_map_w256_n64_rx_gx_bx.png
$$r=x$$ $$g=x$$ $$b=y$$ uv_map_w256_n4_rx_gx_by.png uv_map_w256_n16_rx_gx_by.png uv_map_w256_n64_rx_gx_by.png
$$r=x$$ $$g=x$$ $$b=0$$ uv_map_w256_n4_rx_gx_b0.png uv_map_w256_n16_rx_gx_b0.png uv_map_w256_n64_rx_gx_b0.png
$$r=x$$ $$g=y$$ $$b=x$$ uv_map_w256_n4_rx_gy_bx.png uv_map_w256_n16_rx_gy_bx.png uv_map_w256_n64_rx_gy_bx.png
$$r=x$$ $$g=y$$ $$b=y$$ uv_map_w256_n4_rx_gy_by.png uv_map_w256_n16_rx_gy_by.png uv_map_w256_n64_rx_gy_by.png
$$r=x$$ $$g=y$$ $$b=0$$ uv_map_w256_n4_rx_gy_b0.png uv_map_w256_n16_rx_gy_b0.png uv_map_w256_n64_rx_gy_b0.png
$$r=x$$ $$g=0$$ $$b=x$$ uv_map_w256_n4_rx_g0_bx.png uv_map_w256_n16_rx_g0_bx.png uv_map_w256_n64_rx_g0_bx.png
$$r=x$$ $$g=0$$ $$b=y$$ uv_map_w256_n4_rx_g0_by.png uv_map_w256_n16_rx_g0_by.png uv_map_w256_n64_rx_g0_by.png
$$r=x$$ $$g=0$$ $$b=0$$ uv_map_w256_n4_rx_g0_b0.png uv_map_w256_n16_rx_g0_b0.png uv_map_w256_n64_rx_g0_b0.png
$$r=y$$ $$g=x$$ $$b=x$$ uv_map_w256_n4_ry_gx_bx.png uv_map_w256_n16_ry_gx_bx.png uv_map_w256_n64_ry_gx_bx.png
$$r=y$$ $$g=x$$ $$b=y$$ uv_map_w256_n4_ry_gx_by.png uv_map_w256_n16_ry_gx_by.png uv_map_w256_n64_ry_gx_by.png
$$r=y$$ $$g=x$$ $$b=0$$ uv_map_w256_n4_ry_gx_b0.png uv_map_w256_n16_ry_gx_b0.png uv_map_w256_n64_ry_gx_b0.png
$$r=y$$ $$g=y$$ $$b=x$$ uv_map_w256_n4_ry_gy_bx.png uv_map_w256_n16_ry_gy_bx.png uv_map_w256_n64_ry_gy_bx.png
$$r=y$$ $$g=y$$ $$b=y$$ uv_map_w256_n4_ry_gy_by.png uv_map_w256_n16_ry_gy_by.png uv_map_w256_n64_ry_gy_by.png
$$r=y$$ $$g=y$$ $$b=0$$ uv_map_w256_n4_ry_gy_b0.png uv_map_w256_n16_ry_gy_b0.png uv_map_w256_n64_ry_gy_b0.png
$$r=y$$ $$g=0$$ $$b=x$$ uv_map_w256_n4_ry_g0_bx.png uv_map_w256_n16_ry_g0_bx.png uv_map_w256_n64_ry_g0_bx.png
$$r=y$$ $$g=0$$ $$b=y$$ uv_map_w256_n4_ry_g0_by.png uv_map_w256_n16_ry_g0_by.png uv_map_w256_n64_ry_g0_by.png
$$r=y$$ $$g=0$$ $$b=0$$ uv_map_w256_n4_ry_g0_b0.png uv_map_w256_n16_ry_g0_b0.png uv_map_w256_n64_ry_g0_b0.png
$$r=0$$ $$g=x$$ $$b=x$$ uv_map_w256_n4_r0_gx_bx.png uv_map_w256_n16_r0_gx_bx.png uv_map_w256_n64_r0_gx_bx.png
$$r=0$$ $$g=x$$ $$b=y$$ uv_map_w256_n4_r0_gx_by.png uv_map_w256_n16_r0_gx_by.png uv_map_w256_n64_r0_gx_by.png
$$r=0$$ $$g=x$$ $$b=0$$ uv_map_w256_n4_r0_gx_b0.png uv_map_w256_n16_r0_gx_b0.png uv_map_w256_n64_r0_gx_b0.png
$$r=0$$ $$g=y$$ $$b=x$$ uv_map_w256_n4_r0_gy_bx.png uv_map_w256_n16_r0_gy_bx.png uv_map_w256_n64_r0_gy_bx.png
$$r=0$$ $$g=y$$ $$b=y$$ uv_map_w256_n4_r0_gy_by.png uv_map_w256_n16_r0_gy_by.png uv_map_w256_n64_r0_gy_by.png
$$r=0$$ $$g=y$$ $$b=0$$ uv_map_w256_n4_r0_gy_b0.png uv_map_w256_n16_r0_gy_b0.png uv_map_w256_n64_r0_gy_b0.png
$$r=0$$ $$g=0$$ $$b=x$$ uv_map_w256_n4_r0_g0_bx.png uv_map_w256_n16_r0_g0_bx.png uv_map_w256_n64_r0_g0_bx.png
$$r=0$$ $$g=0$$ $$b=y$$ uv_map_w256_n4_r0_g0_by.png uv_map_w256_n16_r0_g0_by.png uv_map_w256_n64_r0_g0_by.png
$$r=0$$ $$g=0$$ $$b=0$$ uv_map_w256_n4_r0_g0_b0.png uv_map_w256_n16_r0_g0_b0.png uv_map_w256_n64_r0_g0_b0.png