2024年7月

分享是最有效的学习方式。

博客:
https://blog.ktdaddy.com/


最近遇上事儿了,老猫的小小博客网站【
程序员老猫的博客
】被人盗刷CDN流量了。开始的时候收到了欠费的短信也没有怎么去重视。虽然说费用没有多少,但是也是一个教训。

博客从最初地搭建到现在确实也经过好几年了,天真地以为确实很长时间了,差不多快四年都没有充钱了,缺个两块钱应该也是正常的,补上就行,然而让人没有想到的是补偿充值之后的一个小时,有显示欠费了。于是就立马引起了我的重视。于是深夜开始进行排查问题。

(期间其实又陆陆续续充值了一些,发现充值之后瞬间就又没有了,前后算了算,虽然痛失30块。)

陆陆续续充值了一些

写博客的初衷

写博客其实也没有想着去盈利,或者说等网站的流量大了想着去接广告,因为也没有指望博客会有很大的流量。事实也没有让我失望,通过百度统计,发现每天也就几个IP。多的时候会有几十个。

ip趋势

那有小伙伴肯定要问了,不赚钱,又没有流量,那写个鸡屎球啊。那么多第三方平台不用,偏偏要用自己搭建的服务器去搞,那不是费时费力费钱。

哎,之所以想要自己用服务器去搭建博客平台主要原因还是一名技术人员最简单而又淳朴地追求,因为感觉自己搞服务器,自己写文章,就会让人觉得这是在经营自己的东西。另外的话,自己搭建博客的话自由度更加高一点,想要搞成什么样子都可以,使用第三方平台的话,总是觉得限制还是比较多的。指不定哪天平台凉了,自己的东西也就没有了(虽然实际情况往往是平台的寿命比自己个人搭建博客的寿命可能更长一些。)

关于老猫个人博客的技术栈其实也换了好几拨,以前用过wordpress,后来又换成了hexo,到后来差不多是20年左右的时候接触到了vue技术,后来又换成比较熟悉的vuepress,一直到现在用的都是vdonig的模版。

起因-草率地部署

最原始的部署方式,就是在阿里云上,通过nginx服务进行搭建,比较简单,端口用的也是默认端口。部署是完成了,但是外围访问的速度是相当慢的,后来排查原因主要是由于有些图片比较大,另外的话,有的还使用到了第三方的一些js,加载的时候也比较慢,当时也没有想到图床啊什么的,后来就想着优化一下,于是用到了腾讯云的CDN进行全站缓存,包括一些图片。

这次CDN流量被刷排查下来也是一张图片所致,当然图片我也没有经过压缩,本身大小差不多是500k了。排查下来两天这个图片一共刷了我89个G的CDN流量。

图片流量

这个图片访问次数也达到了62.77w次

请求数

然后又看了一下主要的请求归属地,所属地主要是来自于山西,山西的那位大哥,真心求放过啊,咱这小门小户的。

来源地

其实我一直挺好奇的,为什么要刷我的CDN流量呢?这玩意儿应该算是流量攻击?那为啥要攻击我呢?咱这才多少流量,大胆一点,不会是内部人员为了业绩?(⊙o⊙)…(细思极恐,感觉自己是不是想太多了,老马应该也不差我这30块钱吧。。。)

当然也是出于好奇,于是在网上逛了一下,发现还真有朋友遇到和老猫一样的情况,当然那个比较惨可能,是一家创业型的公司,老猫估计也是个菜鸡运维配置的CDN。他们居然还报警了,至于有没有下文,好像文章里面也没有提及。文章的内容大概是下面这样的。

料

从上面的文章中,看起来,这是个最近需要关注的事情,另外的话好像和运营商打击PCDN有点关系。具体的细节老猫后续也没有再深入去研究。看完文章,想想可能是误会老马了(有点以小人之心度君子之腹)
反正如果大家和老猫一样,也是个小站长,那么其实也是时候检查一波自己的网站配置了。

采取一些措施,解决流量盗刷

事情呢,已经发生了,30块巨额也回不来了,但是事情么总归也还是需要解决的,如何解决?

查找盗刷流量的IP地址,并且设置黑名单

老猫查看了之前对CDN请求的top100,发现消耗流量比较多的是下面的IP

我大哥的IP地址

