Определение, попадает ли одно значение rgb в пороговое значение любого значения rgb в массиве — javascript

#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, так был ли на него дан ответ?