Изменение размера изображения на холсте HTML5

282

Я пытаюсь создать уменьшенное изображение на стороне клиента, используя javascript и элемент canvas, но когда я сжимаю изображение, это выглядит ужасно. Похоже, что он был уменьшен в фотошопе с настройкой передискретизации на "Ближайший сосед" вместо Бикубица. Я знаю, что это возможно, чтобы это выглядело правильно, потому что этот сайт может сделать это просто отлично, используя холст. Я пробовал использовать тот же код, что и он, как показано на ссылке "[Source]", но все равно выглядит ужасно. Есть что-то, что мне не хватает, некоторые настройки, которые нужно установить или что-то еще?

РЕДАКТИРОВАТЬ:

Я пытаюсь изменить размер jpg. Я попытался изменить размер одного и того же jpg на связанном сайте и в фотошопе, и он выглядит отлично, когда сокращается.

Вот соответствующий код:

reader.onloadend = function(e)
{
    var img = new Image();
    var ctx = canvas.getContext("2d");
    var canvasCopy = document.createElement("canvas");
    var copyContext = canvasCopy.getContext("2d");

    img.onload = function()
    {
        var ratio = 1;

        if(img.width > maxWidth)
            ratio = maxWidth / img.width;
        else if(img.height > maxHeight)
            ratio = maxHeight / img.height;

        canvasCopy.width = img.width;
        canvasCopy.height = img.height;
        copyContext.drawImage(img, 0, 0);

        canvas.width = img.width * ratio;
        canvas.height = img.height * ratio;
        ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);
    };

    img.src = reader.result;
}

EDIT2:

Кажется, я ошибался, связанный сайт не делал ничего лучше работы по сокращению изображения. Я попробовал другие методы, и ни один из них не выглядел лучше. Это привело к следующим методам:

Photoshop:

Изображение 366

Холст:

Изображение 367

Изображение с рендерингом изображения: optimizeQuality set и масштабируется с шириной/высотой:

Изображение 368

Изображение с помощью рендеринга изображений: optimizeQuality set и масштабируется с помощью -moz-transform:

Изображение 369

Размер холста на пиксели:

Изображение 370

Я предполагаю, что это означает, что firefox не использует бикубическую выборку, как предполагалось. Мне просто нужно подождать, пока они их не добавят.

EDIT3:

Оригинальное изображение

  • 0
    Можете ли вы опубликовать код, который вы используете для изменения размера изображения?
  • 0
    Вы пытаетесь изменить размер изображения GIF или аналогичного изображения с ограниченной палитрой? Даже в фотошопе эти изображения не будут хорошо масштабироваться, если вы не конвертируете их в RGB.
Показать ещё 9 комментариев
Теги:
canvas
image-resizing

18 ответов

399
Лучший ответ

Итак, что вы делаете, если все браузеры (на самом деле, Chrome 5 дал мне неплохую) не даст вам достаточно хорошего качества повторной выборки? Вы сами их реализуете! О да, мы вступаем в новую эпоху Web 3.0, совместимые с HTML5 браузеры, супер оптимизированные JIT javascript-компиляторы, многоядерные (†) машины, с тоннами памяти, чего вы боитесь? Эй, там слово java в javascript, так что это должно гарантировать производительность, правильно? Вот, код генерации миниатюр:

// returns a function that calculates lanczos weight
function lanczosCreate(lobes) {
    return function(x) {
        if (x > lobes)
            return 0;
        x *= Math.PI;
        if (Math.abs(x) < 1e-16)
            return 1;
        var xx = x / lobes;
        return Math.sin(x) * Math.sin(xx) / x / xx;
    };
}

