Joe Maller: FXScript Reference: Building Joe's Noise

How I used FXScript to build my Noise filter for Final Cut Pro.

Visit the New FXScript Reference and Joe's Filters sites. These pages will be phased out soon and may already be out of date.

Joe's Filters
FXScript Reference

This page describes the FXScript concepts behind Joe's Noise. If you're looking for the page about how to use the filter, click here.

Overview

Joe's Noise adds several features to a conventional noise filter. Because noise is generated into an image buffer, it can be processed before compositing back into the image. Also, this filter saves processing time by creating the noise image buffer at specific size needed for later scaling.

Tiny Little Image Buffers

Instead of creating and processing another full-sized image buffer for noise, the script creates a smaller image buffer with the specific dimensions needed to fill the full-sized image after scaling. I sort of lucked into the math on this one even though it's simple when broken down and examined (x/y * y/x = 1).

The custom-sized image buffer scalebuffer is created using the inverse proportion of the noiseScale value. The values w and h refer to the width and height, as determined by the dimensionsOf function:

dimensionsOf(Dest, w, h);
image scalebuffer[w * 100/noiseScale][h * 100/noiseScale * aspectof(dest)];

Because the noiseScale value can't be less than 100, scalebuffer will always be sized equal to or smaller than the overall clip dimensions. The height is multiplied by the aspect ratio for correct sizing.

Random as I wanna be

FXScript's noise pattern can be the same on every frame, or it can change on every frame. This is controlled by the RandomSeed function. When RandomSeed is set to zero, a new random set is generated for each frame, when it's set to anything other than zero, the same random set is generated for each frame. RandomSeed is also explained in Building Joe's Pixelizer.

randomseed(ScatterDance)

Since checkboxes return either zero or one, feeding that value directly into RandomSeed determines whether the noise will be fixed or change on every frame.

Make Some Noise

The amount of noise visible in the final image is determined by the alpha channel values fed into the RandomNoise function. The function has two inputs for each image channel, a minimum and a maximum. Even though the values are labeled this way, they are interchangeable and simply define a range in either direction.

At values below 100, the script uses the value as a percentage of 255 and reduces the range of visible noise by creating an alpha channel between black and varying shades of gray. At values above 100, the minimum is increased and the alpha channel is created with values between varying shades of gray and white. A noise setting of 0 would result in a black alpha channel (completely transparent), a setting of 400 would result in a white alpha channel (completely opaque). Alpha channel results are illustrated below.

Alpha channel of the noise image buffer at various amounts:

Noise: 0.1 Noise: 50 Noise: 100 Noise: 250 Noise: 400

The effect is similar to adjusting output levels on the alpha channel. Below 100, output white is reduced; above 100, output black is increased.

To control these values, I used two variables for the alpha minimum and maximum. amin and amax. To define these, the script uses something called a trinary operator. This looks crazy at first, but is really just a simple way of combining a value assignment with a conditional if-then-else statement as explained below. Trinary operators are also discussed on the FXScript Functions page.

amin = noiseamount < 100 ? noiseamount/100 * 255 : 255;
Amax = noiseamount > 100 ? (noiseamount-100)/300 * 255 : 0;

These two statements set different values depending on noiseamount. Above 100, only Amax varies; below 100, only amin varies.

The variable amin will be assigned one of the two possible values after the question mark and separated by the colon. The statement before the question mark, noiseamount < 100, is the conditional argument that will determines which of the following two assignments will be used. If the statement is true, amin is assigned noiseamount/100 * 255, if the statement is false, amin is assigned 255.

The definition statement for Amax works the same way. Because the range of values are between 100 and 400, the script first subtracts 100 from noiseamount and then divides that value by 300 to arrive at a decimal value between zero and one. The equation(noiseamount-100)/300 * 255 returns a value from 0-255 based on the value of noiseamount.

These values are then plugged into the RandomNoise function along with the r, g and b slider values to fill scalebuffer with ready-to-composite noise.

RandomNoise(scalebuffer, amin, Amax, 0, r, 0, g, 0, b, 1);

Spanana Blit (?)

I'm not clear on what sort of data is stored in a four item point array, but these are needed to store information about the image buffers for the BlitRect function. (I plan to create a four item point arrays value reporting filter at some point.)

Two 4 value point arrays are declared at the beginning of the script:

point noiseRect[4], noiseRect2[4];

These values are later filled with the BoundsOf function based on dest and scalebuffer. Remember that scalebuffer is sized based on the scaling value selected:

boundsof(dest, noiseRect);
boundsof(scalebuffer, noiseRect2);

The BlitRect function is used to copy one section of pixels into a second section of pixels. The good part of this is that the two sections do not have to match in size, the function will stretch or shrink the pixels to fit the target dimensions.

The two variables noiseRect and noiseRect2 contain the dimensions of the target image buffer and the noise image buffer. By plugging these values into the BlitRect function, the noise image buffer is scaled up to fit the target image buffer.

BlitRect(scalebuffer, noiseRect2 , xbuffer, noiseRect);

Unfortunately, there appears to be no way to control the interpolation used when scaling.

Fade to Black (and white)

The script doesn't use the monochrome option in RandomNoise because this option sends the same data to all four channels. The problem with this method is that matching the alpha channel to the color channels masks out all the white areas of noise. True monochrome noise lightens as much as darkens, so a different means of acquiring monochrome noise was devised.

All monochrome images send the same data to all three color channels. Knowing this, a simple ChannelCopy function is used to duplicate the Red channel into Green and Blue. The alpha channel is unaffected and continues to affect the image as indicated earlier on this page.

Channelcopy(xbuffer, xbuffer, kalpha, kred, kred, kred);

Conclusion

The script ends with my standard set of composite controls, combining the noise contained within xbuffer with the original image in src1.

The complete FXScript source code for Joe's Noise is included with the paid version of Joe's Filters.

Buy Joe's Filters Download the Free Trial

 
page last modified: October 23, 2017
Copyright © 1996-2003 Joe Maller