[bmx] Retrieve image information without loading entire image by BlitzSupport [ 1+ years ago ]

Started by BlitzBot, June 29, 2017, 00:28:38

Previous topic - Next topic

BlitzBot

Title : Retrieve image information without loading entire image
Author : BlitzSupport
Posted : 1+ years ago

Description : This code retrieves width, height, number of colours and some other basic (arbitrarily decided!) information directly from image files, ie. you don't have to load the full images into Blitz first.

To use:

Call GetImageInfo with your image filename, test the result in case it's Null (important!), and access the width, height, colors and info fields from the ImageInfo result, eg.

image:ImageInfo = GetImageInfo ("test.jpg")

If image <> Null
Print image.width
Print image.height
Print image.colors
Print image.info
Else
Print "Couldn't get image information!"
EndIf


The currently supported formats are the most popular ones for Blitz usage, ie. BMP, JPEG, TGA and PNG (plus Gif). These all seem to work very well now, having been tested on around 11,500 files. In fact, the code seems to return 100% correct results as far as I can tell; if you find any results that state information couldn't be found for one of the supported images, check whether or not it really is the type of image specified by the file extension -- the chances are it isn't!

NB. You can copy/paste/call the individual formats' functions directly, if you prefer, rather than GetImageInfo (), eg. GetJPEGInfo (). Just be sure to include the ImageInfo type at the top of the code.

To do:

More consistent colour information. Currently returns mixed results between formats, eg. an indexed palette-based image might have only 256 colours, each of which can be (eg.) a 24-bit value, so I'm returning 24-bits. I think I'll return a max colours-per-pixel and number of palette entries where applicable.


If you add any other image formats (following the same conventions as the code below), please drop me an email and I'll update this code. (Please only email me if your code can be declared public domain.) [/i]

Code :
Code (blitzmax) Select
Type ImageInfo
Field width:Int
Field height:Int
Field colors:Int
Field info:String
End Type

Function GetBMPInfo:ImageInfo (f:String)

Local image:ImageInfo = New ImageInfo

If Lower (Left (f, 7)) = "http://"
f = "http::" + Right (f, Len (f) - 7)
EndIf

Local bmp:TStream = LittleEndianStream (ReadFile (f))

If bmp

Try

If ReadByte (bmp) = $42 And ReadByte (bmp) = $4D

For Local loop:Int = 1 To 12
ReadByte bmp
Next

Local width:Int
Local height:Int

If ReadInt (bmp) = 40
width = ReadInt (bmp)
height = ReadInt (bmp)
EndIf

' Not needed...

ReadByte bmp
ReadByte bmp

Local depth:Int = ReadShort (bmp)

Local compression:Int = ReadInt (bmp)

Local version:String

Select compression
Case 0
version = "No compression"
Case 1
version = "RLE-8 compression"
Case 2
version = "RLE-4 compression"
Default
version = "Unknown compression"
End Select

image.width = width
image.height = height
image.colors = 2 ^ depth
image.info = version

Else
image = Null
EndIf

Catch ReadFail:Object
DebugLog "Read error in " + f
image = Null

End Try

CloseFile bmp

Else
image = Null
EndIf

Return image

End Function

Function GetGIFInfo:ImageInfo (f:String)

Local image:ImageInfo = New ImageInfo

If Lower (Left (f, 7)) = "http://"
f = "http::" + Right (f, Len (f) - 7)
EndIf

' Read the file...

Local gif:TStream = LittleEndianStream (ReadFile (f))

If gif

Try

' First 3 bytes must be "GIF"...

Local g:String

Local loop:Int ' For byte-seek loops...

For loop = 0 To 2
g = g + Chr (ReadByte (gif))
Next

If g = "GIF"

Print "Got GIF???"

' Next 3 bytes contain version (87a or 89a)...

Local version:String = "GIF version "

For loop = 3 To 5
version = version + Chr (ReadByte (gif))
Next

' Dimensions...

Local width:Int = ReadShort (gif)
Local height:Int = ReadShort (gif)

' Depth is encoded in first 3 bits of this byte!

Local packed:Int = ReadByte (gif)
Local depth:Int = (packed & 1) + (packed & 1 Shl 1) + (packed & 1 Shl 2) + 1
Local colors:Int = 2 ^ depth

