seniors 发表于 2013-6-17 22:24:46

第十六讲 GDI+图像之图像属性项读取EXIF信息及缩略图读取

一、缩略图读取
_GDIPlus_ImageGetThumbnail($hImage, $ThumbnailSize, $ThumbnailSize)
返回缩略图像句柄
二、图像属性项
_GDIPlus_ImageGetAllPropertyItems;获取保存在图像对象中的所有属性项(元数据)
_GDIPlus_ImageGetPropertyCount;获取保存在图像对象中的属性(元数据片段)的数量
_GDIPlus_ImageGetPropertyIdList;获取保存在图像对象元数据中的属性标识列表
_GDIPlus_ImageGetPropertyItem;获取图像对象的指定属性项
_GDIPlus_ImageGetPropertyItemSize;获取图像对象指定属性项的字节大小
_GDIPlus_ImageGetPropertySize;获取保存在图像对象中所有数量的属性的字节大小由于属性标识较多,具体可以参看
本人正在一项一项翻译,工作量好大
每项属性值的获取方法
大部分属性项有固定的方法,当属性项类型为7时,要根据属性项ID来调整读取方法
_GDIPlus_ImageGetPropertyItem;获取图像对象的指定属性项
或者_GDIPlus_ImageGetAllPropertyItems;获取保存在图像对象中的所有属性项(元数据)
得到的多是四个数据,分别是ID,类型,数据长度,指向值的指针
所以要根据ID或者类型来设置一个数据结构
读取做完后,写入属性项就是一个相反的过程,就比较好弄了
先看一下预览版吧

源码如下
#include <APIConstants.au3>
#include <WinAPIEx.au3>
#include <GDIPlus.au3>
#include <GDIPlusEx.au3>

Global $sFile, $hImageFile, $hImageThumbnail
Global $ThumbnailSize = 128
$hWnd = GUICreate("第十六讲GDI+图像之图像属性项", 390, 200)
$hPic = GUICtrlCreatePic("", 5, 5, 150, 150)
$hPicWnd = GUICtrlGetHandle(-1)
$iMemo = GUICtrlCreateEdit("", 160, 5, 225, 190)
GUISetState()
_GDIPlus_Startup()
update()

While 1
        $Msg = GUIGetMsg()
        Switch $Msg
                Case -3
                        ExitLoop

                Case $hPic
                        open()

        EndSwitch
WEnd
_GDIPlus_ImageDispose($hImageFile)
_GDIPlus_ImageDispose($hImageThumbnail)
_GDIPlus_Shutdown()
GUIDelete()
Exit

Func update()
        Local $HWND_CX = _WinAPI_GetWindowWidth($hPicWnd)
        Local $HWND_CY = _WinAPI_GetWindowHeight($hPicWnd)
        Local $hGraphics = _GDIPlus_GraphicsCreateFromHWND($hPicWnd)
        Local $hBitmap = _GDIPlus_BitmapCreateFromGraphics($HWND_CX, $HWND_CY, $hGraphics)
        Local $hBackbuffer = _GDIPlus_ImageGetGraphicsContext($hBitmap)
        Local $hBrush = _GDIPlus_BrushCreateSolid(0xFFA0A0A0)
        Local $iImageType = _GDIPlus_ImageGetType($hImageThumbnail)
       
        If @error = 10 Then
                _GraphicsDrawString($hBackbuffer, "无法生成缩略图", 10, 70, $hBrush, "Arial", 8)
        Else
                _GDIPlus_GraphicsDrawImageRect($hBackbuffer, $hImageThumbnail, 5, 5, $ThumbnailSize, $ThumbnailSize)
                _GraphicsDrawString($hBackbuffer, "点击打开图片", 10, 5 + $ThumbnailSize + 5, $hBrush, "Arial", 8)
        EndIf
        _GDIPlus_BrushDispose($hBrush)
        _GDIPlus_GraphicsDrawImageRect($hGraphics, $hBitmap, 0, 0, $HWND_CX, $HWND_CY)
        _GDIPlus_BitmapDispose($hBitmap)
        _GDIPlus_GraphicsDispose($hBackbuffer)
        _GDIPlus_GraphicsDispose($hGraphics)
EndFunc   ;==>update
Func open()
        $sFile = FileOpenDialog("请选择文件", "", "图像 (*.jpg;*.bmp;*.png;*.gif;*.tiff)|所有文件 (*.*)")
        If @error Then
                MsgBox(4096, "", "没有选择文件!")
        Else
                _GDIPlus_ImageDispose($hImageFile)
                _GDIPlus_ImageDispose($hImageThumbnail)
                $hImageFile = _GDIPlus_ImageLoadFromFile($sFile)
                $hImageThumbnail = _GDIPlus_ImageGetThumbnail($hImageFile, $ThumbnailSize, $ThumbnailSize)
                update()
                _GetMetaData()
        EndIf
