⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 stdquant.pas

📁 ·ImageEn 2.3.0 ImageEn一组用于图像处理、查看和分析的Delphi控件。能够保存几种图像格式
💻 PAS
字号:
(*
Copyright (c) 1998-2007 by HiComponents. All rights reserved.

This software comes without express or implied warranty.
In no case shall the author be liable for any damage or unwanted behavior of any
computer hardware and/or software.

HiComponents grants you the right to include the compiled component
in your application, whether COMMERCIAL, SHAREWARE, or FREEWARE,
BUT YOU MAY NOT DISTRIBUTE THIS SOURCE CODE OR ITS COMPILED .DCU IN ANY FORM.

ImageEn, IEvolution and ImageEn ActiveX may not be included in any commercial,
shareware or freeware libraries or components.

email: support@hicomponents.com

http://www.hicomponents.com
*)

unit stdquant;

{$R-}
{$Q-}

{$I ie.inc}

interface

uses Windows, Graphics, classes, sysutils, imageen, ImageEnProc, hyiedefs;

function CreateMedianCutQuantizer(SrcBitmap: TObject; var ColorMap: array of TRGB; NCol: integer): pointer;
procedure FreeMedianCutQuantizer(mq: pointer);
function MedianCutFindIndex(mq: pointer; const rgb: TRGB): integer;

implementation

uses hyieutils;

{$R-}

const
  ERR_CANCEL = 128;
  ERR_NOMEMORY = 139;
  RedI = 0;
  GreenI = 1;
  BlueI = 2;
  Bits = 5;
  cBits = 8 - Bits;
  ColorMaxI = 1 shl Bits;
  cHistogramm = ColorMaxI * ColorMaxI * ColorMaxI;

type

  TTrueColor = record
    Blue: byte;
    Green: byte;
    Red: Byte;
  end;

  PRGBByteArray = ^TRGBByteArray;
  TRGBByteArray = array[0..32767] of TTrueColor;

  tDMCoSi = record
    pqBMI: TObject;
    cqWid: word;
    cqHei: word;
    czWid: word;
    czHei: word;
  end;

  tMean = array[RedI..BlueI] of double;
  tFreqZeile = array[0..ColorMaxI - 1] of longint;
  tFreqArray = array[RedI..BlueI] of tFreqZeile;
  tLowHigh = array[RedI..BlueI] of integer;
  pBox = ^tBox;
  tBox = record
    WeiVar: double;
    mean: tMean;
    weight: longint;
    Freq: tFreqArray;
    low: tLowHigh;
    high: tLowHigh;
  end;

  pBoxes = ^tBoxes;
  tBoxes = array[0..255] of tBox;
  pHistogramm = ^tHistogramm;
  tHistogramm = array[0..cHistogramm - 1] of longint;

  pRGBmap = ^tRGBmap;
  tRGBmap = array[0..cHistogramm - 1] of byte;

  PGVar = ^TGVar;
  TGVar = record
    pHisto: pHistogramm;
    pBoxArr: pBoxes;
    pMap: pRGBmap;
    cHBRPix: longint;
    cHBRCol: longint;
    cHBROutCol: longint;
    DMCoSi: tDMCoSi;
  end;

  /////////////////////////////////////////////////////////////////////////////

function Histogramm(gvar: PGVar): boolean;
var
  p24: PRGBByteArray;
  h: integer;
  r, g, b: byte;
  y, x: integer;
