aboutsummaryrefslogtreecommitdiff
path: root/scripts/.local/bin/personal/tcolorget
blob: e3a478afeb9c6e5b0fac61164e1df1320d5c26f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#!/usr/bin/env python

# A script to generate a color palette from an image which still complies with the
# general terminal colors (i.e. red remains red).
#
# Author: David 'davidpkj' Penkowoj <davidpenkow1@gmail.com>
# Version: 0.1.0
# License: EUPL v. 1.2 @ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12

from PIL import Image
from colour import Color

import sys
import math
import colorz as clz
import seaborn as sns
import matplotlib.pyplot as plt

# Rotate hue and saturation to get other colors
def rotate(input):
    # TODO: instead of rotation, maybe calculate deviation from standard value (i.e. hue 60, sat 50)
    # and then apply the same deviation to all standard colors
    # TODO: also do something about these arbitrary factors
    result = []
    iterations = 6
    sat_factor = -0.1
    hue_factor = 1 / iterations

    for i in range(iterations):
        _hue = input.hue
        _sat = input.saturation
        _lum = input.luminance

        _hue = _hue + i * hue_factor
        _hue = _hue - math.floor(_hue)

        _newsat = _sat + abs(i * sat_factor)

        if _newsat <= 1 and _newsat >= 0:
            _sat = _newsat

        result.append(Color(hsl=(_hue, _sat, _lum)))

    return sorted(result, key=lambda x: x.hue)

# Get the distance between two numbers (context: hue)
def get_distance(color, target):
    return abs(color.hue - target)

# Returns the color which is closes in hue to the given value
def select_nearest(value, colors):
    target = value / 360
    color = colors[0]
    distance = get_distance(color, target)

    for c in colors:
        new_distance = get_distance(c, target)

        if new_distance  < distance:
            color = c
            distance = new_distance

    # NOTE: special case for red, since can be numerically closer to
    # 0° or 360° (1) but not the other. not nice but works
    if target == 0:
        new_color, new_distance = select_nearest(360, colors)

        if new_distance < distance:
            color = new_color

    return color, distance

# Show a palette
def show_palette(colors):
    sns.palplot(sns.color_palette(colors))
    plt.show()

# Save the original image with the attached palette back to disk.
def save_image(colors, path):
    palette_size = 30
    input = Image.open(path)
    colors_len = len(colors)

    output = Image.new("RGB", (input.size[0], input.size[1] + palette_size), (0, 0, 0))
    p2 = (input.size[0], input.size[1] + palette_size)

    # Attaches the colors at the bottom of the image
    for i in range(colors_len):
        p1 = (round(input.size[0] / colors_len) * i, input.size[1])

        output.paste(colors[i], [p1[0], p1[1], p2[0], p2[1]])
        output.paste(input)

    # output.save("output.png")
    output.show()

def main(file):
    # NOTE: The arguments to this function were stolen from the library's internal code.
    colorz_tuple = clz.colorz(file, 1, 170, 200, 50)[0][1]
    normalized_tuple = (colorz_tuple[0] / 256, colorz_tuple[1] / 256, colorz_tuple[2] / 256)
    input_color = Color(rgb=normalized_tuple)
    colors = rotate(input_color)

    terminal_colors = {"red": 0, "green": 120, "yellow": 60, "blue": 240, "magenta": 300, "cyan": 180}

    for key in terminal_colors:
        color, _= select_nearest(terminal_colors[key], colors)
        terminal_colors[key] = color.hex

    # Formulate a propper list of colors
    palette = list(terminal_colors.values())
    palette.insert(0, "#1a1a1a") # my favorite constant black
    palette.append("#efefef") # my favorite constant white

    return palette, input_color

if __name__ == "__main__":
    path = sys.argv[1]
    file = open(path, "rb")

    palette, dominant_color = main(file)

    save_image(palette, path)
    print(dominant_color)