test / cli /image-palette.py
bilegentile's picture
Upload folder using huggingface_hub
c19ca42 verified
raw
history blame contribute delete
No virus
5.27 kB
#!/usr/bin/env python
# based on <https://towardsdatascience.com/image-color-extraction-with-python-in-4-steps-8d9370d9216e>
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]
df_percent = [i.split('), ')[1].replace(')','') for i in colors_pre_list]
#convert RGB to HEX code
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.HAMMING)
#crate dataframe
colors_x = extcolors.extract_from_image(img, tolerance = params.color, limit = 13)
df_color = color_to_df(colors_x)
#annotate text
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')
#donut plot
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)
#add image in the center of donut plot
data = np.asarray(img)
imagebox = OffsetImage(data, zoom=2.5)
ab = AnnotationBbox(imagebox, (0, 0))
ax1.add_artist(ab)
#color palette
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')
# add background to force layout
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))
# save image
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)