Редактирование изображений с помощью JavaScript - PullRequest
1 голос
/ 11 октября 2011

При создании своего игрового движка html5 я смог сделать несколько приятных вещей и получить некоторые интересные функции.В контракте на создание игры меня попросили посмотреть, смогу ли я убрать цвет фона со спрайтовых изображений.И я вижу плюсы в этом, поскольку мы могли бы использовать jpgs вместо pngs и уменьшить размер изображений.

Есть ли способ сделать это с чистым JavaScript?Я хотел бы иметь возможность сделать это без использования элемента canvas, чтобы он мог быть быстрее, но если мне нужно, то все в порядке.

Если мне нужно сделать это, у меня есть другой вопрос, я не хочу, чтобы объект canvas показывал, что я использую, могу ли я использовать объект canvas с document.createElement, не применяя его к документу?Это было бы хорошо, так как это не должно быть отображено на веб-странице.Если нет, я думаю, я могу просто переместить объект холста влево из поля зрения.

И, наконец, считаете ли вы, что хорошим способом предварительной обработки изображений является отправка их на серверный скрипт cgi и возвращение массива пикселей json?

Ответы [ 5 ]

3 голосов
/ 12 октября 2011

Вот функция для алгоритма заливки, она удаляет фон с изображения, которое уже нарисовано на холсте.

В следующем коде 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;
}
2 голосов
/ 11 октября 2011

Удаление фона без прерывистых границ не является тривиальной задачей, даже вручную в программах для редактирования изображений. Вам, по крайней мере, придется реализовать какое-то сглаживание.

Кроме того, не рекомендуется манипулировать изображением, сжатым в формат с потерями.

Сжатие

PNG превосходит (с точки зрения размера) значение JPG на простых изображениях с непрерывной заливкой одного цвета и определенными типами градиентов. JPG хорош только для разнородных изображений с множеством разных цветов, смешанных непредсказуемым образом. Нравится фото. Я думаю, чего бы не ожидать в игровых спрайтах. И снова - JPG - это формат с потерями .

Что касается элемента Canvas, его вообще не нужно добавлять в дерево DOM.

Самый наивный алгоритм, позволяющий сделать данный цвет прозрачным, был бы таким: нарисовать изображение, получить его данные пикселей, перебрать данные и сравнить каждый цвет пикселей с вашим заданным цветом. Если это соответствует, установите альфа на 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];
}
1 голос
/ 11 октября 2011

Лично я бы не пошел по этому пути. Изображения 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";
});
0 голосов
/ 18 мая 2014

Не совсем то же самое, но вы можете достичь этого.Я могу дать вам представление об этом - проверить это jsFiddle .Я построил этот редактор, используя FabricJS .

var canvas = new fabric.Canvas('c');
var imgInstance = new fabric.Image(imgElement); 
canvas.add(imgInstance);//initialize the Canvas with the image
0 голосов
/ 11 октября 2011

Вы можете сделать это, используя холст, не знаю, возможно ли это без него.Самый простой способ добиться того, что вы пытаетесь сделать, это использовать 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], редактируя его значения и, следовательно, редактируя изображение.

Вот хорошая статья о редактированииизображения на html5 с холстом: http://beej.us/blog/2010/02/html5s-canvas-part-ii-pixel-manipulation/


Чтобы не показывать, что вы делаете, просто создайте холст с помощью команды css display:none;


(...) если я могу удалить цвет фона из изображений спрайта.И я вижу плюсы в этом, поскольку мы можем использовать jpgs вместо pngs (...)

Я очень рекомендую вам не делать этого.Сжатие JPG изображения может сделать редактирование изображения очень трудным.Удаление фона изображения в формате jpg не легко, и это становится сложнее с количеством границ на изображении.Я не думаю, что размер, который вы сэкономите, компенсирует тяжелую работу по удалению фона из изображения jpg.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...