Возникла следующая проблема:
У меня есть множество различных картинок подобного рода:
На них есть прозрачные области различной формы, в которые мне нужно как-то подставить другие изображения, подогнав их размеры под габариты этих регионов.
Для этого нужно как-то получить координаты этих самых прозрачных областей на картинке.
Подскажите, как можно было бы реализовать данную задумку, так как я никак не могу к ней подступиться...
Начать хочу с того, что катастрофически не согласен с мнением участника @qwabra, что часть вопроса о вставке изображений
выходит за рамки вопроса
Человек описал свою проблему - значит, поможем ее решить полностью)
Итак. С чего бы подступиться к Вашей проблеме?
Для начала определимся с тем, что же Вам нужно. А вам нужно найти такие прямоугольные области на изображении, в которые Вы бы могли вписать другие изображения.
Почему прямоугольные? Я вроде как ничего в этом мире еще не проспал и растровые изображения по сей день
представляются как прямоугольный двумерный массив чисел
Отлично. Мы определились с тем, что необходимо искать на изображении прямоугольную область для вставки. Но как же быть? Вот у Вас на картинке есть круги и прочие отличные от прямоугольников фигуры, куда надо вписать изображение...
Собственно, любую фигуру можно заключить в прямоугольник, для этого нам нужно лишь:
Хорошо. А как же нам получить эти самые X и Y? Надо как-то обследовать изображение, выделив пустые регионы.
Небольшое отступление:
Вы написали про "белые области" в заголовке своего вопроса. Однако, исходя из данной Вами картинки, Вы явно имели в виду области, где альфа-канал пикселей равен 0 (то есть области прозрачны). Дальнейший алгоритм базируется на этом небольшом исправлении.
Если же Вам потребуются все таки белые области, то алгоритм можно будет, конечно, доработать, но совершенно непонятно, что же делать с белыми областями по бокам изображений...
Возвращаемся к нашим рассуждениям)
Как обнаружить пустые регионы? Я предлагаю Вам такой алгоритм:
Обходить изображение мы будем от верхнего левого угла к правому нижнему.
В качестве оптимизации рекурсивного обхода могу предложить проводить его следующим образом:
Так как у Вас предполагаются простые фигуры для областей, куда могут быть вставлены изображения (безо всяких рюшечек посреди них), мы можем исследовать области построчно. Поясню:
Так как мы обходим изображение с самого верха, то и за искомую область мы "зацепимся" в высшей ее точке. Далее мы можем запустить функцию проверки по той же строчке влево и вправо, проверяя соседние пиксели на соответствие условию. Когда проверка заканчивается, мы спускаемся на пиксель ниже и проверяем следующую строку
Данный метод будет отрабатывать достаточно быстро, однако, подчеркну, он пригоден лишь для простых фигур! Если появятся витиеватости, то придется переписать рекурсивный обход
Итак, после обхода изображения у нас есть информация по всем пустым регионам на нем.
Немного спойлеров: пройдясь описанным мною методом по Вашему изображению, я получил следующие области:
(29, 24) - (159, 143)
(183, 26) - (304, 147)
(369, 31) - (625, 187)
(81, 156) - (260, 297)
(369, 274) - (625, 431)
(181, 311) - (302, 431)
(29, 312) - (165, 433)
Или, если визуализировать:
Что же теперь?
А теперь дело за малым!
Еще раз обращу Ваше внимание на то, что изображения у Вас содержат прозрачные области.
В переложении на реальную жизнь: если бы Вы держали данную картинку в руках, в ней были бы дыры. Дыры, которыми очень удобно наложить это самое изображение на те фотографии, что должны быть на их месте
Надеюсь, Вы уловили, к чему я веду
Мы можем отрисовать нужные нам изображения в заранее рассчитанных областях, а потом просто как бы положить сверху "дырявую" картинку, так что наши будут видны из-под нее
Фух. Наговорил я много, но также могу подкрепить свои слова кодом с комментариями)
Приведу его сразу в виде сниппета, чтобы Вы могли узреть результат (разверните на всю страницу):
// Наш обработчик изображений
function ImageExplorer(img, selector) {
// Var
ImageExplorer.minRegion = 5000;
let it = this;
let image;
let canvas;
let context;
let emptyRegions = [];
// Initialization
function init() {
image = getHTMLImageElement(img);
// Создаем канвас для работы, получаем его контекст
canvas = document.createElement("canvas");
context = canvas.getContext("2d");
// Проверяем переданный селектор, определяющий, подходит ли нам пиксель
if (selector == undefined)
// Альфа-канал равен 0 => пиксель прозрачен
selector = color => color[0] == 0;
}
// Funcs
// Ищем пустые регионы
it.findRegions = () => new Promise(resolve => {
// Ожидаем загрузки изображения
new Promise(res => {
let intervalID = setInterval(() => {
if (image.complete)
{
clearInterval(intervalID);
clearCanvas();
// Разрешаем текущее обещание
res();
}
}, 300);
}).then(() => {
emptyRegions = [];
// Отрисовываем картинку
context.drawImage(image, 0, 0);
// Получаем данные о ее пикселях
let imgData = context.getImageData(0, 0, image.width, image.height).data;
// Функция, которая по значениям x и y вернет цвет указанного пикселя ([a, r, g, b])
let getPixel = (dX, dY) => {
let index = 4 * (dX + dY * image.width);
return [imgData[index], imgData[index + 1], imgData[index + 2], imgData[index + 3]];
};
// Проходимся по каждому пикселю изображения
for (let y = 0; y < image.height; y++)
for (let x = 0; x < image.width; x++) {
// Проверяем, существует ли пустой регион, которому принадлежит текущий пиксель
let existing = emptyRegions.find(reg => reg.contains(x, y));
// Если нет, проверяем дальше
if (!existing) {
// Если пиксель подходит нам, начинаем обследование региона
if (selector(getPixel(x, y))) {
// Устанавливаем координаты для крайней левой верхней и крайней правой нижней точек
// Ниже они будут меняться
let left = x;
let up = y;
let right = x;
let down = y;
// Число пикселей в исследованном регионе
let pixels = 1;
// Самый сок
// Самовызывающаяся рекурсивная функция, ссылающаяся на текущий контекст
// Один из немногих поводов любить JS хД
//
// Логика такая: мы вызываем функцию на текущих координатах, а далее она
// "расползается" по всему региону, где соседствующие пиксели
// удовлетворяют селектору
(function(dX, dY, type) {
// Если мы не вышли за пределы изображения, а также пиксель подходит под селектор
if (dX > -1 && dX < image.width && dY < image.height && selector(getPixel(dX, dY))) {
// Заменяем значения, если требуется
if (dX < left)
left = dX;
if (dX > right)
right = dX;
if (dY < up)
up = dY;
if (dY > down)
down = dY;
// Увеличиваем число найденых пикселей в регионе
++pixels;
// type несет следующий смысл:
// 0: текущая функция запускает "исследование" по оси Ox, а потом переходит на пиксель ниже
// 1: текущая функция продолжает "исследование" по оси Ox (в сторону увеличения)
// 2: текущая функция продолжает "исследование" по оси Ox (в сторону убывания)
switch (type) {
case 0:
arguments.callee(dX + 1, dY, 1);
arguments.callee(dX - 1, dY, 2);
arguments.callee(dX, dY + 1, 0);
break;
case 1:
arguments.callee(dX + 1, dY, 1);
break;
case 2:
arguments.callee(dX - 1, dY, 2);
break;
default:
break;
}
}
})(x, y, 0);
// Если пикселей в регионе >= минимального кол-ва пикселей, то добавим этот регион
if (pixels >= ImageExplorer.minRegion)
{
emptyRegions.push(new EmptyRegion([left, up], [right, down]));
//x = right + 1; // Небольшая оптимизация. Дает не слишком большой выигрыш, так что выключена для большей точности
}
}
}
//else
//x = existing.right + 1; // Небольшая оптимизация. Дает не слишком большой выигрыш, так что выключена для большей точности
}
// Разрешаем текущее обещание, отдаем найденные регионы
resolve(emptyRegions);
});
});
// Создадим новое изображение, передав только изображения, которые нужно
// "подложить" под основную картинку (в указанном порядке)
it.createImage = function() {
clearCanvas();
// Преобразуем переданные аргументы в массив изображений
arguments[Symbol.isConcatSpreadable] = true; // https://ru.stackoverflow.com/a/860866/248572
let images = [].concat(arguments).map(getHTMLImageElement);
// Отрисуем каждое изображение, отмасштабировав его по сопутствующему региону
for (let i in images)
context.drawImage(images[i], emptyRegions[i].left, emptyRegions[i].up, emptyRegions[i].getWidth(), emptyRegions[i].getHeight());
// Положим основное изображение поверх
context.drawImage(image, 0, 0);
// Вернем результирующее изображение
let resultImage = new Image();
resultImage.src = canvas.toDataURL();
return resultImage;
};
// Создадим новое изображение, явно задав регионы
// Изображения будут отсортированы по наибольшему размерному соответствию переданным регионам
it.createImageFromRegions = function(regions, ...images) {
clearCanvas();
let regs = regions.slice();
// Преобразовываем входные аргументы в [изображение, наиболее подходящий регион]
let imgReg = images.map(x => {
// Получаем изображение
let result = getHTMLImageElement(x);
// Получаем соотношение его сторон
let ratio = result.width / result.height;
// Сортируем регионы по этому соотношению
regions = regs.sort((a, b) => a == b ? 0 : ((Math.abs(ratio - a.getRatio()) < Math.abs(ratio - b.getRatio())) ? -1 : 1));
// Нулевой регион будет наиболее подходящим
let bestRegion = regs[0];
// Удаляем его из массива
regs.splice(0, 1);
return [result, bestRegion];
});
// Отрисуем каждое изображение, отмасштабировав его по сопутствующему региону
for (let i in imgReg)
context.drawImage(imgReg[i][0], imgReg[i][1].left, imgReg[i][1].up, imgReg[i][1].getWidth(), imgReg[i][1].getHeight());
// Положим основное изображение поверх
context.drawImage(image, 0, 0);
// Вернем результирующее изображение
let resultImage = new Image();
resultImage.src = canvas.toDataURL();
return resultImage;
};
// Загружаем изображение
function getHTMLImageElement(data) {
let result;
if ((typeof data) == "string") {
result = new Image();
// Передана ссылка/Base64-строка
result.src = data;
}
else
if (data instanceof HTMLImageElement)
// Передан элемент <img>
result = data;
else
if (data[0] instanceof HTMLImageElement)
// Передан элемент <img>, бережно укутанный селектором jQuery
result = data[0];
else
throw "Необходимо передать изображение!"
return result;
}
// Растягиваем канвас до размеров изображения и чистим его
function clearCanvas() {
canvas.width = image.width;
canvas.height = image.height;
context.clearRect(0, 0, canvas.width, canvas.height);
}
init();
}
// Пустой регион
function EmptyRegion(LU, RD) {
// Var
// Добавочное значение
EmptyRegion.delta = 3;
let it = this;
it.left;
it.right;
it.up;
it.down;
// Initialization
function init(args) {
switch (args.length) {
case 2:
break;
case 4:
LU = [args[0], args[1]];
RD = [args[2], args[3]];
break;
default:
throw "Необходимо передать две точки!";
}
it.left = LU[0] - EmptyRegion.delta;
it.up = LU[1] - EmptyRegion.delta;
it.right = RD[0] + EmptyRegion.delta;
it.down = RD[1] + EmptyRegion.delta;
}
// Funcs
it.getWidth = () => it.right - it.left;
it.getHeight = () => it.down - it.up;
// Получим соотношение сторон региона
it.getRatio = () => it.getWidth() / it.getHeight();
// Содержит ли текущий регион указанную точку
it.contains = (x, y) => x >= it.left && x <= it.right && y >= it.up && y <= it.down;
init(arguments);
}
<script>
function getImgByID(id) {
return document.getElementById(id);
}
function create() {
let explorer = new ImageExplorer(getImgByID("main"));
explorer.findRegions().then(regions => {
let img = explorer.createImageFromRegions(regions, getImgByID("bill"),
getImgByID("ford"),
getImgByID("stan"),
getImgByID("dipper"),
getImgByID("mabel"),
getImgByID("soos"),
getImgByID("wendy"));
getImgByID("generated").src = img.src;
});
}
</script>
<h1>Главное изображение:</h1>
<img id="main">
<br><br><br>
<h1>Изображения для вставки:</h1>
<img id="bill">
<img id="ford">
<img id="stan">
<img id="dipper">
<img id="mabel">
<img id="soos">
<img id="wendy">
<br><br><br>
<h1>Скомпонованное изображение:</h1>
<img id="generated">
<br>
<button onclick="create()">Создать</button>
<script src="https://kir-antipov.github.io/StackOverflow/q872682.js"></script>
Если Вы нажмете на кнопку в сниппете, то увидите, как Ваша картинка объединится с моими изображениями вот в это:
Думаю, именно этого результата Вы и хотели достичь!
Надеюсь, мой ответ смог Вам помочь и Вы продолжите двигаться к своим свершениями)
Если что-то было не очень понятно, либо же Вам требуется помощь в доработке описанного метода - не стесняйтесь и спрашивайте!
В них должны вставляться другие картинки с автоизменением их размера
выходит за рамки вопроса
Для этого нужно как-то получить координаты этих белых областей на картинке
вот пример создания матрицы искомых областей arr[i][j] = ddq(i * mod, j * mod)
шаг поиска mod = 10
var lib;
(function (lib) {
lib.mod = 10;
})(lib || (lib = {}));
(function (lib) {
let addTobody = el => { document.body.appendChild(el); };
let callF = f => { f(); };
lib.load_elStack = [];
lib.load_fooStack = [];
window.addEventListener("load", () => {
lib.load_elStack.forEach(addTobody);
lib.load_fooStack.forEach(callF);
});
})(lib || (lib = {}));
(function (lib) {
lib.img = document.createElement('img');
lib.img.src = getImg();
})(lib || (lib = {}));
(function (lib) {
lib.canvas = document.createElement('canvas');
lib.load_elStack.push(lib.canvas);
lib.canvas.setAttribute('style', 'border: 1px solid grey;');
lib.ctx = lib.canvas.getContext("2d");
lib.load_fooStack.push(() => {
lib.canvas.width = lib.img.width;
lib.canvas.height = lib.img.height;
lib.ctx.drawImage(lib.img, 0, 0);
});
function ddq(x, y, q = 1) {
let p = lib.ctx.getImageData(x, y, 10, 10);
let d = p.data;
if (q) {
d = d.filter((q, i) => (i % 4) === 0);
d = d.filter(q => q);
}
else {
d = d.filter((q, i) => (i % 4) !== 0);
d = d.filter(q => q !== 255);
}
if (d.length === 0) {
lib.ctx.fillRect(x, y, lib.mod, lib.mod);
return 0;
}
return 1;
}
lib.load_fooStack.push(() => {
let arr = Array.from({ length: lib.canvas.width / lib.mod }, () => Array.from({ length: lib.canvas.height / lib.mod }));
if (true) {
for (var i = 0, l = arr.length; i < l; i++) {
for (var j = 0, l2 = arr[i].length; j < l2; j++) {
arr[i][j] = ddq(i * lib.mod, j * lib.mod);
}
}
}
else {
ddq(50, 50, 1);
ddq(2, 45, 0);
}
});
})(lib || (lib = {}));
function getImg() {
return 'data:image/png;base64,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';
}
//# sourceMappingURL=index.js.map
namespace lib {
/**
* шаг - диаметр минимальной области поиска
*/
export const mod = 10
}
namespace lib {
let addTobody = el => { document.body.appendChild(el) }
let callF = f => { f() }
// --
export let load_elStack = [] as HTMLElement[]
export let load_fooStack = [] as Function[]
// --
window.addEventListener("load", () => {
load_elStack.forEach(addTobody)
load_fooStack.forEach(callF)
})
}
namespace lib {
export let img = document.createElement('img')
img.src = getImg()
// load_elStack.push(img)
}
namespace lib {
export let canvas = document.createElement('canvas')
load_elStack.push(canvas)
canvas.setAttribute('style', 'border: 1px solid grey;')
export let ctx = canvas.getContext("2d")
load_fooStack.push(() => {
canvas.width = img.width
canvas.height = img.height
ctx.drawImage(img, 0, 0)
})
function ddq(x, y, q = 1) {
let p = ctx.getImageData(x, y, 10, 10)
let d = p.data
if (q) {
// --> фильтер по нулефой прозрачности
d = d.filter((q, i) => (i % 4) === 0)
d = d.filter(q => q)
// <--
} else {
// --> фильтер по белым
d = d.filter((q, i) => (i % 4) !== 0)
d = d.filter(q => q !== 255)
// <--
}
// console.log(p)
// console.log(d.length)
// if (d.length !== 0) {
if (d.length === 0) {
ctx.fillRect(x, y, mod, mod)
return 0
}
return 1
}
load_fooStack.push(() => {
let arr = Array.from({ length: canvas.width / mod }, () => Array.from({ length: canvas.height / mod }))
if (true) {
for (var i = 0, l = arr.length; i < l; i++) {
for (var j = 0, l2 = arr[i].length; j < l2; j++) {
arr[i][j] = ddq(i * mod, j * mod)
}
}
} else {
ddq(50, 50, 1) // - прозрачная
ddq(2, 45, 0) // - белая область
}
})
}
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Эта ошибка возникает, когда перемещаю код jquery (отправляет данные на сервер node js) в отделный файл
Допустим есть строка с временем, например 1234, по какой-то причине она не разделена, привычным нам, двоеточиемМы знаем, что двоеточие будет...
Охота зашифровать сообщение в AES256Допустим есть ключ и есть сообщение