没错,我要曝光我大哥。从地址上来看一个是江苏、一个是山西大哥。

相关的图片资源进行迁移,并且设置相关防盗链

于是半夜三更开始进行图片迁移,由于老猫其实还用了某云的第三方的图床CDN,于是老猫就把原来的图片迁移到了某云的图床上,并且给这些图片设置了防盗链。

另外的,当前cdn的相关的图片也进行了清除,清除的方式也比较简单,因为是全站缓存的,所以老猫直接登录到了部署的服务中,将相关的图片进行删除,然后重新刷新cdn的缓存,这样再次去访问该图片的时候就是404了,原始图片地址:
https://blog.ktdaddy.com/img/ui.png

设置单位时间全站的封顶流量

当然为了防止全局其他资源的cdn流量被刷,于是重新设置用量封顶规则,这个规则相当于是为了不让自己受到损失熔断整个网站具体如下:

瞬时封顶设置

老猫也知道自己的小站几斤几两,所以感觉设置这么高的瞬时封顶也就够了。这层也算是最后的防线了。

现在回过头来在想想,小小网站由于目前有CDN图床,现在再做相关的CDN的全站缓存好像意义并不是很大。所以后面还是考虑将全站缓存到CDN这层给干掉,直接服务器+图片图床即可。

一些想法

算是个人的小小损失吧,但是细思极恐,如果当时cdn上面充钱比较多,另外的话,盗刷的也不仅仅这一个资源的话,那么其最终损失就大了。上图中提及的“快科技”就是一个比较好的例子,作为一个资深码农,这件事情上真的是疏忽了,做事情不够严谨,应该自我批评一番。哪怕说是几年前部署的。

当然上面文章中有些疑问也欢迎其他小伙伴能够聊聊,当然还有哪些注意点,老猫这里没有想到的,也欢迎大家可以提出建议。

写到这里也差不多了,相信有很多小伙伴应该会来问老猫当前的博客是如何搭建起来的,用到了哪些东西,在这里,老猫把相关的技术以及资源分享给大家。

技术采用:vuepress,官网地址:
https://vuepress.vuejs.org/zh/

博客模版用的是vuepress-theme-vdoing,相关的gitlab地址是:
https://github.com/xugaoyi/vuepress-theme-vdoing,

上述博客模版对应的官方教学主站:
https://doc.xugaoyi.com/pages/a2f161/

创作不易,当然如果你看到了也欢迎给这位作者github上点个star。

上述有提及某云的图床,其实这里也不卖关子,主要用到的是七牛云。感兴趣的小伙伴可以研究一下。

总结一下

当我们遇到CDN流量盗刷的情况下,我们应该从哪些方面入手去做呢?我们可以按照下面的流程来应对:

  1. 立即监测和分析:尽快确定流量被盗刷的模式、时间、来源和去向等信息。通过 CDN 服务提供商提供的监控工具和数据分析,了解异常流量的特征。

  2. 暂停服务:在情况未明之前,为避免进一步损失,可以暂时停止相关的 CDN 服务。

  3. 通知 CDN 服务提供商:及时向 CDN 服务提供商报告被盗刷的情况,他们通常具有一定的应对机制和经验,能够协助调查和采取措施。

  4. 加强安全防护:检查和强化自身的网络安全措施,例如更新密码、加强认证机制、修补可能存在的安全漏洞等。

  5. 审查授权和访问控制:检查所有的授权和访问权限设置,确保只有合法的用户和应用能够访问和使用 CDN 服务。

  6. 保留证据:保存与流量被盗刷相关的所有数据和记录,以备后续可能的调查和法律行动。

  7. 法律手段:如果损失较大,可以考虑通过法律途径来追究盗刷者的责任,维护自身的合法权益。

总之,CDN 流量被盗刷是一个严重的问题,需要及时、果断地采取措施来应对,以减少损失并防止再次发生。

概述

由于工作需要,需要通过数据类型和方法名控制方法走向
用到的数据类型有8种(string,Int16,Int32,Int64,Boolean,Byte,Single,Double)
读取的方法(参数一致,但是数据不同的泛型方法,返回值也是泛型)暂时只有11种,但肯定的是,后续一定会增加

原本计划排列组合,写个88行代码,但是总觉得重复代码过多,且后续维护极其繁琐
例如:新增一个读取方法,需要额外新增8行数据类型选择代码,繁琐暂且不说,主要是容易出现纰漏

