Compare commits

...

3 Commits

Author SHA1 Message Date
Michael Pilosov, PhD
447b773d03 no annotate, update target 2024-02-13 03:42:37 +00:00
Michael Pilosov, PhD
00cba285c8 minor tweaks, check annotations 2024-02-13 03:34:31 +00:00
Michael Pilosov, PhD
9953327d30 copy over color files, use new plotting 2024-02-13 03:26:37 +00:00
7 changed files with 844 additions and 5 deletions

View File

@ -101,18 +101,24 @@ def plot_preds(
ax.set_xticks([])
ax.set_yticks([])
ax.axis("off")
ax.set_aspect("equal")
ax.axis("off")
radius = 1
ax.set_ylim(-radius, radius)
ax.set_xlim(-radius, radius)
# Overlay white circle
radius = 1 / 3
inner_radius = 1 / 3
circle = patches.Circle(
(0, 0), radius, transform=ax.transData._b, color="white", zorder=2
(0, 0), inner_radius, transform=ax.transData._b, color="white", zorder=2
)
ax.add_patch(circle)
fig.tight_layout(pad=0)
plt.savefig(f"{fname}.png", dpi=dpi, transparent=False)
plt.savefig(
f"{fname}.png", dpi=dpi, transparent=False, pad_inches=0, bbox_inches="tight"
)
plt.close()

235
color_poster.py Normal file
View File

@ -0,0 +1,235 @@
from typing import List
import matplotlib.colors as mcolors
import matplotlib.pyplot as plt
import numpy as np
# set font by default to courier new
plt.rcParams["font.family"] = "PT Mono"
# # sort by the proximity to the colors in viridis
# # Importing necessary functions
# # Calculate the proximity of each color in XKCD_COLORS to the viridis colormap
# def calculate_proximity_to_viridis(color):
# rgb_triple = mcolors.to_rgb(mcolors.XKCD_COLORS[color])
# distances = [(sum((a - b)**2 for a, b in zip(rgb_triple, viridis(i))), i) for i in range(256)]
# _, closest_viridis_value = min(distances, key=lambda x: x[0]) # Find the viridis color with the minimum distance
# return closest_viridis_value / 255 # Normalize to range (0, 1)
# # Calculate the proximity values for each color
# proximity_values = {color: calculate_proximity_to_viridis(color) for color in colors}
# # Sort the colors based on their proximity values
# sorted_colors = sorted(proximity_values.keys(), key=lambda x: proximity_values[x])
def create_color_calibration_image(
colors, ppi: List[int] = [100], index=0, solid_capstyle="butt", antialiased=True
):
first_color = colors[0]
last_color = colors[-1]
print(f"Processing color range: {first_color} to {last_color}")
# Conversion factor: 1 inch = dpi pixels
vert_space = 1 / 2 # inches
fontsize = 12 # points
# Figure settings
fig_width = 4.0 # inches
fig_height = len(colors) * vert_space
fig, ax = plt.subplots(figsize=(fig_width, fig_height))
# plot a vertical black rectangle between x=3 and x=4
ax.axvspan(0.25, 0.75, facecolor="black")
# ax.axvspan(-0.325-0.175, -0.125, facecolor="black")
# Loop through each color
if index % 2 == 0:
skip = -1
else:
skip = 1
for idx, color in enumerate(colors[::skip]):
# print(color)
y_position = 0.5 - 0.125 + idx * vert_space # Offset each color by 0.3 inches
# Draw color name
rgb_triple = mcolors.to_rgb(mcolors.XKCD_COLORS[color])
# round to 4 decimal places
rgb_triple = tuple(round(x, 4) for x in rgb_triple)
# format as string with fixed decimal places
rgb_triple = ", ".join([f"{x:1.4f}" for x in rgb_triple])
hex_code = mcolors.to_hex(mcolors.XKCD_COLORS[color])
ax.text(
1.0,
y_position,
color.replace("xkcd:", ""),
va="center",
fontsize=fontsize,
# bbox=dict(facecolor='gray', alpha=0.21),
)
ax.text(
1.25,
y_position - 1.5 * fontsize / 72,
f"{hex_code}\n({rgb_triple})",
va="center",
fontsize=6,
# bbox=dict(facecolor='gray', alpha=0.33),
)
# ax.text(
# 1.125,
# y_position - 2 * fontsize / 72,
# f"{hex_code}",
# va="center",
# fontsize=fontsize * 0.6,
# )
# Draw color square
rect_height = 0.25
rect_width = 0.25
square_x_start = 0.75 - rect_width / 2 # Offset from the left
square_y_start = y_position - rect_height # + 0.25
ax.add_patch(
plt.Rectangle(
(square_x_start, square_y_start), rect_width, rect_height, fc=color
)
)
# Draw lines with varying stroke sizes
line_x_start = 0 # Offset from the left
line_length = 0.5
line_y_start = y_position - rect_height - 0.075
line_widths = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2.0, 3.0][::-1]
for width in line_widths:
ax.plot(
[line_x_start, line_x_start + line_length],
[line_y_start, line_y_start],
linewidth=width,
color=color,
antialiased=antialiased,
solid_capstyle=solid_capstyle,
)
line_y_start += 0.05
# # now repeat but vertical of height 1
# line_x_start = 3.125 - 0.05 # Offset from the left
# line_y_start = y_position + dpi / 960
# for width in line_widths:
# ax.plot(
# [line_x_start, line_x_start],
# [line_y_start, line_y_start - 0.5],
# linewidth=width,
# color=color,
# antialiased=True,
# )
# ax.plot(
# [0.5 + line_x_start, 0.5 + line_x_start],
# [line_y_start, line_y_start - 0.5],
# linewidth=width,
# color=color,
# antialiased=True,
# )
# line_x_start += 0.05
# Save the image
# Remove axes
ax.axis("off")
# ax.set_aspect("equal")
# plt.tight_layout(pad=0)
ax.set_ylim([0, fig_height])
# ax.set_xlim([0, fig_width])
ax.set_xlim([0, fig_width])
# pad = 0.108
pad = 0
fig.subplots_adjust(left=0, right=1, top=1, bottom=0)
output_paths = []
for _dpi in ppi:
_out_path = f"/tmp/color_calibration-{_dpi}_{index:02d}.png"
plt.savefig(_out_path, pad_inches=pad, dpi=_dpi)
output_paths.append(_out_path)
# plt.show()
plt.close()
return output_paths
if __name__ == "__main__":
import argparse
import os
parser = argparse.ArgumentParser()
parser.add_argument(
"--rows", type=int, default=73, help="Number of entries per column"
)
parser.add_argument(
"--dir", type=str, default="/Volumes/TMP/tests", help="Directory to save images"
)
parser.add_argument(
"-k",
"--kind",
type=str,
nargs="+",
default=["hsv", "lex", "lab", "umap"],
help="Kinds of sorting",
)
parser.add_argument(
"--ppi", action="append", type=int, default=[300], help="Pixels per inch"
)
parser.add_argument("--aliased", action="store_true", help="Disable antialiasing")
parser.add_argument(
"--capstyle", type=str, default="butt", help="Capstyle of lines"
)
args = parser.parse_args()
COLUMN_LENGTH = args.rows
KINDS = args.kind
PPIS = args.ppi
DIR = args.dir
ANTIALIASED = not args.aliased
CAPSTYLE = args.capstyle
# COLUMN_LENGTH = 73 # results in 13 unfiltered columns (perfect)
# COLUMN_LENGTH = (
# 106 # results in 9 unfiltered columns (last one short), square-ish image
# )
OMITTED_COLORS = [
# "black",
# "white",
# "poop",
# "poo brown",
# "shit",
# "shit brown",
]
# OMITTED_COLORS = list(map(lambda s: f"xkcd:{s}", OMITTED_COLORS))
# KIND = "hsv" # choose from umap, hsv
for KIND in KINDS:
colors = list(mcolors.XKCD_COLORS.keys())
sorted_indices = np.load(f"scripts/{KIND}_sorted_indices.npy")
sorted_colors = [colors[idx] for idx in sorted_indices]
colors = sorted_colors
colors = [c for c in colors if c not in OMITTED_COLORS]
print(f"Total number of colors: {len(colors)}")
chunks = [
colors[i : i + COLUMN_LENGTH] for i in range(0, len(colors), COLUMN_LENGTH)
]
for idx, color_part in enumerate(chunks):
image_path = create_color_calibration_image(
colors=color_part,
ppi=PPIS,
index=idx,
antialiased=ANTIALIASED,
solid_capstyle=CAPSTYLE,
)
os.system(f"identify {image_path[0]}")
for PPI in PPIS:
# use imagemagick to stitch together the images horizontally
os.system(
f"convert +append /tmp/color_calibration-{PPI}_*.png /tmp/color_calibration-{PPI}.png"
)
os.system(f"rm /tmp/color_calibration-{PPI}_*")
print(f"Final image saved to /tmp/color_calibration-{PPI}.png")
os.system(
f"mkdir -p {DIR} && cp /tmp/color_calibration-{PPI}.png {DIR}/xkcd_{COLUMN_LENGTH}_{KIND}_{PPI}.png"
)
print(f"Copied to {DIR}/xkcd_{COLUMN_LENGTH}_{KIND}_{PPI}.png")

