Using Python 3.7 and the colormath module, I had some fun trying to find the complete opposite of one color (ex. The opposite color of black [0, 0, 0] is yellow [255, 255, 0] with a DE of 101.20397657762743) or trying to find the two colors with the most color difference (i.e. "Navy" [0, 0, 110] and "Chartreuse" [143, 255, 0] with a DE of 119.4740815993416, from my potentially inaccurate testing).
Unfortunately, the only way I have found to find the opposite of a given color is just to bruteforce with a little bit of optimization by comparing the given color with (almost) every single sRGB color combo (or, [0, 0, 0] to [255, 255, 255]).
Python 3.7 code:
from colormath.color_objects import sRGBColor as RGBC, LabColor as LabC
from colormath.color_conversions import convert_color as cv_c
from colormath.color_diff import delta_e_cie2000 as de2k
from time import perf_counter as clock
# Example: X11's "Midnight Blue", edit as 'RGBC(r, g, b,...'
reference = RGBC(25, 25, 112, is_upscaled=1)
lab_ref = cv_c(reference, LabC)
# Set max delta
max_delta = 0
# Set estimate calculation's range inverals as factors of 255 (1, 3, 5, 15, 17, 51, 85, 255)
resolution = 17
# Estimate the opposite color
for sample_r in range(0, 256, resolution):
for sample_g in range(0, 256, resolution):
for sample_b in range(0, 256, resolution):
# Convert the sample to Lab
sample = RGBC(sample_r, sample_g, sample_b, is_upscaled=1)
lab_samp = cv_c(sample, LabC)
# Find the color difference
delta = de2k(lab_ref, lab_samp)
# Compare current delta with previous highest delta
if delta > max_delta:
# Overwrite highest delta with current delta
max_delta = delta
# Save estimate for optimization
estimate = sample.get_upscaled_value_tuple()
# Keep any 255 values from estimate, leads to 'range(255, 256)'
low_r = 0 if estimate[0] != 255 else 255
low_g = 0 if estimate[1] != 255 else 255
low_b = 0 if estimate[2] != 255 else 255
# Keep any 0 values from estimate, leads to 'range(0, 1)'
high_r = 256 if estimate[0] != 0 else 1
high_g = 256 if estimate[1] != 0 else 1
high_b = 256 if estimate[2] != 0 else 1
# Reset max delta
max_delta = 0
# Find a better opposite color from estimate
for sample_r in range(low_r, high_r):
for sample_g in range(low_g, high_g):
for sample_b in range(low_b, high_b):
# Convert the sample color to Lab
sample = RGBC(sample_r, sample_g, sample_b, is_upscaled=1)
lab_samp = cv_c(sample, LabC)
# Find the color difference
delta = de2k(lab_ref, lab_samp)
# Compare current delta with previous highest delta
if delta > max_delta:
# Overwrite highest delta with current delta
max_delta = delta
# Overwrite best opposite color with current sample
opposite = sample
# Print the reference, opposite color, and delta E
print(f'{reference.get_upscaled_value_tuple()} with {opposite.get_upscaled_value_tuple()}, {max_delta}')
# Print program time
print(clock())
# Example:
# Returns '(25, 25, 112) with (166, 255, 0), 108.95350620860522
# 2.580066949'
The above code will run for 2.5 seconds to about 19 minutes (assumes second opposite calculation takes 128^3 jumps, returns same value) to about 2 hours 15 minutes (assumes worst possible case is 254^3 jumps, as in no optimization occurs) on my system (3.00 GHz)
CIEDE2000's equations can be seen at this site. Notice the notes at the bottom of the page.
I want to know if there is a more efficient algorithm that can find the complete opposite of a given RGB color. I want to be able to use said algorithm to create a spreadsheet that contains every RGB color and its opposite color before CIEDE202X or CIEDE20XX comes out.
Note: this is my first question on StackOverflow
Edit 1: Created better estimate optimization. Instead of just keeping 0 or 255 if the estimate had one, I would reduce the limits so that they were only at a maximum of +-32 from the estimate. Runtime reduced to at most 4 seconds on 3.00 GHz.
# Red channel optimization
if estimate[0] == 0 or estimate[0] == 255:
low_r = estimate[0]
high_r = estimate[0] + 1
elif estimate[0] > 32 and estimate[0] < 224:
low_r = estimate[0] - 32
high_r = estimate[0] + 32
elif estimate[0] < 33:
low_r = 0
high_r = estimate[0] + 32
else:
low_r = estimate[0] - 32
high_r = 256
# Green channel optimization
if estimate[1] == 0 or estimate[1] == 255:
low_g = estimate[1]
high_g = estimate[1] + 1
elif estimate[1] > 32 and estimate[1] < 224:
low_g = estimate[1] - 32
high_g = estimate[1] + 32
elif estimate[1] < 33:
low_g = 0
high_g = estimate[1] + 32
else:
low_g = estimate[1] - 32
high_g = 256
# Blue channel optimization
if estimate[2] == 0 or estimate[2] == 255:
low_b = estimate[2]
high_b = estimate[2] + 1
elif estimate[2] > 32 and estimate[2] < 224:
low_b = estimate[2] - 32
high_b = estimate[2] + 32
elif estimate[2] < 33:
low_b = 0
high_b = estimate[2] + 32
else:
low_b = estimate[2] - 32
high_b = 256
CIE DeltaE 2000 is not appropriate for large colour differences so large differences in the range [10, 20]+ simply cannot be evaluated with this quasi-metric and you should probably look at something else such as HyAB colour difference metric: https://onlinelibrary.wiley.com/doi/abs/10.1002/col.22451 or alike.