dポイントプレゼントキャンペーン実施中!

VBAの参考書を丸写にしながら、17年ほど前から、
時系列・メモ追記の写真整理プログラムを自作して活用してきました。
このCodeは、「写真撮影日時=ファイル更新日時(システム関数)」 とする方式でした。

最近では利用する画像が自分のCameraの *.jpg、だけでなく、
画像の圧縮、動画からの静止画の切り取り、WEBの公開の画像・イラストの取り込み、
PC画面のSnip画像など、*.jpg画像ではありますが多様化してきました。

そのため、「写真撮影日時=ファイル更新日時」方式では不都合になり、
直接、写真撮影日時を読み出したいと思う機会が多くなっていました。
昨年、公開されている「 JpegExifクラス(ユーザー定義関数 )」を使う方法をご指導頂き、
自作プログラムに組み込んで利用できています。

この「JpegExifクラス」は、日付情報以外にも沢山の情報を書き出すプログラムで、
高度のテクニックを駆使して書かれた労作だそうです。
そのためか、このCodeを印刷して読み直していますが、
私のレベルでは、まだ半分以上がブラックボックスで、解読できていません。
これを簡略化したような、撮影日時だけに特化したSubや、ユーザ定義関数をご存知でしたら、
ご紹介頂ければありがたいです。

A 回答 (8件)

’追加 単純化撮影日時読出(VBA Code) 2017/06/11 zy