View File

@ -1,7 +1,7 @@
lint:
black .
isort --profile=black *.py
flake8 --ignore E501,W503 *.py
flake8 --ignore E501,W503,E203 *.py
test:
# python main.py --alpha 1 --lr 1e-2 --max_epochs 200 --bs 256 --seed 856 --width 2048
@ -42,12 +42,27 @@ search: lint
hsv:
python hsv.py
# TODO: replace this with what we used in day-in-the-life
animate:
ffmpeg -i lightning_logs/version_258/e%04d.png \
-c:v libx264 \
-vf "fps=12,format=yuv420p,pad=ceil(iw/2)*2:ceil(ih/2)*2" \
~/animated.mp4
umap:
for seed in `seq 0 100`; do \
python scripts/sortcolor.py -s umap --seed $$seed --dpi 300 ; \
done
sort_lex:
python scripts/sortcolor.py -s lex --dpi 300
sort_hsv:
python scripts/sortcolor.py -s hsv --dpi 300
clean:
rm -rf lightning_logs
rm -rf .lr_find_*.ckpt

Binary file not shown.

63
scripts/place.sh Normal file
View File

@ -0,0 +1,63 @@
#!/bin/bash
# Constants
# SEED=20230920
SEED=0
# INPUT_FILE="${DIR}/arrangement_$SEED.txt"
DIR=/teamspace/studios/this_studio/out_sortcolors
INPUT_FILE="${DIR}/arrangement_grid.txt"
# OUTPUT_IMAGE="${DIR}/circle_composite_$SEED.png"
TYPE=circle
OUTPUT_IMAGE="${DIR}/${TYPE}_composite_grid.png"
DPI=100
CANVAS_SIZE=$((72*$DPI)) # 72 inches
CIRCLE_IMAGE="${DIR}/hsv_sorted_colors_circle.png"
identify $CIRCLE_IMAGE
# PREP
echo "Building ops"
# Build the composite operations string
composite_ops=""
idx=1
while IFS=, read -r x y; do
# Translate so that (0,0) becomes the center of the canvas
fx=$(echo "$x*$DPI + $CANVAS_SIZE/2" | bc -l)
fy=$(echo "$CANVAS_SIZE/2 - $y*$DPI" | bc -l)
# Convert the final float values to integers
ix=$(printf "%.0f" "$fx")
iy=$(printf "%.0f" "$fy")
if [[ idx -eq 42 ]]; then
CIRCLE_IMAGE="${DIR}/hsv_sorted_colors_${TYPE}.png"
else
idx_str=$(printf "%02d" "$idx")
CIRCLE_IMAGE="${DIR}/${idx_str}umap_sorted_colors_${TYPE}.png"
# CIRCLE_IMAGE="${DIR}/hsv_sorted_colors_${TYPE}.png"
fi
# Add to the composite operations string
composite_ops="$composite_ops \( $CIRCLE_IMAGE \) -compose Over -geometry +$ix+$iy -composite"
idx=$((idx+1))
done < $INPUT_FILE
# COMPOSITE
echo "Compositing"
# Use convert with the built composite operations string
eval "convert -units PixelsPerInch \
-size ${CANVAS_SIZE}x${CANVAS_SIZE} xc:white \
$composite_ops \
-density $DPI \
$OUTPUT_IMAGE"
echo "Saved $OUTPUT_IMAGE"
# DEBUG
# eval "convert -units PixelsPerInch \
# $OUTPUT_IMAGE \
# \( ${DIR}/arrangement_$SEED.png -evaluate Multiply 0.5 \) \
# -gravity center -composite \
# -density $DPI \
# /tmp/debug.png"
# open /tmp/debug.png

