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. |