aoce_vulkan_extra把GPUImage里从P到Z的大部分滤镜用vulkan的ComputeShader实现了,也就是最后一部分的移植,整个过程相对前面来说比较简单,大部分我都是直接复制以前的实现改改就行了,还是列一些说明.

PerlinNoise 柏林燥声生成一张静态图

柏林燥声的原理网上很多讲解的,用于生成平滑的图案,其实可以稍微改下,如加个如时间戳,就可变成一张平滑的动态图.

PinchDistortion 收缩,凹面镜

实现类似BulgeDistortion(鱼眼效果),都是针对UV变形,BulgeDistortion在设定圆心处UV缩小,意思在原圆心处相同的地方取更近的像素,这样就导致图像看起来向外扩张.

而PinchDistortion在设定圆心处,周边UV放大,意思在圆心处相同的地方取更远的UV像素,就导致图像看起来向里缩.

PoissonBlend 泊松融合

图像融合之泊松编辑(Poisson Editing)(2)

GPUImage的实现应该是一种简化实现,后面会移植按上面原理求泊松重建方程的实现.

现暂时按照GPUImage里来实现,他的实现比较简单,唯一麻烦的,需要第一张输入图与输出图Ping-pong来回处理,不同于前面savefamelayer的实现,他需要在一桢中来回循环读写,刚开始想的是如何把当前索引当做UBO传入shader,但是在一桢中把一个UBO更新多次,首先不知道能否这样实现,就算能,这种实现并不好,在这引入Vulkan里的PushConstant的概念,能完美解决这个问题,首先vkCmdPushConstants也是插入到CommandBuffer中,在提交给GPU的时候,也是确定的数值,这样每桢多次循环就可以用PushConstant来表明对应次数.感觉在渲染一批次多模型时用来指定索引也不错啊.

void VkPoissonBlendLayer::onCommand() {
// ping-pong 单数次
paramet.iterationNum = paramet.iterationNum / 2 * 2 + 1;
for (int32_t i = 0; i < paramet.iterationNum; i++) {
int32_t pong = i % 2;
inTexs[0]->addBarrier(cmd, VK_IMAGE_LAYOUT_GENERAL,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_ACCESS_SHADER_READ_BIT);
inTexs[1]->addBarrier(
cmd, VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
pong == 0 ? VK_ACCESS_SHADER_READ_BIT : VK_ACCESS_SHADER_WRITE_BIT);
outTexs[0]->addBarrier(
cmd, VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
pong == 0 ? VK_ACCESS_SHADER_WRITE_BIT : VK_ACCESS_SHADER_READ_BIT);
vkCmdPushConstants(cmd, layout->pipelineLayout,
VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(int32_t),
&pong);
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE,
computerPipeline);
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE,
layout->pipelineLayout, 0, 1,
layout->descSets[0].data(), 0, 0);
vkCmdDispatch(cmd, sizeX, sizeY, 1);
}
}
#version 450

layout (local_size_x = 16, local_size_y = 16) in;// gl_WorkGroupSize
layout (binding = 0, rgba8) uniform image2D inTex;
layout (binding = 1, rgba8) uniform readonly image2D inTex1;
layout (binding = 2, rgba8) uniform image2D outTex; layout (std140, binding = 3) uniform UBO {
float percent;
} ubo; layout(push_constant) uniform pushBlock {
int pong;
} constBlock; const ivec2 centerT[4] = {
ivec2(1,0),
ivec2(-1,0),
ivec2(0,1),
ivec2(0,-1)
}; void main(){
ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
ivec2 size = imageSize(inTex);
if(uv.x >= size.x || uv.y >= size.y){
return;
}
vec4 center = vec4(0);
if(constBlock.pong == 0){
center = imageLoad(inTex,uv);
}else{
center = imageLoad(outTex,uv);
}
vec4 center1 = imageLoad(inTex1,uv);
vec4 sum = vec4(0);
vec4 sum1 = vec4(0);
for(int i = 0; i < 4; i++){
ivec2 cuv = uv + centerT[i];
cuv = max(ivec2(0),min(cuv,size));
if(constBlock.pong == 0){
sum += imageLoad(inTex,cuv);
}else{
sum += imageLoad(outTex,cuv);
}
sum1 += imageLoad(inTex1,cuv);
}
vec4 mean = sum / 4.0;
vec4 diff1 = center1 - sum1 /4.0;
vec4 grad = mean + diff1; vec4 result = vec4(mix(center.rgb,grad.rgb,center1.a * ubo.percent),center.a);
if(constBlock.pong == 0){
imageStore(outTex,uv,result);
}else{
imageStore(inTex,uv,result);
}
}

