• 根据照片文件的EXIF信息来分目录整理


    y1pWlMfqpbLgXAk_1MqfbiXQX524PUH9aC1bkzoiuJWBVImEt1Lg3QxQo3bHke_lnnxz1eSIzFlJ7Y

    unit FfrmMain;

    interface

    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, Buttons, ExtCtrls,FileCtrl, ComCtrls;

    type
      TfrmMain = class(TForm)
        edtSrcDir: TLabeledEdit;
        SpeedButton1: TSpeedButton;
        edtDesDir: TLabeledEdit;
        SpeedButton2: TSpeedButton;
        chkIncludeSubDir: TCheckBox;
        rgCat: TRadioGroup;
        rgExist: TRadioGroup;
        cmbDirType: TComboBox;
        Label1: TLabel;
        BitBtn1: TBitBtn;
        BitBtn2: TBitBtn;
        Memo1: TMemo;
        Label2: TLabel;
        memLog: TMemo;
        prgMain: TProgressBar;
        procedure SpeedButton1Click(Sender: TObject);
        procedure SpeedButton2Click(Sender: TObject);
        procedure BitBtn1Click(Sender: TObject);
      private
        { Private declarations }
        function ProcessFile(vFileName:String):Boolean;
        function GetFileExifDate(vFileName:String):TDateTime;
      public
        { Public declarations }
      end;

    var
      frmMain: TfrmMain;

    procedure SearchFile(Path:string;MatchStr:string;var List:TStrings;IncludeSubDir:Boolean);

    implementation

    uses Exif;

    {$R *.dfm}

    procedure SearchFile(Path:string;MatchStr:string;var List:TStrings;IncludeSubDir:Boolean);
    var
      i: Integer;
      bFound:boolean;
      LSrch: TSearchRec;
      SubPath:string;
      SubPaths:TStrings;
    begin
      SubPaths:=TStringList.create;
      //找出当前目录下匹配文件
      i := FindFirst(Path +'\'+ MatchStr, faAnyFile, LSrch);
      try
        while i=0 do
        begin
          if (LSrch.Attr and faDirectory)=0 then
            List.Add(Path+'\'+LSrch.Name);
          i := FindNext(LSrch);
        end;
      finally
        FindClose(LSrch);
      end;

      if not IncludeSubDir then exit;

      //找出子目录
      i := FindFirst(Path +'\*.*', faDirectory, LSrch);
      try
        while i=0 do
        begin
          if (LSrch.Name<>'..') and (LSrch.Name<>'.')
            and ((LSrch.Attr and faDirectory)<>0) then
              SubPaths.Add(Path+'\'+LSrch.Name);
          i := FindNext(LSrch);
        end;
      finally
        FindClose(LSrch);
      end;

      try
        for i:=0 to SubPaths.Count-1 do
           SearchFile(SubPaths[i],MatchStr,List,True);
      finally
        SubPaths.free;
      end;
    end;

    procedure TfrmMain.BitBtn1Click(Sender: TObject);
    var
      R:TStrings;
      i:Integer;
    begin
      if cmbDirType.Text='' then
      begin
        MessageBox(handle,'请选择目录树样式!','提示窗口',mb_iconinformation);
        exit;
      end;
      try
        R:=TStringList.create;
        SearchFile(edtSrcDir.text,'*.jpg',R,chkIncludeSubDir.Checked);
        //写日志
        memLog.clear;
        memLog.Lines.Add('总共有'+inttostr(R.Count)+'张照片要处理!');
        prgMain.Max:=R.Count;
        for I := 0 to R.Count - 1 do
        begin
          if ProcessFile(R[i]) then
            memLog.Lines.Add('文件'+R[i]+'操作成功!')
          else
            memLog.Lines.Add('文件'+R[i]+'操作失败!');
          prgMain.StepIt;
        end;
      finally
        R.Free;
      end;
    end;

    function TfrmMain.GetFileExifDate(vFileName: String): TDateTime;
    var
      ex : TExif;
      ExifDate:String;
    begin
      ex:=TExif.Create;
      try
        ex.ReadFromFile(vFileName);
        if ex.Valid then
        begin
          ExifDate:=ex.DateTime;
          ExifDate[5]:='-';
          ExifDate[8]:='-';
          ExifDate:=Copy(ExifDate,1,10);
          Result:=strtodate(ExifDate);
        end
        else
          Result:=FileDateToDateTime(FileAge(vFileName));
      finally
        ex.Free;
      end;
    end;

    function TfrmMain.ProcessFile(vFileName: String): Boolean;
    var
      DesFileName:String;
      Dir:String;
      DirIsExist,Over:Boolean;
      ExifDateInfo:TDateTime;
    begin
      //操作文件
      //建立目标文件
      ExifDateInfo:=GetFileExifDate(vFileName);
      case cmbDirType.ItemIndex  of
        0://年-月-日
          DesFileName:=edtDesDir.text+'\'+FormatDateTime('yyyy',ExifDateInfo)+'\'
            +FormatDateTime('MM',ExifDateInfo)+'\'
            +FormatDateTime('YYYY-MM-DD',ExifDateInfo)+'\'+ExtractFileName(vFileName);
        1://年-日
          DesFileName:=edtDesDir.text+'\'+FormatDateTime('yyyy',ExifDateInfo)+'\'
            +FormatDateTime('yyyy-mm-DD',ExifDateInfo)+'\'+ExtractFileName(vFileName);
        2://年
          DesFileName:=edtDesDir.text+'\'+FormatDateTime('yyyy',ExifDateInfo)+'\'+ExtractFileName(vFileName);
      end;
      //判断是否已存在
      if FileExists(DesFileName) then
      case rgExist.ItemIndex  of
        0:Over:=True;
        1:Over:=False;
        2:DesFileName:=copy(DesFileName,1,length(DesFileName)-4)+FormatDateTime('yyyymmddhhmmss',now)+'.jpg';
      end;

      Dir:=ExtractFilePath(DesFileName);
      if not DirectoryExists(Dir) then
        ForceDirectories(Dir);
      case rgCat.ItemIndex  of
        0:CopyFile(PAnsiChar(vFileName),PAnsiChar(DesFileName),Over);
        1:MoveFile(PAnsiChar(vFileName),PAnsiChar(DesFileName));
      end;

      Result:=True;
    end;

    procedure TfrmMain.SpeedButton1Click(Sender: TObject);
    var
      strPath:String;
    begin
      if SelectDirectory('请选择原文件路径:','', strPath) then
      begin
        edtSrcDir.Text :=strPath;
      end;
    end;

    procedure TfrmMain.SpeedButton2Click(Sender: TObject);
    var
      strPath:String;
    begin
      if SelectDirectory('请选择目标路径:','', strPath) then
      begin
        edtDesDir.Text :=strPath;
      end;
    end;

    end.

    附录:用到的EXIF信息分析文件

    {==============================================================================
    Component simple read Exif section in Jpeg/Jfif Files.
    More information about Exif at www.exif.org

    裲mponent written by SimBa aka Dimoniusis
    You may use this component absolutely free.

    You may talk with me via
    e-mail: dimonius@mail333.com
    ICQ: 11152101
    Web: http://dimonius.da.ru/

    Changes:
    Version 1.3
    - some more ifd tags implemented
    - some bugs fixes

    Version 1.2 (Some code by Jim Wood,  e-mail: jwood@visithink.com)
    - some more ifd tags implemented
    - corrected work with ReadOnly files

    Version 1.1 (By Ive, e-mail: ive@lilysoft.com)
    - works now with Motorola and Intel byte order tags
    - better offset calculation
    - some more ifd tags implemented
    - some format functions for rational values
    - naming convention changed a little

    NOTE: far away from being complete but it seems to
          work with all example files from www.exif.org

    - Ive (c) 2003

    ==============================================================================}

    unit Exif;

    interface

    uses
      Classes, SysUtils;

    type
      TIfdTag = packed record
        ID      : Word;       //Tag number
        Typ     : Word;       //Type tag
        Count   : Cardinal;   //tag length
        Offset  : Cardinal;   //Offset / Value
      end;

      TExif = class(TObject)
        private
          FImageDesc          : String;     //Picture description
          FMake               : String;     //Camera manufacturer
          FModel              : String;     //Camere model
          FOrientation        : Byte;       //Image orientation - 1 normal
          FOrientationDesc    : String;     //Image orientation description
          FCopyright          : String;     //Copyright
          FValid              : Boolean;    //Has valid Exif header
          FDateTime           : String;     //Date and Time of Change
          FDateTimeOriginal   : String;     //Original Date and Time
          FDateTimeDigitized  : String;     //Camshot Date and Time
          FUserComments       : String;     //User Comments

          FExposure           : String;     //Exposure
          FFstops             : String;
          FShutterSpeed       : string;
          FAperture           : string;
          FMaxAperture        : string;

          FExposureProgram    : Byte;
          FExposureProgramDesc: string;
          FPixelXDimension    : Cardinal;
          FPixelYDimension    : Cardinal;
          FXResolution        : Cardinal;
          FYResolution        : Cardinal;
          FMeteringMode       : byte;
          FMeteringMethod     : string;
          FLightSource        : Byte;
          FLightSourceDesc    : string;
          FFlash              : Byte;
          FFlashDesc          : string;
          FISO                : Word;
          FSoftware           : string;
          FArtist             : string;
          FCompressedBPP      : string;

          f                   : File;
          ifdp                : Cardinal;
          FSwap               : boolean;
          function  ReadAsci(const Offset, Count: Cardinal): String;
          function  ReadRatio(const Offset: Cardinal; frac: boolean): String; overload;
          function  ReadRatio(const Offset: Cardinal): single; overload;
          procedure ReadTag(var tag: TIfdTag);
          procedure Init;
        function ReadLongIntValue(const Offset: Cardinal): LongInt;
        public
          constructor Create;
          procedure ReadFromFile(const FileName: AnsiString);

          property Valid: Boolean read FValid;
          property ImageDesc: String read FImageDesc;
          property Make: String read FMake;
          property Model: String read FModel;
          property Orientation: Byte read FOrientation;
          property OrientationDesc: String read FOrientationDesc;
          property Copyright: String read FCopyright;
          property DateTime: String read FDateTime;
          property DateTimeOriginal: String read FDateTimeOriginal;
          property DateTimeDigitized: String read FDateTimeDigitized;
          property UserComments: String read FUserComments;
          property Software: String read FSoftware;
          property Artist: String read FArtist;

          property Exposure: String read FExposure;
          property ExposureProgram: byte read FExposureProgram;
          property ExposureProgramDesc: string read FExposureProgramDesc;
          property FStops: String read FFStops;
          property ShutterSpeed: String read FShutterSpeed;
          property Aperture: String read FAperture;
          property MaxAperture: String read FMaxAperture;
          property CompressedBPP: String read FCompressedBPP;

          property PixelXDimension: Cardinal read FPixelXDimension;
          property PixelYDimension: Cardinal read FPixelYDimension;
          property XResolution: Cardinal read FXResolution;
          property YResolution: Cardinal read FYResolution;
          property MeteringMode: byte read FMeteringMode;
          property MeteringMethod: string read FMeteringMethod;
          property LightSource: byte read FLightSource;
          property LightSourceDesc: string read FLightSourceDesc;
          property Flash: byte read FFlash;
          property FlashDesc: string read FFlashDesc;
          property ISO: Word read FISO;
      end;

    implementation

    uses
      Math;

    type
      TMarker = packed record
        Marker  : Word;      //Section marker
        Len     : Word;      //Length Section
        Indefin : Array [0..4] of Char; //Indefiner - "Exif" 00, "JFIF" 00 and ets
        Pad     : Char;      //0x00
      end;

      TIFDHeader = packed record
        pad       : Byte; //00h
        ByteOrder : Word; //II (4D4D) or MM
        i42       : Word; //2A00 (magic number from the 'Hitchhikers Guide'
        Offset    : Cardinal; //0th offset IFD
        Count     : Word;     // number of IFD entries
      end;

    function SwapLong(Value: Cardinal): Cardinal;
    asm bswap eax end;

    procedure TExif.ReadTag(var tag: TIfdTag);
    begin
      BlockRead(f,tag,12);
      if FSwap then with tag do begin // motorola or intel byte order ?
        ID  := Swap(ID);
        Typ := Swap(Typ);
        Count := SwapLong(Count);
        if (Typ=1) or (Typ=3) then
          Offset := (Offset shr 8) and $FF
        else
          Offset  := SwapLong(Offset);
        end
      else with tag do begin
        if ID<>$8827 then  //ISO Metering Mode not need conversion
          if (Typ=1) or (Typ=3) then
            Offset := Offset and $FF; // other bytes are undefined but maybe not zero
      end;
    end;

    function TExif.ReadAsci(const Offset, Count: Cardinal): String;
    var
      fp: LongInt;
      i: Word;
    begin
      SetLength(Result,Count);
      fp:=FilePos(f); //Save file offset
      Seek(f, Offset);
      try
        i:=1;
        repeat
          BlockRead(f,Result[i],1);
          inc(i);
        until (i>=Count) or (Result[i-1]=#0);
        if i<=Count then Result:=Copy(Result,1,i-1);
      except
        Result:='';
      end;
      Result:=TrimRight(Result);
      Seek(f,fp);     //Restore file offset
    end;

    function TExif.ReadLongIntValue(const Offset: Cardinal): LongInt;
    var
      fp: LongInt;
    begin
      fp:=FilePos(f); //Save file offset
      Seek(f, Offset);
      try
        BlockRead(f, Result, sizeof(Result));
        if FSwap then Result:=SwapLong(Result);
      except
        Result:=0;
      end;
      Seek(f, fp); //Restore file offset
    end;

    function TExif.ReadRatio(const Offset: Cardinal; frac: boolean): String;
    var
      fp: LongInt;
      nom,denom: cardinal;
    begin
      fp:=FilePos(f); //Save file offset
      Seek(f, Offset);
      try
        BlockRead(f,nom,4);
        BlockRead(f,denom,4);
        if FSwap then begin     // !!!
          nom := SwapLong(nom);
          denom := SwapLong(denom);
        end;
        if frac then begin
          str((nom/denom):1:2, result);
          if (length(result)>0) and (result[length(result)]='0') then Result:=copy(Result,1,length(Result)-1);
        end else
          if denom<>1000000 then
            Result:=inttostr(nom)+'/'+inttostr(denom)
          else Result:='0';
      except
        Result:='';
      end;
      Seek(f,fp);     //Restore file offset
    end;

    function TExif.ReadRatio(const Offset: Cardinal): single;
    var
      fp: LongInt;
      nom,denom: cardinal;
    begin
      fp:=FilePos(f); //Save file offset
      Seek(f, Offset);
      try
        BlockRead(f,nom,4);
        BlockRead(f,denom,4);
        if FSwap then begin     // !!!
          nom := SwapLong(nom);
          denom := SwapLong(denom);
        end;
        Result:=nom/denom;
      except
        Result:=0.0;
      end;
      Seek(f,fp);     //Restore file offset
    end;

    procedure TExif.Init;
    begin
      ifdp:=0;

      FImageDesc:='';
      FMake:='';
      FModel:='';
      FOrientation:=0;
      FOrientationDesc:='';
      FDateTime:='';
      FCopyright:='';
      FValid:=False;
      FDateTimeOriginal:='';
      FDateTimeDigitized:='';
      FUserComments:='';
      FExposure:='';
      FFstops:='';
      FShutterSpeed := '';
      FAperture := '';
      FExposureProgram:=0;
      FExposureProgramDesc:='';
      FPixelXDimension:=0;
      FPixelYDimension:=0;
      FMeteringMode:=0;
      FMeteringMethod:='';
      FLightSource:=0;
      FLightSourceDesc:='';
      FFlash:=0;
      FFlashDesc:='';
      FISO:=0;
      FCompressedBPP:='';
      FArtist:='';
      FSoftware:='';
      FMaxAperture:='';
      FXResolution:=0;
      FYResolution:=0;
    end;

    constructor TExif.Create;
    begin
      Init;
    end;

    procedure TExif.ReadFromFile(const FileName: AnsiString);
    const
      orient   : Array[1..9] of String=('Normal','Mirrored','Rotated 180','Rotated 180, mirrored','Rotated 90 left, mirrored','Rotated 90 right','Rotated 90 right, mirrored','Rotated 90 left','Unknown');
      ExplType : Array[1..9] of String=('Unknown','Manual Control','Normal Program','Aperture Priority', 'Shutter Priority', 'Creative Program','Action Program','Portrait Mode','Landscape Mode');
      Meter    : Array[0..7] of String=('Unknown','Average','Center Weighted Average','Spot','Multi Spot','Pattern','Partial','Other');
    var
      j:      TMarker;
      ifd:    TIFDHeader;
      off0:   Cardinal; //Null Exif Offset
      tag:    TIfdTag;
      i:      Integer;
      n:      Single;
      SOI:    Word; //2 bytes SOI marker. FF D8 (Start Of Image)
      IfdCnt: Word;
      Tmp   : string;

    begin
      if not FileExists(FileName) then exit;
      Init;

      System.FileMode:=0; //Read Only open
      AssignFile(f,FileName);
      reset(f,1);

      BlockRead(f,SOI,2);
      if SOI=$D8FF then begin //Is this Jpeg
        BlockRead(f,j,9);

        if j.Marker=$E0FF then begin //JFIF Marker Found
          Seek(f,20); //Skip JFIF Header
          BlockRead(f,j,9);
        end;

        //Search Exif start marker;
        if j.Marker<>$E1FF then begin
          i:=0;
          repeat
            BlockRead(f,SOI,2); //Read bytes.
            inc(i);
          until (EOF(f) or (i>1000) or (SOI=$E1FF));
          //If we find maker
          if SOI=$E1FF then begin
            Seek(f,FilePos(f)-2); //return Back on 2 bytes
            BlockRead(f,j,9);     //read Exif header
          end;
        end;

        if j.Marker=$E1FF then begin //If we found Exif Section. j.Indefin='Exif'.
          FValid:=True;
          off0:=FilePos(f)+1;   //0'th offset Exif header
          BlockRead(f,ifd,11);  //Read IDF Header
          FSwap := ifd.ByteOrder=$4D4D; // II or MM  - if MM we have to swap
          if FSwap then begin
            ifd.Offset := SwapLong(ifd.Offset);
            ifd.Count  := Swap(ifd.Count);
          end;
          if ifd.Offset <> 8 then begin
            Seek(f, FilePos(f)+abs(ifd.Offset)-8);
          end;

          if (ifd.Count=0) then ifd.Count:=100;

          for i := 1 to ifd.Count do begin
            ReadTag(tag);
            case tag.ID of
                  0: break;
      // ImageDescription
              $010E: FImageDesc:=ReadAsci(tag.Offset+off0, tag.Count);
      // Make
              $010F: FMake:=ReadAsci(tag.Offset+off0, tag.Count);
      // Model
              $0110: FModel:=ReadAsci(tag.Offset+off0, tag.Count);
      // Orientation
              $0112: begin
                       FOrientation:= tag.Offset;
                       if FOrientation in [1..8] then
                         FOrientationDesc:=orient[FOrientation]
                       else
                         FOrientationDesc:=orient[9];//Unknown
                     end;
      // DateTime
              $0132: FDateTime:=ReadAsci(tag.Offset+off0, tag.Count);
      // CopyRight
              $8298: FCopyright:=ReadAsci(tag.Offset+off0, tag.Count);
      // Software
              $0131: FSoftware:=ReadAsci(tag.Offset+off0, tag.Count);
      // Artist
              $013B: FArtist:=ReadAsci(tag.Offset+off0, tag.Count);
      // Exif IFD Pointer
              $8769: ifdp:=Tag.Offset; //Read Exif IFD offset
      //XResolution
              $011A: FXResolution := ReadLongIntValue(Tag.Offset+off0);
      //YResolution
              $011B: FYResolution := ReadLongIntValue(Tag.Offset+off0);
            end;
          end;

          if ifdp>0 then begin
            Seek(f,ifdp+off0);
            BlockRead(f,IfdCnt,2);
            if FSwap then IfdCnt := swap(IfdCnt);
            for i := 1 to IfdCnt do begin
              ReadTag(tag);
      {
              You may simple realize read this info:

              Tag |Name of Tag

              9000 ExifVersion
              0191 ComponentsConfiguration
              0392 BrightnessValue
              0492 ExposureBiasValue
              0692 SubjectDistance
              0A92 FocalLength
              9092 SubSecTime
              9192 SubSecTimeOriginal
              9292 SubSecTimeDigitized
              A000 FlashPixVersion
              A001 Colorspace
      }
              case tag.ID of
                    0: break;
      // ExposureTime
                $829A: FExposure:=ReadRatio(tag.Offset+off0, false)+' seconds';
      // Compressed Bits Per Pixel
                $9102: FCompressedBPP:=ReadRatio(tag.Offset+off0, true);
      // F-Stop
                $829D: FFStops:=ReadRatio(tag.Offset+off0, true);
      // FDateTimeOriginal
                $9003: FDateTimeOriginal:=ReadAsci(tag.OffSet+off0,tag.Count);
      // DateTimeDigitized
                $9004: FDateTimeDigitized:=ReadAsci(tag.OffSet+off0,tag.Count);
      // ShutterSpeed
                $9201: try
                         n:=ReadRatio(tag.Offset+off0);
                         if n<65535 then begin
                           str(power(2,n):1:0,tmp);
                           FShutterSpeed:='1/'+tmp+' seconds';
                         end else FShutterSpeed:='1 seconds';
                       except
                         FShutterSpeed:='';
                       end;
      //ISO Speed
                $8827: FISO:=Tag.Offset;
      // Aperture
                $9202: FAperture:=ReadRatio(tag.Offset+off0, true);
      // Max Aperture
                $9205: FMaxAperture:=ReadRatio(tag.Offset+off0, true);
      // UserComments
                $9286: FUserComments:=ReadAsci(tag.OffSet+off0,tag.Count);
      // Metering Mode
                $9207: begin
                         FMeteringMode := Tag.OffSet;
                         if Tag.OffSet in [0..6] then
                           FMeteringMethod := Meter[Tag.OffSet]
                         else
                           if Tag.OffSet=7 then
                             FMeteringMethod := Meter[7]  //Other
                           else
                             FMeteringMethod := Meter[0]; //Unknown
                       end;
      // Light Source
                 $9208: begin
                         FLightSource:=Tag.OffSet;
                         case Tag.OffSet of
                            0: FLightSourceDesc := 'Unknown';
                            1: FLightSourceDesc := 'Daylight';
                            2: FLightSourceDesc := 'Flourescent';
                            3: FLightSourceDesc := 'Tungsten';
                           10: FLightSourceDesc := 'Flash';
                           17: FLightSourceDesc := 'Standard Light A';
                           18: FLightSourceDesc := 'Standard Light B';
                           19: FLightSourceDesc := 'Standard Light C';
                           20: FLightSourceDesc := 'D55';
                           21: FLightSourceDesc := 'D65';
                           22: FLightSourceDesc := 'D75';
                          255: FLightSourceDesc := 'Other';
                         else
                           FLightSourceDesc := 'Unknown';
                         end;
                       end; 
      //Flash
                $9209: begin
                         FFlash:=Tag.OffSet;
                         case Tag.OffSet of
                           0: FFlashDesc := 'No Flash';
                           1: FFlashDesc := 'Flash';
                           5: FFlashDesc := 'Flash No Strobe';
                           7: FFlashDesc := 'Flash Strobe';
                          25: FFlashDesc := 'Flash (Auto)';
                         else
                           FFlashDesc := 'No Flash';
                         end;
                       end;
      //Exposure
                $8822: begin
                         FExposureProgram:=Tag.OffSet;
                         if Tag.OffSet in [1..8] then
                           FExposureProgramDesc := ExplType[Tag.OffSet]
                         else
                           FExposureProgramDesc := ExplType[9];
                       end;
      //PixelXDimension
                 $A002: FPixelXDimension := Tag.Offset;
      //PixelYDimension
                 $A003: FPixelYDimension := Tag.Offset;
              end;
            end;
          end;
        end;
      end;
      CloseFile(f);
    end;

    end.

  • 相关阅读:
    机器学习与AI相关的资料
    基于React 的前端UI开发框架 及与Electron 的结合 https://cxjs.io/
    快速开发工具:Servoy
    求同网----专门解决物料编码问题!
    AUTOML --- Machine Learning for Automated Algorithm Design.
    iOS- 给App添加内购& 验证购买iOS7新特性
    ffmpeg在iOS的使用
    iOS小技巧:用runtime 解决UIButton 重复点击问题
    iOS应用性能调优的25个建议和技巧
    iOS js oc相互调用(JavaScriptCore)(二)
  • 原文地址:https://www.cnblogs.com/GarfieldTom/p/1408955.html
Copyright © 2020-2023  润新知