最新最热模糊

This commit is contained in:
2025-12-03 13:10:16 +08:00
parent f7d2168dac
commit 2de3be271e
3 changed files with 335 additions and 2 deletions

View File

@ -19,8 +19,6 @@ fx [滤镜名称] <参数1> <参数2> ...
## 可用滤镜列表
### 基础滤镜
* ```fx 模糊 <半径=10>```
* ```fx 马赛克 <像素大小=10>```
* ```fx 轮廓```
* ```fx 锐化```
* ```fx 边缘增强```
@ -33,6 +31,15 @@ fx [滤镜名称] <参数1> <参数2> ...
* ```fx 素描```
* ```fx 阴影 <x偏移量=10> <y偏移量=10> <模糊量=10> <不透明度=0.5> <阴影颜色=black>```
### 模糊滤镜
* ```fx 模糊 <半径=10>```
* ```fx 马赛克 <像素大小=10>```
* ```fx 径向模糊 <强度=3.0> <采样量=6>```
* ```fx 旋转模糊 <强度=30.0> <采样量=6>```
* ```fx 方向模糊 <角度=0.0> <距离=20> <采样量=6>```
* ```fx 缩放模糊 <强度=0.1> <采样量=6>```
* ```fx 边缘模糊 <半径=10.0>```
### 色彩处理滤镜
* ```fx 反色```
* ```fx 黑白```

View File

