当前位置: 首页 > 科技观察

如何实现模拟人类视觉注意力的循环神经网络?

时间:2023-03-20 14:04:28 科技观察

原理本文使用的代码在https://github.com/SquarePants1991/OpenGLESLearn.git的ARKit分支中。iOS11引入了新的框架ARKit,通过ARKit和SceneKit可以轻松创建ARApp。Apple也提供了AR的基础应用框架,你可以从中直接开始你的ARApp的开发。不过,本系列文章将使用OpenGLES为ARKit提供渲染支持。接下来,我们先来了解一下ARKit的理论知识。AR的基本概念AR最基本的概念是将虚拟计算机图形与真实环境相结合的技术。有很多方法可以实现这种技术。使用2D或3D图形修饰人脸,常见于一些相机、视频类APP,主要使用人脸识别和追踪技术。基于标记的3D模型放置,例如基于AR的故事书、阴阳师的生命呼唤。标记可以是用黑框包裹的简单标记,也可以是复杂图片的特征点训练数据。如果您有兴趣,请前往ARToolKit,这是一个主要用于基于标记的AR的开源AR框架。ARToolkit6Beta最近发布了,不知道有没有新功能开放。跟踪真实环境的特征点,计算真实相机在真实环境中的位置。所谓特征点就是图片中灰度变化比较剧烈的位置。因此,要想计算得更准确、更稳定,就需要在真实环境中有更丰富的颜色变化。ARKit使用此原理进行相机定位。WorldTracking是AR实现中最重要的部分,通过跟踪现实世界的特征点,计算出真实的摄像机位置并将其应用于3D世界中的虚拟摄像机。计算结果的准确性直接影响渲染结果。ARKit使用ARSession来管理整个AR处理管道,包括相机位置的计算。#pragmamake-ARControl-(void)setupAR{if(@available(iOS11.0,*)){self.arSession=[ARSessionnew];self.arSession.delegate=self;}}-(void)runAR{if(@available(iOS11.0,*)){ARWorldTrackingSessionConfiguration*config=[ARWorldTrackingSessionConfigurationnew];config.planeDetection=ARPlaneDetectionHorizo??ntal;[self.arSessionrunWithConfiguration:config];}}-(void)pauseAR{if(@available(iOS11.0,*)){[self.arSession暂停];}}ARSession的使用方法很简单,初始化,设置delegate,开启ARSession需要传入一个配置ARWorldTrackingSessionConfiguration,ARWorldTrackingSessionConfiguration意味着AR系统会追踪现实世界的特征点,计算相机位置。Apple也可能会在未来发布诸如ARMarkerTrackingSessionConfiguration之类的配置来识别跟踪标记。开启ARSession后,会启动摄像头,通过传感器感知手机的位置。借用WWDC的一张图。ARSession将摄像头捕捉到的视频流和位置信息进行合成,生成一系列连续的ARFrame。-(void)session:(ARSession*)sessiondidUpdateFrame:(ARFrame*)frame{...}每个ARFrame包含从摄像头捕获的图片,关于摄像头位置的信息等。在这个方法中我们需要绘制捕获的图像通过相机。根据相机位置等信息绘制3D对象等。平面检测ARKit提供了另一个很酷的功能,可以检测真实世界的平面,并提供一个ARPlaneAnchor对象来描述平面的位置、大小、方向等。-(void)runAR{if(@available(iOS11.0,*)){ARWorldTrackingSessionConfiguration*config=[ARWorldTrackingSessionConfiguration新];config.planeDetection=ARPlaneDetectionHorizo??ntal;[self.arSessionrunWithConfiguration:config];}}以上config.planeDetection=ARPlaneDetectionHorizo??ntal;将检测平面的类型设置为水平。但是,这是当前唯一可用的选项。如果ARKit检测到平面,它会通过委托中的方法-(void)session:(ARSession*)sessiondidAddAnchors:(NSArray*)anchors向你提供数据。可以通过判断ARAnchor是否为ARPlaneAnchor来判断是否检测到平面。ARAnchor用于表示3D对象在真实环境中的位置。您只需要保持您的3D对象和ARAnchor的3D变换同步即可实现AR效果。HitTestHitTest允许您方便地将对象放置在检测到的平面上。当你点击屏幕时,使用HitTest检测你点击的位置有哪些平面,并提供ARAnchor来设置放置物体的位置。[帧hitTest:CGPointMake(0.5,0.5)类型:ARHitTestResultTypeExistingPlane];使用ARFrame的hitTest方法,第一个传入点的范围是(0,0)到(1,1),第二个参数表示可以检测到哪些物体。可以检测的对象如下。ARHitTestResultTypeFeaturePoint,根据最近的特征点检测的连续表面。ARHitTestResultTypeEstimatedHorizo??ntalPlane,以不精确的方式计算的垂直于重力的平面。ARHitTestResultTypeExistingPlane,已经检测到的平面,检测时忽略平面本身的大小,将其视为无限平面。ARHitTestResultTypeExistingPlaneUsingExtent,检测到的平面,检测时考虑平面本身的大小。如果检测成功,将返回NSArray*。ARHitTestResult包含检测类型、交点距离、平面的ARAnchor。请注意,仅当检测到ARHitTestResultTypeExistingPlane和ARHitTestResultTypeExistingPlaneUsingExtent时,ARAnchor才可用。这四种检测类型可以通过|同时存在,比如ARHitTestResultTypeEstimatedHorizo??ntalPlane|ARHitTestResultTypeExistingPlane。光照强度调节ARKit还提供了检测光照强度的功能,主要是让3D模型的光照强度与环境光照强度保持一致。ARFrame中有一个lightEstimate变量,如果光强检测成功,就会有一个值。该值是ARLightEstimate类型,它只包含一个变量ambientIntensity。在3D光照模型中,它对应于环境光,它的值是从0到2000。当用OpenGL渲染时,你可以用这个值来调整光照模型中的环境光强度。ARKit的理论知识到这里就差不多结束了,下篇文章将介绍如何使用OpenGLES渲染ARFrame中的内容。用于实现本文的代码在https://github.com/SquarePants1991/OpenGLESLearn.git的ARKit分支中。本文使用的OpenGL基础代码来自OpenGLES系列,具有渲染几何、纹理等基础功能,实现细节不再赘述。集成ARKit的关键代码在ARGLBaseViewController中。我们来看看它的代码。处理ARFrame-(void)session:(ARSession*)sessiondidUpdateFrame:(ARFrame*)frame{//同步YUV信息到yTexture和uvTextureCVPixelBufferRefpixelBuffer=frame.capturedImage;GLsizeiimageWidth=(GLsizei)CVPixelBufferGetWidthOfPlane(pixelBuffer,0);GLsizeiimageHeight=(GLsizei)CVPixelBufferGetHeightOfPlane(pixelBuffer,0);void*baseAddress=CVPixelBufferGetBaseAddressOfPlane(pixelBuffer,0);glBindTexture(GL_TEXTURE_2D,self.yTexture);glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE,imageWidth,imageHeight,0,GL_LUMINANCE,GL_UNSIGNED_BYTE,baseAddress);glBindTexture(GL_TEXTURE_2D,0);imageWidth=(GLsizei)CVPixelBufferGetWidthOfPlane(pixelBuffer,1);imageHeight=(GLsizei)CVPixelBufferGetHeightOfPlane(pixelBuffer,1);void*laAddress=CVPixelBufferGetBaseAddressOfPlane(pixelBuffer,1);glBindTexture(GL_TEXTURE_2D,self.uvTexture);glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE_ALPHA,imageWidth,imageHeight,0,GL_LUMINANCE_ALPHA,GL_UNSIGNED_BYTE,laAddress);glBindTexture(GL_TEXTURE_2D,0);self.videoPlane.yuv_yTexture=self.yTexture;self.videoPlane.yuv_uvTexture=self.uvTexture;[自行设置视口:CGSizeMake(imageHeight,synccamera)matrix_float4x4cameraMatrix=matrix_invert([frame.cameratransform]);GLKMatrix4newCameraMatrix=GLKMatrix4Identity;for(intcol=0;col<4;++col){for(introw=0;row<4;++row){newCameraMatrix.m[col*4+row]=cameraMatrix.columns[col][排];}}self.cameraMatrix=newCameraMatrix;GLKVector3forward=GLKVector3Make(-self.cameraMatrix.m13,-self.cameraMatrix.m23,-self.cameraMatrix.m33);GLKMatrix4rotationMatrix=GLKMatrix4MakeRotation(M_PI/2,forward.x,forward.y,forward.z);self.cameraMatrix=GLKMatrix4Multiply(rotationMatrix,newCameraMatrix);}上面的代码展示了如何处理ARKit捕获的ARFrame,ARFrame的capturedImage存放的是相机捕获的图片信息,类型为CVPixelBufferRef默认情况下,图片信息格式为YUV,由两个Planes存储,也可以理解为两张图片。一种格式是Y(Luminance),保存亮度信息,另一种是UV(Chrominance,Chroma),保存色度和密度。我们需要将这两个图像绑定到不同的纹理上,然后使用Shader中的算法将YUV转换为RGB。下面是处理贴图的FragmentShader,使用公式进行颜色转换。精度highp浮点数;变化的vec3fragNormal;变化的vec2fragUV;均匀的浮点数;均匀的mat4normalMatrix;均匀的sampler2DyMap;均匀的sampler2DuvMap;voidmain(void){vec4Y_planeColor=texture2D(yMap,fragUV);,碎片紫外线);浮动Cb、Cr、Y;浮动R,G,B;Y=Y_planeColor.r*255.0;Cb=CbCr_planeColor.r*255.0-128.0;Cr=CbCr_planeColor.a*255.0-128.0;R=1.402*铬+Y;G=-0.344*Cb-0.714*Cr+Y;B=1.772*Cb+Y;vec4videoColor=vec4(R/255.0,G/255.0,B/255.0,1.0);gl_FragColor=videoColor;}对纹理进行处理绑定后,为了保证纹理在不同屏幕尺寸下不被不均匀拉伸,重新计算视口[selfsetupViewport:CGSizeMake(imageHeight,imageWidth)];。接下来,将ARKit计算的相机变换分配给self.cameraMatrix。注意ARKit抓取的图片需要旋转90度才能正常显示,所以在设置Viewport的时候故意把宽高调反了,最后旋转了相机。VideoPlaneVideoPlane是显示视频写入的几何体,它可以接收两个贴图,Y和UV。@interfaceVideoPlane:GLObject@property(assign,nonatomic)GLuintyuv_yTexture;@property(assign,nonatomic)GLuintyuv_uvTexture;-(instancetype)initWithGLContext:(GLContext*)context;-(void)update:(NSTimeInterval)timeSinceLastUpdate;-(void)draw:(GLContext*)glContext;@end...-(void)draw:(GLContext*)glContext{[glContextsetUniformMatrix4fv:@"modelMatrix"value:self.modelMatrix];布尔可以反转;GLKMatrix4normalMatrix=GLKMatrix4InvertAndTranspose(self.modelMatrix,&canInvert);[glContextsetUniformMatrix4fv:@“normalMatrix”值:canInvert?正常矩阵:GLKMatrix4Identity];[glContextbindTextureName:self.yuv_yTextureto:GL_TEXTURE0uniformName:@"yMap"];[glContextbindTextureName:self.yuvTextureName:self.yuv:GL_TEXTURE1uniformName:@"uvMap"];[glContextdrawTrianglesWithVAO:vaovertexCount:6];}其他函数很简单,就是画一个正方形,最后配合Shader显示视频渲染成YUV格式的数据。透视投影矩阵可以得到ARFrame中渲染所需的纹理和相机矩阵。除了这些,匹配真实相机的透视投影矩阵也是必须的。它可以使渲染的3D对象的透视看起来自然。-(void)session:(ARSession*)sessioncameraDidChangeTrackingState:(ARCamera*)camera{matrix_float4x4projectionMatrix=[cameraprojectionMatrixWithViewportSize:self.viewport.sizeorientation:UIInterfaceOrientationPortraitzNear:0.1zFar:1000];GLKMatrix4newWorldProjectionMatrix=GLKMatrix4Identity;for(intcol=0;col<4;++col){for(introw=0;row<4;++row){newWorldProjectionMatrix.m[col*4+row]=projectionMatrix.columns[col][排];}}self.worldProjectionMatrix=newWorldProjectionMatrix;}上面的代码演示了如何通过ARKit获取3D透视投影矩阵。有了透视投影矩阵和相机矩阵,就可以很方便的使用OpenGL渲染物体。-(void)glkView:(GLKView*)viewdrawInRect:(CGRect)rect{[superglkView:viewdrawInRect:rect];[self.objectsenumerateObjectsUsingBlock:^(GLObject*obj,NSUIntegeridx,BOOL*stop){[obj.contextactive];[obj.contextsetUniform1f:@"elapsedTime"值:(GLfloat)self.elapsedTime];[obj.contextsetUniformMatrix4fv:@"projectionMatrix"value:self.worldProjectionMatrix];[obj.contextsetUniformMatrix4fv:@"cameraMatrix"value:self.cameraMatrix];[obj.contextsetUniform3fv:@"lightDirection"value:self.lightDirection];[对象绘制:obj.context];}];}本文主要介绍OpenGLES渲染ARKit的基本思想,不过多描述OpenGLES技术细节。有兴趣的可以直接cloneGithub上的代码进行更深入的理解。