image.width = width
image.height = height
image.colors = colors
image.info = version:String

Else
image = Null
EndIf

Catch ReadFail:Object
DebugLog "Read error in " + f
image = Null

End Try

CloseFile gif

Else
image = Null
EndIf

Return image

End Function

Function GetJPEGInfo:ImageInfo (f:String)

Global remote:Int = False ' Used for online images

If Lower (Left (f, 7)) = "http://"
f = "http::" + Right (f, Len (f) - 7)
remote = True
EndIf

Local image:ImageInfo = New ImageInfo

Local jpeg:TStream = BigEndianStream (ReadFile (f))

If jpeg

Try

' Start of image (SOI) marker ($FFD8) -- MUST BE PRESENT!

If ReadByte (jpeg) = $FF And ReadByte (jpeg) = $D8

' ... followed by JFIF 'APP0' marker ($FFE0). In theory must be present, but reality says otherwise...

ReadByte jpeg ' Should be $FF but not always true...
ReadByte jpeg ' Should be $E0 but not always true...

' Start of first block...

Local block_length:Int = ReadShort (jpeg) - 2 ' Less these two bytes!

' Check for JFIF identification string (generally treated as optional)...

Local jfif:Int = 0

' Have to check each byte separately as BlitzMax's 'early-out' feature may mean the
' wrong number of bytes are read if one doesn't match, eg. If ReadByte (x) And ReadByte (y)...

If ReadByte (jpeg) = 74 Then jfif = jfif + 1 ' ASCII code for "J"
If ReadByte (jpeg) = 70 Then jfif = jfif + 1 ' ASCII code for "F"
If ReadByte (jpeg) = 73 Then jfif = jfif + 1 ' ASCII code for "I"
If ReadByte (jpeg) = 70 Then jfif = jfif + 1 ' ASCII code for "F"
If ReadByte (jpeg) = 0 Then jfif = jfif + 1 ' 0

If jfif = 5 Then jfif = True Else jfif = False

' Read next two bytes. If the file has a JFIF marker, this is the version string. If
' not, it's probably random bollocks...

Local major:String = String (ReadByte (jpeg)) ' Major revision number
Local minor:String = RSet (String (ReadByte (jpeg)), 2) ' Minor revision (padded with leading space)

Local version:String

If jfif

' JFIF-compliant! Yay!

minor = Replace (minor, " ", "0") ' Replace space with 0!

' The above changes version from (eg.) "1.2" to "1.02",
' as in common rendering of "JFIF, version 1.02"...

version = "JFIF version " + major + "." + minor

Else

' Missing either APP0 marker or "JFIF" string. Boo!

version = "Not a 100% JFIF-compliant JPEG file"

EndIf

image.info = version

Local loop:Int ' For byte seek loops...

' Skip block length, minus the previous 7 reads since start of block...

If remote

' Online image, read byte-by-byte...

For loop = 1 To block_length - 7
ReadByte jpeg
Next
Else
' Local image, just stream...
SeekStream jpeg, StreamPos (jpeg) + (block_length - 7)
EndIf

Local back_byte:Int = 0 ' See below...

While Not Eof (jpeg)

' We should be at the start of a block; if not, bail out...

' DebugLog "---------------------------------------------------------------------------------------"
' DebugLog "New block at " + StreamPos (jpeg)
' DebugLog "---------------------------------------------------------------------------------------"

Local checkff:Byte ' Byte to be tested for $FF (start of block)...

' See further down -- needed as we can't seek backwards with online images...

If back_byte
' Byte from last time around...
checkff = back_byte
Else
checkff = ReadByte (jpeg)
EndIf

If checkff = $FF

back_byte = 0 ' Reset for next loop...

' Read the byte AFTER a $FF marker...

Local afterff:Byte = ReadByte (jpeg)

' Some debug information, perhaps of interest...
' DebugLog "$FF" + Right (Hex (afterff), 2)
' $D8 = Start of Image (SOI) marker
' $D9 = End of Image (EOI) marker
' $ED = Photoshop data marker
' $E1 = Start of Exif data

' Grab next two bytes (length of block) before proceeding...

block_length = ReadShort (jpeg) - 2 ' The 2 subtracted bytes store the length itself...