EndFunc   ;==>open

Func save()
        Local $sCLSID = _GDIPlus_EncodersGetCLSID("JPG")
        _GDIPlus_ImageSaveToFileEx($hImageFile, @DesktopDir & "\15讲图像.jpg", $sCLSID)
        MsgBox(4096, "", "图像保存成功!")
EndFunc   ;==>save

Func _GetMetaData()
        Local $aItems = _GDIPlus_ImageGetAllPropertyItems($hImageFile)
        If $aItems = -1 Then
                MemoWrite("图像没有属性项")
                Return 0
        EndIf
        Local $Struct_String, $string = ""
        For $i = 1 To $aItems;遍历所有属性项
                Switch $aItems[$i];根据属性项类型设置数据结构来读取属性值                       
                        Case 1;$GDIP_EPTBYTE
                                $Struct_String = DllStructCreate("BYTE[" & $aItems[$i] & "];", $aItems[$i])
                                $string = DllStructGetData($Struct_String, 1)
                        Case 2;$GDIP_EPTASCII
                                $Struct_String = DllStructCreate("CHAR[" & $aItems[$i] & "];", $aItems[$i])
                                $string = DllStructGetData($Struct_String, 1)
                        Case 3;$GDIP_EPTSHORT
                                $Struct_String = DllStructCreate("USHORT[" & $aItems[$i] & "];", $aItems[$i])
                                $string = DllStructGetData($Struct_String, 1)
                        Case 4;$GDIP_EPTLONG
                                $Struct_String = DllStructCreate("ULONG[" & $aItems[$i] & "];", $aItems[$i])
                                $string = DllStructGetData($Struct_String, 1)
                        Case 5;$GDIP_EPTRATIONAL
                                $Struct_String = DllStructCreate("ULONG numberator;ULONG denominator;", $aItems[$i])
                                $string = DllStructGetData($Struct_String, "numberator") / DllStructGetData($Struct_String, "denominator")
                        Case 7;$GDIP_EPTUNDEFINED;未定义的要根据ID来决定用哪个方式;这个还要处理,暂且先这样,下次做成UDF再改
                                $Struct_String = DllStructCreate("CHAR[" & $aItems[$i] & "];", $aItems[$i])
                                $string = DllStructGetData($Struct_String, 1)
                        Case 6;读文件的话是9$GDIP_EPTLONGRANGE
                                $Struct_String = DllStructCreate("LONG[" & Ceiling($aItems[$i] / 4) & "];", $aItems[$i])
                                $string = DllStructGetData($Struct_String, 1)
                        Case 8;读文件的话是是10$GDIP_EPTRATIONALRANGE
                                $Struct_String = DllStructCreate("LONG numberator;LONG denominator;", $aItems[$i])
                                $string = DllStructGetData($Struct_String, "numberator") / DllStructGetData($Struct_String, "denominator")
                EndSwitch
                MemoWrite(tags2string($aItems[$i], $string))
                $string = ""
                $Struct_String = 0
        Next
EndFunc   ;==>_GetMetaData

; Write a line to the memo control
Func MemoWrite($sMessage = '')
        GUICtrlSetData($iMemo, $sMessage & @CRLF, 1)
EndFunc   ;==>MemoWrite

