有人想要24位图象转化为256色图象的Code,我贴这里了.(100分)

  • 主题发起人 主题发起人 lha
  • 开始时间 开始时间
L

lha

Unregistered / Unconfirmed
GUEST, unregistred user!
// 写的很匆忙 可能效率不高 请见谅 --- LHA 2001.11.7
// Function: 24位颜色的BMP 转换为8位(256色)的BMP
//
// 它的思想是:准备一个长度为4096的数组,代表4096种颜色。
// 对图中的每一个像素,取R,G,B的最高四位,拼成一个12位的整数,
// 对应的数组元素加1。全部统计完后,就得到了这4096种颜色的使用频率。
// 这其中,可能有一些颜色一次也没用到,即对应的数组元素为零
// (假设不为零的数组元素共有M个)。将这些为零的数组元素清除出去,
// 使得前M个元素都不为零。将这M个数按从大到小的顺序排列,这样,
// 前256种颜色就是用的最多的颜色,它们将作为调色板上的256种颜色。
// 对于剩下的M-256种颜色并不是简单的丢弃,而是用前256种颜色中的
// 一种来代替,代替的原则是找有最小平方误差的那个。

// 存在的问题: 在该算法中 只取了R、G、B的最高四位,
// 这样剩下的几位被舍去,会使图像亮度降低。
// 当也可以取全R、G、B的八位,那样效率太低。
// 我们可以加上一个小于16的随机数来补偿。

unit CUnit2;

interface
uses
Windows, Graphics;
type
PRGBColor = ^TRGBColor;
TRGBColor = record
B, G, R: Byte;
end;
PByte = ^Byte;
LColor = Record
Color ,Times : Integer;
end;

procedure Convert(SBitmap : TBitMap ; var DBitMap : TBitMap) ;

implementation

var
ColorCount : array[0..4096] of LColor; //为记录颜色使用频率的数组
ColorTable : array[0..4096] of Byte; // 为记录颜色索引值的数组

//统计颜色使用频率
procedure CountColor(BitMap : TBitMap;Var ClrCount : array of LColor);
var
Ptr : PRGBColor;
i,j : Integer;
CIndex : Integer;
begin
for i := 0 to 4096 do // 初始化ColorCount数组
begin
ClrCount.Color := i;
ClrCount.Times := 0;
end;

with BitMap do
for i := 0 to ( Height - 1 ) do
begin
Ptr := ScanLine;
for j := 0 to (Width - 1) do
begin //取 R、G、B三种颜色的前4位组成12位,共4096种颜色
CIndex := (Ptr.R and $0F0) shl 4;
CIndex := CIndex + (Ptr.G and $0F0);
CIndex := CIndex + ((Ptr.B and $0F0) shr 4);
Inc(ClrCount[CIndex].Times,1); //计算颜色的使用次数
Inc(Ptr);
end;
end;
end;//procedure CountColor

// 清除使用次数为 0 的颜色数据,返回值为当前图像中颜色的种类
function Delzero(Var ClrCount : array of LColor): Integer;
var i,CIndex : Integer;
begin
CIndex := 0;
for i := 0 to 4096 do
begin
if (ClrCount.Times <> 0) then
begin
ClrCount[CIndex] := ClrCount;
ClrCount.Times := 0;
Inc(CIndex);
end;
end;
Result := CIndex;
end;//function Delzero

// 快速排序, 将各种颜色 按使用的频率排序(Hight -- Low )
procedure Sort(var A: array of LColor; Top : Integer);

procedure QuickSort(var A: array of LColor; iLo, iHi: Integer);
var
Lo, Hi, Mid: Integer;
Temp : LColor;
begin
Lo := iLo;
Hi := iHi;
Mid := A[(Lo + Hi) div 2].Times;
repeat
while A[Lo].Times > Mid do Inc(Lo);
while A[Hi].Times < Mid do Dec(Hi);
if Lo <= Hi then
begin
Temp := A[Lo];
A[Lo] := A[Hi];
A[Hi] := Temp;
Inc(Lo);
Dec(Hi);
end;
until Lo > Hi;
if Hi > iLo then QuickSort(A, iLo, Hi);
if Lo < iHi then QuickSort(A, Lo, iHi);
end;