Option Explicit
Sub JpegExif撮影日時読出() 'myDate= *.jpg 写真の撮影日時
step01: '変数宣言、初期値の設定 最近のコンパクトカメラファイル容量は 2,000,000_Byteを超える
Dim myA(3199) As Byte
Dim str As String ' 例 : 十進数の255を、Hex(255)で16進数化 OneByteの "FF"
' ASCII_Code記載の撮影日時の領域では、 70⇒&H46⇒F 50⇒&H32⇒2
Dim Tag As String ' BEでTag="4D4D"、LEでTag="4949"
Dim Offset As String '撮影日時記載のOffset位置変数
Dim ci As Long, ri As Long, rii As Long, riii As Long ' 配列変数の検索時の変数
Dim myDate As String ' 古写真、"0000/00/00 00:00:00" もある Date()の規定外値 As dateをAs Stringでエラー回避
Dim ret As Integer: ret = 0 ' 各種 Tag確認変数
Dim sign As Integer: sign = 0 'イレギュラー識別子
Dim myFile As String ' *.jpg名
step02: ' 3200バイト読込
myFile = Application.GetOpenFilename("C:(*.jpg),*.jpg") 'システム関数 ファイル選択窓
If myFile <> "False" Then Open myFile For Binary As #1
Get #1, , myA '3200 Byte 読込
step03: '16進数(&H)の 2_Byteの Tag 作成 TIFF・JPEG・EXIF・Endian_Tag 確認
Tag = Right("00" & Hex(myA(0)), 2) & Right("00" & Hex(myA(1)), 2)
If Tag = "FFD8" Then sign = 0 '--- 蛇足で、"FFD8"を確認4下だけ。
Tag = Right("00" & Hex(myA(2)), 2) & Right("00" & Hex(myA(3)), 2)
If Tag <> "FFE1" Then
sign = 1 ' Exifファイルではない。
GoTo step09 ' イレギュラー処理へskip
End If
Tag = Right("00" & Hex(myA(12)), 2) & Right("00" & Hex(myA(13)), 2) ' Byte-Order(Data配置の扱いルース)確認
If Tag = "4D4D" Then ret = 1 ' myA(12 to 13) で TIFFファイル確認
If Tag = "4949" Then ret = 2 ' Bye_Order( BE=4D4D ) or ( LE=4949 )確認
For ri = 0 To 3198 Step 2 ' 2_Byte_Tagの作成 写真撮影日時_Tag 検出
Tag = Right("00" & Hex(myA(ri)), 2) & Right("00" & Hex(myA(ri + 1)), 2)
If Tag = "9003" Or Tag = "0390" Then ' --- 撮影日時_Tag BEで zTa="9003"、LEで Tag="0390"
rii = ri ' 撮影日時Tag位置記録
GoTo step04 '撮影日時読出へ Skip
End If
Next ri
sign = 2 ' 撮影日時Tag無
GoTo step09 'イレギュラー処理
step04: 'Tag="9003" or Tag="0390" 時の撮影日時情報読込
rii = rii - 1 ' myA(16 to 19)に、オフセット=8と記載 (起点+8)=(起点-1)+9 バイト番地 myA(9 to 12)読み取り。
If ret = 1 Then ' BEの撮影日時記載位置検出
Offset = Right("00" & Hex(myA(rii + 9)), 2) & Right("00" & Hex(myA(rii + 10)), 2) & Right("00" & Hex(myA(rii + 11)), 2) & Right("00" & Hex(myA(rii + 12)), 2)
End If
If ret = 2 Then ' -- LEの撮影日時記載位置検出
Offset = Right("00" & Hex(myA(rii + 12)), 2) & Right("00" & Hex(myA(rii + 11)), 2) & Right("00" & Hex(myA(rii + 10)), 2) & Right("00" & Hex(myA(rii + 9)), 2)
End If
riii = CLng("&H" & Offset) ' Offset読取 起点 十進数変換 例 riii=700
step05: ' ASCII文字記載の撮影日時読出 19_Byte 連結 One_String
str = ""
For ci = 0 To 18
str = str & Chr(myA(riii + 12 + ci)) ' -- yyyy:mm:dd hh:mm:ss で 19_Byte
Next ci
Mid(str, 5, 1) = "/" ' --- ":" を "/" に変更 (汎用Soft用 日付情報利用可)
Mid(str, 8, 1) = "/"
myDate = str ' --- 読み出し例 : myDate="2017/01/12 09:49:14"
If myDate = "0000/00/00 00:00:00" Then
sign = 3
GoTo step09 'イレギュラー処理 撮影日時 Tag?、日付 0 ? As Dateでエラー?(関数の限度外)
End If
GoTo step10 'FileCloseへ Skip
step09: 'イレギュラー処理
If sign = 1 Then MsgBox "イレギュラー理由 : FFE1無 Exifファイルでない 撮影日時=更新日時"
If sign = 2 Then MsgBox "イレギュラー理由 : 撮影日時 Tag = 9003 or =3090無 撮影日時=更新日時"
If sign = 3 Then MsgBox "イレギュラー理由 : 読出撮影日付= 0000/00/00 00:00:00 撮影日時=更新日時"
myDate = FileDateTime(myFile) ' 組込関数 更新日時表示
GoTo step10
MsgBox "イレギュラー理由 原因不明 撮影日時 =更新日時"
myDate = FileDateTime(myFile)
step10: ' 終了
Close #1
MsgBox "フォルダ名+ファイル名 = " & myFile & _
" 写真 撮影日時 = " & myDate & _
" 結果確認 OK?"
End Sub
    • good
    • 0
この回答へのお礼

goosj12zyさん、こんばんは。
サンプルCodeを貼って頂き、有難う御座いました。

貼って頂いたCodeをそっくりコピペし、
実行したら瞬時に写真撮影日時がMsgBoxに表示されました。
「格納データのフォーマットにエラーがあった」*.jpgファイルも、
エラーメッセージ付きで書き出されていました。
このCodeを若干修正して、自作のアルバムSoftに組み込み、
希望通りのアルバム表示ができました。
20年近く使ってきた自作アルバムSoftですので、
これでCode全体が理解でき、不都合があれば書き直せる、
完全な自前のプログラムになりました。本当に有難う御座いました。

お礼日時:2017/06/12 19:36

>どこでどんなエラーが出るんでしょうね?


> #いや?そこは本筋じゃないんでどうでも良いです。
すみません言葉悪いですね
サンプルってことでエラー処理してないからパターンマッチングしないとそりゃエラーですわね-"-
If .test(buf) = True Then
  ret = .Execute(buf)(0)
End If