148
scripts/scatter.py Normal file
View File

@ -0,0 +1,148 @@
import matplotlib.patches as patches
import matplotlib.pyplot as plt
import numpy as np
from pathlib import Path
def get_quadrant(x, y):
"""Return the quadrant of a given point."""
if x >= 0 and y >= 0:
return 1
elif x < 0 and y >= 0:
return 2
elif x < 0 and y < 0:
return 3
else:
return 4
def is_overlapping(x, y, existing_points, radius):
"""Check if a point overlaps with existing points, crosses a quadrant or goes beyond the quadrant lines."""
current_quadrant = get_quadrant(x, y)
buffer = 0.25
# Check if circle touches/crosses the x=0 or y=0 lines.
if (
abs(x) + radius > buffer
and abs(x) - radius < buffer
or abs(y) + radius > buffer
and abs(y) - radius < buffer
):
return True
for ex, ey in existing_points:
# Check overlap with existing points
if np.sqrt((x - ex) ** 2 + (y - ey) ** 2) <= 2 * radius + 0.5:
return True
# Check if the circle touches another quadrant
if (
get_quadrant(ex, ey) != current_quadrant
and np.sqrt((x - ex) ** 2 + (y - ey) ** 2) <= radius
):
return True
return False
DIR = "/teamspace/studios/this_studio/out_sortcolors"
N = 100
DPI = 300
SIZE = 72 # canvas size?
radius = 3
variance = 72 # Adjust variance as needed
Path(DIR).mkdir(exist_ok=True, parents=True)
with open(f"{DIR}/arrangement_grid.txt", "w") as f:
radius = 3
half = SIZE / 2 - 4
# make points an equispaced grid of 10 x 10 ranging from [-35, 35]
interval = (half - (-half)) / 9 # 10 points, so 9 intervals
# Generate the grid points
points = [
(x, y)
for x in np.arange(-half, half + 0.1, interval)
for y in np.arange(-half, half + 0.1, interval)
]
for x, y in points:
f.write(f"{x-radius}, {y+radius}\n")
# f.write(f"{x}, {y}\n")
print("wrote grid")
for seed in range(11, 22):
try:
np.random.seed(seed)
# To store plotted points
points = []
max_iterations = int(1e7)
iterations = 0
# Generate points
for k in range(N):
while True:
# x, y = np.random.normal(0, variance), np.random.normal(0, variance)
random_angle = np.random.uniform(0, 2 * np.pi)
# random_radius = np.random.uniform(0.25+radius, SIZE/2 - radius - 0.25)
random_radius = abs(np.random.normal(0, variance))
x = random_radius * np.cos(random_angle)
y = random_radius * np.sin(random_angle)
iterations += 1
if not is_overlapping(x, y, points, radius):
if max(abs(x), abs(y)) + radius < SIZE / 2 - 0.25:
points.append((x, y))
break
if iterations > max_iterations:
raise ValueError(f"Too many iterations: {k} points")
print(f"{k}: ({x}, {y}) @ {iterations:09d}")
# Create plot with circles
fig, ax = plt.subplots(1, 1, figsize=(SIZE, SIZE))
for x, y in points:
circle = patches.Circle((x, y), radius, color="black")
ax.add_patch(circle)
# Draw the standard quadrants
ax.axhline(0, color="black", linewidth=0.5)
ax.axvline(0, color="black", linewidth=0.5)
# Use square axis and set limits
lim_x = (
max(
abs(max(points, key=lambda t: t[0])[0]),
abs(min(points, key=lambda t: t[0])[0]),
)
+ radius
)
lim_y = (
max(
abs(max(points, key=lambda t: t[1])[1]),
abs(min(points, key=lambda t: t[1])[1]),
)
+ radius
)
lim_x = lim_y = SIZE / 2
ax.axis("off")
ax.set_aspect("equal")
ax.set_xlim(-lim_x, lim_x)
ax.set_ylim(-lim_y, lim_y)
# Save and show
fig.tight_layout(pad=0)
plt.savefig(
f"{DIR}/arrangement_{seed}.png", dpi=DPI, bbox_inches="tight", pad_inches=0
)
# plt.show()
# also save x/y coords as text file
with open(f"{DIR}/arrangement_{seed}.txt", "w") as f:
for x, y in points:
f.write(f"{x-radius}, {y+radius}\n")
# f.write(f"{x}, {y}\n")
except ValueError as e:
print(f"{seed}: {e}")
except AssertionError as e:
print(f"{seed}: {e}")

