Встречаются ситуации, когда картинка изображена на черном фоне и требуется сделать его прозрачным для сохранения в 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>