Sign in to follow this  
Followers 0
Lazycat

Udf For Getting Jpeg Image Size

26 posts in this topic

#1 ·  Posted (edited)

This function gets size info from any JPEG file. This work almost good on any image I tested, but anyway test this.

Example:

$file = FileOpenDialog("Please select file", "", "JPEG files (*.jpg;*.jpeg)");
$aSize = _GetJpegSize($file)
if @error then Exit
MsgBox (0, "Picture Size", $aSize[0] & "x" & $aSize[1])

UDF:

;===============================================================================
;
; Description:      Return JPEG size
; Parameter(s):     File name
; Requirement(s):   None
; Return Value(s):  On Success - array with width and height
;                   On Failure empty string and sets @ERROR:
;                       1 - File is not valid JPEG
;                       2 - Info not found
; Author(s):        YDY (Kot) <mpc@nm.ru>
; Version:          1.1.00
; Date:             10.08.2004
; Note(s):          None
;
;===============================================================================

Func _GetJpegSize($file)
Local $size[2]
Local $fs, $pos = 3

if not (_FileReadAtOffsetHEX ($file, 1, 2) = "FFD8") then
    SetError(1); Not Jpeg
    Return("")
endif

$fs = FileGetSize($file)

While $pos < $fs
    $data = _FileReadAtOffsetHEX ($file, $pos, 4)
    if StringLeft($data, 2) = "FF" then; Valid segment start
        if StringInStr("C0 C2 CA C1 C3 C5 C6 C7 C9 CB CD CE CF", StringMid($data, 3, 2)) then; Segment with size data
           $seg = _FileReadAtOffsetHEX ($file, $pos+5, 4)
           $size[1] = Dec(StringLeft($seg, 4))
           $size[0] = Dec(StringRight($seg, 4))
           Return($size)
        else
           $pos = $pos + Dec(StringRight($data, 4)) + 2
        endif
    else
        exitloop
    endif
Wend
SetError(2); Segment not found
Return("")
EndFunc

Func _FileReadAtOffsetHEX ($file, $offset, $bytes)
    Local $tfile = FileOpen($file, 0)
    Local $tstr = ""
    FileRead($tfile, $offset-1)
    For $i = $offset To $offset + $bytes - 1
        $tstr =  $tstr & Hex(Asc(FileRead($tfile, 1)), 2)
    Next
    FileClose($tfile)
    Return ($tstr)
Endfunc

Thanks Bartokv for idea for speeding up read function.

Edited by Kot

Share this post


Link to post
Share on other sites



#4 ·  Posted (edited)

BTW I found, this code don't work with some (rare) images are not conform specifications. I don't know what to do with them...  :ph34r:

<{POST_SNAPBACK}>

