17

Firstly I want to say I tried many times to find the answer by using google search, and I found many results but I did not understand, because I don't know the idea of reading a binary file, and convert the value that Obtained to readable value.

What I tried doing it.

unsigned char fbuff[16];
FILE *file;
file = fopen("C:\\loser.jpg", "rb");
if(file != NULL){
   fseek(file, 0, SEEK_SET);
   fread(fbuff, 1, 16, file);
   printf("%d\n", fbuff[1]);
   fclose(file);
}else{
   printf("File does not exists.");
}

I want a simple explanation with example shows, how to get width/height of jpeg file from its header, and then convert that value to readable value.

8
  • Do you have the details of what is contained in the jpeg files? If you have, please include it in your question. I doubt your above method will work since there is generally a header at the beginning and then the actual pixel values start. If you need only the height and width information, I believe you can get that by reading the header alone. Commented Aug 16, 2013 at 2:01
  • @mishr: I'm talking about jpeg files in general. Commented Aug 16, 2013 at 2:04
  • I understand that, but the question is do you know what is the format for jpeg files? Or do you want us to find it for you ? Commented Aug 16, 2013 at 2:06
  • 1
    Take a look at this fastgraph.com/help/jpeg_header_format.html. It says that the header contains the width and height information at offsets 2 and 4 respectively. All you need to do is that point the fread to these offsets using fseek and read 2 bytes from each location. Then you need to convert these bytes to integers. Give it a try. Commented Aug 16, 2013 at 2:16
  • 1
    @shm, the link you gave (fastgraph.com/help/jpeg_header_format.html) contains totally wrong information about JPEG header. Even the stamp is wrong. Then width and height at offset 2 and 4 resp/ly? These people must be crazy. Commented May 12, 2019 at 9:16

8 Answers 8

21

Unfortunately, it doesn't seem to be simple for JPEG. You should look at the source to the jhead command line tool. It provides this information. When going through the source, you will see the function ReadJpegSections. This function scans through all the segments contained within the JPEG file to extract the desired information. The image width and height is obtained when processing the frames that have an SOFn marker.

I see the source is in the public domain, so I'll show the snippet that gets the image info:

static int Get16m(const void * Short)
{
    return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1];
}

static void process_SOFn (const uchar * Data, int marker)
{
    int data_precision, num_components;

    data_precision = Data[2];
    ImageInfo.Height = Get16m(Data+3);
    ImageInfo.Width = Get16m(Data+5);

From the source code, it is clear to me there is no single "header" with this information. You have to scan through the JPEG file, parsing each segment, until you find the segment with the information in it that you want. This is described in the wikipedia article:

A JPEG image consists of a sequence of segments, each beginning with a marker, each of which begins with a 0xFF byte followed by a byte indicating what kind of marker it is. Some markers consist of just those two bytes; others are followed by two bytes indicating the length of marker-specific payload data that follows.


A JPEG file consists of a sequence of segments:

SEGMENT_0
SEGMENT_1
SEGMENT_2
...

Each segment begins with a 2-byte marker. The first byte is 0xFF, the second byte determines the type of the segment. This is followed by an encoding of the length of the segment. Within the segment is data specific to that segment type.

The image width and height is found in a segment of type SOFn, or "Start of frame [n]", where "n" is some number that means something special to a JPEG decoder. It should be good enough to look only for a SOF0, and its byte designation is 0xC0. Once you find this frame, you can decode it to find the image height and width.

So the structure of a program to do what you want would look like:

file_data = the data in the file
data = &file_data[0]
while (data not at end of file_data)
    segment_type = decoded JPEG segment type at data
    if (type != SOF0)
        data += byte length for segment_type
        continue
    else
        get image height and width from segment
        return

This is essentially the structure found in Michael Petrov's get_jpeg_size() implementation.

Sign up to request clarification or add additional context in comments.

5 Comments

@LionKing, let me know if the explanation is not clear, or if you need additional help.
Thanks, but I don't understand it, I want a very simple way and example in order to understand it.
I would very much appreciate a reason for the down vote. Thanks!
I'm so sorry but this is occurred unintentionally(incorrectly), forgive me.
@LionKing: There is really no problem. Just FYI, down votes are totally anonymous, so there is no way for me to know who down voted me. I just wanted a reason so I could improve the answer.
14

then you have to find hight and width marker of jpeg that is [ffc0].

after finding ffc0 in binary formate, the the four,five bytes are hight and six and seven bytes are width.

eg: [ff c0] d8 c3 c2 [ff da] [00 ff]
                      |         |
                      |         |
                      ->height  ->width

int position;
unsigned char len_con[2];
/*Extract start of frame marker(FFC0) of width and hight and get the position*/
for(i=0;i<FILE_SIZE;i++)
{
    if((image_buffer[i]==FF) && (image_buffer[i+1]==c0) )
    {
        position=i;
    }
}
/*Moving to the particular byte position and assign byte value to pointer variable*/
position=position+5;
*height=buffer_src[position]<<8|buffer_src[position+1];
*width=buffer_src[position+2]<<8|buffer_src[position+3];

printf("height %d",*height);
printf("width %d",*width);

Comments

6

the question is old and the other answers are correct but their format is not the easiest one. I just use getc to quickly get the dimensions, while skipping irrelevant markers (it also supports Progressive JPEGs):

  int height, width;
  // start of image (SOI)
  getc(f);   // oxff
  getc(f);   // oxd8
  // Scan miscellaneous markers until we reach SOF0 marker (0xC0)
  for(;;) {
     // next marker
     int marker;
     while((marker = getc(f)) != 0xFF);
     while((marker = getc(f)) == 0xFF);
     // SOF
     if (marker == 0xC0 || marker == 0xC2) {
        getc(f);   // length (2 bytes)
        getc(f);   // #
        getc(f);   // bpp, usually 8
        height = (getc(f) << 8) + getc(f);   // height
        width = (getc(f) << 8) + getc(f);   // width
        break;
     }
  }

7 Comments

Unless I'm missing something, this and all of the other answers that read all bytes will fail if the ff c0 or ff c2 segment comes after some other segment where the payload happens to contain ff c0 / ff c2.
@Dave S never heard about byte stuffing?
@Luca When I skimmed wiki entry on the spec I read it as saying segments only did byte stuffing for entropy-coded data, so ff co / ff c2 could occur in other payloads.
Also, jpeg files can contain a thumbnail, which is an embedded jpeg file with its own SOFn marker, and appears before the general SOFn marker (in a APPn segment).
@DaveS Do you know some other more reliable way to figure out this information?
@HRK44 just to do some minimal parsing, reading the record sizes and jumping past them until you see the ff c0 record.
|
3

Image dimensions in JPEG files can be found as follows:

1) Look for FF C0

