Встречаются ситуации, когда картинка изображена на черном фоне и требуется сделать его прозрачным для сохранения в PNG. Например, брызги воды, которые невозможно сфотографировать на белом фоне. Дизайнеры для наложения таких слоёв используют в Photoshop метод наложения экран (screen), получая в итоге красивый макет. Но при резке макета на прозрачные PNG возникает неразрешимая проблема с фоном. Вырезать фон из полупрозрачных изображений обводкой и удалением не представляется возможным в Photoshop. Создать в Photoshop нужный α-канал оказалось сложнее, чем написать небольшой скрипт.
Предлагаю метод, реализованный на JavaScript. Реализация алгоритма возможна на любом языке, обладающим библиотекой попиксельной обработки изображений.
Маска прозрачности (α-канал) для каждого пикселя изображения будет формироваться из значений RGB пикселя.
Создадим обычный html файл, с заголовками (transparetbg.html)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Image glass</title> </head> <body> </body> </html>
Позовём красивую девушку, положив файл girl.jpg в корень диска
<body style="background:url(girl.jpg) right bottom no-repeat">

Добавим кнопки управления в body и canvas
<canvas id="_canvas"></canvas> <div class="btn"> Temperature <input type="range" min="0" max="255" step="1" value="255" id="_temp" onChange="decrunching();"> <button onclick="decrunching('min')">Min</button> <button onclick="decrunching('max')">Max</button> <button onclick="decrunching('avg')">Avg</button> <button onclick="decrunching('r')">R</button> <button onclick="decrunching('g')">G</button> <button onclick="decrunching('b')">B</button> </div>
Добавим в head стиль для кнопок
<style> .btn input { float: left } </style>
Подготовим переменные. Для доступа к пикселям изображения используем canvas. При загрузке страницы загрузится картинка black_problem.jpg из корневой директории с которой мы будем работать. При загрузке методом onload её копия сохранится в переменной original.

<script> var canvas = document.getElementById("_canvas"); var temp = document.getElementById("_temp"); var context = canvas.getContext('2d'); var original; var select_type; window.onload = function () { var i = new Image(); i.onload = function () { canvas.width = i.width; canvas.height = i.height; context.drawImage(i, 0, 0, i.width, i.height); original = context.getImageData(0, 0, i.width, i.height); }; i.src = '/black_problem.jpg'; }; </script>
Напишем функцию обработки изображения
function decrunching(type) { // копируем исходное изображение context.putImageData(original, 0, 0); // создаём новое изображение на основе размеров исходного var newdata = context.createImageData(original); var newpx = newdata.data; //Сохраняем тип алгоритма или используем сохраненный if (type != "") { select_type = type; } else { type = select_type; } // получаем доступ до пикселей изображения var imageData = context.getImageData(0, 0, canvas.width, canvas.height); // считаем количество пикселей изображения var pixels = imageData.data; var len = pixels.length; // организуем цикл для перебора всех пикселей изображения for (var i = 0; i < len; i += 4) { // копируем значение цвета каждого пикселя в переменную и новое изображение (без изменений) let r = newpx[i] = pixels[i]; let g = newpx[i + 1] = pixels[i + 1]; let b = newpx[i + 2] = pixels[i + 2]; // Объявляем переменную для α-канала let rgba; switch (type) { // Рассчитываем прозрачность из значений красного цвета case 'r': rgba = r; break; // Рассчитываем прозрачность из значений зеленого цвета case 'g': rgba = g; break; // рассчитываем прозрачность из значений голубого цвета case 'b': rgba = b; break; // рассчитываем прозрачность из минимальных значений всех цветов case 'min': rgba = r; if (g > r) rgba = g; if (b > g) rgba = b; break; // рассчитываем прозрачность из максимальных значений всех цветов case 'max': rgba = b; if (g < b) rgba = g; if (b < r) rgba = r; break; // рассчитываем прозрачность из средних значений всех цветов default: rgba = ((r + g + b) / 3).toFixed(0); break; } // записываем значение прозрачности пикселя с учетом настройки "температуры прозрачности" newpx[i + 3] = (rgba * temp.value / 255).toFixed(0); } // Выводим на экран новую картинку context.putImageData(newdata, 0, 0); }
Для формирования прозрачности каждого пикселя используются различные методы, но общая суть в формировании значения прозрачности в обратной зависимости от яркости пикселя. Белый пиксель будет полностью непрозрачным. Черный пиксель будет полностью прозрачным. В большинстве случаев подходит метод усреднения значений, т.е. default по коду.
Температура позволяет регулировать порог прозрачности изображения
Поиграемся
Чтобы Chrome разрешил нам работать с локальными изображениями запустим его с нужным ключиком (сделаем ярлык на рабочем столе) и откроем в нем наш html файл.
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --disable-web-security"

Нажмём кнопку Avg
Черный фон изображения стал прозрачным. Чудо!

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



Поиграем температурой

Сохраним изображение, кликнув правой кнопкой мышки

Полный листинг программы
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Image glass</title> <style> .btn input { float: left } </style> </head> <body style="background:url(girl.jpg) right bottom no-repeat"> <canvas id="_canvas"></canvas> <div class="btn"> Temperature <input type="range" min="0" max="255" step="1" value="255" id="_temp" onChange="decrunching();"> <button onclick="decrunching('min')">Min</button> <button onclick="decrunching('max')">Max</button> <button onclick="decrunching('avg')">Avg</button> <button onclick="decrunching('r')">R</button> <button onclick="decrunching('g')">G</button> <button onclick="decrunching('b')">B</button> </div> <script> var canvas = document.getElementById("_canvas"); var temp = document.getElementById("_temp"); var context = canvas.getContext('2d'); var original; var select_type; window.onload = function () { var i = new Image(); i.onload = function () { canvas.width = i.width; canvas.height = i.height; context.drawImage(i, 0, 0, i.width, i.height); original = context.getImageData(0, 0, i.width, i.height); }; i.src = '/black_problem.jpg'; }; function decrunching(type) { context.putImageData(original, 0, 0); var newdata = context.createImageData(original); var newpx = newdata.data; if (type != "") { select_type = type; } else { type = select_type; } var imageData = context.getImageData(0, 0, canvas.width, canvas.height); var pixels = imageData.data; var len = pixels.length; for (var i = 0; i < len; i += 4) { let r = newpx[i] = pixels[i]; let g = newpx[i + 1] = pixels[i + 1]; let b = newpx[i + 2] = pixels[i + 2]; let rgba; switch (type) { case 'r': rgba = r; break; case 'g': rgba = g; break; case 'b': rgba = b; break; case 'min': rgba = r; if (g > r) rgba = g; if (b > g) rgba = b; break; case 'max': rgba = b; if (g < b) rgba = g; if (b < r) rgba = r; break; default: rgba = ((r + g + b) / 3).toFixed(0); break; } newpx[i + 3] = (rgba * temp.value / 255).toFixed(0); } context.putImageData(newdata, 0, 0); } </script> </body> </html>
