You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
4.5 KiB
168 lines
4.5 KiB
2 years ago
|
# coding:utf-8
|
||
|
from math import floor
|
||
|
|
||
|
import numpy as np
|
||
|
from colorthief import ColorThief
|
||
|
from PIL import Image
|
||
|
from PySide6.QtGui import QImage, QPixmap
|
||
|
from scipy.ndimage.filters import gaussian_filter
|
||
|
|
||
|
from .exception_handler import exceptionHandler
|
||
|
|
||
|
|
||
|
|
||
|
def gaussianBlur(imagePath, blurRadius=18, brightFactor=1, blurPicSize= None):
|
||
|
if not imagePath.startswith(':'):
|
||
|
image = Image.open(imagePath)
|
||
|
else:
|
||
|
image = Image.fromqpixmap(QPixmap(imagePath))
|
||
|
|
||
|
if blurPicSize:
|
||
|
# adjust image size to reduce computation
|
||
|
w, h = image.size
|
||
|
ratio = min(blurPicSize[0] / w, blurPicSize[1] / h)
|
||
|
w_, h_ = w * ratio, h * ratio
|
||
|
|
||
|
if w_ < w:
|
||
|
image = image.resize((int(w_), int(h_)), Image.ANTIALIAS)
|
||
|
|
||
|
image = np.array(image)
|
||
|
|
||
|
# handle gray image
|
||
|
if len(image.shape) == 2:
|
||
|
image = np.stack([image, image, image], axis=-1)
|
||
|
|
||
|
# blur each channel
|
||
|
for i in range(3):
|
||
|
image[:, :, i] = gaussian_filter(
|
||
|
image[:, :, i], blurRadius) * brightFactor
|
||
|
|
||
|
# convert ndarray to QPixmap
|
||
|
h, w, _ = image.shape
|
||
|
return QPixmap.fromImage(QImage(image.data, w, h, 3*w, QImage.Format_RGB888))
|
||
|
|
||
|
|
||
|
class DominantColor:
|
||
|
""" Dominant color class """
|
||
|
|
||
|
@classmethod
|
||
|
@exceptionHandler((24, 24, 24))
|
||
|
def getDominantColor(cls, imagePath):
|
||
|
""" extract dominant color from image
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
imagePath: str
|
||
|
image path
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
r, g, b: int
|
||
|
gray value of each color channel
|
||
|
"""
|
||
|
if imagePath.startswith(':'):
|
||
|
return (24, 24, 24)
|
||
|
|
||
|
colorThief = ColorThief(imagePath)
|
||
|
|
||
|
# scale image to speed up the computation speed
|
||
|
if max(colorThief.image.size) > 400:
|
||
|
colorThief.image = colorThief.image.resize((400, 400))
|
||
|
|
||
|
palette = colorThief.get_palette(quality=9)
|
||
|
|
||
|
# adjust the brightness of palette
|
||
|
palette = cls.__adjustPaletteValue(palette)
|
||
|
for rgb in palette[:]:
|
||
|
h, s, v = cls.rgb2hsv(rgb)
|
||
|
if h < 0.02:
|
||
|
palette.remove(rgb)
|
||
|
if len(palette) <= 2:
|
||
|
break
|
||
|
|
||
|
palette = palette[:5]
|
||
|
palette.sort(key=lambda rgb: cls.colorfulness(*rgb), reverse=True)
|
||
|
|
||
|
return palette[0]
|
||
|
|
||
|
@classmethod
|
||
|
def __adjustPaletteValue(cls, palette):
|
||
|
""" adjust the brightness of palette """
|
||
|
newPalette = []
|
||
|
for rgb in palette:
|
||
|
h, s, v = cls.rgb2hsv(rgb)
|
||
|
if v > 0.9:
|
||
|
factor = 0.8
|
||
|
elif 0.8 < v <= 0.9:
|
||
|
factor = 0.9
|
||
|
elif 0.7 < v <= 0.8:
|
||
|
factor = 0.95
|
||
|
else:
|
||
|
factor = 1
|
||
|
v *= factor
|
||
|
newPalette.append(cls.hsv2rgb(h, s, v))
|
||
|
|
||
|
return newPalette
|
||
|
|
||
|
@staticmethod
|
||
|
def rgb2hsv(rgb):
|
||
|
""" convert rgb to hsv """
|
||
|
r, g, b = [i / 255 for i in rgb]
|
||
|
mx = max(r, g, b)
|
||
|
mn = min(r, g, b)
|
||
|
df = mx - mn
|
||
|
if mx == mn:
|
||
|
h = 0
|
||
|
elif mx == r:
|
||
|
h = (60 * ((g - b) / df) + 360) % 360
|
||
|
elif mx == g:
|
||
|
h = (60 * ((b - r) / df) + 120) % 360
|
||
|
elif mx == b:
|
||
|
h = (60 * ((r - g) / df) + 240) % 360
|
||
|
s = 0 if mx == 0 else df / mx
|
||
|
v = mx
|
||
|
return (h, s, v)
|
||
|
|
||
|
@staticmethod
|
||
|
def hsv2rgb(h, s, v):
|
||
|
""" convert hsv to rgb """
|
||
|
h60 = h / 60.0
|
||
|
h60f = floor(h60)
|
||
|
hi = int(h60f) % 6
|
||
|
f = h60 - h60f
|
||
|
p = v * (1 - s)
|
||
|
q = v * (1 - f * s)
|
||
|
t = v * (1 - (1 - f) * s)
|
||
|
r, g, b = 0, 0, 0
|
||
|
if hi == 0:
|
||
|
r, g, b = v, t, p
|
||
|
elif hi == 1:
|
||
|
r, g, b = q, v, p
|
||
|
elif hi == 2:
|
||
|
r, g, b = p, v, t
|
||
|
elif hi == 3:
|
||
|
r, g, b = p, q, v
|
||
|
elif hi == 4:
|
||
|
r, g, b = t, p, v
|
||
|
elif hi == 5:
|
||
|
r, g, b = v, p, q
|
||
|
r, g, b = int(r * 255), int(g * 255), int(b * 255)
|
||
|
return (r, g, b)
|
||
|
|
||
|
@staticmethod
|
||
|
def colorfulness(r: int, g: int, b: int):
|
||
|
rg = np.absolute(r - g)
|
||
|
yb = np.absolute(0.5 * (r + g) - b)
|
||
|
|
||
|
# Compute the mean and standard deviation of both `rg` and `yb`.
|
||
|
rg_mean, rg_std = (np.mean(rg), np.std(rg))
|
||
|
yb_mean, yb_std = (np.mean(yb), np.std(yb))
|
||
|
|
||
|
# Combine the mean and standard deviations.
|
||
|
std_root = np.sqrt((rg_std ** 2) + (yb_std ** 2))
|
||
|
mean_root = np.sqrt((rg_mean ** 2) + (yb_mean ** 2))
|
||
|
|
||
|
return std_root + (0.3 * mean_root)
|
||
|
|
||
|
|