// elem: canvas element, img: image element, sx: scaled width, lobes: kernel radius
function thumbnailer(elem, img, sx, lobes) {
    this.canvas = elem;
    elem.width = img.width;
    elem.height = img.height;
    elem.style.display = "none";
    this.ctx = elem.getContext("2d");
    this.ctx.drawImage(img, 0, 0);
    this.img = img;
    this.src = this.ctx.getImageData(0, 0, img.width, img.height);
    this.dest = {
        width : sx,
        height : Math.round(img.height * sx / img.width),
    };
    this.dest.data = new Array(this.dest.width * this.dest.height * 3);
    this.lanczos = lanczosCreate(lobes);
    this.ratio = img.width / sx;
    this.rcp_ratio = 2 / this.ratio;
    this.range2 = Math.ceil(this.ratio * lobes / 2);
    this.cacheLanc = {};
    this.center = {};
    this.icenter = {};
    setTimeout(this.process1, 0, this, 0);
}

thumbnailer.prototype.process1 = function(self, u) {
    self.center.x = (u + 0.5) * self.ratio;
    self.icenter.x = Math.floor(self.center.x);
    for (var v = 0; v < self.dest.height; v++) {
        self.center.y = (v + 0.5) * self.ratio;
        self.icenter.y = Math.floor(self.center.y);
        var a, r, g, b;
        a = r = g = b = 0;
        for (var i = self.icenter.x - self.range2; i <= self.icenter.x + self.range2; i++) {
            if (i < 0 || i >= self.src.width)
                continue;
            var f_x = Math.floor(1000 * Math.abs(i - self.center.x));
            if (!self.cacheLanc[f_x])
                self.cacheLanc[f_x] = {};
            for (var j = self.icenter.y - self.range2; j <= self.icenter.y + self.range2; j++) {
                if (j < 0 || j >= self.src.height)
                    continue;
                var f_y = Math.floor(1000 * Math.abs(j - self.center.y));
                if (self.cacheLanc[f_x][f_y] == undefined)
                    self.cacheLanc[f_x][f_y] = self.lanczos(Math.sqrt(Math.pow(f_x * self.rcp_ratio, 2)
                            + Math.pow(f_y * self.rcp_ratio, 2)) / 1000);
                weight = self.cacheLanc[f_x][f_y];
                if (weight > 0) {
                    var idx = (j * self.src.width + i) * 4;
                    a += weight;
                    r += weight * self.src.data[idx];
                    g += weight * self.src.data[idx + 1];
                    b += weight * self.src.data[idx + 2];
                }
            }
        }
        var idx = (v * self.dest.width + u) * 3;
        self.dest.data[idx] = r / a;
        self.dest.data[idx + 1] = g / a;
        self.dest.data[idx + 2] = b / a;
    }

    if (++u < self.dest.width)
        setTimeout(self.process1, 0, self, u);
    else
        setTimeout(self.process2, 0, self);
};
thumbnailer.prototype.process2 = function(self) {
    self.canvas.width = self.dest.width;
    self.canvas.height = self.dest.height;
    self.ctx.drawImage(self.img, 0, 0, self.dest.width, self.dest.height);
    self.src = self.ctx.getImageData(0, 0, self.dest.width, self.dest.height);
    var idx, idx2;
    for (var i = 0; i < self.dest.width; i++) {
        for (var j = 0; j < self.dest.height; j++) {
            idx = (j * self.dest.width + i) * 3;
            idx2 = (j * self.dest.width + i) * 4;
            self.src.data[idx2] = self.dest.data[idx];
            self.src.data[idx2 + 1] = self.dest.data[idx + 1];
            self.src.data[idx2 + 2] = self.dest.data[idx + 2];
        }
    }
    self.ctx.putImageData(self.src, 0, 0);
    self.canvas.style.display = "block";
};

... с помощью которого вы можете создавать такие результаты!

Изображение 5028

так или иначе, вот "фиксированная" версия вашего примера:

img.onload = function() {
    var canvas = document.createElement("canvas");
    new thumbnailer(canvas, img, 188, 3); //this produces lanczos3
    // but feel free to raise it up to 8. Your client will appreciate
    // that the program makes full use of his machine.
    document.body.appendChild(canvas);
};

Теперь настало время вытащить ваши лучшие браузеры и посмотреть, какой из них, скорее всего, увеличит кровяное давление вашего клиента!

Умм, где мой тег сарказма?

(так как многие части кода основаны на Anrieff Gallery Generator, это также распространяется на GPL2? Я не знаю)

