How to find bad JPEG files in a directory – via Delphi (Source Code)

UPDATE: Source, EXE and ZIP updated to include new corruption detected routine…

Let’s just dive right into this one…
Here is the exe:
http://www.TheCodeCave.com/downloads/delphi/BadImageFinder.exe

Here is the Zip of the exe, forms and source:
http://www.TheCodeCave.com/downloads/delphi/BadImageFinder.zip

Here’s an image:The main screen

Here is the source for the main BadImageFinder_Main.pas file… (Hope this helps!)

[delphi]
// ****************************************************************************
// BadImageFinder_Main 06/Jun/2005
// Written by Brian Layman (AKA Capt. Queeg AKA SilverPaladin)
// Visit him at http://www.TheCodeCave.com
//
// A response for Bob Selby who had a bunch of bad JPegs in a directory.
// http://www.ztw3.com/forum/forum.cgi?Read = 78758
// This example Handles a limited number of Image types. But could be
// extended easily by simply adjusting the uses Statement.
//
// It is fairly straight forward. if the JPEG or BMP loads into a TImage
// without error, it is deemed GOOD; if it errors out, it is deemed BAD.
// This could produce FALSE Positives (Good Files that are declared bad) if
// it is a Image format not supported by Delphi 5’s TImage component.
// It could produce FALSE negatives (Bad Files declared good) if the Image
// is able to be loaded but looks bad. not much would be able to detect
// that.
//
// Warning: I can’t think of any way that this routine could cause harm.
// But it is a good best practice to understand every line of new code
// before you run it. Who knows what could be lurking. Better yet, do not
// run this example at all. You should stop right now and erase the Files.
// for if it causes blue smoke to be emitted from your network card, if
// it erases all users from your computer, or if it makes your sister
// break up with her lawyer boyfriend and start dating a caver, it is
// not my fault. (Actually that last one might be an improvement, but
// it is still not my fault.) But the fact of the matter is, computers
// have a mind of their own and we programmers live on the wild side.
//
// Usage: BadImage.exe
// Put it in the directory with the Images, run it, Click detect bad
// Images. The bad Files will be listed in the memo box in the bottom
// right.
//
// History:
// 10/Jul/2004 – BL – Created
//
// ****************************************************************************
unit BadImageFinder_Main;

interface

uses
Windows, Messages, SysUtils, classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, FileCtrl, ExtCtrls, jpeg;

type
TfrmBadImageFinder = class(TForm)
btnDoWork: TButton;
flbxMatchingFiles: TFileListBox;
EditWorkingDir: TEdit;
btnChgDir: TButton;
EditMask: TEdit;
Image1: TImage;
memoBadResults: TMemo;
btnALL: TButton;
btnNone: TButton;
xboxInOneLine: TCheckBox;
Label1: TLabel;
procedure btnDoWorkClick(Sender: TObject);
procedure btnChgDirClick(Sender: TObject);
procedure flbxMatchingFilesDblClick(Sender: TObject);
procedure formCreate(Sender: TObject);
procedure btnALLClick(Sender: TObject);
procedure btnNoneClick(Sender: TObject);
private
{ Private declarations }
function IsValidJPG(FileName: String): Boolean;
public
{ Public declarations }
protected
end;

var
frmBadImageFinder: TfrmBadImageFinder;

implementation

{$R *.DFM}

{******************************************************************************
btnDoWorkClick
In this case, this routine attempts to load each File matching the Filespec
into a TImage component. if it fails, we assume it is bad.
******************************************************************************}
procedure TfrmBadImageFinder.btnDoWorkClick(Sender: TObject);
var
TotalImages: Integer;
GoodCount: Integer;
I: Integer;
FName: String;
begin // btnDoWorkClick
GoodCount := 0;
memoBadResults.Lines.Clear;
for I := 0 to (flbxMatchingFiles.Items.Count – 1)
do begin
if ((flbxMatchingFiles.Selected[I]) or (flbxMatchingFiles.SelCount <= 0)) then begin try FName := UpperCase(flbxMatchingFiles.Items[I]); Image1.Picture.LoadFromFile(FName); if ((Pos('JPEG', FName) > 0) or (Pos(‘JPG’, FName) > 0))
then if (not IsValidJPG(FName))
then raise exception.Create(‘Found a bad one: ‘ + FName);
Inc(GoodCount);
except // wrap up
if (xboxInOneLine.Checked)
then memoBadResults.Text := memoBadResults.Text + flbxMatchingFiles.Items[I] + ‘;’
else memoBadResults.Lines.Add(flbxMatchingFiles.Items[I]);
end; // try/finally
end;
end; // for
if (flbxMatchingFiles.SelCount <= 0) then TotalImages := flbxMatchingFiles.Items.Count else TotalImages := flbxMatchingFiles.SelCount; ShowMessage(IntToStr(GoodCount) + ' Good Images. ' + IntToStr(memoBadResults.Lines.Count) + ' Images would not load. ' + IntToStr(TotalImages) + ' Total Tested Images.'); end; // btnDoWorkClick {****************************************************************************** btnChgDirClick Instantiates the directory and mask Edit field changes. - NO NICE ERROR HANDLING tsk tsk tsk ******************************************************************************} procedure TfrmBadImageFinder.btnChgDirClick(Sender: TObject); begin // btnChgDirClick flbxMatchingFiles.Mask := EditMask.Text; // Change the working directory. Or default to the current dir if it is blank. if (EditWorkingDir.Text = '') then EditWorkingDir.Text := flbxMatchingFiles.Directory else flbxMatchingFiles.Directory := EditWorkingDir.Text; end; // btnChgDirClick {****************************************************************************** FileListBox1DblClick Checks one File - does not Add Name to list. ******************************************************************************} procedure TfrmBadImageFinder.flbxMatchingFilesDblClick(Sender: TObject); begin // FileListBox1DblClick if (flbxMatchingFiles.ItemIndex <> – 1)
then begin
try
Image1.Picture.LoadFromFile(flbxMatchingFiles.Items[flbxMatchingFiles.ItemIndex]);
ShowMessage(‘This Image appears to be good’);
except // wrap up
ShowMessage(‘This Image could not be loaded.’);
end; // try/finally
end;
end; // FileListBox1DblClick

