从B页面进行xx操作后需要跳转到A页面,并定位到AA职位,上图为A页面。
A页面的左侧是div,内层包裹List组件
给div定义ref=leftRef,在代码中写如下:
function scrollTop() { if (leftRef.value) { console.log('99', leftRef.value); nextTick(() => { leftRef.value.scrollTop = 1000; // scrollBy(0, document.body.scrollWidth); }); } } onMounted(async () => { if (router.currentRoute.value.query.id) { positionChooseCode.value = router.currentRoute.value.query.id; positionStatusValue.value = router.currentRoute.value.query.id; } const positionId = router.currentRoute.value.query.positionId; if (!!positionId) { cStore.setPositionId(positionId); } console.log('mounted--positionId', positionId); await getPositionDictionary(positionChooseCode.value, ''); await getDictionaries(); scrollTop(); }); 第一,需要等待数据渲染完成后,再调用scrollTop,设置scrollTop=1000,这样页面初始化滚动条位置会改变。
第二,找到当前职位的高度,也要等职位列表数据渲染完成后,获取
console.log('positionList.value', positionList.value); rowItemId.value = item.id; //找到前面有多少个元素 let index = positionList.value.findIndex((it) => it.id === rowItemId.
通义灵码与GitHub Copilot的对比主要集中在几个方面:代码编写能力、免费性、操作界面和适配性。
首先,在代码编写能力上,虽然GitHub Copilot在整体上要强于通义灵码,但通义灵码的能力也不算弱,并且在某些特定的小类任务上表现更好[1][9]。这表明通义灵码在特定领域具有一定的优势,能够满足开发者在这些领域的特定需求。
其次,关于免费性,通义灵码目前是免费提供的[2][3]。这一点对于开发者来说是一个非常吸引人的特点,因为它降低了使用AI编码辅助工具的成本门槛。
在操作界面和适配性方面,通义灵码与GitHub Copilot的操作基本类似,这意味着用户可以较为容易地从一个工具切换到另一个工具[2][3]。此外,通义灵码的生成速度很快,且与IDE的适配很好,这使得它在实际开发中更加高效和便捷[4]。
综上所述,通义灵码虽然在整体代码编写能力上不及GitHub Copilot,但其免费提供、特定领域的优势以及良好的操作界面和适配性,使其成为了一个值得尝试的AI编码辅助工具。特别是对于那些寻求低成本或特定功能支持的开发者来说,通义灵码是一个不错的选择[1][2][3]。
【福利!!】目前通义灵码使用有盲盒赠送:通义灵码
通义灵码在哪些特定领域具有优势? 通义灵码在特定领域具有明显的优势,主要体现在以下几个方面:
编程教育:通义灵码在教育领域的应用尤为显著,它能够帮助学生学习编程的基础知识,并创造属于他们自己的项目[14]。这表明通义灵码在编程教育方面具有独特的优势,能够使学习过程更加直观和高效。代码解释与智能问答:通义灵码支持30+种语言的代码解释,并能对特定领域的知识进行问答,如阿里云OSS相关问题[18][20]。这一功能使得开发者能够快速理解代码内容和解决技术难题,提高了开发效率。问题解决能力:根据HumanEval测试结果,通义灵码的问题解决率高达66.4%,远超过行业平均水平的50%左右[17]。这一显著成绩代表了它在问题解决方面的卓越能力,无论面临何种问题都能提供有效的解决方案。商业效益增长:在数字商业化领域,通义灵码能够赋能营销策略,提高广告和推广的精准度和效果,从而带动企业收入的增长[23]。这表明通义灵码不仅在技术开发领域有优势,也在商业运营和市场推广方面展现出其价值。 通义灵码在编程教育、代码解释与智能问答、问题解决能力以及商业效益增长等特定领域具有明显的优势。
GitHub Copilot的代码编写能力具体表现在哪些方面? GitHub Copilot的代码编写能力主要体现在以下几个方面:
智能提示和实时编程建议:GitHub Copilot能够根据上下文自动提示代码,为开发者提供实时的编程建议,从而节省编程时间[26]。这意味着它可以根据当前的上下文和已有的代码自动生成编码建议,极大地提高了开发效率和代码质量[29]。广泛的语言支持:GitHub Copilot支持多种编程语言,这使得它能够理解和生成不同语言的代码,满足开发者在不同项目中的需求[26]。理解上下文并生成准确的代码:通过训练大量的开源代码库和编程语言知识,GitHub Copilot能够理解上下文并生成准确的代码。这表明它不仅能够理解代码的意图,还能根据具体的编程任务和环境生成相应的代码[28]。集成在主流编辑器中:Copilot可以直接集成在Visual Studio Code编辑器或者Intellij IDEA中,帮助程序员更快、更轻松地编写代码。这种集成方式使得开发者可以在他们最熟悉的开发环境中直接使用Copilot的功能,进一步提高了工作效率[30]。 GitHub Copilot的代码编写能力主要表现在其智能提示和实时编程建议的能力、对多种编程语言的支持、理解上下文并生成准确代码的能力,以及与主流编辑器的集成能力。这些特点共同作用,使得GitHub Copilot成为了一个强大的编程助手,能够显著提高开发效率和代码质量。
通义灵码和GitHub Copilot的操作界面和适配性有哪些具体差异? 通义灵码和GitHub Copilot在操作界面和适配性方面存在一些具体差异。首先,从收费角度来看,通义灵码目前是免费的,而GitHub Copilot则需要支付费用[32]。这一点对于用户来说是一个明显的区别,因为免费工具通常会吸引更多初学者或预算有限的开发者。
其次,在产品设计维度上,通义灵码的界面设计被描述为简洁明了[33]。这种设计风格可能使得通义灵码对于那些偏好简单直观界面的用户更具吸引力。相比之下,虽然证据中没有直接提到GitHub Copilot的界面设计,但可以推测,作为一个商业产品,其界面设计可能会更加注重用户体验和功能性,以满足更广泛用户的需求。
此外,通义灵码在Chat界面下方提供了一些扩展功能[32],这可能是为了增强其与用户的互动性和实用性。这种设计可能使得通义灵码在某些特定场景下比GitHub Copilot更具优势,尤其是当用户需要这些额外功能来提高工作效率时。
通义灵码和GitHub Copilot在操作界面和适配性方面的具体差异主要体现在收费政策、界面设计风格以及提供的扩展功能上[32][33]。
通义灵码的免费提供政策是否包括所有功能,还是有隐藏费用? 通义灵码被描述为一款不需要充钱就能使用的插件,可以称之为中国的copilot的平替品[34]。然而,这段证据并没有明确说明通义灵码的免费提供政策是否包括所有功能,也没有提及是否有隐藏费用。因此,基于现有的证据,无法确定通义灵码的免费提供政策是否完全包括所有功能,或者是否存在隐藏费用。需要更多的信息来明确回答这个问题。
通义灵码与GitHub Copilot在实际开发中的效率对比如何? 通义灵码与GitHub Copilot在实际开发中的效率对比,可以得出以下结论:
通义灵码总体能力上离GitHub Copilot还有一些差距[35]。这表明虽然两者都是AI人工智能驱动的代码生成工具,但GitHub Copilot在某些方面可能更为先进或成熟。尽管存在差距,通义灵码仍然被推荐为GitHub Copilot的最佳免费平替[35]。这意味着通义灵码在一定程度上能够满足开发者的需求,尤其是在成本敏感的情况下。通义灵码支持VS Code、JetBrains等主流IDE,与GitHub Copilot对标[36]。这表明通义灵码具有良好的兼容性和灵活性,能够适应不同的开发环境和需求。 虽然通义灵码在总体能力上不如GitHub Copilot,但它作为一个免费的替代品,对于预算有限或希望探索新工具的开发者来说是一个不错的选择。同时,它的良好兼容性和灵活性使其成为值得尝试使用的工具[35][36]。因此,在实际开发中,通义灵码与GitHub Copilot各有优势,具体选择应根据个人或团队的具体需求和条件来决定。
参考资料 1. 通义灵码与githubcopilot的对比评测 - 阿里云开发者社区 [2023-11-21]
2. GitHub Copilot 最佳免费平替:阿里通义灵码 - 稀土掘金 [2024-01-01]
3. GitHub Copilot 最佳免费平替:阿里通义灵码原创 - CSDN博客 [2024-01-02]
整合Knife4j 介绍:Spring MVC框架集成Swagger2生成Api文档的增强解决方案 整合参考: https://doc.xiaominfo.com/docs/quick-start
新建:创建一个名为web的SpringBoot3项目
依赖:web模块引入knife4j依赖
<!-- https://mvnrepository.com/artifact/com.github.xiaoymin/knife4j-openapi3-jakarta-spring-boot-starter --> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> </dependency> 配置:在web模块resources目录 application-config.yml文件中添加 # springdoc-openapi项目访问访问地址: http://127.0.0.1:8080/doc.html springdoc: swagger-ui: path: /swagger-ui.html # path: 配置swagger-ui.html/UI界面的访问路径,默认为/swagger-ui.html tags-sorter: alpha # tags-sorter: 接口文档中的tags排序规则,默认为alpha,可选值为alpha(按字母顺序排序)或as-is(按照在代码中定义的顺序排序) operations-sorter: alpha api-docs: path: /v3/api-docs # path: 配置api-docs的访问路径,默认为/v3/api-docs group-configs: # group-configs: 配置分组信息 - group: 'default' # group: 分组名称 paths-to-match: '/**' # paths-to-match: 配置要匹配的路径,默认为/** packages-to-scan: cn.bytewisehub.pai.web # packages-to-scan: 配置要扫描的包的路径,直接配置为Controller类所在的包名即可 # knife4j项目访问访问地址:http://127.0.0.1:8080/doc.html#/home knife4j: enable: true # 设置为true以启用Knife4j增强功能,这将再应用程序中启用Knife4j UI setting: # language: 设置Knife4j UI的语言,默认为zh_cn,可选值为zh_cn或en language: zh_cn #开启生产环境屏蔽 production: false #是否启用登录认证 basic: enable: true username: # 自己设置一个 password: # 自己设置一个 测试:web模块web包下TestController包下新建 Knife4jTestRestController类 注:test目录下新建的非测试类在生成target时会被删掉
目录 什么时候需要分片上传?分片上传流程获取文件专属MD5码接口1:传MD5码获取该文件已上传的片数接口2:将文件分片成promise请求数组promise请求数组做线程池限制处理 p-limit接口3:promise.all执行结束后,请求一个是否合并成功的接口完整主函数待优化的地方: 使用Web Workers进行分片上传 什么时候需要分片上传? 如果将大文件一次性上传,耗时会非常长,甚至可能传输失败,那么我们怎么解决这个问题呢?既然大文件上传不适合一次性上传,那么我们可以尝试将文件分片散上传。
这样的技术就叫做分片上传。分片上传就是将大文件分成一个个小文件(切片),将切片进行上传,等到后端接收到所有切片,再将切片合并成大文件。通过将大文件拆分成多个小文件进行上传,确实就是解决了大文件上传的问题。因为请求时可以并发执行的,这样的话每个请求时间就会缩短,如果某个请求发送失败,也不需要全部重新发送。
分片上传流程 Created with Raphaël 2.3.0 开始 获取文件专属MD5码 接口1:传MD5码获取该文件已上传的片数 将文件分片成promise请求数组 promise请求数组做线程池限制处理 接口2:promise.all并发发送异步请求上传文件分片 接口3:promise.all执行结束后,请求一个是否合并成功的接口 结束 获取文件专属MD5码 import sparkMD5 from 'spark-md5' // 安装下spark-md5包 // 定义hash函数,接受一个名为chunks的数组作为参数 hash = (chunks) => { // 返回一个Promise,这样调用者可以使用.then()或async/await来处理结果 return new Promise((resolve) => { // 创建一个新的sparkMD5实例,用于计算MD5哈希值 const spark = new sparkMD5(); // 定义一个递归函数_read,用于逐个处理chunks数组中的blob function _read(i) { // 如果索引i大于等于chunks数组的长度,说明所有blob都已经处理完毕 if (i >= chunks.length) { // 调用sparkMD5实例的end方法,并传入resolve回调,以返回最终的哈希值 resolve(spark.end()); return; // 读取完成,退出函数 } // 获取当前索引i对应的blob const blob = chunks[i]; // 创建一个新的FileReader实例,用于读取blob的内容 const reader = new FileReader(); // 设置FileReader的onload事件处理函数,当blob内容读取完成后调用 reader.
文章目录 一、sqlmap介绍二、sqlmap命令行参数用法讲解2.1常用用法-u--batch--flush-session--dbms--level--random-agent--user-agent--tamper--technique-p--skip基础用法查询列表2.2 高阶用法-v高阶用法查询列表 一、sqlmap介绍 官网下载地址:https://github.com/sqlmapproject/sqlmapsqlmap 是一款开源的渗透测试工具,可以自动化进行SQL注入的检测、利用,并能接管数据库服务器。它具有功能强大的检测引擎,为渗透测试人员提供了许多专业的功能并且可以进行组合,其中包括数据库指纹识别、数据读取和访问底层文件系统,甚至可以通过带外数据连接的方式执行系统命令。使用方法:python sqlmap.py -参数,sqlmap可以运行在python2.6、2.7和3.x的任何平台上。官方使用指南:https://github.com/sqlmapproject/sqlmap/wiki/Usage 二、sqlmap命令行参数用法讲解 2.1常用用法 -u 使用方法:python sqlmap.py -u URL
-u为基础参数,后跟需要测试的url,通常是GET类型注入的必备参数
–batch 使用方法:python sqlmap.py -u URL --batch
使用–batch参数,可以在所有需要用户输入的部分(通常是询问执行yes还是no),执行默认操作,不需要用户再输入
–flush-session 使用方法:python sqlmap.py -u URL --flush-session
使用–batch参数表示清除当前目标的会话文件。
sqlmap在测试某一目标URL后会生成session文件,该文件保存了本次测试的结果信息。当我们再次测试该目标URL时,会自动加载上一次的结果
当我们想重新测试该目标URL时,可以使用–flush-session清除当前目标的会话文件,可以看到加了参数后,sqlmap对目标URL进行重新测试了。
–dbms 使用方法:python sqlmap.py -u URL --dbms 数据库名
使用–batch参数可指定数据库类型。
sqlmap默认情况下会自动检测Web应用程序的后端数据库管理系统。
在我们明确知道测试的数据库类型时,可以使用–dbms可以指定数据库。
备注:sqlmap完全支持以下数据库管理系统:
MySQLOraclePostgreSQLMicrosoft SQL ServerMicrosoft AccessIBM DB2SQLiteFirebirdSybaseSAP MaxDBInformixMariaDBPerconaMemSQLTiDBCockroachDBHSQLDBH2MonetDBApache DerbyAmazon RedshiftVerticaMckoiPrestoAltibaseMimerSQLCrateDBGreenplumDrizzleApache IgniteCubrid
IRIS
eXtremeDB
FrontBase –level 使用方法:python sqlmap.py -u URL --level 等级
使用–batch参数可指定payload测试复杂等级。共有五个级别,从1-5,默认值为1。等级越高,测试的payload越复杂,当使用默认等级注入不出来时,可以尝试使用–level来提高测试等级。
–random-agent 使用方法:python sqlmap.py -u URL --random-agent
本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭霖 即可关注,每个工作日都有文章更新。
有些小伙伴的消息渠道实在过于灵通,所以这件事我也不用藏着掖着了。
(此处插件指的就是Extension,虽然Extension最标准的翻译应该是扩展)
其实Extension这个功能我们是想要先小范围秘密推出,看看数据再说的。没想到一些海外的极客博主,在我们发布带Extension功能版本的当天,就把隐藏的这个开关给找到了,并立刻引起了很多的新闻报道,这个功能也就藏不住了。
是的,这个功能就是我做的。关于这一点,连我自己都未曾想到过。
长期以来,支持Extension一直是Android版Edge浏览器最强烈的用户呼声,我们内部收到过很多类似的用户反馈。我自己的公众号上也曾有不少朋友留过言,希望Android版Edge能支持Extension。
那个时候,我还能很耐心地跟大家解释为什么Edge Android不支持Extension,反正这个功能和我没什么关系嘛。
有趣的是,你永远不知道明天会发生什么。
2021年底,我们部门来了一位新同事,jiayi。jiayi最早在腾讯做QQ浏览器,之后又去了猎豹浏览器,再后来又去过阿里和快手,现在来到了微软Edge浏览器。
jiayi担任的岗位是Android端架构师,所以他一开始做的也是一些偏基础架构之类的工作。后来我听说他准备去搞Android端的Extension,这就太牛了,Extension可是自从我进入Edge项目组之后就时不时听说的超级难题,确实得是架构师级别的才能搞得定。
结果在2022年底,Edge项目组进行了一次组织架构调整,jiayi不再担任架构师了,而是成为了基础架构组的Manager,还把我划到了他的组里。
新组成立,Manager要和每个组员单独聊天沟通,俗称1:1。jiayi当时找到我,就跟我说:“郭神,Extension就交给你了啊。”
澄清一下,这活确实不是我主动要求的,但却是我主动接下的。因为当时如果我拒绝的话,是可以安排我到其他组继续做传统Android开发的。
但是对于我来说,这是一个非常大的挑战。如果做成功了,不仅能够产生巨大的影响力,同时还能让我跳出Android的技术栈,扩展自己的技术领域(Extension主要都是用C++开发的)。
这活我接了。
考虑到接手之前别人的研究成果总比自己从零开始强,于是我问jiayi之前研究到了哪里,我可以继续接着搞。结果jiayi说,这不是最近太忙了么,还没开始研究呢。。。
不过jiayi还是给我提供了一个重要信息,那就是Android上的Kiwi浏览器是支持Extension的,并且Kiwi还开源了自己的代码。
于是我去查看了一下Kiwi的源码,好消息是,它确实成功支持了Extension。坏消息是,开源的代码已经是4年前的版本了,和现在Chromium的代码差距巨大。
因此,指望着靠照搬Kiwi的代码来让Edge支持Extension是不可能的事情了,但是我仍然从Kiwi的源码中受到了很大的启发。
因为本质上来说,我们并没有想要去发明Extension这个东西,Extension本来就在那里,只是Google不允许它在Android上运行而已。所以我们要做的事情,就是要把PC平台现有的Extension代码给带到Android平台上。
那么具体要怎么带呢?这时我去参考了一下Kiwi的思路:
这是一段GN代码,在Chromium当中,GN可以用来进行编译配置,指定哪些文件参与编译,哪些文件不参与。
而上述的代码中,注释里很明确写了!is_android,也就是说这些代码是不给Android使用的。但是Extension却又依赖这些代码,所以Kiwi使用了if (true),来把这些代码强制带到Android平台上。
不得不说,Kiwi的这种做法虽然暴力,但确实管用。我在思路上进行了部分借鉴,不过写法要远比Kiwi更加优雅,不然这种写法在微软的代码审查上也是过不去的。
当然,这只是一个非常简单的例子,事实上Extension的代码量极其庞大,依赖关系错综复杂。我在整个的实现过程中能深深感受到,Google是完全没打算让Extension能在Android上运行,才会把代码写成这样。所以有小伙伴还期待着Chrome Android能支持Extension的,短期内就不要想了。
另外,只是把Extension相关的代码带到Android平台上并不算完。要知道,带进来的这些代码都不是为Android平台设计的,必然会出现海量的编译错误。
因此,接下来才是更加困难的部分,就是如何让这些Extension关联的代码在Android上能够编译通过。
这个就没有什么特别好的办法了,只能一个一个错误地去解决。
Chromium项目在编译的时候可以通过一个-k number的指令,告诉编译器遇到多少个编译错误之后才停止编译。我把这个数字调到了1000都不能一次性编译完整个项目,可见引入了Extension之后会带来多少的编译问题。
这些所有的编译问题一共花两个多月时间才全部处理完。两个多月时间,只是为了让项目能够编译通过,大家可以想象一下这个过程有多艰辛。
当然,即使所有的代码都编译通过了,也仅仅意味着Extension相关的代码被带到了Android平台上,这和在Edge Android上可以正常使用Extension了仍然是两回事。
因此,接下来的一段时间,我还要对Extension的具体功能进行开发调试,验证整体的Extension框架能否在Android平台上正常运行。不过相比起来,这已经算是回归到比较正常的软件开发流程了,之前做的那些工作才真的是整天摸着石头过河。
最后闯过无数难关,我终于做出了一个能够在Edge Android上运行Extension的Demo,这个项目到这里才算是正式立项。
之后就是以正规的流程去开发这个项目了,但仍有非常非常多的事情要做,包括功能开发、代码质量审查,安全性隐私性审查,应用性能检测等等等等一系列的事情。另外,Extension改动的基本全是桌面端的代码,因此还需要Edge美国团队和印度团队的成员来检查我这边的改动。总之没有一件事情是不花时间的。
最后从上线的日期来看,这距离我接手这个项目已经过去了一年左右的时间,这一年时间里我就只做了这一件事。
不过如此困难的一个项目,功劳也并不都是我一个人的。有许多同事在期间参与到了这个项目当中,并给我提供了帮助。
这里首先我要特别感谢我们组的一位同事,wadai。
wadai差不多参与了半个Extension项目的开发过程,帮助我解决了很多高难度的技术问题,并且还在这个过程中发明出了GN Overlay这种对Edge开发具有重大意义的技术。
我记得在解决编译问题的后期,有大约3000+ undefined symbol的报错。基本就是我们俩一人分一半,逐个解决的。光处理这些问题就得花上至少两周以上的时间,枯燥又乏味,但我们就是这么趟过来的。
另外要感谢的就是jiayi,虽说jiayi没有参与这个项目的研发,但是他作为Manager的一些决策直接决定了这个项目的成败。
开发进入中期的时候,我发现Chromium在Extension的设计上并没有做到很好的独立性,其实它是依赖于很多模块的。而Edge桌面端又会在这些被依赖的模块里做很多自己的功能,最后把Extension带到Android上的同时,我也几乎把半个Edge桌面端的功能都给带到了Android上。
这个事情对我当时打击很大,因为代码写成这个样子,PR文件改动数甚至达到了1000多个,就算Extension在Android上真的能跑起来了,我们也绝无可能上线。那个时候我甚至觉得这是一个注定要失败的项目,想要中途放弃了。
jiayi在这个时候做出了非常重要的决策。他让我先不要管代码写的合不合理,优先就是去实现一个能在Android上运行Extension的Demo,代码后期都可以再进行优化。哪怕是后期真的没有优化空间,这东西就是做不了,我们出过一个可运行的Demo,也比现在什么都没有的情况下结束项目强。
事实上也正如jiayi所说,我们在后期做了很大程度的代码优化工作,最低时PR的文件改动数仅剩200多个。扫清了这些障碍,Extension项目才得以顺利上线。
我还要感谢Edge美国团队的Kevin。Kevin是Edge桌面端的架构师,在我们开发Android端Extension的时候给了我们巨大的帮助。
Kevin特别喜欢中国的文化,他还关注了我的公众号,因此这篇文章他很有可能会看到。
Hey Kevin, I would like to give you a big thanks for everything.
传统的转绘流程是将视频里的所有画面进行逐帧转绘,再拼接起来,这样做的结果就是绘制速度很慢,而且画面的闪烁会很严重,因为AI绘制的画面会非常的不稳定。而在EbSynth当中,我们的流程就有了一些的改变,首先是使用插件将视频拆帧和抠出蒙版,然后提取出图片中动作幅度变化比较大的几幅作为关键帧并进行风格转绘,最后再利用EbSynth绘制出中间的过渡帧,最后再将输出的所有画面组合成完整的视频就可以了。
理解了这个原理,接下来我就将通过这篇文章来全面地介绍EbSynth插件从安装到使用的整个流程。
01、安装流程
首先,我们先下载这个流程的核心程序EbSynth,整个项目所需安装包已在文末打包好了,直接可获取
下载完成之后,将压缩包解压到一个文件夹里即可,这个软件我们要到最后几步才能用到,先留在这里。
接下来是安装EbSynth在WebUI中的一个扩展插件Ebsynth Utility,我们的整个流程都将围绕这个插件来进行,请看文末获取,下载完成之后,安装重启即可,这里就不多赘述了。
安装成功后就可以看到这个界面了。
接下来装第三个东西——FFmpeg,它是一款轻量级的用于视频编解码的工具。请看文末获取下载
将下载的压缩包解压到一个文件夹中备用。
接下来,我们来到win的开始菜单中搜索“环境变量”。
点击“环境变量”。
在“系统变量”中选择“Path”,点击“编辑”。
依次点击“新建”——“浏览”
找到我们刚才下载的“ffmpeg”里的“bin”文件夹。
这样就添加好了。然后把刚才所有的窗口全部点确定。
重启电脑,在开始菜单中搜索“cmd”,打开命令提示符。
输入“ffmpeg -version”
看到一下版本信息,就表示我们已经安装好了。
最后,需要在我们的电脑上安装一个transparent background,它的主要作用是来帮助我们提取蒙版的。网址为:https://pypi.org/project/transparent-background/#files。
我们复制这段代码,粘贴到cmd当中,回车。
等待安装完成就可以了。
如果这一步安装出现问题的,大概是因为秋叶整合包中的python是单独的,我们可以打开秋叶整合包中的python文件夹,然后选中文件夹地址栏,全选为蓝色,删除后,输入CMD,然后出现以下:
E:\Stable Diffusion V4\sd-webui-aki-v4.4\python>
然后再输入python.exe -m pip install transparent-background,也就是:
E:\Stable Diffusion V4\sd-webui-aki-v4.4\python>-m pip install transparent-background
然后就可以成功安装了。
02、视频分帧与蒙版添加
首先,我们先选取一段视频,不要太长,我这里找了一个表演飞天的舞者,长度20秒。尺寸720x1280。
接下来新建一个“feitian”的文件夹,注意路径上不要有中文,文件夹的名字也不要有特殊符号。
填写好项目文件夹地址,并放入视频。
接下来,我们来到“插件设置”,步骤一主要是进行拆帧和抠像。如果你希望将人物抠出来的话,可以将两个“蒙版阈值”分别设置为0.04和0.4,如果你只是拆帧,后面人物和背景一起转绘,则可以不设置。
点击生成,后台就会开始运算了。
生成完毕之后,我们的“feitian”文件夹里就会出现两个新的文件夹。
它们分别是逐帧拆分的图片。
和对每一帧的抠像蒙版。
03、提取关键帧
第二步就是从这些众多的图片中,提取出变化较大的图片作为关键帧,可以通过设置关键帧的间隔来控制。间隔越小,关键帧越多,视频越流畅,但是渲染时间更长。
点击生成,文件夹中又会多一个提取出来的关键帧文件夹。
04、图片转绘
来到图生图当中,选取其中一帧图片,选择合适的模型和参数,进行二次元的转绘,还可以加入controlnet进行控制。
对比一下原图和转绘后的效果。
如果对这个结果很满意,就可以将种子保存下来。
接下来打开“ebsynth utility”脚本,填好工程目录,其它参数可以保持不变,如果需要连背景一起重绘的话,可以关掉蒙版模式。
点击生成,就会开始对我们所有的关键帧进行图生图重绘。
生成完毕之后,我们就又多了一个文件夹,里面就是所有关键帧的转绘。
05、图像放大
进入到“后期处理”当中,对刚才生成的图片进行批量处理。填入输入目录和输出目录,图片尺寸和原视频一样,放大算法选对应二次元的。
经过这一步处理,虽然图像大小没变,但是图片会清晰很多。
06、绘制过渡帧
直接点击生成就好了。
文件夹里面就会生成关于关键帧的ebs文件,这个数量是根据视频的长度定的,短的可能只有一个。
这个时候就要打开我们一开始下载的那个ebsynth的文件了,这里主要是利用它的算法,给我们的每帧之间添加过渡帧。
点击下面的“run all”,软件就会自动开始绘制了。我们要把刚才的5个ebs文件全部运行完。
大家都是考计算机的,直接去官网上验证一点也不难 我去了南京大学的官网,并没有找到相关的通知,目前只能看到南京大学2024硕士研究生招生目录中相关的描述,可以看到还没有改,至于要不要改,真正的官方消息出来之前,都是小道消息。这对于想要报考南大人工智能专业的同学肯定有很大的影响。
因为855专业课和408专业课相差还是非常大的,只有一个数据结构是重合的。
考生该怎么办? 既然有这样的消息放出来了,有可能是有人带节奏,也有可能是真的要改考,放出来消息看看考生的反应
我个人觉得改考的概率应该不大,因为从原来的855专业课改考到408,这个跨度确实是非常大的,而且原来的专业课偏重理论,比如算法,人工智能,概率统计之类的,这种才符合逻辑,而408的专业课原本就是CS的专业课,与人工智能这种偏理论的学科不太想干。
如果要改考,我觉得学校也会及时放出来消息,比如东北大学在3月5号的时候就早早的通知25考研专业课改考408,这样给了考生足够的时间调整:
作为南京大学这样高级学府来说,只要拍板决定的领导不傻,肯定不会给自己招黑的,所以大家如果真的确定要冲南大人工智能就放心的冲!如果实在是不放心,那就先按照408来复习,并且先复习数据结构。
考研别太老实,渣一点多给自己找备胎 其实考408的都是大猪蹄子,每个人心里都门清,不到最后报名的时候,你根本不知道他爱的是哪一个。
所以我真的建议考408的不要太老实,多给最找几个备胎,让自己上岸的成功率高一些,毕竟九月底才报名,大家三月份就开始复习了。到九月底的时候,自己复习的什么样,可以考到什么水平,心里应该就有数了。
现在考408的院校有很多,一共127所,大家要都把这127所院校的信息都搜集在一起,那可就太费事了,所以,我帮助大家完成了这个事情。我做了一个包含300+院校的计算机择校数据库。
整理汇总在了notion的数据库中,方便大家筛选:
这个数据库我在整理的时候,也是花费了巨大的时间,大家考研时间有限,所以直接使用这个数据库可以节省很多时间,之所以放在notion里面是因为notion的数据库筛选功能非常好用,比如我想知道上海地区考408的院校有哪些,只需要设置好条件,就立刻可以选择出来。
每一个院校卡片都可以点进去,整理了包括招生学院,专业课考察科目以及录取分数等信息。不仅如此,我还升级了这个数据库,帮助大家快速找到心仪的院校。
数据库就在文章末尾,大家需要的话自取!能帮一个是一个!
如果你决定报考408,那么我想分享一下我的408备考经验,希望帮助大家少走弯路。
408上岸的关键点 其实408专业课很好复习,而且越复习越上头,第一遍复习可能会慢一点,因为大家本科学过的内容都忘了,学完最难的计算机组成原理,后面基本上就没什么的难的了,操作系统和计算机网络很容易就可以过一遍,主要就是需要记的东西很多。
408上岸的真正关键的是专业课和考研数学都要考的好,考计算机必考考研数学,考研数学的难度一点也不比408低。只要是408没有上岸的,一般都是因为考研数学没有考好,为什么呢,因为408好好学想考的低不太容易,想考的高也不太容易,但是考研数学不好好学,上100分都难!
复习过408的都知道,只要开始复习专业课,就很容易占用到考研数学的复习时间,因此,平衡好408和考研数学的学习很重要,下面我就分享一下如何平衡好408和考研数学的学习。
一、我是如何平衡考研数学和408的学习 3月-6月底 这个时候我是大三下学期,学校开设了一门操作系统,所以,专业课方面,我就跟着学校的老师,用的是学校的教材《计算机操作系统(第四版)》汤小丹版。
专业课其他方面的复习,我只是保持每天用力扣刷刷题目,保持做算法题的手感,不用做太多,毕竟力扣middle难度的题目还是挺耗时间的。每日一题即可。
这个阶段,高等数学处于基础阶段的学习,我每天就跟着张宇老师的视频课,用的是张宇30讲,如果有课(大三下学期还有课),我就在课堂上听。u1s1,有的水课能利用起来就利用起来。
做题方面,专业课我做的是王道讲义上的题目,题目难度并不算大,第一遍做选择题,二刷的时候把大题給刷了
考研数学在基础阶段的时候,我做的是1800题,做了一段时间之后,感觉效果不是很好,虽然1800题的题目比较全,像一个百科全书,但是我想要专精某一类我掌握的比较薄弱的题目的时候,在1800题很难找到类似的题目。
后面我就换成了「知能行考研数学」,我看朋友用这个刷题效果很好,于是我也跟着用。知能行考研数学可以精准的找到我的薄弱点,然后针对薄弱点,有针对性的复习,反复的针对薄弱点训练,直到练会为止。
6月底-9月 这个阶段是复习的黄金时间,一定要把握住,将娱乐活动降到最低,把时间和精力最大化分配到学习上面!
6月份我正式开始专业课的学习,因为前期学过了操作系统,所以我的学习顺序就是数据结构,计算机组成原理,计算机网络,操作系统(再回顾一遍)。
全部都学完差不多要两个月的时间,学习每一科的过程,同步刷王道的讲义。我的视频课也是看的王道的视频。
这个阶段,数学进入强化阶段,这个阶段的主要任务就是做题。我几乎所有的时间都花在做题上面,这个阶段我做的题目油660题+880题还有知能行考研数学。
因为在基础阶段,「知能行考研数学」及时的发现我的薄弱点,然后帮我吃透,练会,所以我的基础非常的扎实!
在强化阶段,我很轻松的就适应了综合度较高的模式。知能行和660题和880题能够很好的配合。
比如知能行有一个AI预猜你会不会的功能,因为我基础阶段在深度使用知能行,他能根据我的刷题数据,预测出来660题还有880题上面哪些题目我会做,哪些题目我不会做,这个功能真的给我节省了不少时间,我会把时间和精力花在那些知能行预测我不会做的题目上面。
9月底-考试 到这个阶段,我们的复习基本上已经成型,如果基础复习的不扎实,那很难有大的提分了。但是如果你前期复习的很扎实,最后三个月就会产生质的改变。
在专业课房买呢,我就是不断的刷真题,然后看王道讲义上的错题。这个时候大家还要回归课本,因为最近几年的408考研,考察的知识比较偏,但是都不会超过课本的范围,王道有些知识点没有涉及到。
最后三个月,考研数学的主要任务除了做真题,就是做一做各个老师出的模拟卷,我这里比较推荐的就是李林六套卷和合工大共创超越卷。
知能行在冲刺阶段对我的帮助也很大,做真题的时候,知能行有一个「真题预测功能」,我还没做,就能预测出来哪一年,我能考多少分,哪些题目我可以做出来,我后来做了一下对应的真题,预测的准确率很高。
除了做真题喝模拟题,最后阶段还要注意整理以前做错的题目,这个很关键,搞懂一道错题要胜过做10道新题!
以上就是我的整个考研数学喝408的备考计划,实操性很强,考研小白可以完全按照我的复习方法来。
二、408实操规划(听话+照做=高分) 资料选择 其实408的资料还是很有限的,没有考研数学的选择余地多,讲义方面,我建议王道!
此外,将王道的视频课程与讲义结合使用效果更加显著。有人提到王道的讲义可能存在一些质量问题,其中可能会有错误。不可否认,毕竟它并非正式的教材。但相较于其他讲义,王道的讲义仍然是一个不错的选择,既有长处又有短处。
在涉及数据结构的学习方面,您也可以参考天勤的数据结构讲义,许多人认为他在代码解释方面做得相当出色。
辅助教材 现在的考研强度,你就算把王道的讲义刷3遍,顶多也只能120分,我的建议是,将课本作为补充材料,补充王道的空白和薄弱部分:
数据结构:《数据结构(C语言版)》严蔚敏。对于零基础且跨领域考研的同学,数据结构可能有一定难度,此时可以参考《大话数据结构》。计算机组成原理:《计算机组成原理(第2版)》唐朔飞。操作系统:《计算机操作系统(第四版)》汤小丹。计算机网络:《计算机网络(第7版)》谢希仁。 真题就直接做王道的真题,它们整理得非常详尽,每年都有新的版本,解答也十分仔细。最重要的是,每道题都有相应的视频解析可供参考,这对于遇到不懂的地方提供了很大帮助。
时间规划: 408基础阶段(6月-9月底)
408的第一轮复习就是跟着王道的视频课把四门课都过一遍,三个月的时间完全可以吧四门课给过一遍,基础阶段我的建议是,听一章的视频课,就把王道书上的课后题给做了。
其实大部分都是选择题,大题也要做,不要留给后面,后面也不会有太多的时间来做。每一章学完之后,可以自己试着画一下思维导图,尽量不要用网上现成的,思维导图可以帮助我们梳理学过的知识点,让我们的学习更有条理。
408强化阶段(10月-11月底)
408强化阶段主要有两个任务:
408真题要刷两遍以上;408要进行第二轮复习; 因为408的内容太多,所以一定要进行第二轮的复习,你进行第二轮复习的时候会发现第一遍学习的很多内容都忘掉了。第二轮复习,王道课本上的课后题可以只做错题,其他题目可以看一下。
强化阶段最主要的就是做真题,做真题可以和第二轮复习结合起来,根据真题反应出来的薄弱点,进行针对性学习是很有必要的。
408冲刺阶段(11月-考试)
复习内容:
翻看真题的错题,回忆是否是知识点未掌握导致错误,如果是,必须马上通过王道书来查漏补缺通过笔记来回忆知识点框架,这个时候必须很迅速的回忆起知识点的内容、特点及需要注意的部分。对于一些内容,需要硬背下来,比如计算机网络的一些知识点,很零碎,但是选择题会考。 480在冲刺阶段的任务主要就是整理前期复习过程中产生的各种错题,还有各种比较零散的知识点,408是需要去背的,比如数据结构的一些模版,计算机组成原理的一些硬件的名称和构造,还有计算机网络和操作系统的很多知识点都需要背。
三、考研数学实操规划(25考研全阶段) 考研数学基础阶段(3月-6月底) 在数学复习基础阶段,给大家一个实用的建议,慢!
此次安装的的jdk版本为1.8,Hadoop版本为3.1.3。
一:实验前准备 1.虚拟机自行下好,要带有vim编辑器
sudo apt install vim 如若懒的安装,可将文章中一些vim的命令替换成
gedit 要打开的文件路径 2.在此次实验之前命令行必须进入超级用户,以免出现不必要的麻烦(权限不足)
sudo su 如下图所示
用户名为root,且末尾带有“#”号。
二:安装JDK 1.执行以下命令,下载JDK1.8安装包(华为云下载)。
wget --no-check-certificate https://repo.huaweicloud.com/java/jdk/8u151-b12/jdk-8u151-linux-x64.tar.gz 2.执行以下命令,解压下载的JDK1.8安装包。
tar -zxvf jdk-8u151-linux-x64.tar.gz 3.移动并重命名JDK包。
mv jdk1.8.0_151/ /usr/java8 4.配置Java环境变量。
source 为读取变量的作用。
echo 'export JAVA_HOME=/usr/java8' >> /etc/profile echo 'export PATH=$PATH:$JAVA_HOME/bin' >> /etc/profile source /etc/profile 5.查看Java是否成功安装。
java -version 三:安装Hadoop 1. 执行以下命令,下载Hadoop安装包(华为云下载)。
wget --no-check-certificate https://repo.huaweicloud.com/apache/hadoop/common/hadoop-3.1.3/hadoop-3.1.3.tar.gz 2. 执行以下命令,解压Hadoop安装包至/opt/hadoop。
tar -zxvf hadoop-3.1.3.tar.gz -C /opt/ mv /opt/hadoop-3.1.3 /opt/hadoop 3. 执行以下命令,配置Hadoop环境变量。
source 为读取变量的作用。
echo 'export HADOOP_HOME=/opt/hadoop/' >> /etc/profile echo 'export PATH=$PATH:$HADOOP_HOME/bin' >> /etc/profile echo 'export PATH=$PATH:$HADOOP_HOME/sbin' >> /etc/profile source /etc/profile 4.
一、直接在AndroidManifest.xml文件中的acitivity加入以下配置:
<intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> </intent-filter> <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/device_filter" /> 二、动态代码获取
private static final String ACTION_USB_PERMISSION = “com.android.usb.USB_PERMISSION”;
///申请权限
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
///注册广播
mContext.registerReceiver(mUsbReceiver, filter);
///获取权限
mUsbManager.requestPermission(mUSBDevice, pendingIntent); // 该代码执行后,系统弹出一个对话框/等待权限
///广播
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
@SuppressLint(“NewApi”)
public void onReceive(Context context, Intent intent) {
String action = intent.
作者公众号 大数据与AI杂谈 (TalkCheap),转载请标明出处
ControlNet以及Lora是什么,玩过stable diffusion AI图像生成的同学应该都不陌生。
一般来说,如果你用以SD 或 SDXL为基础的模型来生成图像,产出的图像往往非常随机,很难对图像的内容做相对精确的控制。尤其是原始的SD和SDXL的底模,拥有很好的图像泛化能力(也就是说能根据提示词输出各种类型的图像),但也使得图像的效果通常不是最佳的,对内容的定向精确化控制的能力往往也不足。
比如你想让生成的图像更接近真人摄影风格一些(肤质,环境,灯光,胶片感等等)
又或者你想精确控制图像生成的内容领域,比如3D建筑模型或者游戏图标LOGO等
也可能你想指定生成人物的容貌特征,比如生成特定人物,或者你自己的头像,又或者你想精确控制人物的姿势,高低胖瘦,画面的布局构造等等。
这时候,你可以使用一些经过Finetune微调训练过的专有模型(比如专门针对摄影图片进行二次训练的模型)来对图像的内容做一些相对确定性的定制化输出。一定程度上,这种在特定图像集内进行二次训练的模型,实际上也就是一种对特定内容或风格的图像进行过拟合的过程,使得输出的内容能够尽可能靠近指定领域的图像,但这么做的结果,往往也导致其图像通用泛化输出能力的丧失,也就是牺牲了宽度换取了深度(比如很多人物画像模型就丧失了绘制风景图片的能力)
所以,你往往需要有各种不同的模型来输出不同的内容(至于Mid Journey等商业产品如何做到泛化内容的高质量输出,我猜大体要不然是模型规模大得多,能容纳更多的知识,要不然就是内部做了不同领域内容的预分流)。比如civitai上就有各种风格的模型可以下载
但是,类似SD这样的开源基础模型的体积基本都比较大(通常在2GB~8GB之间,相对普通消费者用户来说),如果要大量使用的话,存储和加载的代价都比较高,另外,如果你需要生成多种不同的风格或者内容的混合图像呢?那可能得组合就更多了。
那么,有没有更低成本和更灵活的方式呢,这时候就轮到Lora登场了
LORA LoRA全称Low-Rank Adaptation(低秩适配器) ,实际上最早是微软的同学在大语言模型的训练中发明并使用的一种低成本的模型微调技术 (论文的名字就是 LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS https://arxiv.org/pdf/2106.09685.pdf )
其根本出发点也是为了降低大语言模型finetune的代价和大量Finetune模型加载的代价问题,毕竟商业化的语言模型通常体积要远大于图像模型,如果有大量的比如175B尺寸的大语言模型需要定制,其训练,存储和加载的代价即使是大公司也是难以承受的(主要是商业盈利成本方面的可操作性)。只是后来发现LoRA相关原理技术,应用在图像生成领域的模型和场景中,也是特别的适合。
LoRA的基本思想,是大部分的模型微调(或者定制,过拟合等等)过程,可以把微调后的模型等价的看作是一个原有的网络模型上旁路叠加上一个同样尺寸的针对增量微调内容的网络模型。微调训练后的模型的输出,相当于在原有网络模型的输出结果里混合上这个增量微调的网络模型的输出结果。
与此同时,既然是模型的定制化微调,那么我们有理由可以假定,差异的内容应该不会特别的大,也就是说,这个旁路模型的有效信息密度可能相比原先的模型要小的多。那么用同样结构和尺寸的网络模型来存储有效信息密度小得多的数据,是不是就有点浪费呢?
我们知道,大部分的网络模型,其核心数据就是各种网络参数,而这些参数大体上都是以各种大小的矩阵向量的形式存在的。比如我们在前面“通俗深入的理解Sora的架构原理”一文中介绍的SD图像扩散模型中的UNet网络结构,就是由数十个卷积网络层组成,Transformer模型中的各种Cross Attention层也类似
从数学的角度来说,一个矩阵的信息密度,大体可以用矩阵的“秩”(Rank)来粗略衡量(这也是LoRA中Rank的语义来源)
什么是秩呢,学过线性代数的同学应该都懂,忘了也没关系,它等价于一个矩阵中互不相关的行或列的数量。也就是不能通过四则运算从别的行或列的数据组合计算出来的行列有多少。简单来说,就是有多少行列的数据是不重复冗余的,冗余的行列数据越多,可想而知有效信息密度就越低,矩阵的秩也就越小。
而这个秩的上限,就是矩阵行列数的最小值,比如1000x1000的矩阵,秩的上限就是1000。而1000x2或2x1000的矩阵,秩的上限也就只有2(毕竟最多也就两行或两列数据互不相关)。
所以如果矩阵中的有效数据密度很低(或者数据很稀疏),可能它的秩就很低,那我们就没必要用高秩的矩阵来存储这些数据,用低秩的矩阵来近似替代就好了。
那怎么控制参数矩阵的秩呢? 那些参数不都是随机生成然后通过训练得到的嘛,而且,原始网络模型的输入输出的维度也是固定的,你怎么替换呢?
LoRA的方式是把旁路的矩阵替换成两个低秩矩阵的乘积。比如1000x1000的矩阵,用1000x2 和 2x1000的矩阵来替换,后者两个矩阵乘积运算的结果也是一个1000x1000的矩阵,也就是输入和输出可以和原矩阵的维度相匹配,但秩的上限就只有2了(因为按秩的定义,结果矩阵可以用相乘的两个矩阵的两行或两列数据的四则运算得到所有行列的数据)
所以,LoRA实际上并不精确控制参数矩阵的秩,只是限制了秩的上限,至于最终的实际数值还是训练出来的,理论上训练完的模型参数,能充分利用参数矩阵的信息密度上限当然更好,如果不能,那也没办法,但反过来,无论如何,你突破不了理论上限。
这么做的目的,当然是为了减少这部分旁路模型的参数数量,进而也就降低了训练的代价(时间和空间成本),比如上面的例子中,参数的数量就从1000x1000=100万 降低到了 1000x2x2=4000,降低了250倍。
实际训练过程中,原先的基础模型的参数是固定的,在Finetune迭代过程中只需要训练旁路网络的参数。而在使用过程中,只需要替换不同的旁路网络模型(甚至旁路叠加多个不同目的的网络模型),就能实现以较小的代价(训练,存储,推理)支持不同的目标场景输出的目的了。
比如在典型的SD图像生成场景中,一个基础模型的典型大小可能是2GB到8GB之间,而一个Finetune的LoRA的典型大小可能只有16MB到128MB之间(代价就是一个LoRA模型往往只能表达少数的特定语义),而训练的过程,简单的LoRA往往也可能在消费级的显卡上用几十分钟到几十小时的时间范围就能完成。实际使用中,可能也是一个基础模型叠加多个LoRA使用,比如一个LoRA控制画面风格,一个LoRA生成指定人物的外貌特征。
喜欢刨根到底的同学可能还会问,既然没有改变最终的网络结构,为啥叠加一个网络模型以后,就可以控制图像的特征了呢,你不是说AIGC图像生成随机性很大,没法精确控制吗?
确实,的确是没法精确控制,所以本质上Finetune的过程,相对于原模型的泛化能力来说,还是一个类似过拟合的过程,通过减少模型的输出可能性,降模型的输出限制到特定的范围内,来降低这个随机性,从而实现输出特定内容的目的。
实际训练LoRA模型的时候,大体都是选择一些特定方向内容的图片进行反复训练(特定人物图像,特定画风图像),使得LoRA模型学习到特定内容的表达方式。比如,下面这个Ink Painting的风格就是通过LoRA来控制实现的。
当然,有些Silder滑轨调节类的LoRA(比如填充不同数值控制从瘦到胖,皮肤白到黑,画面细节增强)等等,则是通过训练相应特征不同阶段性差异的对比图像来实现的,这个就是具体应用层面的技巧了。
而SD图像扩散模型,如我们之前文章所述,其生成过程是通过去除噪声来实现的,按不同比例混合特定内容的噪声数据,也就能实现对特定内容输出控制的强度大小。在使用的时候,也就可以在原模型的泛化能力和LoRA的过拟合控制之间寻找一个合适的平衡点,来实现对特定内容的输出干预。
ControlNet 那么,有了LoRA为什么还需要ControlNet呢,这两者有啥区别不?
实际上,ControlNet的基本核心思路和LoRA非常类似,也是通过混合旁路网络的输出结果,来实现对原网络模型输出结果的干预和调整。因为是模型叠加,所以和LoRA类似,它也能实现基础模型和一个或多个ControlNet模型按不同比例混合叠加使用之类的效果。
但它和LoRA模型最大的不同,还是在于网络结构和与原模型参数混合方式的设计上。LoRA的原理相对简单粗暴,Low Rank的压缩方式减少了模型体积,但一定程度上也限制了它的能力上限。
而ControlNet对原模型的参数矩阵并没有进行压缩,而是进行了1比1的精确复刻。当然他并没有复刻所有的模型层次,而是只复刻了部分编码层的模型参数结构。实际应用中,是将每一层编码层的输出结果通过一些线性变换再混合回原模型的解码层中来实现对最终输出效果的干预和控制。
因为没有压缩模型参数规模,所以其能力上限很容易理解,比LoRA是要高不少的,也具备更好的泛化能力。当然,ControlNet模型的尺寸相应的也就要大不少(典型的大小比如从500MB到2GB之间)
当然,说它和LoRA的原理类似,只是从对基础模型干预的基本流程和原理的角度来说的。实际上,ControlNet最大的卖点还是在他各种不同的应用模型的具体实现上。
不同于大部分LoRA模型是通过文本的激活和微调控制图像的内容,大部分的ControlNet模型都是以图像作参考来干预内容输出的(能这么做,根本的原因,当然还是因为能力上限空间更高,所以具体的应用模式就有了较大的区别)
一个典型的ControlNet模型的应用方式,大体分为两个步骤
第一个步骤是通过一幅参考图像生成一个引导图像,这个过程其实并没有用到ControlNet的模型,往往是单独训练的各种特定用途的图像算法的模型,用来抽取这个参考图像的某方面的特征。这个抽取特征的模型,可能是各种经典的图像处理算法(比如边缘轮廓检测,模糊,反向,灰度梯度),也可能是其它深度学习算法(比如图像切割,姿态检测)。多数情况下也需要和第二个步骤真正使用到的ControlNet模型配套使用。
第二个步骤才是把这个表达了参考图像指定特征的引导图像给到ControlNet的模型作为输入控制条件,用这个引导图像结合用户的其它文本或图像提示条件,再去干预最终的图像生成结果。比如这个人物姿态和脸部特征检测及控制生成的例子。就是通过ControlNet OpenPose模型来实现的。
使用which python显示当前使用的是/Users/username/anaconda3/bin/python
现在想卸载Anaconda,恢复使用mac系统自带的Python
删除隐藏文件目录
rm -rf ~/.anaconda 修改~/.bash_profile文件,将anaconda相关删除
也有可能不是~/.bash_profile而是~/.zshrc文件,具体使用echo $SHELL查询
vi ~/.bash_profile 最后在mac的应用目录删除Anaconda
查看python版本
python -V 如果提示command not found: python,主要原因是python命令没有映射到已经安装的python版本上,则需要添加python的映射
查看所有版本的python所在安装目录
ls -l /usr/bin/python* 再次查看
python3 -V 建立软连接,添加映射
sudo ln -s /usr/bin/python3 /usr/bin/python 再次查看python映射
ls -l /usr/bin/python* 或者不使用软连接映射,也可使用~/.bash_profile文件配置
# 使用 alias 设置 python 和 pip 的别名 alias python='/usr/bin/python3' alias pip='/usr/bin/pip3' 然后
source ~/.bash_profile 现在可以直接使用python指令了
python -V
文章目录 前言相关链接Nuget选择知识补充JWT不是加密算法可逆加密和不可逆加密 普通Jwt(不推荐)项目环境Nuget 最小JWT测试在WebApi中简单使用简单使用运行结果 WebApi 授权,博客太老了,尝试失败 WebApi .net core 8.0 最新版Jwt (微软官方集成)重新新建一个Webapi简单Controller 最简单的Jwt认证获取JwtConfig新建JwtHelper类完善JwtConfig授权授权测试 能通过Jwt的请求PostMan测试过期测试 Swagger 全局Header 获取Jwt中的信息简单封装一下 Jwt授权模式基础讲解简单的【角色授权】获取不同权限的Token不同权限的jwt认证 封装好的代码总结 前言 我一起写后端Api我都是直接裸连的,但是现在为了规范一些,我还是打算上一个JWT功能
相关链接 ASP.NET Web API 2系列(四):基于JWT的token身份认证方案
Jwt-dotnet github
Nuget选择 选好了模板,就进去看看号了,42M的下载量已经很高了,一般来说,只要超过500k的下载量,基本就是一个稳定的代码仓库了。
进去看看里面写的怎么样
可以看到写的还是比较清晰的
知识补充 JWT不是加密算法 JWT全名 JSON Web Token。Token这部分才是加密得出来的,JWT只是一个网页认证策略。
可逆加密和不可逆加密 无论是可逆加密还是不可逆加密,都需要一个自定义的【Key】来辅助加密。可逆加密就类似于一元二次方程,key就类似于修改方程各项的系数,【Key=125】得到【x^2+2x+5】。我的原文是【1】,得到的密文就是计算后的结果【8】。所以加密比解密要方便。
不可逆加密就是不能逆向解决的方程,比如高阶方程【x^7- x^3 +5x-3】,正向好算,逆向基本不可解。
所以现实使用的时候,可逆加密一般是解密后使用。因为可逆,所以可以用于复杂的敏感信息。
不可逆加密是判断加密后的的密文是否相同,比如密码,是不能泄漏的,所以我们的密码只能重置,因为系统也不知道原密码是什么。
普通Jwt(不推荐) 项目环境 visual studio 2022.net core 8.0win 10 Nuget 最小JWT测试 我们先不管JWT是如何添加到ASP.NET Core里面的,我们先测试JWT的加密和登录验证的功能。
/// <summary> /// 加密密钥 /// </summary> private static readonly string jwtKey = "
Topaz Photo AI中文汉化安装版是一款强大的人工智能降噪软件,允许用户使用复杂的锐化算法来提高图像清晰度,还包括肖像编辑选项,如面部重塑、肤色优化和面部表情增强,功能强大!鉴于官方提供的模型在中国大陆下载特别慢,有的时候中途还出错,特意给大家整理出独立的下载链接。
http://models.topazlabs.com/v1/WhiteBalanceData-v2.bin
http://models.topazlabs.com/v1/apnb-v2-fp32-512x512-ov.tz2
http://models.topazlabs.com/v1/clc-v2-fp32-512x512-ov.tz2
http://models.topazlabs.com/v1/clc-v3-fp32-512x512-ox-rt809-rt.tz2
http://models.topazlabs.com/v1/clc-v3-fp32-512x512-rt809-rt.tz2
http://models.topazlabs.com/v1/dnt_beta-v5-fp32-512x512-ov.tz2
http://models.topazlabs.com/v1/dnt_beta-v5-fp32-512x512-rt809-rt.tz2
http://models.topazlabs.com/v1/draw_linear-v1-fp32-512x512-ov.tz2
http://models.topazlabs.com/v1/draw_linear-v1-fp32-512x512-rt809-rt.tz2
http://models.topazlabs.com/v1/drw_native-v1-fp16-512x512-ov.tz2
http://models.topazlabs.com/v1/drw_native-v1-fp32-512x512-rt809-rt.tz2
http://models.topazlabs.com/v1/drw_standard-v1-fp16-512x512-ov.tz2
http://models.topazlabs.com/v1/drw_standard-v1-fp32-512x512-rt809-rt.tz2
http://models.topazlabs.com/v1/expog-v1-fp16-512x512-1x-ov.tz2
http://models.topazlabs.com/v1/expog-v1-fp32-512x512-1x-rt809-rt.tz2
http://models.topazlabs.com/v1/expoi-v1-fp32-256x256-1x-ov.tz2
http://models.topazlabs.com/v1/expoi-v1-fp32-256x256-1x-rt809-rt.tz2
http://models.topazlabs.com/v1/gclc-v1-fp16-128x128-2x-ov.tz2
http://models.topazlabs.com/v1/gclc-v1-fp16-128x128-4x-ov.tz2
http://models.topazlabs.com/v1/gclc-v1-fp16-96x96-2x-ov.tz2
http://models.topazlabs.com/v1/gclc-v1-fp16-96x96-4x-ov.tz2
http://models.topazlabs.com/v1/gclc-v1-fp32-128x128-2x-rt809-rt.tz2
http://models.topazlabs.com/v1/gclc-v1-fp32-128x128-4x-rt809-rt.tz2
http://models.topazlabs.com/v1/gclc-v1-fp32-192x192-2x-ov.tz2
http://models.topazlabs.com/v1/gclc-v1-fp32-192x192-2x-rt809-rt.tz2
http://models.topazlabs.com/v1/gclc-v1-fp32-192x192-4x-ov.tz2
http://models.topazlabs.com/v1/gclc-v1-fp32-192x192-4x-rt809-rt.tz2
http://models.topazlabs.com/v1/gclc-v1-fp32-96x96-2x-rt809-rt.tz2
http://models.topazlabs.com/v1/gclc-v1-fp32-96x96-4x-rt809-rt.tz2
http://models.topazlabs.com/v1/gfclc-v1-fp32-512x512-ov-11.tz
http://models.topazlabs.com/v1/gffm-v1-fp32-512x512-ov.tz2
http://models.topazlabs.com/v1/gfg-v1-fp16-512x512-ov.tz2
http://models.topazlabs.com/v1/gfg-v1-fp32-512x512-rt809-rt.tz2
http://models.topazlabs.com/v1/gfp-l-v1-fp32-2048x2048-ov.tz2
http://models.topazlabs.com/v1/gfp-s-v1-fp32-1024x1024-ov.tz2
http://models.topazlabs.com/v1/gfpf-v1-fp16-48x48-ov.tz2
http://models.topazlabs.com/v1/ggi-v1-fp32-192x192-2x-ov.tz2
http://models.topazlabs.com/v1/ggi-v1-fp32-192x192-4x-ov.tz2
http://models.topazlabs.com/v1/ggi-v2-fp32-192x192-2x-rt809-rt.tz2
http://models.topazlabs.com/v1/ggi-v2-fp32-192x192-4x-rt809-rt.tz2
http://models.topazlabs.com/v1/ggn-v3-fp16-128x128-2x-ov.tz2
http://models.topazlabs.com/v1/ggn-v3-fp16-128x128-4x-ov.tz2
http://models.topazlabs.com/v1/ggn-v3-fp32-128x128-2x-rt809-rt.tz2
http://models.topazlabs.com/v1/ggn-v3-fp32-128x128-4x-rt809-rt.tz2
http://models.topazlabs.com/v1/ggn_ap-v2-fp16-128x128-ov.tz2
http://models.topazlabs.com/v1/ghq-v1-fp16-96x96-2x-ov.tz2
http://models.topazlabs.com/v1/ghq-v1-fp16-96x96-4x-ov.tz2
http://models.topazlabs.com/v1/ghq-v1-fp32-96x96-2x-rt809-rt.tz2
http://models.topazlabs.com/v1/ghq-v1-fp32-96x96-4x-rt809-rt.tz2
http://models.topazlabs.com/v1/gmp-v1-fp32-192x192-2x-ov.tz2
http://models.topazlabs.com/v1/gmp-v1-fp32-192x192-4x-ov.tz2
http://models.topazlabs.com/v1/gmp-v2-fp32-192x192-2x-rt809-rt.tz2
http://models.topazlabs.com/v1/gmp-v2-fp32-192x192-4x-rt809-rt.tz2
http://models.topazlabs.com/v1/isoa-v1-fp32-512x512-ov.tz2
http://models.topazlabs.com/v1/isoa-v1-fp32-512x512-rt809-rt.tz2
http://models.topazlabs.com/v1/isob-v1-fp32-512x512-ov.tz2
http://models.topazlabs.com/v1/isob-v1-fp32-512x512-rt809-rt.tz2
http://models.topazlabs.com/v1/lmx-v1-fp16-512x512-ov.tz2
http://models.topazlabs.com/v1/s_mask_l_flexible-v2-fp16-320x320-ov.tz2
http://models.topazlabs.com/v1/sdi_dec-v2-fp16-512x512-ov.tz2
http://models.topazlabs.com/v1/sdi_dec-v2-fp16-512x512-ox.tz2
http://models.topazlabs.com/v1/sdi_embed0.bin
http://models.topazlabs.com/v1/sdi_enc-v2-fp16-512x512-ov.tz2
http://models.topazlabs.com/v1/sdi_enc-v2-fp16-512x512-ox.tz2
http://models.topazlabs.com/v1/sdi_imdn-v1-fp32-96x96-2x-ov.tz2
http://models.topazlabs.com/v1/sdi_unet-v2-fp16-64x64-ort.tz2
Android 11 引入了强制执行分区存储的限制,导致应用默认不能访问外部文件。
针对以前涉及较多文件的操作,可采用申请所有文件访问权限的方式来解决这一问题,实现方式如下。
(虽然这样做安全性低,官方并不推荐这样,但确实最快适配原有应用程序的方式)
1. AndroidManifest.xml中添加如下内容:
用于声明应用程序需要使用 MANAGE_EXTERNAL_STORAGE 权限。同时,使用了 tools:ignore=“ScopedStorage” 来忽略与分区存储(Scoped Storage)相关的 Lint 检查。
<!-- Android11额外添加 --> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" /> 2. 申请所有文件访问权限,该操作会跳转到所有文件权限申请页面
// 请求文件访问权限的请求码,可以是任意整数值 private static final int REQUEST_MANAGE_FILES_ACCESS = 2; //申请所有文件访问权限 public void requestPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { //判断是否有管理外部存储的权限 if (!Environment.isExternalStorageManager()) { Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); intent.setData(Uri.parse("package:" + getPackageName())); startActivityForResult(intent, REQUEST_MANAGE_FILES_ACCESS); } else { // TODO: 2023/11/22 // 已有所有文件访问权限,可直接执行文件相关操作 } } else { // TODO: 2023/11/22 //非android11及以上版本,走正常申请权限流程 } }
Caused by: com.mysql.cj.exceptions.CJException: Access denied for user 'root'@'localhost' (using password: YES) 报错:springboot项目启动时一直报连接数据库有误 报下面三个错误:
Caused by: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is java.sql.SQLNonTransientConnectionException: Could not create connection to database server. Attempted reconnect 3 times. Giving up.
Caused by: java.sql.SQLNonTransientConnectionException: Could not create connection to database server. Attempted reconnect 3 times. Giving up.
Caused by: com.mysql.cj.exceptions.CJException: Access denied for user 'root'@'localhost' (using password: YES) 被这困扰了很久,所以来记录下。网上有很多贴子,有相关的解决问题,但是对我的没有效果。大家可以先尝试按照别的帖子解决下,下面贴一下别的博主链接连接数据库报错Access denied for user 'root'@'localhost' (using password:YES)
终究是一个非常痛苦的夜晚。我没想到我会活着这么痛苦。
帮别人做一个小功能。把一个 android 设备里面固定目录下的log 导出到本地,然后打成压缩包.
需求很简单,在听到这个需求之后,我当时的预估时间是10分钟到1小时最多,不会超过1小时。。。。
我没想到,真正让我痛苦的,不是遇到了 windows 文件夹权限的问题,而是没有遇到这个问题,但是我以为我遇到的是这个问题…
这句话有的绕口,但是读完之后你就知道,这句总结还算到位.。
长话短说,我在使用 open()函数的时候,传入了一个文件夹作为参数,比如open("D:\\Games"), 然后 python 一直报错,说权限不足。
PermissionError: [Errno 13] Permission denied: 'D:\\Projects' 如果是一个正常人类可能会去查看API,或者网上找一下原因。我直接去网上找原因了。因为我要打开的这个文件夹比较特殊,是从其他设备上面导过来的,导致我以为是 windows 权限的问题。
因为网上有很多很多提示Windows 权限的报错。
基于此,我在网上找了好久,主要的方向就是,怎么去满足这个权限,不让它open失败。当然,没有效果的,因为根本原因是:通过open(file_path), 这个 file_path如果是一个目录而不是文件的话,就必然报错提示权限不足。
当然,也不能说是一无所获。
我找到了以下几种可能可以避免Windows 权限的解决方案了,只是目前用不上。
使用管理员权限去执行这个程序脚本。
具体做法就是,找到 cmd.exe 这个程序,用鼠标右击去通过管理员的权限运行,然后在这个 cmd 窗口里面去执行脚本。通过多进程的方式去执行。
这个就更脑洞大开了。我看到的一个现象是,我通过给目标文件夹设置权限之后,不能立即去打开,但是把程序关闭,再次执行,又可以运行了。(当时情况特殊,后面我无法复现这个场景了)基于这么一个现象,我想到的原因是,因为当前进程跟之前的权限有关联,新启动的进程可以看到目标文件夹已经有权限了,所以用新的进程去打开。 这个地方门道还有点多,我一共使用了两种方式:
第一种是使用 multiprocessing.Process的方式去开启子进程,然后把提权的操作放在这个子进程里面,然后在这个子进程执行结束之后,再去在父进程里面去调用 open() 函数。这个 multiprocessing 还是蛮不错的,它可以开任意多个进程,而且可以传参数,可以进行进程之间的数据共享。第二种就更牛批了,我都佩服自己,对于第二种实现方式。因为第一种方式也是失败(现在当然知道失败是因为open不能去传文件夹路径作为参数,但是当时不清楚),于是就想到,是不是因为这个父子进程之间也是互相关联的呢,导致在父进程依然不能获取到正确的权限。那么,我直接把当前进程关闭,重新开一个进程,这样总不会互相影响了吧。所以就有了第二种实现方法。大体的实现逻辑就是利用 sys.argv去自己给自己传参数,实现一个脚本在运行不同次数的时候,执行的逻辑不同,然后每次执行都是一个新的进程,完全不会被前面一次执行所影响。 为了实现第二种不互相影响的实现,我还搞了一个临时文件用来存储上一次执行的结果。因为不能互相影响导致前面执行的内存数据,在下次执行的时候无法读取,因为上一次的程序已经执行结束了,这些内存里面的数据也就消失了。那么,我就搞一个文本文件,相当于数据库一样,去记录上一次执行的结果,然后第二次执行的时候可以去读这个文件的内容,然后再执行具体的逻辑。
哎,贴一下代码,不过,因为一直失败,所以前面有的代码没有保留,直接被删除了。不过第二种实现方式倒是保留了,有兴趣的可以看看。
这里最困难不是这两种实现方案,而是 Windows 的文件夹权限处理。这一块的文档不是很多,然后也有一些文档写的比较详情,但是里面的专业术语太多了,看不懂。
cacls Music /p everyone:f /t ====> 只有 everyone 了,但是要手动确认 cacls.exe Music /t /e /g everyone:F ====> 多了一个 everyone 但是反应迟钝。 cacls Music /p everyone:f /t /e /g ====》 无效 echo y|cacls Music /p everyone:f /t ===> 只有一个 everyone, 并且不需要手动确认了 这几个命令我找了好久才找到,也都实验了。还有一些其他的无效的命令,就没有记录了。
将公司Kafka集群的Cluster name, Kafka Cluster Version, Zookeeper Host等配置信息按照要求填写到相关设置项中.
使用
==
预览
==
Kafka Tool提供了集群Brokers,Topics,Consumers信息预览功能,查看指定Topic下消息时支持关键词过滤等。
选中Topic,进入topic设置详情页,从页面中可以设置从Oldest还是Newest位置读取消息,同时可以设置读取数据的格式(String或者ByteArray)。由于测试Topic中数据是JSON格式,演示中将key和value都设置为String格式,设置完成后如果update没有变为灰色需要手动点击update更新配置。
可以通过”详情展示“ 按钮来查看详情,也可以选择读取位置。
在详情展示页面,支持修改查看消息的格式类型,在Text、Hex、JSON与XML之间任意切换
分区管理,新增和删除
==========
在 ”topic分区管理“中,点击 ”+“ 按钮,设置 ”topic“ 信息(名字,分区数,副本数),即可创建topic。
有创建当然也会有删除啦,在topic分区管理中,选择需要删除的分区,点击 ”ד 按钮,弹出来的提示框中选择是,即可删除topic。
删除操作一定要慎重!请仔细确认!!!
删除操作一定要慎重!请仔细确认!!!
删除操作一定要慎重!请仔细确认!!!
为分区增加消息
=======
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
最后 Java架构学习技术内容包含有:Spring,Dubbo,MyBatis, RPC, 源码分析,高并发、高性能、分布式,性能优化,微服务 高级架构开发等等。
还有Java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板可以领取+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书+2021年最新大厂面试题。
视频+一线大厂面试宝典+面试简历模板可以领取+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书+2021年最新大厂面试题。
[外链图片转存中…(img-S1qhLLyv-1710434046933)]
本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录
目录 1 提示词1.1 分类和书写方式1.1.1 内容型提示词1.1.2 标准化提示词1.1.3 通用模板 1.2 权重1.2.1 套括号1.2.2 数字权重1.2.3 进阶语法 1.3 负面提示词 2 参数详解2.1 Sampling steps2.2 Sampling method2.3 Width, Height2.4 CFG Scale2.5 Seed2.6 Batch count, Batch size 3 新手技巧3.1 翻译大法3.2 借助工具3.3 抄作业 感谢前辈种树:哔哩哔哩
1 提示词 Prompts:提示词,告诉AI我们要画什么,多多益善,需要英文书写。提示词以词组为单位,不需要像完整的句子那样需要有完整的语法结构。
词组之间需要插入英文半角逗号作为分隔符,可以分行,每一行的末尾最好也加上分隔符。
1.1 分类和书写方式 1.1.1 内容型提示词 人物及主体特征
服饰穿搭:white dress
发型发色:blonde hair, long hair
五官特点:small eyes, big mouth
面部表情:smilling
肢体动作:stretching arms
场景特征
室内室外:indoor / outdoor
大场景:forest, city, street
小细节:tree, bush, white flower
环境光照
一、vector的简单认识 vector是表示可变大小数组的序列容器。 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素 进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存 储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末 尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。
二、vector的简单模拟实现 vector的底层我模仿库里面的实现方法,设计了三个指针:
class vector { public: typedef T* iterator; private: iterator _start; // 指向数据块的开始 iterator _finish; // 指向有效数据的尾 iterator _endOfStorage; // 指向存储容量的尾 } 以及正向迭代器,不可修改迭代器,各种构造函数,析构函数, reserve(预开辟出空间,字符串还是原来的大小(一般不缩容)),resize(将vector设定为指定大小,字符串占满所开辟的空间),push_back(尾插),pop_back(尾删),insert(在pos位置上插入x,并返回pos位置的地址),erase(删除pos位置上的元素,并返回该位置的地址)。
注意事项:迭代器失效 以reserve为例,当reserve开辟新空间时会释放原来的旧空间,导致_start,_finish都不是原来的_start,_finish,如果此时贸然对_start,_finish进行运算,很可能会导致程序崩溃。我的做法是先记录下原来vector的长度,再用_start加上记录下的长度,就能得到正确的_finish,具体实现见下面代码。
完整代码 #pragma once #include <iostream> using namespace std; namespace sxb { template<class T> class vector { public: typedef T* iterator; private: iterator _start; // 指向数据块的开始 iterator _finish; // 指向有效数据的尾 iterator _endOfStorage; // 指向存储容量的尾 public: // Vector的迭代器是一个原生指针 typedef const T* const_iterator; iterator begin() { return _start; } iterator end() { return _finish; } const_iterator cbegin() const { return _start; } const_iterator cend() const { return _finish; } // construct and destroy vector() {} //构造一个长度为n,值为value的vector vector(int n, const T& value = T()) { _start = new T[n+1]; _finish = _start + n; _endOfStorage = _start + n; for (int i = 0; i < n; i++) { *(_start + i) = value; } } template<class InputIterator> //利用一段迭代器构造 vector(InputIterator first, InputIterator last) { int len = 0; while (first !