How to get the width/height of jpeg file without using library?

后端 未结 7 1513
清歌不尽
清歌不尽 2020-12-25 08:43

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 read

相关标签:
7条回答
  • 2020-12-25 08:53

    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.

    0 讨论(0)
  • 2020-12-25 08:59

    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.

    0 讨论(0)
  • 2020-12-25 09:04

    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];
                }
    
    0 讨论(0)
  • 2020-12-25 09:05
    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;
    }
    
    0 讨论(0)
  • 2020-12-25 09:16

    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;
         }
      }
    
    0 讨论(0)
  • 2020-12-25 09:16

    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};
    }
    
    0 讨论(0)
提交回复
热议问题