网络上一翻搜寻,找到了反射,完美解决了我的问题,现在想把这个反射分享给大家:

排列组合

没错,我最开始就是排列组合


int datatype = default(int) ; // 决定值类型
int GetType = default(int) ; // 决定通过什么方法读取

//类型选择
switch (datatype)
{
    case 2: res[0] += "'" + Convert.ToString(ReadString(dataaddress , datalen , gettype)) + "',"; break;
    case 4: res[0] += Convert.ToString(ReadInt(dataaddress , datalen , gettype)) + ","; break;
    case 6: res[0] += "'" + Convert.ToString(ReadString(dataaddress , datalen , gettype)) + "',"; break;
    case 7: res[0] += Convert.ToString(ReadUshort(dataaddress , datalen , gettype)) + ","; break;
    case 8: res[0] += Convert.ToString(ReadUint(dataaddress , datalen , gettype)) + ","; break;
    case 9: res[0] += Convert.ToString(ReadULong(dataaddress , datalen , gettype)) + ","; break;
    case 10: res[0] += Convert.ToString(ReadInt(dataaddress , datalen , gettype)) + ","; break;
    case 11: res[0] += Convert.ToString(ReadShort(dataaddress , datalen , gettype)) + ","; break;
    case 12: res[0] += Convert.ToString(ReadByte(dataaddress , datalen , gettype)) + ","; break;
    case 13: res[0] += Convert.ToString(ReadFloat(dataaddress , datalen , gettype)) + ","; break;
    case 14: res[0] += Convert.ToString(ReadDouble(dataaddress , datalen , gettype)) + ","; break;
    case 15: res[0] += Convert.ToString(ReadDouble(dataaddress , datalen , gettype)) + ","; break;
    case 20: res[0] += Convert.ToString(ReadBool(dataaddress , datalen , gettype)).ToLower() + ","; break;
    case 31: res[0] += Convert.ToString(ReadLong(dataaddress , datalen , gettype)) + ","; break;
    default: res[0] += Convert.ToString(ReadUshort(dataaddress , datalen , gettype)) + ","; break;
}
//方法选择
//string
public string ReadString(string StartAddress , int Length , int GetType)
{
    switch (GetType)
    {
        case 1: return ModbusTcpRead<string>(StartAddress , Length);
        case 2: return ModbusRtuRead<string>(StartAddress , Length);
        case 3: return ModbusRtuOverTcpRead<string>(StartAddress , Length);
        case 5: return InovanceTcpNetRead<string>(StartAddress , Length);
        case 6: return KeyenceMcNetRead<string>(StartAddress , Length);
        case 7: return MelsecMcNetRead<string>(StartAddress , Length);
        case 8: return OmronFinsNetRead<string>(StartAddress , Length);
        case 9: return PanasonicMcNetRead<string>(StartAddress , Length);
        case 10: return SiemensS7NetRead<string>(StartAddress , Length);
        case 11: return MelsecFxSerialOverTcpRead<string>(StartAddress , Length);
        case 12: return KeyenceMcAsciiNetRead<string>(StartAddress , Length);
        default: return ModbusTcpRead<string>(StartAddress , Length);
    }
}
//Bool
public bool ReadBool(string StartAddress , int Length , int GetType)
{
    switch (GetType)
    {
        case 1: return ModbusTcpRead<bool>(StartAddress , Length);
        case 2: return ModbusRtuRead<bool>(StartAddress , Length);
        case 3: return ModbusRtuOverTcpRead<bool>(StartAddress , Length);
        case 5: return InovanceTcpNetRead<bool>(StartAddress , Length);
        case 6: return KeyenceMcNetRead<bool>(StartAddress , Length);
        case 7: return MelsecMcNetRead<bool>(StartAddress , Length);
        case 8: return OmronFinsNetRead<bool>(StartAddress , Length);
        case 9: return PanasonicMcNetRead<bool>(StartAddress , Length);
        case 10: return SiemensS7NetRead<bool>(StartAddress , Length);
        case 11: return MelsecFxSerialOverTcpRead<bool>(StartAddress , Length);
        case 12: return KeyenceMcAsciiNetRead<bool>(StartAddress , Length);
        default: return ModbusTcpRead<bool>(StartAddress , Length);
    }
}
......(就不全部列出来了,排列组合,懂得吧?)