在N卡2070上,一次迭代在0.2-0.3ms之间.

这个后面在内部又加了一个节点,用来保存第一个输入,应该这个层会改变第一个输入的内容,而别的层可能还需要这个输入,所以添加一层保存第一层输入.

PrewittEdgeDetection

和SobelEdgeDetection没多大区别,算子不同,其实算子区别也不大,垂直与水平的正对方向上一个是1,一个是2,别的都没啥区别.

RGBDilation/RGBErosion

把原来的Dilation/Erosion的glsl文件添加下编译符,针对处理下,取第一个横向的代码贴出来.

#version 450

layout (local_size_x = 16, local_size_y = 16) in;// gl_WorkGroupSize

#if CHANNEL_RGBA
layout (binding = 0, rgba8) uniform readonly image2D inTex;
layout (binding = 1, rgba8) uniform image2D outTex;
#elif CHANNEL_R8
layout (binding = 0, r8) uniform readonly image2D inTex;
layout (binding = 1, r8) uniform image2D outTex;
#endif layout (binding = 2) uniform UBO {
int ksize;
} ubo; #if EROSION
#define OPERATE min
#if CHANNEL_RGBA
#define INIT_VUL vec4(1.0)
#elif CHANNEL_R8
#define INIT_VUL 1.0f
#endif
#endif #if DILATION
#define OPERATE max
#if CHANNEL_RGBA
#define INIT_VUL vec4(0.0)
#elif CHANNEL_R8
#define INIT_VUL 0.0f
#endif
#endif #if IS_SHARED
// 限定最大核为32 #if CHANNEL_RGBA
shared vec4 row_shared[16][16*3];
#elif CHANNEL_R8
shared float row_shared[16][16*3];
#endif void main(){
ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
ivec2 size = imageSize(inTex);
if(uv.x >= size.x || uv.y >= size.y){
return;
}
ivec2 locId = ivec2(gl_LocalInvocationID.xy);
for(int i = 0; i < 3; i++){
uint gIdx = max(0,min(uv.x+(i-1)*16,size.x-1));
#if CHANNEL_RGBA
row_shared[locId.y][locId.x + i*16] = imageLoad(inTex,ivec2(gIdx,uv.y));
#elif CHANNEL_R8
row_shared[locId.y][locId.x + i*16] = imageLoad(inTex,ivec2(gIdx,uv.y)).r;
#endif
}
memoryBarrierShared();
barrier();
#if CHANNEL_RGBA
vec4 result = INIT_VUL;
#elif CHANNEL_R8
float result = INIT_VUL;
#endif
for(int i =0; i < ubo.ksize; i++){
int ix = locId.x - ubo.ksize/2 + i;
#if CHANNEL_RGBA
vec4 fr = row_shared[locId.y][16 + ix];
result = OPERATE(fr,result);
#elif CHANNEL_R8
float fr = row_shared[locId.y][16 + ix];
result = OPERATE(fr,result);
#endif
}
imageStore(outTex, uv, vec4(result));
} #else void main(){
ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
ivec2 size = imageSize(inTex);
if(uv.x >= size.x || uv.y >= size.y){
return;
}
#if CHANNEL_RGBA
vec4 result = INIT_VUL;
#elif CHANNEL_R8
float result = INIT_VUL;
#endif
for(int i = 0; i< ubo.ksize; ++i){
int x = uv.x-ubo.ksize/2+i;
x = max(0,min(x,size.x-1));
#if CHANNEL_RGBA
vec4 r = imageLoad(inTex,ivec2(x,uv.y));
result = OPERATE(result,r);
#elif CHANNEL_R8
float r = imageLoad(inTex,ivec2(x,uv.y)).r;
result = OPERATE(result,r);
#endif
}
imageStore(outTex, uv, vec4(result));
} #endif