2) At offsets +4 and +6 after this location are height and width (words), resp/ly.

In most cases, the absolute offsets of height and width are A3 and A5, resp/ly.

Comments

1

Here's some simple code I wrote which seems to work reliably.

#define MOTOSHORT(p) ((*(p))<<8) + *(p+1)
unsigned char cBuf[32];
int iBytes, i, j, iMarker, iFilesize;
unsigned char ucSubSample;
int iBpp, iHeight, iWidth;

         Seek(iHandle, 0, 0); // read the first 32 bytes
         iBytes = Read(iHandle, cBuf, 32);

         i = j = 2; /* Start at offset of first marker */
         iMarker = 0; /* Search for SOF (start of frame) marker */
         while (i < 32 && iMarker != 0xffc0 && j < iFileSize)
            {
            iMarker = MOTOSHORT(&cBuf[i]) & 0xfffc;
            if (iMarker < 0xff00) // invalid marker, could be generated by "Arles Image Web Page Creator" or Accusoft
               {
               i += 2;
               continue; // skip 2 bytes and try to resync
               }
            if (iMarker == 0xffc0) // the one we're looking for
               break;
            j += 2 + MOTOSHORT(&cBuf[i+2]); /* Skip to next marker */
            if (j < iFileSize) // need to read more
               {
               Seek(iHandle, j, 0); // read some more
               iBytes = Read(iHandle, cBuf, 32);
               i = 0;
               }
            else // error, abort
               break;
            } // while
         if (iMarker != 0xffc0)
            goto process_exit; // error - invalid file?
         else
            {
            iBpp = cBuf[i+4]; // bits per sample
            iHeight = MOTOSHORT(&cBuf[i+5]);
            iWidth = MOTOSHORT(&cBuf[i+7]);
            iBpp = iBpp * cBuf[i+9]; /* Bpp = number of components * bits per sample */
            ucSubSample = cBuf[i+11];
            }

2 Comments