begin
QuickSort(A, Low(A), Top);
end;

// 构建调色表
function BuildColorTable(var ClrCount : array of LColor;
var Pal :PLogPalette):HPalette;
var i : Integer;
begin
Pal.palVersion:=$300;
Pal.palNumEntries:=256;
for i := 0 to 255 do
begin
Pal.palPalEntry.peRed := ((ClrCount.Color and $0F00) shr 4) + 7;
Pal.palPalEntry.peGreen := (ClrCount.Color and $0F0) + 7;
Pal.palPalEntry.peBlue := ((ClrCount.Color and $00F) shl 4) + 7;
pal.palPalEntry.peFlags := 0;
end;
Result := CreatePalette(Pal^);
end;


//根据统计的信息调整图像中的颜色, 将不常用的颜色用常用的颜色代替
procedure AdjustColor(ClrNumber : Integer; ClrCount : array of LColor);
var i ,C,Error,m: Integer;
CIndex : Byte;
begin
// for i := 0 to 4096 do ColorTable := 0;
for i := 0 to 255 do
ColorTable[ClrCount.Color] := i;

for i := 256 to ClrNumber do
begin
Error := 10000;
CIndex := 0;
C := ClrCount.Color;
for m := 0 to 255 do
if abs(ClrCount[m].Color - C) < Error then
begin
Error := abs(ClrCount[m].Color - C);
CIndex := m;
end;
ColorTable[ClrCount.Color] := CIndex;
end;
end;//procedure AdjustColor

procedure Convert(SBitmap : TBitMap; var DBitMap: TBitMap) ;
var
Pal: PLogPalette;
i , j , t, ColorNumber: integer;
SPtr : PRGBColor;
DPtr : PByte;
begin
if (SBitMap.Empty) then
Exit;

CountColor(SBitMap,ColorCount); //统计颜色的使用频率
ColorNumber := DelZero(ColorCount); //去处不使用的颜色
Sort(ColorCount,ColorNumber); // 将颜色按使用频率排序
AdjustColor(ColorNumber,ColorCount);

With DBitMap do
begin
PixelFormat := pf8bit;
SBitMap.PixelFormat := pf24bit;
Width := SBitMap.Width;
Height := SBitMap.Height;

GetMem(pal, sizeof(TLogPalette) + sizeof(TPaletteEntry) * 255);
BuildColorTable(ColorCount,Pal);
Palette := BuildColorTable(ColorCount,Pal); // Set DBitMap.Palette
FreeMem(pal);

for i := 0 to ( Height - 1 ) do
begin
SPtr := SBitMap.ScanLine;
DPtr := ScanLine;
for j := 0 to (Width - 1) do
begin
t := (SPtr.R and $0F0) shl 4;
t := t + (SPtr.G and $0F0);
t := t + ((SPtr.B and $0F0) shr 4);
DPtr^ := ColorTable[t];
Inc(SPtr);
Inc(DPtr);
end;
end;

end;
end; //procedure Convert

end.


/////////////////////////////
在主程序中调用
uses CUnit2;
...
procedure TForm1.Button1Click(Sender: TObject);
begin
if OpenDialog1.Execute then
Image1.Picture.LoadFromFile(OpenDialog1.FileName);
end;

procedure TForm1.Button2Click(Sender: TObject);
var Bmp : TBitMap;

begin
Bmp := TBitMap.Create;
// Bmp.Assign(Image1.Picture.Bitmap);
Convert(Image1.Picture.Bitmap,Bmp);
PaintBox1.Canvas.Draw(0,0,Bmp);
Bmp.Free;
end;

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

