使用javascript进行图像编辑

时间:2011-10-10 20:11:38

标签: javascript html5 canvas

在创建我的html5游戏引擎时,我已经能够做一些不错的事情并获得一些很酷的功能。在制作游戏的合同中,我被要求查看是否可以从精灵图像中删除背景颜色。我看到了这个因素,因为我们可以在pngs上使用jpgs并减小图像的大小。

我有什么方法可以使用纯JavaScript进行此操作吗?我希望能够在不使用canvas元素的情况下做到这一点,因此它可以更快,但如果我必须这样做。

如果我必须这样做,我还有另一个问题,我不希望canvas对象显示我使用的,我可以使用带有document.createElement的canvas对象而不将其应用于文档吗?这将是很好的,因为它不必呈现到网页。如果不是,我想我可以将画布对象移到视图左侧。

最后你认为预处理图像的好方法是将它们发送到服务器cgi脚本并让它返回一个json像素数组吗?

5 个答案:

答案 0 :(得分:3)

这是Floodfill算法的功能,它从已经在画布上绘制的图像中删除了背景。

在下面的代码中,canvas是HTML5 canvas元素,上下文是canvas.getContext(“2d”)。您可以更改colorRange的值并尝试使用不同颜色的函数。函数的最后一行

 imageElement.src=canvas.toDataURL("image/png");

是在img标签内显示图像。所以你需要在你的页面上有一个img和一个画布。如果您不想在img元素中显示图像,只需删除最后一行。

// Remove backgroud without ajax call, can be used in non IE browsers.
function RemoveBackground(){

    var startR,startG,startB;
    var canvasData;

    var canvasWidth=canvas.width;
    var canvasHeight=canvas.height;
    canvasData=mainContext.getImageData(0,0,canvasWidth,canvasHeight);
    startR = canvasData.data[0];
    startG = canvasData.data[1];
    startB = canvasData.data[2];
    if(startR==0&& startG==0 && startR==0) return;
    var pixelStack = [[0, 0]];  
    while(pixelStack.length)
    {
      var newPos, x, y, pixelPos, reachLeft, reachRight;
      newPos = pixelStack.pop();
      x = newPos[0];
      y = newPos[1];

      pixelPos = (y*canvasWidth + x) * 4;
      while(y-- >= 0 && matchStartColor(pixelPos,canvasData,startR,startG,startB)){
        pixelPos -= canvasWidth * 4;
    }
      pixelPos += canvasWidth * 4;
      ++y;
      reachLeft = false;
      reachRight = false;
      while(y++ < canvasHeight-1 && matchStartColor(pixelPos,canvasData,startR,startG,startB))
      {
        colorPixel(pixelPos,canvasData);
        if(x > 0)
        {
          if(matchStartColor(pixelPos-4,canvasData,startR,startG,startB))
          {
            if(!reachLeft){
              pixelStack.push([x - 1, y]);
              reachLeft = true;
            }
          }
          else if(reachLeft)
          {
            reachLeft = false;
          }
        }

        if(x < canvasWidth-1)
        {
          if(matchStartColor(pixelPos+4,canvasData,startR,startG,startB))
          {
            if(!reachRight)
            {
              pixelStack.push([x + 1, y]);
              reachRight = true;
            }
          }
          else if(reachRight)
          {
            reachRight = false;
          }
        }   
        pixelPos += canvasWidth * 4;
      }
    }
    context.putImageData(canvasData, 0, 0);
    imageElement.src=canvas.toDataURL("image/png");

}

// Helper function for remove background color.
function matchStartColor(pixelPos,canvasData,startR,startG,startB)
{
  var r = canvasData.data[pixelPos];    
  var g = canvasData.data[pixelPos+1];  
  var b = canvasData.data[pixelPos+2];
  var colorRange=8;

  return ((r >= startR-colorRange && r<=startR+colorRange) 
        &&( g >= startG-colorRange && g<=startG+colorRange)
        &&( b >= startB-colorRange && b<= startB+colorRange));
}
// Helper function for remove background color.
function colorPixel(pixelPos,canvasData)
{
  canvasData.data[pixelPos] = 255;
  canvasData.data[pixelPos+1] = 255;
  canvasData.data[pixelPos+2] = 255;
}

答案 1 :(得分:2)

删除没有波动边框的背景并不是一项简单的任务,即使是在图像编辑程序中也是如此。至少,你必须实现某种抗锯齿效果。

此外,操纵压缩成有损格式的图像并不是一个好主意。

PNG压缩在JPG上对于更简单的图像具有优越性(连续填充相同颜色和某些类型的渐变)。 JPG仅适用于以不可预测的方式混合大量不同颜色的异构图像。喜欢照片。我想,哪一个人不会期待游戏精灵。而且 - JPG是一种lossy格式。