† на самом деле из-за ограничения javascript, многоядерность не поддерживается.

  • 2
    Я действительно пытался реализовать это сам, делая то же самое, что и вы, копируя код из редактора изображений с открытым исходным кодом. Так как я не смог найти надежную документацию по алгоритму, мне было трудно его оптимизировать. В конце концов, мой был немного медленным (потребовалось несколько секунд, чтобы изменить размер изображения). Когда я получу шанс, я попробую ваш и посмотрим, будет ли это быстрее. И я думаю, что теперь веб-разработчики делают возможным многоядерный JavaScript. Я собирался попытаться использовать их, чтобы ускорить его, но у меня были проблемы с выяснением, как превратить это в многопоточный алгоритм
  • 0
    Похоже, вы пропустили часть кода. lanczosCreate (), кажется, отсутствует. Не могли бы вы отредактировать свой пост, чтобы включить это, чтобы я мог проверить ваш код?
Показать ещё 18 комментариев
37

Быстрый алгоритм изменения размера/пересчета с использованием фильтра Hermite с JavaScript. Поддержка прозрачности, дает хорошее качество. Предварительный просмотр:

Изображение 5029

Обновить: версия 2.0 добавлена ​​в GitHub (быстрее, веб-рабочие + переносимые объекты). Наконец, я получил его!

Git: https://github.com/viliusle/Hermite-resize
Демо: http://viliusle.github.io/miniPaint/

/**
 * Hermite resize - fast image resize/resample using Hermite filter. 1 cpu version!
 * 
 * @param {HtmlElement} canvas
 * @param {int} width
 * @param {int} height
 * @param {boolean} resize_canvas if true, canvas will be resized. Optional.
 */
function resample_single(canvas, width, height, resize_canvas) {
    var width_source = canvas.width;
    var height_source = canvas.height;
    width = Math.round(width);
    height = Math.round(height);

    var ratio_w = width_source / width;
    var ratio_h = height_source / height;
    var ratio_w_half = Math.ceil(ratio_w / 2);
    var ratio_h_half = Math.ceil(ratio_h / 2);

    var ctx = canvas.getContext("2d");
    var img = ctx.getImageData(0, 0, width_source, height_source);
    var img2 = ctx.createImageData(width, height);
    var data = img.data;
    var data2 = img2.data;

    for (var j = 0; j < height; j++) {
        for (var i = 0; i < width; i++) {
            var x2 = (i + j * width) * 4;
            var weight = 0;
            var weights = 0;
            var weights_alpha = 0;
            var gx_r = 0;
            var gx_g = 0;
            var gx_b = 0;
            var gx_a = 0;
            var center_y = (j + 0.5) * ratio_h;
            var yy_start = Math.floor(j * ratio_h);
            var yy_stop = Math.ceil((j + 1) * ratio_h);
            for (var yy = yy_start; yy < yy_stop; yy++) {
                var dy = Math.abs(center_y - (yy + 0.5)) / ratio_h_half;
                var center_x = (i + 0.5) * ratio_w;
                var w0 = dy * dy; //pre-calc part of w
                var xx_start = Math.floor(i * ratio_w);
                var xx_stop = Math.ceil((i + 1) * ratio_w);
                for (var xx = xx_start; xx < xx_stop; xx++) {
                    var dx = Math.abs(center_x - (xx + 0.5)) / ratio_w_half;
                    var w = Math.sqrt(w0 + dx * dx);
                    if (w >= 1) {
                        //pixel too far
                        continue;
                    }
                    //hermite filter
                    weight = 2 * w * w * w - 3 * w * w + 1;
                    var pos_x = 4 * (xx + yy * width_source);
                    //alpha
                    gx_a += weight * data[pos_x + 3];
                    weights_alpha += weight;
                    //colors
                    if (data[pos_x + 3] < 255)
                        weight = weight * data[pos_x + 3] / 250;
                    gx_r += weight * data[pos_x];
                    gx_g += weight * data[pos_x + 1];
                    gx_b += weight * data[pos_x + 2];
                    weights += weight;
                }
            }
            data2[x2] = gx_r / weights;
            data2[x2 + 1] = gx_g / weights;
            data2[x2 + 2] = gx_b / weights;
            data2[x2 + 3] = gx_a / weights_alpha;
        }
    }
    //clear and resize canvas
    if (resize_canvas === true) {
        canvas.width = width;
        canvas.height = height;
    } else {
        ctx.clearRect(0, 0, width_source, height_source);
    }

    //draw
    ctx.putImageData(img2, 0, 0);
}
  • 0
    Может быть, вы можете включить ссылки на демо miniPaint и репозиторий Github?
  • 1
    Вы также поделитесь версией для веб-работников? Вероятно, из-за накладных расходов при настройке он медленнее для небольших изображений, но может быть полезен для больших исходных изображений.
