Canvas в HTML5 контрастность изображения

я писал программу обработки изображений, которая применяет эффекты через обработку пикселей холста HTML5. Я достиг молотилки, Vintaging и ColorGradient пиксельных манипуляций, но невероятно, я не могу изменить контраст изображения! Я пробовал несколько решений, но я всегда получаю слишком много яркости на картинке и меньше контрастного эффекта и я не планирую использовать библиотеки Javascript, так как я пытаюсь достичь этих эффектов прирожденно.

основной код манипуляции пикселями:

var data = imageData.data;
for (var i = 0; i < data.length; i += 4) {
 //Note: data[i], data[i+1], data[i+2] represent RGB respectively
data[i] = data[i];
data[i+1] = data[i+1];
data[i+2] = data[i+2];
}

пример манипулирования пикселями

значения находятся в режиме RGB, что означает, что data[i] - красный цвет. Поэтому, если data[i] = data[i] * 2; яркость будет увеличена в два раза для красного канала этого пикселя. Пример:

var data = imageData.data;
for (var i = 0; i < data.length; i += 4) {
 //Note: data[i], data[i+1], data[i+2] represent RGB respectively
 //Increases brightness of RGB channel by 2
data[i] = data[i]*2;
data[i+1] = data[i+1]*2;
data[i+2] = data[i+2]*2;
}

*примечание: Я не прошу вас, чтобы завершить код! Это было бы просто одолжением! Я прошу алгоритм (даже псевдо код) показывает, как контраст в пиксельной манипуляции возможен! Я был бы рад, если кто-то может предоставить хороший алгоритм контраста изображений в HTML5 canvas.

7 ответов


более быстрый вариант (на основе Эшера!--73-->) составляет:

function contrastImage(imgData, contrast){  //input range [-100..100]
    var d = imgData.data;
    contrast = (contrast/100) + 1;  //convert to decimal & shift range: [0..2]
    var intercept = 128 * (1 - contrast);
    for(var i=0;i<d.length;i+=4){   //r,g,b,a
        d[i] = d[i]*contrast + intercept;
        d[i+1] = d[i+1]*contrast + intercept;
        d[i+2] = d[i+2]*contrast + intercept;
    }
    return imgData;
}

вывод, подобный приведенному ниже; эта версия математически одинакова, но работает намного быстрее.


оригинальный ответ

вот упрощенная версия с объяснением подход уже обсудили (который был основан на этот статья):

function contrastImage(imageData, contrast) {  // contrast as an integer percent  
    var data = imageData.data;  // original array modified, but canvas not updated
    contrast *= 2.55; // or *= 255 / 100; scale integer percent to full range
    var factor = (255 + contrast) / (255.01 - contrast);  //add .1 to avoid /0 error

    for(var i=0;i<data.length;i+=4)  //pixel values in 4-byte blocks (r,g,b,a)
    {
        data[i] = factor * (data[i] - 128) + 128;     //r value
        data[i+1] = factor * (data[i+1] - 128) + 128; //g value
        data[i+2] = factor * (data[i+2] - 128) + 128; //b value

    }
    return imageData;  //optional (e.g. for filter function chaining)
}

Примечания

  1. я решил использовать


после попытки ответа на Schahriar SaffarShargh, он не ведет себя, как контраст должен вести себя. Я, наконец, наткнулся на этот алгоритм, и он работает как шарм!

для получения дополнительной информации об алгоритме, читать в этой статье и это раздел комментариев.

function contrastImage(imageData, contrast) {

    var data = imageData.data;
    var factor = (259 * (contrast + 255)) / (255 * (259 - contrast));

    for(var i=0;i<data.length;i+=4)
    {
        data[i] = factor * (data[i] - 128) + 128;
        data[i+1] = factor * (data[i+1] - 128) + 128;
        data[i+2] = factor * (data[i+2] - 128) + 128;
    }
    return imageData;
}

использование:

var newImageData = contrastImage(imageData, 30);

надеюсь, это будет экономия времени для кого-то. Ура!


я узнал, что вы должны использовать эффект, отделяя темноты и огни или технически все, что меньше 127 (среднее значение R+G+B / 3) в шкале rgb является черным и более 127 является белым, поэтому по вашему уровню контраста вы минус значение, скажем, 10 контраст от черных и добавить то же значение к белым!

вот пример: У меня есть два пикселя с цветами RGB, [105,40,200] / [255,200,150] Так я знаю, что для моего первого пиксела 105 + 40 + 200 = 345, 345/3 = 115 и 115 меньше моей половины 255, которая составляет 127, поэтому я считаю пиксель ближе к [0,0,0], поэтому, если я хочу минус 10 контраст, то я забираю 10 из каждого цвета в среднем Таким образом, я должен разделить значение каждого цвета на среднее значение итога, которое было 115 для этого случая, и умножить его на мой контраст и минус конечное значение из этого конкретного цвета:

например, я возьму 105 (красный) из моего пикселя, поэтому я разделю его на общий avg RGB. который 115 и умножает его на мой значение контраста 10, (105/115) * 10, которое дает вам что-то около 9 (вы должны округлить его!) а затем уберите это 9 от 105, чтобы цвет стал 96, поэтому мой красный после контрастности 10 на темном пикселе.