begin
  p24 := nil;
  with gvar^ do
  begin
    with pBoxArr^[0] do
    begin
      fillchar(Freq[RedI], sizeof(tFreqZeile), #0);
      fillchar(Freq[GreenI], sizeof(tFreqZeile), #0);
      fillchar(Freq[BlueI], sizeof(tFreqZeile), #0);
      for y := 0 to DMCoSi.czHei - 1 do
      begin
        if DMCoSi.pqBMi is TBitmap then
          p24 := (DMCoSi.pqBMi as TBitmap).Scanline[y]
        else if DMCoSi.pqBMi is TIEBitmap then
          p24 := (DMCoSi.pqBMi as TIEBitmap).Scanline[y];
        for x := 0 to DMCoSi.czWid - 1 do
        begin
          r := p24[x].Red shr cBits;
          inc(Freq[RedI, r]);
          g := p24[x].Green shr cBits;
          inc(Freq[GreenI, g]);
          b := p24[x].Blue shr cBits;
          inc(Freq[BlueI, b]);
          h := r shl Bits;
          h := (h or g) shl Bits;
          h := h or b;
          inc(pHisto^[h]);
        end;
      end;
    end;
    Result := true;
  end;
end;

/////////////////////////////////////////////////////////////////////////////

procedure BoxStats(gvar: pgvar; var pn: tBox);
var
  mean1, vari1: double;
  hw: double;
  i, col: integer;
begin
  with gvar^ do
  begin
    pn.WeiVar := 0.0;
    if (pn.Weight = 0) then
      exit;
    for col := RedI to BlueI do
    begin
      vari1 := 0.0;
      mean1 := 0.0;
      for i := pn.Low[col] to pn.High[col] - 1 do
      begin
        hw := pn.Freq[col, i];
        hw := hw * i;
        mean1 := mean1 + hw;
        hw := hw * i;
        vari1 := vari1 + hw;
      end;
      pn.Mean[col] := mean1 / pn.Weight;
      hw := pn.mean[col];
      hw := hw * hw * pn.Weight;
      hw := vari1 - hw;
      pn.WeiVar := pn.WeiVar + hw;
    end;
    pn.WeiVar := pn.WeiVar / cHBRPix;
  end;
end;

/////////////////////////////////////////////////////////////////////////////

function FindCutPoint(gvar: pgvar; var pn, nBox1, nBox2: tBox; RGB: byte): boolean;
var
  u, v, max: double;
  hw: double;
  OptWei: longint;
  CurWei: longint;
  myfreq: longint;
  h: integer;
  rOff, gOff: integer;
  i, CutPt: integer;
  maxIdx, minIdx: integer;
  l1, l2, h1, h2: integer;
  b, g, r: byte;
begin
  with gvar^ do
  begin
    Result := false;
    if (pn.Low[RGB] + 1 = pn.High[RGB]) then
      exit;
    MinIdx := round((pn.Mean[RGB] + pn.Low[RGB]) * 0.5);
    MaxIdx := round((pn.Mean[RGB] + pn.High[RGB]) * 0.5);
    CutPt := MinIdx;
    OptWei := pn.Weight;
    CurWei := 0;
    for i := pn.Low[RGB] to MinIdx - 1 do
      CurWei := CurWei + longint(pn.Freq[RGB, i]);
    u := 0.0;
    Max := -1.0;
    for i := MinIdx to MaxIdx do
    begin
      inc(CurWei, pn.Freq[RGB, i]);
      if (CurWei = pn.Weight) then
        break;
      hw := i;
      hw := (hw * pn.Freq[RGB, i]) / pn.Weight;
      u := u + hw;
      hw := pn.Mean[RGB];
      hw := hw - u;
      hw := hw * hw;
      v := CurWei;
      v := (v / (pn.Weight - CurWei)) * hw;
      if (v > max) then
      begin
        max := v;
        CutPt := i;
        OptWei := CurWei;
      end;
    end;
    inc(CutPt);
    Move(pn, nBox1, sizeof(tBox));
    Move(pn, nBox2, sizeof(tBox));
    nBox1.Weight := OptWei;
    nBox2.Weight := nBox2.Weight - OptWei;
    if (nBox1.Weight = 0) or (nBox2.Weight = 0) then
    begin
      exit;
    end;
    nBox1.High[RGB] := CutPt;
    nBox2.Low[RGB] := CutPt;
    fillchar(nBox1.Freq[RedI], sizeof(tFreqZeile), #0);
    fillchar(nBox1.Freq[GreenI], sizeof(tFreqZeile), #0);
    fillchar(nBox1.Freq[BlueI], sizeof(tFreqZeile), #0);
    for r := nBox1.Low[RedI] to nBox1.High[RedI] - 1 do
    begin
      rOff := r shl Bits;
      for g := nBox1.Low[GreenI] to nBox1.High[GreenI] - 1 do
      begin
        gOff := (rOff or g) shl Bits;
        for b := nBox1.Low[BlueI] to nBox1.High[BlueI] - 1 do
        begin
          h := gOff or b;
          myfreq := pHisto^[h];
          if (myfreq <> 0) then
          begin
            inc(nBox1.Freq[RedI, r], myfreq);
            inc(nBox1.Freq[GreenI, g], myfreq);
            inc(nBox1.Freq[BlueI, b], myfreq);
            dec(nBox2.Freq[RedI, r], myfreq);
            dec(nBox2.Freq[GreenI, g], myfreq);
            dec(nBox2.Freq[BlueI, b], myfreq);
          end;
        end;
      end;
    end;
    for r := RedI to BlueI do
    begin
      l1 := ColorMaxI;
      l2 := ColorMaxI;
      h1 := 0;
      h2 := 0;
      for g := 0 to ColorMaxI - 1 do
      begin
        if (nBox1.Freq[r, g] <> 0) then
        begin
          if (g < l1) then
            l1 := g;
          if (g > h1) then
            h1 := g;
        end;
        if (nBox2.Freq[r, g] <> 0) then
        begin
          if (g < l2) then
            l2 := g;
          if (g > h2) then
            h2 := g;
        end;
      end;
      nBox1.Low[r] := l1;
      nBox2.Low[r] := l2;
      nBox1.High[r] := h1 + 1;
      nBox2.High[r] := h2 + 1;
    end;
    BoxStats(gvar, nBox1);
    BoxStats(gvar, nBox2);
    Result := true;
  end;
end;

/////////////////////////////////////////////////////////////////////////////

function CutBox(gvar: pgvar; var pn, nBox1: tBox): boolean;
const
  Hugo = 1.7 * 10308;
var
  i: integer;
  TotVar: array[RedI..BlueI] of double;
  nBoxes: array[RedI..BlueI, 0..1] of tBox;
begin
  with gvar^ do
  begin
    if (pn.WeiVar = 0.0) or (pn.Weight = 0) then
    begin
      pn.WeiVar := 0.0;
      Result := false;
      exit;
    end
    else
      Result := true;
    for i := RedI to BlueI do
    begin
      if (FindCutPoint(gvar, pn, nBoxes[i, 0], nBoxes[i, 1], i)) then
        TotVar[i] := nBoxes[i, 0].WeiVar + nBoxes[i, 1].WeiVar
      else
        TotVar[i] := Hugo;
    end;
    if (TotVar[RedI] < Hugo)
      and (TotVar[RedI] <= TotVar[GreenI])
      and (TotVar[RedI] <= TotVar[BlueI]) then
    begin
      Move((nBoxes[RedI, 0]), pn, sizeof(tBox));
      Move((nBoxes[RedI, 1]), nBox1, sizeof(tBox));
      exit;
    end
    else if (TotVar[GreenI] < Hugo)
      and (TotVar[GreenI] <= TotVar[RedI])
      and (TotVar[GreenI] <= TotVar[BlueI]) then
    begin
      Move((nBoxes[GreenI, 0]), pn, sizeof(tBox));
      Move((nBoxes[GreenI, 1]), nBox1, sizeof(tBox));
      exit;
    end
    else if (TotVar[BlueI] < Hugo) then
    begin
      Move((nBoxes[BlueI, 0]), pn, sizeof(tBox));
      Move((nBoxes[BlueI, 1]), nBox1, sizeof(tBox));
      exit;
    end;
    pn.WeiVar := 0.0;
    Result := false;
  end;
end;

/////////////////////////////////////////////////////////////////////////////

function CutBoxes(gvar: pgvar): integer;
var
  CurBox, n, i: integer;
  Max: double;
begin
  with gvar^ do
  begin
    with pBoxArr^[0] do
    begin
      Low[RedI] := 0;
      Low[GreenI] := 0;
      Low[BlueI] := 0;
      High[RedI] := ColorMaxI;
      High[GreenI] := ColorMaxI;
      High[BlueI] := ColorMaxI;
      Weight := cHBRPix;
    end;
    BoxStats(gvar, pBoxArr^[0]);
    CurBox := 1;
    while (CurBox < cHBRCol) do
    begin
      n := CurBox;
      max := 0.0;
      for i := 0 to CurBox - 1 do
        with pBoxArr^[i] do
        begin
          if (WeiVar > Max) then
          begin
            Max := WeiVar;
            n := i;
          end;
        end;
      if (n = CurBox) then
        break;
      if (CutBox(gvar, pBoxArr^[n], pBoxArr^[CurBox])) then
        inc(CurBox);
    end;
    Result := CurBox;
  end;
end;

/////////////////////////////////////////////////////////////////////////////

function MakeRGBmap(gvar: pgvar): boolean;
var
  i, p: integer;
  r, g, b: integer;
  rOff, gOff: integer;
begin
  with gvar^ do
  begin
    for i := 0 to cHBROutCol - 1 do
      with pBoxArr^[i] do
      begin
        for r := Low[RedI] to High[RedI] - 1 do
        begin
          rOff := r shl Bits;
          for g := Low[GreenI] to High[GreenI] - 1 do
          begin
            gOff := (rOff or g) shl Bits;
            for b := Low[BlueI] to High[BlueI] - 1 do
            begin
              p := gOff or b;
              pMap^[p] := i;
            end;
          end;
        end;
      end;
    Result := true;
  end;
end;

/////////////////////////////////////////////////////////////////////////////

function GetHBRmem(gvar: pgvar): boolean;
begin
  with gvar^ do
  begin
    GetMem(pHisto, sizeof(tHistogramm));
    GetMem(pBoxArr, sizeof(tBoxes));
    GetMem(pMap, sizeof(tRGBmap));
    fillchar(pHisto^, sizeof(tHistogramm), #0);
    fillchar(pBoxArr^, sizeof(tBoxes), #0);
    fillchar(pMap^, sizeof(tRGBmap), #0);
    Result := true;
  end;
end;

/////////////////////////////////////////////////////////////////////////////

procedure FreeHBRmem(gvar: pgvar);
begin
  with gvar^ do
  begin
    if (pHisto <> nil) then
    begin
      FreeMem(pHisto);
      pHisto := nil;
    end;
    if (pBoxArr <> nil) then
    begin
      FreeMem(pBoxArr);
      pBoxArr := nil;
    end;
    if (pMap <> nil) then
    begin
      FreeMem(pMap);
      pMap := nil;
    end;
  end;
end;

/////////////////////////////////////////////////////////////////////////////
// SrcBitmap can be TIEBitmap or TBitmap

function CreateMedianCutQuantizer(SrcBitmap: TObject; var ColorMap: array of TRGB; NCol: integer): pointer;
var
  gvar: PGVar;
  i: integer;
begin
  getmem(gvar, sizeof(TGVar));
  with gvar^ do
  begin
    fillchar(DMCoSi, sizeof(tDMCoSi), #0);
    pHisto := nil;
    pBoxArr := nil;
    pMap := nil;
    with DMCoSi do
    begin
      pqBMI := SrcBitmap;
      if pqBMi is TBitmap then
      begin
        with pqBMI as TBitmap do
        begin
          czWid := Width;
          czHei := Height;
        end;
      end
      else if pqBMi is TIEBitmap then
      begin
        with pqBMI as TIEBitmap do
        begin
          czWid := Width;
          czHei := Height;
        end;
      end;
      cHBRPix := czWid;
      cHBRPix := cHBRPix * czHei;
      cHBRCol := ncol;
    end;
    GetHBRmem(gvar);
    Histogramm(gvar);
    cHBROutCol := CutBoxes(gvar);
    for i := 0 to cHBROutCol - 1 do
      with pBoxArr^[i] do
      begin
        ColorMap[i].r := round(Mean[RedI]) shl cBits;
        ColorMap[i].g := round(Mean[GreenI]) shl cBits;
        ColorMap[i].b := round(Mean[BlueI]) shl cBits;
      end;
  end;
  MakeRGBmap(gvar);
  result := gvar;
end;

/////////////////////////////////////////////////////////////////////////////

procedure FreeMedianCutQuantizer(mq: pointer);
begin
  with PGVar(mq)^ do
    FreeHBRmem(PGVar(mq));
  freemem(mq);
end;

/////////////////////////////////////////////////////////////////////////////

function MedianCutFindIndex(mq: pointer; const rgb: TRGB): integer;
var
  b, g, r, p: integer;
begin
  with PGVar(mq)^ do
  begin
    r := (rgb.r and $F8) shl (Bits + Bits - cBits);
    g := (rgb.g and $F8) shl (Bits - cBits);
    b := (rgb.b and $F8) shr cBits;
    p := r or g or b;
    result := pmap^[p];
  end;
end;

end.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -