标签 图像处理 下的文章

售后(?)服务群:992474988

各位佬们好,

之前在版里分享过一个叫 Raw Alchemy 的小工具,主要用来解决 “想把视频的 S-Log/F-Log LUT 套在 RAW 照片上,结果颜色不对” 的问题。

当时那个版本虽然也能用,但界面比较“直男”(见下图1),只能算是个功能性软件,选图、对比都不太方便,甚至有点简陋。

听取了大家的建议,这段时间我把整个软件重写了!

带来了全新的 Raw Alchemy Studio

先看对比:

以前的版本 (功能单一,界面简陋):

现在的版本 (暗色模式,现代化管理,支持选片):


这次更新了什么?

不再只是一个单纯的“格式转换器”,它现在是一个完整的选片+基础调色工作台

  1. 交互体验大升级
  • 抛弃了以前那种分离的窗口设计,改成了类似 Lightroom/Capture One 的暗色系集成界面

  • 增加了文件库管理 ,左侧可以直接浏览文件夹缩略图,不需要再一张张“打开文件”了。

  1. 核心功能:视频 LUT 完美兼容 (依然是杀手锏)
  • 如果你是双修党,手里的 S-Log3、F-Log2、LogC4 视频 LUT,现在可以在这个软件里精准 地套用在 RAW 照片上。

  • 底层逻辑是把 RAW 解码为 Linear 空间后,通过数学模型伪装成 Log 信号,色彩还原度吊打直接在 PS 里硬套。

  1. 选片效率提升
  • 支持空格键对比 (原图 vs LUT后)。

  • 增加了 Tag (标记) 功能,可以快速过一遍片子,打上标,然后一键“导出所有标记图片”。

  1. 更多实用功能
  • 高光保护测光 :自动曝光时优先保护高光不溢出。

  • 镜头矫正 :内置 Lensfun,自动修正畸变。

  • 直方图 :实时的 RGB 直方图,不再盲调。

下载地址 (GitHub):
https://github.com/shenmintao/Raw-Alchemy/releases/tag/studio-v0.3.0

(Releases 里有打包好的 exe,开箱即用,无需安装环境)

这次改版主要是为了解决“好用”的问题。以前那是为了“能用”,现在终于像个正经的后期软件了。

代码完全开源,免费使用。欢迎各位老哥下载体验,如果是索尼、富士、松下用户,一定要试试把你拍视频用的 LUT 套进去看看,效果绝对不一样。

有 Bug 欢迎反馈,觉得好用求个 Star!



📌 转载信息
转载时间: 2026/1/27 08:05:21

首先上github链接:

这个项目本质上是一个把图片的一部分剪下来,经过api/手动处理后回填到原图片的项目。

我们都知道banana可以翻译正常漫画,但是合理利用这个项目也可以轻松把涩图漫画也翻译成毫无P图痕迹的漫画哦。

原理很简单,banana拒绝翻译涩图的原因是,你把涩图发给了banana!

这好像是一句废话,但我们反过来思考就能发现,如果我们不把完整的涩图发给,而是把有台词的部分切片发给它,它就不会拒绝~

(这么一想,我其实是不是不用加nsfw标签?)

那么通往真理的大道就打开了:

  1. 上传你的图片到本项目

  2. 圈出台词所在的图片(我看到其他漫画翻译项目可以自动检测文本框,使用comic text detector之类的工具实现的,先摆烂一会,等一个pr~)

  3. 使用nano banana pro的api发起翻译请求(当然也提供了不使用api手动替换的途径)

  4. 翻译后的对话自动回填到涩图中!

还有一些注意事项:

  1. gemini-business2api项目可以白嫖banana pro,但是通过2api的生图成功率比较低,因为图片虽然生成了,但url好像经常没有被项目返回。可能需要看看项目能不能二开一下修复这个问题才能爽嫖?使用纯banana api就不会有问题,成功率99.99%。实在没有纯api的话也可以用手动模式,在gemini-business一张一张翻译再复制回项目ww

    gemini-business2api生图问题解决了,现在可以稳定生图,等我给这个项目提pr

    更新:pr已经提交了,如果2api项目的佬友还没合并的话,你们可以参考我pr的内容改两句代码后直接运行。

  2. 圈出图片所在的台词时,不要圈太小了,尽量圈大点,比如这样:

除了文本框,还要多截一些周围的景色。

因为结果测试,如果不截一些景色的话,就会导致banan喜欢偷懒不嵌字,把日文原图发回来。

  1. 推荐提示词,可以自行修改:

    0. 使用banana pro
    
    1. 请用banana中文翻译替换掉图片里的日文。是的,生成一张只有中文的图!
    
    3. 这句台词的大意是:[]
    

📌 转载信息
转载时间: 2026/1/25 08:07:06

一句话简介: 让 AI 帮你设计并更换 Windows 文件夹图标。

最近闲的疼,在整理文件夹时,脑子抽了,突然感觉看这些一模一样的黄色文件夹很不爽,要是能有像手机里面那样可以自定义的主题图标就好了,于是就搓了一个这个工具。

大概的思路就是让 ai 分析一下目录结构(不读取实际文件)后给出建议,然后结合自己想要的图标风格来生成提示词,输入生图模型来生成图标,最后再调用一下抠图的工具更换图标就可以了。

现在应该能正常使用。。。(应该)
虽然感觉这项目没啥鸟用,不过我自己用着挺爽的,,希望能有同感的佬友支持一波

效果示例


这我感觉很爽啊 特别是分好类的文件夹这种

基本流程

添加文件夹 → AI 分析内容 → 选择/自定义风格 → 生成图标 → 预览 → 应用

操作步骤

  1. 添加文件夹 - 点击左下角
  2. 选择风格 - 从模板库选择,或用自然语言描述
  3. 生成图标 - 点击「生成图标」或说「生成」
  4. 预览调整 - 在右侧面板查看生成版本
  5. 应用图标 - 满意后点击「应用」

里面我也内置了一些模板 可以直接一键套用

项目地址

了解更详细的内容,请点击:FolderPainter

目前感觉可拓展性也挺高的,也许还可以去改快捷方式的图标,这样桌面也可以个性化了。
觉得有意思的佬们可以去 GitHub 留个脚印,感谢支持!
任何有意思的想法和改进的建议也欢迎讨论!!!


📌 转载信息
转载时间:
2026/1/24 06:39:30

基于YOLOv8的蚊蝇位置智能检测识别项目|完整源码数据集+PyQt5界面+完整训练流程+开箱即用!

源码包含:完整YOLOv8训练代码+数据集(带标注)+权重文件+直接可允许检测的yolo检测程序+直接部署教程/训练教程

基本功能演示

https://www.bilibili.com/video/BV1zYrhBxEau/

源码在哔哩哔哩视频简介处

项目摘要

本项目基于 YOLOv8 深度学习检测模型,结合 PyQt5 图形界面,实现了对蚊子和苍蝇的自动检测与定位。项目核心特点包括:

  1. 多输入源支持:可处理单张图片、图片文件夹、视频文件以及实时摄像头输入。
  2. 高精度识别:利用定制蚊蝇数据集训练,准确识别蚊子与苍蝇,同时兼顾背景样本,降低误报率。
  3. 开箱即用:提供完整源码、训练数据、预训练权重及部署教程,用户可直接运行检测系统或继续训练自定义模型。
  4. 可视化界面:PyQt5 图形界面直观展示检测结果,支持边框显示、类别标注、置信度显示等功能。
  5. 灵活扩展:项目结构清晰,可快速扩展到其他小型生物检测任务或多分类目标检测场景。

通过本项目,用户可实现蚊蝇数量监测、位置统计及风险评估,为实验室、公共卫生、农业及城市环境管理提供智能化工具。

前言