If afterff => $C0 And afterff <= $C3

' Bits per pixel...

Local bpp:Int = ReadByte (jpeg)

' Height and width...

Local height:Int = ReadShort (jpeg)' (ReadByte (jpeg) Shl 8) + ReadByte (jpeg)
Local width:Int = ReadShort (jpeg)'(ReadByte (jpeg) Shl 8) + ReadByte (jpeg)

' Components per pixel (1 for grayscale, 3 for RGB)...

Local components:Int = ReadByte (jpeg)

' Depth/total colours...

Local depth:Int = bpp * components
Local colors:Int = 2 ^ depth

' Fill in ImageInfo data...

image.width = width
image.height = height
image.colors = colors

' Done!

Exit

Else

' Go to next block...

If remote

' Online image, read byte-by-byte...

For loop = 1 To block_length
ReadByte jpeg
Next

Else
' Local image, just seek...
SeekStream jpeg, StreamPos (jpeg) + block_length
EndIf

' Found huge string of zeroes after jumping block_length in PS 7 JPEG! Skip...

Local next_byte:Byte = 0

Repeat
next_byte = ReadByte (jpeg)
Until next_byte

' OK, found non-zero byte, so go back one byte and return to start of loop...

back_byte = next_byte ' Store last-read byte (can't seek back with online images)...

' back_byte will be checked at start of While/Wend loop...

EndIf

Else

' Not at a block marker. Oops! Bail...

image = Null
Exit

EndIf

Wend

Else
image = Null
EndIf

Catch ReadFail:Object
DebugLog "Read error in " + f
image = Null

End Try

CloseFile jpeg

Else
image = Null
EndIf

Return image

End Function

Function GetPNGInfo:ImageInfo (f:String)

If Lower (Left (f, 7)) = "http://"
f = "http::" + Right (f, Len (f) - 7)
EndIf

Local image:ImageInfo = New ImageInfo

Local png:TStream = BigEndianStream (ReadFile (f))

If png

Try

' PNG header...

If ReadByte (png) = $89 And Chr (ReadByte (png)) = "P" And Chr (ReadByte (png)) = "N" And Chr (ReadByte (png)) = "G"

' PNG header continued...

If ReadByte (png) = 13 And ReadByte (png) = 10 And ReadByte (png) = 26 And ReadByte (png) = 10

For Local loop:Int = 1 To 4
ReadByte png
Next

' IHDR chunk (always first)...

If Chr (ReadByte (png)) = "I" And Chr (ReadByte (png)) = "H" And Chr (ReadByte (png)) = "D" And Chr (ReadByte (png)) = "R"

Local width:Int = ReadInt (png)
Local height:Int = ReadInt (png)
Local depth:Int = ReadByte (png)

Local colortype:Int = ReadByte (png)

Local info:String

Select colortype

Case 0
info = "Pixels represented by grayscale values"

Case 2
info = "Pixels represented by RGB values"

Case 3
info = "Pixels represented by palette indices"

Case 4
info = "Pixels represented by grayscale values plus alpha"

Case 6
info = "Pixels represented by RGB values plus alpha"

Default
info = "Unknown pixel format"

End Select

image.width = width
image.height = height
image.colors = 2 ^ depth
image.info = info

Else
image = Null
EndIf

Else
image = Null
EndIf

Else
image = Null
EndIf

Catch ReadFail:Object
DebugLog "Read error in " + f
image = Null

End Try

CloseFile png

Else
image = Null
EndIf

Return image

End Function

Function GetTGAInfo:ImageInfo (f$)

If Lower (Left (f, 7)) = "http://"
f = "http::" + Right (f, Len (f) - 7)
EndIf

Local image:ImageInfo = New ImageInfo

Local tga:TStream = LittleEndianStream (ReadFile (f$))

If tga

Try

Local idlength:Byte = ReadByte (tga)

Local colormap:Byte = ReadByte (tga)

Local imagetype:Byte = ReadByte (tga)

Local info:String

Select imagetype

' First three bits:

Case 0
info = "No image data present"
Case 1
info = "Uncompressed color-mapped image"
Case 2
info = "Uncompressed RGB image"
Case 3
info = "Uncompressed grayscale image"

