French Fried Files

Back to the Menu Main


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.

A WFTID Website