或许你发现了什么异常,感觉我这么写不太对,应该先控制方法,在控制值类型,但,没区别,还是排列组合,只不过分散了而已

以上代码也不难看出,重复代码非常多,几乎就是copy一份,然后改一下泛型传入

所以我在想,能不能吧参数转换成泛型填入,然后显然不行,后来找到了
反射
这个法宝

反射(正片开始)

int datatype = default(int) ; // 决定值类型
int GetType = default(int) ; // 决定通过什么方法读取

//方法名
string methodName = "ModbusTcpRead";
switch (gettype)
{
    case 1: methodName = "ModbusTcpRead"; break;
    case 2: methodName = "ModbusRtuRead"; break;
    case 3: methodName = "ModbusRtuOverTcpRead"; break;
    case 5: methodName = "InovanceTcpNetRead"; break;
    case 6: methodName = "KeyenceMcNetRead"; break;
    case 7: methodName = "MelsecMcNetRead"; break;
    case 8: methodName = "OmronFinsNetRead"; break;
    case 9: methodName = "PanasonicMcNetRead"; break;
    case 10: methodName = "SiemensS7NetRead"; break;
    case 11: methodName = "MelsecFxSerialOverTcpRead"; break;
    case 12: methodName = "KeyenceMcAsciiNetRead"; break;
    default: methodName = "ModbusTcpRead"; break;
}
//数据值类型
string type = "System.Int32";
switch (datatype)
{
    case 2: type = "System.String"; break;
    case 4: type = "System.Int32"; break; 
    case 6: type = "System.String"; break;
    case 7: type = "System.UInt16"; break; 
    case 8: type = "System.UInt32"; break; 
    case 9: type = "System.UInt64"; break;
    case 10: type = "System.Int32"; break;
    case 11: type = "System.Int16"; break;
    case 12: type = "System.Byte"; break;
    case 13: type = "System.Single"; break;//float
    case 14: type = "System.Double"; break;
    case 15: type = "System.Double"; break;
    case 20: type = "System.Boolean"; break;
    case 31: type = "System.Int64"; break;
    default: type = "System.UInt16"; break;
}
MethodInfo method = typeof(PLCOper).GetMethod(methodName , BindingFlags.Instance | BindingFlags.Public);
MethodInfo genericMethod = method.MakeGenericMethod(Type.GetType(type , false));
object result = genericMethod.Invoke(this , new object[] { dataaddress , datalen });

//这里有几点要提醒
//1、typeof(PLCOper) 括号里的类名必须是调用方法的父类名
//2、GetMethod 第二个参数,前者用于过滤方法的属性,比如是静态还是非静态,我方法全是非静态,所以需要BindingFlags.Instance 参数
//3、Invoke 第一个参数,如果调用静态方法,传入null就好,实例内的方法,需要传入this
//4、Invoke 第二个参数,是方法的入参

是不是简洁非常多?(看起来没区别或许是因为排列组合我没全部写出来?)

以后新增方法名也只需要在这里改一次就可以了

非常完美!

结束

感谢看到这里。

前言:

学习ComfyUI是一场持久战,而 ComfyUI Impact 是一个庞大的模块节点库,内置许多非常实用且强大的功能节点 ,例如检测器、细节强化器、预览桥、通配符、Hook、图片发送器、图片接收器等等。通过这些节点的组合运用,我们可以实现的工作有很多,例如自动人脸检测和优化修复、区域增强、局部重绘、控制人群、发型设计、更换模特服饰等。在ComfyUI的进阶之路上,ComfyUI Impact是每个人不可绕过的一条大河,所以本篇文章将带领大家理解并学会使用这些节点。祝大家学习顺利,早日成为ComfyUI的高手!

目录

一、安装方式

二、SAMLoader节点

三、UltralyticsDetectorProvider节点

四、BBOX Detector(SEGS) / BBOX Detector(combined)节点

五、SAMDetector(combined) / SAMDetector(segmented)节点

六、Simple Detector(SEGS) / Simple Detector(SEGS/pipe) / Simple Detector for AnimateDiff(SEGS)

七、DetailerDebug(SEGS)节点

八、示例工作流

一、安装方式

方法一:通过ComfyUI Manager安装(推荐)