ちなみに"Wia.ImageFile"は参照設定しないでも使えますよ
(環境:win7/office2010)

Sub sample()
  test "C:\@tool\IMG_0001.JPG"
End Sub

Sub test(ByVal s As String)
  Dim ret As String

  ret = "error"
  On Error Resume Next
  With CreateObject("Wia.ImageFile")
    .LoadFile (s)
    ret = .Properties("36867")
  End With
  On Error GoTo 0
  Debug.Print ret
End Sub
    • good
    • 1
この回答へのお礼

end-uさん、こんばん
また、追加のコメントを頂き、本当に有難う御座います。
頂いたJpegExifクラスのret=-11の取り扱いなどのコメントを、じっくり吟味してみます。
goosj12zyさんが貼ってくれたCodeも、ret=-11も含めたエラー対策で、
これで問題が解決しました。

自作アルバムSoftは、写真の、
撮影日時=更新日時(システム組み込み関数)とするCoddeでした。
昨日、DropBox経由で娘が送ってくれた孫の動画・静止画の静止画は、
11,190.761_Byteもありました。
PC上では、1200*900ピクセル、300,000バイトもあれば十分です。
11.190,761/300,000≒40倍!
幾らメモリ容量を気にしない時代とは言え、
毎日送られてくる写真の圧縮保存をしたくなります。
圧縮Softを通すと、撮影日時<>更新日時となり、
画像の撮影日時を直接書き出す必要に迫られる事になりました。

半年前に、gooにお尋ねし、対応策はユーザー定義関数を使うでした。
公開のSoftとしてご紹介頂いたのが、JpegExifクラスでした。
後期高齢者の久しぶりのVBAで、JpegExifクラスの組み込みに四苦八苦し、
沢山の方々に、励まされ、叱責されながら、
このSoftの組み込みに成功して感謝感激しました。

今回、若干の不具合を避けようとして、再度のお尋ねになりました。
また??がありましたら、よろしくお願いいたします。

お礼日時:2017/06/12 20:55

>しかし、JpegExifクラスと同様に、*.JpegであるがExifではない,


>ファイルは、エラーメッセージが出てしまいます。
どこでどんなエラーが出るんでしょうね?
 #いや?そこは本筋じゃないんでどうでも良いです。

他の方のお礼欄ですが
>その理由:「格納データのフォーマットにエラーがある」との判定で、
>除外され、アルバムには、この対象ファイルが読み出されない様です。
>EpegExifクラスはファイルから読み出す項目が多く、
>いまだに、どの項目でフォーマットエラーがあって除外されているのかは分かっていません。
>このようなエラーファイルでも、除外せずにアルバムに表示したいと思っています。
これが目的なら、クラスJpegExif通す前にjpgファイルを読み込めばいいんじゃないでしょうか。
アルバムに表示させるのは自前のコードですよね?

もしくは、元のコード
http://www.btinc.jp/vba_labo/study1.html
あまり変更せず使われてるなら
>If objJpeg.InitSet(fileName) >= 0 Then
ここが-11でもリストアップするようにすれば良いのでは?

Dim chk As Long
チェック用の変数追加して
chk = objJpeg.InitSet(fileName)
If chk >= 0 Or chk = -11 Then

とか?
    • good
    • 0
この回答へのお礼

二件の追加コメントに対して後のコメントにお礼文を書きました。
有難う御座いました。

お礼日時:2017/06/12 20:53

こんばんは。


WEB検索をしても、*.jpgファイルの構造や情報の読み出しは、一読ではなかなか理解しにくいですね。
しかし、個人的なお勉強の範囲であれば、大雑把に要点だけを知ってしまえば、
自分の言葉でVBA_Codingをして、Setp by Stepで淡々と進めれば、簡単に写真の撮影日時は読み出せますよ。

この*.jpgファイル情報は、DCF・CIPA・TIFF・Jpeg・Exifなどの公開されているルールに従って埋め込まれている。
この情報は、16進数(&H)の2_Byte(8bits*2)で構成されている、Tag、を目印に検索すれば容易に読み出せる。
一つのTagfフィールドは12_Byte 構成。その中で、Offset_情報は後半の4_Byteブロックに埋め込まれている。
この4_Byteブロックから、値へのオフセット情報を読み取る。