372
scripts/sortcolor.py Normal file
View File

@ -0,0 +1,372 @@
import argparse
from pathlib import Path
import matplotlib.colors as mcolors
import matplotlib.patches as patches
import matplotlib.pyplot as plt
import numpy as np
from hilbertcurve.hilbertcurve import HilbertCurve
# Extract XKCD colors
colors = list(mcolors.XKCD_COLORS.keys())
rgb_values = [mcolors.to_rgb(mcolors.XKCD_COLORS[color]) for color in colors]
# Parse command-line arguments
parser = argparse.ArgumentParser()
parser.add_argument("-s", "--sort-by", type=str, default="hsv", help="kind of sorting")
parser.add_argument("--seed", type=int, default=21, help="seed for UMAP")
parser.add_argument("--dpi", type=int, default=100, help="dpi for saving")
parser.add_argument("--size", type=float, default=6.0, help="size of figure")
parser.add_argument(
"--fontsize",
type=float,
default=0,
help="fontsize of annotation (default: 0 = None)",
)
parser.add_argument(
"--radius", type=float, default=1 / 3, help="inner radius of circle"
)
args = parser.parse_args()
KIND = args.sort_by
SEED = args.seed
DPI = args.dpi
SIZE = args.size
FONTSIZE = args.fontsize
INNER_RADIUS = args.radius
DIR = "/teamspace/studios/this_studio/out_sortcolors"
Path(DIR).mkdir(exist_ok=True, parents=True)
def peano_curve(n):
"""
Generate Peano curve coordinates for a given order `n`.
"""
if n == 0:
return [(0, 0)]
prev_curve = peano_curve(n - 1)
max_coord = 3 ** (n - 1)
# Define the transformations for the Peano curve's 9 segments
transforms = [
lambda x, y: (x, y), # Bottom-left square
lambda x, y: (x + max_coord, y), # Bottom-middle square
lambda x, y: (x + 2 * max_coord, y), # Bottom-right square
lambda x, y: (x + 2 * max_coord, y + max_coord), # Middle-right square
lambda x, y: (
x + max_coord,
y + max_coord,
), # Center square (traversed in reverse)
lambda x, y: (x, y + max_coord), # Middle-left square
lambda x, y: (x, y + 2 * max_coord), # Top-left square
lambda x, y: (x + max_coord, y + 2 * max_coord), # Top-middle square
lambda x, y: (x + 2 * max_coord, y + 2 * max_coord), # Top-right square
]
curve = []
for transform in transforms:
segment = [transform(x, y) for x, y in prev_curve]
# Reverse the traversal for the center square
if transform == transforms[4]:
segment = segment[::-1]
curve += segment
return curve
if KIND in ("lex", "alpha", "abc"):
preds = np.array(colors)
elif KIND == "umap":
import umap
# Use UMAP to create a 1D representation
reducer = umap.UMAP(
n_components=1,
n_neighbors=250,
min_dist=0.005,
metric="cosine",
random_state=SEED,
)
embedding = reducer.fit_transform(np.array(rgb_values))
# Sort colors by the 1D representation
preds = embedding[:, 0]
elif KIND in ("cielab", "lab", "ciede2000"):
from skimage.color import deltaE_ciede2000, rgb2lab
# CIELAB
# Convert RGB values to CIELAB
lab_values = rgb2lab([rgb_values])
# Reference color for sorting (can be the first color or any other reference point)
reference_color = lab_values[0]
# Compute CIEDE2000 distances of all colors to the reference color
distances = [deltaE_ciede2000(reference_color, color) for color in lab_values]
# Sort colors by their CIEDE2000 distance to the reference color
# preds = distances).flatten() # awful
lab_values_flat = lab_values.reshape(-1, 3)
# Sort colors based on the L* value in the CIELAB space
# 0 corresponds to the L* channel
preds = lab_values_flat[:, 0]
elif KIND == "hsv":
from matplotlib.colors import rgb_to_hsv
# Convert RGB values to HSV
hsv_values = np.array([rgb_to_hsv(np.array(rgb)) for rgb in rgb_values])
# Sort colors based on the hue value
# 0 corresponds to the hue component
preds = hsv_values[:, 0]
else:
raise ValueError(f"Unknown kind: {KIND}")
sorted_indices = np.argsort(preds)
# Save the sorted indices to disk
if (KIND == "umap" and SEED == 21) or (KIND != "umap"):
file_path = f"scripts/{KIND}_sorted_indices.npy"
np.save(file_path, sorted_indices)
print(f"Sorted indices saved to {file_path}")
# Sort colors by the 1D representation
sorted_colors = [colors[i] for i in sorted_indices]
# # Display the sorted colors around the ring of a circle
# # Create a new figure for the circle visualization
# fig, ax = plt.subplots(figsize=(SIZE, SIZE))
# # Circle parameters
# center = (0, 0)
# radius = 1.0
# # Angle increment (in radians) based on the number of colors
# angle_increment = 2 * np.pi / len(sorted_colors)
# # roll the colors so that the first color is white
# reordered_sorted_colors = sorted_colors.copy()
# white_index = reordered_sorted_colors.index("xkcd:white")
# reordered_sorted_colors = reordered_sorted_colors[white_index:] + reordered_sorted_colors[:white_index]
# # Plot each color around the circle
# for i, color in enumerate(reordered_sorted_colors):
# # Compute start and end angles for the segment
# start_angle = i * angle_increment
# end_angle = (i + 1) * angle_increment
# # Create a wedge (segment of the circle) for each color
# wedge = patches.Wedge(
# center, radius, 90 + np.degrees(start_angle), 90 + np.degrees(end_angle), fc=color
# )
# ax.add_patch(wedge)
# # Overlay a white circle in the center
# inner_circle = patches.Circle(center, INNER_RADIUS, color="white")
# ax.add_patch(inner_circle)
# if INNER_RADIUS > 0.0:
# fcolor = "black"
# else:
# fcolor = "white"
# if FONTSIZE > 0.0:
# ax.annotate(f"{KIND.upper()}", center, ha="center", va="center", size=FONTSIZE, color=fcolor)
# # Set equal scaling and remove axis
# ax.set_aspect("equal")
# ax.axis("off")
# ax.set_ylim(-radius, radius)
# ax.set_xlim(-radius, radius)
# # Save and display the circle visualization
# prefix = ""
# if KIND == "umap":
# prefix = f"{SEED:02d}"
# fname = f"{DIR}/{prefix}{KIND}_sorted_colors_circle.png"
# fig.tight_layout(pad=0)
# fig.savefig(
# fname,
# dpi=DPI,
# transparent=True,
# pad_inches=0,
# bbox_inches="tight",
# )
# print(f"Saved {fname}")
# # plt.show()
def plot_preds(
preds,
rgb_values,
fname: str,
roll: bool = False,
dpi: int = 150,
inner_radius: float = 1 / 3,
figsize=(3, 3),
):
sorted_inds = np.argsort(preds.ravel())
colors = rgb_values[sorted_inds, :3]
if roll:
# find white in colors, put it first.
white = np.array([1, 1, 1])
white_idx = np.where((colors == white).all(axis=1))
if white_idx:
white_idx = white_idx[0][0]
colors = np.roll(colors, -white_idx, axis=0)
else:
print("no white, skipping")
# print(white_idx, colors[:2])
N = len(colors)
# Create a plot with these hues in a circle
fig, ax = plt.subplots(figsize=figsize, subplot_kw=dict(polar=True))
# Each wedge in the circle
theta = np.linspace(0, 2 * np.pi, N, endpoint=False) + np.pi / 2
width = 2 * np.pi / (N) # equal size for each wedge
for i in range(N):
ax.bar(
# 2 * np.pi * preds[i],
theta[i],
height=1,
width=width,
edgecolor=colors[i],
linewidth=0.25,
# facecolor=[rgb_values[i][1]]*3,
# facecolor=rgb_values[i],
facecolor=colors[i],
bottom=0.0,
zorder=1,
alpha=1,
align="edge",
)
ax.set_xticks([])
ax.set_yticks([])
ax.set_aspect("equal")
ax.axis("off")
radius = 1
ax.set_ylim(0, radius)
# Overlay white circle
circle = patches.Circle(
(0, 0), inner_radius, transform=ax.transData._b, color="white", zorder=2
)
ax.add_patch(circle)
if FONTSIZE > 0.0:
center = (0, 0)
fcolor = "black"
ax.annotate(
f"{KIND.upper()}",
center,
ha="center",
va="center",
size=FONTSIZE,
color=fcolor,
)
fig.tight_layout(pad=0)
plt.savefig(fname, dpi=dpi, transparent=True, pad_inches=0, bbox_inches="tight")
plt.close()
prefix = ""
if KIND == "umap":
prefix = f"{SEED:02d}"
fname = f"{DIR}/{prefix}{KIND}_sorted_colors_circle.png"
plot_preds(
preds,
np.array(rgb_values),
fname,
roll=True,
dpi=DPI,
inner_radius=INNER_RADIUS,
figsize=(SIZE, SIZE),
)
HILBERT = False
if HILBERT:
# Create Hilbert curve
# We'll set the order such that the number of positions is greater than or equal to the number of colors
hilbert_order = int(np.ceil(0.5 * np.log2(len(sorted_colors))))
hilbert_curve = HilbertCurve(hilbert_order, 2)
# Create an image for visualization
image_size = 2**hilbert_order
image = np.ones((image_size, image_size, 3))
for i, color in enumerate(sorted_colors):
# Convert linear index to Hilbert coordinates
coords = hilbert_curve.point_from_distance(i)
image[coords[1], coords[0]] = mcolors.to_rgb(color)
# annotation in upper right
# Display the image
fig, ax = plt.subplots(1, 1, figsize=(SIZE, SIZE))
ax.imshow(image)
ax.annotate(
f"{KIND.upper()}",
(1.0, 1.0),
ha="right",
va="top",
size=FONTSIZE,
xycoords="axes fraction",
)
ax.axis("off")
ax.set_aspect("equal")
fig.tight_layout()
fname = f"{DIR}/{prefix}{KIND}_sorted_colors_hilbert.png"
fig.savefig(
fname,
dpi=DPI,
transparent=True,
# bbox_inches="tight",
# pad_inches=0
)
print(f"Saved {fname}")
# plt.show()
# # Create peano curve
# order = 0
# while (3**order)**2 < len(sorted_colors):
# order += 1
# # Generate peano curve coordinates for the determined order
# curve_coords = peano_curve(order)
# unique_coords = set(curve_coords)
# print(f"Total coordinates: {len(curve_coords)}")
# print(f"Unique coordinates: {len(unique_coords)}")
# # If there are more points on the curve than colors, truncate the curve
# if len(curve_coords) > len(sorted_colors):
# print(f"Will be missing {len(curve_coords) - len(sorted_colors)} pixels, percentage: {100 * (len(curve_coords) - len(sorted_colors)) / len(curve_coords)}")
# curve_coords = curve_coords[:len(sorted_colors)]
# if len(curve_coords) < len(sorted_colors):
# raise ValueError("Not enough curve coordinates for the number of colors")
# # Create an image for visualization
# image_size = (3**order)
# image = np.ones((image_size, image_size, 3))
# for i, color in enumerate(sorted_colors):
# # Get the Moore curve coordinates for the current index
# coords = curve_coords[i]
# image[coords[1], coords[0]] = mcolors.to_rgb(color)
# # Display the image
# plt.figure(figsize=(8, 8))
# plt.imshow(image)
# plt.axis("off")
# plt.savefig(f"{DIR}/{KIND}_sorted_colors_moore.png", dpi=DPI)
# plt.show()