Getting Image Dimensions

Hi all,
Having not looked at any code since graduating from university 8 years ago, i'm feeling rather rusty with my C++! I'm trying to write a program that reads the pixel dimensions of a jpg file. Eventually I'd like the program to run through a directory of files and generate an XML file containing the file names and their dimensions, but one step at a time!

I have found a few c++ files on the web that can be used to extract the image size, but I can't seem to get them to work properly in my program. At present, my program returns the same dimensions regardless of which jpg you point it to (unless you point it to an non-existant image). I have a feeling it is to do with the way the pointers are working (or not as the case may be) or the size position in the jpg header file on a 64bit platform?

Any help would be greatly appreciated.

I'm using code blocks with GNU C compiler on a 64bit Windows 7 platform.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <iostream>
#include <fstream>

using namespace std;


bool GetImageSize(const char *fn, int *x,int *y)
{
    FILE *f=fopen(fn,"rb");
    if (f==0) return false;
    fseek(f,0,SEEK_END);
    long len=ftell(f);
    fseek(f,0,SEEK_SET);
    if (len<24) {
        fclose(f);
        return false;
        }
    cout << fn << endl;
  // Strategy:
  // reading GIF dimensions requires the first 10 bytes of the file
  // reading PNG dimensions requires the first 24 bytes of the file
  // reading JPEG dimensions requires scanning through jpeg chunks
  // In all formats, the file is at least 24 bytes big, so we'll read that always
  unsigned char buf[24]; fread(buf,1,24,f);

  // For JPEGs, we need to read the first 12 bytes of each chunk.
  // We'll read those 12 bytes at buf+2...buf+14, i.e. overwriting the existing buf.
  if (buf[0]==0xFF && buf[1]==0xD8 && buf[2]==0xFF && buf[3]==0xE0 && buf[6]=='J' && buf[7]=='F' && buf[8]=='I' && buf[9]=='F')
  { long pos=2;
    while (buf[2]==0xFF)
    { if (buf[3]==0xC0 || buf[3]==0xC1 || buf[3]==0xC2 || buf[3]==0xC3 || buf[3]==0xC9 || buf[3]==0xCA || buf[3]==0xCB) break;
      pos += 2+(buf[4]<<8)+buf[5];
      if (pos+12>len) break;
      fseek(f,pos,SEEK_SET); fread(buf+2,1,12,f);
    }
  }

  fclose(f);

  // JPEG: (first two bytes of buf are first two bytes of the jpeg file; rest of buf is the DCT frame
  if (buf[0]==0xFF && buf[1]==0xD8 && buf[2]==0xFF)
  { *y = (buf[7]<<8) + buf[8];
    *x = (buf[9]<<8) + buf[10];
    //cout << *x << endl;
    return true;
  }

  // GIF: first three bytes say "GIF", next three give version number. Then dimensions
  if (buf[0]=='G' && buf[1]=='I' && buf[2]=='F')
  { *x = buf[6] + (buf[7]<<8);
    *y = buf[8] + (buf[9]<<8);
    return true;
  }

  // PNG: the first frame is by definition an IHDR frame, which gives dimensions
  if ( buf[0]==0x89 && buf[1]=='P' && buf[2]=='N' && buf[3]=='G' && buf[4]==0x0D && buf[5]==0x0A && buf[6]==0x1A && buf[7]==0x0A
    && buf[12]=='I' && buf[13]=='H' && buf[14]=='D' && buf[15]=='R')
  { *x = (buf[16]<<24) + (buf[17]<<16) + (buf[18]<<8) + (buf[19]<<0);
    *y = (buf[20]<<24) + (buf[21]<<16) + (buf[22]<<8) + (buf[23]<<0);
    return true;
  }

  return false;
}

int main()
{

const char *theFile = "01.jpg";
int the_x =0;
int the_y =0;
bool didRun = false;

didRun = GetImageSize(theFile, &the_x, &the_y);

  cout << "Dimensions: " << the_x << " x " << the_y << endl;
    return 0;
}
Whilst I applaud very much your desire and dedication to examine the binary jpg file yourself to read the relevant data, as a quick n' dirty means of just getting the work done, you might consider using an image library.

For example, using CImg (code not tested - this is to show a rough outline rather than give working code);


1
2
3
4
5
6
7
8
9
10
#include "CImg.h"
using namespace cimg_library;
int main() { 

  CImg<unsigned char> image("someFile.jpg");
  int width = image.width;
  int height = image.height;

  return 0;
}


CImg relies on having the imageMagick package installed as well, I suspect to use the jpg libraries).
Thanks for the advice Moschops, I originally tried to install the Boost libraries, but after a solid 4 days without managing to get them to compile and link I thought i'd get back to basics.

Just had a look at installing Image Magick via the windows binary distribution, have successfully installed it but again, cannot get it to link with my project in Code Blocks.
Oh yes, I remember now. Magick++ is a real pig for getting the libraries linking under Windows, and it's a bit fiddly under *nix as well.

I just knocked together the following code using the DevIL image library.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <IL/il.h>
#include <iostream>

int main()
{
ilInit();  // Have to init DevIL first
ILuint texid; /* ILuint is a 32bit unsigned integer.
              Variable texid will be used to store image name. */
ilGenImages(1, &texid); /* Generation of one image name - this is what DevIL uses 
                            to keep track of image object */
ilBindImage(texid); /* Binding of image name - setting the "current working image", I think */
bool success = ilLoadImage((const ILstring)"thingy01.jpg"); /* Loading of image */

ILuint Width, Height;
Width = ilGetInteger(IL_IMAGE_WIDTH);
Height = ilGetInteger(IL_IMAGE_HEIGHT);

std::cout << "Height = " << Height << std::endl;
std::cout << "Width = " << Width << std::endl;
return 0;
}


I built it like this:

g++ 156.cpp -lIL where 156.cpp is the name of the source file above, and -lIL instructs the linker to link against the IL library.

The output was as follows:

Height = 405
Width = 443


which is exactly correct for the test image I used.

The whole thing took literally a few minutes, but I'm using a popular Linux and installing DevIL correctly is an absolute breeze. I suspect it'll be a bit harder under Win.

Last edited on
Topic archived. No new replies allowed.