Интерполяция цвета

КонстантинБ
Offline
Зарегистрирован: 02.11.2017

Здравствуйте, делаю подсветку на адресной ленте - плавное переливание светодиодов. Использую библиотеку FastLed

#define NUM_LEDS 1
#include "FastLED.h"
#include "lib8tion.h"
#define PIN 3
CRGB leds[NUM_LEDS];
CHSV targetColor[NUM_LEDS];

void setup()
{
  for (uint16_t i = 0; i < NUM_LEDS; i++)
  {
    targetColor[i] = CHSV(random(0, 255), random(0, 255), random(0, 255));
  }
  Serial.begin(9600);
  randomSeed(2648469515618); // read noise from component for set rnd seed

  FastLED.addLeds<WS2811, PIN, GRB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
  FastLED.setBrightness(255);
  pinMode(PIN, OUTPUT);
}

CHSV RgbToHsv(CRGB rgb)
{
  CHSV hsv;
  unsigned char rgbMin, rgbMax;

  rgbMin = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b);
  rgbMax = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b);

  hsv.v = rgbMax;
  if (hsv.v == 0)
  {
    hsv.h = 0;
    hsv.s = 0;
    return hsv;
  }

  hsv.s = 255 * long(rgbMax - rgbMin) / hsv.v;
  if (hsv.s == 0)
  {
    hsv.h = 0;
    return hsv;
  }

  if (rgbMax == rgb.r)
    hsv.h = 0 + 43 * (rgb.g - rgb.b) / (rgbMax - rgbMin);
  else if (rgbMax == rgb.g)
    hsv.h = 85 + 43 * (rgb.b - rgb.r) / (rgbMax - rgbMin);
  else
    hsv.h = 171 + 43 * (rgb.r - rgb.g) / (rgbMax - rgbMin);

  return hsv;
}

CHSV InterpolateColor(CHSV a, CHSV b)
{
  float ah = a.h;
  float as = a.s;
  float av = a.v;
  float bh = b.h;
  float bs = b.s;
  float bv = b.v;
  
  ah = ah + (bh - ah) * 0.3;
  as = as + (bs - as) * 0.3;
  av = av + (bv - av) * 0.3;

  return CHSV(ah, as, av);
}

void loop()
{
  for (uint16_t i = 0; i < NUM_LEDS; i++)
  {
    CHSV col = RgbToHsv(leds[i]);

    col = InterpolateColor(col, targetColor[i]);

    Serial.print(col.h);
    Serial.print(" ");
    Serial.print(col.s);
    Serial.print(" ");
    Serial.println(col.v);

    Serial.print(targetColor[i].h);
    Serial.print(" ");
    Serial.print(targetColor[i].s);
    Serial.print(" ");
    Serial.println(targetColor[i].v);

    if (
      abs(col.h - targetColor[i].h) < 4 &&
      abs(col.s - targetColor[i].s) < 4 &&
      abs(col.v - targetColor[i].v) < 4)
    {
      targetColor[i] = CHSV(random(0, 255), random(0, 255), random(0, 255));
    }

    leds[i] = col;
  }

  delay(10);

  FastLED.show();
}

В один момент цвета перестают меняться.

 

Проверил ту же формулу на числе uint8_t все работает. В чем проблема?

andriano
andriano аватар
Онлайн
Зарегистрирован: 20.06.2015

Какую формулу?

КонстантинБ
Offline
Зарегистрирован: 02.11.2017

Функция считает по формуле a+(b-a) * t где 0<= t <=1

andriano
andriano аватар
Онлайн
Зарегистрирован: 20.06.2015

Так а что не работает?

 

КонстантинБ
Offline
Зарегистрирован: 02.11.2017

КонстантинБ пишет:

В один момент цвета перестают меняться.

 

Проверил ту же формулу на числе uint8_t все работает. В чем проблема?

Есть цвет пикселя - случайный. Есть цвет пикселя который нам нужен - тоже случайный. Оно должно плавно переливаться из цвета который уже имеется в тот, который нам нужен. Цвет несколько раз меняется, а потом перестает, не дойдя до нужного значения. Не понимаю почему

rkit
Онлайн
Зарегистрирован: 23.11.2016

В fastled уже есть всё необходимое. Не надо ничего самому изобретать.

КонстантинБ
Offline
Зарегистрирован: 02.11.2017

rkit пишет:

В fastled уже есть всё необходимое. Не надо ничего самому изобретать.

Я нашел функцию lerp8by8, но не понял как ей дать значение frac меньше 1. Покажите пример если не сложно

andriano
andriano аватар
Онлайн
Зарегистрирован: 20.06.2015

КонстантинБ пишет:

Цвет несколько раз меняется, а потом перестает, не дойдя до нужного значения. Не понимаю почему

Программа работает в точности так, как она написана.

Расставьте комментарии в тексте. Может, сами найдете проблему, а может, мы найдем, где код не совпадает с комментариями. Гадать, как должно быть, и прочему в коде написано именно так, а не иначе (не зная, как должно быть), - дело неблагодарное.

b707
Offline
Зарегистрирован: 26.05.2017

Переведите цвет в CHS и вообще ничего делать не надо

КонстантинБ
Offline
Зарегистрирован: 02.11.2017
#define NUM_LEDS 1 //количество светодиодов в ленте. 1 - для простоты отладки
#include "FastLED.h"
#include "lib8tion.h"
#define PIN 3
CRGB leds[NUM_LEDS]; // текущий цвет светодиодов
CHSV targetColor[NUM_LEDS]; // требуемый цвет светодиодов

void setup()
{
  for (uint16_t i = 0; i < NUM_LEDS; i++)
  {
    targetColor[i] = CHSV(random(0, 255), random(0, 255), random(0, 255)); // случайно заполняем требуемые цвета
  }

  Serial.begin(9600);

  randomSeed(2648469515618); // read noise from component for set rnd seed

  FastLED.addLeds<WS2811, PIN, GRB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );//инициализируем ленту
  FastLED.setBrightness(255); //ставим макс яркость
  pinMode(PIN, OUTPUT);
}

CHSV RgbToHsv(CRGB rgb)//преобразование ргб в хсв. Точно рабочий, много где использовал - проблем небыло
{
  CHSV hsv;
  unsigned char rgbMin, rgbMax;

  rgbMin = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b);
  rgbMax = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b);

  hsv.v = rgbMax;
  if (hsv.v == 0)
  {
    hsv.h = 0;
    hsv.s = 0;
    return hsv;
  }

  hsv.s = 255 * long(rgbMax - rgbMin) / hsv.v;
  if (hsv.s == 0)
  {
    hsv.h = 0;
    return hsv;
  }

  if (rgbMax == rgb.r)
    hsv.h = 0 + 43 * (rgb.g - rgb.b) / (rgbMax - rgbMin);
  else if (rgbMax == rgb.g)
    hsv.h = 85 + 43 * (rgb.b - rgb.r) / (rgbMax - rgbMin);
  else
    hsv.h = 171 + 43 * (rgb.r - rgb.g) / (rgbMax - rgbMin);

  return hsv;
}

CHSV InterpolateColor(CHSV a, CHSV b) // функция интерполяции
{
  float ah = a.h; // берем тон цвета a. a - тот который есть
  float as = a.s; // берем насыщенность а
  float av = a.v; // берем яркость а
  float bh = b.h; // берем тон б. б - тот который нужен
  float bs = b.s; // берем насыщенность
  float bv = b.v; // берем яркость

  ah = ah + (bh - ah) * 0.3; // считаем
  as = as + (bs - as) * 0.3;
  av = av + (bv - av) * 0.3;

  return CHSV(ah, as, av);//возвращаем новый цвет
}

void loop()
{
  for (uint16_t i = 0; i < NUM_LEDS; i++) //проходим по всем светодиодам ленты
  {
    CHSV col = RgbToHsv(leds[i]); //берем ргб цвет, переводим в хсв

    col = InterpolateColor(col, targetColor[i]); // интерполируем

    Serial.print(col.h); //отладочная информация
    Serial.print(" ");
    Serial.print(col.s);
    Serial.print(" ");
    Serial.println(col.v);

    Serial.print(targetColor[i].h);
    Serial.print(" ");
    Serial.print(targetColor[i].s);
    Serial.print(" ");
    Serial.println(targetColor[i].v);

    if (
      abs(col.h - targetColor[i].h) < 4 &&
      abs(col.s - targetColor[i].s) < 4 &&
      abs(col.v - targetColor[i].v) < 4) // сравниваем получившийся цвет и тот который нужен с допуском 4
    {
      targetColor[i] = CHSV(random(0, 255), random(0, 255), random(0, 255)); // если совпало - меняем нужный нам цвет
    }

    leds[i] = col; // присваиваем полученный цвет тому, который отображается
  }

  delay(10); //ждем для плавнности

  FastLED.show(); // показываем
}

andriano пишет:

Программа работает в точности так, как она написана.

Да, с простым числом все работает так, как оно и должно. Но с цветом что то не так, хотя действия те же самые.

andriano
andriano аватар
Онлайн
Зарегистрирован: 20.06.2015

Программа всегда работает так, как написана.

1. Как должна работать программа?

2. Как она на самом деле работает?

3. Чем 1 отличается от 2?

Bruzzer
Offline
Зарегистрирован: 17.03.2020

Я не в теме FastLed. Может поэтому для меня не очевидно, почему в строке 077 вы присваиваете через преобразование, а в строке 101 без преобразования.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Фастлед в одну сторону сам умеет конвертить цветовые пространства.

КонстантинБ
Offline
Зарегистрирован: 02.11.2017

andriano пишет:

Программа всегда работает так, как написана.

1. Как должна работать программа?

2. Как она на самом деле работает?

3. Чем 1 отличается от 2?

1. должна плавно менять цвет н-ого числа светодиодов по hue до требуемого

2. плавно меняет цвет светодиода, но не доходит до требуемого

3. цвет не доходит до требуемого значения

КонстантинБ
Offline
Зарегистрирован: 02.11.2017

Bruzzer пишет:

Я не в теме FastLed. Может поэтому для меня не очевидно, почему в строке 077 вы присваиваете через преобразование, а в строке 101 без преобразования.

077: я не нашел как из rgb сделать hsv цвет в библиотеке fastled. 

101: если переменной с типом CRGB присваивать переменную с типом CHSV то все работает, но не наоборот

b707
Offline
Зарегистрирован: 26.05.2017

Константин, несколько вопросов по коду

1. Если у вас вся работа с цветом в CHSV, зачем начальный цвет в RGB ?

CRGB leds[NUM_LEDS]; // текущий цвет светодиодов
CHSV targetColor[NUM_LEDS]; // требуемый цвет светодиодов

задайте сразу весь цвет в CHSV и сложная конверсия будет не нужна. Я не вникал в детали вашей процедуры RgbToHsv() - но в ней легко может быть ошибка. А задав цвет в CHSV вы заведомо исключите этот источник проблем.

2. Теперь интерполяция цвета

CHSV InterpolateColor(CHSV a, CHSV b) // функция интерполяции
{
  float ah = a.h; // берем тон цвета a. a - тот который есть
  float as = a.s; // берем насыщенность а
  float av = a.v; // берем яркость а
  float bh = b.h; // берем тон б. б - тот который нужен
  float bs = b.s; // берем насыщенность
  float bv = b.v; // берем яркость

  ah = ah + (bh - ah) * 0.3; // считаем
  as = as + (bs - as) * 0.3;
  av = av + (bv - av) * 0.3;

  return CHSV(ah, as, av);//возвращаем новый цвет
}

зачем компоненты цвета имеют тип float? - они же целые

И почему в вашем понимании интерполяция - это именно 0.3 от разницы компонентов цвета? - логичнее было бы 0.5

Если же вы хотите интерполяцию за несколько шагов, то нужно разделить разность величин ah и bh на число отрезков, равное числу шагов... А число 0.3 - это ни к селу ни к городу...

КонстантинБ
Offline
Зарегистрирован: 02.11.2017

b707 пишет:

Константин, несколько вопросов по коду

1. Если у вас вся работа с цветом в CHSV, зачем начальный цвет в RGB ?

CRGB leds[NUM_LEDS]; // текущий цвет светодиодов
CHSV targetColor[NUM_LEDS]; // требуемый цвет светодиодов

1. При инициализации ленты 

FastLED.addLeds<WS2811, PIN, GRB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );

в качестве массива светодиодов принимает только массив с типом CRGB

b707 пишет:

RgbToHsv() - но в ней легко может быть ошибка

Сомневаюсь. Использую этот фрагмент где только можно - все работает.   

b707 пишет:

зачем компоненты цвета имеют тип float? - они же целые

если при расчетах, как я понимаю, использовать целые числа, то оно будет округляться в ближайшую сторону. А если использовать float, то округление произойдет в конце и потерь точности будет меньше. Думал из-за этого не работает, но нет.

b707 пишет:

И почему в вашем понимании интерполяция - это именно 0.3 от разницы компонентов цвета? - логичнее было бы 0.5

так переливание выглядит плавнее.

b707 пишет:

Если же вы хотите интерполяцию за несколько шагов, то нужно разделить разность величин ah и bh на число отрезков, равное числу шагов... А число 0.3 - это ни к селу ни к городу...

Попробую если заработает

andriano
andriano аватар
Онлайн
Зарегистрирован: 20.06.2015

Константин, везде по исходнику, где успользуется цвет в том или ином виде, вставьте комментарии, какая цветовая модель используется.

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

И еще, Вы уверены, что (106, 16, 90) и (158, 19, 232) - это разные цвета, а не один и тот же в разных цветовых пространствах?

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Надо ещё учитывать, что в HSV Fastled-а спектр на байте повторяется.

 

КонстантинБ
Offline
Зарегистрирован: 02.11.2017

andriano пишет:

Константин, везде по исходнику, где успользуется цвет в том или ином виде, вставьте комментарии, какая цветовая модель используется.

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

И еще, Вы уверены, что (106, 16, 90) и (158, 19, 232) - это разные цвета, а не один и тот же в разных цветовых пространствах?

Проверил, все модели правильные, а при интерполяции rgb конвертируется в hsv

Да, это разные цвета.

andriano
andriano аватар
Онлайн
Зарегистрирован: 20.06.2015

Я бы еще распечатал значения :

- col и leds после строки 77,

- значения (разности) передаваемые в функцию abs() в строках 94-96,

- значения targetColor после строки 98, 

- col и leds после строки 101.

 
Bruzzer
Offline
Зарегистрирован: 17.03.2020

Автор пытается Значение хранящиеся в RGB преобразовывать в HSV, интерполировть к значению хранящемуся в HSV и снова преобразовывать из HSV в RGB для сохранения. При этом преобразование RGB -> HSV -> RGB приводит к существенно другому RGB.