' Fourth bit:

Case 9
info = "RLE-compressed color-mapped image"
Case 10
info = "RLE-compressed RGB image"
Case 11
info = "RLE-compressed grayscale image"

' From http://www.gamers.org/dEngine/quake3/TGA.txt ...

Case 32
info = "Color-mapped image (Huffman/Delta/RLE-compressed)"

Case 33
info = "Color-mapped image (Huffman/Delta/RLE-compressed, 4-pass quadtree)"

Default
info = "Unknown image type"

End Select

Local colormapstart:Short = ReadShort (tga)
Local colormaplength:Short = ReadShort (tga)
Local colormapbpp:Byte = ReadByte (tga)

Local xorigin:Short = ReadShort (tga)
Local yorigin:Short = ReadShort (tga)

Local width:Short = ReadShort (tga)
Local height:Short = ReadShort (tga)

Local depth:Byte = ReadByte (tga)

If colormap

depth = colormapbpp

' DebugLog "Color map start: " + colormapstart
' DebugLog "Color map length: " + colormaplength
' DebugLog "Color map bits per pixel: " + colormapbpp

' Select colormap
' Case 0
' DebugLog "Image has no indexed palette"
' Case 1
' DebugLog "Image has indexed palette (" + colormaplength + " entries)"
' Case colormap =>2 And colormap <= 127
' DebugLog "Truevision-specific color map"
' Case colormap => 128 And colormap <= 255
' DebugLog "Third-party color map"
' End Select

EndIf

Local desc:Byte = ReadByte (tga)

Local pixelattr:Byte = desc & (Int (2 ^ 3) | Int (2 ^ 2) | Int (2 ^ 1) | Int (2 ^ 0))

Select pixelattr

Case 0

info = info + ", no alpha mask"

Case 1

info = info + ", with background mask"

Case 8

info = info + ", with alpha mask"

End Select

' 32-bit depth may or may not include an alpha mask, but RGB values are max 24-bit...

If depth = 32 Then depth = 24

Local colors:Int = Int (2 ^ depth)

' NOTE: colors value is the maximum number of colours available to each
' pixel. This applies even in images with a limited number of palette
' entries, eg. a palette of 8 indexed colours may still contain 24-bit values!

image.width = width
image.height = height
image.colors = colors
image.info = info

Catch ReadFail:Object
DebugLog "Read error in " + f
image = Null

End Try

CloseFile tga

Else
image = Null
EndIf

Return image

End Function

' -----------------------------------------------------------------------------
' File format tests...
' -----------------------------------------------------------------------------

Function GotBMP:Int (f:String)

If Lower (Left (f, 7)) = "http://"
f = "http::" + Right (f, Len (f) - 7)
EndIf

Local result:Int = False

Local bmp:TStream = LittleEndianStream (ReadFile (f))

If bmp

Try

If ReadByte (bmp) = $42 And ReadByte (bmp) = $4D

For Local loop:Int = 1 To 12
ReadByte bmp
Next

If ReadInt (bmp) = 40
result = True
EndIf

EndIf

Catch ReadFail:Object
DebugLog "Read error in " + f

End Try

CloseFile bmp

EndIf

Return result

End Function

Function GotGIF:Int (f:String)

If Lower (Left (f, 7)) = "http://"
f = "http::" + Right (f, Len (f) - 7)
EndIf

Local result:Int = False

Local gif:TStream = LittleEndianStream (ReadFile (f))

If gif

Try

' First 3 bytes must be "GIF"...

Local g:String ' /beavis: Uh... huh huh!

Local loop:Int ' For byte-seek loops...

For loop = 0 To 2
g = g + Chr (ReadByte (gif))
Next

If g = "GIF"

' Next 3 bytes contain version (87a or 89a)...

Local version:String

For loop = 3 To 5
version = version + Chr (ReadByte (gif))
Next

If version = "87a" Or version$ = "89a"
result = True
EndIf

EndIf

Catch ReadFail:Object
DebugLog "Read error in " + f

End Try

CloseFile gif

EndIf

Return result

End Function

Function GotJPEG:Int (f:String)

If Lower (Left (f, 7)) = "http://"
f = "http::" + Right (f, Len (f) - 7)
EndIf

Local result:Int = False

Local jpeg:TStream = BigEndianStream (ReadFile (f))

If jpeg

Try

If ReadByte (jpeg) = $FF And ReadByte (jpeg) = $D8

ReadByte jpeg
ReadByte jpeg

Local block_length:Int = ReadShort (jpeg) - 2

ReadByte jpeg
ReadByte jpeg
ReadByte jpeg
ReadByte jpeg
ReadByte jpeg

ReadByte jpeg
ReadByte jpeg

Local loop:Int

For loop = 1 To block_length - 7
ReadByte jpeg
Next

If ReadByte (jpeg) = $FF
result = True
EndIf

EndIf

Catch ReadFail:Object
DebugLog "Read error in " + f

End Try

CloseFile jpeg

EndIf

Return result

End Function

Function GotPNG:Int (f:String)

If Lower (Left (f, 7)) = "http://"
f = "http::" + Right (f, Len (f) - 7)
EndIf

Local result:Int = False

Local png:TStream = BigEndianStream (ReadFile (f))

If png

Try

If ReadByte (png) = $89 And Chr (ReadByte (png)) = "P" And Chr (ReadByte (png)) = "N" And Chr (ReadByte (png)) = "G"

' PNG header continued...

If ReadByte (png) = 13 And ReadByte (png) = 10 And ReadByte (png) = 26 And ReadByte (png) = 10

For Local loop:Int = 1 To 4
ReadByte png
Next

' IHDR chunk (always first)...

If Chr (ReadByte (png)) = "I" And Chr (ReadByte (png)) = "H" And Chr (ReadByte (png)) = "D" And Chr (ReadByte (png)) = "R"

result = True

EndIf

EndIf

EndIf

Catch ReadFail:Object
DebugLog "Read error in " + f

End Try

CloseFile png

EndIf

Return result

End Function

Function GotTGA:Int (f:String, ext:Int = True)

' Best to take extension into account here, since there are no 100% identifying TGA markers...

If ext
If Lower (ExtractExt (f)) <> "tga"
Return False
EndIf
EndIf

If Lower (Left (f, 7)) = "http://"
f = "http::" + Right (f, Len (f) - 7)
EndIf

Local result:Int = False

Local tga:TStream = LittleEndianStream (ReadFile (f))

If tga

Try

ReadByte tga
ReadByte tga
ReadByte tga

ReadShort tga
ReadShort tga
Local mapbits:Byte = ReadByte (tga)

ReadShort tga
ReadShort tga

' Width and height > 0...

If ReadShort (tga) > 0 And ReadShort (tga) > 0

' Depth > 0 or bits per palette entry > 0...

Local depth:Byte = ReadByte (tga)

If depth

result = True

Select depth
Case 8
Case 16
Case 24
Case 32
Default
result = False
End Select

Else
If mapbits

result = True

Select depth
Case 15
Case 16
Case 24
Case 32
Default
result = False
End Select
EndIf
EndIf

EndIf

Catch ReadFail:Object
DebugLog "Read error in " + f

End Try

CloseFile tga

EndIf

Return result

End Function

Function GetImageInfo:ImageInfo (f:String)

Local i:ImageInfo = New ImageInfo

Local ext:String = Lower (ExtractExt (f))

Select ext:String

Case "jpg", "jpeg", "jpe", "jfif"
i = GetJPEGInfo (f)

Case "gif"
i = GetGIFInfo (f)

Case "bmp"
i = GetBMPInfo (f)

Case "png"
i = GetPNGInfo (f)

Case "tga"
i = GetTGAInfo (f)

Default
i = Null

End Select

If i = Null
If GotJPEG (f) Then i = GetJPEGInfo (f); If i Then i.info = "This is really a JPEG file!"; Return i
If GotBMP (f) Then i = GetBMPInfo (f); If i Then i.info = "This is really a BMP file!"; Return i
If GotPNG (f) Then i = GetPNGInfo (f); If i Then i.info = "This is really a PNG file!"; Return i
If GotGIF (f) Then i = GetGIFInfo (f); If i Then i.info = "This is really a GIF file!"; Return i
If GotTGA (f) Then i = GetTGAInfo (f); If i Then i.info = "This is really a TGA file!"; Return i
EndIf

Return i

