Конференция "Media" » HSV, Scanline, Bitmap (Delphi) [D7, WinXP]
 
  • Илья_666 (29.09.14 21:25) [0]
    Всем доброго времени суток!

    Появилась такая проблема: необходимо изменить HSV изображения.
    С этим вроде окей - нашел формулы в википедии, постарался, перевел их (кажется, корректно) в язык и работает все. Но только через Pixels[x,y]. Это, безусловно, дико медленно, если изображение имеет размеры более 50х50 пикселей.
    В связи с этим делом решил изучить метод Scanline.
    Погуглил, нашел интересные сайты. Вроде бы понял, что это такое и с чем едят, но только не смог понять, как адаптировать полученные знания к текущей задаче ибо примеры давались довольно простенькие, но ничего специфичного не объяснялось).

    Код, работающий через Pixels[x, y]:

    var
     i,j: integer;
     R,G,B: real;
     H,S,V: real;
    begin
     with Bitmap do
     begin
       for i:=0 to Bitmap2.Width-1 do
       for j:=0 to Bitmap2.Height-1 do
       begin
           ColorToRGB(Pixels[i,j],R,G,B);
           RGBtoHSV(R,G,B,H,S,V);
           S:=S*TrackBar6.Position/100;
           V:=V*TrackBar7.Position/50;
           H:=TrackBar5.Position;
           HSVtoRGB(H,S,V,R,G,B);
           Bitmap2.Canvas.Pixels[i,j]:=RGBtoColor(R,G,B);
       end;
     end;
     Image2.Canvas.Draw(0, 0, Bitmap2);



    "Переделанный" код под Scanline:

    var
       R,G,B: real;
       H,S,V: real;
       RowOut: PRGBArray;
       x, y: integer;
       AColor: integer;
       _R, _G, _B: byte;
    begin
       Bitmap2.PixelFormat := pf24bit;
     with Bitmap2.Canvas do
     begin
       for y:=0 to Bitmap2.Height-1 do
       begin
         RowOut := Bitmap2.ScanLine[y];
         for x:=0 to Bitmap2.Width-1 do
         begin
         _R := GetRValue(RowOut[x].rgbtRed);
         _G := GetGValue(RowOut[x].rgbtGreen);
         _B := GetBValue(RowOut[x].rgbtBlue);
         AColor := RGBToColor(_R, _G, _B);
         ColorToRGB(AColor,R,G,B);
         RGBtoHSV(R,G,B,H,S,V);
         S:=S*TrackBar1.Position/100;
         V:=V*TrackBar2.Position/50;
         H:=TrackBar3.Position;
         HSVtoRGB(H,S,V,R,G,B);
         RowOut[x].rgbtRed := GetRValue(RGBToColor(R, G, B));
         RowOut[x].rgbtGreen := GetGValue(RGBToColor(R, G, B));;
         RowOut[x].rgbtBlue := GetBValue(RGBToColor(R, G, B));
       end;
     end;
    end;
     Image1.Canvas.Draw(0, 0, Bitmap2);



    Самопальные процедуры:

    procedure ColorToRGB(Color: integer; var R,G,B: real);
    begin
     R:=(Color and $0000FF)/255;
     G:=((Color and $00FF00) shr 8)/255;
     B:=((Color and $FF0000) shr 16)/255;
    end;

    function RGBtoColor(R,G,B: real): integer;
    begin
     Result:=Trunc(B*255) shl 16+Trunc(G*255) shl 8+Trunc(R*255);
    end;



    Собственно, не пойму, почему, когда я использую Scanline, то в пременные _R, _G, _B не передаются данные о цвете пикселя? Когда пытаюсь использовать процедуру изменения HSV со Scanline, то изображение пропадает и все (после теста я понял, что оно просто окрашивается белым цветом, получаемым из RGBToColor).

    Заранее спасибо за дельные советы.
  • Pavia © (29.09.14 22:17) [1]
    _R := GetRValue(RowOut[x].rgbtRed);
         _G := GetGValue(RowOut[x].rgbtGreen);
         _B := GetBValue(RowOut[x].rgbtBlue);
    зачем дважды 1 метод применять? Либо одно либо другой

        _R := RowOut[x].rgbtRed;
         _G :=RowOut[x].rgbtGreen;
         _B :=RowOut[x].rgbtBlue;

        _R := GetRValue(RowOut[x]);
         _G := GetGValue(RowOut[x]);
         _B := GetBValue(RowOut[x]);
  • Илья_666 (30.09.14 15:36) [2]

    >
    >     _R := GetRValue(RowOut[x]);
    >      _G := GetGValue(RowOut[x]);
    >      _B := GetBValue(RowOut[x]);

    Получить составляющие цвета из указтеля на пикселы невозможно.


    >     _R := RowOut[x].rgbtRed;
    >      _G :=RowOut[x].rgbtGreen;
    >      _B :=RowOut[x].rgbtBlue;

    Этот вариант не работает, ибо пробовал его.
    В принципе, только поэтому и задал вопрос здесь, на форуме
  • DQ (30.09.14 16:36) [3]
    Немного странный код. Зачем-то GetRValue и "and $00FF00"... Как и откуда грузите картинку?
    Вот самое интересное откуда берутся Bitmap и Bitmap2 ?
  • Илья_666 (30.09.14 18:22) [4]

    > Немного странный код. Зачем-то GetRValue и "and $00FF00".
    > .. Как и откуда грузите картинку?
    > Вот самое интересное откуда берутся Bitmap и Bitmap2 ?

    Битмапы создаются при создании формы:

           BitMap:=TBitMap.Create;
    BitMap2:=TBitMap.Create;
    Bitmap2.Width:=Bitmap.Width;
    Bitmap2.Height:=Bitmap.Height;



    Картинку грузим при нажатии на кнопку "Открыть":

    if OpenPictureDialog1.Execute then begin
       BitMap.LoadFromFile(OpenPictureDialog1.FileName);
       Image1.Canvas.Draw(0, 0, Bitmap);
       Image2.Canvas.Draw(0, 0, Bitmap);
       Bitmap2.Width:=Bitmap.Width;
       Bitmap2.Height:=Bitmap.Height;
    end;



    Мне непонятно, почему, когда я вот тут:

        _R := RowOut[x].rgbtRed;
        _G := RowOut[x].rgbtGreen;
        _B := RowOut[x].rgbtBlue;



    пытаюсь присвоить цвет пикселей переменным, цвет всегда равен (255, 255, 255) независимо от изображения.

    Когда работа идет через Pixels[x,y], то, по идее, происходит то же самое, но используя Scanline, я этого не смог добиться.
    Однако, если я просто напишу:

        RowOut[x].rgbtRed := 255;
        RowOut[x].rgbtGreen := 255;
        RowOut[x].rgbtBlue := 0;


    то, закономерно, получу желтый цвет.
  • Pavia © (30.09.14 20:32) [5]

    > пытаюсь присвоить цвет пикселей переменным, цвет всегда
    > равен (255, 255, 255) независимо от изображения.Когда работа
    > идет через Pixels[x,y], то, по идее, происходит то же самое,
    >  но используя Scanline, я этого не смог добиться.Однако,
    >  если я просто напишу:

    Ну так всё правильно. Вы же из пустой картинке читаете. Поэтому там и 255.
    Вы же в Bitmap2 после создание ничего не положили. А при использование Pixels вы читаете из другой картинке из Bitmap.

    Надо просто скопировать в Bitmap в Btamp2

    По хорошему лучше 2 указателя. RowIn и RowOut

      for y:=0 to Bitmap2.Height-1 do
       begin
         RowIn := Bitmap.ScanLine[y];
         RowOut := Bitmap2.ScanLine[y];
         for x:=0 to Bitmap2.Width-1 do
         begin
         _R := RowIn[x].rgbtRed;
         _G := RowIn[x].rgbtGreen;
         _B := RowIn[x].rgbtBlue;
         AColor := RGBToColor(_R, _G, _B);
         ColorToRGB(AColor,R,G,B);
         RGBtoHSV(R,G,B,H,S,V);
         S:=S*TrackBar1.Position/100;
         V:=V*TrackBar2.Position/50;
         H:=TrackBar3.Position;
         HSVtoRGB(H,S,V,R,G,B);
         RowOut[x].rgbtRed := GetRValue(RGBToColor(R, G, B));
         RowOut[x].rgbtGreen := GetGValue(RGBToColor(R, G, B));;
         RowOut[x].rgbtBlue := GetBValue(RGBToColor(R, G, B));
         end;
      end;

  • Pavia © (30.09.14 20:35) [6]
    И последние 3 строчки надо упростить, не нужно туда сюда конвертировать цвета.

         RowOut[x].rgbtRed :=R
         RowOut[x].rgbtGreen := G;
         RowOut[x].rgbtBlue := B;
  • Илья_666 (01.10.14 11:28) [7]

    > Pavia

    Спасибо ОГРОМНОЕ за помощь!
    Я немного переделал код, поскольку небольшая ошибка искажает изображение:

        AColor := RGBToColor(_R, _G, _B);


    и ее надо заменить на:

        AColor := RGB(Round(R), Round(G), Round(B));



    Также "усовершенствовал" эту часть:

        RowOut[x].rgbtRed := GetRValue(RGBToColor(R, G, B));
        RowOut[x].rgbtGreen := GetGValue(RGBToColor(R, G, B));;
        RowOut[x].rgbtBlue := GetBValue(RGBToColor(R, G, B));


       
    просто замените ее на это:

        RowOut[x].rgbtRed := GetRValue(AColor);
        RowOut[x].rgbtGreen := GetGValue(AColor);
        RowOut[x].rgbtBlue := GetBValue(AColor);



    К тому же, я избавился от переменных _R, _G, _B.
    Теперь все работает как часы.
    Собственно, сам работающий код:

    var
       R,G,B: real;
       H,S,V: real;
       RowOut, RowIn: PRGBArray;
       x, y: integer;
       AColor: integer;
    begin
       Bitmap.PixelFormat := pf24bit;
       Bitmap2.PixelFormat := pf24bit;
    with Bitmap do
    begin
     for y:=0 to Bitmap2.Height - 1 do
     begin
       RowIn := Bitmap.ScanLine[y];
       RowOut := Bitmap2.ScanLine[y];
         for x:=0 to Bitmap2.Width - 1 do
         begin
           R := RowIn[x].rgbtRed;
           G := RowIn[x].rgbtGreen;
           B := RowIn[x].rgbtBlue;
           AColor := RGB(Round(R), Round(G), Round(B));
           ColorToRGB(AColor, R, G, B);
           RGBtoHSV(R, G, B, H, S, V);
           S := S * TrackBar1.Position / 100;  //saturation
           V := V * TrackBar2.Position / 50;   //value
           H := TrackBar3.Position;               //HUE
           HSVtoRGB(H, S, V, R, G, B);
           AColor := RGBtoColor(R, G, B);
           RowOut[x].rgbtRed := GetRValue(AColor);
           RowOut[x].rgbtGreen := GetGValue(AColor);
           RowOut[x].rgbtBlue := GetBValue(AColor);
         end;
     end;
    end;
    end;



    Процедура и функция ColorToRGB и RGBToColor приведены в первом сообщении темы.
    Процедуры RGBToHSV и HSVToRGB можно найти либо в интернете, либо просто немного подумать и перевести формулы, которые есть в википедии.
    (даже я, далеко не математик осилил их :)).

    Всем спасибоза участие!
  • MBo © (01.10.14 13:34) [8]
    убери из циклов наружу TrackBar.Position
  • invis © (01.10.14 14:47) [9]
    Плавающую точку нужно убрать и лишние преобразования (RGB/ColorToRGB). TrackBar.Position скорее с т.з. чистоты кода, а по скорости не так важно, там свойство без геттера.
  • Илья_666 (01.10.14 15:19) [10]

    > убери из циклов наружу TrackBar.Position

    Да, исправил, спасибо за замечание. А какие могут быть негативные последствия оставления операций с расчетами TrackBar.Position's в цикле?
  • MBo © (01.10.14 15:27) [11]
    >А какие могут быть негативные последствия оставления операций с расчетами TrackBar.Position's в цикле?
    Замедление, иногда огромное
  • Илья_666 (01.10.14 16:05) [12]

    > Плавающую точку нужно убрать и лишние преобразования (RGB/ColorToRGB).
    >  TrackBar.Position скорее с т.з. чистоты кода, а по скорости
    > не так важно, там свойство без геттера.

    Так с ней вроде все окей, с плавающей точкой.
    А вот лишние преобразования убрал, Вы правы - они были совсем не к месту.
    Спасибо за указание!
  • invis © (01.10.14 18:43) [13]
    Компилятор Дельфи не очень эффективно работает с плавающей точкой, поэтому целые почти всегда быстрее. Плюс ещё преобразования целые-вещественные занимают какое-то время.
    Сейчас проверил - преобразование RGB->HSV в 2 раза быстрее в целых. Из-за разных режимов округления результат немного отличается (на 1-2 единицы).
  • Pavia © (01.10.14 21:26) [14]

    > Компилятор Дельфи не очень эффективно работает с плавающей
    > точкой, поэтому целые почти всегда быстрее. Плюс ещё преобразования
    > целые-вещественные занимают какое-то время.Сейчас проверил
    > - преобразование RGB->HSV в 2 раза быстрее в целых. Из-за
    > разных режимов округления результат немного отличается (на
    > 1-2 единицы).

    А у вас какая скорость? У меня 40 тактов на пиксель.
  • invis © (03.10.14 01:51) [15]
    Такты не считал и вообще не сильно упирался с оптимизацией, просто переделал функцию с efg2 на целые.

    type
     PFColor =^TFColor;
     TFColor = packed record
       b,g,r: Byte;
     end;

    procedure RGB2HSV(Src : PFColor; Out H, S, V : Integer);
    Const HSV_MAX_MUL = 360 shl 8;
    Var Min, Max, Diff : Integer;
    begin
    H := 0; S := 0; V := 0;
    If (Src.r < Src.g) and (Src.r < Src.b) then Min := Src.r else
     If (Src.g < Src.b) then Min := Src.g else Min := Src.b;
    If (Src.r > Src.g) and (Src.r > Src.b) then Max := Src.r else
     If (Src.g > Src.b) then Max := Src.g else Max := Src.b;

    Diff := Max - Min;
    If (Max <> 0) and (Diff <> 0) then begin
     S := ((Diff shl 8) div Max);
     If S > 255 then S := 255;
     If Src.r = Max then
       H := ((Src.g - Src.b) shl 8) div Diff
     else If Src.g = Max then
       H := $200 + ((Src.b - Src.r) shl 8) div Diff
     else
       H := $400 + ((Src.r - Src.g) shl 8) div Diff;

     H := H * 60;
     If H < 0 then Inc(H, HSV_MAX_MUL) else
       If H >= HSV_MAX_MUL then Dec(H, HSV_MAX_MUL);
     H := H shr 8;
     V := Max;
    end;
    end;

  • Илья_666 (06.10.14 08:22) [16]
    В принципе, тема раскрыта, хорошие мысли есть - можно закрывать)))
    Всем спасибо!
 
Конференция "Media" » HSV, Scanline, Bitmap (Delphi) [D7, WinXP]
Есть новые Нет новых   [134427   +37][b:0][p:0.005]