Finding the closest color using hex-codes - python

A few days ago, I created a small color comparing program here.
Now I'm trying to make it nicer and better, but I can't get it to return anything. It should take in a hex code and compare it to my excising list of hex codes to find the closest value. I'm new to Python, I can't see what I'm doing wrong, it's not crashing eighter.
Currently just to get the algorithm working, the input_hex is just hardcoded.
import math
def compare(input_hex: str, colors: str) -> str:
colors = [
'#FFFCF2',
'#08112C',
'#646206',
'#82130D',
'#674246',
'#ED5A06',
'#E28819',
'#FAE300',
'#7FB72E',
'#005D44',
'#115D78',
'#005A98',
'#223D53',
'#421D10',
'#B2470E',
'#88564E',
'#800919',
'#D60075',
'#E59300',
]
input_hex = '#900919'
color_distance_list = []
input_color = tuple(int(input_hex.lstrip('#')[i:i+2], 16) for i in (0, 2, 4))
for i in range (len(colors)):
use_color = colors[i]
my_color = tuple(int(use_color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4))
get_distance = math.sqrt(sum([(a - b) ** 2 for a, b in zip(my_color, input_color)]))
colors.append(get_distance)
sorted_color_distance_list = min(color_distance_list)
closest_hex = color_distance_list.index(sorted_color_distance_list)
return print("closest color hex is: ", closest_hex)

Got it working.
import math
def compare(input_hex: str) -> str:
colors = [
'#FFFCF2',
'#08112C',
'#646206',
'#82130D',
'#674246',
'#ED5A06',
'#E28819',
'#FAE300',
'#7FB72E',
'#005D44',
'#115D78',
'#005A98',
'#223D53',
'#421D10',
'#B2470E',
'#88564E',
'#800919',
'#D60075',
'#E59300',
]
color_distance_list = []
input_color = tuple(int(input_hex.lstrip('#')[i:i+2], 16) for i in (0, 2, 4))
for i in range (len(colors)):
use_color = colors[i]
my_color = tuple(int(use_color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4))
get_distance = math.sqrt(sum([(a - b) ** 2 for a, b in zip(my_color, input_color)]))
color_distance_list.append(get_distance)
sorted_color_distance_list = min(color_distance_list)
closest_hex = color_distance_list.index(sorted_color_distance_list)
return print("closest color hex is: ", colors[closest_hex])
compare('#905919')

Related

Finding the midpoint of tuple of any length in python

I need to take a tuple of any length and preforming an operation to return the midpoint. However, I need to function to work with a tuple of any length so I'm not sure how to go about it.
def findMidpoint(P: tuple, Q: tuple) -> tuple:
user_input1 = input('Enter space-separated integers: ')
P = tuple(int(item) for item in user_input1.split())
user_input2 = input('Enter space-separated integers: ')
Q = tuple(int(item) for item in user_input2.split())
Midpoint
pass
def main():
# use to test the findMidpoint function
pass
if __name__ == "__main__":
main()
Okay, taking some liberties here with what you're asking, but assuming what you want is to find the midpoint of any two points in an N-dimensional space, you can average the value of each point axis-wise. For example:
P = (px, py)
Q = (qx, qy)
midpoint = ( (px + qx)*0.5, (py + qy)*0.5 )
Obviously, for more dimensions you have to extend this. A general N-dimensional solution, with your code, can make use of zip:
def findMidpoint(P: tuple, Q: tuple) -> tuple:
return tuple((q + p) / 2 for p, q in zip(P, Q))
def main():
# use to test the findMidpoint function
assert findMidpoint(P=(0, 0), Q=(2, 2)) == (1, 1)
assert findMidpoint(P=(0, 0, 0), Q=(2, 2, 2)) == (1, 1, 1)
assert findMidpoint(P=(-2, -2, -2), Q=(2, 2, 2)) == (0, 0, 0)
if __name__ == "__main__":
main()
This assumes P and Q are the same length. If they're not, you could go one further and use zip_longest:
from itertools import zip_longest
def findMidpoint(P: tuple, Q: tuple) -> tuple:
return tuple((q + p) / 2 for p, q in zip_longest(P, Q, fillvalue=0))
def main():
# use to test the findMidpoint function
assert findMidpoint(P=(0, 0), Q=(2, 2, 2)) == (1, 1, 1)
if __name__ == "__main__":
main()
This would essentially say "if no coordinate is given for an axis, assume it is zero".