打开Manager界面

1
2

方法二:使用git clone命令安装

在ComfyUI/custom_nodes目录下输入cmd按回车进入电脑终端

3

在终端输入下面这行代码开始下载

git clone https://github.com/ltdrdata/ComfyUI-Impact-Pack

4

二、SAMLoader节点

加载和处理SAM(Segment Anything Model)模型文件。该节点的主要功能是将预训练的SAM模型加载到内存中,以便在后续的节点中进行图像分割或其他相关操作。

5

输入:

SAM模型的路径 **假如配置好了路径文件,模型可自行选择**

device_name → 设备模式,可以选择在GPU或CPU上运行,推荐选择自动即可

输出:

SAM_MODEL → 根据调用该模型的节点情况对目标进行分割,生成目标的轮廓

注意:该模型不能单独使用,需要其他模型为其指定分割区域,随后该模型可以做到精细的分割。如下图对比:

6
7

使用场景:

· 加载模型:SAMLoader节点用于从指定路径加载预训练的SAM模型文件。通常,这些模型文件会存储在本地磁盘或其他存储介质上。

· 配置参数:节点会提供一些参数配置选项,让用户可以指定模型文件的路径和其他加载参数。

· 输出模型:一旦模型加载完成,SAMLoader节点会输出一个表示该模型的对象,该对象可以传递给其他节点以执行具体的图像分割任务。

通过SAMLoader节点,可以方便地加载和使用预训练的SAM模型,进行各种图像分割任务,实现更高效的图像处理工作流程。

三、UltralyticsDetectorProvider节点

提供一个基于Ultralytics的目标检测器,用于替换现有基于mmdet的检测器,可加载BBOX_Model和SEGM_Model两种模型之一。

8

输入:

目标检测模型的路径 **主流的模型有脸部检测、手部检测、身体检测等,假如配置好了路径文件,模型可自行选择**

输出:

BBOX_DETECTOR → 使用矩形框进行检测

SEGM_DETECTOR → 检测出目标的轮廓

注意:该节点只能加载BBOX和SEGM中的一种模型,并且要根据model_name中所选的模型类型进行连接,例如我的bbox/face_yolov8m.pt是一个BBOX类型的脸部检测模型,所以再连线时我就要选择BBOX_DETECTOR进行连线,这也是SEGM_DETECTOR会显示红叉的原因。

下面两张图片说明BBOX模型和SEGM模型的区别:

9
10

节点注意事项:

· 确保选择的模型文件路径正确,或者如果使用预定义模型,确保网络连接稳定以下载模型文件。

· 调整置信度阈值可以控制检测结果的精确度和召回率。较高的阈值会减少误报,但可能漏检,较低的阈值会增加召回率,但可能误报更多。

通过UltralyticsDetectorProvider节点,可以方便地利用Ultralytics提供的高效目标检测模型,进行各种目标检测任务,如物体识别、跟踪等,实现智能图像分析。

四、BBOX Detector(SEGS) / BBOX Detector(combined)节点

这两个节点用于目标检测任务,这些节点会输出检测到的对象的边界框(Bounding Box, BBOX)信息。它们的主要区别在于处理的输入类型和方法。

BBOX Detector(SEGS) 节点通常用于从分割结果中提取边界框。这种方法适用于图像分割模型的输出,其中每个对象的像素已经被分割出来,节点通过分析这些分割结果来确定每个对象的边界框。

BBOX Detector(combined) 节点用于直接处理输入图像,通过目标检测模型同时进行对象识别和边界框提取。这个节点适合使用预训练的目标检测模型,如YOLO等。

01cdfd89674abb457cdf2c49cf5bb3f

输入:

bbox_detector → BBOX类型的检测模型

image → 输入加载的图片

detailer_hook → impact库的一个特殊参数,用于在模型的不同阶段或特定事件发生时执行一些额外的操作

参数:

threshold → 阈值,仅检测已识别置信度高于此值的对象

dilation → 调整最终显示时,检测到的掩码区域的大小

crop_factor → 根据检测到的掩码区域,确定应将周围区域包含在细节修复过程中的次数 **如果此值较小,则修复可能无法正常工作,因为无法知道周围的上下文**

drop_size → 降低尺寸,用于控制细节增强过程中的图像降低尺寸的大小。

