How to create Photoshop Stamp filter analog in any pixel shading language? (I need at least filter algorithm...)
views:
270answers:
1
A:
I've look at the Stamp Filter in Photoshop and it looks like a Threshold and a strong Blur added together.
I've used the HorizontalGaussianBlur kernel found Pixel Bender Basics Article on Devnet and just added a cheap threshold. Here is how my kernel looks like:
<languageVersion : 1.0;>
kernel stamp
< namespace : "toMaterial";
vendor : "George Profenza";
version : 1;
description : "Attempt to simulate Photoshop Stamp Filter. Original blur code: http://www.adobe.com/devnet/flash/articles/pixel_bender_basics_05.html";
>
{
input image4 src;
output pixel4 dst;
parameter float threshold<
minValue: 0.0;
defaultValue:0.5;
maxValue:1.0;
>;
parameter int radius
<
minValue : 1;
maxValue : 6;
defaultValue : 6;
>;
void
evaluatePixel()
{
pixel4 center, band1, band2, band3, band4, band5, band6;
float2 pos = outCoord();
//Sample image in bands
if(radius > 5) {
pixel4 left = dst;
pixel4 right = dst;
left.x -= 6.0;
right.x += 6.0;
band6 = left+right;
}
if(radius > 4) {
pixel4 left = dst;
pixel4 right = dst;
left.x -= 5.0;
right.x += 5.0;
band5 = left+right;
}
if(radius > 3) {
pixel4 left = dst;
pixel4 right = dst;
left.x -= 4.0;
right.x += 4.0;
band4 = left+right;
}
if(radius > 2) {
pixel4 left = dst;
pixel4 right = dst;
left.x -= 3.0;
right.x += 3.0;
band3 = left+right;
}
if(radius > 1) {
pixel4 left = dst;
pixel4 right = dst;
left.x -= 2.0;
right.x += 2.0;
band2 = left+right;
}
pixel4 left = dst;
pixel4 right = dst;
left.x -= 1.0;
right.x += 1.0;
band1 = left+right;
dst = sampleNearest(src,pos);
//quick'n'dirty grayscale
dst.rgb = float3(dst.r + dst.g + dst.b) * 0.333333333;
//threshold
if(dst.r < threshold) dst.r = 0.0; else dst.r = 1.0;
if(dst.g < threshold) dst.g = 0.0; else dst.g = 1.0;
if(dst.b < threshold) dst.b = 0.0; else dst.b = 1.0;
center = dst;
//Apply weights and compute resulting pixel
if( radius == 6 )
{
dst = (band6 + (band5 * 12.0) + (band4 * 66.0) + (band3 * 220.0) + (band2 * 495.0) + (band1 * 792.0) + (center * 924.0)) * 0.000244140625;//4096.0;
}
if( radius == 5 )
{
dst = (band5 + (band4 * 10.0) + (band3 * 45.0) + (band2 * 120.0) + (band1 * 210.0) + (center * 252.0)) * 0.0009765625;//1024.0;
}
if( radius == 4 )
{
dst = (band4 + (band3 * 8.0) + (band2 * 28.0) + (band1 * 56.0) + (center * 70.0)) * 0.00390625;//256.0;
}
if( radius == 3 )
{
dst = (band3 + (band2 * 6.0) + (band1 * 15.0) + (center * 20.0)) * 0.015625;//64.0;
}
if( radius == 2 )
{
dst = (band2 + (band1 * 4.0) + (center * 6.0)) * 0.0625;//16.0
}
if( radius == 1 )
{
dst = (band1 + (center * 2.0)) * 0.25;//4.0
}
}
}
This is far from perfect, it is what I could hack quickly at this hour. This should give you some ideas though.
UPDATE:
Here is an updated version using proper grayscale and the Gaussian Blur code is mostly from the Pixel Bender Guide.
<languageVersion : 1.0;>
kernel Stamp
< namespace : "gp";
vendor : "George Profenza";
version : 1;
description : "Attempt to do a filter similar to Photoshop's Stamp Filter, blur code mostly from the guide: http://www.adobe.com/go/pixelbender_devguide";
>
{
input image4 source;
output pixel4 result;
parameter int blur<
minValue:1;
defaultValue:3;
maxValue:8;
>;
parameter float threshold<
minValue:0.0;
maxValue:1.0;
defaultValue:0.5;
>;
void
evaluatePixel()
{
//blur
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;
if(blur > 1) float weight2 = exp( -( 2.0 * 2.0 ) / ec ) * c;
if(blur > 2) float weight3 = exp( -( 3.0 * 3.0 ) / ec ) * c;
if(blur > 3) float weight4 = exp( -( 4.0 * 4.0 ) / ec ) * c;
if(blur > 4) float weight5 = exp( -( 5.0 * 5.0 ) / ec ) * c;
if(blur > 5) float weight6 = exp( -( 6.0 * 6.0 ) / ec ) * c;
if(blur > 6) float weight7 = exp( -( 7.0 * 7.0 ) / ec ) * c;
if(blur > 7) float weight8 = exp( -( 8.0 * 7.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;
if(blur > 1) {
acc += sampleNearest( source, outCoord() + float2( 2.0, 0.0 ) ) * weight2;
acc += sampleNearest( source, outCoord() + float2( -2.0, 0.0 ) ) * weight2;
}
if(blur > 2) {
acc += sampleNearest( source, outCoord() + float2( 3.0, 0.0 ) ) * weight3;
acc += sampleNearest( source, outCoord() + float2( -3.0, 0.0 ) ) * weight3;
}
if(blur > 3) {
acc += sampleNearest( source, outCoord() + float2( 4.0, 0.0 ) ) * weight4;
acc += sampleNearest( source, outCoord() + float2( -4.0, 0.0 ) ) * weight4;
}
if(blur > 4) {
acc += sampleNearest( source, outCoord() + float2( 5.0, 0.0 ) ) * weight5;
acc += sampleNearest( source, outCoord() + float2( -5.0, 0.0 ) ) * weight5;
}
if(blur > 5) {
acc += sampleNearest( source, outCoord() + float2( 6.0, 0.0 ) ) * weight6;
acc += sampleNearest( source, outCoord() + float2( -6.0, 0.0 ) ) * weight6;
}
if(blur > 6) {
acc += sampleNearest( source, outCoord() + float2( 7.0, 0.0 ) ) * weight7;
acc += sampleNearest( source, outCoord() + float2( -7.0, 0.0 ) ) * weight7;
}
if(blur > 7) {
acc += sampleNearest( source, outCoord() + float2( 8.0, 0.0 ) ) * weight8;
acc += sampleNearest( source, outCoord() + float2( -8.0, 0.0 ) ) * weight8;
}
//grayscale
float luma = 0.299 * acc.r + 0.587 * acc.g + 0.114 * acc.b;
acc = float4(luma,luma,luma,1.0);
//threshold
if(acc.r < threshold) acc.r = 0.0; else acc.r = 1.0;
if(acc.g < threshold) acc.g = 0.0; else acc.g = 1.0;
if(acc.b < threshold) acc.b = 0.0; else acc.b = 1.0;
result = acc;
}
}
Note: I applied the threshold after the blur, so the result is not exactly a Stamp Filter but close.
HTH, George
George Profenza
2010-02-27 02:35:49