累死我了:(
 
OK 继续灌水!

unit 24bitto256;
{$A-}
interface

uses
Sysutils, Classes, Dialogs, Graphics;

procedure DitherImage(FullBMP : TBitmap);

implementation

var
CurrBMP : TBitmap;
CurrPxlPos : Integer;
CmpRVal,
CmpGVal,
CmpBVal : Byte;
RunRErr,
RunGErr,
RunBErr : SmallInt;

function DitherPixel(ABmp.Canvas.Pixels(Row, Col) : TColor) : TColor;
var
I, J, K : Integer;
RedVal,
GreenVal,
BlueVal : Byte;
CurrRErr,
CurrGErr,
CurrBErr : SmallInt;
MyClr : TColor;

begin
CurrRErr := 0;
CurrGErr := 0;
CurrBErr := 0;
RedVal := ABmp.Canvas.Pixels(Row,Col).Red;
GreenVal := ABmp.Canvas.Pixels(Row,Col).Green;
BlueVal := ABmp.Canvas.Pixels(Row,Col).Blue;
CmpRVal := ABmp.Canvas.Pixels(Row, Col + 1).Red;
CmpGVal := ABmp.Canvas.Pixels(Row, Col + 1).Green;
CmpBVal := ABmp.Canvas.Pixels(Row, Col + 1).Blue;
CurrRErr := CurrRErr + (RedVal - CmpRVal) div 16;
CurrGErr := CurrGErr + (GreenVal - CmpGVal) div 16;
CurrBErr := CurrBErr + (BlueVal - CmpBVal) div 16;
CmpRVal := ABmp.Canvas.Pixels(Row + 1, Col - 1).Red;
CmpGVal := ABmp.Canvas.Pixels(Row + 1, Col - 1).Green;
CmpBVal := ABmp.Canvas.Pixels(Row+ 1, Col - 1).Blue;
CurrRErr := CurrRErr + ((RedVal - CmpRVal) div 16) * 7;
CurrGErr := CurrGErr + ((GreenVal - CmpGVal) div 16) * 7;
CurrBErr := CurrBErr + ((BlueVal = CmpBVal)) div 16) * 7;
CmpRVal := ABmp.Canvas.Pixels(Row + 1, Col).Red;
CmpGVal := ABmp.Canvas.Pixels(Row + 1, Col).Green;
CmpBVal := ABmp.Canvas.Pixels(Row + 1, Col).Blue;
CurrRErr := CurrRErr + ((RedVal - CmpRVal) div 16) * 5;
CurrGErr := CurrGErr + ((GreenVal - CmpGVal) div 16) * 5;
CurrBErr := CurrBErr + ((BlueVal - cmpBVal) div 16) * 5;
CmpRVal := ABmp.Canvas.Pixels(Row + 1, Col + 1).Red;
CmpGVal := ABmp.Canvas.Pixels(Row + 1, Col + 1).Green;
CmpBVal := ABmp.Canvas.Pixels(Row + 1, Col + 1).Blue;
CurrRErr := CurrRErr + ((RedVal - CmpRVal) div 16) * 3;
CurrGErr := CurrGErr + ((GreenVal - CmpGVal) div 16) * 3;
CurrBErr := CurrBErr + ((BlueVal - CmpBVal) div 16) * 3;
RunRErr := RunRErr + CurrRErr;
RunGErr := RunGErr + CurrGErr;
RunBErr := RunBErr + CurrBErr;
MyClr.Red := RedVal + RunRErr;
MyClr.Green := GreenVal + RunGErr;
MYClr.Blue := BlueVal + RunBErr;
DitherPixel := MyClr;
end;

procedure DitherLine(TheBMP : TBitmap; LineCntr : Integer);

begin
For J := 0 to TheBMP.Width - 1 do
begin
TheBMP.Canvas.Pixels(LineCntr, J) := DitherPxl(TheBMP.Canvas.Pixels(LineCntr, J));
end;
end;

procedure DitherImage(FullBMP : TBitmap);
var
I : Integer;

begin
CurrBMP := TBitmap.Create;
CurrBMP.Assign(FullBMP);
RunErr := 0;
for I := 0 to FullBMP.Height - 1 do
begin
DitherLine(CurrBMP, I);
end;
FullBMP.Assign(CurrBMP);
CurrBMP.Free;
end;

end.
 
好!我要!
 
谢谢!:)
 
YB_unique用的是经典的Floyed-Steinberg抖动算法,其中心思想是把误差按比例分给相邻的像素.
类似的方法还有Stucki算法:
x 8 4
2 4 8 4 2
1 2 4 2 1
Burkes算法:
x 8 4
2 4 8 4 2
其实我个人更倾向于用模式匹配的方法,效率很高. 比如Bayer模式.
 
lha的算法有值得商量的地方:
我觉得新图象的256色不一定非得从原图的分布概率最高的里面取.如果一辐图像颜色比较少
(接近256色),这种算法的效果会很好,但更一般的情况则不然.我认为应该在求出分布的基础
上决定新生成256种颜色,前提是使得变换后的方差最小.
 
哎,这里成了热帖,可怜我自己的问题
http://www.delphibbs.com/delphibbs/dispq.asp?lid=696660
 
to LeeChange:
我这个算法当然不是最佳的算法,但是最容易懂,也简单,效率也不差。

>>我认为应该在求出分布的基础上决定新生成256种颜色,前提是使得变换后的方差最小.
可是,按什么分布呢? 你这种算法,和我上面的算法其实是差不多的,你可以比较一下
看看结果如何。
 
其实,还要考虑的一点是,每种颜色的亮度不一样,因此给人的影响也不一样。
因此我们还可以考虑,给亮度高的颜色加权。 这种算法也很烦。
 
to lha:
咱俩用的算法,在前半部分(既求原图象的概率密度函数部分)是一样的,只是在后面产生256
种新颜色时有分歧.在一般的情况下,方差小的效果好.
 
好!!!

辛苦你们这些大虾了!!!!!
 
》lha,
在刷新的时候,抖动的很厉害。
 
to Tense:
》》在刷新的时候,抖动的很厉害
什么意思?这个转换过程在0.x秒就搞定了,哪里来的刷新?
就象彩色图象转换为灰度图象,瞬间就搞定了(300*300的图象,CPU K6-266)
什么时候会看到抖动?
 
to Tense:
当然要"抖动的很厉害",要不然怎么把颜色数降下来.
 
>>lha:
>>什么意思?这个转换过程在0.x秒就搞定了,哪里来的刷新?
我加的一个刷新按钮,调用后发现的。
 
哈哈!好东西!不过还要改进!
 
如果你像DirectX一样使用与设备无关位图(DIB)作为原始图形格式进行位图转换,速度会有很大的提高!
我有一个算法就是基于此之上写的,所以我觉得你可以从这方面也改进一下(除了算法之外)。
 
to YB_unique:
//使用与设备无关位图(DIB)作为原始图形格式进行位图转换
能否指点一二?
 
DIB,英文即Device-indepentent bitmap,中文即与设备无关的位图。
DIB的颜色模式与设备无关。例如,一个256色的DIB即可以在真彩色显示模式下使用,也可以在16色模式下使用。
256色以下(包括256色)的DIB拥有自己的颜色表,像素的颜色独立于系统调色板。
由于DIB不依赖于具体设备,因此可以用来永久性地保存图象。DIB一般是以*.BMP文件的形式保存在磁盘中的,有
时也会保存在*.DIB文件中。运行在不同输出设备下的应用程序可以通过DIB来交换图象。
DIB还可以用一种RLE算法来压缩图像数据,但一般来说DIB是不压缩的。
以上为SDK中关于DIB的官方说明!
呵呵!它的应用很广,在游戏,图象,打印等等领域都有独到之处。在DirectX中还专门封装了对其操作的函数。
你可以找一些DIB控件看看,很多带有源码,关于它的编程不是一言两语说得清的,你先自己看看,OK?

 
 
后退
顶部