End Function


Comments :


BlitzSupport(Posted 1+ years ago)

 Quick test -- add some image filenames:

' ************ Copy main code archive entry here! ************

' -----------------------------------------------------------------------------
' D E M O . . .
' -----------------------------------------------------------------------------

' Provide some files of jpg, gif, bmp, tga and png types...

Local f1:String = "test.jpg"
Local f2:String = "test.gif"
Local f3:String = "test.bmp"
Local f4:String = "test.png"
Local f5:String = "test.tga"

' IMPORTANT! ALWAYS check return value from GetImageInfo is non-Null before
' attempting to access information...

' File names defined at top of code!

Print ""

Local info:ImageInfo

' -----------------------------------------------------------------------------
' JPEG test...
' -----------------------------------------------------------------------------

info = GetImageInfo (f1$)

If info
Print "Info for ~q" + f1$ + "~q:"
Print ""
Print "~t" + info.width + " x " + info.height + ", " + info.colors + " colours (" + info.info + ")"
Print ""
Else
Print "No information returned for ~q" + f1$ + "~q!"
EndIf

' -----------------------------------------------------------------------------
' GIF test...
' -----------------------------------------------------------------------------

info = GetImageInfo (f2$)

If info
Print "Info for ~q" + f2$ + "~q:"
Print ""
Print "~t" + info.width + " x " + info.height + ", " + info.colors + " colours (" + info.info + ")"
Print ""
Else
Print "No information returned for ~q" + f2$ + "~q!"
EndIf

' -----------------------------------------------------------------------------
' BMP test...
' -----------------------------------------------------------------------------

info = GetImageInfo (f3$)

If info
Print "Info for ~q" + f3$ + "~q:"
Print ""
Print "~t" + info.width + " x " + info.height + ", " + info.colors + " colours (" + info.info + ")"
Print ""
Else
Print "No information returned for ~q" + f3$ + "~q!"
EndIf

' -----------------------------------------------------------------------------
' PNG test...
' -----------------------------------------------------------------------------

info = GetImageInfo (f4$)

If info
Print "Info for ~q" + f4$ + "~q:"
Print ""
Print "~t" + info.width + " x " + info.height + ", " + info.colors + " colours (" + info.info + ")"
Print ""
Else
Print "No information returned for ~q" + f4$ + "~q!"
EndIf

' -----------------------------------------------------------------------------
' TGA test...
' -----------------------------------------------------------------------------

info = GetImageInfo (f5$)

If info
Print "Info for ~q" + f5$ + "~q:"
Print ""
Print "~t" + info.width + " x " + info.height + ", " + info.colors + " colours (" + info.info + ")"
Print ""
Else
Print "No information returned for ~q" + f5$ + "~q!"
EndIf
If you have a folder full of images (including any sub-folders), this will go through all of them and print the details.
' ************ Copy main code archive entry here! ************

Local f:String = "G:DocsMy Pictures" ' Your image folder!

' -----------------------------------------------------------------------------
' D E M O . . .
' -----------------------------------------------------------------------------

Print ""
ParseFolder f ' Defined above!
End

Function PrintImageInfo (f:String)

Local info:ImageInfo = GetImageInfo (f:String)

If info
Print "Info for ~q" + f:String + "~q:"
Print ""
Print "~t" + info.width + " x " + info.height + ", " + info.colors + " colours (" + info.info + ")"
Print ""
Else
Print "No information returned for ~q" + f:String + "~q!"
EndIf

Print ""

End Function

Function ParseFolder (dir:String)

If Right (dir:String, 1) <> "" And Right (dir:String, 1) <> "/"
dir:String = dir:String + "/"
EndIf

Local folder:Int = ReadDir (dir:String)

If folder

Repeat

Local entry:String = NextFile (folder)

If entry = "" Then Exit

If entry <> "." And entry <> ".."

Local file:String
Local full:String

If FileType (dir + entry) = FILETYPE_FILE

file = entry

full = dir

If Right (full, 1) <> "" And Right (full, 1) <> "/"
full = full + ""
EndIf

full = full + file

PrintImageInfo (full)

Else

If FileType (dir + entry) = FILETYPE_DIR

file = entry

If file <> "." And file <> ".."

Local ffolder:String = dir