至于Canvas元素,它根本就是doesn't have to be added到DOM树。

使给定颜色透明的最天真的算法是这样的:绘制图像,获取其像素数据,迭代数据并将每个像素颜色与给定颜色进行比较。如果匹配,则将alpha设置为0.

您需要的Canvas API方法:

它的简单部分有点棘手的是CanvasPixelArray。要检查此类数组中的每个像素,您可以执行以下操作:

for (var i = 0; i < pixelAr.length; i += 4) {
    var r = pixelAr[i];
    var g = pixelAr[i + 1];
    var b = pixelAr[i + 2];
    var alpha = pixelAr[i + 3];
}

答案 2 :(得分:1)

我个人不会走这条路。 JPEG图像被压缩,这意味着无论您定义为背景颜色在压缩文件中可能会稍微改变(即,您将获得经典的JPEG伪像)。此外,除非您为背景颜色定义范围,否则您将无法支持部分透明度,这反过来会使编辑更加复杂。在我看来,文件大小和性能/质量之间的权衡在这里是值得的。

话虽如此,您可以从图像访问像素数据的唯一方法是首先将其放在画布上。正如您所提到的,您可以在内存中使用画布屏幕,而无需将其附加到文档中。

如果我正确理解了您的上一个问题,则无法使用服务器端的canvas元素。要处理服务器上的像素数据,您必须使用类似PHP的图像库。

如果所有这些都不支持您只使用PNG图像,这里有一些示例代码可以从加载的图像中删除指定的背景颜色:

$(document).ready(function() {
    var matte_color = [0, 255, 0, 255]; // rgba: [0, 255];

    // Handles the loaded image element by erasing the matte color
    // and appending the transformed image to the document.
    function handleLoadedImage() {
        eraseMatte(); // Eliminate the matte.

        // Append the canvas element to the document where it is needed.
        document.getElementById("parent_container").appendChild(canvas);
    }

    // Given the matte color defined at the top, clear all pixels to 100% transparency
    // within the loaded sprite.
    function eraseMatte() {
        canvas.width = sprite.width;
        canvas.height = sprite.height;
        context.drawImage(sprite, 0, 0); // Draw the image on the canvas so we can read the pixels.

        // Get the pixel data from the canvas.
        var image_data = context.getImageData(0, 0, sprite.width, sprite.height);
        var data = image_data.data; // Obtaining a direct handle is a huge performance boost.
        var end = sprite.width * sprite.height * 4; // W x H x RGBA

        // Loop over each pixel from the image and clear matte pixels as needed.
        for (var i = 0; i < end; i += 4) {
            if (data[i] == matte_color[0] && data[i + 1] == matte_color[1] &&
                data[i + 2] == matte_color[2] && data[i + 3] == matte_color[3]) { // Color match.
                data[i] = data[i + 1] = data[i + 2] = data[i + 3] = 0; // Set pixel to transparent.
            }
        }

        // Put the transformed image data back on the canvas.
        context.putImageData(image_data, 0, 0);
    }

    var canvas = document.createElement("canvas");
    var context = canvas.getContext("2d");
    var sprite = new Image();
    sprite.onload = handleLoadedImage;
    sprite.src = "sprite.jpg";
});

答案 3 :(得分:0)

你可以使用画布来做,不知道没有它是否可行。 实现您要执行的操作的简单方法是在画布的上下文中使用getImageData

imgData = myCanvasContext.getImageData(x1, y1, x2, y2);

x1,y1,x2,y2是您想要获取数据的区域的共存,用于整个使用0, 0, width, height图像。 getImageData会返回ImageData,其中包含一个包含每个像素的rgba值的数组。这些值将按如下顺序排序: http://i.stack.imgur.com/tdHNJ.png

您可以操纵数组imgData.data[index],编辑数值,然后编辑图像。

这是一篇关于使用canvas在html5上编辑图像的好文章: http://beej.us/blog/2010/02/html5s-canvas-part-ii-pixel-manipulation/


要显示您正在执行的操作,只需使用css命令创建画布display:none;


  

(...)如果我可以从精灵图像中删除背景颜色。我看到了这个因为我们可以在pngs(...)

上使用jpgs

我真的建议你不要这样做。图像的jpg压缩可以使图像编辑非常困难。删除jpg图像的背景并不容易,并且图像上的边框数量变得越来越难。我不认为你会节省的尺寸将弥补从jpg图像中删除背景的艰苦工作。

答案 4 :(得分:0)

不完全相同,但你可以做到这一点。我可以给你一个开头 - 结账这个jsFiddle。我使用FabricJS构建了这个编辑器。

var canvas = new fabric.Canvas('c');
var imgInstance = new fabric.Image(imgElement); 
canvas.add(imgInstance);//initialize the Canvas with the image