Фильтрация изображений на Javascript
Ниже показан пример обработки изображения фильтрами, описанными в Википедии. На серверной стороне средствами PHP нечто подобное делалось здесь.
Листинг может быть сохранён в файле типа .html, картинка для теста применялась стандартная.
Файл подключаемого к скрипту изображения должен быть локальным или загружаться через форму HTML,
но не через внешний URL, иначе будет "Uncaught DOMException: The operation is insecure" на
методе getImageData
(или включите в своём домене CORS). Также скрипт должен выполняться на локалхосте, а не открываться кликом по локальному файлу (т.е., не выполняться по протоколу file://
).
Конкретный фильтр в коде, конечно же, легко заменить:
Например, фильтру Gaussian blur 5 × 5 будет соответствовать матрица
[ 1/256, 4/256, 6/256, 4/256, 1/256, 4/256, 16/256, 24/256, 16/256, 4/256, 6/256, 24/256, 36/256, 24/256, 6/256, 4/256, 16/256, 24/256, 16/256, 4/256, 1/256, 4/256, 6/256, 4/256, 1/256 ]
, переданная вторым аргументом функции convolve
.
Вот исходник и результат тестового выполнения скрипта:
<p>Скрипт выполняется на локалхосте, а не по протоколу file:// <p>В текущей папке присутствует картинка Lenna.png <script> function convolve (imageIn, kernel, callback) { //вернёт картинку, отсильтрованную с помощью kernel //в callback-функцию function (Error error, Image imageOut) let dim = Math.sqrt(kernel.length), pad = Math.floor(dim / 2); if (dim % 2 !== 1) { //Должна быть нечётная размерность return callback(new RangeError("Invalid kernel dimension"), null); } let w = imageIn.width, h = imageIn.height, can = document.createElement('canvas'), cw, ch, ctx, imgIn, imgOut, datIn, datOut; can.width = cw = w + pad * 2; can.height = ch = h + pad * 2; //добавлены отступы ctx = can.getContext('2d'); ctx.fillStyle = '#000'; //начальное заполнение чёрным ctx.fillRect(0, 0, cw, ch); ctx.drawImage(imageIn, pad, pad); imgIn = ctx.getImageData(0, 0, cw, ch); datIn = imgIn.data; imgOut = ctx.createImageData(w, h); datOut = imgOut.data; let row, col, pix, i, dx, dy, r, g, b; for (row = pad; row <= h; row++) { for (col = pad; col <= w; col++) { r = g = b = 0; for (dx = -pad; dx <= pad; dx++) { for (dy = -pad; dy <= pad; dy++) { i = (dy + pad) * dim + (dx + pad); //индекс в фильтре pix = 4 * ((row + dy) * cw + (col + dx)); //индекс в картинке r += datIn[pix++] * kernel[i]; g += datIn[pix++] * kernel[i]; b += datIn[pix ] * kernel[i]; } } pix = 4 * ((row - pad) * w + (col - pad)); //индекс в целевой картинке datOut[pix++] = (r + .5) ^ 0; datOut[pix++] = (g + .5) ^ 0; datOut[pix++] = (b + .5) ^ 0; datOut[pix ] = 255; //без прозрачности } } can.width = w; can.height = h; //ставим размеры канвы ctx.putImageData(imgOut, 0, 0); let imageOut = new Image(); imageOut.addEventListener('load', function () { callback (null, imageOut); }); imageOut.addEventListener('error', function (error) { callback(error, null); }); imageOut.src = can.toDataURL('image/png'); } //Как применять функцию, пример: let image = new Image(); image.addEventListener ('load', function () { //Вызвать по загрузке картинки image.alt = 'Image is here!'; document.body.appendChild(image); convolve (image, [ 0, -1, 0, -1, 5, -1, 0, -1, 0], // фильтр "Sharpen" function (error, result) { if (error !== null) console.error(error); else { result.alt = 'Filtered image'; document.body.appendChild(result); } } ); }); image.src = './Lenna.png'; //Файл Lenna.png должен находиться в текущей папке, //см. http://blog.kislenko.net/show.php?id=2524 </script>
тестовое выполнение скрипта, скриншот автоматически сжат и утратил качество
24.10.2021, 14:06 [751 просмотр]