最新最热
This commit is contained in:
@ -1,10 +1,15 @@
|
||||
from PIL import Image, ImageFilter
|
||||
from PIL import ImageEnhance
|
||||
from PIL import ImageChops
|
||||
from PIL import ImageOps
|
||||
|
||||
from konabot.plugins.fx_process.color_handle import ColorHandle
|
||||
|
||||
import math
|
||||
|
||||
from konabot.plugins.fx_process.gradient import GradientGenerator
|
||||
import numpy as np
|
||||
|
||||
class ImageFilterImplement:
|
||||
@staticmethod
|
||||
def apply_blur(image: Image.Image, radius: float = 10) -> Image.Image:
|
||||
@ -50,14 +55,16 @@ class ImageFilterImplement:
|
||||
# 反色
|
||||
@staticmethod
|
||||
def apply_invert(image: Image.Image) -> Image.Image:
|
||||
# 确保图像是RGBA模式,保留透明度通道
|
||||
if image.mode != 'RGBA':
|
||||
image = image.convert('RGBA')
|
||||
r, g, b, a = image.split()
|
||||
r = r.point(lambda i: 255 - i)
|
||||
g = g.point(lambda i: 255 - i)
|
||||
b = b.point(lambda i: 255 - i)
|
||||
return Image.merge('RGBA', (r, g, b, a))
|
||||
|
||||
# 转换为 numpy 数组
|
||||
arr = np.array(image)
|
||||
|
||||
# 只反转 RGB 通道,保持 Alpha 不变
|
||||
arr[:, :, :3] = 255 - arr[:, :, :3]
|
||||
|
||||
return Image.fromarray(arr)
|
||||
|
||||
# 黑白灰度
|
||||
@staticmethod
|
||||
@ -103,26 +110,30 @@ class ImageFilterImplement:
|
||||
def apply_to_color(image: Image.Image, color: str = 'rgb(255,0,0)') -> Image.Image:
|
||||
if image.mode != 'RGBA':
|
||||
image = image.convert('RGBA')
|
||||
# 转为灰度图
|
||||
gray = image.convert('L')
|
||||
# 获取目标颜色的RGB值
|
||||
|
||||
rgb_color = ColorHandle.parse_color(color)
|
||||
# 高光默认为白色,阴影默认为黑色
|
||||
highlight = (255, 255, 255)
|
||||
shadow = (0, 0, 0)
|
||||
# 创建新的图像
|
||||
new_image = Image.new('RGBA', image.size)
|
||||
width, height = image.size
|
||||
for x in range(width):
|
||||
for y in range(height):
|
||||
lum = gray.getpixel((x, y))
|
||||
# 计算新颜色
|
||||
new_r = int((rgb_color[0] * lum + shadow[0] * (highlight[0] - lum)) / 255)
|
||||
new_g = int((rgb_color[1] * lum + shadow[1] * (highlight[1] - lum)) / 255)
|
||||
new_b = int((rgb_color[2] * lum + shadow[2] * (highlight[2] - lum)) / 255)
|
||||
a = image.getpixel((x, y))[3] # 保留原图的透明度
|
||||
new_image.putpixel((x, y), (new_r, new_g, new_b, a))
|
||||
return new_image
|
||||
|
||||
# 转换为灰度并获取数组
|
||||
gray = image.convert('L')
|
||||
lum = np.array(gray, dtype=np.float32) / 255.0 # 归一化到 [0,1]
|
||||
|
||||
# 获取 alpha
|
||||
alpha = np.array(image.getchannel('A'))
|
||||
|
||||
target_r = rgb_color[0] * lum
|
||||
target_g = rgb_color[1] * lum
|
||||
target_b = rgb_color[2] * lum
|
||||
|
||||
# 堆叠通道
|
||||
result_rgb = np.stack([target_r, target_g, target_b], axis=-1)
|
||||
result_rgb = np.clip(result_rgb, 0, 255).astype(np.uint8)
|
||||
|
||||
# 创建结果图像
|
||||
result = np.zeros((image.height, image.width, 4), dtype=np.uint8)
|
||||
result[:, :, :3] = result_rgb
|
||||
result[:, :, 3] = alpha
|
||||
|
||||
return Image.fromarray(result, 'RGBA')
|
||||
|
||||
# 缩放
|
||||
@staticmethod
|
||||
@ -135,33 +146,424 @@ class ImageFilterImplement:
|
||||
# 波纹
|
||||
@staticmethod
|
||||
def apply_wave(image: Image.Image, amplitude: float = 5, wavelength: float = 20) -> Image.Image:
|
||||
if image.mode != 'RGBA':
|
||||
image = image.convert('RGBA')
|
||||
|
||||
width, height = image.size
|
||||
new_image = Image.new('RGBA', (width, height))
|
||||
for x in range(width):
|
||||
for y in range(height):
|
||||
offset_x = int(amplitude * math.sin(2 * math.pi * y / wavelength))
|
||||
offset_y = int(amplitude * math.cos(2 * math.pi * x / wavelength))
|
||||
new_x = x + offset_x
|
||||
new_y = y + offset_y
|
||||
if 0 <= new_x < width and 0 <= new_y < height:
|
||||
new_image.putpixel((x, y), image.getpixel((new_x, new_y)))
|
||||
else:
|
||||
new_image.putpixel((x, y), (0, 0, 0, 0)) # 透明像素
|
||||
return new_image
|
||||
arr = np.array(image)
|
||||
|
||||
# 创建坐标网格
|
||||
y_coords, x_coords = np.mgrid[0:height, 0:width]
|
||||
|
||||
# 计算偏移量(向量化)
|
||||
offset_x = (amplitude * np.sin(2 * np.pi * y_coords / wavelength)).astype(np.int32)
|
||||
offset_y = (amplitude * np.cos(2 * np.pi * x_coords / wavelength)).astype(np.int32)
|
||||
|
||||
# 计算新坐标
|
||||
new_x = x_coords + offset_x
|
||||
new_y = y_coords + offset_y
|
||||
|
||||
# 创建有效坐标掩码
|
||||
valid_mask = (new_x >= 0) & (new_x < width) & (new_y >= 0) & (new_y < height)
|
||||
|
||||
# 创建结果图像(初始为透明)
|
||||
result = np.zeros((height, width, 4), dtype=np.uint8)
|
||||
|
||||
# 只复制有效像素
|
||||
if valid_mask.any():
|
||||
# 使用花式索引复制像素
|
||||
result[valid_mask] = arr[new_y[valid_mask], new_x[valid_mask]]
|
||||
|
||||
return Image.fromarray(result, 'RGBA')
|
||||
|
||||
def apply_color_key(image: Image.Image, target_color: str = 'rgb(255,0,0)', tolerance: int = 60) -> Image.Image:
|
||||
if image.mode != 'RGBA':
|
||||
image = image.convert('RGBA')
|
||||
|
||||
target_rgb = ColorHandle.parse_color(target_color)
|
||||
arr = np.array(image)
|
||||
|
||||
# 计算颜色距离(使用平方距离避免 sqrt)
|
||||
target_arr = np.array(target_rgb, dtype=np.int32)
|
||||
diff = arr[:, :, :3] - target_arr
|
||||
distance_sq = np.sum(diff * diff, axis=2) # (r-r0)² + (g-g0)² + (b-b0)²
|
||||
|
||||
# 创建掩码(距离 <= 容差)
|
||||
mask = distance_sq <= (tolerance * tolerance)
|
||||
|
||||
# 复制原图,只修改 alpha 通道
|
||||
result = arr.copy()
|
||||
result[:, :, 3] = np.where(mask, 0, arr[:, :, 3]) # 符合条件的设为透明
|
||||
|
||||
return Image.fromarray(result)
|
||||
|
||||
# 暗角
|
||||
@staticmethod
|
||||
def apply_vignette(image: Image.Image, radius: float = 1.5) -> Image.Image:
|
||||
if image.mode != 'RGBA':
|
||||
image = image.convert('RGBA')
|
||||
# 转换为 numpy 数组
|
||||
arr = np.array(image, dtype=np.float32)
|
||||
height, width = arr.shape[:2]
|
||||
# 创建网格
|
||||
y_coords, x_coords = np.ogrid[:height, :width]
|
||||
# 计算中心距离
|
||||
center_x = width / 2
|
||||
center_y = height / 2
|
||||
max_distance = np.sqrt(center_x**2 + center_y**2)
|
||||
# 向量化距离计算
|
||||
distances = np.sqrt((x_coords - center_x)**2 + (y_coords - center_y)**2)
|
||||
# 计算暗角因子
|
||||
factors = 1 - (distances / max_distance) ** radius
|
||||
factors = np.clip(factors, 0, 1)
|
||||
# 应用暗角效果到 RGB 通道
|
||||
arr[:, :, :3] = arr[:, :, :3] * factors[:, :, np.newaxis]
|
||||
# 转换回 uint8
|
||||
result = np.clip(arr, 0, 255).astype(np.uint8)
|
||||
return Image.fromarray(result)
|
||||
|
||||
# 发光
|
||||
@staticmethod
|
||||
def apply_glow(image: Image.Image, intensity: float = 1.5, blur_radius: float = 15) -> Image.Image:
|
||||
if image.mode != 'RGBA':
|
||||
image = image.convert('RGBA')
|
||||
# 创建发光图层
|
||||
glow_layer = image.filter(ImageFilter.GaussianBlur(blur_radius))
|
||||
# 增强亮度
|
||||
enhancer = ImageEnhance.Brightness(glow_layer)
|
||||
glow_layer = enhancer.enhance(intensity)
|
||||
# 转换为 numpy 数组
|
||||
img_arr = np.array(image, dtype=np.float32) # 使用 float32 避免溢出
|
||||
glow_arr = np.array(glow_layer, dtype=np.float32)
|
||||
|
||||
# 向量化合并(只合并 RGB,A 保持不变)
|
||||
result_arr = np.zeros_like(img_arr, dtype=np.float32)
|
||||
|
||||
# RGB 通道相加并限制到 255
|
||||
result_arr[:, :, :3] = np.clip(img_arr[:, :, :3] + glow_arr[:, :, :3], 0, 255)
|
||||
|
||||
# Alpha 通道保持原图
|
||||
result_arr[:, :, 3] = img_arr[:, :, 3]
|
||||
|
||||
return Image.fromarray(result_arr.astype(np.uint8))
|
||||
|
||||
# RGB分离
|
||||
@staticmethod
|
||||
def apply_rgb_split(image: Image.Image, offset: int = 5) -> Image.Image:
|
||||
if image.mode != 'RGBA':
|
||||
image = image.convert('RGBA')
|
||||
r, g, b, a = image.split()
|
||||
r_offset = r.transform(r.size, Image.AFFINE,
|
||||
(1, 0, offset, 0, 1, 0))
|
||||
g_offset = g.transform(g.size, Image.AFFINE,
|
||||
(1, 0, 0, 0, 1, offset))
|
||||
return Image.merge('RGBA', (r_offset, g_offset, b, a))
|
||||
|
||||
# 光学补偿
|
||||
@staticmethod
|
||||
def apply_optical_compensation(image: Image.Image,
|
||||
amount: float = 100.0, reverse: bool = False) -> Image.Image:
|
||||
if image.mode != 'RGBA':
|
||||
image = image.convert('RGBA')
|
||||
|
||||
width, height = image.size
|
||||
new_image = Image.new('RGBA', (width, height))
|
||||
for x in range(width):
|
||||
for y in range(height):
|
||||
r, g, b, a = image.getpixel((x, y))
|
||||
# 计算与目标颜色的距离
|
||||
distance = math.sqrt((r - target_rgb[0]) ** 2 + (g - target_rgb[1]) ** 2 + (b - target_rgb[2]) ** 2)
|
||||
if distance <= tolerance:
|
||||
new_image.putpixel((x, y), (r, g, b, 0)) # 设置为透明
|
||||
arr = np.array(image)
|
||||
|
||||
# 中心点
|
||||
center_x, center_y = width / 2, height / 2
|
||||
|
||||
# 归一化amount
|
||||
amount_norm = amount / 100.0
|
||||
|
||||
# 创建坐标网格
|
||||
y_coords, x_coords = np.mgrid[0:height, 0:width]
|
||||
|
||||
# 计算相对中心的归一化坐标
|
||||
dx = (x_coords - center_x) / center_x
|
||||
dy = (y_coords - center_y) / center_y
|
||||
|
||||
# 计算距离(避免除零)
|
||||
distance = np.sqrt(dx**2 + dy**2)
|
||||
|
||||
# 创建掩码:中心点和其他点
|
||||
center_mask = distance == 0
|
||||
other_mask = ~center_mask
|
||||
|
||||
# 初始化缩放因子
|
||||
scale_factor = np.ones_like(distance)
|
||||
|
||||
if reverse:
|
||||
# 反鱼眼效果
|
||||
# 对于非中心点
|
||||
if other_mask.any():
|
||||
# 使用arcsin进行反鱼眼映射
|
||||
theta = np.arcsin(np.clip(distance[other_mask], 0, 0.999))
|
||||
new_distance = np.sin(theta * amount_norm)
|
||||
scale_factor[other_mask] = new_distance / distance[other_mask]
|
||||
else:
|
||||
# 鱼眼效果
|
||||
if other_mask.any():
|
||||
# 使用sin或tanh进行鱼眼映射
|
||||
theta = distance[other_mask] * amount_norm
|
||||
if amount_norm <= 1.0:
|
||||
new_distance = np.sin(theta)
|
||||
else:
|
||||
new_image.putpixel((x, y), (r, g, b, a)) # 保留原像素
|
||||
return new_image
|
||||
new_distance = np.tanh(theta)
|
||||
scale_factor[other_mask] = new_distance / distance[other_mask]
|
||||
|
||||
# 计算源坐标
|
||||
src_x = center_x + dx * center_x * scale_factor
|
||||
src_y = center_y + dy * center_y * scale_factor
|
||||
|
||||
# 裁剪坐标到有效范围
|
||||
src_x = np.clip(src_x, 0, width - 1)
|
||||
src_y = np.clip(src_y, 0, height - 1)
|
||||
|
||||
# 准备插值
|
||||
# 获取整数和小数部分
|
||||
x0 = np.floor(src_x).astype(np.int32)
|
||||
x1 = np.minimum(x0 + 1, width - 1)
|
||||
y0 = np.floor(src_y).astype(np.int32)
|
||||
y1 = np.minimum(y0 + 1, height - 1)
|
||||
|
||||
fx = src_x - x0
|
||||
fy = src_y - y0
|
||||
|
||||
# 确保索引在范围内
|
||||
x0 = np.clip(x0, 0, width - 1)
|
||||
x1 = np.clip(x1, 0, width - 1)
|
||||
y0 = np.clip(y0, 0, height - 1)
|
||||
y1 = np.clip(y1, 0, height - 1)
|
||||
|
||||
# 双线性插值 - 向量化版本
|
||||
# 获取四个角的像素值
|
||||
c00 = arr[y0, x0]
|
||||
c01 = arr[y0, x1]
|
||||
c10 = arr[y1, x0]
|
||||
c11 = arr[y1, x1]
|
||||
|
||||
# 扩展fx, fy用于3D广播
|
||||
fx_3d = fx[:, :, np.newaxis]
|
||||
fy_3d = fy[:, :, np.newaxis]
|
||||
|
||||
# 双线性插值公式
|
||||
top = c00 * (1 - fx_3d) + c01 * fx_3d
|
||||
bottom = c10 * (1 - fx_3d) + c11 * fx_3d
|
||||
result_arr = top * (1 - fy_3d) + bottom * fy_3d
|
||||
|
||||
# 转换为uint8
|
||||
result_arr = np.clip(result_arr, 0, 255).astype(np.uint8)
|
||||
|
||||
return Image.fromarray(result_arr, 'RGBA')
|
||||
|
||||
# 球面化
|
||||
@staticmethod
|
||||
def apply_spherize(image: Image.Image, strength: float = 0.5) -> Image.Image:
|
||||
if image.mode != 'RGBA':
|
||||
image = image.convert('RGBA')
|
||||
|
||||
width, height = image.size
|
||||
arr = np.array(image)
|
||||
|
||||
# 创建坐标网格
|
||||
y_coords, x_coords = np.mgrid[0:height, 0:width]
|
||||
|
||||
# 计算中心点
|
||||
center_x = width / 2
|
||||
center_y = height / 2
|
||||
|
||||
# 计算归一化坐标
|
||||
norm_x = (x_coords - center_x) / (width / 2)
|
||||
norm_y = (y_coords - center_y) / (height / 2)
|
||||
radius = np.sqrt(norm_x**2 + norm_y**2)
|
||||
|
||||
# 计算球面化偏移(向量化)
|
||||
factor = 1 + strength * (radius**2)
|
||||
new_x = (norm_x * factor) * (width / 2) + center_x
|
||||
new_y = (norm_y * factor) * (height / 2) + center_y
|
||||
|
||||
new_x = new_x.astype(np.int32)
|
||||
new_y = new_y.astype(np.int32)
|
||||
|
||||
# 创建有效坐标掩码
|
||||
valid_mask = (new_x >= 0) & (new_x < width) & (new_y >= 0) & (new_y < height)
|
||||
|
||||
# 创建结果图像(初始为透明)
|
||||
result = np.zeros((height, width, 4), dtype=np.uint8)
|
||||
|
||||
# 只复制有效像素
|
||||
if valid_mask.any():
|
||||
# 使用花式索引复制像素
|
||||
result[valid_mask] = arr[new_y[valid_mask], new_x[valid_mask]]
|
||||
|
||||
return Image.fromarray(result, 'RGBA')
|
||||
|
||||
# 平移
|
||||
@staticmethod
|
||||
def apply_translate(image: Image.Image, x_offset: int = 10, y_offset: int = 10) -> Image.Image:
|
||||
return image.transform(image.size, Image.AFFINE,
|
||||
(1, 0, x_offset, 0, 1, y_offset))
|
||||
|
||||
# 拓展边缘
|
||||
@staticmethod
|
||||
def apply_expand_edges(image: Image.Image, border_size: int = 10) -> Image.Image:
|
||||
# 拓展边缘,填充全透明
|
||||
return ImageOps.expand(image, border=border_size, fill=(0, 0, 0, 0))
|
||||
|
||||
# 旋转
|
||||
@staticmethod
|
||||
def apply_rotate(image: Image.Image, angle: float = 45) -> Image.Image:
|
||||
return image.rotate(angle, expand=True)
|
||||
|
||||
# 透视变换
|
||||
@staticmethod
|
||||
def apply_perspective_transform(image: Image.Image, coeffs: list[float]) -> Image.Image:
|
||||
return image.transform(image.size, Image.PERSPECTIVE, coeffs, Image.Resampling.BICUBIC)
|
||||
|
||||
# 裁剪
|
||||
@staticmethod
|
||||
def apply_crop(image: Image.Image, left: float = 0, upper: float = 0, right: float = 100, lower: float = 100) -> Image.Image:
|
||||
# 按百分比裁剪
|
||||
width, height = image.size
|
||||
left_px = int(width * left / 100)
|
||||
upper_px = int(height * upper / 100)
|
||||
right_px = int(width * right / 100)
|
||||
lower_px = int(height * lower / 100)
|
||||
# 如果为负数,则扩展边缘
|
||||
if left_px < 0 or upper_px < 0 or right_px > width or lower_px > height:
|
||||
border_left = max(0, -left_px)
|
||||
border_top = max(0, -upper_px)
|
||||
border_right = max(0, right_px - width)
|
||||
border_bottom = max(0, lower_px - height)
|
||||
image = ImageOps.expand(image, border=(border_left, border_top, border_right, border_bottom), fill=(0,0,0,0))
|
||||
left_px += border_left
|
||||
upper_px += border_top
|
||||
right_px += border_left
|
||||
lower_px += border_top
|
||||
return image.crop((left_px, upper_px, right_px, lower_px))
|
||||
|
||||
# 噪点
|
||||
@staticmethod
|
||||
def apply_noise(image: Image.Image, amount: float = 0.05) -> Image.Image:
|
||||
if image.mode != 'RGBA':
|
||||
image = image.convert('RGBA')
|
||||
|
||||
arr = np.array(image)
|
||||
noise = np.random.randint(0, 256, arr.shape, dtype=np.uint8)
|
||||
|
||||
# 为每个像素创建掩码,然后扩展到所有通道
|
||||
mask = np.random.rand(*arr.shape[:2]) < amount
|
||||
mask_3d = mask[:, :, np.newaxis] # 添加第三个维度
|
||||
|
||||
# 混合噪点
|
||||
result = np.where(mask_3d, noise, arr)
|
||||
|
||||
return Image.fromarray(result, 'RGBA')
|
||||
|
||||
# 素描
|
||||
@staticmethod
|
||||
def apply_sketch(image: Image.Image) -> Image.Image:
|
||||
if image.mode != 'RGBA':
|
||||
image = image.convert('RGBA')
|
||||
|
||||
# 转为灰度图
|
||||
gray_image = image.convert('L')
|
||||
|
||||
# 反相
|
||||
inverted_image = ImageChops.invert(gray_image)
|
||||
|
||||
# 高斯模糊
|
||||
blurred_image = inverted_image.filter(ImageFilter.GaussianBlur(radius=10))
|
||||
|
||||
# 混合
|
||||
def dodge(front, back):
|
||||
result = front * 255 / (255 - back)
|
||||
result[result > 255] = 255
|
||||
result[back == 255] = 255
|
||||
return result.astype(np.uint8)
|
||||
|
||||
gray_arr = np.array(gray_image, dtype=np.float32)
|
||||
blurred_arr = np.array(blurred_image, dtype=np.float32)
|
||||
|
||||
sketch_arr = dodge(gray_arr, blurred_arr)
|
||||
|
||||
# 创建结果图像,保留原始 alpha 通道
|
||||
alpha_channel = np.array(image.getchannel('A'))
|
||||
result_arr = np.zeros((image.height, image.width, 4), dtype=np.uint8)
|
||||
result_arr[:, :, 0] = sketch_arr
|
||||
result_arr[:, :, 1] = sketch_arr
|
||||
result_arr[:, :, 2] = sketch_arr
|
||||
result_arr[:, :, 3] = alpha_channel
|
||||
|
||||
return Image.fromarray(result_arr, 'RGBA')
|
||||
|
||||
# 两张图像混合,可指定叠加模式
|
||||
@staticmethod
|
||||
def apply_blend(image1: Image.Image, image2: Image.Image, mode: str = 'normal', alpha: float = 0.5) -> Image.Image:
|
||||
if image1.mode != 'RGBA':
|
||||
image1 = image1.convert('RGBA')
|
||||
if image2.mode != 'RGBA':
|
||||
image2 = image2.convert('RGBA')
|
||||
|
||||
image2 = image2.resize(image1.size, Image.Resampling.LANCZOS)
|
||||
|
||||
arr1 = np.array(image1, dtype=np.float32)
|
||||
arr2 = np.array(image2, dtype=np.float32)
|
||||
|
||||
if mode == 'normal':
|
||||
blended = arr1 * (1 - alpha) + arr2 * alpha
|
||||
elif mode == 'multiply':
|
||||
blended = (arr1 / 255.0) * (arr2 / 255.0) * 255.0
|
||||
elif mode == 'screen':
|
||||
blended = 255 - (1 - arr1 / 255.0) * (1 - arr2 / 255.0) * 255.0
|
||||
elif mode == 'overlay':
|
||||
mask = arr1 < 128
|
||||
blended = np.zeros_like(arr1)
|
||||
blended[mask] = (2 * (arr1[mask] / 255.0) * (arr2[mask] / 255.0)) * 255.0
|
||||
blended[~mask] = (1 - 2 * (1 - arr1[~mask] / 255.0) * (1 - arr2[~mask] / 255.0)) * 255.0
|
||||
else:
|
||||
blended = arr1
|
||||
|
||||
blended = np.clip(blended, 0, 255).astype(np.uint8)
|
||||
|
||||
return Image.fromarray(blended, 'RGBA')
|
||||
|
||||
# 叠加渐变色
|
||||
@staticmethod
|
||||
def apply_gradient_overlay(
|
||||
image: Image.Image,
|
||||
color_list: str = '[rgb(255,0,0)|(0,0),rgb(0,255,0)|(0,100),rgb(0,0,255)|(50,100)]',
|
||||
overlay_mode: str = 'overlay',
|
||||
) -> Image.Image:
|
||||
gradient_gen = GradientGenerator()
|
||||
color_nodes = gradient_gen.parse_color_list(color_list)
|
||||
gradient = gradient_gen.create_gradient(image.size[0], image.size[1], color_nodes)
|
||||
return ImageFilterImplement.apply_blend(image, gradient, mode=overlay_mode, alpha=0.5)
|
||||
|
||||
# 阴影
|
||||
@staticmethod
|
||||
def apply_shadow(image: Image.Image,
|
||||
x_offset: int = 10,
|
||||
y_offset: int = 10,
|
||||
blur = 10,
|
||||
opacity = 0.5,
|
||||
shadow_color = "black") -> Image.Image:
|
||||
if image.mode != 'RGBA':
|
||||
image = image.convert('RGBA')
|
||||
offset = (x_offset, y_offset)
|
||||
# 创建阴影图层
|
||||
shadow = Image.new('RGBA', image.size, (0,0,0,0))
|
||||
shadow_rgb = ColorHandle.parse_color(shadow_color)
|
||||
shadow_draw = Image.new('RGBA', image.size, shadow_rgb + (0,))
|
||||
alpha = image.split()[3].point(lambda p: int(p * opacity))
|
||||
shadow.paste(shadow_draw, (0,0), alpha)
|
||||
shadow = shadow.filter(ImageFilter.GaussianBlur(blur))
|
||||
# 创建结果图像
|
||||
result = Image.new('RGBA', (image.width + abs(offset[0]), image.height + abs(offset[1])), (0,0,0,0))
|
||||
shadow_position = (max(offset[0],0), max(offset[1],0))
|
||||
image_position = (max(-offset[0],0), max(-offset[1],0))
|
||||
result.paste(shadow, shadow_position, shadow)
|
||||
result.paste(image, image_position, image)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
Reference in New Issue
Block a user