文本框 → 用于输入文本指定被允许检测的地方

输出:

SEGS → 包含每次检测的裁剪图像、掩码图案、裁剪位置和置信度

MASK → 所有检测到的矩形框组合而成的单个掩码

使用方法:

①BBOX Detector(SEGS) 节点

· 输入分割结果:节点接受图像分割模型的输出,通常是一个包含分割掩码的图像。

· 提取边界框:通过分析分割掩码,节点确定每个对象的边界框。

· 输出边界框:节点输出一个包含所有检测对象边界框的信息,包括每个边界框的位置和大小。

②BBOX Detector(combined) 节点

· 输入图像:节点接受一个输入图像。

· 目标检测:通过预训练的目标检测模型(如YOLO)在图像中检测对象并提取边界框。

· 输出边界框:节点输出一个包含所有检测对象边界框的信息,包括每个边界框的位置、大小以及对象类别和置信度分数。

合理使用BBOX Detector(SEGS)和BBOX Detector(combined)节点,可以实现复杂的图像分析任务,包括对象检测、定位和分类,从而提升各种应用场景下的智能化水平。

五、SAMDetector(combined) / SAMDetector(segmented)节点

用于目标检测和图像分割任务。这俩节点利用Segment Anything Model (SAM) 来进行高级的图像处理。

13

输入:

sam_model → 接收SAMLoader节点加载的SAM模型

segs → 接收UltralyticsDetectorProvider节点加载的目标检测模型

image → 接收加载的图片

参数:

detection_hint → 用来指定在分割时使用哪种类型的检测结果作为提示来帮助生成掩码

dilation → 膨胀分割结果的边界

threshold → 阈值,仅检测已识别置信度高于此值的对象

bbox_expansion → 控制在生成轮廓时扩展边界的大小,以确保更好地包含目标对象

mask_hint_threshold → 与mask_hint_use_nagative参数一起使用,用于指定detection_hint的阈值,将掩码区域中的掩码值等于或高于阈值的情况解释为正提示

注意:大于 0 但小于mask_hint_threshold的值不用作否定提示。

mask_hint_use_nagative → 控制是否使用负提示来辅助分割

输出:

combined_mask → 分割节点的深度信息,包含整个图像的分割结果

batch_masks → 分割节点的深度信息,包含批处理中每个样本的分割结果

MASK → 分割节点的深度信息,包含整个图像的分割结果

注意事项

· 模型选择:确保选择合适的SAM模型版本和配置参数,以获得最佳的检测和分割效果。

· 输入数据质量:无论是直接输入图像还是预先分割的结果,输入数据的质量都会影响最终的检测和分割效果。

合理使用SAMDetector(combined)和SAMDetector(segmented)节点,可以实现高效的图像检测和分割任务,满足多种应用场景的需求。

六、Simple Detector(SEGS) / Simple Detector(SEGS/pipe) / Simple Detector for AnimateDiff(SEGS)

与前面几个检测器的功能类似,都是接收模型->分割->输出分割结果,旨在处理不同类型的输入和工作流程,以满足特定应用需求。

14

输入:

bbox_detector → 接收BBOX模型

image_frames → 动画帧的输入图像序列

image → 接收待分割的图片

sam_model_opt → 接收SAM模型

segm_detector_opt → 接收SEGM模型

detailer_pipe → 输入细节调整的参数,使分割结果更加完美

参数:

bbox_threshold → BBOX模型的检测阈值

bbox_dilation → BBOX模型边界框的膨胀参数,用于扩展边界框的范围

crop_factor → 裁剪因子,用于裁剪图像

drop_size → 设置一个尺寸阈值,用于过滤较小目标的参数

sub_dilation → 子图的膨胀参数

sub_bbox_expansion → 子图BBOX模型的边界框扩展参数

sam_mask_hint_threshold → SAM模型的掩码提示阈值

masking_mode → 调节掩码模式

segs_pivot → 选择动画差异检测的基准点

post_dilation → 后期处理膨胀参数

输出:

SEGS → 分割结果的深度信息

注意事项

· 输入数据质量:无论是直接输入图像还是预先分割的结果,输入数据的质量都会影响最终的检测和分割效果。

· 工作流程配置:确保在复杂工作流程中,各节点的连接和数据流动顺畅,以获得最佳处理效果。