最近は、コンパクトデジカメの*.jpgファイルでも、3,000,000_Byte位の情報が埋め込まれている。
撮影日時情報は、先頭から3,000_Byte位までに埋め込まれている。
イメージとしては、バイト番地で、00 to 3199 を配列(myA)に読み込んだと仮定 Option Base 0;
Tag=myA(00 to 01) が"FFD8"があればJpegファイル。Tag=myA(02 to 03)が"FFE1"であればExifファイル。
Tag=myA(12 to 13) が"4D4D" or "4949"であればTIFFファイル。

なお、"4D4D" or "4949"は、Byte_Order(データを配列する規則)。
"4D4D"の時は、BL(Big_Endian)と言いい、"4949"の時はLE(Littele_Endian)と言う。
BEの時は、上位バイトを先頭に配置 例: "00 00 02 BC" 。
LEの時は、下位バイトを先頭に配置 例: "BC 02 00 00" 。 ここでは、LEはBE基準に直して、同一の値として扱う。

この3つのTagを確認後、撮影日時を示す、Tag="9003" or Tag="0390" のバイト番地を検索する。
このTagフィールド内に埋め込まれている、基準点からOffsetが示すバイト番地に、
撮影日時情報が書かれているので、バイト番地を読み取る。
このバイト番地から、撮影日時情報のバイト番地を特定し、日時を読み出す。
以上です。
容量が許せば、追加解答で、ご参考までに愚作「*.jpgファイルから撮影日時を読み取る」のVBA_Codeを添付してみます。
(かえって混乱させてしまったらごめんなさい)
    • good
    • 0
この回答へのお礼

goosj12zyさん、こんばんは。コメント有難う御座いました。
コメントされた、三つの規格(FIFF-JPTG-Exif)について、
自分なりに頭の整理をしました。

FIFF:1986年に制定されたデータフォーマット規格
JPEG:その6年後に制定された画像圧縮規格
EXIF:またその18年後に制定されたファイルフォーマット規格

長い年月をかけて、
FIFFを骨格にして、JPEGが組み込まれ、更にEXIFも組み込まれていき、
デジタルカメラの記録体系は、TIFF/JPEG/EXIFが混然と交じりあったルールに則って記録されていると理解しました。

お陰様で、Tagを検索するという概念が理解できました。
追加で、更に理解を深められそうな、サンプルのVBA_CODE、を貼って頂けるのを楽しみしています。

お礼日時:2017/06/12 18:49

#いやいやgoogle君情報鮮度あまりよくない..


#or私のchoiceがか;|

最近のOSならWIA.ImageFileで取れるらしいです
http://blog.livedoor.jp/hamu1962/archives/514669 …
http://niwakan.blogspot.jp/2016/11/vba-wia-objec …
    • good
    • 0
この回答へのお礼

end-uさん、こんばん
最初に貼って頂いたCode_1を、コードペインにコピペして、
動作を確認しました。
本当に、正常なファイルの日付は、簡単に読み出してくれますね!
しかし、JpegExifクラスと同様に、*.JpegであるがExifではない,
ファイルは、エラーメッセージが出てしまいます。
また、このCodeはAdobe.System体系の様で、
なじみのないCodeばかりでした。
本当に、
「Sub TestFile() "C:\tFile\Sample.jpg" End sub」 のCodeだけで、
これを、後続のCodeが読み取って、
日付を読に出してくれるのは不思議ですね!!!
Adobe.System体系は、今後の研究課題とさせて頂きます。

2番目にご紹介頂いた、Code_2_1は馴染みのあるCodingでしたが、
転送エラーか、単純なコピペでは動作しませんでした。
なお、Code_2_2は、転送エラーの他に、WIA_Objecctは、
Access(or Excel)の参照設定をする必要がありました。
Accessはあまり馴染みがないので、今後の検討課題とさせて頂きます。

お礼日時:2017/06/12 16:23