随着智能视觉技术的发展,小型害虫检测在公共卫生、农作物管理及环境监测中具有重要意义。传统人工检测方法不仅耗时长、效率低,而且容易漏检或误判。借助 YOLO 系列目标检测算法,本项目提供了一种快速、准确、可扩展的蚊蝇检测解决方案。

项目基于无人机或固定摄像头拍摄的实验样本,通过训练专用数据集,使模型能够在复杂背景下自动识别蚊子和苍蝇位置。结合 PyQt5 图形界面,用户无需掌握深度学习底层技术即可完成检测、可视化及数据统计。

一、软件核心功能介绍及效果演示

核心功能

  1. 图片检测

    • 支持单张图片检测,自动标注蚊子和苍蝇位置。
    • 输出标注图与 YOLO 格式检测结果。
  2. 批量图片处理

    • 支持文件夹中所有图片的批量检测。
    • 自动生成检测报告,包括数量统计及置信度分析。
  3. 视频检测

    • 支持本地视频文件输入,实时识别视频中的蚊子与苍蝇。
    • 可选择保存检测后的视频,标注框清晰展示目标。
  4. 摄像头实时检测

    • 支持 USB 摄像头或笔记本内置摄像头实时捕捉并检测蚊蝇。
    • 界面显示实时检测帧,支持帧率与置信度调节。
  5. 检测结果可视化

    • 在 PyQt5 界面中显示目标框、类别及置信度。
    • 支持结果导出,包括图片、视频和 CSV 数据。
  6. 训练与模型管理

    • 提供完整训练代码与数据集标注示例。
    • 可加载自定义权重继续训练或微调模型。
    • 支持 YOLOv8 标准训练流程,包括训练集划分、超参数配置和结果可视化。

效果演示

  • 图片示例

    • 检测后每只蚊子与苍蝇都会被框出,类别和置信度清晰显示。
  • 视频示例

    • 视频播放时,模型实时标注移动的目标,统计目标数量并可导出检测数据。
  • 实时摄像头示例

    • 界面上可即时显示检测框与数量统计,操作简单,无需命令行操作。

二、软件效果演示

为了直观展示本系统基于 YOLOv8 模型的检测能力,我们设计了多种操作场景,涵盖静态图片、批量图片、视频以及实时摄像头流的检测演示。

(1)单图片检测演示

用户点击“选择图片”,即可加载本地图像并执行检测:

image-20260112012732195


(2)多文件夹图片检测演示

用户可选择包含多张图像的文件夹,系统会批量检测并生成结果图。

image-20260112012821538


(3)视频检测演示

支持上传视频文件,系统会逐帧处理并生成目标检测结果,可选保存输出视频:

image-20260112012846148


(4)摄像头检测演示

实时检测是系统中的核心应用之一,系统可直接调用摄像头进行检测。由于原理和视频检测相同,就不重复演示了。

image-20260112012858804


(5)保存图片与视频检测结果

用户可通过按钮勾选是否保存检测结果,所有检测图像自动加框标注并保存至指定文件夹,支持后续数据分析与复审。

image-20260112012943268

三、模型的训练、评估与推理

YOLOv8是Ultralytics公司发布的新一代目标检测模型,采用更轻量的架构、更先进的损失函数(如CIoU、TaskAlignedAssigner)与Anchor-Free策略,在COCO等数据集上表现优异。
其核心优势如下:

  • 高速推理,适合实时检测任务
  • 支持Anchor-Free检测
  • 支持可扩展的Backbone和Neck结构
  • 原生支持ONNX导出与部署

3.1 YOLOv8的基本原理

YOLOv8 是 Ultralytics 发布的新一代实时目标检测模型,具备如下优势:

  • 速度快:推理速度提升明显;
  • 准确率高:支持 Anchor-Free 架构;
  • 支持分类/检测/分割/姿态多任务
  • 本项目使用 YOLOv8 的 Detection 分支,训练时每类表情均标注为独立目标。

