Floyd–Steinberg dithering
Hi team, gosh this is one I’ve been slowly working on as I come to better grips with using blinkscript. Finally got something working so wanted to post it and I’ll get around to optimizing it for Nukepedia.
First what is Floyd–Steinberg dithering ?
Read the wikipedia page and the general page on dithering. But what it essentially boils down to is ‘an intentionally applied form of noise used to randomize quantization error, preventing large-scale patterns such as color banding in images’. especially helpful when we are reducing an images bit count. It can also be used as away of cheating a stylized pixelated look if thats your intention.
Comparing bit reduction. Notice the lack of information in the darks especially
WRITE UP COMING SOON for now heres the kernel
//Floyd-Steinburg Dithering //function to get the value stored at a specific spot in the array inline int index(int x, int y, int width){ return (y*width + x); } //function to quantize value inline float4 quantizeVal(float oldRed, float oldGreen, float oldBlue, int quantize){ float qRed = (round(quantize*oldRed)) /(float)quantize; float qGreen = (round(quantize*oldGreen)) /(float)quantize; float qBlue = (round(quantize*oldBlue)) /(float)quantize; return float4(qRed, qGreen, qBlue, 1.0); } //function to get the error that results from quantizing values inline float4 errorVal(float oldRed, float oldGreen, float oldBlue, int quantize){ float4 quantizefloat = quantizeVal(oldRed, oldGreen, oldBlue, quantize); float errR = oldRed - quantizefloat.x;; float errG = oldGreen - quantizefloat.y; float errB = oldBlue - quantizefloat.z; return float4(errR, errG, errB, 1.0); } kernel dithering : ImageComputationKernel<ePixelWise> { Image<eRead, eAccessRandom, eEdgeClamped> src; // the input image Image<eWrite, eAccessPoint> dst; // the output image // Theses parameter are made available to the user. param: int quantize; int clampMin; int clampMax; // These local variable are not exposed to the user. local: int indexInt; int invY; int2 resolution; float4 output; float4 colour; float4 errorfloat; float4 allPixels[9000000];//arrary need to be hard coded. if the image is larger than increase this values // In define(), parameters can be given labels and default values. void define() { } // The init() function is run before any calls to process(). // Local variables can be initialized here. void init() { resolution.x = src.bounds.width(); resolution.y = src.bounds.height(); } void process(int2 pos) { //initial setup populate array indexInt = index(pos.x,pos.y,resolution.x); float oldRed = clamp(src(pos.x,pos.y).x, clampMin, clampMax); float oldGreen = clamp(src(pos.x,pos.y).y, clampMin, clampMax); float oldBlue = clamp(src(pos.x,pos.y).z, clampMin, clampMax); //error diffusion if (pos.x - 1 > 0){ colour = allPixels[indexInt]; errorfloat = errorVal(src(pos.x,pos.y).x, src(pos.x,pos.y).y, src(pos.x,pos.y).z, quantize); indexInt = index(pos.x, pos.y, resolution.x); colour.x = colour.x + (errorfloat.x *7/16.0); colour.y = colour.y + (errorfloat.y *7/16.0); colour.z = colour.z + (errorfloat.z *7/16.0); allPixels[indexInt] = float4(colour.x, colour.y, colour.z, 1.0); } if (pos.x -1 >=0 && pos.y -1 >= 0){ colour = allPixels[indexInt]; errorfloat = errorVal(src(pos.x,pos.y).x, src(pos.x,pos.y).y, src(pos.x,pos.y).z, quantize); indexInt = index(pos.x, pos.y, resolution.x); colour.x = colour.x + (errorfloat.x *3/16.0); colour.y = colour.y + (errorfloat.y *3/16.0); colour.z = colour.z + (errorfloat.z *3/16.0); allPixels[indexInt] = float4(colour.x, colour.y, colour.z, 1.0); } if (pos.y -1 >= 0){ colour = allPixels[indexInt]; errorfloat = errorVal(src(pos.x,pos.y).x, src(pos.x,pos.y).y, src(pos.x,pos.y).z, quantize); indexInt = index(pos.x, pos.y, resolution.x); colour.x = colour.x + (errorfloat.x *5/16.0); colour.y = colour.y + (errorfloat.y *5/16.0); colour.z = colour.z + (errorfloat.z *5/16.0); allPixels[indexInt] = float4(colour.x, colour.y, colour.z, 1.0); } if (pos.x+1 < resolution.x && pos.y +1 < resolution.y){ colour = allPixels[indexInt]; errorfloat = errorVal(src(pos.x,pos.y).x, src(pos.x,pos.y).y, src(pos.x,pos.y).z, quantize); indexInt = index(pos.x, pos.y, resolution.x); colour.x = colour.x + (errorfloat.x *1/16.0); colour.y = colour.y + (errorfloat.y *1/16.0); colour.z = colour.z + (errorfloat.z *1/16.0); allPixels[indexInt] = float4(colour.x, colour.y, colour.z, 1.0); } output = allPixels[indexInt] + src(pos.x,pos.y); allPixels[indexInt] = quantizeVal(output.x, output.y, output.z, quantize); dst() = allPixels[indexInt] ; } };