Как сделать красивый конвертер RGB 2 HSV и обратно?

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Как сделать красивый конвертер RGB 2 HSV и обратно?

Сообщение Alex2013 » 31.07.2018 12:03:21

В общем суть проблемы в следующем .
Есть вот такой неказистый код ...
Код: Выделить всё
// HSV+ -> RGB ( -1 =< (S,V) <=1   -360=<H<=360 )
procedure PlusHSV(Const Src: TBitmap;H,S,V : extended);
Type
RG=Record  B,G,R  : byte; end;
Arr=Array [0..1] of RG;
var
H0,S0,V0 : extended;
R0,G0,B0 : extended;
var
  C:RG;
   //PC:^RG;
   w1,h1,x,y:integer;
   Ps:^Arr;
Procedure TST(var T: extended);
begin
  if (T>1) then T:=1;
  If (T<0) then T:=0;
end;

begin
  W1:=Src.Width ;
  H1:=Src.height;
  for Y:=0 to H1-1 do begin
   Src.BeginUpdate(False);
   Ps:=Src.scanline[y];
   for X:=0 to W1-1 do
    With Ps^[X] do begin
      c:= Ps^[X];
    Try
     R0:=R/255; G0:=G/255; B0:=B/255;
     RGB_to_HSV(R0,G0,B0 ,H0,S0,V0);
     H0:=H0+H;S0:=S0+S;V0:=V0+V;
     HSV_to_RGB(H0,S0,V0,R0,G0,B0);

TST(R0); c.R:=Lo(Round(r0*255 ) );
TST(G0); c.g:=Lo(Round(g0*255 ) );
TST(B0); c.b:=Lo(Round(b0*255 ) );
     finally
      Ps^[X]:=C;
     end;
   end;
   Src.EndUpdate(False);
   end;
end;

//R,G,B  H,S,V - [0.0 - 1.0]
Procedure RGB_to_HSV(R,G,B : extended; var H,S,V : extended);
const undefined=359;

  function max_of(Red,Green,Blue : extended) : extended;
  var max : extended;
  begin
    if red>green then max:=red else max:=green;
    if blue>max then max:=blue;
    max_of:=max;
  end;

  function min_of(Red,Green,Blue : extended) : extended;
  var min : extended;
  begin
    if red<green then min:=red else min:=green;
    if blue<min then min:=blue;
    min_of:=min;
  end;

var Max_value, Min_value,diff,r_dist,g_dist,b_dist : extended;
begin
  max_value:=max_of(R,G,B);
  min_value:=min_of(R,G,B);
  diff:=max_value-min_value;
  V:=max_value;
  if max_value<>0 then s:=(diff / max_value) else s:=0;
  if s=0  then h:=undefined
          else begin
                 r_dist:=(max_value-R) / diff;
                 g_dist:=(max_value-G) / diff;
                 b_dist:=(max_value-B) / diff;
                 if R=max_value
                    then H:=b_dist-g_dist
                    else if G=Max_value
                            then H:=2+r_dist-b_dist
                            else if B=max_value
                                    then H:=4+g_dist-r_dist;
                 H:=H*60;
                 if H<0 then h:=h+360;
               end;
end;

// HSV -> RGB ( 0=< (R,G,B,S,V) <=1 и 0=<H<=360 )
Procedure HSV_to_RGB(H,S,V : extended; var R,G,B : extended);
var f,p,q,t : extended ; i:integer;
begin
  if S=0
   then begin
          R:=V;
          G:=V;
          B:=V;
         end
   else begin
          if H=360 then H:=0;
          H:=H/60;
          I:=trunc(H);  { the integer part of H }
          f:=H-i;
          p:=V*(1-S);
          q:=V*(1-(S*f));
          t:=V*(1-(S*(1-f)));
          case i of
               0: begin R:=V; G:=t; B:=p; end;
               1: begin R:=q; G:=V; B:=p; end;
               2: begin R:=p; G:=V; B:=t; end;
               3: begin R:=p; G:=q; B:=V; end;
               4: begin R:=t; G:=p; B:=V; end;
               5: begin R:=V; G:=p; B:=q; end;
          end;
  end;
end;

..задача которого очень проста : пересчитать для каждого пикселя произвольной картинки RGB -> HSV прибавить/отнять дельту по HSV и вернуть обратно HSV -> RGB ...
Проще некуда ! Вроде работает но ... результат "болен ветрянкой" :wink: то есть сильно пятнист...

В редакторах и разных смотрелках картинок та же самая операция проходит нормально и гладко
(настройка тона, насыщенности и "светлоты"[гамма конкреция] ) ...

Вопрос: как добиться похожего результата ? И откуда взялись "пятна на солнце" ?
Зы
Извиняюсь вначале поспешил и PlusHSV влезла в старой "отладочной версии"...
Кстати интересно и то из за чего я возился с отладкой
Пишу так :
R0:=R/255; G0:=G/255; B0:=B/255;
RGB_to_HSV(R0,G0,B0 ,H0,S0,V0);
Работает ("не шатко не валко" но хоть не вылетает )...
А пишу так :
RGB_to_HSV(R/255, G/255,B/255,H0,S0,V0);
Начинаются разные "чудеса" ... :roll:
Зы Зы
Кстати набор конвертеров на ЯваСкриптах ...
https://raw.githubusercontent.com/e673/ ... convert.js
Alex2013
долгожитель
 
Сообщения: 2923
Зарегистрирован: 03.04.2013 11:59:44

Re: Как сделать красивый конвертер RGB 2 HSV и обратно?

Сообщение runewalsh » 31.07.2018 15:41:15

В предположении, что формулы правильные — я думаю, что насыщенность правильнее умножать, а не прибавлять, наподобие S0 := clamp(S0 * (1 + S), 0, 1). Если у тебя был цвет с маленькой насыщенностью, к примеру, 0–0.02, в нём могут быть случайные и невидимые флуктуации тона, которые ты слишком усиливаешь, добавляя фиксированную S вместо множителя.
Аватара пользователя
runewalsh
энтузиаст
 
Сообщения: 578
Зарегистрирован: 27.04.2010 00:15:25

Re: Как сделать красивый конвертер RGB 2 HSV и обратно?

Сообщение Alex2013 » 31.07.2018 16:02:05

runewalsh писал(а):В предположении, что формулы правильные — я думаю, что насыщенность правильнее умножать, а не прибавлять, наподобие S0 := clamp(S0 * (1 + S), 0, 1). Если у тебя был цвет с маленькой насыщенностью, к примеру, 0–0.02, в нём могут быть случайные и невидимые флуктуации тона, которые ты слишком усиливаешь, добавляя фиксированную S вместо множителя.


Спасибо, попробую !

Хотя у меня есть мысль, что "упор упал" в r0 = r / 255 (и т. д.) и нужно как-то сгладить "гребенку" . :roll:
Alex2013
долгожитель
 
Сообщения: 2923
Зарегистрирован: 03.04.2013 11:59:44

Re: Как сделать красивый конвертер RGB 2 HSV и обратно?

Сообщение runewalsh » 31.07.2018 18:22:29

И вообще я не заметил, чтобы ты клампил значения или брал mod от тона: что если в некоторой точке H=300 и ты хочешь прибавить 180?
Вот мои варианты функций, которые точно правильно работают. Только у меня полагается H∈[0; 1), т. е. как твой, делённый на 360, ну и нужно будет с векторов переделать:
Код: Выделить всё
   function HSVToRGB(const hsv: Vec3): Vec3;
   var
      h, v, vMin, a: float;
   begin
      h := clamp(frac(hsv.x), 0, 1) * 6;
      v := clamp(hsv.z, 0, 1);
      vMin := (1 - clamp(hsv.y, 0, 1)) * v;
      a := (v - vMin) * frac(h);

      case trunc(h) of
         0: result := Vec3.Make(v, vMin + a, vMin);
         1: result := Vec3.Make(v - a, v, vMin);
         2: result := Vec3.Make(vMin, v, vMin + a);
         3: result := Vec3.Make(vMin, v - a, v);
         4: result := Vec3.Make(vMin + a, vMin, v);
         else result := Vec3.Make(v, vMin, v - a);
      end;
   end;

   function RGBToHSV(const rgb: Vec3): Vec3;
   var
      cmax, cmin: float;
   begin
      cmax := max(max(rgb.x, rgb.y), rgb.z);
      cmin := min(min(rgb.x, rgb.y), rgb.z);
      if cmax = cmin then result.x := 0
      else if cmax = rgb.x then
         if rgb.y >= rgb.z then result.x := (rgb.y - rgb.z) / (cmax - cmin) / 6 else result.x := 1 + (rgb.y - rgb.z) / (cmax - cmin) / 6
      else if cmax = rgb.y then result.x := (2/6) + (rgb.z - rgb.x) / (cmax - cmin) / 6
      else result.x := (4/6) + (rgb.x - rgb.y) / (cmax - cmin) / 6;
      if cmax = 0 then result.y := 0 else result.y := 1 - cmin/cmax;
      result.z := cmax;
   end;
Аватара пользователя
runewalsh
энтузиаст
 
Сообщения: 578
Зарегистрирован: 27.04.2010 00:15:25

Re: Как сделать красивый конвертер RGB 2 HSV и обратно?

Сообщение Alex2013 » 31.07.2018 23:54:00

Да разумеется я положился на "авось" но полно примеров где похожий фокус проскакивает .
(Первый вариант чуть помог но пока все равно "небо в горошек"...) :idea: Иду пробовать дальше .... :arrow:
Alex2013
долгожитель
 
Сообщения: 2923
Зарегистрирован: 03.04.2013 11:59:44


Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 30

Рейтинг@Mail.ru