YOLOv8 由Ultralytics 于 2023 年 1 月 10 日发布,在准确性和速度方面具有尖端性能。在以往YOLO 版本的基础上,YOLOv8 引入了新的功能和优化,使其成为广泛应用中各种物体检测任务的理想选择。

image-20250526165954475

YOLOv8原理图如下:

image-20250526170118103

3.2 数据集准备与训练

采用 YOLO 格式的数据集结构如下:

dataset/
├── images/
│   ├── train/
│   └── val/
├── labels/
│   ├── train/
│   └── val/

每张图像有对应的 .txt 文件,内容格式为:

4 0.5096721233576642 0.352838390077821 0.3947600423357664 0.31825755058365757

分类包括(可自定义):

image-20260112013102185

image-20260112013042045

3.3. 训练结果评估

训练完成后,将在 runs/detect/train 目录生成结果文件,包括:

  • results.png:损失曲线和 mAP 曲线;
  • weights/best.pt:最佳模型权重;
  • confusion_matrix.png:混淆矩阵分析图。
若 mAP@0.5 达到 90% 以上,即可用于部署。

在深度学习领域,我们通常通过观察损失函数下降的曲线来评估模型的训练状态。YOLOv8训练过程中,主要包含三种损失:定位损失(box_loss)、分类损失(cls_loss)和动态特征损失(dfl_loss)。训练完成后,相关的训练记录和结果文件会保存在runs/目录下,具体内容如下:

image-20260112013024393

3.4检测结果识别

使用 PyTorch 推理接口加载模型:

import cv2
from ultralytics import YOLO
import torch
from torch.serialization import safe_globals
from ultralytics.nn.tasks import DetectionModel

# 加入可信模型结构
safe_globals().add(DetectionModel)

# 加载模型并推理
model = YOLO('runs/detect/train/weights/best.pt')
results = model('test.jpg', save=True, conf=0.25)

# 获取保存后的图像路径
# 默认保存到 runs/detect/predict/ 目录
save_path = results[0].save_dir / results[0].path.name

# 使用 OpenCV 加载并显示图像
img = cv2.imread(str(save_path))
cv2.imshow('Detection Result', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

预测结果包含类别、置信度、边框坐标等信息。

image-20260112013207795

四.YOLOV8+YOLOUI完整源码打包

本文涉及到的完整全部程序文件:包括python源码、数据集、训练代码、UI文件、测试图片视频等(见下图),获取方式见【4.2 完整源码下载】:

4.1 项目开箱即用

作者已将整个工程打包。包含已训练完成的权重,读者可不用自行训练直接运行检测。

运行项目只需输入下面命令。

python main.py

读者也可自行配置训练集,或使用打包好的数据集直接训练。

自行训练项目只需输入下面命令。

yolo detect train data=datasets/expression/loopy.yaml model=yolov8n.yaml pretrained=yolov8n.pt epochs=100 batch=16 lr0=0.001

4.2 完整源码

至项目实录视频下方获取:https://www.bilibili.com/video/BV1zYrhBxEau/

image-20250801135823301

包含:

📦完整项目源码

📦 预训练模型权重

🗂️ 数据集地址(含标注脚本)

总结

本项目基于 YOLOv8 深度学习检测模型与 PyQt5 图形界面,实现了蚊子与苍蝇的高效、智能化检测与定位。通过专用数据集训练,系统能够在复杂背景下准确识别目标,同时提供图片、视频及摄像头多种输入方式。

项目核心优势包括:

  1. 高精度识别:模型在小型目标和复杂背景下表现稳定,误报率低。
  2. 多场景适用:支持单张图片、批量图片、视频和实时摄像头输入。
  3. 可视化与易用性:界面直观,标注清晰,用户无需深度学习经验即可使用。
  4. 可扩展性:源码结构清晰,可快速应用于其他小型生物检测任务或扩展目标类别。
  5. 开箱即用:提供完整训练流程、权重文件和部署教程,用户可直接上手或自定义训练。

整体而言,本项目为公共卫生监测、实验室研究和环境管理提供了一个 快速、可靠、可视化的智能检测解决方案,降低人工检测成本,提高数据收集效率,为小型害虫监控提供了可落地的技术工具。

第六十三章 运动侦测实验

乐鑫AI库中提供了一种名为运动侦测API接口的功能。该功能的原理非常简单:只需要获取两张图像数据,然后通过AI计算判断这两个图像是否匹配。如果图像不匹配,则说明当前处于运动状态;如果图像匹配,则说明当前图像处于相对静止状态。本章,我们调用乐鑫AI库的运动侦测API接口来实现运动侦测功能。
本章分为如下几个部分:
63.1 硬件设计
63.2 软件设计
63.3 下载验证

63.1 硬件设计

1.例程功能

本章实验功能简介:使用乐鑫官方的ESP32-WHO AI库对OV2640和OV5640摄像头输出的数据进行运动侦测。

2.硬件资源

1)LED灯
LED-IO1

