资源描述
Pixel Bender工具包概述
开始
编写Pixel Bender滤镜
本章讨论Pixel Bender语言编写的特殊功能。
一个内核部分
在Pixel Bender图像处理的基本单位是内核。每个像素机核心语言程序定义了一个内核,由一个单一的字符串,包含语言版本的内核语句中指定的元素。
<languageVersion : 1.0;> ß所需要的语言版本的元素
kernel name
<
kernel metadata pairs ß在尖括号中的元数据部分
>
{
kernel members ß变量和函数包括在这对花括号内
}
内核语句包含一个名字,一组元数据的方括号中,描述了内核,和一组成员的花括号括起来的定义的过滤操作
内核的数据元素
每一个内核定义之前需要的语言版本的元素。这个声明和元数据部分(可以修改)被提供时自动创建在Pixel Bender工具包IDE的一个新的过滤器。
元数据部分提供命名空间,内核版本,和其他的识别和描述信息。这是特别重要的是当你收集的几个内核的图形来执行更复杂的操作。比如说呢。
<
namespace : "Tutorial";
vendor : "Adobe";
version : 1;
description: "My Filter";
>
命名空间,供应商,和版本值是必需的;说明是可选的。
1. 供应商的公司或个人谁写的筛选器名称。
2.他是一个版本的整数版本号应该开始在1增加了新的内核版本。这允许你产生一个错误修正或改进性能的滤波器的新版本,但仍让老版本
3. 命名空间是一个公司或作者进一步分离过滤器。例如,Adobe可能对PS图象处理软件高斯模糊滤镜不同版本和之后的效果,和使用的命名空间中的领域产品的名称来区分它们。
kernel GaussianBlur
<
namespace : "Photoshop";
vendor : "Adobe Systems";
version : 1;
>
{
// ... 高斯模糊滤镜所使用的PS图象处理软件
}
kernel GaussianBlur
<
namespace : "After Effects";
vendor : "Adobe Systems";
version : 1;
>
{
// ... 高斯模糊滤镜所使用的后效应
}
命名空间中的值与其他过滤标识符的组合来确定实际的命名空间,因此不需要全局唯一的。
在这一章简短,只有内核名称和构件截面显示;尝试的例子,你可以粘贴到内核内核构件截面定义包含一个语言版本的声明和元数据段。
核心成员
一个内核定义像C++中的类的成员变量和成员函数,与核心成员的部分包含一系列的声明,和一组函数定义。每一个内核必须提供至少evaluatepixel()功能和类型的像素的至少一个输出参数。
最简单的Pixel Bender 程序由一个内核,返回一个坚实的色彩无处不在:
kernel FillWithBlack
< ... >
{
output pixel4 dst;
region generated()
{
return everywhere();
}
void evaluatePixel()
{
dst = pixel4(0,0,1,0);
}
}
这个内核产生一个输出图像的四通道(红,绿,蓝,α),如通过申报输出pixel4 dst的定义。因为一个内核的所有像素的图像的每个像素执行输出,输出参数定义了一个完整的图。
Pixel Bender是强类型语言。除了标准的数字(标量)类型,它定义了一组用于像素,1,2,3的图像向量类型,或4的成员,或通道。作为一个完整的清单和Pixel Bender数据类型的描述,看看Pixel Bender参考。
参数和变量
在申明定义函数前,你可以指定参数,这是通过内核程序和固定的值,依赖变量,使用evaluateDependents()函数,它们仍然是只读,见本教程36页
一个核可以采取任意数量的任意类型的参数。参数被传递到Pixel Bender运行系统,和它们的值在所有像素的常数,就象“均匀”用在三维着色语言变量。
应用程序的内核运行提供一个用户界面中,用户可以设置的参数值。例如,它可以显示一个对话框,当过滤器被调用。在开发过程中,Pixel Bender工具包提供这个用户界面。
此示例将一个参数的fillwithblack内核,它定义了一个用来填充黑色以外的其他颜色:
kernel FillWithColor
< ... >
{
parameter pixel4 color;
output pixel4 dst;
region generated()
{
return everywhere();
}
void evaluatePixel()
{
dst = color;
}
}
记住,所有Pixel Bender程序必须指定Pixel Bender核心语言的版本
他们所写的,使用languageversion声明。试试这个代码,粘贴到一个内核
程序包含必要的基础设施。
参数的数据类型提供了一个线索,什么样的控制主机的应用,适当的参数设置值。你通常添加约束,有助于参数值甚至更多。例如,这个参数定义允许用户界面显示的最小值和最大值
允许的值,以及初始默认值
parameter pixel4 color
<
minValue: float4(0.0,0.0,0.0,0.0);
maxValue: float4(1.0,1.0,1.0,1.0);
defaultValue: float4(0.0,0.0,0.0,1.0);
>;
Pixel Bender坐标系统
Pixel Bender有一个单一的世界坐标系统(有时被称为世界空间)。世界坐标系是方形的,均匀的(各向同性,笛卡尔,和正交),和无限的。X轴增加向右,Y轴向下增加
输入和输出图像的起源是对齐到世界坐标系的原点。
在Pixel Bender模型,图像没有大小。相反,每个图像被认为是定义,在离散的像素无限平面坐标。Pixel Bender运行时引擎执行内核决定需要存储和操作上的像素缓冲区大小。只有个别Pixel Bender有一个尺寸。
这Pixel Bender坐标模型对滤波器设计的影响。例如,它是不可能写的一个反映在其“中心图像像素机内核,“因为中心是未定义的。而不是,该中心的坐标必须明确表示通过它们作为内核参数(参见“通过在坐标”在30页)。
没有坐标转换是可能的;一个是Transformed by being明确形象resampled to the输出网格这可能很严厉的限制,但它怎样到原子图像处理在实践操作必须执行更复杂的推理关于坐标系统,如连接和重新排序顺序执行的转换- must be of at a higher level,before内核执行
访问的像素坐标
一个核心是并行执行的所有像素的输出图像,具有完全相同的参数值或每个像素。在每一像素的变化是当前输出像素坐标唯一。
访问当前坐标值,使用内置的功能outcoord()。这个函数返回一个值float2型(两个浮动向量),给出了(x,y)的输出像素的中心坐标被evaluatepixel()功能的当前调用评估。输出电流协调各不相同的图像的像素,但不变的任何一个打电话的寿命期间evaluatepixel()。
如果我们假设像素的平方(它们不一定;看到“非正方形像素”在33页)的outcoord()函数返回这些值为4×3像素的图像输出:
下面的示例是如何演示outcoord()函数,用它来产生输出图像中的图案。它也说明了矢量型float2使用和向量函数length()。Pixel Bender核心语言包括一组丰富的载体的类型和操作的详细信息,参见;Pixel Bender参考。
这个内核提供一个(非抗锯齿)以原点为中心的实心圆,使用颜色和半径比参数:
<languageVersion : 1.0;>
kernel t1
< namespace : "test.t";
vendor : "my";
version : 1;
>
{
parameter float radius
<
minValue: 0.0;
maxValue: 600.0;
defaultValue: 100.0;
>;
parameter pixel4 color
<
minValue: float4(0.0,0.0,0.0,0.0);
maxValue: float4(1.0,1.0,1.0,1.0);
defaultValue: float4(0.0,0.0,0.0,1.0);
>;
input image4 src;
output pixel4 dst;
void evaluatePixel() {
float2 coord_for_this_pixel = outCoord();
float cur_radius = length(coord_for_this_pixel);
if (cur_radius < radius)
{
dst = color;
}
else
{
dst = sampleNearest(src, coord_for_this_pixel);
}
}
}
通过在坐标
这个内核呈现圆一个指定的大小和颜色,而是围绕圈在原点,你通过一个水平和垂直的圆的圆心坐标。
<languageVersion : 1.0;>
kernel t1
< namespace : "test.t";
vendor : "my";
version : 1;
>
{
parameter float2 center
<
minValue: float2(0);
maxValue: float2(500);
defaultValue: float2(180);
>;
parameter float radius
<
minValue: 0.0;
maxValue: 100.0;
defaultValue: 30.0;
>;
parameter pixel4 color
<
minValue: float4(0.0,0.0,0.0,0.0);
maxValue: float4(1.0,1.0,1.0,1.0);
defaultValue: float4(0.0,0.0,0.0,1.0);
>;
input image4 src;
output pixel4 dst;
void evaluatePixel()
{
// where are we relative to the center of the circle
float2 coord_for_this_pixel = outCoord() - center;
float cur_radius = length(coord_for_this_pixel);
if (cur_radius < radius) {
dst = color;
}
else
{
dst = sampleNearest(src, coord_for_this_pixel + center);
}
}
}
输入图像和采样
一个没有图像输入的内核被称为源或发电机。一个实际的例子是一个程序纹理的常规发电机。然而,大多数内核需要一个或多个图像输入。
你可以声明一个或多个输入图像到一个内核访问,然后通过图像像素值和像素坐标的内置采样功能:
sampleNearest()
返回该像素的中点为最近的给定的坐标值
sampleLinear()
双线性插值四像素相邻的给定的协调执行
简单的可能的内核采样叶片图像不变:
kernel Identity
< ... >
{
input image4 source;
output pixel4 dst;
void evaluatePixel()
{
dst = sampleNearest( source, outCoord() );
}
}
你能提供任何坐标采样功能;然而,如果你的样本图像定义的区域外的一点,该函数返回透明黑(0,0,0,0)。所有的采样函数返回完全相同的信道数的像素值为通过图像。
非正方形像素
当你认为像素广场,一个图像的像素网格总线与潜在的世界坐标空间:
然而,事实上,像素不总是广场。例如,作用通常涉及视频图像具有非正方形像素后。如果是1.1 x 1像素高,像素网格不与世界线空间:
尽管该像素的大小已更改,所有的坐标都仍然在世界坐标系统。例如,返回的值为4×3像素的图像outcoord()现在的这个样子:
在内核中,所有的坐标都在世界坐标空间。这包括:
1、 outcoord()函数的返回值
2、 坐标传递到采样功能
3、 该地区通过从区域功能,还包括needed(),changed(),generated(),和dod()
任何参数传递给内核,测量距离或区域(如一个模糊半径)也应该被指定在世界坐标系。
一个核可以假设,输出图像的像素大小将作为第一输入图像的像素尺寸相同。除此之外,然而,它不能选择或制作的像素大小的假设任何输入或输出图像。所有的输入图像和输出图像的像素大小可以不同。发现任何输入或输出图像的像素大小,使用pixelsize()和pixelaspectratio()功能。
1、内置函数pixelsize()返回像素的大小对于一个给定的输入或输出图像。结果是
测量世界的像素坐标大小。
2、内置函数pixelaspectratio()获取像素宽度除以像素高度为一个给定的
图像
pixelAspectRatio( i ) == pixelSize( i ).x / pixelSize( i ).y
一个正方形的像素具有1的纵横比。
快讯:在pixelsize()和pixelaspectratio()功能的Flash Player可用;然而
Flash播放器都采用1×1像素。
如果有可能,在你的图像的像素不是正方形的,你必须以像素大小为
帐户来保证精确的采样结果。要选择相邻的像素,例如,得到的像素大小
输入图像和用它来修改你的坐标,通过采样函数。
例如,该核平均每个像素与它的直接的左和右的邻居,产生一个轻微的
水平模糊:
kernel HorizontalAverage
< ... >
{
input image4 source;
output pixel4 result;
void evaluatePixel()
{
float2 coord = outCoord();
float2 hOffset = float2(pixelSize(source).x, 0.0);
pixel4 left = sampleNearest(source, coord - hOffset);
pixel4 center= sampleNearest(source, coord);
pixel4 right = sampleNearest(source, coord + hOffset);
result = (left + center + right)/3.0;
}
}
这个例子使用evaluatedependents()变半径,已经提供了世界坐标转换为半径测量在适当大小的像素:
<languageVersion : 1.0;>
kernel t1
< namespace : "test.t";
vendor : "my";
version : 1;
>
{
input image4 src;
output pixel4 dst;
parameter float radius
<
minValue:0.0;
maxValue:100.0;
defaultValue:1.0;
>;
dependent int intRadius;
dependent float weights[ 100 ];
void evaluateDependents() {
float w = 1.0 / float( radius );
float s = w / float( radius );
intRadius = int( ceil( radius / pixelSize(src).x ) );
weights[ 0 ] = w;
for( int i = 1; i <= intRadius; ++i ) {
w -= s;
weights[ i ] = w;
}
}
void evaluatePixel()
{
dst = sampleNearest( src, outCoord() ) * weights[ 0 ];
for( int i = 1; i <= intRadius; ++i )
{
float x = float( i ) * pixelSize( src ).x;
dst += sampleNearest( src, outCoord() + float2( x, 0 ) ) * weights[ i ];
dst += sampleNearest( src, outCoord() + float2( -x, 0 ) ) * weights[ i ];
}
}
}
多输入图像
一个核可以采取任何数量的输入图像,每一个都可以有不同的通道数。的
下面的内核乘以一个单通道磨砂一四图像通道
kernel MatteRGBA
< ... >
{
input image4 source;
input image1 matte;
output pixel4 result;
void evaluatePixel()
{
pixel4 in_pixel = sampleNearest( source, outCoord() );
pixel1 matte_value = sampleNearest( matte, outCoord() );
result = in_pixel * matte_value;
}
}
使用相关的值
快讯:依赖值不可用时,像素被用Flash播放器。考虑一个程序生成的查找表的像素的权重的卷积运算
<languageVersion : 1.0;>
kernel t1
< namespace : "test.t";
vendor : "my";
version : 1;
>
{
input image4 source;
output pixel4 result;
void evaluatePixel()
{
const float sigma = 2.0;
float c = 1.0 / ( sqrt(2.0 * 3.1415926535 ) * sigma );
float ec = 2.0 * sigma * sigma;
float weight0 = exp( -( 0.0 * 0.0 ) / ec ) * c;
float weight1 = exp( -( 1.0 * 1.0 ) / ec ) * c;
float weight2 = exp( -( 2.0 * 2.0 ) / ec ) * c;
float weight3 = exp( -( 3.0 * 3.0 ) / ec ) * c;
float weight4 = exp( -( 4.0 * 4.0 ) / ec ) * c;
float4 acc = float4( 0.0 );
acc += sampleNearest( source, outCoord() ) * weight0;
acc += sampleNearest( source, outCoord() + float2( 1.0, 0.0 ) ) * weight1;
acc += sampleNearest( source, outCoord() + float2( -1.0, 0.0 ) ) * weight1;
acc += sampleNearest( source, outCoord() + float2( 2.0, 0.0 ) ) * weight2;
acc += sampleNearest( source, outCoord() + float2( -2.0, 0.0 ) ) * weight2;
acc += sampleNearest( source, outCoord() + float2( 3.0, 0.0 ) ) * weight3;
acc += sampleNearest( source, outCoord() + float2( -3.0, 0.0 ) ) * weight3;
acc += sampleNearest( source, outCoord() + float2( 4.0, 0.0 ) ) * weight4;
acc += sampleNearest( source, outCoord() + float2( -4.0, 0.0 ) ) * weight4;
result = acc;
}
}
这个代码的工作但有一个问题:查找表的每一个像素的再生,失败一个查找表的目的。我们要预先计算这些值一次,然后运用他们所有的像素。你可以使用内核参数值传递,但你需要外部的代码来计算的值。
为了保持计算内核中执行,但只有一次,你可以声明表一组相关的成员变量。然后使用evaluatedependents()函数来计算变量的值。为初始化值依赖于身体内的
evaluatedependents(),这是运行一次,在任何像素的处理。然后这些值只读,并且为evaluatepixel()执行在所有像素常数。这里有完整的高斯模糊内核,改为使用相关的变量
提示和技巧
这里有一些事情要注意您的内核代码设计
未定义的结果
一些内置操作符和函数产生不确定的结果,在某些条件下,如除数为零,或一个负数的平方根。未定义的结果会导致不可预知的行为。内核完全可能停止运行,这可能生成黑色或白色像素的像素。它可能会产生不同的运行不同的结果,也可能产生不同的结果在运行时的CPU,GPU或Flash播放器。它可能出现在一个工作版本,然后在下一个版本失败。你不能让你的内核在一个不确定的方式使用运算符或函数。例如,如果你有一个内核,并分,你必须考虑到除数为零的可能性。确保你的代码的检测条件和采取适当的行动
不精确的浮点运算
浮点运算的CPU执行可能会略有不同的结果来进行在GPU,或在Flash Player中,由于不同的优化和舍入。这可能会导致问题当比较平等或不平等的浮点值。你可以避免这些问题
更换这样的代码:
if( f1 == f2 )
{
// Do something
}
with code like this:
if( abs( f1 - f2 ) < epsilon )
{
// Do something
}
超出范围的输出像素值
像素值通常被认为是在范围0到1。然而,它是可能的,以一套价值
输出像素是在此范围之外。
1、 在GPU运行时,所有的输出像素值的0至1的范围内夹。
2、 在CPU运行时,输出像素值不夹
建议你写的内核,同样在所有的执行环境工作。为了确保这一,如果你的内核可能产生超出范围的值,你应该夹他们期望的范围内,在你的自己的代码。
数组的大小
有些老年人对数组大小的限制,他们可以处理。注意,如果你写一个内核使用
数组,它可能无法运行在所有的GPU。
支持功能
快讯:支持的功能是不可用时,使用Flash Player Pixel Bender。
1、一个核可以包含区域函数的定义,其中有预定义的名字和签名,并提供像素机运行时引擎有多少空间的帮助信息从这一核心计算输出图像的像素分配建筑。有关详细信息,参见4章,“工作区”
2、内核也可以定义附加任意的支持功能,它可以被称为只有从evaluatepixel()或evaluatedependents()。
区域功能的例子
<languageVersion : 1.0;>
kernel RotateAndComposite
<
namespace : "Tutorial";
vendor : "Adobe";
version : 1;
>
{
parameter float theta; // 旋转角度
parameter float2 center // 旋转中心
<
minValue: float2(0);
maxValue: float2(1000);
defaultValue: float2(200);
>;
dependent float3x3 back_xform; // rotation matrix (from
dependent float3x3 fwd_xform; // and inverse
input image4 foreground;
input image4 background;
output pixel4 result;
//计算变换矩阵及其逆矩阵(inverse)
void evaluateDependents()
{
// translate center to origin
float3x3 translate = float3x3(
1, 0, 0,
0, 1, 0,
-center.x, -center.y, 1 );
// 旋转
float3x3 rotate = float3x3(
cos(theta), sin(theta), 0,
-sin(theta), cos(theta), 0, 0, 0, 1 );
// 合成得到完整的FWD变换
fwd_xform = -translate*rotate*translate;
// 得到逆矩阵
rotate[0][1] = -rotate[0][1];
rotate[1][0] = -rotate[1][0];
// 合成得到完整的反变换
back_xform = translate*rotate*-translate;
}
// 主要职能的转变outcoord找到前景像素
void evaluatePixel()
{
// 背景坐标是目的地的坐标
float2 bg_coord = outCoord();
// 坐标转换outcoord前景
// 注意转换至float3,然后滴W与调酒
float2 fg_coord = (back_xform*float3(bg_coord.x, bg_coord.y, 1)).xy;
// Alpha混合前景从背景
pixel4 bg_pixel = sampleNearest(background, bg_coord);
pixel4 fg_pixel = sampleLinear(foreground, fg_coord);
result = mix(bg_pixel, fg_pixel, fg_pixel.a);
}
// 需要的功能是不同的取决于输入
region needed(region output_region,imageRef input_index )
{
region result;
if( input_index == background )
{
// 背景是未转化的,所以只是路过
result = output_region;
}
else
{
// 变换输出区为前景空间
result = transform( back_xform, output_region );
}
return result;
}
// 改变的功能是需要的功能而不同
// transform sense
region changed(region input_region, imageRef input_index )
{
region result;
if( input_index == background )
{
result = input_region;
}
else
{
result = transform( fwd_xform, input_region );
}
return result;
}
}
一个支持函数的例子
你可以定义附加支持的功能在一个内核减少代码重复。在这示例代码来计算高斯模糊滤镜高斯重量转移到一个支持功能
<languageVersion : 1.0;>
kernel GaussianBlur
<
namespace : "Tutorial";
vendor : "Adobe";
version : 1;
>
{
dependent float weight0;
dependent float weight1;
dependent float weight2;
dependent float weight3;
dependent float weight4;
input image4 source;
output pixel4 result;
float calculateWeight( float r, float sigma )
{
float c = 1.0 / ( sqrt( 2.0 * 3.1415926535 ) * sigma );
float ec = 2.0 * sigma * sigma;
return exp( -( r * r ) / ec ) * c;
}
void evaluateDependents()
{
const float sigma = 2.0;
weight0 = calculateWeight( 0.0, sigma );
weight1 = calculateWeight( 1.0, sigma );
weight2 = calculateWeight( 2.0, sigma );
weight3 = calculateWeight( 3.0, sigma );
weight4 = calculateWeight( 4.0, sigma );
}
void evaluatePixel()
{
float4 acc = float4( 0.0 );
acc += sampleNearest( source, outCoord() ) * weight0;
acc += sampleNearest( source, outCoord() + float2( 1.0, 0.0 ) ) * weight1;
acc += sampleNearest( source, outCoord() + float2( -1.0, 0.0 ) ) * weight1;
acc += sampleNearest( source, outCoord() + float2( 2.0, 0.0 ) ) * weight2;
acc += sampleNearest( source, outCoord() + float2( -2.0, 0.0 ) ) * w
展开阅读全文