Показать ещё 5 комментариев
21

Попробуйте pica - это очень оптимизированный размерный размер с выбираемыми алгоритмами. См. demo.

Например, исходное изображение из первого сообщения изменяется в 120 мс с помощью фильтра Ланцоса и окна 3 пикселя или 60 мс с фильтром Box и окном 0,5 пиксела. Для огромного размера изображения размером 5000х3000 пикселей размером 17 мб занимает ~ 1 с на рабочем столе и 3 с на мобильном устройстве.

Все принципы изменения размера были очень хорошо описаны в этой теме, а pica не добавляет ракетной науки. Но он очень хорошо оптимизирован для современных JIT-s и готов к использованию из коробки (через npm или bower). Кроме того, он использует веб-мастеров, когда они доступны, чтобы избежать зависания интерфейса.

Я также планирую добавить поддержку unsharp mask в ближайшее время, потому что это очень полезно после уменьшения масштаба.

14

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

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

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

$(document).on('ready', createImage);
$(window).on('resize', createImage);

var createImage = function(){
  var canvas = document.getElementById('myCanvas');
  canvas.width = window.innerWidth || $(window).width();
  canvas.height = window.innerHeight || $(window).height();
  var ctx = canvas.getContext('2d');
  img = new Image();
  img.addEventListener('load', function () {
    ctx.drawImage(this, 0, 0, w, h);
  });
  img.src = 'http://www.ruinvalor.com/Telanor/images/original.jpg';
};
html, body{
  height: 100%;
  width: 100%;
  margin: 0;
  padding: 0;
  background: #000;
}
canvas{
  position: absolute;
  left: 0;
  top: 0;
  z-index: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Canvas Resize</title>
  </head>
  <body>
    <canvas id="myCanvas"></canvas>
  </body>
</html>

Моя функция createImage вызывается один раз, когда документ загружается, и после этого он вызывается каждый раз, когда окно получает событие изменения размера.

Я тестировал его в Chrome 6 и Firefox 3.6, как на Mac. Эта "техника" ест процессор, как если бы это было мороженое летом, но это делает трюк.

9

Я применил некоторые алгоритмы для интерполяции изображений на html-хэш-массивах пикселей, которые могут быть полезны здесь:

https://web.archive.org/web/20170104190425/http://jsperf.com:80/pixel-interpolation/2

Они могут быть скопированы/вставлены и могут использоваться внутри веб-работников для изменения размеров изображений (или любой другой операции, требующей интерполяции). Я использую их для выявления изображений на данный момент).

Я еще не добавил материал lanczos выше, поэтому не стесняйтесь добавлять это как сравнение, если хотите.

6

Это функция javascript, адаптированная из кода @Telanor. При передаче базы данных64 в качестве первого аргумента функции она возвращает base64 измененного изображения. maxWidth и maxHeight являются необязательными.