2)XL9555
IIC_INT-IO0(需在P5连接IO0)
IIC_SDA-IO41
IIC_SCL-IO42

3)SPILCD
CS-IO21
SCK-IO12
SDA-IO11
DC-IO40(在P5端口,使用跳线帽将IO_SET和LCD_DC相连)
PWR- IO1_3(XL9555)
RST- IO1_2(XL9555)

4)CAMERA
OV_SCL-IO38
OV_SDA- IO39
VSYNC- IO47
HREF- IO48
PCLK- IO45
D0- IO4
D1- IO5
D2- IO6
D3- IO7
D4- IO15
D5- IO16
D6- IO17
D7- IO18
RESET-IO0_5(XL9555)
PWDN-IO0_4(XL9555)

3.原理图

本章实验使用的KPU为ESP32-S3的内部资源,因此并没有相应的连接原理图。

63.2 软件设计

63.2.1 程序流程图

程序流程图能帮助我们更好的理解一个工程的功能和实现的过程,对学习和设计工程有很好的主导作用。下面看看本实验的程序流程图:

图63.2.1.1 程序流程图

63.2.2 程序解析

在本章节中,我们将重点关注两个文件:esp_motion_detection.cpp和esp_motion_detection.hpp。其中,esp_motion_detection.hpp主要声明了esp_motion_detection函数,其内容相对简单,因此我们暂时不作详细解释。本章节的核心关注点是esp_motion_detection.cpp文件中的函数。
接下来,我们将详细解析esp_motion_detection_ai_strat函数的工作原理。

TaskHandle_t camera_task_handle;
TaskHandle_t ai_task_handle;
QueueHandle_t xQueueFrameO = NULL;
QueueHandle_t xQueueAIFrameO = NULL;


/**
 * @brief       摄像头图像数据获取任务
 * @param       arg:未使用
 * @retval      无
 */
static void esp_camera_process_handler(void *arg)
{
    arg = arg;
    camera_fb_t *camera_frame = NULL;

    while (1)
    {
        /* 获取摄像头图像 */
        camera_frame = esp_camera_fb_get();

        if (camera_frame)
        {
            /* 以队列的形式发送 */
            xQueueSend(xQueueFrameO, &camera_frame, portMAX_DELAY);
        }
    }
}

/**
 * @brief       摄像头图像数据传入AI处理任务
 * @param       arg:未使用
 * @retval      无
 */