『VBA 撮影日時?』ってgoogle君に尋ねると



[VBA] 指定した画像撮影日付+時刻を取得
http://hanatsunami.blogspot.jp/2013/05/vba.html
..辺りが簡単そうかな

Sub sample()
  test "C:\@tool\IMG_0001.JPG"
End Sub

Sub test(ByVal s As String)
  Dim buf As String
  Dim ret As String
  
  With CreateObject("ADODB.Stream")
    .Open
    .Type = 2
    .Charset = "SHIFT-JIS"
    .LoadFromFile (s)
    buf = .ReadText
    .Close
  End With
  With CreateObject("VBScript.RegExp")
    .Global = True
    .Pattern = "\d{4}:\d{2}:\d{2} \d{2}:\d{2}:\d{2}"
    ret = .Execute(buf)(0)
  End With
  Debug.Print ret
End Sub
    • good
    • 0
この回答へのお礼

end-uさん、こんばんは。
早速、いろいろ調べてからコメントを頂き、大変有難う御座いました。
この後にも追加して頂いたコメントを含めてフォローしてみます。
その結果は、追ってご報告いたします。

お礼日時:2017/06/11 20:53

興味があったのでソースをざっと見てみましたが



・不要なプロパティ定義
・Private Function readJPEGの画像サイズ取得部分
・Private Function readIFDのCASE中の不要な条件

のみが削除可能で、あまり簡略化できるようには思えません。
バイナリファイル中の複数データのデータの中から、データタイプを確認しながら特定のデータを探すという構造上、仕方がないように思えます。

Exif情報のデータフォーマットは知らないのでですが、写真撮影日時の情報がファイルの先頭から何バイト目というようには決まっていないのではないでしょうか?
それに対応するためのプログラム構造だと思います。

間違っていましたらご容赦を。
    • good
    • 0
この回答へのお礼

siffon9さん、こんばんは。
詳細に原本を精査しての貴重なコメントを頂き、有難う御座いました。
原本では、For i=00 to ファイルエンドまで、
撮影日時_Tag=9003 or =0390を精査して、
写真.jpg・Exifの撮影日時を読み出しています。
yokomayaさんへの御礼にも書きましたが、このクラスを通すと、
フォーマットエラーがある*.jpgは除外されて、
アルバムには表示されません。エラーがあってもアルバムには表示したいのですが、いまだ解決するCodingが書けていません。

お礼日時:2017/06/11 20:43

ごめんなさい。


おっしゃっている事はわからなくないのですが
不都合は何ですか?

仮にコードが判らなくても、使えるなら特に問題を
感じないのですが。
例えば、アドインとかVBAで書かれているのがあっても
中身考える人あまりいないと思うんです。

>組み込んで利用できています。
これで充分に思うのですが。
    • good
    • 0
この回答へのお礼

yokomayaさん、こんばんは。コメント 有難う御座いました。
不都合というほどではないのですが:

① 雑多に集めた*.JPG画像100枚を仮フォルダに収納し、
JpegExifクラス経由で、アルバムフォルダに登録すると、
97枚になっているような現象が起きることがあります。
原因は、自作Soft⇔EpegExifクラス呼び出しFunction⇔EpegExifクラス
がらみで、私のレベルでは複雑にみえて原因特定には数ヶ月かかりました。
その理由:「格納データのフォーマットにエラーがある」との判定で、
除外され、アルバムには、この対象ファイルが読み出されない様です。
EpegExifクラスはファイルから読み出す項目が多く、
いまだに、どの項目でフォーマットエラーがあって除外されているのかは分かっていません。
このようなエラーファイルでも、除外せずにアルバムに表示したいと思っています。

② Jpeg画像であるがExifではない場合は、アルバムには、
システムのDate関数のデフォルト値:"1900/1/0 0:00:00"が撮影日時として表示されました。
アルバムでは、1900年より、システムのファイル更新日時の方がまだ参考になりますので、アルバムには、Exifではなくても、ファイル更新日時を表示するようにCodingしました。

お礼日時:2017/06/11 20:16

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!