Fixed color comparison

This commit is contained in:
Rokuz 2017-11-20 14:45:55 +03:00 committed by Daria Volvenkova
parent 5f415ddb32
commit 86bfbd101b
3 changed files with 151 additions and 20 deletions

View file

@ -6,7 +6,7 @@
"text": "231F20"
},
"gray": {
"clear": "8B8B8C",
"clear": "A4A4A6",
"night": "464647",
"text": "231F20"
},
@ -121,17 +121,17 @@
"text": "01579B"
},
"blue_light": {
"clear": "90CAF9",
"clear": "B2D8F8",
"night": "466178",
"text": "01579B"
},
"navy_dark": {
"clear": "1A357D",
"clear": "193380",
"night": "0F1B42",
"text": "1A357D"
},
"navy": {
"clear": "0068AD",
"clear": "243BB3",
"night": "003557",
"text": "1A357D"
},
@ -140,13 +140,8 @@
"night": "3D4866",
"text": "1A357D"
},
"indigo": {
"clear": "4824B5",
"night": "25135B",
"text": "4A148C"
},
"purple_dark": {
"clear": "4A148C",
"clear": "52138B",
"night": "360E66",
"text": "4A148C"
},

View file

@ -1,3 +1,5 @@
import math
def to_rgb(color_str):
if len(color_str) != 6:
return (0, 0, 0)
@ -7,6 +9,90 @@ def to_rgb(color_str):
return (r, g, b)
def blend_colors(rgb_array1, rgb_array2, k):
return (rgb_array1[0] * (1.0 - k) + rgb_array2[0] * k,
rgb_array1[1] * (1.0 - k) + rgb_array2[1] * k,
rgb_array1[2] * (1.0 - k) + rgb_array2[2] * k)
def rgb_pivot(n):
result = n / 12.92
if n > 0.04045:
result = ((n + 0.055) / 1.055) ** 2.4
return result * 100.0;
def to_xyz(rgb_array):
r = rgb_pivot(rgb_array[0] / 255.0);
g = rgb_pivot(rgb_array[1] / 255.0);
b = rgb_pivot(rgb_array[2] / 255.0);
return (r * 0.4124 + g * 0.3576 + b * 0.1805,
r * 0.2126 + g * 0.7152 + b * 0.0722,
r * 0.0193 + g * 0.1192 + b * 0.9505)
#https://en.wikipedia.org/wiki/Lab_color_space#CIELAB
def lab_pivot(n):
if n > 0.008856:
return n ** (1.0/3.0)
return (903.3 * n + 16.0) / 116.0
def to_lab(rgb_array):
xyz = to_xyz(rgb_array)
x = lab_pivot(xyz[0] / 95.047)
y = lab_pivot(xyz[1] / 100.0)
z = lab_pivot(xyz[2] / 108.883)
l = 116.0 * y - 16.0
if l < 0.0:
l = 0.0
a = 500.0 * (x - y)
b = 200.0 * (y - z)
return (l, a, b)
def lum_distance(ref_color, src_color):
return 30 * (ref_color[0] - src_color[0]) ** 2 +\
59 * (ref_color[1] - src_color[1]) ** 2 +\
11 * (ref_color[2] - src_color[2]) ** 2
def is_bluish(rgb_array):
d1 = lum_distance((255, 0, 0), rgb_array)
d2 = lum_distance((0, 0, 255), rgb_array)
return d2 < d1
#http://en.wikipedia.org/wiki/Color_difference#CIE94
def cie94(ref_color, src_color):
lab_ref = to_lab(ref_color)
lab_src = to_lab(src_color)
deltaL = lab_ref[0] - lab_src[0]
deltaA = lab_ref[1] - lab_src[1]
deltaB = lab_ref[2] - lab_src[2]
c1 = math.sqrt(lab_ref[0] * lab_ref[0] + lab_ref[1] * lab_ref[1])
c2 = math.sqrt(lab_src[0] * lab_src[0] + lab_src[1] * lab_src[1])
deltaC = c1 - c2
deltaH = deltaA * deltaA + deltaB * deltaB - deltaC * deltaC
if deltaH < 0.0:
deltaH = 0.0
else:
deltaH = math.sqrt(deltaH)
# cold tones if a color is more bluish.
Kl = 1.0
K1 = 0.045
K2 = 0.015
sc = 1.0 + K1 * c1
sh = 1.0 + K2 * c1
deltaLKlsl = deltaL / Kl
deltaCkcsc = deltaC / sc
deltaHkhsh = deltaH / sh
i = deltaLKlsl * deltaLKlsl + deltaCkcsc * deltaCkcsc + deltaHkhsh * deltaHkhsh
if i < 0:
return 0.0
return math.sqrt(i)
class Palette:
def __init__(self, colors):
self.colors = {}
@ -16,16 +102,26 @@ class Palette:
def get_default_color(self):
return 'default'
def get_nearest_color(self, color_str):
def get_nearest_color(self, color_str, casing_color_str, excluded_names):
"""Returns the nearest color from the palette."""
nearest_color_name = self.get_default_color()
color = to_rgb(color_str)
if (casing_color_str is not None and len(casing_color_str) != 0):
color = blend_colors(color, to_rgb(casing_color_str), 0.5)
min_diff = None
bluish = is_bluish(color)
for name, palette_color in self.colors.iteritems():
diff = 30 * (palette_color[0] - color[0]) ** 2 +\
59 * (palette_color[1] - color[1]) ** 2 +\
11 * (palette_color[2] - color[2]) ** 2
if name in excluded_names:
continue
if bluish:
diff = lum_distance(palette_color, color)
else:
diff = cie94(palette_color, color)
if min_diff is None or diff < min_diff:
min_diff = diff
nearest_color_name = name
# Left here for debug purposes.
#print("Result: " + color_str + "," + str(casing_color_str) +
# " - " + nearest_color_name + ": bluish = " + str(bluish))
return nearest_color_name