function thumbnail(base64, maxWidth, maxHeight) {

  // Max size for thumbnail
  if(typeof(maxWidth) === 'undefined') var maxWidth = 500;
  if(typeof(maxHeight) === 'undefined') var maxHeight = 500;

  // Create and initialize two canvas
  var canvas = document.createElement("canvas");
  var ctx = canvas.getContext("2d");
  var canvasCopy = document.createElement("canvas");
  var copyContext = canvasCopy.getContext("2d");

  // Create original image
  var img = new Image();
  img.src = base64;

  // Determine new ratio based on max size
  var ratio = 1;
  if(img.width > maxWidth)
    ratio = maxWidth / img.width;
  else if(img.height > maxHeight)
    ratio = maxHeight / img.height;

  // Draw original image in second canvas
  canvasCopy.width = img.width;
  canvasCopy.height = img.height;
  copyContext.drawImage(img, 0, 0);

  // Copy and resize second canvas to first canvas
  canvas.width = img.width * ratio;
  canvas.height = img.height * ratio;
  ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);

  return canvas.toDataURL();

}
  • 0
    ваш подход очень быстрый, но он создает нечеткое изображение, как вы можете видеть здесь: stackoverflow.com/questions/18922880/…
6

Я настоятельно рекомендую вам проверить эту ссылку и убедиться, что она установлена в true.

Управление режимом масштабирования изображения

Представлен в Gecko 1.9.2 (Firefox 3.6/Thunderbird 3.1/Fennec 1.0)

Gecko 1.9.2 ввел свойство mozImageSmoothingEnabled в элемент холста; если это логическое значение ложно, изображения не будут сглажены при масштабировании. По умолчанию это свойство истинно. просмотреть прозрачность?

  1. cx.mozImageSmoothingEnabled = false;
  • 0
    Да, это установлено в истину
6

Если вы просто пытаетесь изменить размер изображения, я бы рекомендовал установить width и height изображения с помощью CSS. Вот краткий пример:

.small-image {
    width: 100px;
    height: 100px;
}

Обратите внимание, что height и width также могут быть установлены с использованием JavaScript. Вот пример быстрого кода:

var img = document.getElement("my-image");
img.style.width = 100 + "px";  // Make sure you add the "px" to the end,
img.style.height = 100 + "px"; // otherwise you'll confuse IE

Кроме того, чтобы обеспечить правильное изменение размера изображения, добавьте следующие правила CSS для селектора изображений:

Насколько я могу судить, все браузеры, кроме IE, используют бикубический алгоритм для изменения размера изображений по умолчанию, поэтому ваши измененные изображения должны выглядеть хорошо в Firefox и Chrome.

Если установка css width и height не работает, вы можете играть с помощью css transform:

Если по какой-либо причине вам нужно использовать холст, обратите внимание, что есть два способа изменения размера изображения: путем изменения размера холста с помощью css или путем рисования изображения меньшего размера.

Подробнее см. этот вопрос.

Надеюсь, это поможет!

  • 5
    К сожалению, ни изменение размера холста, ни рисование изображения в меньшем размере не решают проблему (в Chrome).
  • 1
    Chrome 27 создает хорошее изображение с измененным размером, но вы не можете скопировать результат на холст; попытка сделать это скопирует оригинальное изображение.
4

Для изменения размера изображения с шириной меньше, чем оригинала, я использую:

    function resize2(i) {
      var cc = document.createElement("canvas");
      cc.width = i.width / 2;
      cc.height = i.height / 2;
      var ctx = cc.getContext("2d");
      ctx.drawImage(i, 0, 0, cc.width, cc.height);
      return cc;
    }
    var cc = img;
    while (cc.width > 64 * 2) {
      cc = resize2(cc);
    }
    // .. than drawImage(cc, .... )

и работает =).

4

Я получил это изображение, щелкнув правой кнопкой мыши элемент canvas в firefox и сохраняя как.

Изображение 5030

var img = new Image();
img.onload = function () {
    console.debug(this.width,this.height);
    var canvas = document.createElement('canvas'), ctx;
    canvas.width = 188;
    canvas.height = 150;
    document.body.appendChild(canvas);
    ctx = canvas.getContext('2d');
    ctx.drawImage(img,0,0,188,150);
};
img.src = 'original.jpg';

так или иначе, вот "фиксированная" версия вашего примера:

var img = new Image();
// added cause it wasnt defined
var canvas = document.createElement("canvas");
document.body.appendChild(canvas);

