Как точно отфильтровать значение RGB для эффекта хроматического ключа

Я просто читалэтот учебник и попробовал этот пример. Поэтому я скачал видео из Интернета для собственного тестирования. Все, что мне нужно сделать, это настроить значения RGB в условиях

ЗДЕСЬ пример кода из примера

computeFrame: function() {
    this.ctx1.drawImage(this.video, 0, 0, this.width, this.height);
    let frame = this.ctx1.getImageData(0, 0, this.width, this.height);
    let l = frame.data.length / 4;

    for (let i = 0; i < l; i++) {
      let r = frame.data[i * 4 + 0];
      let g = frame.data[i * 4 + 1];
      let b = frame.data[i * 4 + 2];
      if (g > 100 && r > 100 && b < 43)
        frame.data[i * 4 + 3] = 0;
    }
    this.ctx2.putImageData(frame, 0, 0);
    return;
  }

В учебном примере это фильтрация желтого (не желтого цвета). В загруженном образце видео используется зеленый фон. Поэтому я подправил значение rgb в условии if, чтобы получить желаемый результат

После нескольких попыток мне удалось получить это.

Теперь я хочу знать, как я могу точно отфильтровать зеленый экран (или любой другой экран), не догадываясь. Или случайное изменение значений.

Просто догадываясь, что это займет несколько часов, чтобы понять все правильно. И это всего лишь пример с приложением реального мира. Это может занять больше.

ПРИМЕЧАНИЕ: пример работает в Firefox сейчас ..

Ответы на вопрос(2)

Решение Вопроса

вам просто нужен лучший алгоритм. Вот один, он не идеален, но вы можете настроить его намного проще.

В основном вам просто понадобится палитра цветов и выберите самые светлые и самые темные значения из видео (поместив значения RGB в переменные l_ и d_ соответственно). При необходимости вы можете немного откорректировать допуск, но правильное получение значений l_ и r_ путем выбора разных областей с помощью палитры цветов даст вам лучший ключ.

let l_r = 131,
    l_g = 190,
    l_b = 137,

    d_r = 74,
    d_g = 148,
    d_b = 100;

let tolerance = 0.05;

let processor = {
  timerCallback: function() {
    if (this.video.paused || this.video.ended) {
      return;
    }
    this.computeFrame();
    let self = this;
    setTimeout(function () {
        self.timerCallback();
      }, 0);
  },

  doLoad: function() {
    this.video = document.getElementById("video");
    this.c1 = document.getElementById("c1");
    this.ctx1 = this.c1.getContext("2d");
    this.c2 = document.getElementById("c2");
    this.ctx2 = this.c2.getContext("2d");
    let self = this;
    this.video.addEventListener("play", function() {
        self.width = self.video.videoWidth;
        self.height = self.video.videoHeight;
        self.timerCallback();
      }, false);
  },

  calculateDistance: function(c, min, max) {
      if(c < min) return min - c;
      if(c > max) return c - max;

      return 0;
  },

  computeFrame: function() {
    this.ctx1.drawImage(this.video, 0, 0, this.width, this.height);
    let frame = this.ctx1.getImageData(0, 0, this.width, this.height);
        let l = frame.data.length / 4;

    for (let i = 0; i < l; i++) {
      let _r = frame.data[i * 4 + 0];
      let _g = frame.data[i * 4 + 1];
      let _b = frame.data[i * 4 + 2];

      let difference = this.calculateDistance(_r, d_r, l_r) + 
                       this.calculateDistance(_g, d_g, l_g) +
                       this.calculateDistance(_b, d_b, l_b);
      difference /= (255 * 3); // convert to percent
      if (difference < tolerance)
        frame.data[i * 4 + 3] = 0;
    }
    this.ctx2.putImageData(frame, 0, 0);
    return;
  }
};
// :/ 
 Entity18 июл. 2016 г., 00:23
@ Акаш Кумар по направлению к центру всегда намного ярче, чем углы зеленого экрана. Если зеленый цвет еще не отфильтрован, отбросьте его и используйте в качестве нового светлого или темного. Это может занять несколько проходов, но должно занять всего несколько минут.
 Akash Kumar17 июл. 2016 г., 21:13
Действительно хороший пример ... но у меня все еще есть проблемы с выяснением .. Фактически это выглядит так же ... как я могу определить темные и светлые оттенки, глядя на значения RGB.
 Akash Kumar18 июл. 2016 г., 17:14
понял .. нужно немного отрегулировать толерантность ..

вы можете работать в другом цветовом пространстве, например HSV. Вы можете использовать левый верхний пиксель в качестве ссылки.

Вы сравниваете значение цветового тона контрольной точки со значением цветового тона других пикселями, и исключить все пиксели, которые превышают определенный порог и темные и светлые области, используя насыщенность и значение.

Это, однако, не полностью избавляет от цветового кровотечения, там вам может потребоваться сделать некоторую коррекцию цвета / ненасыщенность.

function rgb2hsv () {
    var rr, gg, bb,
        r = arguments[0] / 255,
        g = arguments[1] / 255,
        b = arguments[2] / 255,
        h, s,
        v = Math.max(r, g, b),
        diff = v - Math.min(r, g, b),
        diffc = function(c){
            return (v - c) / 6 / diff + 1 / 2;
        };

    if (diff == 0) {
        h = s = 0;
    } else {
        s = diff / v;
        rr = diffc(r);
        gg = diffc(g);
        bb = diffc(b);

        if (r === v) {
            h = bb - gg;
        }else if (g === v) {
            h = (1 / 3) + rr - bb;
        }else if (b === v) {
            h = (2 / 3) + gg - rr;
        }
        if (h < 0) {
            h += 1;
        }else if (h > 1) {
            h -= 1;
        }
    }
    return {
        h: Math.round(h * 360),
        s: Math.round(s * 100),
        v: Math.round(v * 100)
    };
}


let processor = {
  timerCallback: function() {
    if (this.video.paused || this.video.ended) {
      return;
    }
    this.computeFrame();
    let self = this;
    setTimeout(function () {
        self.timerCallback();
      }, 0);
  },

  doLoad: function() {
    this.video = document.getElementById("video");
    this.c1 = document.getElementById("c1");
    this.ctx1 = this.c1.getContext("2d");
    this.c2 = document.getElementById("c2");
    this.ctx2 = this.c2.getContext("2d");
    let self = this;
    this.video.addEventListener("play", function() {
        self.width = self.video.videoWidth / 2;
        self.height = self.video.videoHeight / 2;
        self.timerCallback();
      }, false);
  },

  computeFrame: function() {
    this.ctx1.drawImage(this.video, 0, 0, this.width, this.height);
    let frame = this.ctx1.getImageData(0, 0, this.width, this.height);
        let l = frame.data.length / 4;


    let reference = rgb2hsv(frame.data[0], frame.data[1], frame.data[2]);

    for (let i = 0; i < l; i++) {
      let r = frame.data[i * 4 + 0];
      let g = frame.data[i * 4 + 1];
      let b = frame.data[i * 4 + 2];
      let hsv = rgb2hsv(r, g, b);

      let hueDifference = Math.abs(hsv.h - reference.h);

      if( hueDifference < 20 && hsv.v > 50 && hsv.s > 50 ) {
        frame.data[i * 4 + 3] = 0;
      }


    }
    this.ctx2.putImageData(frame, 0, 0);
    return;
  }
};
 Akash Kumar18 июл. 2016 г., 19:00
Производительность имеет значение. но я попробую .. в любом случае.

Ваш ответ на вопрос