• [技术干货] 基于MATLAB的指纹识别算法仿真实现-转载
    一、理论基础        在指纹图像预处理部分,论文对预处理的各个步骤包括规格化、图像分割、中值滤波、二值化、细化等以及各个步骤的方法进行了深入的分析和研究,选择了一种图像预处理方案。在指纹特征提取部分,采用基于Matlab实现的指纹细节特征提取方法,并给出了去伪算法。指纹特征提取是从细化后的指纹图中得到细节特征点(即端点和分叉点),此特征点含有大量的伪特征,既耗时又影响匹配精度。采用了边缘去伪和距离去伪,使得特征点去伪前后减小了近1/3,然后提取可靠特征点信息,以便实现指纹匹配。       指纹识别技术是指使用取像设备读取指纹图像,通过识别软件提取出指纹图像中的特征数据,然后根据匹配算法得到的结果鉴别指纹所有人身份的生物特征识别技术。         指纹识别系统主要涉及三大步骤:指纹图像预处理、特征提取、特征匹配三个部分,其中预处理部分又可分为归一化、图像滤波增强、二值化和细化等几个步骤。系统流程框图如图1-1所示。下面对这三个个部分做一下简单的介绍。          指纹图像处理中物体的形状信息是十分重要的,为了提取指纹图像特定区域的特征,对指纹图像通常需要采用细化算法处理,得到与原来指纹图像形状近似的由简单的弧或曲线组成的图形,这些细线处于物体的中轴附近,这就是所谓的指纹图像的细化。       细化方法不同,细化结果就有差异。在指纹识别中要求在不改变原来指纹图像的拓朴连通性的同时,细化的结果应为严格的八邻域图像骨架;纹线中除去特征点以外,每个像素均只与相邻两个像素为八邻域,抹去任意一像素都将破坏纹线的连接性。概括起来说就是纹线细化处理要满足收敛性、连接性、拓朴性、保持性、细化性、中轴性、快速性的要求。目前为止,关于细化方法的研究工作已有很多成果,所采用的方法从使用的观点来看,比较多的是采用模板匹配的方法(如迭代法、OPTA单连通法等)。这种方法是根据某个像素的局部邻域(如3×3,5×5等)的图像特征对其进行处理,此外也有采用边缘搜索编码、外轮廓计算以及神经网络等细化方法。从处理的过程来看,主要可以分为串行和并行两类,前者对图像中当前像素处理依据其邻域内像素的即时化结果,且不同的细化阶段采用不同的处理方法;后者对当前的像素处理该像素及其邻域内各像素的前一轮   迭代处理的结果,自始至终采用相同的细化准则。         对于任意形状的区域,细化实质上是腐蚀操作的变体,细化过程中要根据每个像素点的八个相邻点的情况来判断该点是否可以剔除或保留。          细节特征提取的方法分为两种:一种是从灰度图像中提取特征,另一种是从细化二值图像中提取特征。直接从灰度图像中提取特征的算法一般是对灰度指纹纹线进行跟踪,根据跟踪结果寻找特征的位置和判断特征的类型。这种方法省去了复杂的指纹图像预处理过程,但是特征提取的算法却十分复杂,而且由于噪声等因素影响,特征信息(位置、方向等)也不够准确。目前大多数系统采用第二种方法,从细化二值图像中提取特征,该方法比较简单,在得到可靠的细化二值图像后,只需要一个3×3的模板就可以将端点和分叉点提取出来。          特征点提取的好坏将直接影响匹配的结果。现实中,指纹输入时,由于汗渍、干燥、按压力度不同等影响,得到的指纹图像大都含有断纹、褶皱、模糊、灰度不均匀等质量问题,虽然经过预处理,图像质量会有所改观,但预处理算法对各个指纹的适应性和有效性也会不同,并且会引入新的噪声,因此得到的细化二值图像往往含有大量的伪特征点。伪特征点不仅会影响匹配的速度,严重的会影响整个识别的正确率。所以提取特征点后要进行去伪处理,尽可能滤除伪特征点、保留真特征点。实践中发现,伪特征点的数量一般占总特征数量的一半以上,所以去伪是必不可少的过程。去伪过程可以在两个阶段进行:一是在特征提取之前对细化二值图像进行平滑、去除毛刺、连接断纹等操作,然后提取特征作为真特征;另一种是在特征提取之后,根据特征之间的相互关系,尽可能准确的识别伪特征点并滤除它们。前者直接对图像进行修补,操作比较复杂,容易引入新的伪特征;后者对特征提取后的数据进行判断,识别比较麻烦,但是速度较快本文采用第二种方法,即从已提取的特征点中滤除伪特征,保留真特征。  二、核心程序 clc; clear all; close all; warning off;   %读取图片 %注意,为了和论文中的结果对应起来,我们使用论文中的图片进行代码的测试 I=imread('images.jpg'); if isrgb(I) == 1    I = rgb2gray(I); else    I = I; end I_tmp = I;     figure; subplot(121); imshow(I);   %图像预处理,去噪滤波 I = medfilt2(I,[3,3]);%进行中值滤波; subplot(122); imshow(I);   %背景分割 %图像二值化处理 %首先进行归一化 I=norms(I,120,600);     figure; %为了显示处理效果,需要数据类型转换 subplot(121); imshow(double(I));   Ker   = 8; [m,n] = size(I); m1    = m/Ker; n1    = n/Ker; for i=1:m1    for j=1:n1        %设定门限       t=mean2(I((i-1)*Ker+1:(i-1)*Ker+Ker,(j-1)*Ker+1:(j-1)*Ker+Ker));       for k=(i-1)*Ker+1:(i-1)*Ker+Ker           for l=(j-1)*Ker+1:(j-1)*Ker+Ker               if I(k,l) < t                   I(k,l)=1;               else                    I(k,l)=0;               end           end       end    end end     %为了显示处理效果,需要数据类型转换 subplot(122); imshow(double(I));           %异常点的处理 for i=1:m     for j=1:n         if I(i,j)>1             I(i,j)=0;         end     end end for i=1:m     for j=1:n         if I(i,j)==1             for k=1:j                 I(i,k)=1;             end             break;         end     end end for i=1:m     for j=n:-1:1         if I(i,j)==1             for k=n:-1:j                I(i,k)=1;             end             break;    end     end end for i=1:m     for j=1:n         if I(i,j)==1             I(i,j)=0;         else             I(i,j)=1;         end     end end figure; %为了显示处理效果,需要数据类型转换 imshow(double(I));   %对细化前的图像进行维纳滤波 figure; subplot(121); imshow(double(I));   I=wiener2(I,[3 3]);   subplot(122); imshow(double(I));   %以下为细化处理 [r,c] = size(I); for i1=1:r;     for j1=1:c         if (I(i1,j1)==1)            I(i1,j1)= 255;             I2      = I;         end     end end     y = thinning(I2); figure; imshow(y);     %以上的代码是你之前的第三章的代码的仿真图 %以下的代码是对应第四章的仿真图 %首先需要提取指纹图像的中心点——对应论文的“图4-1提取中心点后的细化图像” [XC,YC] = find_central_point(y);   %找寻细化图像的特征点 %特征点的建立——对应论文的“图4-2特征点提取后的图像” [dpx3,dpy3,dpcount3,fpx3,fpy3,fpcount3]=characterpoint(y); figure; imshow(y); hold on; plot(dpy3,dpx3,'o'); hold on; plot(fpy3,fpx3,'+'); hold on; plot(XC,YC,'*r');  hold off;           %以下代码是对最后的识别结果和数据库中的进行匹配 %计算每个特征点到中心点的距离, Dpcount=size(dpx3,2); Fpcount=size(fpx3,2); for i=1:Dpcount     DP_Feature(i)=sqrt((dpx3(i)-YC)^2+(dpy3(i)-XC)^2); end for j=1:Fpcount     FP_Feature(j)=sqrt((fpx3(j)-YC)^2+(fpy3(j)-XC)^2); end      %计算指纹图像的方向角 theta = func_theta(I_tmp);   %建立待识别的特征库 for i=1:Dpcount     PointOfModel(i,1)=1;     PointOfModel(i,2)=DP_Feature(i);     PointOfModel(i,3)=theta(dpx3(i),dpy3(i))-theta(YC,XC); end for i=Dpcount+1:Dpcount+Fpcount     PointOfModel(i,1)=2;     PointOfModel(i,2)=FP_Feature(i-Dpcount);     PointOfModel(i,3)=theta(fpx3(i-Dpcount),fpy3(i-Dpcount))-theta(YC,XC); end         %调用database_feature计算得到PointOfMatch I=imread('database/22.tif');   if isrgb(I) == 1    I = rgb2gray(I); else    I = I; end PointOfMatch = database_feature(I);   %调用MATCH函数进行匹配 %图4-3 匹配后的图像 NUM = 90;%这个参数是新加的,设置相似度,即多少个匹配点对上才算成功 Count=match(y,PointOfModel,PointOfMatch,dpx3,dpy3,fpx3,fpy3,NUM);        三、测试结果 边缘保护  请查看原文图片  进行适应性去噪过滤处理  请查看原文图片  细化处理得到的结果  请查看原文图片 二值化图像细化处理,并标注特征点和中心点。特征端点用'o'标注,特征分叉点用'+'标注,中心点用红色'*'标注  请查看原文图片 指纹匹配  ​​​​​​​请查看原文图片​​​​​​​A10-11 ———————————————— 版权声明:本文为CSDN博主「fpga和matlab」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/ccsss22/article/details/127582230 
  • [热门活动] 体验文字生成图片Stable Diffusion
    Stable Diffusion文字生成图像Stable Diffusion 是由 CompVis、Stability AI 和 LAION 共同开发的一个文本转图像模型,它通过 LAION-5B 子集大量的 512x512 图文模型进行训练,我们只要简单的输入一段文本,Stable Diffusion 就可以迅速将其转换为图像,同样我们也可以置入图片或视频,配合文本对其进行处理。这个活动简单又好玩Ta-Da!机会来啦!AI Gallery 正式上线了 AI 作画中最火的模型 “Stable Diffusion”,通过 ModelArts Notebook能力,即可实现【一键 AI 作画】,自动生成作品海报,人人都是大艺术家!https://developer.huaweicloud.com/develop/aigallery/notebook/detail?id=03aab198-dc21-4974-ab33-352e9f56939c&ticket=ST-193587-MgJGfa1fxl6GFZ77F7vFjRod-sso切换GPU免费规格Prompt框架:[图片的风格]+[内容主题]+[细节描述]+[绘画风格或者艺术家风格]图片的风格按照这个框架,简单构造一个图片风格,比如:可以是一幅美丽的画,或者是一幅美丽的山水画。内容的主体+详细描述主体可以是各种各样的选择。推荐一些可以激发大家灵感的事物,如山水,人物,建筑房屋,动物等等,尽可能细致刻画这些实体事物。如果是简单的输入“大海”两个字,模型不知道我们想要的风景是什么样子的,一段作品的相对准确的描述,比如:梦幻的大海,白沙滩岸边铺满了粉色的玫瑰花,月光轻柔的人洒在海面上,绿色发光的海浪,我们要去尽量用详细的语言,用细节清楚的描述我们幻想的场景。比如:云中的Skyrim风格的山,有一个照明螺栓击中尖端导致雪崩,山区风景。
  • [行业动态] 中山大学HCP实验室在AIGC领域的新突破:有效表示多样化衣物的3D神经表示模型
    中山大学HCP实验室在AIGC领域的新突破:有效表示多样化衣物的3D神经表示模型中山大学 HCP 实验室联合牛津大学 TVG 实验室共同发表论文《Structure-Preserving 3D Modeling with Neural Sewing Machines》,该论文已被 NeurIPS 2022 接收。该工作主要由陈曦鹏、王广润博士等人完成。3D 衣物建模是计算机领域的一个关键且具有挑战性的任务,具体是指如何在计算机中构建一件 3D 的虚拟衣物。构建 3D 衣物具有多种实际应用,包括 3D 虚拟试衣、虚拟数字人和服装设计。最近基于学习的衣物建模方法收到越来越多的关注,然而,现有方法多针对特定类别或相对简单拓扑的衣物进行建模。提出了 Neural Sewing Machine (NSM),一种新颖的保持 3D 衣物结构的学习框架,可以有效表示多样化形状和拓扑结构的 3D 衣物,并应用于 3D 衣物表征,3D 衣物重建和可控衣物编辑。目前基于学习的方法要么使用固定的 3D 网格模板,将衣物表示为 SMPL 模型上的位移,或是借助人体的 UV 参数化来表示衣物。这些方法主要针对特定的衣物类别,或是拓扑结构相对简单的衣物进行建模。缝纫纸样(sewing pattern)是衣物建模和生产中广泛使用的一种结构。缝纫纸样由一组 2D 面板以及面板间的缝合信息组成。例如,一条裙子的缝纫纸样有 4 个 2D 面板(panel)。每个面板对应于 3D 衣物的一部分。使用缝纫纸样来建模 3D 衣物可带来以下好处:首先,可以表达各种不同类别和形状的衣物其次,描述了 3D 衣物的内在结构最后,提供了衣物的 UV 参数化论文链接: https://arxiv.org/abs/2211.06701
  • [云实验室] 华为云沙箱实验
    进入沙箱实验后,为什么账号登不进去?
  • [问题求助] 请问MDC300域控上能进行神经网络模型的训练吗?
    请问MDC300域控上能进行神经网络模型的训练吗?
  • [热门活动] 【获奖公示】华为云HCSD秋招特训营
    活动原帖:【华为云HCSD秋招特训营】活动福利贴获奖公示啦~~~快来看看幸运鹅是不是自己哟!重要的事情放前面,请务必在12月5日之前填写问卷提交收件地址信息,逾期视为放弃领奖>点这里,填问卷<环节一:报名抽奖中奖华为云账号奖品hid_****ta77_tbvplzmHUAWEI WATCH GT3lvji****n华为周边随机发hwid****3_g2y74smi3d3华为周边随机发hid_****ondy046x8nh0华为周边随机发sl62****3华为周边随机发hw21****235华为周边随机发hw87****992华为周边随机发bdjh****eo华为周边随机发hw94****820华为周边随机发hid_****o_5x8x2x5n6w华为周边随机发hw03****024华为周边随机发hid_****9nomvmczpvve华为周边随机发hw45****571华为周边随机发hw02****0421华为周边随机发hw84****168华为周边随机发hid_****9vya0_l5lpio华为周边随机发hid_****q6wm5s5k1jhv华为周边随机发hid_****888gol5d-igo华为周边随机发hid_****r6gg2-jbzk_o华为周边随机发hid_****znt7f-ghi0wl华为周边随机发hid_****fmafvc3ysxtv华为周边随机发hw04****3155华为周边随机发hw07****879华为周边随机发hid_****3ms5j93nujgm华为周边随机发hw82****170华为周边随机发jiao****2华为周边随机发hid_****oefxpcvs00aa华为周边随机发hid_****pd2kbvl2cga4华为周边随机发hw03****8656华为周边随机发hid_****50tmpezyre2t华为周边随机发wanj****ao040401华为周边随机发hw26****459华为周边随机发hid_****u0wwl_dkxj6u华为周边随机发hw03****1023华为周边随机发hx18****820176华为周边随机发hid_****zf2z-8nfy8by华为周边随机发hw95****941华为周边随机发hw88****705华为周边随机发hid_****43s3yzsc2o3h华为周边随机发hid_****f37enjbqoim2华为周边随机发hid_****5gal-y4x76kp华为周边随机发wj20****13华为周边随机发hid_****024cmlce_60s华为周边随机发hid_****m63db4gzn6kw华为周边随机发joey****11华为周边随机发hid_****vj81_eg6mqn5华为周边随机发hid_****icjo5ejwcgde华为周边随机发hid_****hnamb33byawu华为周边随机发hid_****6vwirdb0sgvb华为周边随机发deng****yu00华为周边随机发hid_****0hf3avjgkrzb华为周边随机发环节二:产品体验抽奖中奖华为云账号奖品hw46****5134荣耀手表2hw****4_01HUAWEI 背包(灰色)Arch****ve华为云周边礼包1bail****cool华为云周边礼包1hid_****i972华为云周边礼包1hid_****gnbd华为云周边礼包1hid_****v2ss华为云周边礼包1hid_****uf8o华为云周边礼包1hw_****474_01华为云周边礼包1hw_****968_01华为云周边礼包1hw05****5831华为云周边礼包1hw19****7844华为云周边礼包1hw57****9863华为云周边礼包1hw63****2350华为云周边礼包1hw70****8544华为云周边礼包1hw71****5899华为云周边礼包1ko****ikin华为云周边礼包1m128****5953华为云周边礼包1QK****20华为云周边礼包1sunx****obei华为云周边礼包1the_****aby华为云周边礼包1zz****li华为云周边礼包1caux****owei华为云周边礼包2cuil****ngbo华为云周边礼包2easy****asic华为云周边礼包2enjo****0724华为云周边礼包2fl****10华为云周边礼包2gm_****an华为云周边礼包2hid_****o9_2华为云周边礼包2hid_****n25k华为云周边礼包2hid_****61oc华为云周边礼包2hid_****_w-d华为云周边礼包2hw_****509_01华为云周边礼包2hw01****8841华为云周边礼包2hw48****2267华为云周边礼包2hw72****3025华为云周边礼包2hw78****9453华为云周边礼包2ka****arch华为云周边礼包2l****hanq华为云周边礼包2shes****tive华为云周边礼包2wljs****mz华为云周边礼包2yaoy****n666华为云周边礼包2cha****111000码豆conq****erok1000码豆hid_****z4u11000码豆hid_****9qyw1000码豆hw_****331_011000码豆hw_****672_011000码豆hw07****16461000码豆hw67****90791000码豆Ja****201000码豆vx17****89421000码豆wang_****_jlu1000码豆环节三:考试抽奖中奖华为云账号奖品hw****4623HUAWEI WATCH GT3zhan****iyida1000码豆hw7****2191000码豆以上就是本次活动的全部中奖用户啦,让我们恭喜这群幸运鹅!如果异议请加入群里反馈~~附:抽奖录屏(点击下载查看)
  • [技术干货] CUDA入门教程;Transformer太火不是好事?;探求GPU极限性能的利器|AI系统前沿动态...-转载
     1. PyTorch创始人:Transformer的流行可能是一把双刃剑  要说 Transformer 有多厉害,比如 OpenAI 重磅推出的 GPT-3,就是基于 Transformer 实现的。至于传播速度方面,短短 5 年,Transformer 便在 TensorFlow 、PyTorch 等主流深度学习框架支持的 AI 程序中占据一席之地。  不过与高调宣传 Transformer 的学者不同,这次 PyTorch 创始人、Meta 杰出工程师 Soumith Chintala 却唱起了反调,并警告说,Transformer 如此流行,可能是一把双刃剑。   链接:https://mp.weixin.qq.com/s/ZgdP3HFM4jZsWZfJvciQ3A  2. MAIProf:Meta生产PyTorch模型的性能调试工具  PyTorch在生产环境中的性能调优越来越重要。一个功能强大的性能调试工具是这个过程的关键。通过一个关于生产模型的案例研究,PyTorch证明MAIProf是一个用于识别优化机会的强大AI性能调试基础架构。   在Meta,从性能调试新手到专家的100多名工程师使用MAIProf来识别更多类型的瓶颈。其中包括缓慢的数据加载、分布式训练问题(例如负载不平衡和过度通信)。MAIPprof涵盖了包括推荐、视觉和自然语言处理等主要类别模型。总之,它现在是生产PyTorch工作负载时调试性能的不可或缺的工具。  链接:https://pytorch.org/blog/performance-debugging-of-production-pytorch-models-at-meta/?utm_source=organic_social&utm_medium=linkedin&utm_campaign=performance_debugging  3. 精度、耗时、显存消耗、网络结构...一行代码深度解析训练性能指标  正所谓“工欲善其事,必先利其器”, 一个能实时监控模型训练指标、硬件性能消耗情况、可视化网络结构和每层网络参数变化的工具可大幅缩减模型开发时间,推动人工智能技术快速应用落地。  飞桨可视化分析工具VisualDL以丰富的图表及清晰的可视化解析功能帮助开发者直观地理解深度学习模型训练过程中的各项信息。  链接:https://mp.weixin.qq.com/s/Vi6erY48x8IUU1rQNw95Uw  4. 视频教程|OneFlow源码解析(2):OneFlow的4种执行模式  OneFlow 的执行模式有2个维度,其一可从计算图执行方式划分,可分为静态图模式、动态图模式;其二可从张量类型划分,即单机单卡的local tensor模式,以及分布式训练下的global tensor模式。这两个维度可以组合得到4种执行实际情况。OneFlow提供给用户的接口又是尽量统一的。  本视频将介绍OneFlow如何在运行时判断应该使用哪种模式。如何做到用户尽可能易用、无感于多种模式的差别,同时又能尽量发挥每种模式的特点。   链接:https://www.bilibili.com/video/BV17P411G76y/?spm_id_from=333.999.0.0&vd_source=25c3e9bdd8a5701b69d322f471c11c38  5. SIMT、SIMD和DSA(2)  SIMD和DSA以及SIMT这些词虽然经常放到一起说,但三者的指代比较混乱,很容易产生文字游戏。有时指的是指令、有时候指的是体系结构、有时候又是指编程模型。同时在每个层面的scope也不是很清晰,于是就有了一定偷换概念的空间。SIMT通常指代一种编程模型,而SIMD通常指代的指令,DSA在某些场合指代指令,某些场合又指代架构。   链接:https://zhuanlan.zhihu.com/p/564623647  6. CuAssembler 开源 | 探求 NVIDIA GPU 极限性能的利器  CuAssembler是个CUDA SASS汇编器,主要是把 nvdisasm 生成的反汇编输出汇编成可加载执行的 cubin 文件,实现对最终汇编机器码的绝对控制,弥补当前 NV 没有官方汇编器支持的不足。  链接:https://mp.weixin.qq.com/s/ousE3sYtDd7zhV-_AtvA7w  7. 用于大规模深度推荐模型的专用GPU推理参数服务器   在大规模推荐系统中,Embedding通常在数据中心消耗大量内存进行存储。并且整个模型运算过程中,有大量时间花费在参数服务器上搜索对应Embedding向量,这个步骤增加了整体latency,拖慢了下游的计算。  基于在真实数据集中观察到的方法,本文提出Embedding的Key往往具有很强局部性,并且遵循幂律分布。基于上述观察作者提出了HugeCTR分层参数服务器,他们将热数据保存在GPU显存中,而其他部分则有参数服务器进行补充,其保存了完整的Embedding副本。   链接:https://zhuanlan.zhihu.com/p/568639935  8. CUDA编程入门极简教程  2006年,NVIDIA公司发布了CUDA,CUDA是建立在NVIDIA的CPUs上的一个通用并行计算平台和编程模型,基于CUDA编程可以利用GPUs的并行计算引擎来更加高效地解决比较复杂的计算难题。近年来,GPU最成功的一个应用就是深度学习领域,基于GPU的并行计算已经成为训练深度学习模型的标配。   链接:https://zhuanlan.zhihu.com/p/34587739  9. Jeff Dean:机器学习在硬件设计中的潜力  为什么芯片设计需要很长时间?能不能加速芯片设计周期?能否在几天或几周之内完成芯片的设计?这是一个非常有野心的目标。   Google在这个领域已率先出发。Google AI负责人Jeff Dean分享了《机器学习在硬件设计中的潜力》,他介绍了神经网络发展的黄金十年,机器学习如何影响计算机硬件设计以及如何通过机器学习解决硬件设计中的难题,并展望了硬件设计的发展方向。   链接:https://mp.weixin.qq.com/s/_JmINzustpH1bEDMjz9WaA  10. AI加速器与机器学习算法:协同设计与进化  关注AI和半导体行业的朋友近来可能听说过“机器学习(ML)专用处理器”(即AI加速器)。最常见的AI加速器莫过于NVIDIA GPU,此外还有Intel的Habana Gaudi处理器、Graphcore的Bow IPU、Google的TPU、AWS的Trainium和Inferentia芯片等。  为什么如今有这么多AI加速器可供选择?它们和CPU有什么不同?算法如何改变才能适应这些硬件?硬件又该如何发展才能支持最新的算法?本文将一一解答。   链接:https://mp.weixin.qq.com/s/8ObtUlKqfDfCbE__bWE1hQ  11. 免费用Stable Diffusion“脑补”世界名画画框外世界  Stable Diffusion Infinity,是大火的AI绘图新星Stable Diffusion的一项子功能。只需要一两句话提示,Stable Diffusion就可以画出你想要的东西,而且和已有部分衔接自然,没什么违和感。  链接:https://mp.weixin.qq.com/s/rX5I6iJFgHeGShR2w5wxkw  12. 逆向工程:揭示Google Colab未公开的秘密  对于负责在Jupyter Notebook编程的数据科学家来说,Colab早已成为了默认的运行环境。然而,将Colab的算力运用到除 Jupter Notebooks 以外的其他应用,则是一件极其困难的事。  出于这个原因,作者将探究Google Colab的内部结构,并尝试稍微改变Colab 的内置规则。需要提前声明的是,作者只是想探究Colab,不会对Colab本身或者它的用户造成任何影响。  链接:https://mp.weixin.qq.com/s/OQOQ4Z0DVv_C0tsrRDJ7TQ ———————————————— 版权声明:本文为CSDN博主「OneFlow深度学习框架」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/OneFlow_Official/article/details/127168575 
  • [技术干货] Python图形处理-转载
     一、Pillow PIL:Python Imaging Library,已经是Python平台事实上的图像处理标准库了。PIL功能非常强大,但API却非常简单易用。  由于PIL仅支持到Python 2.7,加上年久失修,于是一群志愿者在PIL的基础上创建了兼容的版本,名字叫Pillow,支持最新Python 3.x,又加入了许多新特性,因此,我们可以直接安装使用Pillow。  安装Pillow  如果安装了Anaconda,Pillow就已经可用了。否则,需要在命令行下通过pip安装:  $ pip install pillow 如果遇到Permission denied安装失败,请加上sudo重试。  操作图像  来看看最常见的图像缩放操作,只需三四行代码:  from PIL import Image   # 打开一个jpg图像文件,注意是当前路径: im = Image.open('test.jpg') # 获得图像尺寸: w, h = im.size print('Original image size: %sx%s' % (w, h)) # 缩放到50%: im.thumbnail((w//2, h//2)) print('Resize image to: %sx%s' % (w//2, h//2)) # 把缩放后的图像用jpeg格式保存: im.save('thumbnail.jpg', 'jpeg') 其他功能如切片、旋转、滤镜、输出文字、调色板等一应俱全。  比如,模糊效果也只需几行代码:  from PIL import Image, ImageFilter   # 打开一个jpg图像文件,注意是当前路径: im = Image.open('test.jpg') # 应用模糊滤镜: im2 = im.filter(ImageFilter.BLUR) im2.save('blur.jpg', 'jpeg') 效果如下:   PIL的ImageDraw提供了一系列绘图方法,让我们可以直接绘图。比如要生成字母验证码图片:  from PIL import Image, ImageDraw, ImageFont, ImageFilter   import random   # 随机字母: def rndChar():     return chr(random.randint(65, 90))   # 随机颜色1: def rndColor():     return (random.randint(64, 255), random.randint(64, 255), random.randint(64, 255))   # 随机颜色2: def rndColor2():     return (random.randint(32, 127), random.randint(32, 127), random.randint(32, 127))   # 240 x 60: width = 60 * 4 height = 60 image = Image.new('RGB', (width, height), (255, 255, 255)) # 创建Font对象: font = ImageFont.truetype('Arial.ttf', 36) # 创建Draw对象: draw = ImageDraw.Draw(image) # 填充每个像素: for x in range(width):     for y in range(height):         draw.point((x, y), fill=rndColor()) # 输出文字: for t in range(4):     draw.text((60 * t + 10, 10), rndChar(), font=font, fill=rndColor2()) # 模糊: image = image.filter(ImageFilter.BLUR) image.save('code.jpg', 'jpeg')  我们用随机颜色填充背景,再画上文字,最后对图像进行模糊,得到验证码图片如下:     如果运行的时候报错:  IOError: cannot open resource 这是因为PIL无法定位到字体文件的位置,可以根据操作系统提供绝对路径,比如:  '/Library/Fonts/Arial.ttf' 要详细了解PIL的强大功能,请请参考Pillow官方文档:  Pillow (PIL Fork) 9.2.0 documentation  总结:  PIL提供了操作图像的强大功能,可以通过简单的代码完成复杂的图像处理。  二、海龟绘图  在1966年,Seymour Papert和Wally Feurzig发明了一种专门给儿童学习编程的语言——LOGO语言,它的特色就是通过编程指挥一个小海龟(turtle)在屏幕上绘图。  海龟绘图(Turtle Graphics)后来被移植到各种高级语言中,Python内置了turtle库,基本上100%复制了原始的Turtle Graphics的所有功能。  我们来看一个指挥小海龟绘制一个长方形的简单代码:  # 导入turtle包的所有内容: from turtle import *   # 设置笔刷宽度: width(4)   # 前进: forward(200) # 右转90度: right(90)   # 笔刷颜色: pencolor('red') forward(100) right(90)   pencolor('green') forward(200) right(90)   pencolor('blue') forward(100) right(90)   # 调用done()使得窗口等待被关闭,否则将立刻关闭窗口: done()  在命令行运行上述代码,会自动弹出一个绘图窗口,然后绘制出一个长方形:   从程序代码可以看出,海龟绘图就是指挥海龟前进、转向,海龟移动的轨迹就是绘制的线条。要绘制一个长方形,只需要让海龟前进、右转90度,反复4次。  调用width()函数可以设置笔刷宽度,调用pencolor()函数可以设置颜色。更多操作请参考turtle库的说明。  绘图完成后,记得调用done()函数,让窗口进入消息循环,等待被关闭。否则,由于Python进程会立刻结束,将导致窗口被立刻关闭。  turtle包本身只是一个绘图库,但是配合Python代码,就可以绘制各种复杂的图形。例如,通过循环绘制5个五角星:  from turtle import *   def drawStar(x, y):     pu()     goto(x, y)     pd()     # set heading: 0     seth(0)     for i in range(5):         fd(40)         rt(144)   for x in range(0, 250, 50):     drawStar(x, 0)   done()  程序执行效果如下:     使用递归,可以绘制出非常复杂的图形。例如,下面的代码可以绘制一棵分型树:  from turtle import *   # 设置色彩模式是RGB: colormode(255)   lt(90)   lv = 14 l = 120 s = 45   width(lv)   # 初始化RGB颜色: r = 0 g = 0 b = 0 pencolor(r, g, b)   penup() bk(l) pendown() fd(l)   def draw_tree(l, level):     global r, g, b     # save the current pen width     w = width()       # narrow the pen width     width(w * 3.0 / 4.0)     # set color:     r = r + 1     g = g + 2     b = b + 3     pencolor(r % 200, g % 200, b % 200)       l = 3.0 / 4.0 * l       lt(s)     fd(l)       if level < lv:         draw_tree(l, level + 1)     bk(l)     rt(2 * s)     fd(l)       if level < lv:         draw_tree(l, level + 1)     bk(l)     lt(s)       # restore the previous pen width     width(w)   speed("fastest")   draw_tree(l, 4)   done()  执行上述程序需要花费一定的时间,最后的效果如下:    ———————————————— 版权声明:本文为CSDN博主「wespten」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_35029061/article/details/127356389 
  • 【AI使能】【必读】政务一网通军团伙伴使能中心-AI使能版块指南
    Hi~!合作伙伴们!欢迎来到政务一网通军团伙伴使能中心-AI使能版块!!不管你是为了什么来到这里,我们都会想办法满足你的需求!在AI使能中心,我们会为您介绍政务一网通军团AI领域使能地图、技术干货、使用宝典、培训资料、问题求助等模块,为各位开发者提供开发必须的资料和平台~下面就来一一介绍!AI领域使能地图 LINK由云能力中心,结合政务一网通各个行业场景,整理得出的行业场景流程内使用AI领域各个云服务做了哪些事情,从全局到具体,一览无遗,清晰各个云服务定位。技术干货 LINK我们会为合作伙伴通过通用场景,聚合云服务原子化API形成场景化API,加速开发。也会为您推送AI领域更多技术干货~使用宝典 LINK为您提供更细节的使用手册,提高开发效率,快速集成场景化能力!培训资料 LINK针对政务一网通军团开发场景,为开发者针对性整理培训资料,包括常用的开发架构、开发工具等等。磨刀不误砍柴工,懂得越多,干得越快!!问题求助 LINK 如果您在开发中遇到问题或者有流程问题,欢迎来本社区发帖求助。如果您的问题较为敏感,也可以私信版主 -》 E青年发帖格式如下~标题:【场景 如:一网通办】【涉及华为云产品:OCR算法】具体问题描述内容:问题一句话总结:使用场景或目的:(选填)问题现象截图:(选填)操作步骤:(选填)希望优化的地方或希望实现的效果:填写完成后,“接收回复通知”,上传必要的附件,点击“发表帖子”。注意:如果要输入手机号和邮箱等隐私数据时可选择“回帖仅作者可见”。 “回帖仅作者可见”模式下其余论坛用户将看到不到回帖内容,仅园区版主可见。更多资讯 LINK后面我们也会不定期发布应用开发招募令,一起来加入政务一网通AI开发吧!!当前版块还在搭建中,材料缺失,还望海涵,我们会不断完善版块资料,快速成为合作伙伴可以依赖的开发者社区 ٩( ´︶` )( ´︶` )۶ ~
  • [技术干货] AI绘画突然爆火?快速体验二次元画师NovelAI(diffusion)-转载
     目录 0 写在前面 1 diffusion vs GAN 2 NovelAI 3 AI绘画环境搭建 4 体验AI创作 0 写在前面 机器学习强基计划聚焦深度和广度,加深对机器学习模型的理解与应用。“深”在详细推导算法模型背后的数学原理;“广”在分析多个机器学习模型:决策树、支持向量机、贝叶斯与马尔科夫决策、强化学习等。  🚀详情:机器学习强基计划(附几十种经典模型源码合集)  话不多说,先看看AI绘画的效果  接下来带大家体验一下二次元画师novelAI  1 diffusion vs GAN 所谓扩散算法diffusion是指先将一幅画面逐步加入噪点,一直到整个画面都变成白噪声。记录这个过程,然后逆转过来给AI学习。  AI看到的是什么?一个全是噪点的画面如何一点点变清晰直到变成一幅画,AI通过学习这个逐步去噪点的过程来学会作画。  diffusion和之前大火的GAN模型相比,有什么优势呢?用OpenAI的一篇论文内容来讲,用diffusion生成的图像质量明显优于GAN模型;而且与GAN不同,diffusion不用在鞍点问题上纠结——涉及稳定性问题,只需要去最小化一个标准的凸交叉熵损失即可,这样就大大简化了模型训练过程中,数据处理的难度。  总结来说,目前的训练技术让diffusion直接跨越了GAN领域调模型的阶段,而是直接可以用来做下游任务,是一个新的数学范式在图像领域应用的实例。  2 NovelAI NovelAI是基于人工智能的绘画创作辅助工具,单纯训练就使用了数千个网站的数十亿张图片。NovelAI是一个商业化的网站,绘画是需要付费的,然而这次其商业模型疑似流出,不管是小白还是画师都能体验一次AI创作的乐趣。这次流出的模型是stable-diffusion-webui,也就是第一节介绍的扩散算法。   3 AI绘画环境搭建 主要分为以下步骤:  创建Python3.10的虚拟环境  conda create -n ai_draw python=3.10 1 虚拟环境相关操作请参考:Anaconda安装与Python虚拟环境配置保姆级图文教程(附速查字典)  在虚拟环境中安装Pytorch 这步比较涉及显卡相关的配置,比较复杂,请参考:最新CUDA/cuDNN与Pytorch保姆级图文安装教程(速查字典版)  下载模型stable-diffusion-webui  git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui 1 安装依赖库 首先进入虚拟环境  conda activate ai_draw 1 接着进入stable-diffusion-webui根目录运行  pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt 1 采用清华源可以更快完成安装  下载模型和权重文件 模型文件model.cpkt:model.cpkt,权重文件GFPGANv1.4.pth:GFPGANv1.4.pth,模型文件移动到这个目录stable-diffusion-webui-master\models\Stable-diffusion,权重文件留在根目录即可,最后的文件组织如图所示   根目录运行启动文件  python launch.py 1 期间会下载一些新的依赖,有可能超时报错,多启动几次即可  4 体验AI创作 通过第三节的配置,成功后即可在终端看到  开放了一个本地端口,在浏览器中输入即可  接下来测试最简单的文本生成图像功能:  输入正面标签: NSFW,anime fine details portrait of a white haired girl ,black eyes,wearing hoodie on the city street background, close-up view, anime masterpiece,4k, sharp high quality anime, artstation Prhololive, uruha_rushia, 1girl, bangs, bare shoulders, red eyes, blue dress, blue green hair,blue sleeves, blush, bow, breasts, chick, collarbone, detached collar, detached sleeves, double bun, eyebrows visible through hair,frills, hair orhament, medium hair, off-shoulder dress 1girl looking_at_viewer upper_body, too many flowers,kyoto animation, bishojo, bare_shoulders black sleeves yellow_eyes emeraldblue_hair, sailor_collar and neckerchief, detached_sleeves frilled_shirt_collar frills grey_shirt headset heart long_hairsleeveless shirt twintails verv long hair wide sleeves highres 2girl, two bishojo snuggled up selfie, with white marble glowing skin and perfect symmetrical pretty face with blush cheeks and glaring eyes wearing a school uniform,golden hour lighting, strong rim light, intense shadowse , Ernst Thoms 1 输入反面标签: lowres, bad anatomy, bad hands, text,error, missing fngers,extra digt ,fewer digits,cropped, wort quality ,low quality,normal quality, jpeg artifacts,signature,watermark, username, blurry, bad feet 1 设置采样步长sampling step为28 配置CFG Scale为12 点击Generate  当然这里还有img2img等丰富的功能,读者可自行测试 ———————————————— 版权声明:本文为CSDN博主「Mr.Winter`」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/FRIGIDWINTER/article/details/127471600 
  • [技术干货] 如何用人工智能自动玩游戏-转载
     如何用人工智能自动玩游戏 一、前言 让AI玩游戏的思想早在上世纪就已经有了,那个时候更偏向棋类游戏。像是五子棋、象棋等。在上世纪“深蓝”就击败了国际象棋冠军,而到2016年“Alpha Go”击败了人类围棋冠军。  到现在,AI涉略的不仅仅是棋类游戏。像是超级马里奥、王者荣耀这种游戏,AI也能有比较好的表现。今天我们就来用一个实际的例子讨论AI自动玩游戏这一话题,本文会用非常简单的机器学习算法让AI自动玩Google小恐龙游戏。  二、Google小恐龙与监督学习 2.1、Google小恐龙 如果你使用的是Chrome浏览器,那么相信你应该见过下面这个恐龙:  当我们用Chrome断网访问网页时,就会显示这个恐龙,或者直接在地址栏输入:chrome://dino直接访问该游戏。  游戏的玩法非常简单,只需要按空格键即可。比如下面左图,快碰到障碍物,这时需要按空格,而下面右图没有障碍(或离障碍比较远),则不需要按按键。  当然还有出现鸟的情况,我们也可以归为跳的情况。大家可以玩一下。  2.2、监督学习 玩游戏很多时候会使用一个叫强化学习的方式来实现,而本文使用比较简单的监督学习来实现。  本文会使用逻辑回归算法实现,其代码如下:  from sklearn.linear_model import LogisticRegression # 逻辑回归模型 from sklearn.model_selection import train_test_split    # 数据集拆分 # 1、准备数据 X = [     # 天河区的坐标     [1, 1],     [1, 2],     [2, 0],     [3, 2],     [3, 3],     # 花都区的坐标     [7, 7],     [6, 7],     [7, 6],     [8, 6],     [8, 5] ] y = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] # 2、拆分数据集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) # 3、定义模型 model = LogisticRegression() # 4、填充数据并训练 model.fit(X_train, y_train) # 5、评估模型 score1 = model.score(X_train, y_train) score2 = model.score(X_test, y_test) print(score1, score2) # 6、预测 input = [     [4, 4] ] pred = model.predict(input) print(pred)  关于逻辑回归的讲解可以查看:Python快速构建神经网络 。  我们可以把玩游戏看作一个分类问题,即输入为当前游戏的图像,输出为0、1的一个二分类问题(0表示跳,1表示不跳)。要让AI实现自动玩游戏,我们需要做几件事情。分别如下:  玩游戏,收集一些需要跳的图片和一些不需要条的图片 选择合适的分类算法,训练一个模型 截取当前游戏画面,预测结果,判断是否需要跳跃 如果需要跳跃,则用程序控制键盘,按下跳跃键 下面我们来依次完成上面的事情。  三、收集数据 收集数据我们需要在玩游戏的过程中不停地截图,这里可以用Pillow模块来实现截图。Pillow模块需要单独安装,安装语句如下:  pip install pillow 截图的代码如下:  import time from PIL import ImageGrab   # 截图 time.sleep(3) while True:     # 截图     img = ImageGrab.grab()     # print(img.size) # 960 540 480 270     img = img.resize((960, 540))     # 保存图片     img.save(f'imgs/{str(time.time())}.jpg')     # 修改name     time.sleep(0.1) 运行程序后就可以切换到Chrome开始游戏了。进行一段时间后,我们会截取一些图片,大致如下:  这时就轮到人类智能上场了,我们手动的把我们决定需要跳的场景放置到imgs/jump目录下,把觉得不需要跳的场景放到imgs/none目录下。然后就可以进行下一步了,这里截取的图片通常不需要跳的要多很多,所有可以多收集几次。  收集完成后我们就可以把图片读入,并转换成一个1维数组,这部分代码如下:  import os import cv2 # 所有图片的全路径 files = [os.path.join(jump_path, jump) for jump in os.listdir(jump_path)] + \         [os.path.join(none_path, none) for none in os.listdir(none_path)] X = [] y = [0] * len(os.listdir(jump_path)) + [1] * len(os.listdir(none_path)) # 遍历jump目录下的图片 for idx, file in enumerate(files):     filepath = os.path.join(none_path, file)     x = cv2.imread(filepath, 0).reshape(-1)     X.append(x) 此时X和y就是我们的特征和目标了。有了X和y就可以开始训练模型了。  四、训练分类模型 训练部分的代码非常简单,我们可以在训练完成后保存模型。代码如下:  import os import cv2 import joblib from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression jump_path = os.path.join('imgs', 'jump')    # 需要跳的图片的根目录 none_path = os.path.join('imgs', 'none')    # 不需要跳的图片的根目录 # 所有图片的全路径 files = [os.path.join(jump_path, jump) for jump in os.listdir(jump_path)] + \         [os.path.join(none_path, none) for none in os.listdir(none_path)] X = [] y = [0] * len(os.listdir(jump_path)) + [1] * len(os.listdir(none_path)) # 遍历jump目录下的图片 for file in files:     x = cv2.imread(file, 0).reshape(-1)     X.append(x)  # 2、拆分数据集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) # 3、定义模型 model = LogisticRegression(max_iter=500) # 4、训练模型 model.fit(X_train, y_train) # 5、评估模型 train_score = model.score(X_train, y_train) test_score = model.score(X_test, y_test) print(train_score, test_score) # 保存模型 joblib.dump(model, 'auto_play.m') 在我电脑上训练的准确率在90%以上,总体效果还是不错的。不过有几个可以改进的地方。这里说几点:  图像只有中间部分会对下一步操作有影响,因此可以选择对训练图片进行一些处理。把上面和下面部分设置为0。如果做了这个处理,那么在实际应用时也要做同样的处理。 这些图片如果移植到其它电脑可能不适用,因为分辨率等原因。所有可以选择使用更复杂的模型,比如CNN网络。 因为手动收集数据比较麻烦,可以选择做一下数据增强。 在这里我们不做这些改进,直接使用最简单的模型。  五、自动玩游戏 自动玩游戏需要借助pynput模块来实现,其安装如下: pip install pynput 我们可以用下面的代码实现按下键盘的空格键:  from pynput import keyboard from pynput.keyboard import Key # 创建键盘 kb = keyboard.Controller() # 按下空格键 kb.press(Key.space) 知道了如何控制键盘后,我们就可以使用模型截取预测,如何判断是否要按空格,代码如下:  import time import cv2 import joblib import numpy as np from PIL import ImageGrab from pynput import keyboard from pynput.keyboard import Key  time.sleep(3) # 0、创建键盘 kb = keyboard.Controller() # 1、加载模型 model = joblib.load('auto_play.m') while True:     # 2、准备数据     ImageGrab.grab().resize((960, 540)).save('current.jpg')  # 保存当前屏幕截屏     x = cv2.imread('current.jpg', 0).reshape(-1)     x = [x]     # 3、预测     pred = model.predict(x)     print(pred)     # 如果需要跳,则按下空格     if pred[0] == 0:         kb.press(Key.space) 运行上面的程序后,打开浏览器即可开始游戏。程序的代码和图片文件:https://download.csdn.net/download/ZackSock/86543410 GitHub地址为:https://github.com/IronSpiderMan/AutoPlayGoogleDino ———————————————— 版权声明:本文为CSDN博主「ZackSock」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/ZackSock/article/details/126908649 
  • [技术干货] FunctionGraph+OBS+FRS+IVS实现人证核身
    FunctionGraph+OBS+FRS+IVS实现人证核身目录场景设计方案架构图方案设计云服务介绍环境准备开发流程1.客户端开发——上传图片/视频到OBS2.FunctionGraph函数代码开发3.下载OBS对象+Base64编码4.FRS活体检测5.IVS人证核身6.SMN发布结果消息7.客户端开发——SMN之HTTP(S)终端接口开发8.FunctionGraph函数整合实现效果部署测试示例工程一、场景设计客户通过页面上传人物照片、视频到OBS并触发FunctionGraph函数,函数调用FRS人脸识别服务判断是否为真人,如果是真人继续调用IVS人证核身服务校验身份真实性,最后通过SMN将结果消息发布出去。方案架构图![方案架构图]二、方案设计创建FunctionGraph函数,并为该函数配置OBS触发器,上传图片或视频到OBS后触发FunctionGraph函数,函数下载OBS对象并调用FRS进行活体检测,活体检测通过后函数再调用IVS进行人证核身,最后函数通过SMN发布人证核身结果消息。云产品提供服务作用OBS对象操作实现上传/下载 图片/视频FunctionGraph配置OBS触发器实现OBS上传时触发函数FunctionGraph创建Java函数创建Java语言的函数FRS活体检测对图片或视频进行动作/静默活体检测IVS认证核身标准版实现对上传的人脸图片进行身份验证SMN发送HTTP(S)消息实现将结果发送到HTTP(S)终端三、云服务介绍华为云OBS: 对象存储服务(Object Storage Service,OBS)是一个基于对象的海量存储服务,为客户提供海量、安全、高可靠、低成本的数据存储能力,使用时无需考虑容量限制,并且提供多种存储类型供选择,满足客户各类业务场景诉求。产品页面华为云FunctionGraph:函数工作流(FunctionGraph)是一项基于事件驱动的函数托管计算服务。通过函数工作流,只需编写业务函数代码并设置运行的条件,无需配置和管理服务器等基础设施,函数以弹性、免运维、高可靠的方式运行。产品页面华为云FRS:人脸识别服务(Face Recognition Service),能够在图像中快速检测人脸、分析人脸关键点信息、获取人脸属性、实现人脸的精确比对和检索。该服务可应用于身份验证、电子考勤、客流分析等场景。产品页面华为云IVS:人证核身服务(Identity Verification Solution)将用户本人与身份证信息关联起来,应用人脸识别与文字识别等技术,对接权威数据库,支持基于二要素(姓名、身份证)认证或三要素(人脸、姓名、身份证)认证,实现对身份真实性的精准核验。产品页面华为云SMN:消息通知服务(Simple Message Notification)为用户提供快速简便、稳定可靠、简化运维、高可扩展、安全可信的消息通知能力。最终用户可以通过HTTP、HTTPS、邮件、短信、触发函数执行、即时通讯工具等方式接收通知信息。华为云用户也可以在应用之间通过消息通知服务实现应用的功能集成,降低系统的复杂性。产品页面四、环境准备| 开发语言:Java| 开发环境:jdk1.8 maven3.6.3| 华为云环境:注册云账号,并完成实名认证五、开发流程| 分为客户端开发和FunctionGraph的Java函数开发。1、客户端开发--上传图片/视频到OBS华为云OBS服务控制台创建桶用来存储人脸图片或视频 如果涉及视频需要压缩上传,需要配置在线解压策略(具体桶中->数据处理>在线解压) 拿到OBS的maven依赖坐标,使用SDK开发上传OBS对象客户端,这里文件传到固定的文件夹下,并使用固定对象名命名,比如face。帮助文档上传对象方法代码参考public void uploadFile(MultipartFile multipartFile){ String endPoint = "https://终端节点"; String ak = "ak"; String sk = "sk"; //1.创建OBS客户端对象 ObsClient obsClient = new ObsClient(ak, sk, endPoint); //2.获取文件输入流 InputStream in = null; try { in = multipartFile.getInputStream(); //3.上传对象,后面根据该对象名下载对象,保持上传下载一致 PutObjectResult putObjectResult = obsClient.putObject("桶名", "对象名", in); System.out.println(putObjectResult); } catch (IOException e) { throw new RuntimeException(e); }finally { if (in!=null){ try { in.close(); } catch (IOException e) { throw new RuntimeException(e); } } } }2、FunctionGraph函数代码开发(1)FunctionGraph函数时序图(2)Java函数环境搭建创建maven项目,导入FunctionGraph提供的Runtime依赖 操作指导创建FunctionGraph Java函数,并配置OBS触发器。 3、下载OBS对象+Base64编码| 终端节点查询 | ak、sk获取从OBS下载对象并进行Base64编码,后面使用该Base64编码结果进行活体检测。导入OBS的Maven依赖。可以从SDK中心查询依赖坐标下载对象+Base64编码示例代码:public static String download() { //1.相关配置数据 String endPoint = "https://终端节点"; String ak = "ak"; String sk = "sk"; //2.创建ObsClient实例 final ObsClient obsClient = new ObsClient(ak, sk, endPoint); //对象名保持和上传对象名一致 ObsObject obsObject = obsClient.getObject("桶名", "对象名"); //3.读取对象内容 ByteArrayOutputStream bos = null; InputStream input = null; String imageStr = null; try { System.out.println("Object content:"); input = obsObject.getObjectContent(); byte[] b = new byte[1024]; bos = new ByteArrayOutputStream(); int len; while ((len = input.read(b)) != -1) { bos.write(b, 0, len); } //4.Base64编码 byte[] data = new byte[bos.size()]; data = bos.toByteArray(); imageStr = new String(Base64.encodeBase64(data)); System.out.println(imageStr); } catch (Exception e) { e.printStackTrace(); } finally { try { bos.close(); } catch (IOException e) { throw new RuntimeException(e); } try { input.close(); } catch (IOException e) { throw new RuntimeException(e); } } return imageStr; }4、FRS活体检测活体检测,判断图片或视频中的人物是否为真人活体。对Base64编码后的结果进行活体检测,图片使用静默活体检测,视频使用动作活体检测。以图片对应的静默活体检测为例,使用SDK开发。开通活体检测服务控制台 导入FRS的Maven依赖。可以从SDK中心查询依赖坐标静默活体检测示例代码://方法参数imageStr:人像图片Base64编码 public static DetectLiveFaceByBase64Response detectLive(String imageStr) { //1.配置基本信息 ICredential credential = new BasicCredentials().withAk("ak").withSk("sk"); FrsClient client = FrsClient.newBuilder() .withCredential(credential) .withRegion(FrsRegion.valueOf("区域")) // 选择服务所在区域,如华北-北京四: FrsRegion.valueOf("cn-north-4") .build(); //2.通过base64编码进行活体检测 DetectLiveFaceByBase64Request request = new DetectLiveFaceByBase64Request(); LiveDetectFaceBase64Req body = new LiveDetectFaceBase64Req(); body.withImageBase64(imageStr); request.withBody(body); DetectLiveFaceByBase64Response response = null; try { response = client.detectLiveFaceByBase64(request); System.out.println(response.toString()); } catch (Exception e) { e.printStackTrace(); } return response; }5、IVS人证核身这里用到IVS标准版(三要素)对图片中人物进行身份认证,这里用到的图片Base64是FRS活体检测返回的结果中的图片Base64编码。开通人证核身服务控制台 导入IVS的Maven依赖。可以从SDK中心查询依赖坐标人证核身标准版:使用身份证图片Base64、人像图片Base64进行认证,示例代码://方法参数imageStr:人像图片Base64编码 public static String ivsVerify(String imageStr) { //1.配置基本信息 ICredential credential = new BasicCredentials().withAk("ak").withSk("sk"); IvsClient client = IvsClient.newBuilder() .withCredential(credential) .withRegion(IvsRegion.valueOf("区域"))// 选择服务所在区域,如华北-北京四就是 FrsRegion.valueOf("cn-north-4") .build(); DetectStandardByIdCardImageRequest request = new DetectStandardByIdCardImageRequest(); IvsStandardByIdCardImageRequestBody body = new IvsStandardByIdCardImageRequestBody(); List listIvsStandardByIdCardImageRequestBodyDataReqData = new ArrayList<>(); listIvsStandardByIdCardImageRequestBodyDataReqData.add( new ReqDataByIdCardImage() .withIdcardImage1("身份证人像面图片Base64") .withFaceImage(imageStr) ); IvsStandardByIdCardImageRequestBodyData databody = new IvsStandardByIdCardImageRequestBodyData(); databody.withReqData(listIvsStandardByIdCardImageRequestBodyDataReqData); Meta metabody = new Meta(); //唯一标识此次请求的ID,用户自定义,不超过64位 metabody.withUuid(UUID.randomUUID().toString()); body.withData(databody); body.withMeta(metabody); request.withBody(body); String result = null; try { //2.调用服务 DetectStandardByIdCardImageResponse response = client.detectStandardByIdCardImage(request); System.out.println(response.toString()); //result取值:valid-成功;invalid-失败 result = response.getResult().getRespData().get(0).getVerificationResult(); } catch (Exception e) { e.printStackTrace(); } //3.返回人证核身结果 return result; }6、SMN发布结果消息这里用SMN向HTTP(S)终端发送消息,将人证核身结果发布到指定的URL地址。开通服务(创建主题+创建订阅)控制台 导入IVS的Maven依赖。可以从SDK中心查询依赖坐标SMN发布消息示例代码://方法请求参数msgContent:消息内容 public static void sendMsg(String msgContent){ String ak = "ak"; String sk = "sk"; //创建SMN客户端 ICredential credential = new BasicCredentials().withAk(ak).withSk(sk); SmnClient client = SmnClient.newBuilder() .withCredential(credential) .withRegion(区域)//如华北-北京四:SmnRegion.CN_NORTH_4 .build(); //创建请求 PublishMessageRequest publishMessageRequest = new PublishMessageRequest(); //创建请求体 PublishMessageRequestBody body = new PublishMessageRequestBody(); body.withMessage(msgContent); body.withSubject("主题"); publishMessageRequest.withTopicUrn("主题URN,可在控制台获取"); publishMessageRequest.withBody(body); //执行请求 PublishMessageResponse response = client.publishMessage(publishMessageRequest); System.out.println(response); }7、客户端开发--SMN之HTTP(S)终端接口开发HTTP(S)终端对应的URL接口需要根据type参数判断是确定订阅、消息或是取消订阅。接口示例代码:@PostMapping("/smn") //该接口完整地址就是创建时配置的终端地址 public void subscriptionConfirmation2(@RequestBody SubscriptionReq subscriptionReq,HttpServletRequest request,HttpServletResponse response){ System.out.println(subscriptionReq); try{ //1.获取参数,封装到MAP中 Map parameterMap = BeanUtils.describe(subscriptionReq); String signing_cert_url = parameterMap.get("signing_cert_url"); String signature = parameterMap.get("signature"); //2.检验消息签名 boolean flag = SmnUtil.isMessageValid(signing_cert_url, signature, parameterMap); //3.判断是订阅还是发布消息 String type = parameterMap.get("type"); if ("SubscriptionConfirmation".equals(type)){ //4.访问订阅确认页面 String map = restTemplate.getForObject(parameterMap.get("subscribe_url"), String.class); System.out.println(map); }else if ("Notification".equals(type)){ //5.消息,打印消息 System.out.println(parameterMap); }else{ //6.取消订阅 } }catch (Exception e){ e.printStackTrace(); } }接口接收请求参数实体类可参考:@Data //Lombok注解 public class SubscriptionReq { private String type; private String signature; private String subject; private String topic_urn; private String message_id; private String signature_version; private String message; private String subscribe_url; private String signing_cert_url; private String timestamp; }8、FunctionGraph函数整合在触发函数对应的方法中调用上面服务方法,实现认证核身认证流程。即在搭建的FunctionGraph Java函数项目中的TriggerTests.java中的apiTest方法中编写人证核身业务逻辑。TriggerTests代码:import java.util.HashMap; import java.util.Map; import com.huawei.services.runtime.Context; import com.huawei.services.runtime.entity.apig.APIGTriggerEvent; import com.huawei.services.runtime.entity.apig.APIGTriggerResponse; import com.huawei.utils.FrsUtils; import com.huawei.utils.IvsUtils; import com.huawei.utils.ObsUtils; import com.huawei.utils.SmnUtils; import com.huaweicloud.sdk.frs.v1.model.DetectLiveFaceByBase64Response; public class TriggerTests { //functionGraph函数触发方法 public APIGTriggerResponse apigTest(APIGTriggerEvent event, Context context) { //1.下载对象,并得到base64编码后结果 String imageBase64 = ObsUtils.download(); //2.活体检测 DetectLiveFaceByBase64Response response = FrsUtils.detectLive(imageBase64); Boolean alive = response.getResult().getAlive(); //3.人证核身 String msg = null; if (alive) { //活体检测成功 String result = IvsUtils.ivsVerify(response.getResult().getPicture()); //result取值:valid-成功;invalid-失败 msg = "valid".equals(result) ? "人证核身成功!是XXX本人。" : "人证核身失败!"; System.out.println(result+"--"+msg); //4.发送结果信息 SmnUtils.sendMsg(msg); } else { msg = "活体检测失败!"; System.out.println(msg); //发送结果消息 SmnUtils.sendMsg(msg); } Map headers = new HashMap(); headers.put("Content-Type", "application/json"); return new APIGTriggerResponse(200, headers, msg); } }六、实现效果1、部署测试将FunctionGraph Java函数对应项目打成jar包。参照帮助文档FunctionGraph创建Java函数,并配置OBS触发器(对应:开发流程->二、FunctionGraph函数代码开发->1、Java函数环境搭建)。FunctionGraph上传项目jar包到函数。通过开发的客户端上传图片或视频到OBS。在客户端刷新接收SMN消息的HTTP(S)终端接口,查看结果信息;查看FunctionGraph日志。 2、示例工程示例代码附件,待补充……
  • [其他] 使用华为云实验来学习AI
    好的学习资源是宝贵的,让人开心的事,现在华为云上有不少的学习资源。质量很不错,而且目前可以免费使用哦。比如华为云实验那么不仅仅是理论上的学习,通过云实验可以进行实践操作。在云环境里已经规划设置好了学习用的虚拟资源,可以说是开箱即用。而且操作指引非常的简单明了。可要好好的珍惜哦。
  • [技术干货] 教你用300行Python代码实现一个人脸识别系统-转载
     用300行Python代码实现一个人脸识别系统 最近又多了不少朋友关注,先在这里谢谢大家。关注我的朋友大多数都是大学生,而且我简单看了一下,低年级的大学生居多,大多数都是为了完成课程设计,作为一个过来人,还是希望大家平时能多抽出点时间学习一下,这种临时抱佛脚的策略要少用嗷。今天我们来python实现一个人脸识别系统,主要是借助了dlib这个库,相当于我们直接调用现成的库来进行人脸识别,就省去了之前教程中的数据收集和模型训练的步骤了。  B站视频:用300行代码实现人脸识别系统_哔哩哔哩_bilibili  CSDN博客:用300行Python代码实现一个人脸识别系统_dejahu的博客-CSDN博客  码云地址:face_dlib_py37_42: 用300行代码开发一个人脸识别系统-42 (gitee.com)  预编译dlib库下载地址:人脸识别系统+windows64位-dlib-19.17.0-cp37-cp37m-win_amd64.zip-深度学习文档类资源-CSDN文库   注:直接安装dlib库可能会有编译错误,可以通过下列方式获取编译好的dlib库  获取方式1: 直接从付费资源下载  人脸识别系统+windows64位-dlib-19.17.0-cp37-cp37m-win_amd64.zip-深度学习文档类资源-CSDN文库  获取方式2: 在B站视频三连并在评论区留下你的邮箱地址  用300行代码实现人脸识别系统_哔哩哔哩_bilibili  获取方式3:  在CSDN博客中三连并在评论区留下你的邮箱地址  用300行Python代码实现一个人脸识别系统_dejahu的博客-CSDN博客  基本原理 人脸识别和目标检测这些还不太一样,比如大家传统的训练一个目标检测模型,你只有对这个目标训练了之后,你的模型才能找到这样的目标,比如你的目标检测模型如果是检测植物的,那显然就不能检测动物。但是人脸识别就不一样,以你的手机为例,你发现你只录入了一次你的人脸信息,不需要训练,他就能准确的识别你,这里识别的原理是通过人脸识别的模型提取你脸部的特征向量,然后将实时检测到的你的人脸同数据库中保存的人脸进行比对,如果相似度超过一定的阈值之后,就认为比对成功。不过我这里说的只是简化版本的人脸识别,现在手机和门禁这些要复杂和安全的多,也不是简单平面上的人脸识别。  总结下来可以分为下面的步骤:  上传人脸到数据库 人脸检测 数据库比对并返回结果 这里我做了一个简答的示意图,可以帮助大家简单理解一下。   代码实现 废话不多说,这里就是我们的代码实现,代码我已经上传到码云,大家直接下载就行,地址就在博客开头。  不会安装python环境的兄弟请看这里:如何在pycharm中配置anaconda的虚拟环境_dejahu的博客-CSDN博客_如何在pycharm中配置anaconda  创建虚拟环境 创建虚拟环境前请大家先下载博客开头的码云源码到本地。  本次我们需要使用到python3.7的虚拟环境,命令如下:  conda create -n face python==3.7.3 conda activate face 1 2 安装必要的库 pip install -r requirements.txt 1 愉快地开始你的人脸识别吧! 执行下面的主文件即可  python UI.py 1 或者在pycharm中按照下面的方式直接运行即可   首先将你需要识别的人脸上传到数据库中   通过第二个视频检测功能识别实时的人脸   详细的代码如下:  # -*- coding: utf-8 -*- """ ------------------------------------------------- Project Name: yolov5-jungong File Name: window.py.py Author: chenming Create Date: 2021/11/8 Description:图形化界面,可以检测摄像头、视频和图片文件 ------------------------------------------------- """ # 应该在界面启动的时候就将模型加载出来,设置tmp的目录来放中间的处理结果 import shutil import PyQt5.QtCore from PyQt5.QtGui import * from PyQt5.QtCore import * from PyQt5.QtWidgets import * import threading import argparse import os import sys from pathlib import Path import cv2 import torch import torch.backends.cudnn as cudnn import os.path as osp FILE = Path(__file__).resolve() ROOT = FILE.parents[0]  # YOLOv5 root directory if str(ROOT) not in sys.path:     sys.path.append(str(ROOT))  # add ROOT to PATH ROOT = Path(os.path.relpath(ROOT, Path.cwd()))  # relative  from models.common import DetectMultiBackend from utils.datasets import IMG_FORMATS, VID_FORMATS, LoadImages, LoadStreams from utils.general import (LOGGER, check_file, check_img_size, check_imshow, check_requirements, colorstr,                            increment_path, non_max_suppression, print_args, scale_coords, strip_optimizer, xyxy2xywh) from utils.plots import Annotator, colors, save_one_box from utils.torch_utils import select_device, time_sync  # 添加一个关于界面 # 窗口主类 class MainWindow(QTabWidget):     # 基本配置不动,然后只动第三个界面     def __init__(self):         # 初始化界面         super().__init__()         self.setWindowTitle('Target detection system')         self.resize(1200, 800)         self.setWindowIcon(QIcon("images/UI/lufei.png"))         # 图片读取进程         self.output_size = 480         self.img2predict = ""         self.device = 'cpu'         # # 初始化视频读取线程         self.vid_source = '0'  # 初始设置为摄像头         self.stopEvent = threading.Event()         self.webcam = True         self.stopEvent.clear()         self.model = self.model_load(weights="runs/train/exp_yolov5s/weights/best.pt",                                      device="cpu")  # todo 指明模型加载的位置的设备         self.initUI()         self.reset_vid()      '''     ***模型初始化***     '''     @torch.no_grad()     def model_load(self, weights="",  # model.pt path(s)                    device='',  # cuda device, i.e. 0 or 0,1,2,3 or cpu                    half=False,  # use FP16 half-precision inference                    dnn=False,  # use OpenCV DNN for ONNX inference                    ):         device = select_device(device)         half &= device.type != 'cpu'  # half precision only supported on CUDA         device = select_device(device)         model = DetectMultiBackend(weights, device=device, dnn=dnn)         stride, names, pt, jit, onnx = model.stride, model.names, model.pt, model.jit, model.onnx         # Half         half &= pt and device.type != 'cpu'  # half precision only supported by PyTorch on CUDA         if pt:             model.model.half() if half else model.model.float()         print("模型加载完成!")         return model      '''     ***界面初始化***     '''     def initUI(self):         # 图片检测子界面         font_title = QFont('楷体', 16)         font_main = QFont('楷体', 14)         # 图片识别界面, 两个按钮,上传图片和显示结果         img_detection_widget = QWidget()         img_detection_layout = QVBoxLayout()         img_detection_title = QLabel("图片识别功能")         img_detection_title.setFont(font_title)         mid_img_widget = QWidget()         mid_img_layout = QHBoxLayout()         self.left_img = QLabel()         self.right_img = QLabel()         self.left_img.setPixmap(QPixmap("images/UI/up.jpeg"))         self.right_img.setPixmap(QPixmap("images/UI/right.jpeg"))         self.left_img.setAlignment(Qt.AlignCenter)         self.right_img.setAlignment(Qt.AlignCenter)         mid_img_layout.addWidget(self.left_img)         mid_img_layout.addStretch(0)         mid_img_layout.addWidget(self.right_img)         mid_img_widget.setLayout(mid_img_layout)         up_img_button = QPushButton("上传图片")         det_img_button = QPushButton("开始检测")         up_img_button.clicked.connect(self.upload_img)         det_img_button.clicked.connect(self.detect_img)         up_img_button.setFont(font_main)         det_img_button.setFont(font_main)         up_img_button.setStyleSheet("QPushButton{color:white}"                                     "QPushButton:hover{background-color: rgb(2,110,180);}"                                     "QPushButton{background-color:rgb(48,124,208)}"                                     "QPushButton{border:2px}"                                     "QPushButton{border-radius:5px}"                                     "QPushButton{padding:5px 5px}"                                     "QPushButton{margin:5px 5px}")         det_img_button.setStyleSheet("QPushButton{color:white}"                                      "QPushButton:hover{background-color: rgb(2,110,180);}"                                      "QPushButton{background-color:rgb(48,124,208)}"                                      "QPushButton{border:2px}"                                      "QPushButton{border-radius:5px}"                                      "QPushButton{padding:5px 5px}"                                      "QPushButton{margin:5px 5px}")         img_detection_layout.addWidget(img_detection_title, alignment=Qt.AlignCenter)         img_detection_layout.addWidget(mid_img_widget, alignment=Qt.AlignCenter)         img_detection_layout.addWidget(up_img_button)         img_detection_layout.addWidget(det_img_button)         img_detection_widget.setLayout(img_detection_layout)          # todo 视频识别界面         # 视频识别界面的逻辑比较简单,基本就从上到下的逻辑         vid_detection_widget = QWidget()         vid_detection_layout = QVBoxLayout()         vid_title = QLabel("视频检测功能")         vid_title.setFont(font_title)         self.vid_img = QLabel()         self.vid_img.setPixmap(QPixmap("images/UI/up.jpeg"))         vid_title.setAlignment(Qt.AlignCenter)         self.vid_img.setAlignment(Qt.AlignCenter)         self.webcam_detection_btn = QPushButton("摄像头实时监测")         self.mp4_detection_btn = QPushButton("视频文件检测")         self.vid_stop_btn = QPushButton("停止检测")         self.webcam_detection_btn.setFont(font_main)         self.mp4_detection_btn.setFont(font_main)         self.vid_stop_btn.setFont(font_main)         self.webcam_detection_btn.setStyleSheet("QPushButton{color:white}"                                                 "QPushButton:hover{background-color: rgb(2,110,180);}"                                                 "QPushButton{background-color:rgb(48,124,208)}"                                                 "QPushButton{border:2px}"                                                 "QPushButton{border-radius:5px}"                                                 "QPushButton{padding:5px 5px}"                                                 "QPushButton{margin:5px 5px}")         self.mp4_detection_btn.setStyleSheet("QPushButton{color:white}"                                              "QPushButton:hover{background-color: rgb(2,110,180);}"                                              "QPushButton{background-color:rgb(48,124,208)}"                                              "QPushButton{border:2px}"                                              "QPushButton{border-radius:5px}"                                              "QPushButton{padding:5px 5px}"                                              "QPushButton{margin:5px 5px}")         self.vid_stop_btn.setStyleSheet("QPushButton{color:white}"                                         "QPushButton:hover{background-color: rgb(2,110,180);}"                                         "QPushButton{background-color:rgb(48,124,208)}"                                         "QPushButton{border:2px}"                                         "QPushButton{border-radius:5px}"                                         "QPushButton{padding:5px 5px}"                                         "QPushButton{margin:5px 5px}")         self.webcam_detection_btn.clicked.connect(self.open_cam)         self.mp4_detection_btn.clicked.connect(self.open_mp4)         self.vid_stop_btn.clicked.connect(self.close_vid)         # 添加组件到布局上         vid_detection_layout.addWidget(vid_title)         vid_detection_layout.addWidget(self.vid_img)         vid_detection_layout.addWidget(self.webcam_detection_btn)         vid_detection_layout.addWidget(self.mp4_detection_btn)         vid_detection_layout.addWidget(self.vid_stop_btn)         vid_detection_widget.setLayout(vid_detection_layout)          # todo 关于界面         about_widget = QWidget()         about_layout = QVBoxLayout()         about_title = QLabel('欢迎使用目标检测系统\n\n 提供付费指导:有需要的好兄弟加下面的QQ即可')  # todo 修改欢迎词语         about_title.setFont(QFont('楷体', 18))         about_title.setAlignment(Qt.AlignCenter)         about_img = QLabel()         about_img.setPixmap(QPixmap('images/UI/qq.png'))         about_img.setAlignment(Qt.AlignCenter)          # label4.setText("如何调整学习率")         label_super = QLabel()  # todo 更换作者信息         label_super.setText("或者你可以在这里找到我-->肆十二")         label_super.setFont(QFont('楷体', 16))         label_super.setOpenExternalLinks(True)         # label_super.setOpenExternalLinks(True)         label_super.setAlignment(Qt.AlignRight)         about_layout.addWidget(about_title)         about_layout.addStretch()         about_layout.addWidget(about_img)         about_layout.addStretch()         about_layout.addWidget(label_super)         about_widget.setLayout(about_layout)          self.left_img.setAlignment(Qt.AlignCenter)         self.addTab(img_detection_widget, '图片检测')         self.addTab(vid_detection_widget, '视频检测')         self.addTab(about_widget, '联系我')         self.setTabIcon(0, QIcon('images/UI/lufei.png'))         self.setTabIcon(1, QIcon('images/UI/lufei.png'))         self.setTabIcon(2, QIcon('images/UI/lufei.png'))      '''     ***上传图片***     '''     def upload_img(self):         # 选择录像文件进行读取         fileName, fileType = QFileDialog.getOpenFileName(self, 'Choose file', '', '*.jpg *.png *.tif *.jpeg')         if fileName:             suffix = fileName.split(".")[-1]             save_path = osp.join("images/tmp", "tmp_upload." + suffix)             shutil.copy(fileName, save_path)             # 应该调整一下图片的大小,然后统一防在一起             im0 = cv2.imread(save_path)             resize_scale = self.output_size / im0.shape[0]             im0 = cv2.resize(im0, (0, 0), fx=resize_scale, fy=resize_scale)             cv2.imwrite("images/tmp/upload_show_result.jpg", im0)             # self.right_img.setPixmap(QPixmap("images/tmp/single_result.jpg"))             self.img2predict = fileName             self.left_img.setPixmap(QPixmap("images/tmp/upload_show_result.jpg"))             # todo 上传图片之后右侧的图片重置,             self.right_img.setPixmap(QPixmap("images/UI/right.jpeg"))      '''     ***检测图片***     '''     def detect_img(self):         model = self.model         output_size = self.output_size         source = self.img2predict  # file/dir/URL/glob, 0 for webcam         imgsz = 640  # inference size (pixels)         conf_thres = 0.25  # confidence threshold         iou_thres = 0.45  # NMS IOU threshold         max_det = 1000  # maximum detections per image         device = self.device  # cuda device, i.e. 0 or 0,1,2,3 or cpu         view_img = False  # show results         save_txt = False  # save results to *.txt         save_conf = False  # save confidences in --save-txt labels         save_crop = False  # save cropped prediction boxes         nosave = False  # do not save images/videos         classes = None  # filter by class: --class 0, or --class 0 2 3         agnostic_nms = False  # class-agnostic NMS         augment = False  # ugmented inference         visualize = False  # visualize features         line_thickness = 3  # bounding box thickness (pixels)         hide_labels = False  # hide labels         hide_conf = False  # hide confidences         half = False  # use FP16 half-precision inference         dnn = False  # use OpenCV DNN for ONNX inference         print(source)         if source == "":             QMessageBox.warning(self, "请上传", "请先上传图片再进行检测")         else:             source = str(source)             device = select_device(self.device)             webcam = False             stride, names, pt, jit, onnx = model.stride, model.names, model.pt, model.jit, model.onnx             imgsz = check_img_size(imgsz, s=stride)  # check image size             save_img = not nosave and not source.endswith('.txt')  # save inference images             # Dataloader             if webcam:                 view_img = check_imshow()                 cudnn.benchmark = True  # set True to speed up constant image size inference                 dataset = LoadStreams(source, img_size=imgsz, stride=stride, auto=pt and not jit)                 bs = len(dataset)  # batch_size             else:                 dataset = LoadImages(source, img_size=imgsz, stride=stride, auto=pt and not jit)                 bs = 1  # batch_size             vid_path, vid_writer = [None] * bs, [None] * bs             # Run inference             if pt and device.type != 'cpu':                 model(torch.zeros(1, 3, *imgsz).to(device).type_as(next(model.model.parameters())))  # warmup             dt, seen = [0.0, 0.0, 0.0], 0             for path, im, im0s, vid_cap, s in dataset:                 t1 = time_sync()                 im = torch.from_numpy(im).to(device)                 im = im.half() if half else im.float()  # uint8 to fp16/32                 im /= 255  # 0 - 255 to 0.0 - 1.0                 if len(im.shape) == 3:                     im = im[None]  # expand for batch dim                 t2 = time_sync()                 dt[0] += t2 - t1                 # Inference                 # visualize = increment_path(save_dir / Path(path).stem, mkdir=True) if visualize else False                 pred = model(im, augment=augment, visualize=visualize)                 t3 = time_sync()                 dt[1] += t3 - t2                 # NMS                 pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)                 dt[2] += time_sync() - t3                 # Second-stage classifier (optional)                 # pred = utils.general.apply_classifier(pred, classifier_model, im, im0s)                 # Process predictions                 for i, det in enumerate(pred):  # per image                     seen += 1                     if webcam:  # batch_size >= 1                         p, im0, frame = path[i], im0s[i].copy(), dataset.count                         s += f'{i}: '                     else:                         p, im0, frame = path, im0s.copy(), getattr(dataset, 'frame', 0)                     p = Path(p)  # to Path                     s += '%gx%g ' % im.shape[2:]  # print string                     gn = torch.tensor(im0.shape)[[1, 0, 1, 0]]  # normalization gain whwh                     imc = im0.copy() if save_crop else im0  # for save_crop                     annotator = Annotator(im0, line_width=line_thickness, example=str(names))                     if len(det):                         # Rescale boxes from img_size to im0 size                         det[:, :4] = scale_coords(im.shape[2:], det[:, :4], im0.shape).round()                          # Print results                         for c in det[:, -1].unique():                             n = (det[:, -1] == c).sum()  # detections per class                             s += f"{n} {names[int(c)]}{'s' * (n > 1)}, "  # add to string                          # Write results                         for *xyxy, conf, cls in reversed(det):                             if save_txt:  # Write to file                                 xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(                                     -1).tolist()  # normalized xywh                                 line = (cls, *xywh, conf) if save_conf else (cls, *xywh)  # label format                                 # with open(txt_path + '.txt', 'a') as f:                                 #     f.write(('%g ' * len(line)).rstrip() % line + '\n')                              if save_img or save_crop or view_img:  # Add bbox to image                                 c = int(cls)  # integer class                                 label = None if hide_labels else (names[c] if hide_conf else f'{names[c]} {conf:.2f}')                                 annotator.box_label(xyxy, label, color=colors(c, True))                                 # if save_crop:                                 #     save_one_box(xyxy, imc, file=save_dir / 'crops' / names[c] / f'{p.stem}.jpg',                                 #                  BGR=True)                     # Print time (inference-only)                     LOGGER.info(f'{s}Done. ({t3 - t2:.3f}s)')                     # Stream results                     im0 = annotator.result()                     # if view_img:                     #     cv2.imshow(str(p), im0)                     #     cv2.waitKey(1)  # 1 millisecond                     # Save results (image with detections)                     resize_scale = output_size / im0.shape[0]                     im0 = cv2.resize(im0, (0, 0), fx=resize_scale, fy=resize_scale)                     cv2.imwrite("images/tmp/single_result.jpg", im0)                     # 目前的情况来看,应该只是ubuntu下会出问题,但是在windows下是完整的,所以继续                     self.right_img.setPixmap(QPixmap("images/tmp/single_result.jpg"))      # 视频检测,逻辑基本一致,有两个功能,分别是检测摄像头的功能和检测视频文件的功能,先做检测摄像头的功能。      '''     ### 界面关闭事件 ###      '''     def closeEvent(self, event):         reply = QMessageBox.question(self,                                      'quit',                                      "Are you sure?",                                      QMessageBox.Yes | QMessageBox.No,                                      QMessageBox.No)         if reply == QMessageBox.Yes:             self.close()             event.accept()         else:             event.ignore()      '''     ### 视频关闭事件 ###      '''      def open_cam(self):         self.webcam_detection_btn.setEnabled(False)         self.mp4_detection_btn.setEnabled(False)         self.vid_stop_btn.setEnabled(True)         self.vid_source = '0'         self.webcam = True         th = threading.Thread(target=self.detect_vid)         th.start()      '''     ### 开启视频文件检测事件 ###      '''      def open_mp4(self):         fileName, fileType = QFileDialog.getOpenFileName(self, 'Choose file', '', '*.mp4 *.avi')         if fileName:             self.webcam_detection_btn.setEnabled(False)             self.mp4_detection_btn.setEnabled(False)             # self.vid_stop_btn.setEnabled(True)             self.vid_source = fileName             self.webcam = False             th = threading.Thread(target=self.detect_vid)             th.start()      '''     ### 视频开启事件 ###      '''      # 视频和摄像头的主函数是一样的,不过是传入的source不同罢了     def detect_vid(self):         # pass         model = self.model         output_size = self.output_size         # source = self.img2predict  # file/dir/URL/glob, 0 for webcam         imgsz = 640  # inference size (pixels)         conf_thres = 0.25  # confidence threshold         iou_thres = 0.45  # NMS IOU threshold         max_det = 1000  # maximum detections per image         # device = self.device  # cuda device, i.e. 0 or 0,1,2,3 or cpu         view_img = False  # show results         save_txt = False  # save results to *.txt         save_conf = False  # save confidences in --save-txt labels         save_crop = False  # save cropped prediction boxes         nosave = False  # do not save images/videos         classes = None  # filter by class: --class 0, or --class 0 2 3         agnostic_nms = False  # class-agnostic NMS         augment = False  # ugmented inference         visualize = False  # visualize features         line_thickness = 3  # bounding box thickness (pixels)         hide_labels = False  # hide labels         hide_conf = False  # hide confidences         half = False  # use FP16 half-precision inference         dnn = False  # use OpenCV DNN for ONNX inference         source = str(self.vid_source)         webcam = self.webcam         device = select_device(self.device)         stride, names, pt, jit, onnx = model.stride, model.names, model.pt, model.jit, model.onnx         imgsz = check_img_size(imgsz, s=stride)  # check image size         save_img = not nosave and not source.endswith('.txt')  # save inference images         # Dataloader         if webcam:             view_img = check_imshow()             cudnn.benchmark = True  # set True to speed up constant image size inference             dataset = LoadStreams(source, img_size=imgsz, stride=stride, auto=pt and not jit)             bs = len(dataset)  # batch_size         else:             dataset = LoadImages(source, img_size=imgsz, stride=stride, auto=pt and not jit)             bs = 1  # batch_size         vid_path, vid_writer = [None] * bs, [None] * bs         # Run inference         if pt and device.type != 'cpu':             model(torch.zeros(1, 3, *imgsz).to(device).type_as(next(model.model.parameters())))  # warmup         dt, seen = [0.0, 0.0, 0.0], 0         for path, im, im0s, vid_cap, s in dataset:             t1 = time_sync()             im = torch.from_numpy(im).to(device)             im = im.half() if half else im.float()  # uint8 to fp16/32             im /= 255  # 0 - 255 to 0.0 - 1.0             if len(im.shape) == 3:                 im = im[None]  # expand for batch dim             t2 = time_sync()             dt[0] += t2 - t1             # Inference             # visualize = increment_path(save_dir / Path(path).stem, mkdir=True) if visualize else False             pred = model(im, augment=augment, visualize=visualize)             t3 = time_sync()             dt[1] += t3 - t2             # NMS             pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)             dt[2] += time_sync() - t3             # Second-stage classifier (optional)             # pred = utils.general.apply_classifier(pred, classifier_model, im, im0s)             # Process predictions             for i, det in enumerate(pred):  # per image                 seen += 1                 if webcam:  # batch_size >= 1                     p, im0, frame = path[i], im0s[i].copy(), dataset.count                     s += f'{i}: '                 else:                     p, im0, frame = path, im0s.copy(), getattr(dataset, 'frame', 0)                 p = Path(p)  # to Path                 # save_path = str(save_dir / p.name)  # im.jpg                 # txt_path = str(save_dir / 'labels' / p.stem) + (                 #     '' if dataset.mode == 'image' else f'_{frame}')  # im.txt                 s += '%gx%g ' % im.shape[2:]  # print string                 gn = torch.tensor(im0.shape)[[1, 0, 1, 0]]  # normalization gain whwh                 imc = im0.copy() if save_crop else im0  # for save_crop                 annotator = Annotator(im0, line_width=line_thickness, example=str(names))                 if len(det):                     # Rescale boxes from img_size to im0 size                     det[:, :4] = scale_coords(im.shape[2:], det[:, :4], im0.shape).round()                      # Print results                     for c in det[:, -1].unique():                         n = (det[:, -1] == c).sum()  # detections per class                         s += f"{n} {names[int(c)]}{'s' * (n > 1)}, "  # add to string                      # Write results                     for *xyxy, conf, cls in reversed(det):                         if save_txt:  # Write to file                             xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(                                 -1).tolist()  # normalized xywh                             line = (cls, *xywh, conf) if save_conf else (cls, *xywh)  # label format                             # with open(txt_path + '.txt', 'a') as f:                             #     f.write(('%g ' * len(line)).rstrip() % line + '\n')                          if save_img or save_crop or view_img:  # Add bbox to image                             c = int(cls)  # integer class                             label = None if hide_labels else (names[c] if hide_conf else f'{names[c]} {conf:.2f}')                             annotator.box_label(xyxy, label, color=colors(c, True))                             # if save_crop:                             #     save_one_box(xyxy, imc, file=save_dir / 'crops' / names[c] / f'{p.stem}.jpg',                             #                  BGR=True)                 # Print time (inference-only)                 LOGGER.info(f'{s}Done. ({t3 - t2:.3f}s)')                 # Stream results                 # Save results (image with detections)                 im0 = annotator.result()                 frame = im0                 resize_scale = output_size / frame.shape[0]                 frame_resized = cv2.resize(frame, (0, 0), fx=resize_scale, fy=resize_scale)                 cv2.imwrite("images/tmp/single_result_vid.jpg", frame_resized)                 self.vid_img.setPixmap(QPixmap("images/tmp/single_result_vid.jpg"))                 # self.vid_img                 # if view_img:                 # cv2.imshow(str(p), im0)                 # self.vid_img.setPixmap(QPixmap("images/tmp/single_result_vid.jpg"))                 # cv2.waitKey(1)  # 1 millisecond             if cv2.waitKey(25) & self.stopEvent.is_set() == True:                 self.stopEvent.clear()                 self.webcam_detection_btn.setEnabled(True)                 self.mp4_detection_btn.setEnabled(True)                 self.reset_vid()                 break         # self.reset_vid()      '''     ### 界面重置事件 ###      '''      def reset_vid(self):         self.webcam_detection_btn.setEnabled(True)         self.mp4_detection_btn.setEnabled(True)         self.vid_img.setPixmap(QPixmap("images/UI/up.jpeg"))         self.vid_source = '0'         self.webcam = True      '''     ### 视频重置事件 ###      '''      def close_vid(self):         self.stopEvent.set()         self.reset_vid()  if __name__ == "__main__":     app = QApplication(sys.argv)     mainWindow = MainWindow()     mainWindow.show()     sys.exit(app.exec_())  ———————————————— 版权声明:本文为CSDN博主「肆十二」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/ECHOSON/article/details/122404926 
  • Faster R-CNN最全讲解-转载
     一:Faster R-CNN的改进 想要更好地了解Faster R-CNN,需先了解传统R-CNN和Fast R-CNN原理,可参考本人呕心撰写的两篇博文 R-CNN史上最全讲解 和 Fast R-CNN讲解。  回到正题,经过R-CNN和Fast RCNN的积淀,Ross B. Girshick在2016年提出了新的Faster RCNN。从网络命名上看就很直白,那么相较于Faster R-CNN到底Faster在哪儿里呢?答案就是:region proposal的提取方式的改变。  Fast R-CNN虽然提出了ROI Pooling的特征提取方式,很好地解决了传统R-CNN中将Region Proposal区域分别输入CNN网络中的弊端。但是!!!始终都是用的传统Selective Search搜索方式确定Region Proposal,训练和测试时消耗了大量时间在RP搜索上。而Faster R-CNN突破性地使用了RPN网络直接提取出RP,并将其融入进整体网络中,使得综合性能有较大提高,在检测速度方面尤为明显。  二:网络架构  上图展示了python版本中的VGG16模型中的faster_rcnn_test.pt的网络结构,可以清晰的看到该网络架构分为以下几个模块:  Conv layers 该Backbone层主要用来提取输入图像中的特征,生成Feature Map以供后两个模块使用。 Region Proposal Networks(RPN) RPN模块用来训练提取出原图中的Region Proposal区域,是整个网络模型中最重要的一个模块。 Semi-Fast R-CNN Semi-Fast R-CNN是我自创的命名,因为和Fast R-CNN的head层几乎一模一样,更多叫法是RoiHead层。当通过RPN模块确定了RP后,就可以训练Fast R-CNN网络了,完成对RP区域的分类与bbox框的微调。 综上述可见,细心的人会发现,Conv layers+Semi-Fast R-CNN不就是Fast R-CNN嘛!所以,Faster R-CNN网络实际上就是RPN + Fast R-CNN,也就是two-stage,训练时也是对两个模块分开训练,测试时先由RPN生成RP,再将带有RP的Feature Map输入进Fast R-CNN中完成分类和预测框回归任务。下面,我将依次对三个模块进行详细讲解。  三:Conv layers模块  Conv layers包含了conv,pooling,relu三种层。以python版本中的VGG16模型中faster_rcnn_test.pt的网络结构为例。Conv layers部分共有13个conv层,13个relu层,4个pooling层。不太熟悉VGG16的小伙伴们要注意一下两个细节:  所有的conv层都是:kernel_size=3,pad=1,stride=1 所有的pooling层都是:kernel_size=2,pad=0,stride=2 经过Conv layers模块后,一个MxN(800×600)大小的输入图像就变为了(M/16)x(N/16)的Feature Map!这样Conv layers生成的feature map中都可以和原图对应起来。  四:Region Proposal Networks(RPN)模块  终于迎来了最重要的RPN模块,大家提起精神,和我一起分析!RPN说到底就是两个功能模块,第一个功能模块是利用二分类给每个anchor打出前景得分,并利用回归计算出每个anchor与其对应的GT间的四个微调参数。第二个功能模块则是根据第一个功能模块输出的得分和四个微调参数,得出ROI并选出合适的RP。 其实RPN只有第一个功能模块需要训练,第二个模块都是基本的选择运算,没有参数需要训练,只是为RoiHead选出训练和测试要用到的region proposal。下面我从这两个功能模块依次讲解:  【Module 1】 在第一模块中,将讲解如何在原图中生成anchors并进行标注,又是如何利用标注好的anchors训练RPN对每个anchor进行前景与背景的二分类与anchor位置的回归微调。下面分步骤进行讲解:  step1: generate_anchor_base 首先,我们需要使用generate_anchor_base函数生成anchors。代码的主要实现思想:首先以特征图feature map的左上角点为基准产生9个anchor,三种尺度,每个尺度再对应三种比例。接着,将特征图左上角的9个anchors乘上原图的缩放比例base_size,也就是经过4个池化层后的16倍。锚点由特征图中的(0.5,0.5)变为了原图上的(8,8),原图上的9个anchors的w和h也变为16倍。然后以原图左上角的anchors为基准,每隔base_size个像素,画9个anchors。画完之后,原图上大概有20000个anchors。 具体实现方法,见如下代码并附手写图:  def generate_anchor_base(base_size=16, ratios=[0.5, 1, 2], #                          anchor_scales=[8, 16, 32]):   #对特征图features以基准长度为16、选择合适的ratios和scales取基准锚点anchor_base。(选择长度为16的原因是图片大小为600*800左右,基准长度16对应的原图区域是256*256,考虑放缩后的大小有128*128,512*512比较合适) #根据基准点生成9个基本的anchor的功能,ratios=[0.5,1,2],anchor_scales=[8,16,32]是长宽比和缩放比例,anchor_scales也就是在base_size的基础上再增加的量,本代码中对应着三种面积的大小(16*8)^2 ,(16*16)^2  (16*32)^2  也就是128,256,512的平方大小     py = base_size / 2.     px = base_size / 2.         anchor_base = np.zeros((len(ratios) * len(anchor_scales), 4),                                 dtype=np.float32)  #(9,4),注意:这里只是以特征图的左上角点为基准产生的9个anchor,     for i in six.moves.range(len(ratios)): #six.moves 是用来处理那些在python2 和 3里面函数的位置有变化的,直接用six.moves就可以屏蔽掉这些变化         for j in six.moves.range(len(anchor_scales)):             h = base_size * anchor_scales[j] * np.sqrt(ratios[i])             w = base_size * anchor_scales[j] * np.sqrt(1. / ratios[i]) #生成9种不同比例的h和w  ''' 这9个anchor形状应为: 90.50967 *181.01933    = 128^2 181.01933 * 362.03867 = 256^2 362.03867 * 724.07733 = 512^2 128.0 * 128.0 = 128^2 256.0 * 256.0 = 256^2 512.0 * 512.0 = 512^2 181.01933 * 90.50967   = 128^2 362.03867 * 181.01933 = 256^2 724.07733 * 362.03867 = 512^2 该函数返回值为anchor_base,形状9*4,是9个anchor的左上右下坐标: -37.2548 -82.5097 53.2548 98.5097 -82.5097    -173.019    98.5097    189.019 -173.019    -354.039    189.019    370.039 -56    -56    72    72 -120    -120    136    136 -248    -248    264    264 -82.5097    -37.2548    98.5097    53.2548 -173.019    -82.5097    189.019    98.5097 -354.039    -173.019    370.039    189.019 '''             index = i * len(anchor_scales) + j             anchor_base[index, 0] = py - h / 2.             anchor_base[index, 1] = px - w / 2.             anchor_base[index, 2] = py + h / 2.             anchor_base[index, 3] = px + w / 2.  #计算出anchor_base画的9个框的左下角和右上角的4个anchor坐标值     return anchor_base   由于后面讲解中有一些转换函数的运用,在这里先贴上,大家可以根据注释先行理解:  def loc2bbox(src_bbox, loc): #已知源bbox 和位置偏差dx,dy,dh,dw,求目标框G     if src_bbox.shape[0] == 0:         return xp.zeros((0, 4), dtype=loc.dtype)        #src_bbox:(R,4),R为bbox个数,4为左下角和右上角四个坐标(这里有误,按照标准坐标系中y轴向下,应该为左上和右下角坐标)     src_bbox = src_bbox.astype(src_bbox.dtype, copy=False)      src_height = src_bbox[:, 2] - src_bbox[:, 0]      #ymax-ymin     src_width = src_bbox[:, 3] - src_bbox[:, 1]     #xmax-xmin     src_ctr_y = src_bbox[:, 0] + 0.5 * src_height    y0+0.5h     src_ctr_x = src_bbox[:, 1] + 0.5 * src_width   #x0+0.5w,计算出中心点坐标 #src_height为Ph,src_width为Pw,src_ctr_y为Py,src_ctr_x为Px     dy = loc[:, 0::4]      #python [start:stop:step]      dx = loc[:, 1::4]     dh = loc[:, 2::4]     dw = loc[:, 3::4] RCNN中提出的边框回归:寻找原始proposal与近似目标框G之间的映射关系,公式在上面     ctr_y = dy * src_height[:, xp.newaxis] + src_ctr_y[:, xp.newaxis]  #ctr_y为Gy     ctr_x = dx * src_width[:, xp.newaxis] + src_ctr_x[:, xp.newaxis] # ctr_x为Gx     h = xp.exp(dh) * src_height[:, xp.newaxis] #h为Gh     w = xp.exp(dw) * src_width[:, xp.newaxis] #w为Gw #上面四行得到了回归后的目标框(Gx,Gy,Gh,Gw)     dst_bbox = xp.zeros(loc.shape, dtype=loc.dtype)  #loc.shape:(R,4),同src_bbox     dst_bbox[:, 0::4] = ctr_y - 0.5 * h     dst_bbox[:, 1::4] = ctr_x - 0.5 * w     dst_bbox[:, 2::4] = ctr_y + 0.5 * h     dst_bbox[:, 3::4] = ctr_x + 0.5 * w   #由中心点转换为左上角和右下角坐标     return dst_bbox      def bbox2loc(src_bbox, dst_bbox): #已知源框和目标框求出其位置偏差     height = src_bbox[:, 2] - src_bbox[:, 0]     width = src_bbox[:, 3] - src_bbox[:, 1]     ctr_y = src_bbox[:, 0] + 0.5 * height     ctr_x = src_bbox[:, 1] + 0.5 * width #计算出源框中心点坐标      base_height = dst_bbox[:, 2] - dst_bbox[:, 0]     base_width = dst_bbox[:, 3] - dst_bbox[:, 1]     base_ctr_y = dst_bbox[:, 0] + 0.5 * base_height     base_ctr_x = dst_bbox[:, 1] + 0.5 * base_width ##计算出目标框中心点坐标      eps = xp.finfo(height.dtype).eps  #求出最小的正数     height = xp.maximum(height, eps)      width = xp.maximum(width, eps)  #将height,width与其比较保证全部是非负      dy = (base_ctr_y - ctr_y) / height     dx = (base_ctr_x - ctr_x) / width     dh = xp.log(base_height / height)     dw = xp.log(base_width / width)  #根据上面的公式二计算dx,dy,dh,dw      loc = xp.vstack((dy, dx, dh, dw)).transpose()    #np.vstack按照行的顺序把数组给堆叠起来     return loc  def bbox_iou(bbox_a, bbox_b):  #求两个bbox的相交的交并比     if bbox_a.shape[1] != 4 or bbox_b.shape[1] != 4:         raise IndexError  #确保bbox第二维为bbox的四个坐标(ymin,xmin,ymax,xmax)     tl = xp.maximum(bbox_a[:, None, :2], bbox_b[:, :2])  #tl为交叉部分框左上角坐标最大值,为了利用numpy的广播性质,bbox_a[:, None, :2]的shape是(N,1,2),bbox_b[:, :2]shape是(K,2),由numpy的广播性质,两个数组shape都变成(N,K,2),也就是对a里每个bbox都分别和b里的每个bbox求左上角点坐标最大值     br = xp.minimum(bbox_a[:, None, 2:], bbox_b[:, 2:]) #br为交叉部分框右下角坐标最小值     area_i = xp.prod(br - tl, axis=2) * (tl < br).all(axis=2) #所有坐标轴上tl    area_a = xp.prod(bbox_a[:, 2:] - bbox_a[:, :2], axis=1)  #计算bboxa的面积     area_b = xp.prod(bbox_b[:, 2:] - bbox_b[:, :2], axis=1) #计算bboxb的面积     return area_i / (area_a[:, None] + area_b - area_i) #计算IOU step2: AnchorTargetCreator 在原图中生成了近乎20000个anchors后,使用AnchorTargetCreator函数对它们进行标注以用于训练。代码的主要实现思想:针对于label的标注,首先剔除掉超过原图边界的anchors,剩下将近15000个。接着,计算每一个anchor与哪个bbox的iou最大以及这个iou值,IOU>0.7的anchor为pos_anchor,IOU<0.3的anchor为neg_anchor。同时,还需计算每个bbox与哪个anchor的iou最大(其实就是矩阵中行最大和列最大的区别),每个bbox对应的最大IOU的anchors也直接设为pos_anchor。但是最后还需要在pos和neg中各随机选128个,也就是128个正样本和128个负样本,将128个正样本的label设置为1,将128个负样本的label设置为0,剩下的(20000-256)个anchors的labels都设为0。针对4个回归框的参数标注,首先对超框的都设为(0,0,0,0),框内的近乎15000个anchors的4个参数就是它们与最大IOU对应的bbox的实际偏移量。具体代码见下:  # 下面是AnchorTargetCreator()代码,作用是生成训练要用的anchor(与对应框iou值最大或者最小的各128个框的坐标和256个label(0或者1)) class AnchorTargetCreator(object):  # 利用每张图中bbox的真实标签来为所有任务分配ground truth!     # 为Faster-RCNN专有的RPN网络提供自我训练的样本,RPN网络正是利用AnchorTargetCreator产生的样本作为数据进行网络的训练和学习的,这样产生的预测anchor的类别和位置才更加精确,anchor变成真正的ROIS需要进行位置修正,而AnchorTargetCreator产生的带标签的样本就是给RPN网络进行训练学习用哒     def __call__(self, bbox, anchor, img_size):  # anchor:(S,4),S为anchor数         img_H, img_W = img_size         n_anchor = len(anchor)  # 一般对应20000个左右anchor         inside_index = _get_inside_index(anchor, img_H, img_W)  # 将那些超出图片范围的anchor全部去掉,只保留位于图片内部的序号         anchor = anchor[inside_index]  # 保留位于图片内部的anchor         argmax_ious, label = self._create_label(inside_index, anchor, bbox)  # 筛选出符合条件的正例128个负例128并给它们附上相应的label         loc = bbox2loc(anchor, bbox[argmax_ious])  # 计算每一个anchor与对应bbox求得iou最大的bbox计算偏移量(注意这里是位于图片内部的每一个)         label = _unmap(label, n_anchor, inside_index, fill=-1)  # 将位于图片内部的框的label对应到所有生成的20000个框中(label原本为所有在图片中的框的)         loc = _unmap(loc, n_anchor, inside_index, fill=0)  # 将回归的框对应到所有生成的20000个框中(label原本为所有在图片中的框的)         return loc, label          # 下面为调用的_creat_label() 函数      def _create_label(self, inside_index, anchor, bbox):         label = np.empty((len(inside_index),), dtype=np.int32)  # inside_index为所有在图片范围内的anchor序号         label.fill(-1)  # 全部填充-1         argmax_ious, max_ious, gt_argmax_ious = self._calc_ious(anchor, bbox, inside_index)         调用_calc_ious()函数得到每个anchor与哪个bbox的iou最大以及这个iou值、每个bbox与哪个anchor的iou最大(需要体会从行和列取最大值的区别)         label[             max_ious < self.neg_iou_thresh] = 0  # 把每个anchor与对应的框求得的iou值与负样本阈值比较,若小于负样本阈值,则label设为0,pos_iou_thresh=0.7, neg_iou_thresh=0.3         label[gt_argmax_ious] = 1  # 把与每个bbox求得iou值最大的anchor的label设为1         label[max_ious >= self.pos_iou_thresh] = 1  ##把每个anchor与对应的框求得的iou值与正样本阈值比较,若大于正样本阈值,则label设为1         n_pos = int(self.pos_ratio * self.n_sample)  # 按照比例计算出正样本数量,pos_ratio=0.5,n_sample=256         pos_index = np.where(label == 1)[0]  # 得到所有正样本的索引         if len(pos_index) > n_pos:  # 如果选取出来的正样本数多于预设定的正样本数,则随机抛弃,将那些抛弃的样本的label设为-1             disable_index = np.random.choice(                 pos_index, size=(len(pos_index) - n_pos), replace=False)             label[disable_index] = -1         n_neg = self.n_sample - np.sum(label == 1)  # 设定的负样本的数量         neg_index = np.where(label == 0)[0]  # 负样本的索引         if len(neg_index) > n_neg:             disable_index = np.random.choice(                 neg_index, size=(len(neg_index) - n_neg),                 replace=False)  # 随机选择不要的负样本,个数为len(neg_index)-neg_index,label值设为-1             label[disable_index] = -1         return argmax_ious, label      # 下面为调用的_calc_ious()函数     def _calc_ious(self, anchor, bbox, inside_index):         ious = bbox_iou(anchor, bbox)  # 调用bbox_iou函数计算anchor与bbox的IOU, ious:(N,K),N为anchor中第N个,K为bbox中第K个,N大概有15000个         argmax_ious = ious.argmax(axis=1)  # 1代表行,0代表列         max_ious = ious[np.arange(len(inside_index)), argmax_ious]  # 求出每个anchor与哪个bbox的iou最大,以及最大值,max_ious:[1,N]         gt_argmax_ious = ious.argmax(axis=0)         gt_max_ious = ious[gt_argmax_ious, np.arange(ious.shape[1])]  # 求出每个bbox与哪个anchor的iou最大,以及最大值,gt_max_ious:[1,K]         gt_argmax_ious = np.where(ious == gt_max_ious)[0]  # 然后返回最大iou的索引(每个bbox与哪个anchor的iou最大),有K个         return argmax_ious, max_ious, gt_argmax_ious step3:训练RPN 生成并标注完了训练样本,终于来到了第一功能模块的训练环节。首先对Feature Map进行3×3卷积操作,而后分为两个分支,每一分支都先进行1×1卷积操作,目的是压缩channel。第一个分支的通道数压缩成9×2,9代表每一个锚点的9个anchors,2代表每一个anchor是前景或后景的概率。第二个分支的通道数压缩成9×4,9代表每一个锚点的9个anchors,4代表每一个anchor的4个位置参数预测值。每一个min-batch,只对128个负样本和128个正样本计算分类损失和回归损失( 实际上只对正样本进行回归损失计算)。损失函数如下:  分类损失函数选择的是传统的交叉熵损失函数,分类损失函数选择的是Smooth L1 Loss回归损失函数,如下:  由于在实际过程中,N c N_cN  c ​   = min_batch ,N r N_rN  r ​   = feature map的大小,两者差距过大,用参数λ平衡二者,使总的网络Loss计算过程中能够均匀考虑2种Loss。  【Module 2】 第二个模块则是根据第一个功能模块输出的得分和四个位置参数,得出ROI并选出合适的RP。该模块在ProposalCreator函数中完成,代码的核心思想:通过训练好的第一个模块输出的约20000个anchor的4个位置参数,微调原图中所有anchor,生成20000个ROI。接着,对ROI进行裁剪,并且剔除掉裁剪后长和宽小于设定阈值的ROI。然后,根据前景score对剩余ROI进行由大到小的排序,若用于RoiHead训练,则取前12000个ROI,经过NMS二次筛选后只取前2000个ROI作为最终的region proposals。若用于RoiHead测试,则取前2000个ROI,经过NMS二次筛选后只取前300个ROI作为最终的region proposals。具体代码实现如下:  # 下面是ProposalCreator的代码: 这部分的操作不需要进行反向传播,因此可以利用numpy/tensor实现 class ProposalCreator:  # 对于每张图片,利用它的feature map,计算(H/16)x(W/16)x9(大概20000)个anchor属于前景的概率,然后从中选取概率较大的12000张,利用位置回归参数,修正这12000个anchor的位置, 利用非极大值抑制,选出2000个ROIS以及对应的位置参数。     def __call__(self, loc, score, anchor, img_size,                  scale=1.):  # 这里的loc和score是经过region_proposal_network中经过1x1卷积分类和回归得到的         if self.parent_model.training:             n_pre_nms = self.n_train_pre_nms  # 12000             n_post_nms = self.n_train_post_nms  # 经过NMS后有2000个          else:             n_pre_nms = self.n_test_pre_nms  # 6000             n_post_nms = self.n_test_post_nms  # 经过NMS后有300个          roi = loc2bbox(anchor, loc)  # 将bbox转换为近似groudtruth的anchor(即rois)         roi[:, slice(0, 4, 2)] = np.clip(roi[:, slice(0, 4, 2)], 0, img_size[0])  # 裁剪将rois的ymin,ymax限定在[0,H]         roi[:, slice(1, 4, 2)] = np.clip(roi[:, slice(1, 4, 2)], 0, img_size[1])  # 裁剪将rois的xmin,xmax限定在[0,W]          min_size = self.min_size * scale  # 16         hs = roi[:, 2] - roi[:, 0]  # rois的宽         ws = roi[:, 3] - roi[:, 1]  # rois的长         keep = np.where((hs >= min_size) & (ws >= min_size))[0]  # 确保rois的长宽大于最小阈值         roi = roi[keep, :]          score = score[keep]  # 对剩下的ROIs进行打分(根据region_proposal_network中rois的预测前景概率)         order = score.ravel().argsort()[::-1]  # 将score拉伸并逆序(从高到低)排序         if n_pre_nms > 0:             order = order[:n_pre_nms]  # train时从20000中取前12000个rois,test取前6000个         roi = roi[order, :]          keep = non_maximum_suppression(         cp.ascontiguousarray(cp.asarray(roi)),             thresh=self.nms_thresh)  # (具体需要看NMS的原理以及输入参数的作用)调用非极大值抑制函数,将重复的抑制掉,就可以将筛选后ROIS进行返回。经过NMS处理后Train数据集得到2000个框,Test数据集得到300个框         if n_post_nms > 0:             keep = keep[:n_post_nms]         roi = roi[keep]         return roi 五:Semi-Fast R-CNN(RoiHead) 介绍完了RPN模块后,最重要的RP提取任务已经完成。接下来RoiHead只要将RPN输出的RP结果作为输入,来训练和测试。我将训练阶段和测试模块分开讲解:  【训练阶段】 step1:RP中标注训练样本 如果是在训练阶段,RPN会输出大约2000个region proposals。那么如何从中选取样本并标注呢?ProposalTargetCreator函数实现了这一任务,代码的核心思想是:首先,将2000个RP和M个Ground Truth拼接起来,也就是把所有的GT也都作为RP。为什么呢?  前方核能:其实答案很简单,现在是RoiHead的训练阶段,训练RoiHead的分类和二次回归能力。也就是说,需要给该网络输入带有类别标注和实际位置参数的训练数据,经过RPN选出的大范围包括实物的ROI可以作为训练数据,说实话也都是歪歪扭扭的,拿它们训练RoiHead其实主要是出于测试阶段的实际情况出发的,毕竟要从任务实际需要适应的情况出发,实际测试的时候都是RPN找出RP,RoiHead再对它们进行分类和bbox修正的,这些RP都是歪歪扭扭的。所以也就是说,拿这些样本进行分类本身就是不严谨的,不是说完完全全包裹住实物合格的类别样本。毕竟现在是训练阶段嘛,稍微偷偷喂给网络一点”优质碳水“,未尝不可,直接把最优质的GT给它训练去,货真价实的分类样本,舒不舒服,白用白不用。  回到正题,拼接好了RP和GT后,计算它们的最大IOU所对应的那个GT的label,将(label+1)作为每一个RP的类别标注(1~20)。然后将IOU>0.5的RP中选64个作为正样本,将IOU<0.5的RP中选出64个作为负样本并将负样本的label设为0,最后将共128个正负样本打包出来,作为RoiHead的训练输入。具体的实现代码如下:  # 下面是ProposalTargetCreator代码:ProposalCreator产生2000个ROIS,但是这些ROIS并不都用于训练,经过本ProposalTargetCreator的筛选产生128个用于自身的训练     class ProposalTargetCreator(object):  # 为2000个rois赋予ground truth!(严格讲挑出128个赋予ground truth!)         # 输入:2000个rois、一个batch(一张图)中所有的bbox ground truth(R,4)、对应bbox所包含的label(R,1)(VOC2007来说20类0-19)         # 输出:128个sample roi(128,4)、128个gt_roi_loc(128,4)、128个gt_roi_label(128,1)         def __call__(self, roi, bbox, label, loc_normalize_mean=(0., 0., 0., 0.),                      loc_normalize_std=(0.1, 0.1, 0.2, 0.2)):  # 因为这些数据是要放入到整个大网络里进行训练的,比如说位置数据,所以要对其位置坐标进行数据增强处理(归一化处理)             n_bbox, _ = bbox.shape             roi = np.concatenate((roi, bbox), axis=0)  # 首先将2000个roi和m个bbox给concatenate了一下成为新的roi(2000+m,4)。             pos_roi_per_image = np.round(                 self.n_sample * self.pos_ratio)  # n_sample = 128,pos_ratio=0.5,round 对传入的数据进行四舍五入             iou = bbox_iou(roi, bbox)  # 计算每一个roi与每一个bbox的iou  (2000+m,m)             gt_assignment = iou.argmax(axis=1)  # 按行找到最大值,返回最大值对应的序号以及其真正的IOU。返回的是每个roi与**哪个**bbox的最大,以及最大的iou值             max_iou = iou.max(axis=1)  # 每个roi与对应bbox最大的iou             gt_roi_label = label[gt_assignment] + 1  # 从1开始的类别序号,给每个类得到真正的label(将0-19变为1-20)             pos_index = np.where(max_iou >= self.pos_iou_thresh)[0]  # 同样的根据iou的最大值将正负样本找出来,pos_iou_thresh=0.5             pos_roi_per_this_image = int(                 min(pos_roi_per_image, pos_index.size))  # 需要保留的roi个数(满足大于pos_iou_thresh条件的roi与64之间较小的一个)             if pos_index.size > 0:                 pos_index = np.random.choice(                     pos_index, size=pos_roi_per_this_image, replace=False)  # 找出的样本数目过多就随机丢掉一些              neg_index = np.where((max_iou < self.neg_iou_thresh_hi) &                                  (max_iou >= self.neg_iou_thresh_lo))[0]  # neg_iou_thresh_hi=0.5,neg_iou_thresh_lo=0.0             neg_roi_per_this_image = self.n_sample - pos_roi_per_this_image  # #需要保留的roi个数(满足大于0小于neg_iou_thresh_hi条件的roi与64之间较小的一个)             neg_roi_per_this_image = int(min(neg_roi_per_this_image,                                              neg_index.size))             if neg_index.size > 0:                 neg_index = np.random.choice(                     neg_index, size=neg_roi_per_this_image, replace=False)  # 找出的样本数目过多就随机丢掉一些              keep_index = np.append(pos_index, neg_index)             gt_roi_label = gt_roi_label[keep_index]             gt_roi_label[pos_roi_per_this_image:] = 0  # 负样本label 设为0             sample_roi = roi[keep_index]             # 那么此时输出的128*4的sample_roi就可以去扔到 RoIHead网络里去进行分类与回归了。同样, RoIHead网络利用这sample_roi+featue为输入,输出是分类(21类)和回归(进一步微调bbox)的预测值,那么分类回归的groud truth就是ProposalTargetCreator输出的gt_roi_label和gt_roi_loc。             gt_roi_loc = bbox2loc(sample_roi, bbox[gt_assignment[keep_index]])  # 求这128个样本的groundtruth             gt_roi_loc = ((gt_roi_loc - np.array(loc_normalize_mean, np.float32)                            ) / np.array(loc_normalize_std,                                         np.float32))  # ProposalTargetCreator首次用到了真实的21个类的label,且该类最后对loc进行了归一化处理,所以预测时要进行均值方差处理             return sample_roi, gt_roi_loc, gt_roi_label  step2: 正式训练 将这128个标注好的训练样本,从原图中投影到Feature Map中对应的ROI区域中,然后进入RoiPooling层,将这些大小不一的ROI区域变为同一长度的向量,再经过两层 4096 FC层,分别得到softmax21分类打分和bbox的84个参数(21 * 4)的预测结果,放入损失函数中进行反向传播更新网络权重,其中只计算正样本的回归框损失。损失函数和RPN的类似,这里就不再赘述了,贴上损失函数核心代码:  def _fast_rcnn_loc_loss(pred_loc, gt_loc, gt_label, sigma): #输入分别为rpn回归框的偏移量与anchor与bbox的偏移量以及label     in_weight = t.zeros(gt_loc.shape).cuda()     # Localization loss is calculated only for positive rois.     # NOTE:  unlike origin implementation,      # we don't need inside_weight and outside_weight, they can calculate by gt_label     in_weight[(gt_label > 0).view(-1, 1).expand_as(in_weight).cuda()] = 1     loc_loss = _smooth_l1_loss(pred_loc, gt_loc, in_weight.detach(), sigma) #sigma设置为1     # Normalize by total number of negtive and positive rois.     loc_loss /= ((gt_label >= 0).sum().float()) # ignore gt_label==-1 for rpn_loss #除去背景类     return loc_loss roi_cls_loss = nn.CrossEntropyLoss()(roi_score, gt_roi_label.cuda())#求交叉熵损失 1 【测试阶段】 RoiHead的测试阶段就是将RPN中输出的300个RP,输入进网络中,最后会输出每个RP的类和4个回归框微调参数。剔除掉高于背景(0)阈值和最大类别(1~20)得分低于阈值的RP,最后根据回归参数,对筛选后剩下的RP框进行微调,得到最终的bounding box!至此,大功告成。  六:Faster R-CNN训练方法 Faster-RCNN有两种训练方式:四步交替迭代训练和联合训练。本文主要讲解四步交替迭代的训练方式,如下所示:  1、训练RPN,使用大型数据集预训练模型初始化共享卷积和RPN权重,端到端训练RPN,用于生成Region Proposals; 2、训练Fast R-CNN,使用相同的预训练模型初始化共享卷积【注意此处是初始化一个新的与第1步结构相同的共享卷积网络,而不是第1步中训练得到的】,锁住第1步训练好的RPN权重,结合RPN得到的Proposals训练RCNN网络; 3、调优RPN,使用第2步训练好的共享卷积和RCNN,固定共享卷积层,继续训练RPN,我认为这一步相当于对第1步训练好的RPN进行微调; 4、调优Fast R-CNN,使用第3步训练好的共享卷积和RPN(固定住共享卷积层),继续对RCNN进行训练微调 5、重复上述步骤3、4,进行迭代。(一般到步骤四其实已经够了,后面迭代训练后的效果几乎无提升)  下面是一张训练过程流程图,应该更加清晰:  七:Faster R-CNN测试方法 接下面讲解整个网络的测试过程,即将大功告成啦!  step1:输入图像经过卷积层得到feature map step2:feature map经过RPN得到300个RP step3:将RP输入到RoiHead网络中 step4:得出每个RP的类别得分和bbox位置参数 step5:由得分阈值选出最终的ROI step6:结合位置参数微调ROI的bbox框 step7:经过NMS后画出最终检测框 八:总结 Fast R-CNN尽管速度和精度上都有了很大的提升,但仍然未能实现端到端(end-to-end)的目标检测,比如候选区域的获得不能同步进行,速度上还有提升空间。  最后附上一个超大原理流程图收尾,供大家参考:     至此我对Faster R-CNN全部流程与细节,进行了深度讲解,希望对大家有所帮助,有不懂的地方或者建议,欢迎大家在下方留言评论。(码字不易,各位看官点个赞,手留余香~谢谢!)  我是努力在CV泥潭中摸爬滚打的江南咸鱼,我们一起努力,不留遗憾! ———————————————— 版权声明:本文为CSDN博主「江南綿雨」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_43702653/article/details/124045469 
总条数:7868 到第 页
上滑加载中