合理使用这三个节点,可以实现多种图像处理任务,包括目标检测、图像分割和动画处理,满足不同应用场景的需求。

七、DetailerDebug(SEGS)节点

放大图像并在内部利用 KSampler 对图像进行绘制。

15

输入:

image → 需要重绘的原始图像

segs → 输入分割模型得到的分割结果

model → checkpoints大模型

clip → 提示词编码模型

vae → vae模型

positive → 正向提示词

negative → 反向提示词

detailer_hook → 对模型进行更加精细调整的拓展接口

注意:这里的正向提示词和反向提示词都是针对重绘部分的,用于指导重绘后的图像。

参数:

guide_size → 参考尺寸 **小于的目标图像被放大以匹配,而大于的图像将被跳过,因为它们不需要细节处理**

guide_size_for → 设置guide_size基于什么 **当设置为bbox时,它使用检测器检测到的bbox作为参考;当设置为crop_region时,它使用基于检测到的bbox所识别的裁剪区域作为参考**

注意:选择BBOX时,基于crop_factor的放大图像的大小可能比guide_size大几倍。

max_size → 最大尺寸 **将目标图像的最长边限制为小于max_size的安全措施,它解决了bbox可能变得过大的问题,特别是如果它有细长的形状**

seed → 内置KSampler的种子

contorl_after_generate → 控制种子的变更方式 **fixed为固定种子,increment为每次增加1,decrement为每次减少1,randomize为种子随机**

steps → 去噪步数(也可以理解成生成图片的步数)

cfg → 提示词引导系数,即提示词对结果产生影响的大小 **过高会产生负面影响**

sampler_name → 选择采样器

scheduler → 选择调度器

denoise → 去噪幅度 **值越大对图片产生的影响和变化越大**

feather → 羽化的大小

noise_mask → 控制在修复过程中是否使用噪声掩码 **虽然在不用噪声掩码的情况下,较低的去噪值有时会产生更自然的结果,但通常建议将该参数设置为enabled**

force_inpaint → 防止跳过基于guide_size的所有过程 **当目标是修复而不是细化时,这很有用。小于guide_size的SEGS不会通过减小去匹配guide_size;相反,它们会被修复为原来的大小**

文本框 → 输入通配符的规范,如果保持为空,此选项将被忽略

cycle → 采样的迭代次数 **当与Detailer_hook一起使用时,此选项允许添加间歇性噪声,也可用于逐渐减小去噪大小,最初建立基本结构,然后对其进行细化。

inpaint_model → 使用修复模型时,需要启用此选项,以确保在低于1.0的降噪值下进行正确的内补

noise_mask_feather → 控制羽化操作是否应用于修复过程的蒙版

注意:此选项不能保证图像更自然,同时它可能会在边缘产生伪影,大家按需设置!

输出:

image → 最终重绘后的图片

cropped → 调整图像的大小、去除不必要的部分、集中注意力于特定区域的效果图

cropped_refined → 经过剪裁和进一步处理的图像

cropped_refined_alpha → 经过裁剪和精细化处理后的alpha通道

cnet_images → 蒙版位置图

注意事项

· 输入数据质量:确保输入的分割结果准确无误,因为输入数据的质量会直接影响分析和调试的效果。

· 节点配置:根据具体需求调整DetailerDebug(SEGS)节点的配置参数,以获得最佳的分析和调试效果。

通过使用DetailerDebug(SEGS)节点,可以深入了解和优化图像分割模型的性能,确保分割结果满足实际应用的需求,同时提升模型的开发和调试效率。

八、示例工作流

熟练使用以上节点,你就可以搭建一个简单的“AI换脸”的工作流了。

16

该工作流先是对加载的图片进行脸部的检测和分割,然后将分割的结果、原图、重绘的提示词输入细节修复器中,在细节修复器中,内置的采样器会根据提示词对图片的脸部进行重新扩散,最后输出换脸之后的图。原图和换脸后的图如下所示:

17
18

**孜孜以求,方能超越自我。坚持不懈,乃是成功关键。**

前言

上一篇文章写到如何上架IOS应用到Appstore,其中漏掉了些许期间遇到的小坑,现在补上

审核不通过原因 5.1.1

Guideline 5.1.1 - Legal - Privacy - Data Collection and Storage