static void esp_ai_process_handler(void *arg)
{
    arg = arg;
    camera_fb_t *face_ai_frameI = NULL;
    camera_fb_t *face_ai_frameI2 = NULL;

    while(1)
    {
        /* 以队列的形式获取摄像头图像数据 */
        if (xQueueReceive(xQueueFrameO, &face_ai_frameI, portMAX_DELAY))
        {
            if (xQueueReceive(xQueueFrameO, &face_ai_frameI2, portMAX_DELAY))
            {
                /* 判断图像是否出现运动 */
                uint32_t moving_point_number = dl::image::
get_moving_point_number(
(uint16_t *)face_ai_frameI->buf,
(uint16_t *)face_ai_frameI2->buf,
face_ai_frameI->height,
face_ai_frameI->width, 8, 15);

                if (moving_point_number > 50)
                {
                    printf("Something moved\r\n");
                    /* 此处是在图像中绘画检测效果 */
                    dl::image::draw_filled_rectangle(
(uint16_t *)face_ai_frameI2->buf, 
face_ai_frameI2->height, 
face_ai_frameI2->width, 0, 0, 40, 40);
                }
                else
                {
                    printf("Something not moved\r\n");
                }
                
                esp_camera_fb_return(face_ai_frameI);
                /* 以队列的形式发送AI处理的图像 */
                xQueueSend(xQueueAIFrameO, &face_ai_frameI2, portMAX_DELAY);
            }
        }
    }
}

/**
 * @brief       AI图像数据开启
 * @param       无
 * @retval      1:创建任务及队列失败;0:创建任务及对了成功
 */
uint8_t esp_motion_detection_ai_strat(void)
{
    /* 创建队列及任务 */
    xQueueFrameO = xQueueCreate(5, sizeof(camera_fb_t *));
    xQueueAIFrameO = xQueueCreate(5, sizeof(camera_fb_t *));
xTaskCreatePinnedToCore(esp_camera_process_handler,
                       "esp_camera_process_handler", 4 * 1024, NULL, 
5, &camera_task_handle, 1);
xTaskCreatePinnedToCore(esp_ai_process_handler, "esp_ai_process_handler", 
6 * 1024, NULL, 5, &ai_task_handle, 1);

    if (xQueueFrameO != NULL 
        || xQueueAIFrameO != NULL 
        || camera_task_handle != NULL 
        || ai_task_handle != NULL)
    {
        return 0;
    }
    
    return 1;
}

上述原理非常简单:只需要在ai_task_handle任务下获取两张图像数据,然后通过AI计算判断这两个图像是否匹配。如果图像不匹配,则说明当前处于运动状态;如果图像匹配,则说明当前图像处于相对静止状态,最后,我们使用消息队列将当前图像数据传输至LCD进行显示。

63.3 下载验证

程序下载成功后,当检测到图像变化时,图像左上角有蓝色块闪烁。

前言

在做图片相关功能时,有一个需求几乎绕不开:
用户拖动参数,图片实时变化。

比如:

  • 调整模糊强度
  • 改变对比度、饱和度
  • 预览滤镜效果,再决定是否应用

在 UIKit 时代,我们可能会用 UIImageView + CoreImage + GCD 硬撸。
但到了 SwiftUI,很多人第一反应是:

SwiftUI + CoreImage + 实时预览,这事靠谱吗?

答案是:靠谱,但得用对方式。

这篇文章就从一个最小可用 Demo开始,一步一步把实时滤镜预览这件事讲清楚。

先说结论:实时预览的关键点是什么?

在 SwiftUI 里做 CoreImage 实时预览,核心其实只有三点:

  1. 图片渲染要尽量轻
  2. 滤镜计算不能阻塞主线程
  3. UI 状态变化要最小化

如果你一上来就把所有滤镜计算都丢进 body
那基本等于在和 SwiftUI 的刷新机制正面硬刚。

一个最基础的目标效果

我们先定一个目标:

  • 显示一张原图
  • 拖动 Slider
  • 实时调整高斯模糊强度
  • 图片随着 Slider 连续变化

这是绝大多数滤镜编辑页的基础形态。

Step 1:准备 CoreImage 的基础组件

先把 CoreImage 的几个核心对象准备好:

import SwiftUI
import CoreImage
import CoreImage.CIFilterBuiltins

let context = CIContext()
let filter = CIFilter.gaussianBlur()

这里有两个细节值得注意:

  • CIContext 应该尽量复用
  • 不要在 body 里反复 new CIContext

CIContext 本身是重量级对象,频繁创建会直接拖垮性能。

