Изменение размера изображения с помощью JavaScript canvas (плавно)
Я пытаюсь изменить размер некоторых изображений с помощью canvas, но я не знаю, как их сгладить. На фотошоп, браузеры и т. д.. есть несколько алгоритмов, которые они используют (например, бикубический, билинейный), но я не знаю, встроены ли они в холст или нет.
вот моя скрипка:http://jsfiddle.net/EWupT/
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width=300
canvas.height=234
ctx.drawImage(img, 0, 0, 300, 234);
document.body.appendChild(canvas);
первый-это обычный тег изображения с измененным размером, а второй-холст. Обратите внимание, как холст один не так гладко. Как я могу достичь "гладкость"?
6 ответов:
вы можете использовать понижающий шаг для достижения лучших результатов. Большинство браузеров, кажется,использовать линейную интерполяцию, а не би-кубический для изменения размера изображений.
(обновление в спецификации было добавлено свойство качества,
imageSmoothingQuality
который в настоящее время доступен только в Chrome.)Если вы не выберете сглаживание или ближайший сосед, браузер всегда будет интерполировать изображение после его масштабирования как эта функция в качестве фильтр нижних частот, чтобы избежать сглаживания.
Bi-linear использует пиксели 2x2 для интерполяции, а bi-cubic использует 4x4, поэтому, делая это шагами, вы можете приблизиться к bi-cubic результату при использовании билинейной интерполяции, как видно на полученных изображениях.
var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); var img = new Image(); img.onload = function () { // set size proportional to image canvas.height = canvas.width * (img.height / img.width); // step 1 - resize to 50% var oc = document.createElement('canvas'), octx = oc.getContext('2d'); oc.width = img.width * 0.5; oc.height = img.height * 0.5; octx.drawImage(img, 0, 0, oc.width, oc.height); // step 2 octx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5); // step 3, resize to final size ctx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5, 0, 0, canvas.width, canvas.height); } img.src = "//i.imgur.com/SHo6Fub.jpg";
<img src="//i.imgur.com/SHo6Fub.jpg" width="300" height="234"> <canvas id="canvas" width=300></canvas>
В зависимости от того, насколько радикальным является ваш размер, вы можете пропустить Шаг 2, если разница меньше.
в демо вы можете увидеть новый результат теперь очень похож на элемент изображения.
Я создал многоразовый угловой сервис для обработки высококачественного изменения размеров изображений / холстов для всех, кто заинтересован:https://gist.github.com/transitive-bullshit/37bac5e741eaec60e983
сервис включает в себя два решения, поскольку обе они имеют свои плюсы / минусы. Метод свертки ланцоса является более качественным за счет снижения скорости, в то время как поэтапный подход к масштабированию дает разумно сглаженные результаты и значительно быстрее.
пример использования:
angular.module('demo').controller('ExampleCtrl', function (imageService) { // EXAMPLE USAGE // NOTE: it's 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 }) })
С скрипка Трунг Ле Нгуен Ната совсем не правильно (он просто использует исходное изображение на последнем шаге)
Я написал свою собственную общую скрипку с сравнением производительности:в основном это:
img.onload = function() { var canvas = document.createElement('canvas'), ctx = canvas.getContext("2d"), oc = document.createElement('canvas'), octx = oc.getContext('2d'); canvas.width = width; // destination canvas size canvas.height = canvas.width * img.height / img.width; var cur = { width: Math.floor(img.width * 0.5), height: Math.floor(img.height * 0.5) } oc.width = cur.width; oc.height = cur.height; octx.drawImage(img, 0, 0, cur.width, cur.height); while (cur.width * 0.5 > width) { cur = { width: Math.floor(cur.width * 0.5), height: Math.floor(cur.height * 0.5) }; octx.drawImage(oc, 0, 0, cur.width * 2, cur.height * 2, 0, 0, cur.width, cur.height); } ctx.drawImage(oc, 0, 0, cur.width, cur.height, 0, 0, canvas.width, canvas.height); }
Я создал библиотеку, которая позволяет снизить любой процент при сохранении всех данных о цвете.
https://github.com/danschumann/limby-resize/blob/master/lib/canvas_resize.js
этот файл можно включить в браузер. Результаты будут выглядеть как photoshop или image magick, сохраняя все цветовые данные, усредняя пиксели, а не принимая близлежащие и отбрасывая другие. Он не использует формулу, чтобы угадать средние, он принимает точное среднее.
основываясь на ответе K3N, я переписываю код вообще для всех, кто хочет
var oc = document.createElement('canvas'), octx = oc.getContext('2d'); oc.width = img.width; oc.height = img.height; octx.drawImage(img, 0, 0); while (oc.width * 0.5 > width) { oc.width *= 0.5; oc.height *= 0.5; octx.drawImage(oc, 0, 0, oc.width, oc.height); } oc.width = width; oc.height = oc.width * img.height / img.width; octx.drawImage(img, 0, 0, oc.width, oc.height);
ОБНОВЛЕНИЕ JSFIDDLE DEMO
вот мой ОНЛАЙН ДЕМО
Я написал небольшую JS-утилиту для обрезки и изменения размера изображения на интерфейсе. Вот это ссылке на проекте GitHub. Также вы можете получить blob из окончательного изображения, чтобы отправить его.
import imageSqResizer from './image-square-resizer.js' let resizer = new imageSqResizer( 'image-input', 300, (dataUrl) => document.getElementById('image-output').src = dataUrl; ); //Get blob let formData = new FormData(); formData.append('files[0]', resizer.blob); //get dataUrl document.getElementById('image-output').src = resizer.dataUrl;