var ctx = canvas.getContext("2d");
var canvasCopy = document.createElement("canvas");
// adding it to the body

document.body.appendChild(canvasCopy);

var copyContext = canvasCopy.getContext("2d");

img.onload = function()
{
        var ratio = 1;

        // defining cause it wasnt
        var maxWidth = 188,
            maxHeight = 150;

        if(img.width > maxWidth)
                ratio = maxWidth / img.width;
        else if(img.height > maxHeight)
                ratio = maxHeight / img.height;

        canvasCopy.width = img.width;
        canvasCopy.height = img.height;
        copyContext.drawImage(img, 0, 0);

        canvas.width = img.width * ratio;
        canvas.height = img.height * ratio;
        // the line to change
        // ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);
        // the method signature you are using is for slicing
        ctx.drawImage(canvasCopy, 0, 0, canvas.width, canvas.height);
};

// changed for example
img.src = 'original.jpg';
  • 0
    Я пытался делать то, что ты сделал, и это не так хорошо, как у тебя. Если я что-то не пропустил, единственное изменение, которое вы сделали, - это использование подписи метода масштабирования вместо разрезания, верно? По некоторым причинам это не работает для меня.
3

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

Функция drawImage() использует метод линейной интерполяции, метод передискретизации ближайшего соседа. Это хорошо работает, если вы не изменяете размер более чем на половину первоначального размера.

Если вы зацикливаете только на макс. Половину за раз, результаты будут довольно хорошими и намного быстрее, чем доступ к данным пикселя.

Эта функция уменьшается вдвое за раз до достижения желаемого размера:

  function resize_image( src, dst, type, quality ) {
     var tmp = new Image(),
         canvas, context, cW, cH;

     type = type || 'image/jpeg';
     quality = quality || 0.92;

     cW = src.naturalWidth;
     cH = src.naturalHeight;

     tmp.src = src.src;
     tmp.onload = function() {

        canvas = document.createElement( 'canvas' );

        cW /= 2;
        cH /= 2;

        if ( cW < src.width ) cW = src.width;
        if ( cH < src.height ) cH = src.height;

        canvas.width = cW;
        canvas.height = cH;
        context = canvas.getContext( '2d' );
        context.drawImage( tmp, 0, 0, cW, cH );

        dst.src = canvas.toDataURL( type, quality );

        if ( cW <= src.width || cH <= src.height )
           return;

        tmp.src = dst.src;
     }

  }
  // The images sent as parameters can be in the DOM or be image objects
  resize_image( $( '#original' )[0], $( '#smaller' )[0] );

Кредиты на эту должность

2

У меня такое чувство, что модуль, который я написал, даст похожие результаты в Photoshop, поскольку он сохраняет цветовые данные, усредняя их, не применяя алгоритм. Это немного медленное, но для меня это лучшее, потому что оно сохраняет все цветовые данные.

https://github.com/danschumann/limby-resize/blob/master/lib/canvas_resize.js

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

пример того, как использовать, находится внизу https://github.com/danschumann/limby-resize

ОБНОВЛЕНИЕ OCT 2018: В наши дни мой пример более академичен, чем что-либо еще. Webgl - это почти 100%, поэтому вам лучше изменить размер, чтобы получить аналогичные результаты, но быстрее. PICA.js делает это, я считаю. -

2

Итак, что-то интересное, что я нашел некоторое время назад при работе с холстом, что может быть полезно:

Чтобы изменить размер элемента управления холста самостоятельно, вам нужно использовать атрибуты height="" и width="" (или canvas.width/canvas.height). Если вы используете CSS для изменения размера холста, он фактически растянет (то есть: изменит размер) содержимое холста, чтобы он соответствовал полному холсту (а не просто увеличивал или уменьшал область холста.

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

Следует также отметить, что холст имеет разные эффекты в разных браузерах (и даже в разных версиях разных браузеров). Алгоритмы и методы, используемые в браузерах, со временем могут измениться (особенно с выходом Firefox 4 и Chrome 6 так быстро, что будет уделять большое внимание рендерингу холста).

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

Удачи!

  • 1
    Установка ширины или высоты холста с помощью атрибутов HTML приводит к тому, что холст очищается, поэтому с помощью этого метода невозможно изменить размер. Также SVG предназначен для работы с математическими изображениями. Мне нужно уметь рисовать PNG и тому подобное, так что это мне не поможет.
  • 0
    Я обнаружил, что установка высоты и ширины холста и изменение размера с помощью CSS не помогает (в Chrome). Даже выполнение изменения размера с использованием -webkit-transform вместо CSS width / height не приводит к интерполяции.
1

Быстрое и простое изменение размера Javascript:

https://github.com/calvintwr/Hermite-resize

Использование:

h.resize({
    source: document.getElementById('image'), // any canvas or image elements, jQuery or native
    width: 400,
    height: 600,
    output: 'image', // [optional] `image` or `canvas`. If not entered output is same as input element.
    quality: 0.7, // [optional] applicable for `image` output only
}, function(output) {
    //your callback
});

История

Это действительно после многих раундов исследований, чтения и попыток.

Алгоритм resizer использует @ViliusL Hermite script (рендеринг Эрмита действительно самый быстрый и дает достаточно хороший выход). Extended с функциями, которые вам нужны.

Работать с Forks 1 для изменения размера, чтобы он не зависал ваш браузер при изменении размера, в отличие от всех других модификаторов JS.

1

Я преобразовал ответ @syockit, а также понижающий подход к сервису многократного использования Angular для всех, кто интересуется: https://gist.github.com/fisch0920/37bac5e741eaec60e983

Я включил оба решения, потому что они оба имеют свои плюсы и минусы. Подход к свертке lanczos является более высоким качеством за счет более медленного, тогда как пошаговый подход к уменьшению масштаба дает разумно сглаженные результаты и значительно быстрее.

Пример использования:

angular.module('demo').controller('ExampleCtrl', function (imageService) {
  // EXAMPLE USAGE
  // NOTE: it bad practice to access the DOM inside a controller, 
  // but this is just to show the example usage.

  // resize by lanczos-sinc filter
  imageService.resize($('#myimg')[0], 256, 256)
    .then(function (resizedImage) {
      // do something with resized image
    })

  // resize by stepping down image size in increments of 2x
  imageService.resizeStep($('#myimg')[0], 256, 256)
    .then(function (resizedImage) {
      // do something with resized image
    })
})
0

Ищете еще одно простое решение?

var img=document.createElement('img');
img.src=canvas.toDataURL();
$(img).css("background", backgroundColor);
$(img).width(settings.width);
$(img).height(settings.height);

Это решение будет использовать алгоритм изменения размера браузера!:)

  • 0
    Вопрос в том, чтобы уменьшить изображение, а не просто изменить его размер.
  • 0
    [...] Я пытаюсь изменить размер JPG. Я попытался изменить размер того же jpg на связанном сайте и в фотошопе, и он выглядит хорошо, когда его уменьшают. [...] почему вы не можете использовать <img> Jesus Carrera?
0

Спасибо @syockit за отличный ответ. однако мне пришлось немного переформатировать, чтобы заставить его работать. Возможно, из-за проблем с DOM-проверкой:

