From f4a3ee056f58ad1a83e41a202d034a70ac523e56 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Sun, 5 Feb 2023 20:04:11 -0500 Subject: [PATCH] add palette module --- TODO.md | 41 +++++++----- cli/modules/palette.py | 92 +++++++++++++++++++++++++ cli/modules/process.py | 43 +++++++++++- cli/palette.py | 148 ----------------------------------------- 4 files changed, 156 insertions(+), 168 deletions(-) create mode 100755 cli/modules/palette.py delete mode 100644 cli/palette.py diff --git a/TODO.md b/TODO.md index 17d64f355..c8b80344a 100644 --- a/TODO.md +++ b/TODO.md @@ -82,21 +82,28 @@ Cool stuff that is not integrated anywhere... - Prevalent colors to interrogate - Auto-Sort inputs by face recognition +## Updates -core library updates: -- run `./automatic.sh install` -- note, this is quite a big one so some testing is reccomended after upgrade -ui updates -new script: -- `cli/watermark.py` to a) strip exif from images, b) add invisible watermark to images which persists even if user modifies image so we can always track it -expose variation seed in main ui -integrated seed travel functionality into core -integrated `pix2pix` functionality to standard `img2img` workflow -- note: requires **pix2pix** model to be loaded -integrated large `cfg scale` values fix -tested `aesthetic gradients` training, not worth it -updated `image browser` -initial work on **queue management** allowing to submit multiple requests to server -initial work on `lora` integration (hidden) -initial work on `custom diffusion` integration (hidden) -spent quite some time making stable-diffusion compatible with upcomming `pytorch` 2.0 release +- core library updates: + - must run `./automatic.sh install` + - note: this is quite a big one so some testing is reccomended after upgrade +- non-trivial ui updates +- added **brightness dynamic range** check to `process.py` +- new script: `watermark.py` + - optionally strip exif from images + - add invisible watermark to images which persists even if user modifies image so we can always track it +- new script: `palette.py` + - creates color palette wheel from image + - not finished +- expose variation seed in main ui +- integrated seed travel functionality into core +- integrated `pix2pix` functionality to standard `img2img` workflow + - note: requires **pix2pix** model to be loaded +- integrated large `cfg scale` values fix +- tested `aesthetic gradients` training, not worth it +- updated `image browser` + was broken for a while and maintainer is gone +- initial work on **queue management** allowing to submit multiple requests to server +- initial work on `lora` integration (hidden) +- initial work on `custom diffusion` integration (hidden) +- spent quite some time making stable-diffusion compatible with upcomming `pytorch` 2.0 release diff --git a/cli/modules/palette.py b/cli/modules/palette.py new file mode 100755 index 000000000..b26ced7f6 --- /dev/null +++ b/cli/modules/palette.py @@ -0,0 +1,92 @@ +#!/bin/env python +# based on + +import os +import sys +import pandas as pd +import numpy as np +import extcolors +import matplotlib.pyplot as plt +import matplotlib.patches as patches +import matplotlib.image as mpimg +from matplotlib.offsetbox import OffsetImage, AnnotationBbox +from colormap import rgb2hex +from PIL import Image + + +def color_to_df(input): + colors_pre_list = str(input).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 color_wheel(input_image, resize, tolerance, zoom): + #resize + img = Image.open(input_image) + if img.size[0] >= resize: + wpercent = (resize / float(img.size[0])) + hsize = int((float(img.size[1]) * float(wpercent))) + img = img.resize((resize, hsize)) + + #crate dataframe + colors_x = extcolors.extract_from_image(img, tolerance = tolerance, 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) + + #donut plot + wedges, _text = ax1.pie(list_precent, labels= text_c, labeldistance= 1.05, colors = list_color, textprops={'fontsize': 140, 'color':'black'}) + plt.setp(wedges, width=0.3) + + #add image in the center of donut plot + data = np.asarray(img) + imagebox = OffsetImage(data, zoom=zoom) + ab = AnnotationBbox(imagebox, (0, 0)) + ax1.add_artist(ab) + + #color palette + x_posi, y_posi, y_posi2 = 160, -200, -200 + for c in list_color: + if list_color.index(c) <= 5: + y_posi += 220 + rect = patches.Rectangle((x_posi, y_posi), 480, 200, facecolor = c) + ax2.add_patch(rect) + ax2.text(x = x_posi + 40, y = y_posi + 120, s = c, fontdict={'fontsize': 140}) + else: + y_posi2 += 220 + rect = patches.Rectangle((x_posi + 600, y_posi2), 480, 200, facecolor = c) + ax2.add_artist(rect) + ax2.text(x = x_posi + 640, y = y_posi2 + 120, s = c, fontdict={'fontsize': 140}) + + #background + tmp_file = 'tmp.png' + fig, _ax = plt.subplots(figsize=(200,140),dpi=10) + fig.set_facecolor('white') + plt.savefig(tmp_file) + plt.close(fig) + + fig.set_facecolor('white') + ax2.axis('off') + tmp = plt.imread(tmp_file) + plt.imshow(tmp) + plt.tight_layout() + plt.savefig('palette.jpg') + plt.close() + os.remove(tmp_file) + return + + +if __name__ == '__main__': + sys.argv.pop(0) + for arg in sys.argv: + color_wheel(arg, 512, 10, 2) diff --git a/cli/modules/process.py b/cli/modules/process.py index 273677ed4..05581cbc8 100755 --- a/cli/modules/process.py +++ b/cli/modules/process.py @@ -20,14 +20,16 @@ process people images import os import sys import io -import filetype +import math import base64 import pathlib +import filetype import numpy as np import mediapipe as mp from PIL import Image, ImageOps from skimage.metrics import structural_similarity as ssim +from scipy.stats import beta from util import log, Map from sdapi import postsync @@ -45,13 +47,15 @@ params = Map({ 'face_score': 0.7, # min face detection score 'face_pad': 0.07, # pad face image percentage 'face_model': 1, # which face model to use 0/close-up 1/standard - 'face_blur_score': 1.4, # max score for face blur detection + 'face_blur_score': 1.5, # max score for face blur detection + 'face_range_score': 0.5, # min score for face dynamic range detection 'body_score': 0.9, # min body detection score 'body_visibility': 0.5, # min visibility score for each detected body part 'body_parts': 15, # min number of detected body parts with sufficient visibility 'body_pad': 0.2, # pad body image percentage 'body_model': 2, # body model to use 0/low 1/medium 2/high - 'body_blur_score': 1.6, # max score for body blur detection + 'body_blur_score': 1.8, # max score for body blur detection + 'body_range_score': 0.5, # min score for body dynamic range detection 'segmentation_face': True, # segmentation enabled 'segmentation_body': False, # segmentation enabled 'segmentation_model': 0, # segmentation model 0/general 1/landscape @@ -76,6 +80,25 @@ def detect_blur(image): return mean +def detect_dynamicrange(image): + # based on + data = np.asarray(image) + image = np.float32(data) + RGB = [0.299, 0.587, 0.114] + height, width = image.shape[:2] + brightness_image = np.sqrt(image[..., 0] ** 2 * RGB[0] + image[..., 1] ** 2 * RGB[1] + image[..., 2] ** 2 * RGB[2]) + hist, _ = np.histogram(brightness_image, bins=256, range=(0, 255)) + img_brightness_pmf = hist / (height * width) + dist = beta(2, 2) + ys = dist.pdf(np.linspace(0, 1, 256)) + ref_pmf = ys / np.sum(ys) + dot_product = np.dot(ref_pmf, img_brightness_pmf) + squared_dist_a = np.sum(ref_pmf ** 2) + squared_dist_b = np.sum(img_brightness_pmf ** 2) + res = dot_product / math.sqrt(squared_dist_a * squared_dist_b) + return round(res, 2) + + images = [] def detect_simmilar(image): img = image.resize((params.similarity_size, params.similarity_size)) @@ -149,6 +172,13 @@ def extract_face(img): else: log.debug({ 'extract face blur': blur }) + range = detect_dynamicrange(squared) + if range < params.face_range_score: + log.info({ 'extract face': 'dynamic range check fail', 'range': range }) + return None, True + else: + log.debug({ 'extract face dynamic range': range }) + similarity = detect_simmilar(squared) if similarity > params.similarity_score: log.info({ 'extract face': 'similarity check fail', 'score': round(similarity, 2) }) @@ -202,6 +232,13 @@ def extract_body(img): else: log.debug({ 'extract body blur': blur }) + range = detect_dynamicrange(squared) + if range < params.body_range_score: + log.info({ 'extract body': 'dynamic range check fail', 'range': range }) + return None, True + else: + log.debug({ 'extract body dynamic range': range }) + similarity = detect_simmilar(squared) if similarity > params.similarity_score: log.info({ 'extract body': 'similarity check fail', 'score': similarity }) diff --git a/cli/palette.py b/cli/palette.py deleted file mode 100644 index 54ffb9cdd..000000000 --- a/cli/palette.py +++ /dev/null @@ -1,148 +0,0 @@ -# based on - -import numpy as np -import pandas as pd -import matplotlib.pyplot as plt -import matplotlib.patches as patches -import matplotlib.image as mpimg - -from PIL import Image -from matplotlib.offsetbox import OffsetImage, AnnotationBbox - -#!pip install easydev #version 0.12.0 -#!pip install colormap #version 1.0.4 -#!pip install opencv-python #version 4.5.5.64 -#!pip install colorgram.py #version 1.2.0 -#!pip install extcolors #version 1.0.0 - -import cv2 -import extcolors - -from colormap import rgb2hex - - -input_name = '' -output_width = 900 #set the output size -img = Image.open(input_name) -wpercent = (output_width/float(img.size[0])) -hsize = int((float(img.size[1])*float(wpercent))) -img = img.resize((output_width,hsize), Image.ANTIALIAS) - -#save -resize_name = 'resize_' + input_name #the resized image name -img.save(resize_name) #output location can be specified before resize_name - -#read -plt.figure(figsize=(9, 9)) -img_url = resize_name -img = plt.imread(img_url) -plt.imshow(img) -plt.axis('off') -plt.show() - -colors_x = extcolors.extract_from_path(img_url, tolerance = 12, limit = 12) -colors_x - - -def color_to_df(input): - colors_pre_list = str(input).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 - -df_color = color_to_df(colors_x) -df_color - - -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, ax = plt.subplots(figsize=(90,90),dpi=10) -wedges, text = ax.pie(list_precent, - labels= text_c, - labeldistance= 1.05, - colors = list_color, - textprops={'fontsize': 120, 'color':'black'} - ) -plt.setp(wedges, width=0.3) - -#create space in the center -plt.setp(wedges, width=0.36) - -ax.set_aspect("equal") -fig.set_facecolor('white') -plt.show() - - -#create background color -fig, ax = plt.subplots(figsize=(192,108),dpi=10) -fig.set_facecolor('white') -plt.savefig('bg.png') -plt.close(fig) - -#create color palette -bg = plt.imread('bg.png') -fig = plt.figure(figsize=(90, 90), dpi = 10) -ax = fig.add_subplot(1,1,1) - -x_posi, y_posi, y_posi2 = 320, 25, 25 -for c in list_color: - if list_color.index(c) <= 5: - y_posi += 125 - rect = patches.Rectangle((x_posi, y_posi), 290, 115, facecolor = c) - ax.add_patch(rect) - ax.text(x = x_posi+360, y = y_posi+80, s = c, fontdict={'fontsize': 150}) - else: - y_posi2 += 125 - rect = patches.Rectangle((x_posi + 800, y_posi2), 290, 115, facecolor = c) - ax.add_artist(rect) - ax.text(x = x_posi+1160, y = y_posi2+80, s = c, fontdict={'fontsize': 150}) - -ax.axis('off') -plt.imshow(bg) -plt.tight_layout() - -img = mpimg.imread('') -bg = plt.imread('bg.png') - -fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(160,120), dpi = 10) - -#donut plot -wedges, text = ax1.pie(list_precent, - labels= text_c, - labeldistance= 1.05, - colors = list_color, - textprops={'fontsize': 160, 'color':'black'}) -plt.setp(wedges, width=0.3) - -#add image in the center of donut plot -imagebox = OffsetImage(img, zoom=2.3) -ab = AnnotationBbox(imagebox, (0, 0)) -ax1.add_artist(ab) - -#color palette -x_posi, y_posi, y_posi2 = 160, -170, -170 -for c in list_color: - if list_color.index(c) <= 5: - y_posi += 180 - rect = patches.Rectangle((x_posi, y_posi), 360, 160, facecolor = c) - ax2.add_patch(rect) - ax2.text(x = x_posi+400, y = y_posi+100, s = c, fontdict={'fontsize': 190}) - else: - y_posi2 += 180 - rect = patches.Rectangle((x_posi + 1000, y_posi2), 360, 160, facecolor = c) - ax2.add_artist(rect) - ax2.text(x = x_posi+1400, y = y_posi2+100, s = c, fontdict={'fontsize': 190}) - -ax2.axis('off') -fig.set_facecolor('white') -plt.imshow(bg) -plt.tight_layout()