;根据属性ID翻译为中文,属性值拆分分析,属性项太多未完成
Func tags2string($TagId, $string, $split = @TAB)
        Switch $TagId
                Case 0x8769
                        Return "特殊ExifPoint" & $split & $string & @CR
                Case 0x8825
                        Return "GPSPoint" & $split & $string & @CR
                Case 0xA005
                        Return "通用ExifPoint" & $split & $string & @CR
                        ;TIFF
                Case 0x100
                        Return "图像宽" & $split & $string & @CR
                Case 0x101
                        Return "图像高" & $split & $string & @CR
                       
                Case 0x102
                        Return "像素字节" & $split & $string & @CR
                Case 0x103
                        Switch $string
                                Case 1
                                        Return "压缩" & $split & "未压缩" & @CR
                                Case 6
                                        Return "压缩" & $split & "JPEG压缩" & @CR
                                Case Else
                                        Return "压缩" & $split & "保留" & @CR
                        EndSwitch
                Case 0x106
                        Switch $string
                                Case 2
                                        Return "JPEG压缩" & $split & "RGB" & @CR
                                Case 6
                                        Return "JPEG压缩" & $split & "YCbCr" & @CR
                                Case Else
                                        Return "JPEG压缩" & $split & "保留" & @CR
                        EndSwitch
                Case 0x112;没来的及进行分类
                        Return "图像排列" & $split & $string & @CR
                Case 0x115
                        Return "每点采样率" & $split & $string & @CR
                Case 0x11C
                Case 0x212, 0x213
                Case 0x11A
                        Return "X分辨率" & $split & $string & @CR
                Case 0x11B
                        Return "Y分辨率" & $split & $string & @CR
                Case 0x128
                        Switch $string
                                Case 2
                                        Return "分辨率单位" & $split & "英吋" & @CR
                                Case 3
                                        Return "分辨率单位" & $split & "厘米" & @CR
                                Case Else
                                        Return "分辨率单位" & $split & "保留" & @CR
                        EndSwitch
                        ;特殊exif
                Case 0x9000
                        Return "Exif版本" & $split & $string & @CR
                Case 0xA000
                        Return "闪光版本" & $split & $string & @CR
                Case 0xA001
                        Switch $string
                                Case 1
                                        Return "色彩空间" & $split & "sRGB" & @CR
                                Case 3
                                        Return "色彩空间" & $split & "未校准" & @CR
                                Case Else
                                        Return "色彩空间" & $split & "保留" & @CR
                        EndSwitch
                Case 0xA500
                        Return "Gamma值" & $split & $string & @CR
                Case 0xA002
                        Return "X点数" & $split & $string & @CR
                Case 0xA003
                        Return "Y点数" & $split & $string & @CR
                Case 0x9101
                        Return "compressed" & $split & $string & @CR
                Case 0x9102
                        Return "像素每单位" & $split & $string & @CR
                Case 0x9003
                        Return "建立时间" & $split & $string & @CR
                Case 0x9004
                        Return "保存时间" & $split & $string & @CR
                Case 0x9290, 0x9291, 0x9292
                Case 0x829A
                        Return "曝光时间" & $split & $string & @CR
                Case 0x829D
                        Return "焦距比数" & $split & $string & @CR
                Case 0x8822
                        Switch $string
                                Case 0
                                        Return "曝光程序" & $split & "未定义" & @CR
                                Case 1
                                        Return "曝光程序" & $split & "手动" & @CR
                                Case 2
                                        Return "曝光程序" & $split & "自动" & @CR
                                Case 3
                                        Return "曝光程序" & $split & "光圈预定" & @CR
                                Case 4
                                        Return "曝光程序" & $split & "快门预定" & @CR
                                Case 5
                                        Return "曝光程序" & $split & "景物" & @CR
                                Case 6
                                        Return "曝光程序" & $split & "运动" & @CR
                                Case 7
                                        Return "曝光程序" & $split & "肖像" & @CR
                                Case 8
                                        Return "曝光程序" & $split & "风景" & @CR
                                Case Else
                                        Return "曝光程序" & $split & "保留" & @CR
                        EndSwitch
                Case 0x8824
                        Return "感光度" & $split & $string & @CR
                Case 0x8827
                        Return "感光度ISO" & $split & $string & @CR
                        ;未完成中,谁来帮我完成,工作量好大
        EndSwitch
       
        Return Hex($TagId, 4) & $split & $string & @CR
EndFunc   ;==>tags2string

Func _GraphicsDrawString($hGraphics, $sString, $nX, $nY, $hBrush = 0, $sFont = "Arial", $nSize = 10, $iFormat = 0)
        Local $hFormat = _GDIPlus_StringFormatCreate($iFormat)
        Local $hFamily = _GDIPlus_FontFamilyCreate($sFont)
        Local $hFont = _GDIPlus_FontCreate($hFamily, $nSize)
        Local $tLayout = _GDIPlus_RectFCreate($nX, $nY, 0, 0)
        Local $aInfo = _GDIPlus_GraphicsMeasureString($hGraphics, $sString, $hFont, $tLayout, $hFormat)
        __GDIPlus_BrushDefCreate($hBrush)
        Local $aResult = _GDIPlus_GraphicsDrawStringEx($hGraphics, $sString, $hFont, $aInfo, $hFormat, $hBrush)
        Local $iError = @error
        __GDIPlus_BrushDefDispose()
        _GDIPlus_FontDispose($hFont)
        _GDIPlus_FontFamilyDispose($hFamily)
        _GDIPlus_StringFormatDispose($hFormat)
        Return SetError($iError, 0, $aResult)
EndFunc   ;==>_GraphicsDrawString

lpxx 发表于 2013-6-17 23:24:32