{******************************************************************************
formCreate
Put a starting point into the path Edit field.
******************************************************************************}
procedure TfrmBadImageFinder.formCreate(Sender: TObject);
begin // formCreate
// The directory field defaults to the current working directory.
// May as well take advantage of that.
EditWorkingDir.Text := flbxMatchingFiles.Directory;
end; // formCreate

{******************************************************************************
btnALLClick
Select All Images.
******************************************************************************}
procedure TfrmBadImageFinder.btnALLClick(Sender: TObject);
var
I: Integer;
begin // btnALLClick
for I := 0 to (flbxMatchingFiles.Items.Count – 1)
do flbxMatchingFiles.Selected[I] := TRUE;
end; // btnALLClick

{******************************************************************************
btnNoneClick
Select no Images.
******************************************************************************}
procedure TfrmBadImageFinder.btnNoneClick(Sender: TObject);
var
I: Integer;
begin // btnNoneClick
for I := 0 to (flbxMatchingFiles.Items.Count – 1)
do flbxMatchingFiles.Selected[I] := FALSE;
end; // btnNoneClick

{******************************************************************************
IsValidJPG
Original routine Open Sourced by someone Named retnyg:
http://www.delphi – forum.de/topic_JPEG + checker_43665.html
******************************************************************************}
function TfrmBadImageFinder.IsValidJPG(FileName: String): Boolean;
var
H, W: Integer;
Jpg: TJPEGImage;
Bmp: TBitmap;
F: File of Word;
Head: Word;
p: pInteger;
pB: pByte;
pE: Cardinal;
pColor: Integer;
Label fin;
begin
Result := FALSE;

if (FileExists(FileName))
then begin
AssignFile(F, FileName);
try
Reset(F);
Read(F, Head);
finally
CloseFile(F);
end;

if (Head <> $D8FF) then exit;

try
Jpg := TJpegImage.Create;
Bmp := TBitmap.Create;

// The First check, does it load?
jpg.LoadFromFile(FileName);

// Yes, now draw it out to a BMP for manipulation
H := Jpg.Height;
W := Jpg.Width;
Bmp.Height := H;
Bmp.Width := W;
Bmp.Canvas.Draw(0, 0, Jpg);

// Grab the First line of the Image, in binary
// if the color is invalid, the Image is bad.
p := Bmp.ScanLine [H – 1];
pColor := p^;
if (pColor = $808080)
then begin
FreeAndNil(Jpg);
FreeAndNil(Bmp);
Result := FALSE;
exit;
end;

// Now jump to the last scanline
// Check every four dwords of last scanline for a suspect blue Value
// It goes backwards through the last line of the image and checks to
// see if the color is less than the constant $E1.
// If it is, then result is changed from FALSE to TRUE and the routine
// exits with IsValidJPEG returning TRUE. Otherwise, if all the pixels
// in that line have a color greater than or equal too $E1 (suspect blue???),
// then the result remains FALSE and IsValidJPEG returns false…
pE := Cardinal(p) + Cardinal(H shr 1);
Cardinal(pB) := Cardinal(p);
while (Cardinal(pB) < pE) do begin if (pB^ < $E1) then begin Result := TRUE; Break; end else begin inc(pB); if (pB^ >= $E1)
then begin
inc(pB);
if (pB^ >= $E1)
then begin
Result := TRUE;
Break;
end;
dec(pB);
end;
dec(pB);
end;
inc(pB, 16);
end;
finally
FreeAndNil(Jpg);
FreeAndNil(Bmp);
end;
end;
end;
end.

[/delphi]

2 Comments

Add a Comment

Your email address will not be published. Required fields are marked *