Get image width and height without loading image

Just starting out? Need help? Post your questions and find answers here.
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Get image width and height without loading image

Post by IdeasVacuum »

Hello chaps, sorry to bother you with this.

I'm using this code with PB5.42Beta1 on Win7 x86. The code is super fast and is giving accurate results.
However, a few files return a size of zero zero. I have set #ImageHeaderSize = 4096

ImageSize.zip

Edit: I should mention that I'm using Michael Vogel's "Wilbert-style" code, posted: Fri Mar 20, 2015 2:48pm
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
Michael Vogel
Addict
Addict
Posts: 2808
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Get image width and height without loading image

Post by Michael Vogel »

IdeasVacuum, maybe you will try the following tool to check if I do something wrong: Speed Renamer - just copy all files in the same directory as your example images.

After starting the SpeedRenamer, all image files should be prepared to be renamed to "<px> x <py>.<e>", what means "width x height plus extension"...
...if everything works as expected, I will extract all parts from the source you need for your program.
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Get image width and height without loading image

Post by IdeasVacuum »

Points of interest

The Win7 sample images verify zero zero with #ImageHeaderSize = 4096. They are all the same Width/Height, all .jpgs, Filesizes range from 549KB to 859KB.
If #ImageHeaderSize = 8182, all but two of the images verify.
If #ImageHeaderSize = 10240, all images verify.

Instead of using the #ImageHeaderSize constant, I tried FileSize() instead. This also worked with the Win7 sample images but not with the bunch of .jpg files I have posted here. Tricky :?
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
Michael Vogel
Addict
Addict
Posts: 2808
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Get image width and height without loading image

Post by Michael Vogel »

Are these values ok?
Image
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Get image width and height without loading image

Post by IdeasVacuum »

Hello Michael

Followed your instructions and sure enough, SpeedRenamer lists the files, new "<px> x <py>.<e>" name and their Width/Height/Size, as shown in your screenshot. The sizes match those reported by the OS and by IrfanView.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
Michael Vogel
Addict
Addict
Posts: 2808
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Get image width and height without loading image

Post by Michael Vogel »

Okay, will extract the code, but you have to be patient, will do so tomorrow morning...
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Get image width and height without loading image

Post by IdeasVacuum »

That is very kind of you Michael and much appreciated.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
Michael Vogel
Addict
Addict
Posts: 2808
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Get image width and height without loading image

Post by Michael Vogel »

Here we are (hope it works)...

Code: Select all

Structure FileType
	ImageX.i
	ImageY.i
EndStructure

Global Dim File.FileType(0)

Procedure.i iAbs(val.i)

	EnableASM

	CompilerIf #PB_Compiler_Processor=#PB_Processor_x86
		MOV eax, val
		NEG eax
		CMOVS eax, val
	CompilerElse
		MOV rax, val
		NEG rax
		CMOVS rax, val
	CompilerEndIf

	DisableASM
	ProcedureReturn