Step 2:一个最简单的 SwiftUI 结构

我们先搭一个最基础的页面结构:

struct ContentView: View {
    @State private var intensity: Double = 0.5
    let image = UIImage(named: "example")!

    var body: some View {
        VStack {
            Image(uiImage: image)
                .resizable()
                .scaledToFit()

            Slider(value: $intensity)
                .padding()
        }
    }
}

到这一步,UI 是没问题的,但还没有任何滤镜逻辑

Step 3:把 CoreImage 滤镜接进来

关键思路是:
不要直接操作 UIImage,而是用 CIImage 作为中间态。

我们先写一个专门负责“生成滤镜图片”的方法:

func applyProcessing() -> UIImage {
    let beginImage = CIImage(image: image)
    filter.inputImage = beginImage
    filter.radius = Float(intensity * 20)

    guard let outputImage = filter.outputImage else {
        return image
    }

    if let cgimg = context.createCGImage(outputImage, from: beginImage!.extent) {
        return UIImage(cgImage: cgimg)
    }

    return image
}

这段代码做了几件事:

  1. UIImage 转成 CIImage
  2. 设置滤镜参数
  3. 通过 CIContext 渲染成 CGImage
  4. 再转回 UIImage

Step 4:把实时预览“接”到 SwiftUI 状态上

接下来是最关键的一步:
让 SwiftUI 在 Slider 变化时刷新图片,但不炸性能。

先引入一个新的状态:

@State private var processedImage: UIImage?

然后改造 body

var body: some View {
    VStack {
        Image(uiImage: processedImage ?? image)
            .resizable()
            .scaledToFit()

        Slider(value: $intensity)
            .padding()
            .onChange(of: intensity) { _ in
                processedImage = applyProcessing()
            }
    }
}

此时你已经可以看到:

  • Slider 一动
  • 图片跟着变
  • 滤镜是实时的

但——
这还不是一个“能上线”的写法。

性能问题从哪开始暴露?

当你快速拖动 Slider 时,会发现:

  • UI 有轻微卡顿
  • 真机上比模拟器更明显
  • 图片越大,问题越严重

原因也很直接:

滤镜计算跑在主线程。

Slider 的 onChange 本身就在主线程,
CoreImage 渲染又是 CPU / GPU 混合操作,
自然会影响 UI 响应。

Step 5:把滤镜计算移出主线程

一个简单、有效的方式是:
Task + MainActor 控制线程切换。

改造 onChange

.onChange(of: intensity) { _ in
    Task.detached {
        let output = applyProcessing()
        await MainActor.run {
            processedImage = output
        }
    }
}

这样做之后:

  • 滤镜计算在后台执行
  • UI 只负责展示结果
  • 拖动 Slider 明显顺滑很多

这一步,是“能不能实时预览”的分水岭。

再往前一步:为什么 SwiftUI 特别适合做这件事?

如果你用 UIKit 做过类似功能,会发现:

  • 手动管理线程
  • 手动刷新 ImageView
  • 状态和 UI 同步很痛苦

而 SwiftUI 的优势在于:

  • 状态驱动 UI
  • 图片只是状态的一个映射
  • 滤镜逻辑和 UI 逻辑可以完全解耦

你只需要保证一件事:

状态更新是轻的,计算是异步的。

一点真实项目里的经验总结

在真实项目中,我一般会遵守这几个原则:

  1. Slider 变化频繁时,必要时做节流
  2. 滤镜链尽量复用,不要每次 new
  3. 大图先 downscale 再做预览
  4. 最终导出时再跑一次“高质量渲染”

实时预览追求的是“看起来对”
而不是“每一帧都是最终质量”

总结

SwiftUI 并不是不适合做图像处理,
而是不能用同步思维去写异步计算

一旦你把:

  • CoreImage 的计算
  • SwiftUI 的状态刷新
  • 主线程和后台线程的职责

这三件事理顺了,
实时滤镜预览这件事,其实比 UIKit 时代要轻松得多。