feat(fx): 添加像素排序 (Pixel Sort) 滤镜

- 新增 '像素排序' 滤镜,实现类似 Photoshop Pixel Sort 效果
- 支持水平/垂直方向排序
- 支持多种排序依据:亮度、色相、红/绿/蓝通道
- 支持自动阈值计算(使用图像亮度中位数)
- 支持自定义遮罩阈值
- 支持反向排序
- 支持块大小参数
- 添加相关单元测试
This commit is contained in:
2026-04-08 14:20:46 +08:00
parent c2161635a8
commit cd010afc24
3 changed files with 200 additions and 0 deletions

View File

@ -1354,6 +1354,140 @@ class ImageFilterImplement:
images.append(text_image)
return image
# Pixel Sort - 像素排序效果
@staticmethod
def apply_pixel_sort(
image: Image.Image,
direction: str = "horizontal",
threshold: float = 0.0,
auto_threshold: bool = True,
sort_by: str = "brightness",
mask_threshold: float = 128.0,
reverse: bool = False,
block_size: int = 1
) -> Image.Image:
"""
Pixel Sort 效果
参数:
image: 输入图像
direction: 排序方向,"horizontal"(水平) 或 "vertical"(垂直)
threshold: 亮度阈值 (0-255),低于此值的像素会被排序(仅在 auto_threshold=False 时生效)
auto_threshold: 是否自动计算阈值(使用图像中位数)
sort_by: 排序依据,"brightness"(亮度)、"hue"(色相)、"red""green""blue"
mask_threshold: 遮罩阈值 (0-255),决定哪些像素参与排序
reverse: 是否反向排序
block_size: 块大小,每 N 行/列作为一个整体排序单位
"""
if image.mode != 'RGBA':
image = image.convert('RGBA')
arr = np.array(image)
height, width = arr.shape[:2]
# 获取排序属性
def get_sort_value(pixel):
r, g, b = pixel[0], pixel[1], pixel[2]
if sort_by == "brightness":
return 0.299 * r + 0.587 * g + 0.114 * b
elif sort_by == "hue":
max_c = max(r, g, b)
min_c = min(r, g, b)
diff = max_c - min_c
if diff == 0:
return 0
if max_c == r:
return 60 * (((g - b) / diff) % 6)
elif max_c == g:
return 60 * ((b - r) / diff + 2)
else:
return 60 * ((r - g) / diff + 4)
elif sort_by == "red":
return r
elif sort_by == "green":
return g
elif sort_by == "blue":
return b
return 0.299 * r + 0.587 * g + 0.114 * b
# 自动计算阈值
if auto_threshold:
# 使用图像亮度中位数作为阈值
gray = np.array(image.convert('L'))
mask_threshold = float(np.median(gray))
# 创建遮罩:哪些像素需要排序
mask = np.zeros((height, width), dtype=bool)
for y in range(height):
for x in range(width):
brightness = 0.299 * arr[y, x, 0] + 0.587 * arr[y, x, 1] + 0.114 * arr[y, x, 2]
mask[y, x] = brightness >= mask_threshold
result = arr.copy()
if direction.lower() in ["horizontal", "h", "水平"]:
# 水平排序(逐行)
for y in range(height):
# 收集当前行中需要排序的像素
if block_size > 1:
# 按块处理
for block_start in range(0, width, block_size):
block_end = min(block_start + block_size, width)
pixels = []
indices = []
for x in range(block_start, block_end):
if mask[y, x]:
pixels.append(arr[y, x].copy())
indices.append(x)
if len(pixels) > 1:
# 按指定属性排序
sorted_pixels = sorted(pixels, key=get_sort_value, reverse=reverse)
for i, x in enumerate(indices):
result[y, x] = sorted_pixels[i]
else:
# 逐像素处理
pixels = []
indices = []
for x in range(width):
if mask[y, x]:
pixels.append(arr[y, x].copy())
indices.append(x)
if len(pixels) > 1:
sorted_pixels = sorted(pixels, key=get_sort_value, reverse=reverse)
for i, x in enumerate(indices):
result[y, x] = sorted_pixels[i]
elif direction.lower() in ["vertical", "v", "垂直"]:
# 垂直排序(逐列)
for x in range(width):
if block_size > 1:
# 按块处理
for block_start in range(0, height, block_size):
block_end = min(block_start + block_size, height)
pixels = []
indices = []
for y in range(block_start, block_end):
if mask[y, x]:
pixels.append(arr[y, x].copy())
indices.append(y)
if len(pixels) > 1:
sorted_pixels = sorted(pixels, key=get_sort_value, reverse=reverse)
for i, y in enumerate(indices):
result[y, x] = sorted_pixels[i]
else:
pixels = []
indices = []
for y in range(height):
if mask[y, x]:
pixels.append(arr[y, x].copy())
indices.append(y)
if len(pixels) > 1:
sorted_pixels = sorted(pixels, key=get_sort_value, reverse=reverse)
for i, y in enumerate(indices):
result[y, x] = sorted_pixels[i]
return Image.fromarray(result, 'RGBA')
class ImageFilterEmpty: