This commit is contained in:
2025-12-10 17:17:52 +08:00
parent de04fcbec1
commit 9904653cc6
3 changed files with 92 additions and 1 deletions

View File

@ -51,6 +51,8 @@ fx [滤镜名称] <参数1> <参数2> ...
* ```fx RGB分离 <偏移量=5>```
* ```fx 叠加颜色 <颜色列表=[rgb(255,0,0)|(0,0)+rgb(0,255,0)|(0,100)+rgb(0,0,255)|(50,100)]> <叠加模式=overlay>```
* ```fx 像素抖动 <最大偏移量=2>```
* ```fx 半调 <半径=5>```
* ```fx 描边 <半径=5> <颜色=black>```
### 几何变换滤镜
* ```fx 平移 <x偏移量=10> <y偏移量=10>```

View File

@ -1,5 +1,5 @@
import random
from PIL import Image, ImageFilter
from PIL import Image, ImageFilter, ImageDraw, ImageStat
from PIL import ImageEnhance
from PIL import ImageChops
from PIL import ImageOps
@ -412,6 +412,13 @@ class ImageFilterImplement:
if valid_mask.any():
# 使用花式索引复制像素
result[valid_mask] = arr[new_y[valid_mask], new_x[valid_mask]]
# 计算裁剪边界
ys, xs = np.where(valid_mask)
min_y, max_y = ys.min(), ys.max() + 1
min_x, max_x = xs.min(), xs.max() + 1
# 裁剪结果图像
result = result[min_y:max_y, min_x:max_x]
return Image.fromarray(result, 'RGBA')
@ -1103,6 +1110,86 @@ class ImageFilterImplement:
return Image.fromarray(result, 'RGBA')
# 描边
@staticmethod
def apply_stroke(image: Image.Image, stroke_width: int = 5, stroke_color: str = 'black') -> Image.Image:
if image.mode != 'RGBA':
image = image.convert('RGBA')
# 基于图像的 alpha 通道创建描边
alpha = image.split()[3]
# 创建描边图像
stroke_image = Image.new('RGBA', image.size, (0, 0, 0, 0))
# 根据 alpha 通道的值,以每个像素为中心,扩大描边区域
expanded_alpha = alpha.filter(ImageFilter.MaxFilter(size=stroke_width*2+1))
draw = Image.new('RGBA', image.size, ColorHandle.parse_color(stroke_color) + (255,))
stroke_image.paste(draw, (0, 0), expanded_alpha)
# 将描边和原图合并
result = Image.alpha_composite(stroke_image, image)
return result
# 半调
@staticmethod
def apply_halftone(image: Image.Image, dot_size: int = 5) -> Image.Image:
if image.mode != 'L':
grayscale = image.convert('L')
else:
grayscale = image.copy()
# 获取图像尺寸
width, height = grayscale.size
# 计算网格数量
grid_width = math.ceil(width / dot_size)
grid_height = math.ceil(height / dot_size)
# 创建输出图像(白色背景)
output = Image.new('RGB', (width, height), 'white')
draw = ImageDraw.Draw(output)
# 遍历每个网格单元
for gy in range(grid_height):
for gx in range(grid_width):
# 计算当前网格的边界
left = gx * dot_size
top = gy * dot_size
right = min(left + dot_size, width)
bottom = min(top + dot_size, height)
# 获取当前网格的区域
grid_region = grayscale.crop((left, top, right, bottom))
# 计算网格内像素的平均亮度0-255
stat = ImageStat.Stat(grid_region)
avg_brightness = stat.mean[0]
# 将亮度转换为网点半径
# 亮度越高(越白),网点越小;亮度越低(越黑),网点越大
max_radius = dot_size / 2
radius = max_radius * (1 - avg_brightness / 255)
# 如果半径太小,则不绘制网点
if radius > 0.5:
# 计算网点中心坐标
center_x = left + (right - left) / 2
center_y = top + (bottom - top) / 2
# 绘制黑色网点
draw.ellipse([
center_x - radius + 0.5,
center_y - radius + 0.5,
center_x + radius + 0.5,
center_y + radius + 0.5
], fill='black')
# 叠加 alpha 通道
if image.mode == 'RGBA':
alpha = image.split()[3]
output.putalpha(alpha)
return output
class ImageFilterEmpty:
# 空滤镜,不做任何处理,形式化参数用
@staticmethod

View File

@ -47,6 +47,8 @@ class ImageFilterManager:
"晃动": ImageFilterImplement.apply_random_wiggle,
"动图": ImageFilterEmpty.empty_filter_param,
"像素抖动": ImageFilterImplement.apply_pixel_jitter,
"描边": ImageFilterImplement.apply_stroke,
"半调": ImageFilterImplement.apply_halftone,
# 图像处理
"存入图像": ImageFilterStorage.store_image,
"读取图像": ImageFilterStorage.load_image,