поэтому, если я перейду на значения моего пикселя, станьте [96,37,183]! (Примечание: масштаб контраста зависит от вас! но мой в конце концов вы должны преобразовать его в некоторый масштаб, например, от 1 до 255)

для более светлых пикселей я также делаю то же самое, за исключением вычитания контраста значение я добавляю его! и если вы достигнете предела 255 или 0, вы прекратите сложение и вычитание для этого конкретного цвета! поэтому мой второй пиксель, который является более легким пикселем, становится [255,210,157]

как вы добавляете больше контраста он будет светлее цвета и темнее темнее и, следовательно, добавляет контраст к вашей картине!

вот пример кода JavaScript ( я еще не пробовал ) :

var data = imageData.data;
for (var i = 0; i < data.length; i += 4) {
 var contrast = 10;
 var average = Math.round( ( data[i] + data[i+1] + data[i+2] ) / 3 );
  if (average > 127){
    data[i] += ( data[i]/average ) * contrast;
    data[i+1] += ( data[i+1]/average ) * contrast;
    data[i+2] += ( data[i+2]/average ) * contrast;
  }else{
    data[i] -= ( data[i]/average ) * contrast;
    data[i+1] -= ( data[i+1]/average ) * contrast;
    data[i+2] -= ( data[i+2]/average ) * contrast;
  }
}

вы можете взглянуть на документы OpenCV, чтобы увидеть, как вы можете это сделать:настройки яркости и контрастности.

тогда есть демо-код:

 double alpha; // Simple contrast control: value [1.0-3.0]
 int beta;     // Simple brightness control: value [0-100]

 for( int y = 0; y < image.rows; y++ )
 { 
      for( int x = 0; x < image.cols; x++ )
      { 
          for( int c = 0; c < 3; c++ )
          {
              new_image.at<Vec3b>(y,x)[c] = saturate_cast<uchar>( alpha*( image.at<Vec3b>(y,x)[c] ) + beta );
          }
      }
 }

который, я полагаю, вы способны перевести на javascript.


по vintaging я предполагаю, что вы пытаетесь применить LUTS..Недавно я пытался добавить цветовые обработки к окнам холста. Если вы хотите фактически применить "LUTS" к окну холста, я считаю, что вам нужно фактически сопоставить массив, который imageData возвращает в массив RGB LUT.

(от иллюзии света) В качестве примера начало 1D LUT может выглядеть примерно так: Примечание: строго говоря, это 3x 1D LUTs, так как каждый цвет (R,G, B) является 1D Лут

R, G, B 
3, 0, 0 
5, 2, 1 
7, 5, 3 
9, 9, 9

это означает, что:

For an input value of 0 for R, G, and B, the output is R=3, G=0, B=0 
For an input value of 1 for R, G, and B, the output is R=5, G=2, B=1 
For an input value of 2 for R, G, and B, the output is R=7, G=5, B=3 
For an input value of 3 for R, G, and B, the output is R=9, G=9, B=9

что является странным LUT, но вы видите, что для заданного значения R, G или B ввода существует заданное значение R, G и B вывода.

Итак, если пиксель имеет входное значение 3, 1, 0 для RGB, выходной пиксель будет 9, 2, 0.

во время этого я также понял после игры с imageData, что он возвращает Uint8Array и что значения в этом массиве являются десятичными. Большинство 3D СПОИ наговор. Итак, сначала вы чтобы сделать некоторый тип преобразования hex в dec на всем массиве перед всем этим отображением.


эта реализация javascript соответствует определению SVG / CSS3 "контраст" (и следующий код будет отображать изображение холста одинаково):

/*contrast filter function*/
//See definition at https://drafts.fxtf.org/filters/#contrastEquivalent
//pixels come from your getImageData() function call on your canvas image
contrast = function(pixels, value){
    var d = pixels.data;
    var intercept = 255*(-value/2 + 0.5);
    for(var i=0;i<d.length;i+=4){
        d[i] = d[i]*value + intercept;
        d[i+1] = d[i+1]*value + intercept;
        d[i+2] = d[i+2]*value + intercept;
        //implement clamping in a separate function if using in production
        if(d[i] > 255) d[i] = 255;
        if(d[i+1] > 255) d[i+1] = 255;
        if(d[i+2] > 255) d[i+2] = 255;
        if(d[i] < 0) d[i] = 0;
        if(d[i+1] < 0) d[i+1] = 0;
        if(d[i+2] < 0) d[i+2] = 0;
    }
    return pixels;
}

Это формула, которую вы ищете ...

var data = imageData.data;
if (contrast > 0) {

    for(var i = 0; i < data.length; i += 4) {
        data[i] += (255 - data[i]) * contrast / 255;            // red
        data[i + 1] += (255 - data[i + 1]) * contrast / 255;    // green
        data[i + 2] += (255 - data[i + 2]) * contrast / 255;    // blue
    }

} else if (contrast < 0) {
    for (var i = 0; i < data.length; i += 4) {
        data[i] += data[i] * (contrast) / 255;                  // red
        data[i + 1] += data[i + 1] * (contrast) / 255;          // green
        data[i + 2] += data[i + 2] * (contrast) / 255;          // blue
    }
}

надеюсь, что это помогает!