You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

243 lines
8.2 KiB

from typing import List
import matplotlib.colors as mcolors
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.font_manager import FontProperties
pt_mono_font = FontProperties(
fname="/usr/share/fonts/truetype/PT_Mono/PTMono-Regular.ttf"
)
# 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,
fontproperties=pt_mono_font,
# 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,
fontproperties=pt_mono_font,
# 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,
# fontproperties=pt_mono_font,
# )
# 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="~/color/out", 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")