Excel (VBA) |
| | (指定なし : 指定なし) jpegのExif情報の書き換え 投稿日時: 19/05/10 20:27:33 投稿者: norinorih |
|---|---|
| 現在,画像の管理をエクセルで行っているのですが, | |
| | 投稿日時: 19/05/10 22:49:13 投稿者: mattuwan44 |
|---|---|
| PropertyID 274 Sub test1()
Dim xf As IImageFile 'WIA.ImageFile
Set xf = CreateObject("Wia.ImageFile")
xf.LoadFile ThisWorkbook.Path & "\test.jpg"
If xf.Properties(4).Value > 1 Then
xf.Properties(4).Value = 1
End If
Set xf = Nothing
End Sub こんな感じになると思うけど、 Exifを見ると変わっているけど、 表示が回転しないですねー><なんでだろー。。。 回転させるメソッドがありそうな、なさそうな。。。。^^; この辺で、ギブアップ>< 他の方の回答をお待ちください。 ぼくなら、フリーのツール探しますけどね^^; | |
| | 投稿日時: 19/05/10 23:26:49 投稿者: norinorih |
|---|---|
| mattuwan44さん | |
| | 投稿日時: 19/05/11 21:12:48 投稿者: simple |
|---|---|
| お望みの回答ではないですがコメントします。 | |
| | 投稿日時: 19/05/11 22:56:24 投稿者: MMYS |
|---|---|
| | |
| | 投稿日時: 19/05/12 22:05:15 投稿者: norinorih |
|---|---|
| simpleさん,MMYSさん | |
| | 投稿日時: 19/05/14 13:11:01 投稿者: baoo |
|---|---|
| WIA.ImageFileというのは初めて知りました。 Option Explicit
Private Declare PtrSafe Function GdiplusStartup Lib "gdiplus" _
(ByRef token As LongPtr, _
ByVal input_ As LongPtr, _
Optional ByVal output_ As LongPtr) As Long
Private Declare PtrSafe Sub GdiplusShutdown Lib "gdiplus" _
(ByVal token As LongPtr)
Private Declare PtrSafe Function GdipCreateBitmapFromFile Lib "gdiplus" _
(ByVal filename_ As LongPtr, _
ByRef bitmap_ As LongPtr) As Long
Private Declare PtrSafe Function GdipDisposeImage Lib "gdiplus" _
(ByVal image_ As LongPtr) As Long
Private Declare PtrSafe Function GdipGetPropertyItemSize Lib "gdiplus" _
(ByVal Image As LongPtr, _
ByVal propID As Long, _
ByRef lngSize As Long) As Long
Private Declare PtrSafe Function GdipGetPropertyItem Lib "gdiplus" _
(ByVal Image As LongPtr, _
ByVal propID As Long, _
ByVal propSize As Long, _
ByRef buffer As Any) As Long
Private Declare PtrSafe Function GdipSetPropertyItem Lib "gdiplus" _
(ByVal nImage As LongPtr, _
item As PropertyItem) As Long
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(Destination As Any, _
Source As Any, _
ByVal Length As LongPtr)
Private Declare PtrSafe Function GdipSaveImageToFile Lib "gdiplus" ( _
ByVal Image As LongPtr, _
ByVal FileName As LongPtr, _
ByRef clsidEncoder As GUID, _
ByVal encoderParams As Long) As Long
Private Declare PtrSafe Function CLSIDFromString Lib "ole32.dll" ( _
ByVal lpszCLSID As LongPtr, _
ByRef pCLSID As GUID) As Long
'プロパティ
Private Type PropertyItem
id As Long
Length As Long
Type As Integer
Value As LongPtr
End Type
Private Type GdiplusStartupInput
GdiplusVersion As Long
dummy1 As LongPtr
dummy2 As Long
dummy3 As Long
End Type
Private Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(0 To 7) As Byte
End Type
'付与する画像の向き
Private Enum RotateFlipType
RotateNoneFlipNone = 1
RotateNoneFlipX = 2
Rotate180FlipNone = 3
Rotate180FlipX = 4
Rotate90FlipNone = 6
Rotate90FlipX = 5
Rotate270FlipNone = 8
Rotate270FlipX = 7
End Enum
Private Const PropertyTagOrientation As Long = &H112& '画像の方向
Private Const PropertyTagTypeShort = 3
Private Const GUID_ENCODER_BMP = "{557CF400-1A04-11D3-9A73-0000F81EF32E}"
Private Const GUID_ENCODER_JPG = "{557CF401-1A04-11D3-9A73-0000F81EF32E}"
Private Const GUID_ENCODER_GIF = "{557CF402-1A04-11D3-9A73-0000F81EF32E}"
Private Const GUID_ENCODER_TIF = "{557CF405-1A04-11D3-9A73-0000F81EF32E}"
Private Const GUID_ENCODER_PNG = "{557CF406-1A04-11D3-9A73-0000F81EF32E}"
Sub test()
Dim strFileIn As String
Dim strFileOut As String
strFileIn = ThisWorkbook.Path & "\test.jpg"
strFileOut = Left(strFileIn, InStrRev(strFileIn, ".") - 1) & "_" & Mid(strFileIn, InStrRev(strFileIn, "."))
'画像の向きを標準に
ChangeRotateedTagToImgFile strFileIn, strFileOut, RotateNoneFlipNone
End Sub
'画像に回転情報を追加する
Private Sub ChangeRotateedTagToImgFile(strFileIn As String, strFileOut As String, enmRot As RotateFlipType)
Dim gpStartupInput As GdiplusStartupInput
Dim lngToken As LongPtr 'トークン
Dim pBm As LongPtr
Dim lngRet As Long
Dim uid As GUID
Dim pItm As PropertyItem
Dim buff() As Byte
Dim lngSize As Long
Dim intOrientation As Integer
On Error GoTo ErrHDL
'初期化とBitmapオブジェクト取得
gpStartupInput.GdiplusVersion = 1
lngRet = GdiplusStartup(lngToken, VarPtr(gpStartupInput))
lngRet = GdipCreateBitmapFromFile(StrPtr(strFileIn), pBm)
'回転情報をPropertyItemに取得(回転情報がなくても変更する場合はコメントアウト
' '取得プロパティサイズ
' lngRet = GdipGetPropertyItemSize(pBm, PropertyTagOrientation, lngSize)
' If lngSize = 0 Then
' GoTo ErrHDL
' End If
' ReDim buff(lngSize - 1) As Byte
'
' 'プロパティをバイト配列へ
' lngRet = GdipGetPropertyItem(pBm, PropertyTagOrientation, lngSize, buff(0))
'
' 'バイト配列からPropertyItem型へ
' Call CopyMemory(pItm, buff(0), LenB(pItm))
'回転情報をPropertyItemに設定(回転情報を元に変更する場合はコメントアウト)
pItm.id = PropertyTagOrientation
pItm.Length = 2 'Shortで2バイト
pItm.Type = PropertyTagTypeShort
pItm.Value = VarPtr(pItm) + LenB(pItm) 'PropertyItemの次に実際のValueが配置
'画像の向きを付与
intOrientation = enmRot
Call CopyMemory(ByVal pItm.Value, intOrientation, LenB(intOrientation))
'プロパティを画像に適用
lngRet = GdipSetPropertyItem(pBm, pItm)
'GUID
lngRet = CLSIDFromString(StrPtr(GUID_ENCODER_JPG), uid)
'画像をファイルに書き出し
lngRet = GdipSaveImageToFile(pBm, StrPtr(strFileOut), uid, 0)
ErrHDL:
'Bitmapオブジェクト破棄と終了
lngRet = GdipDisposeImage(pBm)
GdiplusShutdown lngToken
End Sub | |
| | 投稿日時: 19/05/15 21:02:17 投稿者: norinorih |
|---|---|
| baooさん ありがとうございます.ご返事遅くなりました. | |
| | 投稿日時: 19/05/16 20:33:49 投稿者: baoo |
|---|---|
norinorih さんの引用:JPEGファイルによってはそのようになるとは限りません。 JPEGファイルは先頭にSOIマーカー(FF D8)と呼ばれる2バイトで始まりますが、 それ以降は複数のセグメントと呼ばれる領域になります。 この中からExif情報を示すAPP1(FF E1で始まる)マーカーから始まるセグメントを探す必要があります。 norinorihさんのJPGファイルの場合は3バイト目がそれにあたります。 しかしその画像をMSPaintで開いてから別名で保存してみてください。 そうすると恐らく3バイト目からはFF E0に変わると思います。(APP0セグメント) そして21バイト目からFF E1で始まるAPP1セグメントが見つかると思います。 各セグメントは最初の2バイトがマーカーであり次の2バイトがセグメント長ですので、 そこから次のセグメントの位置が分かります。 そうして各セグメントを調べてAPP1セグメントを探す必要があります。 そしてさらにAPP1セグメントの中からOrientationを表すIFD112探します。 直接バイナリを操作してOrientationを変更するサンプルコードを作ってみました。 GDI+のサンプルと違って、Exif情報が無い場合は何もしませんし、 別名での保存もできませんが、仕組みは分かると思います。 Option Explicit
Private Enum RotateFlipType
RotateNoneFlipNone = 1
RotateNoneFlipX = 2
Rotate180FlipNone = 3
Rotate180FlipX = 4
Rotate90FlipNone = 6
Rotate90FlipX = 5
Rotate270FlipNone = 8
Rotate270FlipX = 7
End Enum
Sub test2()
Dim strFile As String
strFile = ThisWorkbook.Path & "\test.jpg"
ChangeRotateedTagToImgFile2 strFile, RotateNoneFlipNone
End Sub
Private Sub ChangeRotateedTagToImgFile2(strFile As String, enmRot As RotateFlipType)
Dim lngFN As Long
Dim lngAddress As Long
Dim lngSize As Long
Dim blIntel As Boolean
Dim lngNumIFD As Long
Dim i As Long
Dim bt() As Byte
On Error GoTo ErrHDL
lngFN = FreeFile
Open strFile For Binary As #lngFN
ReDim bt(10) As Byte
Get #lngFN, 1, bt
'SOIチェック
If bt(0) <> &HFF Or bt(1) <> &HD8 Then
GoTo ErrHDL
End If
'APP1のアドレスとサイズを取得
lngAddress = 3
Do While Not EOF(lngFN)
ReDim bt(3) As Byte
Get #lngFN, lngAddress, bt
If bt(0) = &HFF Then
'セグメントのサイズ
lngSize = bt(2) * 256& + bt(3)
'APP1だったら抜け、APP1でなかったら次のセグメント
If bt(1) = &HE1 Then
Exit Do
Else
lngAddress = lngAddress + 2 + lngSize
End If
Else
'APP1が見つからなかったら終了
GoTo ErrHDL
End If
Loop
'改めてAPP1全データを配列に格納
ReDim bt(lngSize + 1) As Byte
Get #lngFN, lngAddress, bt
'Exifチェック
If bt(4) <> &H45 Or bt(5) <> &H78 Or bt(6) <> &H69 Or bt(7) <> &H66 Then
GoTo ErrHDL
End If
'エンディアンチェック
If bt(10) = &H49 And bt(11) = &H49 Then
blIntel = True
ElseIf bt(10) = &H4D And bt(11) = &H4D Then
blIntel = False
Else
GoTo ErrHDL
End If
If blIntel = True Then
lngNumIFD = bt(18) + bt(19) * 256& 'タグの数
For i = 0 To lngNumIFD - 1
If bt(20 + i * 12 + 1) = &H1 And bt(20 + i * 12) = &H12 Then 'IFD112
bt(20 + i * 12 + 8) = enmRot 'IFD112の値は2バイトで8バイト目から始まる(0X 00)
Put #lngFN, lngAddress, bt '書き込み
End If
Next i
Else
lngNumIFD = bt(18) * 256& + bt(19) 'タグの数
For i = 0 To lngNumIFD - 1
If bt(20 + i * 12) = &H1 And bt(20 + i * 12 + 1) = &H12 Then 'IFD112
bt(20 + i * 12 + 9) = enmRot 'IFD112の値は2バイトで8バイト目から始まる(00 0X)
Put #lngFN, lngAddress, bt '書き込み
End If
Next i
End If
ErrHDL:
Close #lngFN
End Sub | |
| | 投稿日時: 19/05/18 11:17:00 投稿者: norinorih |
|---|---|
| baooさん ありがとうございます.ご返事遅くなりました. 引用: 全くご指摘の通りです.確かにAPP0の領域もありますし, 場合によっては,Orientationが3番目に無い場合もあるようですね. 「01 12」を探すのが重要なのもわかりました. 加えて再度コードまで提示していただいて理解が深まりました. baooさん,皆さん,今回おつきあいいただきありがとうございました. 閉じます. | |