@ -566,4 +566,325 @@ class ImageFilterImplement:
result.paste(image, image_position, image)
return result
@staticmethod
def apply_radial_blur(image: Image.Image, strength: float = 3.0, samples: int = 6) -> Image.Image:
"""
快速径向模糊 - 使用预计算网格和向量化
"""
if image.mode != 'RGBA':
image = image.convert('RGBA')
width, height = image.size
arr = np.array(image, dtype=np.uint8)
# 转换为float32并归一化
arr_float = arr.astype(np.float32) / 255.0
# 计算中心点
center_x = width / 2
center_y = height / 2
# 预计算坐标网格(只计算一次)
x_indices = np.arange(width, dtype=np.float32)
y_indices = np.arange(height, dtype=np.float32)
x_grid, y_grid = np.meshgrid(x_indices, y_indices)
# 预计算相对坐标和距离
dx = x_grid - center_x
dy = y_grid - center_y
distance = np.sqrt(dx*dx + dy*dy + 1e-6) # 避免除零
max_dist = np.max(distance)
# 生成采样强度
if samples > 1:
strengths = np.linspace(-strength/2, strength/2, samples) / 100
else:
strengths = np.array([0])
# 初始化结果
result = np.zeros_like(arr_float)
for s in strengths:
# 计算缩放因子
scale = 1.0 + s * distance / max_dist
# 计算变形坐标
new_x = np.clip(center_x + dx * scale, 0, width - 1)
new_y = np.clip(center_y + dy * scale, 0, height - 1)
# 快速双线性插值
x0 = np.floor(new_x).astype(np.int32)
x1 = np.minimum(x0 + 1, width - 1)
y0 = np.floor(new_y).astype(np.int32)
y1 = np.minimum(y0 + 1, height - 1)
# 计算权重
wx = new_x - x0
wy = new_y - y0
w00 = (1 - wx) * (1 - wy)
w01 = wx * (1 - wy)
w10 = (1 - wx) * wy
w11 = wx * wy
# 向量化插值所有通道
for c in range(4):
result[:, :, c] += (
arr_float[y0, x0, c] * w00 +
arr_float[y0, x1, c] * w01 +
arr_float[y1, x0, c] * w10 +
arr_float[y1, x1, c] * w11
)
# 平均并转换
result /= len(strengths)
result = np.clip(result * 255, 0, 255).astype(np.uint8)
return Image.fromarray(result, 'RGBA')
@staticmethod
def apply_spin_blur(image: Image.Image, strength: float = 30.0, samples: int = 6) -> Image.Image:
"""
快速旋转模糊
"""
if image.mode != 'RGBA':
image = image.convert('RGBA')
width, height = image.size
arr = np.array(image, dtype=np.uint8)
arr_float = arr.astype(np.float32) / 255.0
# 计算中心点
center_x = width / 2
center_y = height / 2
# 预计算坐标网格
x_indices = np.arange(width, dtype=np.float32)
y_indices = np.arange(height, dtype=np.float32)
x_grid, y_grid = np.meshgrid(x_indices, y_indices)
# 预计算相对坐标
dx = x_grid - center_x
dy = y_grid - center_y
# 生成角度采样
if samples > 1:
angles = np.linspace(-strength/2, strength/2, samples) * np.pi / 180
else:
angles = np.array([0])
result = np.zeros_like(arr_float)
for angle in angles:
# 预计算三角函数值
cos_a = math.cos(angle)
sin_a = math.sin(angle)
# 计算旋转坐标
new_x = center_x + dx * cos_a - dy * sin_a
new_y = center_y + dx * sin_a + dy * cos_a
# 边界裁剪
new_x = np.clip(new_x, 0, width - 1)
new_y = np.clip(new_y, 0, height - 1)
# 快速双线性插值
x0 = np.floor(new_x).astype(np.int32)
x1 = np.minimum(x0 + 1, width - 1)
y0 = np.floor(new_y).astype(np.int32)
y1 = np.minimum(y0 + 1, height - 1)
wx = new_x - x0
wy = new_y - y0
w00 = (1 - wx) * (1 - wy)
w01 = wx * (1 - wy)
w10 = (1 - wx) * wy
w11 = wx * wy
# 向量化插值
for c in range(4):
result[:, :, c] += (
arr_float[y0, x0, c] * w00 +
arr_float[y0, x1, c] * w01 +
arr_float[y1, x0, c] * w10 +
arr_float[y1, x1, c] * w11
)
result /= len(angles)
result = np.clip(result * 255, 0, 255).astype(np.uint8)
return Image.fromarray(result, 'RGBA')
@staticmethod
def apply_directional_blur(image: Image.Image, angle: float = 0.0,
distance: int = 20, samples: int = 6) -> Image.Image:
"""
快速方向模糊 - 使用累积缓冲区技术
"""
if image.mode != 'RGBA':
image = image.convert('RGBA')
width, height = image.size
arr = np.array(image, dtype=np.uint8)
arr_float = arr.astype(np.float32) / 255.0
# 计算角度和步长
rad = math.radians(angle)
cos_a = math.cos(rad)
sin_a = math.sin(rad)
# 生成偏移位置
if samples > 1:
offsets = np.linspace(-distance/2, distance/2, samples)
else:
offsets = np.array([0])
result = np.zeros_like(arr_float)
for offset in offsets:
# 计算偏移量
shift_x = offset * cos_a
shift_y = offset * sin_a
# 预计算变形坐标
x_indices = np.arange(width, dtype=np.float32)
y_indices = np.arange(height, dtype=np.float32)
x_grid, y_grid = np.meshgrid(x_indices, y_indices)
new_x = x_grid - shift_x
new_y = y_grid - shift_y
# 边界裁剪
new_x = np.clip(new_x, 0, width - 1)
new_y = np.clip(new_y, 0, height - 1)
# 快速双线性插值
x0 = np.floor(new_x).astype(np.int32)
x1 = np.minimum(x0 + 1, width - 1)
y0 = np.floor(new_y).astype(np.int32)
y1 = np.minimum(y0 + 1, height - 1)
wx = new_x - x0
wy = new_y - y0
w00 = (1 - wx) * (1 - wy)
w01 = wx * (1 - wy)
w10 = (1 - wx) * wy
w11 = wx * wy
# 向量化插值
for c in range(4):
result[:, :, c] += (
arr_float[y0, x0, c] * w00 +
arr_float[y0, x1, c] * w01 +
arr_float[y1, x0, c] * w10 +
arr_float[y1, x1, c] * w11
)
result /= len(offsets)
result = np.clip(result * 255, 0, 255).astype(np.uint8)
return Image.fromarray(result, 'RGBA')
@staticmethod
def apply_zoom_blur(image: Image.Image, strength: float = 0.1, samples: int = 6) -> Image.Image:
"""
快速缩放模糊
"""
if image.mode != 'RGBA':
image = image.convert('RGBA')
width, height = image.size
arr = np.array(image, dtype=np.uint8)
arr_float = arr.astype(np.float32) / 255.0
# 计算中心点
center_x = width / 2
center_y = height / 2
# 预计算坐标网格
x_indices = np.arange(width, dtype=np.float32)
y_indices = np.arange(height, dtype=np.float32)
x_grid, y_grid = np.meshgrid(x_indices, y_indices)
# 预计算相对坐标
dx = x_grid - center_x
dy = y_grid - center_y
# 生成缩放因子
if samples > 1:
scales = np.linspace(1 - strength, 1 + strength, samples)
else:
scales = np.array([1.0])
result = np.zeros_like(arr_float)
for scale in scales:
# 计算缩放坐标
new_x = center_x + dx / scale
new_y = center_y + dy / scale
# 边界裁剪
new_x = np.clip(new_x, 0, width - 1)
new_y = np.clip(new_y, 0, height - 1)
# 快速双线性插值
x0 = np.floor(new_x).astype(np.int32)
x1 = np.minimum(x0 + 1, width - 1)
y0 = np.floor(new_y).astype(np.int32)
y1 = np.minimum(y0 + 1, height - 1)
wx = new_x - x0
wy = new_y - y0
w00 = (1 - wx) * (1 - wy)
w01 = wx * (1 - wy)
w10 = (1 - wx) * wy
w11 = wx * wy
# 向量化插值
for c in range(4):
result[:, :, c] += (
arr_float[y0, x0, c] * w00 +
arr_float[y0, x1, c] * w01 +
arr_float[y1, x0, c] * w10 +
arr_float[y1, x1, c] * w11
)
result /= len(scales)
result = np.clip(result * 255, 0, 255).astype(np.uint8)
return Image.fromarray(result, 'RGBA')
# 中心清晰,边缘模糊
@staticmethod
def apply_focus_blur(image: Image.Image, radius: float = 10.0) -> 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)
distance = np.sqrt(norm_x**2 + norm_y**2)
# 创建模糊图像
blurred_image = image.filter(ImageFilter.GaussianBlur(radius))
blurred_arr = np.array(blurred_image)
# 创建结果图像(向量化)
result = np.zeros_like(arr, dtype=np.float32)
# 根据距离混合原图和模糊图
for c in range(4): # 对每个通道
result[:, :, c] = arr[:, :, c] * (1 - distance) + blurred_arr[:, :, c] * distance
return Image.fromarray(np.clip(result, 0, 255).astype(np.uint8), 'RGBA')

View File

@ -35,6 +35,11 @@ class ImageFilterManager:
"素描": ImageFilterImplement.apply_sketch,
"叠加颜色": ImageFilterImplement.apply_gradient_overlay,
"阴影": ImageFilterImplement.apply_shadow,
"径向模糊": ImageFilterImplement.apply_radial_blur,
"旋转模糊": ImageFilterImplement.apply_spin_blur,
"方向模糊": ImageFilterImplement.apply_directional_blur,
"边缘模糊": ImageFilterImplement.apply_focus_blur,
"缩放模糊": ImageFilterImplement.apply_zoom_blur,
}
@classmethod