EndProcedure
Procedure GetImageInformation(entry,filename.s)

	#ImageMinimumSize=		24
	#ImageHeaderSize=		2048

	#ImageHeaderGIF=		$38464947;		'GIF8'
	#ImageHeaderJPG0=		$E0FFD8FF;		-
	#ImageHeaderJPG1=		$E1FFD8FF;		-
	#ImageHeaderJP2=		$0C000000;		-
	#ImageHeaderPNG=		$474E5089;		'‰PNG'

	#ImageHeaderBMP=		$4D42;			'BM'
	#ImageHeaderTIF_LSB=	$4949;			'II'
	#ImageHeaderTIF_MSB=	$4D4D;			'MM'

	#ImageHeaderPCX=		$0A;			-
	#ImageHeaderTGA=		$01;			-

	Structure MemArray
		StructureUnion
			a.a[0]
			w.w[0]
			l.l[0]
		EndStructureUnion
	EndStructure

	Protected *Buffer.MemArray
	Protected *MemArray.MemArray
	Protected BytesRead
	Protected FileShift
	Protected a.a,n.l,m

	#ImageFile=666

	File(entry)\ImageX=#Null
	File(entry)\ImageY=#Null

	If #True
		If ReadFile(#ImageFile,FileName, #PB_File_NoBuffering|#PB_File_SharedRead)
			*Buffer=AllocateMemory(#ImageHeaderSize,#PB_Memory_NoClear)
			BytesRead=ReadData(#ImageFile,*Buffer,#ImageHeaderSize)

			If BytesRead>=#ImageMinimumSize

				Select *Buffer\l[0]&$FFFFFFFF

				Case #ImageHeaderJPG0,#ImageHeaderJPG1
					n=*Buffer\a[4]<< 8|*Buffer\a[5]+4
					If n>20
						FileShift=n
						FileSeek(#ImageFile,n)
						BytesRead=ReadData(#ImageFile,*Buffer,#ImageHeaderSize)
						n=0
					EndIf
					*MemArray=*Buffer+n
					While *MemArray-*Buffer+8<=BytesRead;		8 Bytes Information
						If *MemArray\a[0]=$FF
							a=*MemArray\a[1]
							; Debug Hex(*MemArray\w[0])
							If a=$c0 Or (a>>4=$C And a&3)
								File(entry)\ImageX=*MemArray\a[7]<<8|*MemArray\a[8]
								File(entry)\ImageY=*MemArray\a[5]<<8|*MemArray\a[6]
								Break
							EndIf
						Else
							Break
						EndIf
						*MemArray+*MemArray\a[2]<<8|*MemArray\a[3]+2
						If *MemArray-*Buffer>=BytesRead-8;		8 Bytes Information
							FileShift+*MemArray-*Buffer
							FileSeek(#ImageFile,FileShift)
							BytesRead=ReadData(#ImageFile,*Buffer,#ImageHeaderSize)
							*MemArray=*Buffer
						EndIf
					Wend

				Case #ImageHeaderPNG
					File(entry)\ImageX=*Buffer\a[18]<<8|*Buffer\a[19]
					File(entry)\ImageY=*Buffer\a[22]<<8|*Buffer\a[23]

				Case #ImageHeaderGIF
					File(entry)\ImageX=*Buffer\w[3]
					File(entry)\ImageY=*Buffer\w[4]

				Case #ImageHeaderJP2
					n=*Buffer\a[14]<<8|*Buffer\a[15]+35
					If n<#ImageHeaderSize
						File(entry)\ImageX=*Buffer\a[n-1]<<8|*Buffer\a[n]
						File(entry)\ImageY=*Buffer\a[n-5]<<8|*Buffer\a[n-4]
					EndIf

				Default
					Select *Buffer\w[0]&$FFFF

					Case #ImageHeaderBMP
						File(entry)\ImageX=*Buffer\w[9]
						File(entry)\ImageY=iAbs(*Buffer\w[11]&$FFFF)

					Case #ImageHeaderTIF_LSB
						a=0
						n=*Buffer\l[1]
						If n<#ImageHeaderSize
							n>>1
							m=*Buffer\w[n]
							n+1
							While m
								Select *Buffer\w[n]
								Case $100
									File(entry)\ImageX=*Buffer\w[n+4]
									a|1
								Case $101
									File(entry)\ImageY=*Buffer\w[n+4]
									a|2
								EndSelect
								If a=3
									m=0
								Else
									n+6
									m-1
								EndIf
							Wend
						EndIf

					Case #ImageHeaderTIF_MSB
						a=0
						n=*Buffer\a[6]<<8|*Buffer\a[7]
						If n<#ImageHeaderSize
							m=*Buffer\a[n]<<8|*Buffer\a[n+1]
							n+2
							While m
								Select *Buffer\a[n]<<8|*Buffer\a[n+1]
								Case $100
									File(entry)\ImageX=*Buffer\a[n+8]<<8|*Buffer\a[n+9]
									a|1
								Case $101
									File(entry)\ImageY=*Buffer\a[n+8]<<8|*Buffer\a[n+9]
									a|2
								EndSelect
								If a=3
									m=0
								Else
									n+12
									m-1
								EndIf
							Wend
						EndIf

					Default
						Select *Buffer\a[0]

						Case #ImageHeaderPCX
							If *Buffer\a[1]<6
								File(entry)\ImageX=*Buffer\w[4]+1
								File(entry)\ImageY=*Buffer\w[5]+1
							EndIf

						Case #ImageHeaderTGA
							If *Buffer\a[1]<2 And *Buffer\a[2]&%11110100=0
								File(entry)\ImageX=*Buffer\w[6]
								File(entry)\ImageY=*Buffer\w[7]
							EndIf
						EndSelect

					EndSelect

				EndSelect
			EndIf
			FreeMemory(*Buffer)
			CloseFile(#ImageFile)
		EndIf
	EndIf

EndProcedure

If ExamineDirectory(0,".","*.jpg")
	While NextDirectoryEntry(0)
		If DirectoryEntryType(0)=#PB_DirectoryEntry_File
			file.s=DirectoryEntryName(0)
			GetImageInformation(0,file)
			Debug file+": "+Str(File(0)\ImageX)+" x "+Str(File(0)\ImageY)
		EndIf
	Wend
	FinishDirectory(0)
EndIf

IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Get image width and height without loading image

Post by IdeasVacuum »

That was fast Michael, thank you.

So, essentially the change is: replace #ImageHeaderJPG with #ImageHeaderJPG0 and #ImageHeaderJPG1?

I'm getting a crash at FileSeek(0, n) at the moment - possibly a file hitherto untried. Time for me to experiment a bit.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Get image width and height without loading image

Post by IdeasVacuum »

OK, in fact a number of subtle changes have been made.

I have tested the "rogue" images and as many suspect images that I can find, all of which now process beautifully. Also, it's no longer necessary to increase the buffer size in order to verify the Win7 sample images.

Brilliant job Michael!
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Get image width and height without loading image

Post by IdeasVacuum »

So, of any 100 images, 2% are not being verified via the header. In my app, if the Width or Height result is zero, another procedure is called to load the image and verify with the PB functions. There is no noticable lack of speed, but of course if a whole batch of images failed rather than 2%, that could be a bit slower (2.5 seconds on my PC).

The .jpg files seem to be the most unreliable......

Here is one that SpeedRenamer verifies as 0 x 0 size. Actual size is 960 x 960 (104KB)
Denzil Washington.zip

The start of the other files is typically: FF E0 00 10 4A 46 49 46 00
Denzil Washinton.jpg: FF E2 02 1C 49 43 43 5F 50
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
Michael Vogel
Addict
Addict
Posts: 2808
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Get image width and height without loading image

Post by Michael Vogel »

Please check if you'll get better results than 98% by doing a small update...

Change/add the following constants:
#ImageHeaderAPP1= $E1FFD8FF; was #ImageHeaderJPG1
#ImageHeaderAPP2= $E2FFD8FF; new


Change the first line with a Case statement to:
Case #ImageHeaderJPG0,#ImageHeaderAPP1,#ImageHeaderAPP2
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Get image width and height without loading image

Post by IdeasVacuum »

Hello Michael

That has fixed Denzil Washington.jpg types 8)

What I have not tested are tif, tga, jp2 (have not got files of those types)

Edit: Just converted mostly .jpg files to .tif and .tga files using IrfanView. Not one of them verifies....
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
Michael Vogel
Addict
Addict
Posts: 2808
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Get image width and height without loading image

Post by Michael Vogel »

So still not 100%, I fear, we'll never get very close :?

TGA and TIF examples

Code: Select all

Structure FileType
	ImageX.i
	ImageY.i
EndStructure

Global Dim File.FileType(0)

Procedure.i iAbs(val.i)

	EnableASM

	CompilerIf #PB_Compiler_Processor=#PB_Processor_x86
		MOV eax, val
		NEG eax
		CMOVS eax, val
	CompilerElse
		MOV rax, val
		NEG rax
		CMOVS rax, val
	CompilerEndIf

	DisableASM
	ProcedureReturn

EndProcedure
Procedure GetImageInformation(entry,filename.s)

	#ImageMinimumSize=      24
	#ImageHeaderSize=      2048
	#ImageHeaderSizeTif=	48;			Just a guess

	#ImageHeaderGIF=      $38464947;      'GIF8'
	#ImageHeaderJPG=      $E0FFD8FF;      -
	#ImageHeaderAPP1=      $E1FFD8FF;      -
	#ImageHeaderAPP2=      $E2FFD8FF;      -
	#ImageHeaderJP2=      $0C000000;      -
	#ImageHeaderPNG=      $474E5089;      '‰PNG'

	#ImageHeaderBMP=      $4D42;         'BM'
	#ImageHeaderTIF_LSB=   $4949;         'II'
	#ImageHeaderTIF_MSB=   $4D4D;         'MM'

	#ImageHeaderPCX=      $0A;         -
	#ImageHeaderTGA=      $01;         -


	Structure MemArray
		StructureUnion
			a.a[0]
			w.w[0]
			l.l[0]
		EndStructureUnion
	EndStructure

	Protected *Buffer.MemArray
	Protected *MemArray.MemArray
	Protected BytesRead
	Protected FileShift
	Protected a.a,n.l,m

	#ImageFile=666

	File(entry)\ImageX=#Null
	File(entry)\ImageY=#Null

	If #True
		If ReadFile(#ImageFile,FileName, #PB_File_NoBuffering|#PB_File_SharedRead)
			*Buffer=AllocateMemory(#ImageHeaderSize,#PB_Memory_NoClear)
			BytesRead=ReadData(#ImageFile,*Buffer,#ImageHeaderSize)

			If BytesRead>=#ImageMinimumSize


				; Debug Hex(*Buffer\l[0]&$FFFFFFFF)
				Select *Buffer\l[0]&$FFFFFFFF

				Case #ImageHeaderJPG,#ImageHeaderAPP1,#ImageHeaderAPP2
					n=*Buffer\a[4]<< 8|*Buffer\a[5]+4
					If n>20
						FileShift=n
						FileSeek(#ImageFile,n)
						BytesRead=ReadData(#ImageFile,*Buffer,#ImageHeaderSize)
						n=0
					EndIf
					*MemArray=*Buffer+n
					While *MemArray-*Buffer+8<=BytesRead;      8 Bytes Information
						If *MemArray\a[0]=$FF
							a=*MemArray\a[1]
							; Debug Hex(*MemArray\w[0])
							If a=$c0 Or (a>>4=$C And a&3)
								File(entry)\ImageX=*MemArray\a[7]<<8|*MemArray\a[8]
								File(entry)\ImageY=*MemArray\a[5]<<8|*MemArray\a[6]
								Break
							EndIf
						Else
							Break
						EndIf
						*MemArray+*MemArray\a[2]<<8|*MemArray\a[3]+2
						If *MemArray-*Buffer>=BytesRead-8;      8 Bytes Information
							FileShift+*MemArray-*Buffer
							FileSeek(#ImageFile,FileShift)
							BytesRead=ReadData(#ImageFile,*Buffer,#ImageHeaderSize)
							*MemArray=*Buffer
						EndIf
					Wend

				Case #ImageHeaderPNG
					File(entry)\ImageX=*Buffer\a[18]<<8|*Buffer\a[19]
					File(entry)\ImageY=*Buffer\a[22]<<8|*Buffer\a[23]

				Case #ImageHeaderGIF
					File(entry)\ImageX=*Buffer\w[3]
					File(entry)\ImageY=*Buffer\w[4]

				Case #ImageHeaderJP2
					n=*Buffer\a[14]<<8|*Buffer\a[15]+35
					If n<#ImageHeaderSize
						File(entry)\ImageX=*Buffer\a[n-1]<<8|*Buffer\a[n]
						File(entry)\ImageY=*Buffer\a[n-5]<<8|*Buffer\a[n-4]
					EndIf

				Default
					Select *Buffer\w[0]&$FFFF

					Case #ImageHeaderBMP
						File(entry)\ImageX=*Buffer\w[9]
						File(entry)\ImageY=iAbs(*Buffer\w[11]&$FFFF)

					Case #ImageHeaderTIF_LSB
						a=0
						n=*Buffer\l[1]
						If n>#ImageHeaderSize-#ImageHeaderSizeTif
							FileShift=n
							FileSeek(#ImageFile,n)
							BytesRead=ReadData(#ImageFile,*Buffer,#ImageHeaderSize)
							n=0
						EndIf
						n>>1
						m=*Buffer\w[n]
						n+1
						While m And n<#ImageHeaderSizeTif>>1
							Select *Buffer\w[n]
							Case $100
								File(entry)\ImageX=*Buffer\w[n+4]
								a|1
							Case $101
								File(entry)\ImageY=*Buffer\w[n+4]
								a|2
							EndSelect
							If a=3
								m=0
							Else
								n+6
								m-1
							EndIf
						Wend

					Case #ImageHeaderTIF_MSB
						a=0
						n=*Buffer\a[6]<<8|*Buffer\a[7]
						If n>#ImageHeaderSize-#ImageHeaderSizeTif
							FileShift=n
							FileSeek(#ImageFile,n)
							BytesRead=ReadData(#ImageFile,*Buffer,#ImageHeaderSize)
							n=0
						EndIf
						m=*Buffer\a[n]<<8|*Buffer\a[n+1]
						n+2
						While m And n<#ImageHeaderSizeTif
							Select *Buffer\a[n]<<8|*Buffer\a[n+1]
							Case $100
								File(entry)\ImageX=*Buffer\a[n+8]<<8|*Buffer\a[n+9]
								a|1
							Case $101
								File(entry)\ImageY=*Buffer\a[n+8]<<8|*Buffer\a[n+9]
								a|2
							EndSelect
							If a=3
								m=0
							Else
								n+12
								m-1
							EndIf
						Wend

					Default
						Select *Buffer\a[0]

						Case #ImageHeaderPCX
							If *Buffer\a[1]<6
								File(entry)\ImageX=*Buffer\w[4]+1
								File(entry)\ImageY=*Buffer\w[5]+1
							EndIf

						Case #ImageHeaderTGA
							If *Buffer\a[1]<2 And *Buffer\a[2]&%11110100=0
								File(entry)\ImageX=*Buffer\w[6]
								File(entry)\ImageY=*Buffer\w[7]
							EndIf
						EndSelect

					EndSelect

				EndSelect
			EndIf
			FreeMemory(*Buffer)
			CloseFile(#ImageFile)
		EndIf
	EndIf

EndProcedure

If ExamineDirectory(0,".","*.t??")
	While NextDirectoryEntry(0)
		If DirectoryEntryType(0)=#PB_DirectoryEntry_File
			file.s=DirectoryEntryName(0)
			GetImageInformation(0,file)
			Debug RSet(Str(File(0)\ImageX),5," ")+" x"+RSet(Str(File(0)\ImageY),5," ")+" | "+file
		EndIf
	Wend
	FinishDirectory(0)
EndIf

IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Get image width and height without loading image

Post by IdeasVacuum »

That's strange Michael - the new code for tif does not work with your sample file P1170938.tif on my PC.
Actually, I think you have got very close already, it's a brilliant job as-is.

IrfanViewTIF.zip
IrfanViewTGA.zip

If you haven't got the time to do more, or just don't fancy it, that's fine by me, you have been brilliant.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
Post Reply