一直在关注,加分不解释。

破帽遮颜 发表于 2013-6-18 02:11:30

在关注~~~~~~~~~~~~~

虫子樱桃 发表于 2013-6-18 17:35:27

等教程完了,求CHM

浪迹红客 发表于 2013-6-20 17:41:36

支持一下...

netegg 发表于 2013-6-22 23:04:46

seniors,你是专门研究图像处理的吧

seniors 发表于 2013-6-23 09:05:45

回复 7# netegg


    不是,我只是喜欢图像,在386电脑的时代我就喜欢编图形界面,那时用的c编程,现在c忘的差不多了

llztt 发表于 2013-8-6 15:52:45

回复 1# seniors


    请教如何使用_GDIPlus_ImageGetPropertyItem,譬如获取创建时间?谢谢

seniors 发表于 2013-8-6 19:33:01

回复 9# llztt
Local $tGPRITEM = _GDIPlus_ImageGetPropertyItem($hImageFile, 0x9003);0x9003是相片拍摄时间
Local $size = DllStructGetData($tGPRITEM, "length");指$pvalue指向的数据长度
Local $type = DllStructGetData($tGPRITEM, "type");相片拍摄时间的type是2,$GDIP_EPTASCII指$pvalue指向的是字符串
Local $pvalue = DllStructGetData($tGPRITEM, "value");不知道为什么_GDIPlus_ImageGetPropertyItem返回值有误,所以这行不能用,用下面的遍列,寻找这个数值
Local $ti = _GDIPlus_ImageGetAllPropertyItems($hImageFile)
For $i = 1 To $ti
        If $ti[$i] = 0x9003 Then;找到0x9003是相片拍摄时间
                $pvalue = $ti[$i];rcn _GDIPlus_ImageGetAllPropertyItems返回的值给$pvalue
                ExitLoop
        EndIf
Next
$Struct_String = DllStructCreate("CHAR[" & $size & "]", $pvalue)
$string = DllStructGetData($Struct_String, 1)
_GDIPlus_ImageGetPropertyItem返回的指向数值的指针有问题
$string就是返回的创建时间

llztt 发表于 2013-8-6 20:12:07

回复 10# seniors


恩,之前改了顶贴的例子,通过遍历实现了,但总觉得不如一个函数搞定来的简洁,可惜不会处理 _GDIPlus_ImageGetPropertyItem的返回值。。。

多谢指教,既然该函数有问题,那只能用遍历的办法了。。

呵呵,我在整理照片准备冲印呢,想加上时间水印,但有些照片看创建时间和修改时间是不准确的,所以想从EXIF下手,搜来搜去只找到了楼主的帖子,非常感谢哈。。

seniors 发表于 2013-8-6 20:20:30

回复 11# llztt
exif信息太多,一直没时间弄好,我也想弄好后,给照片加上好看的水印

llztt 发表于 2013-8-6 21:05:01

回复 12# seniors


哈哈,希望你早弄好啊,我目前的计划只想到用AU3把批量图片的EXIF信息读出来后,重新用读出来的拍照时间 命名文件,然后剩下的工作就是用PS或美图秀秀 加水印了。。。
也曾考虑用AU3直接加水印到图片,但一是GDI函数不熟,二是怕这么做影响图片质量,所以后来没再想下去。。

哦,其实,我要加的水印是通过拍照时间计算出的宝宝出生日子,哈哈

1007236046 发表于 2014-7-27 22:12:56

还是要遍历获取拍照时间啊,另外楼主能不能把你的gdiplus.au3和gdiplusex.au3上传一份啊

luke 发表于 2015-1-13 10:34:18

{:face (356):}高!

peter13447 发表于 2015-4-3 13:28:02

厉害...
遇到高手忍不住问下.
我在处理图片时,遇到这样的情况.
从原图中截取部分图片.另存为一张图片后,发现产生色差.
请看下面2张图片,人的肤色发生了明显的改变
我使用的函数是:        _GDIPlus_StartUp ()
        $hImage = _GDIPlus_BitmapCreateFromFile($p_pic_old)
        $hClone = _GDIPlus_BitmapCloneArea($hImage,829,0,3456,3456,$GDIP_PXF24RGB)        ;5114*3456 800*800 (5114-3456)/2=829
        _GDIPlus_ImageSaveToFile($hClone,$p_pic_new)

        _GDIPlus_ImageDispose($hClone)
        _GDIPlus_ImageDispose($hImage)
        _GDIPlus_ShutDown()
页: [1] 2
查看完整版本: 第十六讲 GDI+图像之图像属性项读取EXIF信息及缩略图读取