forked from mttu-developers/konabot
feat: add jpeg damage filter to fx
This commit is contained in:
@ -76,6 +76,8 @@ fx [滤镜名称] <参数1> <参数2> ...
|
|||||||
* ```fx 设置遮罩```
|
* ```fx 设置遮罩```
|
||||||
* ```fx 色键 <目标颜色="rgb(255,0,0)"> <容差=60>```
|
* ```fx 色键 <目标颜色="rgb(255,0,0)"> <容差=60>```
|
||||||
* ```fx 晃动 <最大偏移量=5> <运动模糊=False>```
|
* ```fx 晃动 <最大偏移量=5> <运动模糊=False>```
|
||||||
|
* ```fx JPEG损坏 <质量=10>```
|
||||||
|
* 质量范围建议为 1~95,数值越低,压缩痕迹越重、效果越搞笑。
|
||||||
* ```fx 动图 <帧率=10>```
|
* ```fx 动图 <帧率=10>```
|
||||||
|
|
||||||
### 多图像处理器
|
### 多图像处理器
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import random
|
import random
|
||||||
|
from io import BytesIO
|
||||||
from PIL import Image, ImageFilter, ImageDraw, ImageStat, ImageFont
|
from PIL import Image, ImageFilter, ImageDraw, ImageStat, ImageFont
|
||||||
from PIL import ImageEnhance
|
from PIL import ImageEnhance
|
||||||
from PIL import ImageChops
|
from PIL import ImageChops
|
||||||
@ -167,6 +168,29 @@ class ImageFilterImplement:
|
|||||||
|
|
||||||
return Image.fromarray(result, 'RGBA')
|
return Image.fromarray(result, 'RGBA')
|
||||||
|
|
||||||
|
# JPEG 损坏感压缩
|
||||||
|
@staticmethod
|
||||||
|
def apply_jpeg_damage(image: Image.Image, quality: int = 10) -> Image.Image:
|
||||||
|
quality = max(1, min(95, int(quality)))
|
||||||
|
|
||||||
|
alpha = None
|
||||||
|
if image.mode in ('RGBA', 'LA') or (image.mode == 'P' and 'transparency' in image.info):
|
||||||
|
rgba_image = image.convert('RGBA')
|
||||||
|
alpha = rgba_image.getchannel('A')
|
||||||
|
rgb_image = Image.new('RGB', rgba_image.size, (255, 255, 255))
|
||||||
|
rgb_image.paste(rgba_image, mask=alpha)
|
||||||
|
else:
|
||||||
|
rgb_image = image.convert('RGB')
|
||||||
|
|
||||||
|
output = BytesIO()
|
||||||
|
rgb_image.save(output, format='JPEG', quality=quality, optimize=False)
|
||||||
|
output.seek(0)
|
||||||
|
damaged = Image.open(output).convert('RGB')
|
||||||
|
|
||||||
|
if alpha is not None:
|
||||||
|
return Image.merge('RGBA', (*damaged.split(), alpha))
|
||||||
|
return damaged.convert('RGBA')
|
||||||
|
|
||||||
# 缩放
|
# 缩放
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def apply_resize(image: Image.Image, scale: float = 1.5, scale_y = None) -> Image.Image:
|
def apply_resize(image: Image.Image, scale: float = 1.5, scale_y = None) -> Image.Image:
|
||||||
|
|||||||
@ -50,6 +50,7 @@ class ImageFilterManager:
|
|||||||
"描边": ImageFilterImplement.apply_stroke,
|
"描边": ImageFilterImplement.apply_stroke,
|
||||||
"形状描边": ImageFilterImplement.apply_shape_stroke,
|
"形状描边": ImageFilterImplement.apply_shape_stroke,
|
||||||
"半调": ImageFilterImplement.apply_halftone,
|
"半调": ImageFilterImplement.apply_halftone,
|
||||||
|
"JPEG损坏": ImageFilterImplement.apply_jpeg_damage,
|
||||||
"设置通道": ImageFilterImplement.apply_set_channel,
|
"设置通道": ImageFilterImplement.apply_set_channel,
|
||||||
"设置遮罩": ImageFilterImplement.apply_set_mask,
|
"设置遮罩": ImageFilterImplement.apply_set_mask,
|
||||||
# 图像处理
|
# 图像处理
|
||||||
|
|||||||
37
tests/test_fx_process.py
Normal file
37
tests/test_fx_process.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
from importlib.util import module_from_spec, spec_from_file_location
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import nonebot
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
|
nonebot.init()
|
||||||
|
|
||||||
|
MODULE_PATH = Path(__file__).resolve().parents[1] / "konabot/plugins/fx_process/fx_handle.py"
|
||||||
|
SPEC = spec_from_file_location("test_fx_handle_module", MODULE_PATH)
|
||||||
|
assert SPEC is not None and SPEC.loader is not None
|
||||||
|
fx_handle = module_from_spec(SPEC)
|
||||||
|
SPEC.loader.exec_module(fx_handle)
|
||||||
|
ImageFilterImplement = fx_handle.ImageFilterImplement
|
||||||
|
|
||||||
|
|
||||||
|
def test_apply_jpeg_damage_keeps_size_and_rgba_mode():
|
||||||
|
image = Image.new("RGBA", (32, 24), (255, 0, 0, 128))
|
||||||
|
|
||||||
|
result = ImageFilterImplement.apply_jpeg_damage(image, 5)
|
||||||
|
|
||||||
|
assert result.size == image.size
|
||||||
|
assert result.mode == "RGBA"
|
||||||
|
assert result.getchannel("A").getextrema() == (128, 128)
|
||||||
|
|
||||||
|
|
||||||
|
def test_apply_jpeg_damage_clamps_quality_range():
|
||||||
|
image = Image.new("RGB", (16, 16), (123, 222, 111))
|
||||||
|
|
||||||
|
low = ImageFilterImplement.apply_jpeg_damage(image, -10)
|
||||||
|
high = ImageFilterImplement.apply_jpeg_damage(image, 999)
|
||||||
|
|
||||||
|
assert low.size == image.size
|
||||||
|
assert high.size == image.size
|
||||||
|
assert low.mode == "RGBA"
|
||||||
|
assert high.mode == "RGBA"
|
||||||
Reference in New Issue
Block a user