#javascript #arrays #canvas #threshold
#javascript #массивы #холст #пороговое значение
Вопрос:
Я был бы признателен за несколько советов по созданию функции для сравнения одного значения RGB с массивом значений RGB и определения, попадает ли оно в пороговое значение. Это делается в ванильном javascript с использованием элемента canvas HTML5.
Моя первая попытка:
var colorArray = [ //rgb values to search through
[212, 35, 96],
[200, 200, 150],
[100, 100, 75]
];
var threshold = 15; //the threshold
//Given a canvas with an image drawn on it
var pixelData = ctx.getImageData(0, 0, canvas.width, canvas.height); // get the canvas pixel data
for(var row = 0; row < canvas.height; row ){ //stepping through the pixels
for (var col = 0, index = 0, colorTotal = 0; col < canvas.width; col ){
index = (col (row * canvas.width)) * 4;
colorTotal = pixelData.data[index] pixelData.data[index 1] pixelData.data[index 2]; //add the rgb values of the current pixel
for(var i = 0, pixelColorTotal = 0, result = 0; i < colorArray.length; i ){ //stepping through the colorArray
pixelColorTotal = colorArray[i] [0] colorArray[i] [1] colorArray[i] [2]; //add the rgb values of the current array element
result = Math.abs(colorTotal - pixelColorTotal); //find the difference between the color totals
if(result < threshold){
//..do something we've breached the threshold
}
}
}
}
Это работает не так хорошо, потому что, например: [255, 0, 50] и [50, 255, 0] даже близко не совпадают по цвету, но они превышают пороговое значение.
Моя 2-я попытка:
var colorArray = [ //rgb values to search through
[212, 35, 96],
[200, 200, 150],
[100, 100, 75]
];
var threshold = 15; //the threshold
//Given a canvas with an image drawn on it
var pixelData = ctx.getImageData(0, 0, canvas.width, canvas.height); // get the canvas pixel data
for(var row = 0; row < canvas.height; row ){ //stepping through the pixels
for (var col = 0, index = 0; col < canvas.width; col ){
index = (col (row * canvas.width)) * 4;
for(var i = 0, result = 0; i < colorArray.length; i ){ //stepping through the colorArray
result = Math.abs(pixelData.data[index] - colorArray[i] [0]); //load red difference
if(result >= threshold){ //check the red channel to see if it exceeds threshold
result = Math.abs(pixelData.data[index 1] - colorArray[i] [1]); //load green difference
if(result >= threshold){ //check the green channel to see if it exceeds threshold
result = Math.abs(pixelData.data[index 2] - colorArray[i] [2]); //load blue difference
if(result >= threshold){ //check the green channel to see if it exceeds threshold
//do something we have passed all the threshold checks
}
}
}
}
}
}
Это лучше, но очень неэффективно.
Есть ли какие-нибудь идеи получше? Спасибо за чтение.
Ответ №1:
Я не уверен на 100%, чего вы хотите, поэтому дайте мне знать, если я иду в неправильном направлении.
function withinThreshold(colorArr, inputArr, threshold){
let maxThreshold = threshold;
let minThreshold = threshold * -1;
for(var i = 0; i < colorArr.length; i ){
let rDiff = colorArr[i][0] - inputArr[0];
let gDiff = colorArr[i][1] - inputArr[1];
let bDiff = colorArr[i][2] - inputArr[2];
if(rDiff > minThreshold amp;amp; rDiff < maxThreshold amp;amp;
gDiff > minThreshold amp;amp; gDiff < maxThreshold amp;amp;
bDiff > minThreshold amp;amp; gDiff < maxThreshold){
return i
}
}
return -1
}
var colorArray = [ //rgb values to search through
[212, 35, 96],
[200, 200, 150],
[100, 100, 75]
];
var threshold = 15;
for(var row = 0; row < canvas.height; row ){
for (var col = 0; col < canvas.width; col ){
let pixel = ctx.getImageData(row, col, 1, 1);
let match = withinThreshold(colorArray, pixel, threshold);
if(match === -1){
console.log('no match found');
}else{
console.log('match found! index:',match);
}
// I made it like indexOf, it will return the matching index of colorArr.
// You can simplify it like this:
/*
if(match > -1){
console.log('match found');
}
*/
}
}
Также не забывайте, что getImageData, скорее всего, вернет 4-значный массив [r, g, b, a], если вы не изменили настройки, чтобы не разрешать альфа.
Также я сделал так, чтобы он индивидуально захватывал нужные ему пиксельные данные. Таким образом, в будущем вы сможете уточнить его до определенного местоположения, не проверяя все. Просто изменив row и canvas.width на 10 и 20. Таким образом, вы можете сохранить обработку только для измененной области. Вы также можете сделать это с помощью col и canvas.height, чтобы уменьшить его еще больше.
Я создал несколько движков рендеринга canvas, поэтому столкнулся с проблемами производительности, и мне пришлось много заниматься оптимизацией.
Комментарии:
1. Я подсчитал разницу так же, как и вы. Гораздо быстрее. Я думаю, что мое использование Math.abs замедляло его. Как бы то ни было, пользователь выбирает активную область изображения. Параметр colorArray var заполняется выделяющимися цветами этой области. Цветовой массив сравнивается не с самим изображением, а с результатом обработки изображения. В результате я использую альфа-канал, чтобы указать, будут ли пиксели обрабатываться дальше или нет. Код, который мы обсуждали здесь, является одним из методов, которые я могу использовать для фильтрации результата первого уровня. Это достаточно быстро, чтобы поэкспериментировать с ним прямо сейчас. Спасибо за публикацию
2. @lucyPa, так был ли на него дан ответ?