Nadesłany przez Tomasz Lubiński, 30 lipca 2009 01:00
Kod przedstawiony poniżej przedstawia główną część rozwiązania problemu.Pobierz pełne rozwiązanie.
Jeżeli nie odpowiada Ci sposób formatowania kodu przez autora skorzystaj z pretty printer'a i dostosuj go automatycznie do siebie.
Dithering - Delphi/Unit1.pas:
//Tomasz Lubiński (C) 2009
// www.algorytm.org
//Algorytmy Ditheringu
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls, ComCtrls, SHELLAPI;
type
TForm1 = class(TForm)
Image1: TImage;
Label1: TLabel;
Image2: TImage;
Image3: TImage;
Label2: TLabel;
Label3: TLabel;
Button1: TButton;
GroupBox1: TGroupBox;
Label4: TLabel;
Shape1: TShape;
Shape2: TShape;
Shape3: TShape;
Shape4: TShape;
Shape5: TShape;
Shape6: TShape;
Shape7: TShape;
Shape8: TShape;
Shape9: TShape;
Shape10: TShape;
Shape11: TShape;
Shape12: TShape;
Shape13: TShape;
Shape14: TShape;
Shape15: TShape;
Shape16: TShape;
UpDown1: TUpDown;
Edit1: TEdit;
ColorDialog1: TColorDialog;
Label5: TLabel;
ComboBox1: TComboBox;
Label6: TLabel;
procedure Button1Click(Sender: TObject);
procedure Edit1Change(Sender: TObject);
procedure Shape1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormCreate(Sender: TObject);
procedure Label6Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
PPixelRec = ^TPixelRec;
TPixelRec = packed record
B: Byte;
G: Byte;
R: Byte;
Reserved: Byte;
end;
TErrorTable =Array [-3..249] of Array [0..231] of Real;
PErrorTable = ^TErrorTable;
var
Form1: TForm1;
eb, eg, er: TErrorTable;
colors: Array [1..16] of TPixelRec;
dif: Array[0..10] of Array [0..2] of Array [0..5] of Real =
(
(
(0, 0, 0, 0, 7.0/16.0, 0),
(0, 0, 3.0/16.0, 5.0/16.0, 1.0/16.0, 0),
(0, 0 , 0, 0, 0, 0)
),
(
(0, 0, 0, 0, 7.0/48.0, 5.0/48.0),
(0, 3.0/48.0, 5.0/48.0, 7.0/48.0, 5.0/48.0, 3.0/48.0),
(0, 1.0/48.0, 3.0/48.0, 5.0/48.0, 3.0/48.0, 1.0/48.0)
),
(
(0, 0, 0, 0, 8.0/42.0, 4.0/42.0),
(0, 2.0/42.0, 4.0/42.0, 8.0/42.0, 4.0/42.0, 2.0/42.0),
(0, 1.0/42.0, 2.0/42.0, 4.0/42.0, 2.0/42.0, 1.0/42.0)
),
(
(0, 0, 0, 0, 8.0/32.0, 4.0/32.0),
(0, 2.0/32.0, 4.0/32.0, 8.0/32.0, 4.0/32.0, 2.0/32.0),
(0, 0 , 0, 0, 0, 0)
),
(
(0, 0, 0, 0, 7.0/16.0, 0),
(0, 1.0/16.0, 3.0/16.0, 5.0/16.0, 0, 0),
(0, 0 , 0, 0, 0, 0)
),
(
(0, 0, 0, 0, 5.0/32.0, 3.0/32.0),
(0, 2.0/32.0, 4.0/32.0, 5.0/32.0, 4.0/32.0, 2.0/32.0),
(0, 0 , 2.0/32.0, 3.0/32.0, 2.0/32.0, 0)
),
(
(0, 0, 0, 0, 4.0/16.0, 3.0/16.0),
(0, 1.0/16.0, 2.0/16.0, 3.0/16.0, 2.0/16.0, 1.0/16.0),
(0, 0 , 0, 0, 0, 0)
),
(
(0, 0, 0, 0, 2.0/4.0, 0),
(0, 0, 1.0/4.0, 1.0/4.0, 0, 0),
(0, 0, 0, 0, 0, 0)
),
(
(0, 0, 0, 0, 1.0/8.0, 1.0/8.0),
(0, 0, 1.0/8.0, 1.0/8.0, 1.0/8.0, 0),
(0, 0, 0, 1.0/8.0, 0, 0)
),
(
(0, 0, 0, 0, 4.0/8.0, 0),
(0, 1.0/8.0, 1.0/8.0, 2.0/8.0, 0, 0),
(0, 0, 0, 0, 0, 0)
),
(
(0, 0, 0, 0, 8.0/16.0, 0),
(1.0/16.0, 1.0/16.0, 2.0/16.0, 4.0/16.0, 0, 0),
(0, 0, 0, 0, 0, 0)
)
);
implementation
{$R *.DFM}
//---------------------------------------------------------------------------
//pobiera kolory zdefiniowane przez uzytkownika
procedure getColors();
var
n, i: Integer;
begin
for i:=0 to Form1.ComponentCount-1 do
begin
if (Pos('Shape', Form1.Components[i].Name) = 1) then
begin
n := StrToInt(Copy(Form1.Components[i].Name, 6, 2));
colors[n].b := GetBValue(TShape(Form1.Components[i]).Brush.Color);
colors[n].g := GetGValue(TShape(Form1.Components[i]).Brush.Color);
colors[n].r := GetRValue(TShape(Form1.Components[i]).Brush.Color);
colors[n].reserved := 0;
end;
end;
end;
//---------------------------------------------------------------------------
//znajduje kolor w palecie najblizszy zadanemu pikslowi
function findNearest(b: Real; g: Real; r: Real; cnt: Integer): Integer;
var
i: Integer;
d, tmp: Real;
begin
d := (colors[1].b - b) * (colors[1].b - b);
d := d + (colors[1].g - g) * (colors[1].g - g);
d := d + (colors[1].r - r) * (colors[1].r - r);
result := 1;
for i:=2 to cnt do
begin
tmp := (colors[i].b - b) * (colors[i].b - b);
tmp := tmp + (colors[i].g - g) * (colors[i].g - g);
tmp := tmp + (colors[i].r - r) * (colors[i].r - r);
if (d > tmp) then
begin
d := tmp;
result := i;
end;
end;
end;
//---------------------------------------------------------------------------
//ucina podana wartosc do zakresu 0-255 (dopuszczalne wartosci dla piksla)
function clip(x: Real): Real;
begin
if (x > 255) then result := 255
else if (x < 0) then result := 0
else result := x;
end;
//---------------------------------------------------------------------------
//propaguje blad do komorek sasiednich
procedure propagateError(alg: Integer; w: Real; e: PErrorTable; i: Integer; j: Integer);
begin
e[i+1][j ] := e[i+1][j ] + (w*dif[alg][0][4]);
e[i+2][j ] := e[i+2][j ] + (w*dif[alg][0][5]);
e[i-3][j+1] := e[i-3][j+1] + (w*dif[alg][1][0]);
e[i-2][j+1] := e[i-2][j+1] + (w*dif[alg][1][1]);
e[i-1][j+1] := e[i-1][j+1] + (w*dif[alg][1][2]);
e[i ][j+1] := e[i ][j+1] + (w*dif[alg][1][3]);
e[i+1][j+1] := e[i+1][j+1] + (w*dif[alg][1][4]);
e[i+2][j+1] := e[i+2][j+1] + (w*dif[alg][1][5]);
e[i-3][j+2] := e[i-3][j+2] + (w*dif[alg][2][0]);
e[i-2][j+2] := e[i-2][j+2] + (w*dif[alg][2][1]);
e[i-1][j+2] := e[i-1][j+2] + (w*dif[alg][2][2]);
e[i ][j+2] := e[i ][j+2] + (w*dif[alg][2][3]);
e[i+1][j+2] := e[i+1][j+2] + (w*dif[alg][2][4]);
e[i+2][j+2] := e[i+2][j+2] + (w*dif[alg][2][5]);
end;
procedure TForm1.Button1Click(Sender: TObject);
var i,j,cnt,alg: Integer;
pixelOrg, pixelNew: PPixelRec;
begin
//przygotuj wartosci kolorow z palety
getColors();
//przygotuj obrazy wynikowe
Image2.Canvas.Brush.Color := clWhite;
Image2.Canvas.Rectangle(0, 0, Image2.Width, Image2.Height);
Image2.Picture.Bitmap.PixelFormat := pf32Bit;
Image3.Canvas.Brush.Color := clWhite;
Image3.Canvas.Rectangle(0, 0, Image3.Width, Image3.Height);
Image3.Picture.Bitmap.PixelFormat := pf32Bit;
//przygotuj format obrazu zrodlowego
Image1.Picture.Bitmap.PixelFormat := pf32Bit;
//pobierz liczbe kolorow i algorytm do uzycia
cnt := StrToInt(Form1.Edit1.Text);
alg := Form1.ComboBox1.ItemIndex;
//najblizsze sasiedztwo
for j:=0 to 229 do
begin
pixelOrg := Image1.Picture.Bitmap.ScanLine[j];
pixelNew := Image2.Picture.Bitmap.ScanLine[j];
for i:=0 to 247 do
begin
pixelNew^ := colors[findNearest(pixelOrg.b, pixelOrg.g, pixelOrg.r, cnt)];
Inc(pixelOrg);
Inc(pixelNew);
end;
end;
//dithering
for j:=0 to 231 do
for i:=-2 to 249 do
begin
eb[i,j] := 0;
eg[i,j] := 0;
er[i,j] := 0;
end;
for j:=0 to 229 do
begin
pixelOrg := Image1.Picture.Bitmap.ScanLine[j];
pixelNew := Image3.Picture.Bitmap.ScanLine[j];
for i:=0 to 247 do
begin
pixelNew^ := colors[findNearest(clip(pixelOrg.b + eb[i,j]),
clip(pixelOrg.g + eg[i,j]),
clip(pixelOrg.r + er[i,j]), cnt)];
propagateError(alg, clip(pixelOrg.b + eb[i,j]) - pixelNew.b , @eb, i, j);
propagateError(alg, clip(pixelOrg.g + eg[i,j]) - pixelNew.g , @eg, i, j);
propagateError(alg, clip(pixelOrg.r + er[i,j]) - pixelNew.r , @er, i, j);
Inc(pixelOrg);
Inc(pixelNew);
end;
end;
end;
//---------------------------------------------------------------------------
//Pokazywanie i ukrywanie probek z palety barw
procedure TForm1.Edit1Change(Sender: TObject);
var
cnt, n, i: Integer;
begin
cnt := StrToInt(Edit1.Text);
for i:=0 to Form1.ComponentCount-1 do
begin
if Pos('Shape', Components[i].Name) = 1 then
begin
n := StrToInt(Copy(Components[i].Name, 6, 2));
if (n <= cnt) then TShape(Components[i]).Visible := true
else TShape(Components[i]).Visible := false;
end;
end;
end;
//---------------------------------------------------------------------------
// Dialog umozliwiajacy zmiane palety barw
procedure TForm1.Shape1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if (ColorDialog1.Execute()) then
begin
TShape(Sender).Brush.Color := ColorDialog1.Color;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Form1.ComboBox1.ItemIndex := 0;
end;
procedure TForm1.Label6Click(Sender: TObject);
begin
with (Sender as Tlabel) do
ShellExecute(Application.Handle,
PChar('open'),
PChar(Hint),
PChar(0),
nil,
SW_NORMAL);
end;
end.

