Я использую html5 элементы холста, чтобы изменить размеры изображений в моем браузере. Оказывается, качество очень низкое. Я нашел это: Отключить интерполяцию при масштабировании <canvas>, но это не помогает повысить качество.
Ниже мой код CSS и JS, а также изображение, масштабированное с помощью Photoshop и масштабированное в Canvas API.
Что мне нужно сделать, чтобы получить оптимальное качество при масштабировании изображения в браузере?
Примечание. Я хочу уменьшить масштаб большого изображения до небольшого, изменить цвет на холсте и отправить результат с холста на сервер.
CSS:
canvas, img {
image-rendering: optimizeQuality;
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-optimize-contrast;
image-rendering: optimize-contrast;
-ms-interpolation-mode: nearest-neighbor;
}
JS:
var $img = $('<img>');
var $originalCanvas = $('<canvas>');
$img.load(function() {
var originalContext = $originalCanvas[0].getContext('2d');
originalContext.imageSmoothingEnabled = false;
originalContext.webkitImageSmoothingEnabled = false;
originalContext.mozImageSmoothingEnabled = false;
originalContext.drawImage(this, 0, 0, 379, 500);
});
Изображение изменено с помощью фотошопа:
Изображение изменено на холсте:
Редактировать:
Я попытался уменьшить масштаб более чем за один шаг, как это было предложено в:
Изменение размера изображения на холсте HTML5 и холсте Html5 drawImage: как применять сглаживание
Это функция, которую я использовал:
function resizeCanvasImage(img, canvas, maxWidth, maxHeight) {
var imgWidth = img.width,
imgHeight = img.height;
var ratio = 1, ratio1 = 1, ratio2 = 1;
ratio1 = maxWidth / imgWidth;
ratio2 = maxHeight / imgHeight;
// Use the smallest ratio that the image best fit into the maxWidth x maxHeight box.
if (ratio1 < ratio2) {
ratio = ratio1;
}
else {
ratio = ratio2;
}
var canvasContext = canvas.getContext("2d");
var canvasCopy = document.createElement("canvas");
var copyContext = canvasCopy.getContext("2d");
var canvasCopy2 = document.createElement("canvas");
var copyContext2 = canvasCopy2.getContext("2d");
canvasCopy.width = imgWidth;
canvasCopy.height = imgHeight;
copyContext.drawImage(img, 0, 0);
// init
canvasCopy2.width = imgWidth;
canvasCopy2.height = imgHeight;
copyContext2.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvasCopy2.width, canvasCopy2.height);
var rounds = 2;
var roundRatio = ratio * rounds;
for (var i = 1; i <= rounds; i++) {
console.log("Step: "+i);
// tmp
canvasCopy.width = imgWidth * roundRatio / i;
canvasCopy.height = imgHeight * roundRatio / i;
copyContext.drawImage(canvasCopy2, 0, 0, canvasCopy2.width, canvasCopy2.height, 0, 0, canvasCopy.width, canvasCopy.height);
// copy back
canvasCopy2.width = imgWidth * roundRatio / i;
canvasCopy2.height = imgHeight * roundRatio / i;
copyContext2.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvasCopy2.width, canvasCopy2.height);
} // end for
// copy back to canvas
canvas.width = imgWidth * roundRatio / rounds;
canvas.height = imgHeight * roundRatio / rounds;
canvasContext.drawImage(canvasCopy2, 0, 0, canvasCopy2.width, canvasCopy2.height, 0, 0, canvas.width, canvas.height);
}
Вот результат, если я использую размер 2 шага вниз:
Вот результат, если я использую размер 3 шага вниз:
Вот результат, если я использую размер 4 шага вниз:
Вот результат, если я использую 20 пошаговых размеров:
Примечание. Оказывается, что от 1 до 2 шагов значительно улучшается качество изображения, но чем больше шагов вы добавляете в процесс, тем более размытым становится изображение.
Есть ли способ решить проблему, заключающуюся в том, что изображение становится более размытым по мере добавления шагов?
Редактировать 2013-10-04: Я попробовал алгоритм GameAlchemist. Вот результат по сравнению с фотошопом.
Фотошоп Изображение:
Алгоритм GameAlchemist:
источник
Ответы:
Поскольку ваша проблема состоит в том, чтобы уменьшить изображение, нет смысла говорить об интерполяции, то есть о создании пикселя. Проблема здесь в понижении.
Чтобы уменьшить изображение, нам нужно превратить каждый квадрат p * p пикселей в исходном изображении в один пиксель в целевом изображении.
По соображениям производительности браузеры выполняют очень простую понижающую дискретизацию: для создания меньшего изображения они просто выбирают ОДИН пиксель в источнике и используют его значение для пункта назначения. который «забывает» некоторые детали и добавляет шум.
Тем не менее, есть исключение: так как понижающую дискретизацию изображения 2X очень просто вычислить (в среднем 4 пикселя на единицу) и она используется для пикселей Retina / HiDPI, этот случай обрабатывается должным образом - браузер использует 4 пикселя для один-.
НО ... если вы несколько раз используете понижающую дискретизацию 2X, вы столкнетесь с проблемой, заключающейся в том, что последовательные ошибки округления добавят слишком много шума.
Что еще хуже, вы не всегда будете изменять размеры на степень два, а изменение размера до ближайшей степени + последнее изменение размера очень шумно.
То, что вы ищете, это идеальная пиксельная понижающая дискретизация, то есть повторная выборка изображения, которая будет учитывать все входные пиксели - независимо от масштаба.
Для этого мы должны вычислить для каждого входного пикселя его вклад в один, два или четыре целевых пикселя в зависимости от того, находится ли масштабированная проекция входных пикселей прямо внутри целевых пикселей, перекрывает границу X, границу Y или и то, и другое. ,
(Схема была бы хороша здесь, но у меня ее нет.)
Вот пример масштаба холста против моего идеального масштаба пикселя в масштабе 1/3 зомбата.
Обратите внимание, что изображение может быть масштабировано в вашем браузере и .jpegized SO.
Тем не менее, мы видим, что намного меньше шума, особенно в траве позади вомбата и ветвях справа. Шум на меху делает его более контрастным, но похоже, что у него белые волосы - в отличие от исходного изображения -.
Правое изображение менее броское, но определенно приятнее.
Вот код для идеального масштабирования пикселей:
результат скрипки: http://jsfiddle.net/gamealchemist/r6aVp/embedded/result/
сама скрипка: http://jsfiddle.net/gamealchemist/r6aVp/
Это довольно жадная память, поскольку для хранения промежуточных значений целевого изображения требуется плавающий буфер (-> если мы подсчитываем результирующий холст, мы используем в этом алгоритме 6 раз память исходного изображения).
Это также довольно дорого, поскольку каждый исходный пиксель используется независимо от целевого размера, и мы должны платить за getImageData / putImageDate, тоже довольно медленно.
Но в этом случае нет способа быстрее обработать каждое исходное значение, и ситуация не так уж и плоха: для моего образа вомбата размером 740 * 556 обработка занимает от 30 до 40 мс.
источник
Быстрый повторный выбор холста с хорошим качеством: http://jsfiddle.net/9g9Nv/442/
Обновление: версия 2.0 (быстрее, веб-работники + переносимые объекты) - https://github.com/viliusle/Hermite-resize
источник
Предложение 1 - расширить технологический трубопровод
Вы можете использовать пошаговый режим, как я описал в ссылках, на которые вы ссылаетесь, но вы, похоже, используете их неправильно.
Понижение не требуется для масштабирования изображений до соотношений выше 1: 2 (обычно, но не ограничиваясь ими). Это где вам нужно сделать радикальное уменьшение масштаба, вам нужно разделить его на два (и реже, больше) шага в зависимости от содержимого изображения (в частности, там, где возникают высокие частоты, такие как тонкие линии).
Каждый раз, когда вы уменьшаете изображение, вы теряете детали и информацию. Вы не можете ожидать, что полученное изображение будет таким же четким, как и оригинал.
Если вы затем уменьшите изображения в несколько этапов, вы потеряете много информации, и результат будет плохим, как вы уже заметили.
Попробуйте сделать только один дополнительный шаг или две вершины.
сверток
В случае Photoshop обратите внимание, что он применяет свертку после повторной выборки изображения, например, резкость. Это не просто бикубическая интерполяция, поэтому для полной эмуляции Photoshop нужно также добавить шаги, которые выполняет Photoshop (с настройкой по умолчанию).
В этом примере я буду использовать свой исходный ответ, на который вы ссылаетесь в своем посте, но я добавил к нему более четкую свертку, чтобы улучшить качество как постпроцесс (см. Демонстрацию внизу).
Вот код для добавления фильтра резкости (он основан на универсальном сверточном фильтре - я поместил в него матрицу весов для резкости, а также коэффициент смешивания, чтобы скорректировать произношение эффекта):
Использование:
Значение
mixFactor
находится между [0.0, 1.0] и позволяет вам преуменьшить эффект повышения резкости - эмпирическое правило: чем меньше размер, тем меньше необходим эффект.Функция (на основе этого фрагмента ):
Результатом использования этой комбинации будет:
ОНЛАЙН ДЕМО ЗДЕСЬ
В зависимости от того, сколько резкости вы хотите добавить к смеси, вы можете получить результат от «размытого» по умолчанию до очень резкого:
Предложение 2 - реализация алгоритма низкого уровня
Если вы хотите получить наилучший результат с точки зрения качества, вам нужно перейти на низкий уровень и подумать, например, о реализации этого нового алгоритма.
См. Интерполяционно-зависимая понижающая дискретизация изображений (2011) от IEEE.
Вот ссылка на статью полностью (PDF) .
В настоящее время нет никаких реализаций этого алгоритма в JavaScript AFAIK, так что вы готовы, если вы хотите бросить себя в этой задаче.
Суть в том (выдержки из статьи):
Аннотация
(см. предоставленную ссылку для всех деталей, формул и т. д.)
источник
Если вы хотите использовать только холст, лучший результат будет с несколькими шагами. Но это еще не очень хорошо. Для лучшего качества вам нужна чистая реализация js. Мы только что выпустили pica - высокоскоростной даунскалер с переменным качеством / скоростью. Короче говоря, он изменяет размеры 1280 * 1024px за ~ 0,1 с и 5000 * 3000px за 1 с с высочайшим качеством (фильтр Ланцоша с 3 лепестками). В Pica есть демоверсия , где вы можете поиграть со своими изображениями, уровнями качества и даже попробовать его на мобильных устройствах.
Пика еще не имеет нерезкой маски, но она будет добавлена очень скоро. Это гораздо проще, чем внедрить высокоскоростной сверточный фильтр для изменения размера.
источник
Зачем использовать холст для изменения размера изображений? Все современные браузеры используют бикубическую интерполяцию - тот же процесс, который используется в Photoshop (если вы все делаете правильно), - и они делают это быстрее, чем процесс canvas. Просто укажите нужный размер изображения (используйте только одно измерение, высоту или ширину, чтобы пропорционально изменить размер).
Это поддерживается большинством браузеров, включая более поздние версии IE. Более ранние версии могут требовать специфичного для браузера CSS .
Простая функция (использующая jQuery) для изменения размера изображения будет выглядеть так:
Затем просто используйте возвращенное значение, чтобы изменить размер изображения в одном или обоих измерениях.
Очевидно, что вы можете внести некоторые улучшения, но это сделает работу.
Вставьте следующий код в консоль этой страницы и посмотрите, что происходит с граватарами:
источник
Не правильный ответ для людей, которым действительно нужно изменить размер самого изображения, но просто чтобы уменьшить размер файла .
У меня была проблема с фотографиями «прямо с камеры», которые мои клиенты часто загружали в «несжатом» формате JPEG.
Не так хорошо известно, что canvas поддерживает (в большинстве браузеров 2017) изменение качества JPEG
С помощью этого трюка я мог бы уменьшить изображения 4K x 3K с> 10Mb до 1 или 2Mb, конечно, это зависит от ваших потребностей
Смотри сюда
источник
Вот многоразовый сервис Angular для высококачественного изменения размера изображения / холста: https://gist.github.com/fisch0920/37bac5e741eaec60e983
Сервис поддерживает свертку Ланцоша и пошаговое уменьшение. Сверточный подход - более высокое качество за счет того, что он медленнее, тогда как пошаговый метод уменьшения масштаба дает разумно сглаженные результаты и значительно быстрее.
Пример использования:
источник
Это улучшенный фильтр изменения размера Hermite, в котором используется 1 рабочий, чтобы окно не зависало.
https://github.com/calvintwr/blitz-hermite-resize
источник
Я нашел решение, которое не требует прямого доступа к пиксельным данным и их обхода для выполнения понижающей дискретизации. В зависимости от размера изображения это может быть очень ресурсоемким, и было бы лучше использовать внутренние алгоритмы браузера.
Функция drawImage () использует метод линейной интерполяции ближайшего соседа. Это хорошо работает, когда вы не уменьшаете размер более половины исходного размера .
Если вы зациклились, чтобы изменить размер максимум на половину за раз, результаты были бы довольно хорошими и намного быстрее, чем доступ к пиксельным данным.
Эта функция сокращает до половины за раз до достижения желаемого размера:
источник
Может быть, человек, вы можете попробовать это, что я всегда использую в своем проекте. Таким образом, вы можете получить не только высококачественное изображение, но и любой другой элемент на вашем холсте.
источник
вместо 0,85 , если мы добавим 1,0 . Вы получите точный ответ.
Вы можете получить четкое и яркое изображение. пожалуйста, проверьте
источник
Я действительно стараюсь избегать просмотра данных изображений, особенно на больших изображениях. Таким образом, я придумал довольно простой способ приличного уменьшения размера изображения без каких-либо ограничений или ограничений, используя несколько дополнительных шагов. Эта процедура снижается до минимально возможного значения полшага до желаемого целевого размера. Затем он масштабируется до двойного целевого размера, а затем еще раз наполовину. Поначалу это звучит смешно, но результаты поразительно хороши и идут быстро.
источник
context.scale(xScale, yScale)
источник
ДЕМО : изменение размера изображений с помощью JS и HTML Canvas Demo fiddler.
Вы можете найти 3 различных метода для изменения размера, которые помогут вам понять, как работает код и почему.
https://jsfiddle.net/1b68eLdr/93089/
Полный код демонстрационного и метода TypeScript, который вы можете использовать в своем коде, можно найти в проекте GitHub.
https://github.com/eyalc4/ts-image-resizer
Это окончательный код:
источник