If Right (ffolder, 1) <> "" And Right (ffolder, 1) <> "/"
ffolder = ffolder + ""
EndIf

ffolder = ffolder + file

ParseFolder (ffolder)

EndIf

EndIf

EndIf

EndIf

Forever

EndIf

End Function



GfK(Posted 1+ years ago)

 Some JPEG files contain EXIF data - data which shows information from the digital camera/phone the picture was taken on; such as date/time taken, shutter speed, ISO, aperture, white balance and so on.You checked for any correlation between that and the jpeg images that don't work right?


BlitzSupport(Posted 1+ years ago)

 No, it doesn't appear to be related to EXIF information. I believe some files don't contain the expected $FF, or possibly $C0, values, though I need to look into it further...($FF marks the start of a block of information, and a following $C0 marks the block containing width/height information... at a very quick glance, it seems either that $C0 isn't found after a $FF in the files that fail, or the expected $FF block marker isn't found where it's expected at some point.)I believe EXIF would just be another $FF block (or several), but <a href="http://www.takenet.or.jp/~ryuuji/minisoft/exifread/english/download.html" target="_blank">this program</a> seems to prove EXIF isn't the problem.


Nate the Great(Posted 1+ years ago)

 I read somewhere that when you open a jpeg, part of the file is run as an exe so perhaps they built some kind of protection into it... maybe check the disc space it takes up, then ctrl-prtSc it and save as the same resolution jpeg. and see if it still doesnt return info?


BlitzSupport(Posted 1+ years ago)

 <div class="quote"> part of the file is run as an exe </div>I don't think that's true!I've almost got it sussed, though -- only 8 of my 10,000+ JPEGs are failing now (as opposed to around 1,400 yesterday!). I just had to check for some more $FFCx markers and relax the requirement for certain 'correct' details which are often skipped in reality. The only other 'fails' were images with incorrect extensions (eg. a BMP file named as "blah.jpg"), so that's pretty good.I have an idea what might be wrong with the remaining 8 files, but will be updating this code soon anyway...


BlitzSupport(Posted 1+ years ago)

 [EDIT: Sorted!]Gah! Photoshop 7 JPEGs are the only ones failing, and it seems that they're widely incompatible unless saved using the 'Save for Web' function, since Adobe wisely decided to do their own thing that nobody else does. I might have a go at working around this, but I don't think it's that important -- see links below if interested:<a href="http://photo.net/bboard/q-and-a-fetch-msg?msg_id=003j8d" target="_blank">http://photo.net/bboard/q-and-a-fetch-msg?msg_id=003j8d</a><a href="http://www.codeproject.com/KB/graphics/iptc.aspx?fid=2301&df=90&mpp=25&noise=3&sort=Position&view=Quick&fr=26&select=716178" target="_blank">http://www.codeproject.com/KB/graphics/iptc.aspx?fid=2301&df=90&mpp=25&noise=3&sort=Position&view=Quick&fr=26&select=716178</a><a href="http://www.tow.com/photo/articles/1d_jpeg_iptc/" target="_blank">http://www.tow.com/photo/articles/1d_jpeg_iptc/</a>http://209.85.229.132/search?q=cache:Lf0CVkgJLMwJ:www.adobeforums.com/webx%3F14@@...


BlitzSupport(Posted 1+ years ago)

 I've updated the code (and my first post) with the fixed JPEG reader. It seems to correctly retrieve the details of any supported image file I throw at it now! The only exceptions have been images named with the wrong extension, just because the test code passes images to each decoder based on extension (so it actually flags mis-named files!).Just got to test on PPC Mac, attempt to work around stupid PS 7 JPEGs and add some checking for corrupt files (so I don't call ReadByte past the end of broken files, not that this has come up so far).


xlsior(Posted 1+ years ago)

 <div class="quote"> I read somewhere that when you open a jpeg, part of the file is run as an exe so perhaps they built some kind of protection into it. </div>That's not true, although there have been some viruses that did spread through plain images: This was due to an archaic windows 3.x printer image format that contained some macro system which was found to be vulnerable to buffer overflows. This was exploited by renaming the bad files in question to .JPG, which IE would then blindly hand over to the Windows rendering system to display... Except it wasn't really a JPEG but a different file format containing the virus code which then got executed.The problem was 'fixed' by Microsoft by removing support for the other image format altogether.Normal JPEGs have no executable code or 'smarts' inside of them, it's just a common lossy compression algorithm.