View file

@ -6,6 +6,7 @@ import copy
import json
import math
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import numpy as np
import os.path
import sys, io
@ -70,6 +71,7 @@ class TransitGraphBuilder:
self.segments = {}
self.shapes = []
self.transit_graph = None
self.matched_colors = {}
def __get_average_stops_point(self, stop_ids):
"""Returns an average position of the stops."""
@ -176,10 +178,7 @@ class TransitGraphBuilder:
'interval': line_item['interval'],
'stop_ids': []
}
if 'colour' in route_item:
line['color'] = self.palette.get_nearest_color(route_item['colour'])
else:
line['color'] = self.palette.get_default_color()
line['color'] = self.__match_color(route_item.get('colour', ''), route_item.get('casing', ''))
# TODO: Add processing of line_item['shape'] when this data will be available.
# TODO: Add processing of line_item['trip_ids'] when this data will be available.
@ -203,6 +202,17 @@ class TransitGraphBuilder:
self.lines.append(line)
line_index += 1
def __match_color(self, color_str, casing_str):
if len(color_str) == 0:
return self.palette.get_default_color()
matched_colors_key = color_str + "/" + casing_str
if matched_colors_key in self.matched_colors:
return self.matched_colors[matched_colors_key]
c = self.palette.get_nearest_color(color_str, casing_str, self.matched_colors.values())
if c != self.palette.get_default_color():
self.matched_colors[matched_colors_key] = c
return c
def __generate_transfer_nodes(self):
"""Merges stops into transfer nodes."""
for edge in self.edges:
@ -332,6 +342,27 @@ class TransitGraphBuilder:
plt.scatter([point['x']], [point['y']], size, color)
plt.show()
def show_color_maching_table(self, title, colors_ref_table):
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal')
plt.title(title)
sz = 1.0 / (2.0 * len(self.matched_colors))
delta_y = sz * 0.5
for c in self.matched_colors:
tokens = c.split('/')
if len(tokens[1]) == 0:
tokens[1] = tokens[0]
ax.add_patch(patches.Rectangle((sz, delta_y), sz, sz, facecolor="#" + tokens[0], edgecolor="#" + tokens[1]))
rect_title = tokens[0]
if tokens[0] != tokens[1]:
rect_title += "/" + tokens[1]
ax.text(2.5 * sz, delta_y, rect_title + " -> ")
ref_color = colors_ref_table[self.matched_colors[c]]
ax.add_patch(patches.Rectangle((0.3 + sz, delta_y), sz, sz, facecolor="#" + ref_color))
ax.text(0.3 + 2.5 * sz, delta_y, ref_color + " (" + self.matched_colors[c] + ")")
delta_y += sz * 2.0
plt.show()
if __name__ == '__main__':
parser = argparse.ArgumentParser()
@ -342,6 +373,9 @@ if __name__ == '__main__':
help='transit colors file COLORS_FILE_PATH', metavar='COLORS_FILE_PATH')
parser.add_argument('-p', '--preview', action="store_true", default=False,
help="show preview of the transit scheme")
parser.add_argument('-m', '--matched_colors', action="store_true", default=False,
help="show the matched colors table")
parser.add_argument('-a', '--alpha', type=float, default=0.5, help='the curves generator parameter value ALPHA',
metavar='ALPHA')
@ -360,9 +394,9 @@ if __name__ == '__main__':
result = transit.build()
output_file = args.output_file
head, tail = os.path.split(os.path.abspath(args.input_file))
name, extension = os.path.splitext(tail)
if output_file is None:
head, tail = os.path.split(os.path.abspath(args.input_file))
name, extension = os.path.splitext(tail)
output_file = os.path.join(head, name + '.transit' + extension)
with io.open(output_file, 'w', encoding='utf8') as json_file:
result_data = json.dumps(result, ensure_ascii=False, indent=4, sort_keys=True)
@ -371,3 +405,9 @@ if __name__ == '__main__':
if args.preview:
transit.show_preview()
if args.matched_colors:
colors_ref_table = {}
for color_name, color_info in colors['colors'].iteritems():
colors_ref_table[color_name] = color_info['clear']
transit.show_color_maching_table(name, colors_ref_table)