import os
import io
import pathlib
import argparse
import importlib
import pandas as pd
import numpy as np
import extcolors
import filetype
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
from colormap import rgb2hex
from PIL import Image
from util import log
grid = importlib.import_module('image-grid').grid
def color_to_df(param):
colors_pre_list = str(param).replace('([(','').split(', (')[0:-1]
df_rgb = [i.split('), ')[0] + ')' for i in colors_pre_list if len(i.split('), ')) >= 2]
df_percent = [i.split('), ')[1].replace(')','') for i in colors_pre_list if len(i.split('), ')) >= 2]
df_color_up = [rgb2hex(int(i.split(", ")[0].replace("(","")),
int(i.split(", ")[1]),
int(i.split(", ")[2].replace(")",""))) for i in df_rgb]
df = pd.DataFrame(zip(df_color_up, df_percent), columns = ['c_code','occurence'])
return df
def palette(img, params, output):
size = 1024
img.thumbnail((size, size), Image.Resampling.HAMMING)
colors_x = extcolors.extract_from_image(img, tolerance = params.color, limit = 13)
df_color = color_to_df(colors_x)
list_color = list(df_color['c_code'])
list_precent = [int(i) for i in list(df_color['occurence'])]
text_c = [c + ' ' + str(round(p * 100 / sum(list_precent), 1)) +'%' for c, p in zip(list_color, list_precent)]
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(120,60), dpi=10)
fig.set_facecolor('black')
wedges, _text = ax1.pie(list_precent, labels= text_c, labeldistance= 1.05, colors = list_color, textprops={'fontsize': 100, 'color':'white'})
plt.setp(wedges, width=0.3)
data = np.asarray(img)
imagebox = OffsetImage(data, zoom=2.5)
ab = AnnotationBbox(imagebox, (0, 0))
ax1.add_artist(ab)
x_posi, y_posi, y_posi2 = 160, -260, -260
for c in list_color:
if list_color.index(c) <= 5:
y_posi += 240
rect = patches.Rectangle((x_posi, y_posi), 540, 230, facecolor = c)
ax2.add_patch(rect)
ax2.text(x = x_posi + 100, y = y_posi + 140, s = c, fontdict={'fontsize': 140}, color = 'white')
else:
y_posi2 += 240
rect = patches.Rectangle((x_posi + 600, y_posi2), 540, 230, facecolor = c)
ax2.add_artist(rect)
ax2.text(x = x_posi + 700, y = y_posi2 + 140, s = c, fontdict={'fontsize': 140}, color = 'white')
fig.set_facecolor('black')
ax2.axis('off')
tmp = Image.new('RGB', (2000, 1400), (0, 0, 0))
plt.imshow(tmp)
plt.tight_layout(rect = (-0.08, -0.2, 1.18, 1.05))
if output is not None:
buf = io.BytesIO()
plt.savefig(buf, format='png')
pltimg = Image.open(buf)
pltimg = pltimg.convert('RGB')
pltimg.save(output)
buf.close()
log.info({ 'palette created': output })
plt.close()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description = 'extract image color palette')
parser.add_argument('--color', type=int, default=20, help="color tolerance threshdold")
parser.add_argument('--output', type=str, required=False, default='', help='folder to store images')
parser.add_argument('--suffix', type=str, required=False, default='pallete', help='add suffix to image name')
parser.add_argument('--grid', default=False, action='store_true', help = "create grid of images before processing")
parser.add_argument('input', type=str, nargs='*')
args = parser.parse_args()
log.info({ 'palette args': vars(args) })
if args.output != '':
pathlib.Path(args.output).mkdir(parents = True, exist_ok = True)
if not args.grid:
for arg in args.input:
if os.path.isfile(arg) and filetype.is_image(arg):
image = Image.open(arg)
fn = os.path.join(args.output, pathlib.Path(arg).stem + '-' + args.suffix + '.jpg')
palette(image, args, fn)
elif os.path.isdir(arg):
for root, _dirs, files in os.walk(arg):
for f in files:
if filetype.is_image(os.path.join(root, f)):
image = Image.open(os.path.join(root, f))
fn = os.path.join(args.output, pathlib.Path(f).stem + '-' + args.suffix + '.jpg')
palette(image, args, fn)
else:
images = []
for arg in args.input:
if os.path.isfile(arg) and filetype.is_image(arg):
images.append(Image.open(arg))
elif os.path.isdir(arg):
for root, _dirs, files in os.walk(arg):
for f in files:
if filetype.is_image(os.path.join(root, f)):
images.append(Image.open(os.path.join(root, f)))
image = grid(images)
fn = os.path.join(args.output, args.suffix + '.jpg')
palette(image, args, fn)