Question

How to create Photoshop Stamp filter analog in any pixel shading language? (I need at least filter algorithm...)

Was it helpful?

Solution

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top