$(document).ready(function () {

$('img').on("load", clickA);
function clickA() {
    var img = this;
    var canvas = document.createElement("canvas");
    new thumbnailer(canvas, img, 50, 3);
    document.body.appendChild(canvas);
}

function thumbnailer(elem, img, sx, lobes) {
    this.canvas = elem;
    elem.width = img.width;
    elem.height = img.height;
    elem.style.display = "none";
    this.ctx = elem.getContext("2d");
    this.ctx.drawImage(img, 0, 0);
    this.img = img;
    this.src = this.ctx.getImageData(0, 0, img.width, img.height);
    this.dest = {
        width: sx,
        height: Math.round(img.height * sx / img.width)
    };
    this.dest.data = new Array(this.dest.width * this.dest.height * 3);
    this.lanczos = lanczosCreate(lobes);
    this.ratio = img.width / sx;
    this.rcp_ratio = 2 / this.ratio;
    this.range2 = Math.ceil(this.ratio * lobes / 2);
    this.cacheLanc = {};
    this.center = {};
    this.icenter = {};
    setTimeout(process1, 0, this, 0);
}

//returns a function that calculates lanczos weight
function lanczosCreate(lobes) {
    return function (x) {
        if (x > lobes)
            return 0;
        x *= Math.PI;
        if (Math.abs(x) < 1e-16)
            return 1
        var xx = x / lobes;
        return Math.sin(x) * Math.sin(xx) / x / xx;
    }
}

process1 = function (self, u) {
    self.center.x = (u + 0.5) * self.ratio;
    self.icenter.x = Math.floor(self.center.x);
    for (var v = 0; v < self.dest.height; v++) {
        self.center.y = (v + 0.5) * self.ratio;
        self.icenter.y = Math.floor(self.center.y);
        var a, r, g, b;
        a = r = g = b = 0;
        for (var i = self.icenter.x - self.range2; i <= self.icenter.x + self.range2; i++) {
            if (i < 0 || i >= self.src.width)
                continue;
            var f_x = Math.floor(1000 * Math.abs(i - self.center.x));
            if (!self.cacheLanc[f_x])
                self.cacheLanc[f_x] = {};
            for (var j = self.icenter.y - self.range2; j <= self.icenter.y + self.range2; j++) {
                if (j < 0 || j >= self.src.height)
                    continue;
                var f_y = Math.floor(1000 * Math.abs(j - self.center.y));
                if (self.cacheLanc[f_x][f_y] == undefined)
                    self.cacheLanc[f_x][f_y] = self.lanczos(Math.sqrt(Math.pow(f_x * self.rcp_ratio, 2) + Math.pow(f_y * self.rcp_ratio, 2)) / 1000);
                weight = self.cacheLanc[f_x][f_y];
                if (weight > 0) {
                    var idx = (j * self.src.width + i) * 4;
                    a += weight;
                    r += weight * self.src.data[idx];
                    g += weight * self.src.data[idx + 1];
                    b += weight * self.src.data[idx + 2];
                }
            }
        }
        var idx = (v * self.dest.width + u) * 3;
        self.dest.data[idx] = r / a;
        self.dest.data[idx + 1] = g / a;
        self.dest.data[idx + 2] = b / a;
    }

    if (++u < self.dest.width)
        setTimeout(process1, 0, self, u);
    else
        setTimeout(process2, 0, self);
};

process2 = function (self) {
    self.canvas.width = self.dest.width;
    self.canvas.height = self.dest.height;
    self.ctx.drawImage(self.img, 0, 0);
    self.src = self.ctx.getImageData(0, 0, self.dest.width, self.dest.height);
    var idx, idx2;
    for (var i = 0; i < self.dest.width; i++) {
        for (var j = 0; j < self.dest.height; j++) {
            idx = (j * self.dest.width + i) * 3;
            idx2 = (j * self.dest.width + i) * 4;
            self.src.data[idx2] = self.dest.data[idx];
            self.src.data[idx2 + 1] = self.dest.data[idx + 1];
            self.src.data[idx2 + 2] = self.dest.data[idx + 2];
        }
    }
    self.ctx.putImageData(self.src, 0, 0);
    self.canvas.style.display = "block";
}
});
0

Я просто просмотрел страницу сравнения сторон по сторонам и, если что-то не изменилось в последнее время, я не вижу лучшего сокращения (масштабирования) с использованием canvas vs. simple css. Я тестировал в FF6 Mac OSX 10.7. Все еще слегка мягкий по сравнению с оригиналом.

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

Затем я нашел потрясающий плагин jQuery, который делает приложение этих фильтров быстрым: http://codecanyon.net/item/jsmanipulate-jquery-image-manipulation-plugin/428234

Я просто применяю фильтр резкости сразу после изменения размера изображения, которое должно дать вам желаемый эффект. Мне даже не пришлось использовать элемент canvas.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню