copy over color files, use new plotting
This commit is contained in:
parent
0ae8414481
commit
9953327d30
14
check.py
14
check.py
@ -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
235
color_poster.py
Normal 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")
|
18
makefile
18
makefile
@ -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,28 @@ 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 100 --size 6 ; \
|
||||
done
|
||||
|
||||
|
||||
lex:
|
||||
python scripts/sortcolor.py -s lex --dpi 300 --size 6 --radius 0.5
|
||||
|
||||
arrange:
|
||||
python scripts/sortcolor.py -s hsv --dpi 100 --size 3 && \
|
||||
sh scripts/place.sh
|
||||
|
||||
|
||||
clean:
|
||||
rm -rf lightning_logs
|
||||
rm -rf .lr_find_*.ckpt
|
||||
|
BIN
scripts/lex_sorted_indices.npy
Normal file
BIN
scripts/lex_sorted_indices.npy
Normal file
Binary file not shown.
63
scripts/place.sh
Normal file
63
scripts/place.sh
Normal 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
148
scripts/scatter.py
Normal 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}")
|
371
scripts/sortcolor.py
Normal file
371
scripts/sortcolor.py
Normal file
@ -0,0 +1,371 @@
|
||||
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(-radius, radius)
|
||||
ax.set_xlim(-radius, 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:
|
||||
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=False, 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()
|
Loading…
Reference in New Issue
Block a user