下面对应的Closing/Opening都不用改,加个参数表示是R8/RGBA8就行.

ShiTomasiFeatureDetection

和Noble角点检测一样,都和修改HarrisCornerDetection中的角点计算方式,别的流程一样.

SingleComponentGaussianBlur 单通道高斯模糊

在最开始设计GaussianBlur时,就支持R8/RGBA8/RGBA32F这几种,要添加也只需要修改glsl的编译符,添加对应逻辑就行.

SobelEdgeDetection/Sketch Sobel边缘检波/草图素描效果

SobelEdgeDetection利用Sobel算子计算边缘,而Sketch就是SobelEdgeDetection结果的反转.

下面的ThresholdEdgeDetection/ThresholdSketch在这二个基础上加了个Threshold用来确定结果是0还是1.

SmoothToon 高斯模糊后的卡通效果

Toon是卡通效果,而SmoothToon就是先对输入图像高斯模糊后,然后再应用卡通效果.

VoronoiConsumer 接收Voronoi映射,并使用该映射过滤传入的图像

二个输入,第二个输出需要长宽同为2^n的相同整数,根据第一张图中的UV,对应第二张图中的值,生成一个UV,然后取第一张图值,有点类似lookup映射.

ZoomBlur 将定向运动模糊应用于图像

实现大致同MotionBlur,取周边点的算法稍微不同.

中间没有移植的滤镜统计

  1. GPUImageParallelCoordinateLineTransform,同上篇文章里的LineGenerator,还没找到合适的使用GPGPU高效画线方法.

  2. GPUImageSolidColorGenerator,在vulkan里,完全可以用vkCmdClearColorImage替换,效率更高,封装实现方法就在基类VkLayer::clearColor里.

  3. GPUImageToneCurveFilter暂时不移植.看实现有点类似Lookup,但是需要根据传入的数据生成一张查找表.

  4. GPUImageTransformFilter GPUImage利用顶点着色器,可以进行更多视角转换,现aoce_vulkan模块里有上下,左右,以及90/270转换,这个后面再仔细考虑下如何完善.

归类

GPUImage里根据里分四类,在API导出头文件VkExtraExport.hpp里根据这四类重新排下,加下注释,如果更新参数是结构,相应结构添加下对应每个参数注释.

在其layer文件夹,对多个类进行合并,如几乎所有混合模式的类,实现都在glsl上,几乎不需要针对基类VkLayer做任何修改,所以都合并在VkBlendingModeLayer文件里,色彩调整/视觉效果也合并一些普通的类在对应的VkColorAdjustmentlayer/VkVisualEffectLayer中,而图像处理的类相对来说会复杂点,大部分都是分散到各个集合中,如其中的Dilation/Erosion/Closing/Opening合并到VkMorphLayer文件中,边缘检测的几个类实现在VkEdgeDetectionLayer中.

