Необходимо проверить все пиксели
Чтобы найти пиксель, соответствующий цвету, в худшем случае (пиксель этого цвета отсутствует на изображении) вам нужно перешагнуть через каждый пиксель изображения.
Как этого не делать
Преобразование каждого пикселя в строку DOM является наихудшим способом сделать это, поскольку строка DOM использует много памяти и ресурсов ЦП, особенно если создается с использованием jQuery (у которого есть свой дополнительный багаж)
От шестнадцатеричного цвета к массиву
Чтобы найти пиксель, вам нужно только проверить данные цвета каждого пикселя относительно значения HEX. Вы преобразуете шестнадцатеричное значение в массив из 3 байтов.
Следующая функция преобразует из CSS шестнадцатеричных форматов "#HHH"
"#HHHH"
, "#HHHHHH"
и "#HHHHHHHH"
, игнорируя альфа-часть, если она включена, к массиву целых чисел 0-255
const hex2RGB = h => {
if(h.length === 4 || h.length === 5) {
return [parseInt(h[1] + h[1], 16), parseInt(h[2] + h[2], 16), parseInt(h[3] + h[3], 16)];
}
return [parseInt(h[1] + h[2], 16), parseInt(h[3] + h[4], 16), parseInt(h[5] + h[6], 16)];
}
Поиск пикселя
Я не знаю, как вы планируете использовать такую функцию, поэтому приведенный ниже пример - это метод общего назначения, который поможет и может быть изменен при необходимости
Он всегда найдет пиксель, если вы позволите ему, даже если нет идеального соответствия. Это достигается путем нахождения ближайшего к нужному цвету цвета.
Причина, по которой наиболее близкое совпадение заключается в том, что при рисовании изображения на 2D-холсте значения пикселей слегка изменяются, если изображение имеет прозрачные пиксели (предварительно умноженное альфа) * 1026 *
Функция находит пиксель путем измерения пространственного расстояния между пикселем и шестнадцатеричным цветом (простая геометрия Пифагора). Ближайший цвет - это наименьшее расстояние.
Возвращает объект
{
x, // the x coordinate of the match
y, // the y coordinate of the match
distance, // how closely the color matches the requested color.
// 0 means a perfect match
// to 441 completely different eg black and white
// value is floored to an integer value
}
Если изображение испорчено (перекрестное происхождение, локальное устройство хранения) или вы пропустили что-то которые не могут быть преобразованы в пиксели, функция вернет undefined
Функция сохраняет холст, который она использует для получения данных пикселей, поскольку предполагает, что она будет использоваться много раз. Если изображение испорчено, оно поймает ошибку (добавит предупреждение в консоль), очистит испорченный холст и будет готово для другого изображения.
Использование
Чтобы использовать функцию, добавьте его в Ваша база кода будет настроена автоматически.
Получите изображение и шестнадцатеричное значение и вызовите функцию с цветом image
, CSS hex
и, при желании, пороговым расстоянием для соответствия цветов.
Например, найти точное соответствие для # FF0000
const result = findPixel(origImage, "#FF0000", 0); // find exact match for red
if (result) { // only if found
console.log("Found color #FF0000 at pixel " + result.x + ", " + result.y);
} else {
console.log("The color #FF0000 is not in the image");
}
или найти цвет, близкий к
const result = findPixel(origImage, "#FF0000", 20); // find a match for red
// within 20 units.
// A unit is 1 of 256
if (result) { // only if found
console.log("Found closest color within " + result.distance + "units of #FF0000 at pixel " + result.x + ", " + result.y);
}
или найти ближайший
// find the closest, no threshold ensures a result
const result = findPixel(origImage, "#FF0000");
console.log("Found closest color within " + result.distance + "units of #FF0000 at pixel " + result.x + ", " + result.y);
Код
Функция выглядит следующим образом.
const findPixel = (() => {
var can, ctx;
function createCanvas(w, h) {
if (can === undefined){
can = document.createElement("canvas");
ctx = can.getContext("2d");
}
can.width = w;
can.height = h;
}
function getPixels(img) {
const w = img.naturalWidth || img.width, h = img.naturalHeight || img.height;
createCanvas(w, h);
ctx.drawImage(img, 0, 0);
try {
const imgData = ctx.getImageData(0, 0, w, h);
can.width = can.height = 1; // make canvas as small as possible so it wont
// hold memory. Leave in place to avoid instantiation overheads
return imgData;
} catch(e) {
console.warn("Image is un-trusted and pixel access is blocked");
ctx = can = undefined; // canvas and context can no longer be used so dump them
}
return {width: 0, height: 0, data: []}; // return empty pixel data
}
const hex2RGB = h => { // Hex color to array of 3 values
if(h.length === 4 || h.length === 5) {
return [parseInt(h[1] + h[1], 16), parseInt(h[2] + h[2], 16), parseInt(h[3] + h[3], 16)];
}
return [parseInt(h[1] + h[2], 16), parseInt(h[3] + h[4], 16), parseInt(h[5] + h[6], 16)];
}
const idx2Coord = (idx, w) => ({x: idx % w, y: idx / w | 0});
return function (img, hex, minDist = Infinity) {
const [r, g, b] = hex2RGB(hex);
const {width, height, data} = getPixels(img);
var idx = 0, found;
while (idx < data.length) {
const R = data[idx] - r;
const G = data[idx + 1] - g;
const B = data[idx + 2] - b;
const d = R * R + G * G + B * B;
if (d === 0) { // found exact match
return {...idx2Coord(idx / 4, width), distance: 0};
}
if (d < minDist) {
minDist = d;
found = idx;
}
idx += 4;
}
return found ? {...idx2Coord(found / 4, width), distance: minDist ** 0.5 | 0 } : undefined;
}
})();
Эта функция была протестирована и работает, как описано выше.
Примечание Если код, указанный в вашем вопросе, игнорировать альфа-значение изображения и CSS шестнадцатеричный цвет.
Обратите внимание , что если вы собираетесь найти много цветов на том же изображении эта функция не совсем подходит для ваших нужд. Если это так, дайте мне знать в комментарии, и я могу внести изменения или объяснить, как оптимизировать код для таких целей.
Примечание Он не очень подходит для одноразового использования , Однако в этом случае измените строку const findPixel = (() => {
на var findPixel = (() => {
и после ее использования удалите ссылку findpixel = undefined;
, а JS очистит все имеющиеся у нее ресурсы.
Примечание Если вы также хотите получить фактический цвет ближайшего найденного цвета, который тоже тривиально добавить. Спросите в комментариях.
Примечание Это достаточно быстро (вам будет трудно получить более быстрый результат), но имейте в виду, что для очень больших изображений 4K и выше это может занять немного и на устройствах очень низкого уровня это может вызвать ошибку нехватки памяти. Если это проблема, тогда возможно другое решение, но оно намного медленнее.