Is there a simpler approach to store tic-tac-toe state as an integer using binary literals?

The following code allows me to persist the complete tic-tac-toe state as a single integer by converting the numpy array to a binary string. Each cell can be 0, 1 or 2 and therefore keeps 2 bits to represent them and then I concatenate them somehow.
import numpy as np
def convert_to_int(state):
binary = "0b"
for x in range(0, state.shape[0]):
for y in range(0, state.shape[1]):
binary += format(state[x, y].item(), '02b')
return int(binary, 0)
def convert_to_numpy(state):
cells = []
binary_string = "{0:b}".format(state).zfill(18)
for i in range(0, len(binary_string), 2):
cells.append(int(binary_string[i: i + 2], 2))
return np.array(cells).reshape((3, 3))
input_state = np.array((
(1, 2, 1),
(0, 0, 0),
(0, 0, 0)),
dtype="int32")
state_as_int = convert_to_int(input_state)
output_state = convert_to_numpy(state_as_int)
print(state_as_int)
print(output_state)
102400
[[1 2 1]
[0 0 0]
[0 0 0]]
How can I simplify the code. Is there a way to use only binary literals and bitwise operators without using string conversion?
Thank you for asking this question. I have been ignoring numpy, and this problem is an excuse to learn.
This answer uses shifting to convert to integer and vice versa.
import numpy as np
def convert_to_int(state):
a = 0
for num in state.reshape(9):
a = a + num
a = a << 2
return a
def convert_state_to_numpy(state):
cells = []
for i in range(9) :
state >>= 2
cells.insert(0,(3 & state))
return np.array(cells).reshape((3,3))
input_state = np.array((
(1, 2, 1),
(0, 0, 0),
(0, 0, 0)),
dtype="int32")
state_as_int = convert_to_int(input_state)
output_state = convert_state_to_numpy(state_as_int)
print(hex(state_as_int))
print(output_state)
This was a fun problem. Here's what I came up with:
import numpy as np
def convert_to_int(state):
binary = 0b0
for x in range(3):
for y in range(3):
binary <<= 2
binary += state[x, y].item()
return binary
def convert_to_numpy(state):
return np.array([(state & (2**(2*i + 1) + 2**(2*i))) >> 2*i for i in range(8, -1, -1)]).reshape(3, 3)
This should avoid the string conversion, and is hopefully a tad bit faster (though I didn't do any benchmarks).
I wonder if there's away to store numbers of any base in python, since you could store this as a 9 bit number in base 3...
Instead of each cell as a 32bit integer you could use two integers and store the positions as bit flags. Say for example:
Os = 0b000100000
Xs = 0b100000001
X _ _
_ _ O
_ _ X
I was intrigued by a previous comment about using base 3 numbers. Since your original question was looking for a way to compress a 3x3 array into a single integer, I will assume that you want to reduce the size of the data. The following code takes your array which may be viewed as a 9 digit base 3 number and converts it to 15 bit integer. This number is stored as an array of 2 bytes. This may be beyond the scope of your original question but, i think, it follows the spirit of the question.
import numpy as np
def encode_state(state):
a=0
for i,num in enumerate(state.reshape(9)) :
a = a + 3**i * int(num)
return a.to_bytes(2,'big')
def decode_state(byte):
encoded_value = int.from_bytes(byte,'big')
b = [0 for i in range(9)]
for i in range(9) :
num = (encoded_value // 3**i) % 3
b[i] = num
return np.array(b).reshape(3,3)
input_state = np.array((
(2, 2, 2),
(1, 2, 2),
(2, 1, 0)),
dtype="int32")
state_as_byte = encode_state(input_state)
output_state = decode_state(state_as_byte)
print (''.join('{:02x}'.format(x) for x in state_as_byte))
print(output_state)

Getting an Array/Vector from PARI/GP in Python using Ctypes

I have written a code to compare the solution of sympy and PARI/GP, how ever I am facing a problem to get an array/vector from PARI/GP.
When I try to return the vector res from PARI/GP function nfroots, I get a address like this (see the last line) -
[3, 4]
elements as long (only if of type t_INT):
3
4
<__main__.LP_LP_c_long object at 0x00000000056166C8>
how can I get the res as vector/array from nfroots so I can use that array like normal python vector/array?
The code is given below to download the libpari.dll file, click here-
from ctypes import *
from sympy.solvers import solve
from sympy import Symbol
pari = cdll.LoadLibrary("libpari.dll")
pari.stoi.restype = POINTER(c_long)
pari.cgetg.restype = POINTER(POINTER(c_long))
pari.gtopoly.restype = POINTER(c_long)
pari.nfroots.restype = POINTER(POINTER(c_long))
(t_VEC, t_COL, t_MAT) = (17, 18, 19) # incomplete
pari.pari_init(2 ** 19, 0)
def t_vec(numbers):
l = len(numbers) + 1
p1 = pari.cgetg(c_long(l), c_long(t_VEC))
for i in range(1, l):
#Changed c_long to c_float, but got no output
p1[i] = pari.stoi(c_long(numbers[i - 1]))
return p1
def Quartic_Comparison():
x = Symbol('x')
#a=0;A=0;B=1;C=-7;D=13/12 #PROBLEM 1
a=0;A=0;B=1;C=-7;D=12
#a=0;A=0;B=-1;C=-2;D=1
solution=solve(a*x**4+A*x**3+B*x**2+ C*x + D, x)
print(solution)
V=(A,B,C,D)
P = pari.gtopoly(t_vec(V), c_long(-1))
res = pari.nfroots(None, P)
print("elements as long (only if of type t_INT): ")
for i in range(1, pari.glength(res) + 1):
print(pari.itos(res[i]))
return res #PROBLEM 2
f=Quartic_Comparison()
print(f)
res is an element from the PARI/C world. It is a PARI vector of PARI integers (t_VEC of t_INTs). Python does not know it.
If it is to be processed further on the Python side, it must be converted. This is generally necessary if data needs to be exchanged between Python and the PARI/C world.
So if you have a t_VEC with t_INTs on the PARI/C side, as in this case, you most likely want to convert it to a Python list.
One possible approach might look like this:
...
roots = pari.nfroots(None, P)
result = []
for i in range(1, pari.glength(roots) + 1):
result.append(pari.itos(roots[i]))
return result

Navigating a grid

I stumbled upon a problem at Project Euler, https://projecteuler.net/problem=15
. I solved this by combinatorics but was left wondering if there is a dynamic programming solution to this problem or these kinds of problems overall. And say some squares of the grid are taken off - is that possible to navigate? I am using Python. How should I do that? Any tips are appreciated. Thanks in advance.
You can do a simple backtrack and explore an implicit graph like this: (comments explain most of it)
def explore(r, c, n, memo):
"""
explore right and down from position (r,c)
report a rout once position (n,n) is reached
memo is a matrix which saves how many routes exists from each position to (n,n)
"""
if r == n and c == n:
# one path has been found
return 1
elif r > n or c > n:
# crossing the border, go back
return 0
if memo[r][c] is not None:
return memo[r][c]
a= explore(r+1, c, n, memo) #move down
b= explore(r, c+1, n, memo) #move right
# return total paths found from this (r,c) position
memo[r][c]= a + b
return a+b
if __name__ == '__main__':
n= 20
memo = [[None] * (n+1) for _ in range(n+1)]
paths = explore(0, 0, n, memo)
print(paths)
Most straight-forwardly with python's built-in memoization util functools.lru_cache. You can encode missing squares as a frozenset (hashable) of missing grid points (pairs):
from functools import lru_cache
#lru_cache(None)
def paths(m, n, missing=None):
missing = missing or frozenset()
if (m, n) in missing:
return 0
if (m, n) == (0, 0):
return 1
over = paths(m, n-1, missing=missing) if n else 0
down = paths(m-1, n, missing=missing) if m else 0
return over + down
>>> paths(2, 2)
6
# middle grid point missing: only two paths
>>> paths(2, 2, frozenset([(1, 1)]))
2
>>> paths(20, 20)
137846528820
There is also a mathematical solution (which is probably what you used):
def factorial(n):
result = 1
for i in range(1, n + 1):
result *= i
return result
def paths(w, h):
return factorial(w + h) / (factorial(w) * factorial(h))
This works because the number of paths is the same as the number of ways to choose to go right or down over w + h steps, where you go right w times, which is equal to w + h choose w, or (w + h)! / (w! * h!).
With missing grid squares, I think there is a combinatoric solution, but it's very slow if there are many missing squares, so dynamic programming would probably be better there.
For example, the following should work:
missing = [
[0, 1],
[0, 0],
[0, 0],
]
def paths_helper(x, y, path_grid, missing):
if path_grid[x][y] is not None:
return path_grid[x][y]
if missing[x][y]:
path_grid[x][y] = 0
return 0
elif x < 0 or y < 0:
return 0
else:
path_count = (paths_helper(x - 1, y, path_grid, missing) +
paths_helper(x, y - 1, path_grid, missing))
path_grid[x][y] = path_count
return path_count
def paths(missing):
arr = [[None] * w for _ in range(h)]
w = len(missing[0])
h = len(missing)
return paths_helper(w, h, arr, missing)
print paths()

Converting Hex to RGB value in Python

Working off Jeremy's response here: Converting hex color to RGB and vice-versa I was able to get a python program to convert preset colour hex codes (example #B4FBB8), however from an end-user perspective we can't ask people to edit code & run from there. How can one prompt the user to enter a hex value and then have it spit out a RGB value from there?
Here's the code I have thus far:
def hex_to_rgb(value):
value = value.lstrip('#')
lv = len(value)
return tuple(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))
def rgb_to_hex(rgb):
return '#%02x%02x%02x' % rgb
hex_to_rgb("#ffffff") # ==> (255, 255, 255)
hex_to_rgb("#ffffffffffff") # ==> (65535, 65535, 65535)
rgb_to_hex((255, 255, 255)) # ==> '#ffffff'
rgb_to_hex((65535, 65535, 65535)) # ==> '#ffffffffffff'
print('Please enter your colour hex')
hex == input("")
print('Calculating...')
print(hex_to_rgb(hex()))
Using the line print(hex_to_rgb('#B4FBB8')) I'm able to get it to spit out the correct RGB value which is (180, 251, 184)
It's probably super simple - I'm still pretty rough with Python.
I believe that this does what you are looking for:
h = input('Enter hex: ').lstrip('#')
print('RGB =', tuple(int(h[i:i+2], 16) for i in (0, 2, 4)))
(The above was written for Python 3)
Sample run:
Enter hex: #B4FBB8
RGB = (180, 251, 184)
Writing to a file
To write to a file with handle fhandle while preserving the formatting:
fhandle.write('RGB = {}'.format( tuple(int(h[i:i+2], 16) for i in (0, 2, 4)) ))
You can use ImageColor from Pillow.
>>> from PIL import ImageColor
>>> ImageColor.getcolor("#23a9dd", "RGB")
(35, 169, 221)
Just another option: matplotlib.colors module.
Quite simple:
>>> import matplotlib.colors
>>> matplotlib.colors.to_rgb('#B4FBB8')
(0.7058823529411765, 0.984313725490196, 0.7215686274509804)
Note that the input of to_rgb need not to be hexadecimal color format, it admits several color formats.
You can also use the deprecated hex2color
>>> matplotlib.colors.hex2color('#B4FBB8')
(0.7058823529411765, 0.984313725490196, 0.7215686274509804)
The bonus is that we have the inverse function, to_hex and few extra functions such as, rgb_to_hsv.
A lazy option:
webcolors package has a hex_to_rgb function.
PIL also has this function, in ImageColor.
from PIL import ImageColor
ImageColor.getrgb("#9b9b9b")
And if you want the numbers from 0 to 1
[i/256 for i in ImageColor.getrgb("#9b9b9b")]
Try this:
def rgb_to_hex(rgb):
return '%02x%02x%02x' % rgb
Usage:
>>> rgb_to_hex((255, 255, 195))
'ffffc3'
And for the reverse:
def hex_to_rgb(hexa):
return tuple(int(hexa[i:i+2], 16) for i in (0, 2, 4))
Usage:
>>> hex_to_rgb('ffffc3')
(255, 255, 195)
This function will return the RGB values in float from a Hex code.
def hextofloats(h):
'''Takes a hex rgb string (e.g. #ffffff) and returns an RGB tuple (float, float, float).'''
return tuple(int(h[i:i + 2], 16) / 255. for i in (1, 3, 5)) # skip '#'
This function will return Hex code from RGB value.
def floatstohex(rgb):
'''Takes an RGB tuple or list and returns a hex RGB string.'''
return f'#{int(rgb[0]*255):02x}{int(rgb[1]*255):02x}{int(rgb[2]*255):02x}'
As HEX codes can be like "#FFF", "#000", "#0F0" or even "#ABC" that only use three digits. These are just the shorthand version of writing a code, which are the three pairs of identical digits "#FFFFFF", "#000000", "#00FF00" or "#AABBCC".
This function is made in such a way that it can work with both shorthands as well as the full length of HEX codes. Returns RGB values if the argument hsl = False else return HSL values.
import re
def hex_to_rgb(hx, hsl=False):
"""Converts a HEX code into RGB or HSL.
Args:
hx (str): Takes both short as well as long HEX codes.
hsl (bool): Converts the given HEX code into HSL value if True.
Return:
Tuple of length 3 consisting of either int or float values.
Raise:
ValueError: If given value is not a valid HEX code."""
if re.compile(r'#[a-fA-F0-9]{3}(?:[a-fA-F0-9]{3})?$').match(hx):
div = 255.0 if hsl else 0
if len(hx) <= 4:
return tuple(int(hx[i]*2, 16) / div if div else
int(hx[i]*2, 16) for i in (1, 2, 3))
return tuple(int(hx[i:i+2], 16) / div if div else
int(hx[i:i+2], 16) for i in (1, 3, 5))
raise ValueError(f'"{hx}" is not a valid HEX code.')
Here are some IDLE outputs.
>>> hex_to_rgb('#FFB6C1')
(255, 182, 193)
>>> hex_to_rgb('#ABC')
(170, 187, 204)
>>> hex_to_rgb('#FFB6C1', hsl=True)
(1.0, 0.7137254901960784, 0.7568627450980392)
>>> hex_to_rgb('#ABC', hsl=True)
(0.6666666666666666, 0.7333333333333333, 0.8)
>>> hex_to_rgb('#00FFFF')
(0, 255, 255)
>>> hex_to_rgb('#0FF')
(0, 255, 255)
>>> hex_to_rgb('#0FFG') # When invalid hex is given.
ValueError: "#0FFG" is not a valid HEX code.
The following function will convert hex string to rgb values:
def hex_to_rgb(hex_string):
r_hex = hex_string[1:3]
g_hex = hex_string[3:5]
b_hex = hex_string[5:7]
return int(r_hex, 16), int(g_hex, 16), int(b_hex, 16)
This will convert the hexadecimal_string to decimal number
int(hex_string, 16)
For example:
int('ff', 16) # Gives 255 in integer data type
There are two small errors here!
hex == input("")
Should be:
user_hex = input("")
You want to assign the output of input() to hex, not check for comparison. Also, as mentioned in comments (#koukouviou) don't override hex, instead call it something like user_hex.
Also:
print(hex_to_rgb(hex()))
Should be:
print(hex_to_rgb(user_hex))
You want to use the value of hex, not the type's callable method (__call__).
All the answers I've seen involve manipulation of a hex string. In my view, I'd prefer to work with encoded integers and RGB triples themselves, not just strings. This has the benefit of not requiring that a color be represented in hexadecimal-- it could be in octal, binary, decimal, what have you.
Converting an RGB triple to an integer is easy.
rgb = (0xc4, 0xfb, 0xa1) # (196, 251, 161)
def rgb2int(r,g,b):
return (256**2)*r + 256*g + b
c = rgb2int(*rgb) # 12909473
print(hex(c)) # '0xc4fba1'
We need a little more math for the opposite direction. I've lifted the following from my answer to a similar Math exchange question.
c = 0xc4fba1
def int2rgb(n):
b = n % 256
g = int( ((n-b)/256) % 256 ) # always an integer
r = int( ((n-b)/256**2) - g/256 ) # ditto
return (r,g,b)
print(tuple(map(hex, int2rgb(c)))) # ('0xc4', '0xfb', '0xa1')
With this approach, you can convert to and from strings with ease.
The problem with your approach is that a user can input a hex code in many formats such as:
With or without a hash symbol (#ff0000 or ff0000)
Uppercase or lowercase (#ff0000, #FF0000)
Including or not including transparency (#ffff0000 or #ff0000ff or #ff0000)
colorir can be used to format and convert between color systems:
from colorir import HexRGB, sRGB
user_input = input("Enter the hex code:")
rgb = HexRGB(user_input).rgb() # This is safe for pretty much any hex format
This supports also RGBA converting and short form (#fff). It's pretty ugly, but it does the trick.
def parse_hex(h):
if h[0]=="#":
h=h[1:]
try:
col=int(h,16)
except ValueError:
raise ValueError("Invalid HEX string") from None
l=len(h)
if l== 6: #long form , no alpha
return (col&0xff0000)>>16,(col&0xff00)>>8,(col&0xff),1
elif l== 8: #long form, alpha
scol = col >> 8
return (scol&0xff0000)>>16,(scol&0xff00)>>8,(scol&0xff),(col&0xff)/255
elif l == 3: #short form, no alpha
return 17*((col&0xf00)>>8),17*((col&0xf0)>>4),17*(col&0xf),1
elif l == 4: #short form, alpha
print(hex(col))
return 17*((col&0xf000)>>12),17*((col&0xf00)>>8),17*((col&0xf0)>>4),17*(col&0xf)/255
else:
raise ValueError("Invalid HEX string")
#Converting Hex to RGB value in Python
def hex_to_rgb(h, opacity=.8):
r = g = b = 0
if len(h) == 4:
r = int(("0x" + h[1] + h[1]), base=16)
g = int(("0x" + h[2] + h[2]), base=16)
b = int(("0x" + h[3] + h[3]), base=16)
elif len(h) == 7:
r = int(("0x" + h[1] + h[2]), base=16)
g = int(("0x" + h[3] + h[4]), base=16)
b = int(("0x" + h[5] + h[6]), base=16)
if opacity:
return f"rgb({r}, {g}, {b}, {opacity})"
return f"rgb({r}, {g}, {b})"

Categories

Resources