This commit is contained in:
@ -1,3 +1,4 @@
|
||||
import random
|
||||
from PIL import Image, ImageFilter
|
||||
from PIL import ImageEnhance
|
||||
from PIL import ImageChops
|
||||
@ -137,9 +138,22 @@ class ImageFilterImplement:
|
||||
|
||||
# 缩放
|
||||
@staticmethod
|
||||
def apply_resize(image: Image.Image, scale: float = 1.5) -> Image.Image:
|
||||
if scale <= 0:
|
||||
scale = 1.0
|
||||
def apply_resize(image: Image.Image, scale: float = 1.5, scale_y = None) -> Image.Image:
|
||||
# scale 可以为负
|
||||
# 如果 scale 为负,则代表翻转
|
||||
if scale_y is not None:
|
||||
if float(scale_y) < 0:
|
||||
image = ImageOps.flip(image)
|
||||
scale_y = abs(float(scale_y))
|
||||
if scale < 0:
|
||||
image = ImageOps.mirror(image)
|
||||
scale = abs(scale)
|
||||
new_size = (int(image.width * scale), int(image.height * float(scale_y)))
|
||||
return image.resize(new_size, Image.Resampling.LANCZOS)
|
||||
if scale < 0:
|
||||
image = ImageOps.mirror(image)
|
||||
image = ImageOps.flip(image)
|
||||
scale = abs(scale)
|
||||
new_size = (int(image.width * scale), int(image.height * scale))
|
||||
return image.resize(new_size, Image.Resampling.LANCZOS)
|
||||
|
||||
@ -224,7 +238,7 @@ class ImageFilterImplement:
|
||||
|
||||
# 发光
|
||||
@staticmethod
|
||||
def apply_glow(image: Image.Image, intensity: float = 1.5, blur_radius: float = 15) -> Image.Image:
|
||||
def apply_glow(image: Image.Image, intensity: float = 0.5, blur_radius: float = 15) -> Image.Image:
|
||||
if image.mode != 'RGBA':
|
||||
image = image.convert('RGBA')
|
||||
# 创建发光图层
|
||||
@ -423,6 +437,8 @@ class ImageFilterImplement:
|
||||
# 裁剪
|
||||
@staticmethod
|
||||
def apply_crop(image: Image.Image, left: float = 0, upper: float = 0, right: float = 100, lower: float = 100) -> Image.Image:
|
||||
if image.mode != 'RGBA':
|
||||
image = image.convert('RGBA')
|
||||
# 按百分比裁剪
|
||||
width, height = image.size
|
||||
left_px = int(width * left / 100)
|
||||
@ -451,9 +467,10 @@ class ImageFilterImplement:
|
||||
arr = np.array(image)
|
||||
noise = np.random.randint(0, 256, arr.shape, dtype=np.uint8)
|
||||
|
||||
# 为每个像素创建掩码,然后扩展到所有通道
|
||||
# 为每个像素创建掩码,然后扩展到所有通道,除了 alpha 通道
|
||||
mask = np.random.rand(*arr.shape[:2]) < amount
|
||||
mask_3d = mask[:, :, np.newaxis] # 添加第三个维度
|
||||
mask_3d = np.repeat(mask[:, :, np.newaxis], 4, axis=2)
|
||||
mask_3d[:, :, 3] = False # 保持 alpha 通道不变
|
||||
|
||||
# 混合噪点
|
||||
result = np.where(mask_3d, noise, arr)
|
||||
@ -887,4 +904,184 @@ class ImageFilterImplement:
|
||||
result[:, :, c] = arr[:, :, c] * (1 - distance) + blurred_arr[:, :, c] * distance
|
||||
|
||||
return Image.fromarray(np.clip(result, 0, 255).astype(np.uint8), 'RGBA')
|
||||
|
||||
# 复制画面
|
||||
@staticmethod
|
||||
def copy_area(image: Image.Image,
|
||||
dst_move: str = '(100,100)',
|
||||
scale: float = 1.0,
|
||||
src_area: str = '(0,0,100,100)') -> Image.Image:
|
||||
if image.mode != 'RGBA':
|
||||
image = image.convert('RGBA')
|
||||
result = image.copy()
|
||||
# 参数表示的是百分比
|
||||
width, height = image.size
|
||||
# 首先裁剪出源区域
|
||||
src_tuple = eval(src_area)
|
||||
src_area = ImageFilterImplement.apply_crop(image, src_tuple[0], src_tuple[1], src_tuple[2], src_tuple[3])
|
||||
# 计算目标位置
|
||||
dst_tuple = eval(dst_move)
|
||||
dst_x = int(width * dst_tuple[0] / 100)
|
||||
dst_y = int(height * dst_tuple[1] / 100)
|
||||
# 缩放源区域
|
||||
if scale != 1.0:
|
||||
new_width = int(src_area.width * scale)
|
||||
new_height = int(src_area.height * scale)
|
||||
src_area = src_area.resize((new_width, new_height), Image.Resampling.LANCZOS)
|
||||
# 粘贴到目标位置
|
||||
result.paste(src_area, (dst_x, dst_y), src_area)
|
||||
return result
|
||||
|
||||
# 水平翻转
|
||||
@staticmethod
|
||||
def apply_flip_horizontal(image: Image.Image) -> Image.Image:
|
||||
return image.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
|
||||
|
||||
# 垂直翻转
|
||||
@staticmethod
|
||||
def apply_flip_vertical(image: Image.Image) -> Image.Image:
|
||||
return image.transpose(Image.Transpose.FLIP_TOP_BOTTOM)
|
||||
|
||||
# 依方向镜像一半画面
|
||||
@staticmethod
|
||||
def apply_mirror_half(image: Image.Image, angle: float = 90) -> Image.Image:
|
||||
if image.mode != 'RGBA':
|
||||
image = image.convert('RGBA')
|
||||
|
||||
width, height = image.size
|
||||
arr = np.array(image, dtype=np.uint8)
|
||||
|
||||
# 角度标准化
|
||||
angle = angle % 360
|
||||
|
||||
# 计算折痕的斜率
|
||||
# 注意:我们的角度是折痕与水平线的夹角
|
||||
# 0° = 垂直线,90° = 水平线
|
||||
rad = math.radians(angle)
|
||||
|
||||
# 计算中心点
|
||||
center_x = width / 2
|
||||
center_y = height / 2
|
||||
|
||||
# 创建坐标网格
|
||||
y_coords, x_coords = np.mgrid[0:height, 0:width]
|
||||
|
||||
# 计算折痕的方向向量
|
||||
# 折痕方向垂直于法向量
|
||||
# 对于角度θ,折痕方向向量为(cosθ, sinθ)
|
||||
direction_x = math.cos(rad)
|
||||
direction_y = math.sin(rad)
|
||||
|
||||
# 计算法向量(垂直于折痕)
|
||||
# 旋转90度:(-sinθ, cosθ)
|
||||
normal_x = -direction_y
|
||||
normal_y = direction_x
|
||||
|
||||
# 计算每个像素相对于折痕的位置
|
||||
# 点到直线的有向距离: (x-cx)*nx + (y-cy)*ny
|
||||
rel_x = x_coords - center_x
|
||||
rel_y = y_coords - center_y
|
||||
signed_distance = rel_x * normal_x + rel_y * normal_y
|
||||
|
||||
source_mask = signed_distance >= 0
|
||||
|
||||
# 创建结果图像
|
||||
result = arr.copy()
|
||||
|
||||
# 获取镜像侧的像素(需要被替换的像素)
|
||||
mirror_mask = ~source_mask
|
||||
|
||||
if mirror_mask.any():
|
||||
# 对于镜像侧的每个像素,计算其关于折痕的镜像点
|
||||
mirror_x = x_coords[mirror_mask]
|
||||
mirror_y = y_coords[mirror_mask]
|
||||
|
||||
# 计算相对坐标
|
||||
mirror_rel_x = mirror_x - center_x
|
||||
mirror_rel_y = mirror_y - center_y
|
||||
|
||||
# 计算到折痕的距离(带符号)
|
||||
dist_to_line = signed_distance[mirror_mask]
|
||||
|
||||
# 计算镜像点的相对坐标
|
||||
# 镜像公式: p' = p - 2 * (p·n) * n
|
||||
# 其中p是相对中心点的向量,n是单位法向量
|
||||
mirror_target_rel_x = mirror_rel_x - 2 * dist_to_line * normal_x
|
||||
mirror_target_rel_y = mirror_rel_y - 2 * dist_to_line * normal_y
|
||||
|
||||
# 计算镜像点的绝对坐标
|
||||
target_x = np.round(center_x + mirror_target_rel_x).astype(int)
|
||||
target_y = np.round(center_y + mirror_target_rel_y).astype(int)
|
||||
|
||||
# 确保坐标在图像范围内
|
||||
valid = (target_x >= 0) & (target_x < width) & (target_y >= 0) & (target_y < height)
|
||||
|
||||
if valid.any():
|
||||
# 将源侧的像素复制到镜像侧的位置
|
||||
valid_target_x = target_x[valid]
|
||||
valid_target_y = target_y[valid]
|
||||
valid_mirror_x = mirror_x[valid]
|
||||
valid_mirror_y = mirror_y[valid]
|
||||
|
||||
# 获取源侧的像素值
|
||||
source_pixels = arr[valid_target_y, valid_target_x]
|
||||
|
||||
# 复制到镜像位置
|
||||
result[valid_mirror_y, valid_mirror_x] = source_pixels
|
||||
|
||||
return Image.fromarray(result, 'RGBA')
|
||||
|
||||
# 随机画面整体晃动,对于动图会很有效
|
||||
@staticmethod
|
||||
def apply_random_wiggle(image: Image.Image, max_offset: int = 5, motion_blur = False) -> Image.Image:
|
||||
if image.mode != 'RGBA':
|
||||
image = image.convert('RGBA')
|
||||
|
||||
width, height = image.size
|
||||
arr = np.array(image)
|
||||
# 生成随机偏移
|
||||
x_offset = random.randint(-max_offset, max_offset)
|
||||
y_offset = random.randint(-max_offset, max_offset)
|
||||
# 画面平移
|
||||
translated_image = ImageFilterImplement.apply_translate(image, x_offset, y_offset)
|
||||
if motion_blur:
|
||||
# 添加运动模糊
|
||||
translated_image = ImageFilterImplement.apply_directional_blur(translated_image,
|
||||
angle=random.uniform(0, 360),
|
||||
distance=max_offset,
|
||||
samples=6)
|
||||
return translated_image
|
||||
|
||||
# 像素随机抖动
|
||||
@staticmethod
|
||||
def apply_pixel_jitter(image: Image.Image, max_offset: int = 2) -> Image.Image:
|
||||
if image.mode != 'RGBA':
|
||||
image = image.convert('RGBA')
|
||||
|
||||
width, height = image.size
|
||||
arr = np.array(image)
|
||||
|
||||
# 创建结果图像
|
||||
result = np.zeros_like(arr)
|
||||
|
||||
# numpy 向量化随机偏移
|
||||
x_indices = np.arange(width)
|
||||
y_indices = np.arange(height)
|
||||
x_grid, y_grid = np.meshgrid(x_indices, y_indices)
|
||||
x_offsets = np.random.randint(-max_offset, max_offset + 1, size=(height, width))
|
||||
y_offsets = np.random.randint(-max_offset, max_offset + 1, size=(height, width))
|
||||
new_x = np.clip(x_grid + x_offsets, 0, width - 1)
|
||||
new_y = np.clip(y_grid + y_offsets, 0, height - 1)
|
||||
result[y_grid, x_grid] = arr[new_y, new_x]
|
||||
|
||||
return Image.fromarray(result, 'RGBA')
|
||||
|
||||
class ImageFilterEmpty:
|
||||
# 空滤镜,不做任何处理,形式化参数用
|
||||
@staticmethod
|
||||
def empty_filter(image):
|
||||
return image
|
||||
|
||||
@staticmethod
|
||||
def empty_filter_param(image, param = 10):
|
||||
return image
|
||||
|
||||
Reference in New Issue
Block a user