Thanks, is the previous example by using C/C++?, what is Seek, Read functions ?, and What is the benefit of this function MOTOSHORT ?, also what is iHandle variable ?.
The seek and read functions are generic file i/o that should exist in all systems. The MOTOSHORT is a macro (see top of code) which is convenient for reading big endian shorts on any system regardless of endianess. The ihandle variable is the file handle that is assumed to be opened before calling the function.
0
int  GetJpegDimensions(
    char            *pImage,
    size_t          nSize,
    unsigned32      *u32Width,
    unsigned32      *u32Height,
    char            *szErrMsg)
{
    int             nIndex;
    int             nStartOfFrame;
    int             nError = NO_ERROR;
    bool            markerFound = false;
    unsigned char   ucWord0;
    unsigned char   ucWord1;

    // verify START OF IMAGE marker = FF D8
    nIndex = 0;
    ucWord0 = pImage[nIndex];
    ucWord1 = pImage[nIndex+1];

    // marker FF D8  starts a valid JPEG
    if ((ucWord0 == 0xFF)  && (ucWord1 == 0xD8))
    {
        // search for START OF FRAME 0  marker  FF C0
        for (nIndex = 2;
            (nIndex < nSize-2) && (markerFound == false);
             nIndex += 2)
        {
            ucWord0 = pImage[nIndex];
            ucWord1 = pImage[nIndex+1];
            if (ucWord0 == 0xFF)
            {
                if (ucWord1 == 0xC0)
                {
                    markerFound = true;
                    nStartOfFrame = nIndex;
                }
            }
            if (ucWord1 == 0xFF)
            {
                ucWord0 = pImage[nIndex+2];
                if (ucWord0 == 0xC0)
                {
                    markerFound = true;
                    nStartOfFrame = nIndex+1;
                }
            }
        } // while

        if (markerFound)
        {
            nError  = NO_ERROR;
            ucWord0 = pImage[nStartOfFrame+5];
            ucWord1 = pImage[nStartOfFrame+6];
            *u32Height = ucWord1 + (ucWord0 << 8);

            ucWord0 = pImage[nStartOfFrame+7];
            ucWord1 = pImage[nStartOfFrame+8];
            *u32Width =  ucWord1 + (ucWord0 << 8);
        }
        else
        {
            // start of frame 0 not found
            nError = -2;
            sprintf(szErrMsg,
              "Not a valid JPEG image. START OF FRAME 0 marker FFC0 not found");
        }
    }
    else   // START OF IMAGE marker not found
    {
        nError = -1;
        sprintf(szErrMsg,
          "Not a valid JPEG image. START OF IMAGE marker FFD8 not found");
    }
    return nError;
}

Comments

0

Here's a code i wrote in Java. Works fine for jpegs taken from a camera. It scans all the code to find the biggest image size. I could not improve it to skip on the lengths of each block because it doesn't work. If anyone can improve the code to do that it would be great.

int getShort(byte[] p, int i)
{
   int p0 = p[i] & 0xFF;
   int p1 = p[i+1] & 0xFF;
   return p1 | (p0 << 8);
}

int[]  GetJpegDimensions(byte[] b)
{
    int nIndex;
    int height=0, width=0, size=0;
    int nSize = b.length;

    // marker FF D8  starts a valid JPEG
    if (getShort(b,0) == 0xFFD8)
       for (nIndex = 2; nIndex < nSize-1; nIndex += 4)
          if (b[nIndex] == -1/*FF*/ && b[nIndex+1] == -64/*C0*/)
          {
             int w = getShort(b,nIndex+7);
             int h = getShort(b,nIndex+5);
             if (w*h > size)
             {
                size = w*h;
                width = w;
                height = h;
             }
          }
    return new int[]{width,height};
}

Comments

-1

Interesting topic with all the code snippets.

In Hex view all I do is look for the 4 bytes that come after 00 11 08 and that will always be your dimensions.

Let's take a basic image say 82 x 82 pixels. The 4 bytes following 00 11 08 will be 00 52 00 52.

In UTF-8 this will appear visually as NUL DC1 BS NUL R NUL R

The NUL before the R indicates that the image falls below the 256 pixel range.

The R indicates the 82nd character in the ASCII table, hence 82x82.

Let's say the 4 dimension bytes were SOH R SOH R , then the image would be 338x338

Why? Because 338 minus 256 = 82

Hope this simple explanation helps understand it a bit better.

Example of 82x82 https://vanta.host/uploads/1747762013988-477888795.jpg

You can simply drag it into a hex viewer: https://heartwarming-bat.static.domains/imagetohex

2 Comments

Why on earth would you interpret binary values such as these as UTF-8 text?? That makes no sense whatsoever.
Yes, I know that seems strange. But because I have no computer science background at all, I simply began learning what all the Unicode character combinations meant when I opened up images in Notepad++, which is of course a text editor, so everything I was looking at made me rather dizzy. That was back in 2023, and now I can pretty much move bytes around in any image format without ever leaving Notepad...and still learning... :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.