使用 Javascript 将油画/素描效果应用于照片 [英] Apply a Oil Paint/Sketch effect to a photo using Javascript

查看:43
本文介绍了使用 Javascript 将油画/素描效果应用于照片的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用 javascript 模拟从照片开始的人体绘图效果.

我一直在搜索几个进行图像处理的 js 库(主要是在画布上).但似乎没有人尝试过我正在寻找的东西.

我不认为用 javascript 实现这样的效果是不可能的.所以我想知道为什么我找不到任何已经完成的东西.

在原生方面,有几种替代 photoshop 来实现这种效果,如 App Store 中的几个应用所示:

  • 并将其调整为 JS 和 canvas.

    现场演示

    CodePen Demo,带有控制效果的控件

    它的工作原理是对中心像素周围的所有像素求平均值,然后将该平均值乘以所需的强度级别,然后除以 255.然后增加与强度级别相关的 r/g/b.然后检查像素邻居中哪个强度级别最常见,并将该强度级别分配给目标像素.

    edit 对它进行了更多的工作并重写了很多,获得了一些非常巨大的性能提升,现在可以很好地处理尺寸合适的图像.

    var canvas = document.getElementById("canvas"),ctx = canvas.getContext("2d"),img = 新图片();img.addEventListener('load', function () {canvas.width = this.width;canvas.height = this.height;ctx.drawImage(this, 0, 0, canvas.width, canvas.height);oilPaintEffect(canvas, 4, 55);});img.crossOrigin = "匿名";img.src = https://fbcdn-sphotos-h-a.akamaihd.net/hphotos-ak-xpa1/v/t1.0-9/1379992_10202357787410559_1075078295_n.jpg?oh=5b001e9848796dd942f47a0b2f3df6af&oe=542F3FEF&__gda__=1412145968_4dbb7f75b385770ecc3f4b88105cb0f8";函数 oilPaintEffect(画布,半径,强度){var width = canvas.width,高度 = 画布高度,imgData = ctx.getImageData(0, 0, width, height),pixData = imgData.data,destCanvas = document.createElement("canvas"),dCtx = destCanvas.getContext("2d"),pixelIntensityCount = [];destCanvas.width = 宽度;destCanvas.height = 高度;//出于演示目的,删除它以修改原始画布document.body.appendChild(destCanvas);var destImageData = dCtx.createImageData(width, height),destPixData = destImageData.data,强度LUT = [],rgbLUT = [];for (var y = 0; y < height; y++) {强度LUT[y] = [];rgbLUT[y] = [];for (var x = 0; x <宽度; x++) {var idx = (y * 宽度 + x) * 4,r = pixData[idx],g = pixData[idx + 1],b = pixData[idx + 2],avg = (r + g + b)/3;强度LUT[y][x] = Math.round((平均*强度)/255);rgbLUT[y][x] = {r:r,克:克,乙:乙};}}对于 (y = 0; y <高度; y++) {for (x = 0; x <宽度; x++) {pixelIntensityCount = [];//查找半径内最近像素的强度.for (var yy = -radius; yy <= 半径; yy++) {for (var xx = -radius; xx <= 半径; xx++) {如果 (y + yy > 0 && y + yy < height && x + xx > 0 && x + xx < 宽度) {变量强度值 = 强度LUT[y + yy][x + xx];如果(!pixelIntensityCount[intensityVal]){pixelIntensityCount[intensityVal] = {价值:1,r: rgbLUT[y + yy][x + xx].r,g: rgbLUT[y + yy][x + xx].g,b: rgbLUT[y + yy][x + xx].b}} 别的 {pixelIntensityCount[intensityVal].val++;pixelIntensityCount[intensityVal].r += rgbLUT[y + yy][x + xx].r;pixelIntensityCount[intensityVal].g += rgbLUT[y + yy][x + xx].g;pixelIntensityCount[intensityVal].b += rgbLUT[y + yy][x + xx].b;}}}}pixelIntensityCount.sort(function (a, b) {返回 b.val - a.val;});var curMax = pixelIntensityCount[0].val,dIdx = (y * 宽度 + x) * 4;destPixData[dIdx] = ~~ (pixelIntensityCount[0].r/curMax);destPixData[dIdx + 1] = ~~ (pixelIntensityCount[0].g/curMax);destPixData[dIdx + 2] = ~~ (pixelIntensityCount[0].b/curMax);destPixData[dIdx + 3] = 255;}}//将此更改为 ctx 以将数据放在原始画布上dCtx.putImageData(destImageData, 0, 0);}

    I want to simulate an human drawing effect starting from a photo, using javascript.

    I've been searching trough several js libraries that do image manipulation (mostly on canvas). But seems that no one even attempted what I'm looking for.

    I don't think it's impossible to achieve such effects with javascript. So I wonder why I can't find anything already done.

    On the native side there are several alternatives to photoshop to achieve such effects, as seen in several apps in the App Store:

    Here's other examples of possible result (from Artist's Touch App):

    解决方案

    Alright so I found a great explanation of the algorithm used here and adapted it to JS and canvas.

    Live Demo

    CodePen Demo with controls to mess with the effect

    How it works is you average all the pixels around your center pixel, then you multiply that average by the intensity level you want, you then divide it by 255. Then you increment the r/g/b's related to the intensity level. Then you check which intensity level was most common among the pixels neighbors, and assign the target pixel that intensity level.

    edit worked on it a bit more and rewrote a lot of it, gained some really huge performance gains, works with decent sized images now pretty well.

    var canvas = document.getElementById("canvas"),
        ctx = canvas.getContext("2d"),
        img = new Image();
    
    img.addEventListener('load', function () {
        canvas.width = this.width;
        canvas.height = this.height;
        ctx.drawImage(this, 0, 0, canvas.width, canvas.height);
        oilPaintEffect(canvas, 4, 55);
    });
    
    img.crossOrigin = "Anonymous";
    img.src = "https://fbcdn-sphotos-h-a.akamaihd.net/hphotos-ak-xpa1/v/t1.0-9/1379992_10202357787410559_1075078295_n.jpg?oh=5b001e9848796dd942f47a0b2f3df6af&oe=542F3FEF&__gda__=1412145968_4dbb7f75b385770ecc3f4b88105cb0f8";
    
    function oilPaintEffect(canvas, radius, intensity) {
        var width = canvas.width,
            height = canvas.height,
            imgData = ctx.getImageData(0, 0, width, height),
            pixData = imgData.data,
            destCanvas = document.createElement("canvas"),
            dCtx = destCanvas.getContext("2d"),
            pixelIntensityCount = [];
    
        destCanvas.width = width;
        destCanvas.height = height;
    
        // for demo purposes, remove this to modify the original canvas
        document.body.appendChild(destCanvas);
    
        var destImageData = dCtx.createImageData(width, height),
            destPixData = destImageData.data,
            intensityLUT = [],
            rgbLUT = [];
    
        for (var y = 0; y < height; y++) {
            intensityLUT[y] = [];
            rgbLUT[y] = [];
            for (var x = 0; x < width; x++) {
                var idx = (y * width + x) * 4,
                    r = pixData[idx],
                    g = pixData[idx + 1],
                    b = pixData[idx + 2],
                    avg = (r + g + b) / 3;
    
                intensityLUT[y][x] = Math.round((avg * intensity) / 255);
                rgbLUT[y][x] = {
                    r: r,
                    g: g,
                    b: b
                };
            }
        }
    
    
        for (y = 0; y < height; y++) {
            for (x = 0; x < width; x++) {
    
                pixelIntensityCount = [];
    
                // Find intensities of nearest pixels within radius.
                for (var yy = -radius; yy <= radius; yy++) {
                    for (var xx = -radius; xx <= radius; xx++) {
                        if (y + yy > 0 && y + yy < height && x + xx > 0 && x + xx < width) {
                            var intensityVal = intensityLUT[y + yy][x + xx];
    
                            if (!pixelIntensityCount[intensityVal]) {
                                pixelIntensityCount[intensityVal] = {
                                    val: 1,
                                    r: rgbLUT[y + yy][x + xx].r,
                                    g: rgbLUT[y + yy][x + xx].g,
                                    b: rgbLUT[y + yy][x + xx].b
                                }
                            } else {
                                pixelIntensityCount[intensityVal].val++;
                                pixelIntensityCount[intensityVal].r += rgbLUT[y + yy][x + xx].r;
                                pixelIntensityCount[intensityVal].g += rgbLUT[y + yy][x + xx].g;
                                pixelIntensityCount[intensityVal].b += rgbLUT[y + yy][x + xx].b;
                            }
                        }
                    }
                }
    
                pixelIntensityCount.sort(function (a, b) {
                    return b.val - a.val;
                });
    
                var curMax = pixelIntensityCount[0].val,
                    dIdx = (y * width + x) * 4;
    
                destPixData[dIdx] = ~~ (pixelIntensityCount[0].r / curMax);
                destPixData[dIdx + 1] = ~~ (pixelIntensityCount[0].g / curMax);
                destPixData[dIdx + 2] = ~~ (pixelIntensityCount[0].b / curMax);
                destPixData[dIdx + 3] = 255;
            }
        }
    
        // change this to ctx to instead put the data on the original canvas
        dCtx.putImageData(destImageData, 0, 0);
    }
    

    这篇关于使用 Javascript 将油画/素描效果应用于照片的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