在Android用vulkan完成蓝绿幕扣像

在Android用vulkan完成蓝绿幕扣像,第1张

概述效果图(1080P处理)因为摄像头开启自动曝光,画面变动时,亮度变化导致扣像在转动时如上。源码地址vulkan_extratest这个demo主要测试二点,一是测试ndkcamera集成效果,二是本项目对接外部实现的vulkan层是否方便,用于以后移植GPUImage里的实现。我简化了在android下vulkan与opengle 效果图(1080P处理)

因为摄像头开启自动曝光,画面变动时,亮度变化导致扣像在转动时如上。

源码地址vulkan_extratest

这个demo主要测试二点,一是测试ndk camera集成效果,二是本项目对接外部实现的vulkan层是否方便,用于以后移植GPUImage里的实现。

我简化了在android下vulkan与opengles纹理互通里的处理,没有vulkan窗口与交换链这些逻辑,只用到vulkan compute shader计算管线得到结果然后交换给opengl里的纹理。

NDK Camera集成

主要参考 NdkCamera Sample的实现,然后封装成满足Aoce定义设备接口。

说下遇到的坑。

aimAGE_FORMAT_YUV_420_888 可能是YUV420P,也可能是NV12,需要在aimageReader_ImageListener里拿到image通过aimage_getPlanePixelStrIDe里的UV的plan是否为1来判断是否为YUV420P,或者看data[u]-data[y]=1来看是否为NV12.具体可以看getVIDeoFrame的实现。

aimageReader_new里的maxImages比较重要,简单理解为预先申请几张图,这个值越大,显示越平滑。
aimageReader_new如果不开线程,则图像处理加到这个线程里,导致读取图像变慢。打开线程处理,
我用的Redmi K10 pro,可以读40003000,在aimageReader_ImageListener回调不做特殊处理,如下错误。
首先是Unable to acquire a lockedBuffer, very likely clIEnt trIEs to lock more than.
可以看到,运行四次后报的,就是我设的maxImages,通过比对代码逻辑,应该是aimageReader_new读四次后,我还没处理完一桢,没有aimage_delete,也就读不了数据了.
然后检查 aimageReader_acquireNextimage 这个状态,不对不读,然后继续引发读取不可用内存问题,分析应该是处理数据的乱序线程aimage_delete可能释放别的处理线程上的image,然后处理图像线程上加上lock_guard(mutex),不会引发问题,但是会导致每maxImages卡一下,可以理解,读的线程快,处理的慢,后面想了下,直接让thread.join,图片读取很大时慢(比不开线程要快很多,4000
3000快二倍多,平均45ms),但是平滑的,暂时先这样,后面看能不能直接拿aimage的harderbuffer去处理,让处理速度追上读取速度。

Chroma Key

如上所说,项目对接外部实现的vulkan层是否方便,在这重新生成一个模块aoce_vulkan_extra,在这我选择UE4 Matting里的逻辑来测试,因为这个逻辑非常简单,也算让我对手机的性能有个初步的了解。

首先把相关逻辑整理下,UE4上有相关节点,看下实现整理成glsl compute shader实现。

#version 450// https://www.unrealengine.com/en-US/tech-blog/setting-up-a-chroma-key-material-in-ue4layout (local_size_x = 16, local_size_y = 16) in;// gl_WorkGroupSizelayout (binding = 0, rgba8) uniform Readonly image2D inTex;layout (binding = 1, rgba8) uniform image2D outTex;layout (std140, binding = 2) uniform UBO {    // 0.2 控制亮度的强度系数    float lumaMask;    float chromacolorX;    float chromacolorY;    float chromacolorZ;    // 用环境光补受蓝绿幕影响的像素(简单理解扣像结果要放入的环境光的颜色)    float ambIEntScale;    float ambIEntcolorX;      float ambIEntcolorY;     float ambIEntcolorZ;       // 0.4    float AlphaCutoffMin;    // 0.5    float AlphaCutoffMax;    float AlphaExponent;    // 0.8    float despillCuttofMax;    float despillExponent;} ubo;const float PI = 3.1415926;vec3 extractcolor(vec3 color,float lumaMask){       float luma = dot(color,vec3(1.0f));    // 亮度指数    float colorMask = exp(-luma*2*PI/lumaMask);    // color*(1-colorMask)+color*luma    color = mix(color,vec3(luma),colorMask);    // 生成基于亮度的饱和度图        return color / dot(color,vec3(2.0));}voID main(){    ivec2 uv = ivec2(gl_GlobalinvocationID.xy);    ivec2 size = imageSize(outTex);        if(uv.x >= size.x || uv.y >= size.y){        return;    }        vec3 inputcolor = imageLoad(inTex,uv).rgb;       vec3 chromacolor = vec3(ubo.chromacolorX,ubo.chromacolorY,ubo.chromacolorZ);    vec3 ambIEntcolor = vec3(ubo.ambIEntcolorX,ubo.ambIEntcolorY,ubo.ambIEntcolorZ);    vec3 color1 = extractcolor(chromacolor,ubo.lumaMask);    vec3 color2 = extractcolor(inputcolor,ubo.lumaMask);    vec3 subcolor = color1 - color2;    float diffSize = length(subcolor);    float minClamp = diffSize-ubo.AlphaCutoffMin;    float dist = ubo.AlphaCutoffMax - ubo.AlphaCutoffMin;    // 扣像Alpha    float Alpha= clamp(pow(max(minClamp/dist,0),ubo.AlphaExponent),0.0,1.0);    // 受扣像背景影响的颜色Alpha    float inputClamp = ubo.despillCuttofMax - ubo.AlphaCutoffMin;    float despillAlpha = 1.0f- clamp(pow(max(minClamp/inputClamp,0),ubo.despillExponent),0.0,1.0);    // 亮度系数    vec3 lumaFactor = vec3(0.3f,0.59f,0.11f);        // 添加环境光收益    vec3 dcolor = inputcolor*lumaFactor*ambIEntcolor*ubo.ambIEntScale*despillAlpha;    // 去除扣像背景    dcolor -= inputcolor*chromacolor*despillAlpha;    dcolor += inputcolor;        // 为了显示查看效果,后面屏蔽    dcolor = inputcolor*Alpha + ambIEntcolor*(1.0-Alpha);    imageStore(outTex,uv,vec4(dcolor,Alpha)); }

这里面代码最后倒数第二句实现混合背景时去掉,在这只是为了显示查看效果。

然后引用aoce_vulkan里给的基类VkLayer,根据接口完成本身具体实现,相关VkChromKeyLayer的实现可以说是非常简单,至少我认为达到我想要的方便。

还是一样,先说遇到的坑,

开始在glsl中的UBO,我特意把一个float,vec3放一起,想当然的认为是按照vec4排列,这里注意,vec3不管前后接什么,大部分结构定义下,都至少占vec4,所以后面为了和C++结构align一样,全部用float.

层启用/不启用会导致整个运算graph重置,一般情况下,运算线程与结果输出线程不在一起,在重置时,运算线程相关资源会重新生成,而此时输出线程还在使用相关资源就会导致device lost错误,在这使用VkEvent用来表示是否在资源重置中。

然后就是与androID UI层对接,androID的UI没怎么用过,丑也就先这样吧。

总结

以上是内存溢出为你收集整理的在Android用vulkan完成蓝绿幕扣像全部内容,希望文章能够帮你解决在Android用vulkan完成蓝绿幕扣像所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1045048.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-24
下一篇 2022-05-24

发表评论

登录后才能评论

评论列表(0条)

保存