mirror of
https://github.com/vladmandic/automatic
synced 2026-09-19 17:24:32 +02:00
add palette module
This commit is contained in:
@@ -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
|
||||
|
||||
Executable
+92
@@ -0,0 +1,92 @@
|
||||
#!/bin/env python
|
||||
# based on <https://towardsdatascience.com/image-color-extraction-with-python-in-4-steps-8d9370d9216e>
|
||||
|
||||
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)
|
||||
+40
-3
@@ -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 <https://towardsdatascience.com/measuring-enhancing-image-quality-attributes-234b0f250e10>
|
||||
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 })
|
||||
|
||||
-148
@@ -1,148 +0,0 @@
|
||||
# based on <https://towardsdatascience.com/image-color-extraction-with-python-in-4-steps-8d9370d9216e>
|
||||
|
||||
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 = '<photo location/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('<photo location/name>')
|
||||
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()
|
||||
Reference in New Issue
Block a user