BlitzSupport(Posted 1+ years ago)

 For anyone that may be interested, this now appears to provide 100% correct results for all supported image formats -- BMP, PNG, JPEG and Gif so far. Please see updated code (including example in first post), and my updated comments before the code.


TaskMaster(Posted 1+ years ago)

 I haven't looked at your code, but does your code also determine the pic type by looking at the info, or does it use the file extension?Can you just feed it a file (such as picture.dat) and it tell you whether it is jpg, gif, bmp, or png as well as give you the info?


Difference(Posted 1+ years ago)

 This is great!Any thought of extending it to support exif reading?(Not the only place I put this request):<a href="../Community/postsb3b2.html?topic=84682" target="_blank">/Community/posts.php?topic=84682</a><a href="http://code.google.com/p/maxmods/issues/detail?id=9" target="_blank">http://code.google.com/p/maxmods/issues/detail?id=9</a>


Beaker(Posted 1+ years ago)

 Thanks James. I had my own hacked jpg size reader before. I just adapted Marks jpg loader but hacked it so it only read the first few bytes - seemed to work ok, but this is much better.


Space_guy(Posted 1+ years ago)

 Sounds good but dont forget the tga format :)


BlitzSupport(Posted 1+ years ago)

 I've added TGA support, and (@ TaskMaster) I do plan to add proper image type checking rather than just relying on extension.Dunno about Exif... I got really bogged down by that in the JPEG code, but might have another go!(I've put the demos into the first post so the main code can just be copied and pasted into your own projects. You'll have to paste it into the demos to try them!)


BlitzSupport(Posted 1+ years ago)

 Wa-hey... coming in the next day or so:Info for "http://www.hi-toro.com/images/test.tga":

512 x 384, 16777216 colours (Uncompressed color-mapped image, no alpha mask)


Info for "http://www.hi-toro.com/images/test.png":

300 x 300, 256 colours (Pixels represented by RGB values)
URLs (http:// only) can now be passed as easily as filenames. Online images do (eg. JPEG) block jumps byte-by-byte, while local images can just use SeekStream to keep the same speed.I need to do some more error-checking (particularly for EOF) before adding any more features (eg. found a couple of instances of not returning Null on failure to retrieve information).


BlitzSupport(Posted 1+ years ago)

 I've updated this entry with online support (just pass a normal http:// based URL), and I've added Try/Catch around byte read sections, so that broken files are aborted safely. (Tested on some deliberately truncated files, online and locally.)image:ImageInfo = GetImageInfo ("http://www.hi-toro.com/images/test.png")

If image <> Null
Print "Width: " + image.width
Print "Height: " + image.height
Print "Colours: " + image.colors
Print "Info: " + image.info
Else
Print "Couldn't get image information!"
EndIf
Next will be proper file-type checking, rather than choosing which function to call based on the file extension. Also need to add consistency between formats' colour reporting, which is a bit mixed at present.EDIT: Fixed variable name discrepancy when running without SuperStrict.


BlitzSupport(Posted 1+ years ago)

 Fixed endianness so it now works correctly on PPC Macs -- it was failing on some file types.There is a minor problem still on the PPC Mac, but I think it's a <a href="../Community/postsec9a-4.html?topic=84743" target="_blank">Blitz bug/discrepancy</a>. It doesn't crash, though -- it just returns false information for images that are broken/truncated. For normal files it's fine.


BlitzSupport(Posted 1+ years ago)

 Proper file-type checking now added...It now checks using the file extension first, as before (since most files are named correctly), but if the result of GetImageInfo () is Null (the expected result where a file's extension is incorrect), it byte-checks the actual file type. (It puts a warning with the correct file type into the .info field, but the width/height/etc results should be correct regardless, assuming you have a valid, non-Null ImageInfo result.)


BlitzSupport(Posted 1+ years ago)

 The "minor problem" on PPC Mac is non-existent if your BlitzMax installation is up to date -- version 1.33 upwards will be fine. Corrupt files are correctly caught and skipped. [/i]