Vulkan移植GPUImage(五)从P到Z的滤镜的更多相关文章

  1. Vulkan移植GpuImage(四)从D到O的滤镜

    现把D到O的大部分滤镜用vulkan的ComputeShader实现了,列举其中一些有点特殊的说明. GaussianBlurPosition 指定区域高斯模糊 没有按照GPUImage里的方式实现, ...

  2. Vulkan移植GpuImage(三)从A到C的滤镜

    前面移植了几个比较复杂的效果后,算是确认了复杂滤镜不会对框架造成比较大的改动,开始从头移植,现已把A到C的所有滤镜用vulkan的ComputeShader实现了,讲一些其中实现的过程. Averag ...

  3. Vulkan移植GPUImage的安卓Demo展示

    演示Android apk下载 需要Android 8以上. 先看效果图,大约一百多种滤镜,有超过一半的滤镜有参数设置,其参数调整界面使用反射自动生成与绑定. 如下每种选择一些进行展示. 视觉效果 图 ...

  4. Vulkan移植GpuImage(一)高斯模糊与自适应阈值

    自适应阈值效果图 demo 这几天抽空看了下GpuImage的filter,移植了高斯模糊与自适应阈值的vulkan compute shader实现,一个是基本的图像处理,一个是组合基础图像处理聚合 ...

  5. Vulkan移植GpuImage(二)Harris角点检测与导向滤波

    Harris角点检测 UI还是用的上次扣像的,只有前后置可以用,别的没有效果,只看实现就好. 相应源码 在实现之前,我先重新整理编译glsl的生成工具,如Harris角点检测中间计算过程需要针对rgb ...

  6. 从0移植uboot(五) _实现串口输出

    串口作为一种非常简单的通信方式,才是嵌入式系统调试的王道,通过设置串口输出,我们可以将程序运行的情况直接通过串口线输出到屏幕上,对于这种异常重要的功能,uboot原生就提供了支持,但为此我们需要做一些 ...

  7. 基于tiny4412的Linux内核移植 -- SD卡驱动移植(五)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  8. LoRaWAN stack移植笔记(五)__调试1

    先废话一小段 在将LoRaWAN的程序移植的过程中,调试发现了很多的问题. 做好记录工作,防止以后再踩坑 移植使用的是LoRaMac-node库,使用的是STM32L151CBT6 MCU,需要要移植 ...

  9. (转) 从0移植uboot(五) _实现串口输出

    ref : https://www.cnblogs.com/xiaojiang1025/p/6500520.html 串口作为一种非常简单的通信方式,才是嵌入式系统调试的王道,通过设置串口输出,我们可 ...

随机推荐

  1. 一次VLAN标签引发的网络事件的处置

    一次VLAN标签引发的网络事件的处置 一.背景介绍 事件背景: HZ某分公司新装一套业务系统,通过一条专线和BJ总公司连通.分配给HZ公司的ip地址为:a.b.c.X,掩码24位,网关a.b.c.1. ...

  2. Powerful Number 筛学习笔记

    Powerful Number 筛学习笔记 用途 \(Powerful\ number\) 筛可以用来求出一类积性函数的前缀和,最快可以达到根号复杂度. 实现 \(Powerful\ number\) ...

  3. ASP.NET Core依赖注入初识与思考

    文章首发地址 一.前言 在上一篇中,我们讲述了什么是控制反转(IoC)以及通过哪些方式实现的.这其中,我们明白了,控制反转(IoC) 是一种软件设计的模式,指导我们设计出更优良,更具有松耦合的程序,而 ...

  4. 极简实用的Asp.NetCore模块化框架新增CMS模块

    简介 关于这个框架的背景,在前面我已经交代过了.不清楚的可以查看这个链接 极简实用的Asp.NetCore模块化框架决定免费开源了 在最近一段时间内,对这个框架新增了以下功能: 1.新增了CMS模块, ...

  5. HTML5-本地存储浅谈

    Web Storage是HTML5里面引入的一个类似于cookie的本地存储功能,可以用于客户端的本地存储 sessionStorage && localStorage session ...

  6. 常用Linux操作

    常用Linux操作 这里我使用的是Git进行的Linux操作,如果你有服务器.或者LInux系统可以直接尝试 首先保证自己电脑上成功安装好了Git,右键鼠标: 随便找一个文件夹(我这里使用的是桌面的G ...

  7. html+css写出响应式侧边导航栏

    html部分:先写用div画好六个导航的卡片,再利用css添加响应效果 <div class='card-holder'> <div class='card-wrapper'> ...

  8. sql注入之超详细sqlmap使用攻略

    0x00 前言 干过sql注入的小伙伴们一定听说过sqlmap的大名,但是面对一些特殊情况,sqlmap也不一定"好使",这里的"好使"并不是真正不好使的意思, ...

  9. Pyinstaller原理详解

    Pyinstaller原理详解 什么是Pyinstaller Pyinstaller可以把Python程序打包成exe文件,可以在没有Python的电脑上运行,主要用于生产用. Python.h! 在 ...

  10. 击鼓传花联想到了Java设计模式:责任链模式

    目录 应用场景 简单示例 责任链模式 定义 意图 主要解决问题 何时使用 优缺点 击鼓传花的故事 应用场景 http web请求处理,请求过来后将经过转码.解析.参数封装.鉴权等一系列的处理(责任), ...