Interesting... I tested my script on nearly every file I could find: New images with thumbnails, old images from around '92 that didn't have thumbnails, as well as one hi-res image that was over 80Mb. ...It took a while, but I was able to get the image resolution for each of these files. :(

The problem may be that you're just checking for the 'FF C0' Marker. Some JPEG images use different markers. The Most common markers that contain image size information in the form 'FF C0' to 'FF C3'.. The other interesting markers are in the form 'FF C0' to 'FF CF', with the exception of 'FF C4', 'FF C8', and 'FF CC'. (Please note that these are byte values, and not file offsets.)

Check if my script will return the image size, and if it does then you know a different marker was used. Hope this helps!

Edited by Bartokv

Share this post


Link to post
Share on other sites

It took a while, but I was able to get the image resolution for each of these files

Really while - ~30 seconds for 17kb image on 2,4 Ghz :ph34r: , but it found all sizes! :lol:

Hope this helps!

This helps! I found that all my "rare" :( images - are just progressive images. So I will look...

Share this post


Link to post
Share on other sites

I never said that FileRead was fast... :ph34r:

Share this post


Link to post
Share on other sites

With it being part of the compiled AutoIt Source (using fseek), I think that it should be a LOT faster. Anyway, it should save all of those wasted ticks tied up by "jumping" ahead manually via the current FileRead(). (Hopefully it'll be the next best thing to a file pointer!)

Oh, and just make sure that those nasty zero-byte characters don't play havoc with the new built-in. :ph34r:

Share this post


Link to post
Share on other sites

The other interesting markers are in the form 'FF C0' to 'FF CF', with the exception of 'FF C4', 'FF C8', and 'FF CC'

I'm looked for and found that you right. All markers except ones above contains differrent DCT methods of image and may be used for retrieve size. Most usefull C0 (baseline DCT) and C2 (progressive DCT), but all other theoretically can appear. I'll change code soon.

u think that change will speed it up?

Sure this will be faster, espacially on files with the big thumbnails. But now speed already very good and tooks less than second on any image I tried.

Share this post


Link to post
Share on other sites

UDF updated. Now it seems works fine with any image. I've tested this on mix of 812 different images (210 mb, from web and photos), and I got all sizes. Thanks for all help!

It's took ~17 seconds on all collection on P4-1.8, so this about 0.02 second per image. I'm interest how it will change with Larry's changes...

Share this post


Link to post
Share on other sites

Feel free to incorporate the checks that I used for the PNG, GIF, and BMP formats into your UDF as well. These other formats all utilize static offsets for the image dimensions, so they're easy to parse.

Oh, and nice call on the hex/string idea... It proves to be a lot faster than working with arrays. :ph34r:

Share this post


Link to post
Share on other sites

#11 ·  Posted (edited)

I already thought of make universal UDF like yours. Although we can't use these formats with the Autoit directly, they are really simple, so let will be :( Maybe I will try to add other common formats (for ex. TIFF) - for my own learning purposes :ph34r:

Edited by Kot

Share this post


Link to post
Share on other sites

#12 ·  Posted (edited)

I tried to change things... strange, gain in speed is very small :ph34r: I even not sure, maybe this is measuring error.

First I tried to modify just _FileReadAtOffsetHEX function, but this is not give any visible result. Second I tried to to avoid opening/closing file in this function. This give me about 1 second in prize (on the same picture set).

I think, this is not very indicate result because size info offset usually at the begin of file - not more than 5-10kb. Yesterday I've finished TIFF size retrieval UDF, and because TIFF info offset often at the very end of file, this must be more adequate. I'll test it.

Edited by Kot

Share this post


Link to post
Share on other sites

I already thought of make universal UDF like yours. Although we can't use these formats with the Autoit directly, they are really simple, so let will be :lol: Maybe I will try to add other common formats (for ex. TIFF) - for my own learning purposes :ph34r:

<{POST_SNAPBACK}>

Good Job, Kot. :D

What about reading ID2 and ID3 info for MP3 files? :lol:

I think that, it's more easy, although don't contain this information all the files... :(

Share this post


Link to post
Share on other sites

What about reading ID2 and ID3 info for MP3 files? :(

I think that, it's more easy, although don't contain this information all the files... :ph34r:

<{POST_SNAPBACK}>

Once the FileRead() routine is has been revamped, reading header/detail information in any standard file type should be possible. ...We just need to start a collection of UDF's to extract the pertinent info for each class. :lol:

Share this post


Link to post
Share on other sites

#16 ·  Posted (edited)

A new modification, that support few common formats: JPEG, TIFF, BMP, PNG and GIF.

#include "image_get_size.au3"
$file = FileOpenDialog("Please select file", "", "Image files (*.jpg;*.tif;*.gif;*.bmp;*.png)");
If @error Then Exit
$aSize = _ImageGetSize($file)
If @error Then 
    MsgBox (0, "Error", "Not valid image or info not found.")
    Exit
Endif
MsgBox (0, "Picture Size", $aSize[0] & "x" & $aSize[1])

;===============================================================================
;
; Description:    Return JPEG, TIFF, BMP, PNG and GIF image size.
; Parameter(s):  File name
; Requirement(s):   None special
; Return Value(s):  On Success - array with width and height
;                  On Failure empty string and sets @ERROR:
;                      1 - Not valid image or info not found
; Author(s):        YDY (Lazycat)
; Version:        1.1.00
; Date:          15.03.2005
;
;===============================================================================

Func _ImageGetSize($sFile)
    Local $sHeader = _FileReadAtOffsetHEX($sFile, 1, 24); Get header bytes
    Local $asIdent = StringSplit("FFD8 424D 89504E470D0A1A 4749463839 4749463837 4949 4D4D", " ")
    Local $anSize = ""
    For $i = 1 To $asIdent[0]
        If StringInStr($sHeader, $asIdent[$i]) = 1 Then
            Select
                Case $i = 1; JPEG
                    $anSize = _ImageGetSizeJPG($sFile)
                    Exitloop
                Case $i = 2; BMP
                    $anSize = _ImageGetSizeSimple($sHeader, 19, 23, 0)
                    Exitloop
                Case $i = 3; PNG
                    $anSize = _ImageGetSizeSimple($sHeader, 19, 23, 1)
                    Exitloop
                Case ($i = 4) or ($i = 5); GIF
                    $anSize = _ImageGetSizeSimple($sHeader, 7, 9, 0)
                    Exitloop
                Case $i = 6; TIFF MM
                    $anSize = _ImageGetSizeTIF($sFile, 0)
                    Exitloop
                Case $i = 7; TIFF II
                    $anSize = _ImageGetSizeTIF($sFile, 1)
                    Exitloop
            EndSelect
        Endif
    Next
    If not IsArray ($anSize) Then SetError(1)
    Return($anSize)
EndFunc

;===============================================================================
;
; Description:    Get image size at given bytes
; Parameter(s):  File header 
; Return Value(s):  Array with dimension
;
;===============================================================================
Func _ImageGetSizeSimple($sHeader, $nXoff, $nYoff, $nByteOrder)
    Local $anSize[2]
    $anSize[0] = _Dec(StringMid($sHeader, $nXoff*2-1, 4), $nByteOrder)
    $anSize[1] = _Dec(StringMid($sHeader, $nYoff*2-1, 4), $nByteOrder)
    Return ($anSize)
EndFunc

;===============================================================================
;
; Description:    Get JPG image size (may be used standalone with checking)
; Parameter(s):  File name
; Return Value(s):  On Success - array with dimension
;
;===============================================================================
Func _ImageGetSizeJPG($sFile)
    Local $anSize[2], $sData, $sSeg, $nFileSize, $nPos = 3
    $nFileSize = FileGetSize($sFile)
    While $nPos < $nFileSize
        $sData = _FileReadAtOffsetHEX ($sFile, $nPos, 4)
        If StringLeft($sData, 2) = "FF" then; Valid segment start
            If StringInStr("C0 C2 CA C1 C3 C5 C6 C7 C9 CB CD CE CF", StringMid($sData, 3, 2)) Then; Segment with size data
               $sSeg = _FileReadAtOffsetHEX ($sFile, $nPos + 5, 4)
               $anSize[1] = Dec(StringLeft($sSeg, 4))
               $anSize[0] = Dec(StringRight($sSeg, 4))
               Return($anSize)
            Else
               $nPos= $nPos + Dec(StringRight($sData, 4)) + 2
            Endif
        Else
            ExitLoop
        Endif
    Wend
    Return("")
EndFunc

;===============================================================================
;
; Description:    Get TIFF image size (may be used standalone with checking)
; Parameter(s):  File name
; Return Value(s):  On Success - array with dimension
;
;===============================================================================
Func _ImageGetSizeTIF($sFile, $nByteOrder)
    Local $anSize[2], $pos = 3
    $nTagsOffset = _Dec(_FileReadAtOffsetHEX($sFile, 5, 4), $nByteOrder) + 1
    $nFieldCount = _Dec(_FileReadAtOffsetHEX($sFile, $nTagsOffset, 2), $nByteOrder)
    For $i = 0 To $nFieldCount - 1
        $sField = _FileReadAtOffsetHEX($sFile, $nTagsOffset + 2 + 12 * $i, 12)
        $sTag = StringLeft($sField, 4)
        If $nByteOrder Then $sTag = StringRight($sTag, 2) & StringLeft($sTag, 2)
        Select
            Case $sTag = "0001"
                $anSize[0] = _Dec(StringRight($sField, 8), $nByteOrder)
            Case $sTag = "0101"
                $anSize[1] = _Dec(StringRight($sField, 8), $nByteOrder)
        EndSelect
    Next
    If ($anSize[0] = 0) or ($anSize[1] = 0) Then Return("")
    Return($anSize)
Endfunc

;===============================================================================
;
; Description:    Basic function that read binary data into HEX-string
; Parameter(s):  File name, Start offset, Number bytes to read
; Return Value(s):  Hex string
;
;===============================================================================
Func _FileReadAtOffsetHEX ($sFile, $nOffset, $nBytes)
    Local $hFile = FileOpen($sFile, 0)
    Local $sTempStr = ""
    FileRead($hFile, $nOffset - 1)
    For $i = $nOffset To $nOffset + $nBytes - 1
        $sTempStr = $sTempStr & Hex(Asc(FileRead($hFile, 1)), 2)
    Next
    FileClose($hFile)
    Return ($sTempStr)
Endfunc

;===============================================================================
;
; Description:    Basic function, similar Dec, but take in account byte order
; Parameter(s):  Hex string, Byte order
; Return Value(s):  Decimal value
;
;===============================================================================
Func _Dec($sHexStr, $nByteOrder)
    If $nByteOrder Then Return(Dec($sHexStr))
    Local $sTempStr = ""
    While StringLen($sHexStr) > 0
        $sTempStr = $sTempStr & StringRight($sHexStr, 2)
        $sHexStr = StringTrimRight($sHexStr, 2)
    WEnd
    Return (Dec($sTempStr))
EndFunc

Edit: replaced with the most recent version. Cleaned up code and changed function name for consistency with Autoit naming.

Edited by Lazycat

Share this post


Link to post
Share on other sites

Hello Lazycat,

I saw you made an UDF for getting info out of an TIFF File.

I working on a script, that makes me possible to extract all the Tags out from a Tiff or a Multitiff.

Is this possible with your UDF ?

SanchoPanza

Share this post


Link to post
Share on other sites

Extracting all tags was not a goal of script, it extract only useful ones. But script parse TIFF in correct (I hope) way. So with little modification you can extract anything you need. Just get latest version of Image Info reader here

http://www.autoitscript.com/fileman/users/Lazycat/udfs.html

and modify it.

Share this post


Link to post
Share on other sites

OK,

I will try it :)

Share this post


Link to post
Share on other sites

So,

I played now a little bit around with your UDF and after reading tiff specification I got the Tags I needed. But my next problem is, I want to extract these Tags from a Multitiff and I don´t know exactly how to get the Number of pages of this multitiff and how to extract all the information. Do you know how to do this and probably help me a little bit ? :)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0