

![]() |
![]() |
Goals and Process |
|
One of the biggest hurdles to working with the internet is that there's a limit on how much information can be sent to someone at a time. Additionally, the people who run websites are usually only allowed to send so much information over the internet per month.
Thus, it's important for webmasters to balance the quality of their images with the size of the image files. One way to do this is to be aggressive with compression - for JPEGs, this is known as the Quality setting, with lower values increasing how much the algorythum is willing to discard. The goal of this project, therefore, is simply to show how much someone can afford to compress an image before too much is lost. |
![]() |
Source Code |
|
In order to help produce the files needed for this project, three small programs where created using Free Pascal. Each of these programs has a single, specific function, and their source code is shown below.
Crusher.pas
{
Crusher rapidly saves a file 2000 times, in an attempt
to force the artifacting caused by lossy compression to
become more obvious and to test if repeatedly saving an
image really will deteriorate it substantially.
}
uses fpimage, fpreadjpeg, fpwritejpeg, sysutils;
procedure CrushImage( Source, Dest : AnsiString );
var
Img : TFPMemoryImage;
Reader : TFPReaderJPEG;
Writer : TFPWriterJPEG;
begin
Img := TFPMemoryImage.Create(1,1);
Reader := TFPReaderJPEG.Create;
Writer := TFPWriterJPEG.Create;
Writer.CompressionQuality := 75;
Img.LoadFromFile(Source, Reader);
Img.SaveToFile(Dest, Writer);
Img.Free;
Reader.Free;
Writer.Free;
end;
var
i : Longint;
begin
CrushImage( ParamStr(1), '0.jpg' );
for i := 0 to 1999 do
CrushImage( IntToStr(i) + '.jpg', IntToStr(i+1) + '.jpg' );
end.
JQal.pas
{
JQal is a very simple program that saves
an image with the desired level of quality.
}
uses fpimage, fpreadjpeg, fpwritejpeg, sysutils;
var
Img : TFPMemoryImage;
Reader : TFPReaderJPEG;
Writer : TFPWriterJPEG;
begin
Img := TFPMemoryImage.Create(1,1);
Reader := TFPReaderJPEG.Create;
Writer := TFPWriterJPEG.Create;
Writer.CompressionQuality := StrToIntDef( ParamStr(2), 100 );
Img.LoadFromFile(ParamStr(1), Reader);
Img.SaveToFile(ParamStr(2) + '.jpg', Writer);
Writer.Free;
Reader.Free;
Img.Free;
end.
ImgCmp.pas
{
ImgCmp compares two images, producing a colored "heat map"
that shows how much their pixels differ. This program
also prints out a number showing how many pixels were
very different.
}
uses fpimage, fpreadjpeg, fpwritepng;
procedure SetColor( var nColor : TFPColor; Red, Green, Blue : Byte );
begin
nColor.Alpha := 65535;
nColor.Red := (Red shl 8) + Red;
nColor.Green := (Green shl 8) + Green;
nColor.Blue := (Blue shl 8) + Blue;
end;
var
A, B : Byte;
dR, dG, dB : Longint;
Diff : Longint;
imgA : TFPMemoryImage;
imgB : TFPMemoryImage;
imgR : TFPMemoryImage;
readera : TFPReaderJPEG;
readerb : TFPReaderJPEG;
writer : TFPWriterPNG;
aColor : TFPColor;
bColor : TFPColor;
X, Y : Longint;
MDevs : Longint;
DiffLevels : array[0..10] of TFPColor;
begin
{ Load the images }
ReaderA := TFPReaderJPEG.Create;
ReaderB := TFPReaderJPEG.Create;
ImgA := TFPMemoryImage.Create(1,1);
ImgB := TFPMemoryImage.Create(1,1);
ImgA.LoadFromFile( ParamStr(1), ReaderA );
ImgB.LoadFromFile( ParamStr(2), ReaderB );
ReaderA.Free;
ReaderB.Free;
ImgR := TFPMemoryImage.Create( ImgA.Width, ImgA.Height );
{ Track major deviations }
MDevs := 0;
{ Set up the color map }
for X := 0 to 10 do
SetColor(DiffLevels[X], 0, 0, 0);
SetColor(DiffLevels[ 1], 0, 0, 128);
SetColor(DiffLevels[ 2], 0, 0, 255);
SetColor(DiffLevels[ 3], 0, 128, 128);
SetColor(DiffLevels[ 4], 0, 255, 255);
SetColor(DiffLevels[ 5], 0, 255, 0);
SetColor(DiffLevels[ 6], 255, 255, 0);
SetColor(DiffLevels[ 7], 255, 128, 0);
SetColor(DiffLevels[ 8], 255, 0, 0);
SetColor(DiffLevels[ 9], 128, 128, 128);
SetColor(DiffLevels[10], 255, 255, 255);
{ Compare their pixels }
for X := 0 to ImgA.Width-1 do
for Y := 0 to ImgA.Height-1 do
begin
aColor := ImgA.Colors[X,Y];
bColor := ImgB.Colors[X,Y];
{ Red }
A := aColor.Red shr 8;
B := bColor.Red shr 8;
dR := 0;
if A > B then
dR := A - B
else
dR := B - A;
{ Green }
A := aColor.Green shr 8;
B := bColor.Green shr 8;
dG := 0;
if A > B then
dG := A - B
else
dG := B - A;
{ Blue }
A := aColor.Blue shr 8;
B := bColor.Blue shr 8;
dB := 0;
if A > B then
dB := A - B
else
dB := B - A;
{ Calculate the difference }
Diff := trunc(( (dR + dG + dB) / 765 ) * 100 ) * 20;
if Diff > 100 then
Diff := 100;
{ is this a major deviation? }
if Diff > 20 then
inc(MDevs);
{ Save the color }
ImgR.Colors[X,Y] := DiffLevels[ trunc(Diff / 10) ];
end;
{ Save the comparison }
Writer := TFPWriterPNG.Create;
ImgR.SaveToFile('result.png', Writer);
{ Report the number of major deviations }
write(MDevs);
write(' / ');
writeln( ImgA.Width * ImgA.Height );
{ Free everything }
ImgA.Free;
ImgB.Free;
ImgR.Free;
Writer.Free;
end.
|