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:
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]
 
																															
very cool tool..
thanks for sharing
WYATB