5.1.1(ii) Permission Apps that collect user or usage data must secure user consent for the collection, even if such data is considered to be anonymous at the time of or immediately following collection. Paid functionality must not be dependent on or require a user to grant access to this data. Apps must also provide the customer with an easily accessible and understandable way to withdraw consent. Ensure your purpose strings clearly and completely describe your use of the data. Apps that collect data for a legitimate interest without consent by relying on the terms of the European Union’s GeneralData Protection Regulation ("GDPR") or similar statute must comply with all terms of that law.

Issue Description

One or more purpose strings in the app do not sufficiently explain the use of protected resources. Purpose strings must clearly and completely describe the app's use of data and, in most cases, provide an example of how the data will be used.

大概意思就是说你访问相册,拍照,访问位置,上传文件授权提示需要说明你的使用原因,而且要写详细点(B事多)

解决办法

①打开manifest.json文件->源码视图->ios

②添加privacyDescription

image

③添加你项目用到的权限:相册,拍照,访问位置等,有啥写啥,表眀权限申请用途即可

image

image

image

去年也上架过一个项目,是没有这么多b事的,现在上架抓的比较严估计。。。

上述为个人整理内容,水平有限,如有错误之处,望各位园友不吝赐教!如果觉得不错,请点个赞和关注支持一下!谢谢~๑•́₃•̀๑ [鲜花][鲜花][鲜花]


title: useRoute 函数的详细介绍与使用示例
date: 2024/7/27
updated: 2024/7/27
author:
cmdragon

excerpt:
摘要:本文介绍了Nuxt.js中useRoute函数的详细用途与示例,展示了如何在组合式API中使用useRoute获取当前路由信息,包括动态参数、查询参数等,并提供了丰富的计算引用说明,如fullPath、hash、matched等,以及如何正确访问查询参数。

categories:

  • 前端开发

tags:

  • Nuxt.js
  • useRoute
  • 路由
  • API
  • 组件
  • 查询
  • 动态


image
image

扫描
二维码
关注或者微信搜一搜:
编程智域 前端至全栈交流与成长

在nuxt.js开发中,
useRoute
是一个非常实用的组合函数,它能够帮助我们获取当前路由的各种信息,从而实现更加灵活和动态的页面交互。

一、useRoute 函数的基本作用

useRoute
函数的主要作用是返回当前路由的相关信息。在 Vue 组件的模板中,我们可以使用
$route
来访问路由,但在组合式 API
中,就需要使用
useRoute

二、示例展示

以下是一个使用动态页面参数
slug
来通过
useFetch
调用 API 的示例:

<template>
  <div>
    <h1>{{ mountain.title }}</h1>
    <p>{{ mountain.description }}</p>
  </div>
</template>

<script>

export default {
  setup() {
    const route = useRoute();
    const { data: mountain } = await useFetch(`/api/mountains/${route.params.slug}`);
    return {
      mountain
    };
  }
};
</script>

在上述示例中,通过
useRoute
获取到当前路由的
params
中的
slug
值,然后将其拼接到 API 路径中,使用
useFetch
获取数据并在页面中展示。

三、useRoute 提供的计算引用

除了动态参数和查询参数,
useRoute
还提供了以下丰富的计算引用:

  1. fullPath
    :这是与当前路由关联的编码 URL,包含路径、查询和哈希。例如,如果当前 URL

    https://example.com/page?param=value#hash
    ,那么
    fullPath
    将是完整的这个字符串。
  2. hash
    :获取 URL 中以
    #
    开头的解码哈希部分。
  3. matched
    :这是与当前路由位置匹配的规范化的匹配路由数组。
  4. meta
    :可以用于附加到路由记录的自定义数据。
  5. name
    :路由记录的唯一名称。
  6. path
    :获取 URL 中编码的路径名部分。
  7. redirectedFrom
    :记录在到达当前路由位置之前尝试访问的路由位置。

四、查询参数的获取

如果您需要访问路由的查询参数(例如在路径
/test?example=true
中的
example
),那么可以使用
useRoute().query
而不是
useRoute().params

余下文章内容请点击跳转至 个人博客页面 或者 扫码关注或者微信搜一搜:
编程智域 前端至全栈交流与成长
,阅读完整的文章:
https://blog.cmdragon.cn/posts/eb8617e107bf/

往期文章归档: