unit FunUnit;

interface

uses MyGlobal, Math;

procedure TransformRGB3(Mean: Real;var VR,VG,VB: Word);
procedure MoveLeft(var Data: TData; const index: Integer; var len: Integer);
procedure GammaCorrection(var R, G, B: Word; gamma: Real; lowval, highval: Word);
procedure AdjustRGB(var R, G, B: Word; lowval, highval: Word);

function QuickSelect(k: Integer; var Data: TData; len: Integer): Integer; Overload;
function QuickSelect(k: Integer; var Data: TBigData; len: Integer): Integer; Overload;
function QuickMedian(var Data: TData; len: Integer): Word; Overload;
function QuickMedian(var Data: TBigData; len: Integer): Word; Overload;
function Avg(const Data: TData; len: Integer): Real; Overload;
function Avg(const Data: TBigData; len: Integer): Real; Overload;
function MedDev(var Data: TData; len: Integer): Real;

implementation

procedure TransformRGB3(Mean: Real;var VR,VG,VB: Word);
var V: Longint;
    R,G,B,M,l,E: Double;
    Q,MaxR: Double;
    Max: Integer;
begin
  Max:=65535;
  V:=VR+VG+VB;
  if (V<>0) then
    begin
      if Mean<>0 then
        begin
          Mean:=Mean*3;
          Q:=Mean/V;
          R:=Q*VR;
          G:=Q*VG;
          B:=Q*VB;
          M:=R;
          if (G>M) then M:=G;
          if (B>M) then M:=B;
          MaxR:=Max;
          if M-0.0000001>MaxR then
            begin
              l:=(Mean-3*Max)/(R+G+B-3*M);
              if l=0 then
                begin
                  VR:=Max;
                  VG:=Max;
                  VB:=Max;
                end
              else
                begin
                  E:=(Max-l*M)/l;
                  VR:=Round(l*(R+E));
                  VG:=Round(l*(G+E));
                  VB:=Round(l*(B+E));
                end;
            end
          else
            begin
              VR:=Round(R);
              VG:=Round(G);
              VB:=Round(B);
            end;
        end
      else
        begin
          VR:=0; VG:=0; VB:=0;
        end;
    end
  else
    begin
      VR:=Round(Mean); VG:=Round(Mean); VB:=Round(Mean);
    end;
end;


function QuickSelect(k: Integer; var Data: TData; len: Integer): Integer; Overload;   // oznaci k-ty nejmensi prvek
var left, right, i, pos: Integer;
    pivot: Word;

  procedure SwapData(a: Integer; b: Integer);
  var t: Word;
  begin
    t := Data[a];
    Data[a] := Data[b];
    Data[b] := t;
  end;

begin
  left := 0;
  right := len - 1;

  while (left < right) do
  begin
    pivot := Data[k];
    SwapData(k, right);
    pos := left;
    for i := left to right - 1 do
      if (Data[i] < pivot) then
        begin
          SwapData(i, pos);
          inc(pos);
        end;
    SwapData(right, pos);
    if (pos = k) then break;
    if (pos < k) then left := pos + 1
      else right := pos - 1;
  end;
  Result := Data[k];
end;

function QuickSelect(k: Integer; var Data: TBigData; len: Integer): Integer; Overload;   // oznaci k-ty nejmensi prvek
var left, right, i, pos: Integer;
    pivot: Word;

  procedure SwapData(a: Integer; b: Integer);
  var t: Word;
  begin
    t := Data[a];
    Data[a] := Data[b];
    Data[b] := t;
  end;

begin
  left := 0;
  right := len - 1;

  while (left < right) do
  begin
    pivot := Data[k];
    SwapData(k, right);
    pos := left;
    for i := left to right - 1 do
      if (Data[i] < pivot) then
        begin
          SwapData(i, pos);
          inc(pos);
        end;
    SwapData(right, pos);
    if (pos = k) then break;
    if (pos < k) then left := pos + 1
      else right := pos - 1;
  end;
  Result := Data[k];
end;

function QuickMedian(var Data: TData; len: Integer): Word; Overload;
begin
  if (len AND 1 = 0) then // Sudy pocet prvku, median je prumer prostrednich dvou
    Result := Round((QuickSelect(len div 2, Data, len) + QuickSelect((len div 2) - 1, Data, len)) / 2)
    else // Lichy pocet prvku, vybereme prostredni
      Result := QuickSelect(len div 2, Data, len);
end;

function QuickMedian(var Data: TBigData; len: Integer): Word; Overload;
begin
  if (len AND 1 = 0) then // Sudy pocet prvku, median je prumer prostrednich dvou
    Result := Round((QuickSelect(len div 2, Data, len) + QuickSelect((len div 2) - 1, Data, len)) / 2)
    else // Lichy pocet prvku, vybereme prostredni
      Result := QuickSelect(len div 2, Data, len);
end;

function Avg(const Data: TData; len: Integer): Real; Overload;
var i, sum: Integer;
begin
  sum := 0;
  for i := 0 to len - 1 do
    sum := sum + Data[i];
  Result := sum/len;
end;

function Avg(const Data: TBigData; len: Integer): Real; Overload;
var i: Integer;
    sum: Int64;
begin
  sum := 0;
  for i := 0 to len - 1 do
    sum := sum + Data[i];
  Result := sum/len;
end;

function MedDev(var Data: TData; len: Integer): Real;
var i: Integer;
    variance, elem, sum: Real;
    med: Word;
begin
  sum := 0;
  med := QuickMedian(Data, len);
  for i := 0 to len - 1 do
    begin
      elem := Data[i];
      sum := sum + sqr(elem - med);
    end;
  variance := sum / len;
  Result := sqrt(variance);
end;

procedure MoveLeft(var Data: TData; const index: Integer; var len: Integer);
var i: Integer;
begin
  if index > len - 1 then
    Exit
    else if index = len - 1 then
           begin
             dec(len);
             Exit;
           end;
  for i := index to len - 2 do
    Data[i] := Data[i + 1];
  dec(len);
end;

procedure GammaCorrection(var R, G, B: Word; gamma: Real; lowval, highval: Word);
var v: Real;
begin
  gamma := 1/gamma;
  v := (R + G + B)/3;    //jas pixelu
  v := (v - lowval) / (highval - lowval);      //jas v omezenem intervalu v (0,1)
  v := power(v, gamma)*65535;  //zmenim ho pomoci mocninne funkce s exponentem gamma
  TransformRGB3(v, R, G, B);
end;

procedure AdjustRGB(var R, G, B: Word; lowval, highval: Word);
var v: Real;
begin
  v := (R + G + B)/3;    //jas pixelu
  if (v < lowval) OR (v > highval) then
    begin
      if v < lowval then v := lowval + 1
        else if v > highval then v := highval - 1;
      TransformRGB3(v, R, G, B);
    end;
end;

end.
