Wie erhalte ich die Breite/Höhe einer JPEG-Datei, ohne die Bibliothek zu verwenden?

Lesezeit: 10 Minuten

Zunächst möchte ich sagen, dass ich viele Male versucht habe, die Antwort mithilfe der Google-Suche zu finden, und ich habe viele Ergebnisse gefunden, aber ich habe sie nicht verstanden, weil ich die Idee nicht kenne, eine Binärdatei zu lesen und den erhaltenen Wert zu konvertieren ablesbarer Wert.

Was ich dabei versucht habe.

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.");
}

Ich möchte eine einfache Erklärung mit Beispielshows, wie man die Breite/Höhe einer JPEG-Datei aus ihrem Header erhält und diesen Wert dann in einen lesbaren Wert umwandelt.

  • Haben Sie die Details darüber, was in den JPEG-Dateien enthalten ist? Wenn ja, geben Sie es bitte in Ihre Frage ein. Ich bezweifle, dass Ihre obige Methode funktionieren wird, da am Anfang normalerweise ein Header steht und dann die eigentlichen Pixelwerte beginnen. Wenn Sie nur die Höhen- und Breiteninformationen benötigen, können Sie diese meiner Meinung nach allein durch Lesen der Kopfzeile erhalten.

    – schrm

    16. August 2013 um 2:01 Uhr


  • @mishr: Ich rede von jpeg files Im Algemeinen.

    – König der Löwen

    16. August 2013 um 2:04 Uhr

  • Ich verstehe das, aber die Frage ist, wissen Sie, welches Format JPEG-Dateien haben? Oder möchten Sie, dass wir es für Sie finden?

    – schrm

    16. August 2013 um 2:06 Uhr

  • @mishr: Dies ist das erste Mal, dass ich mich mit Binärdateien wie JPEG befasse, und ich verstehe nichts davon.

    – König der Löwen

    16. August 2013 um 2:10 Uhr


  • Schau dir das an fastgraph.com/help/jpeg_header_format.html. Es besagt, dass der Header die Breiten- und Höheninformationen an den Offsets 2 bzw. 4 enthält. Alles, was Sie tun müssen, ist dieser Punkt fread zu diesen Offsets mit fseek und lese 2 Bytes von jeder Stelle. Dann müssen Sie diese Bytes in ganze Zahlen umwandeln. Versuche es.

    – schrm

    16. August 2013 um 2:16 Uhr


Benutzeravatar von jxh
jxh

Leider scheint es für JPEG nicht einfach zu sein. Sie sollten sich die Quelle dazu ansehen jhead Kommandozeilen-Tool. Es liefert diese Informationen. Wenn Sie durch die Quelle gehen, sehen Sie die Funktion ReadJpegSections. Diese Funktion durchsucht alle in der JPEG-Datei enthaltenen Segmente, um die gewünschten Informationen zu extrahieren. Die Bildbreite und -höhe wird bei der Verarbeitung der Rahmen mit einem erhalten SOFn Marker.

Ich sehe, dass die Quelle gemeinfrei ist, also zeige ich das Snippet, das die Bildinformationen erhält:

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);

Aus dem Quellcode ist mir klar, dass es keinen einzigen “Header” mit diesen Informationen gibt. Sie müssen die JPEG-Datei durchsuchen und jedes Segment parsen, bis Sie das Segment mit den gewünschten Informationen darin finden. Dies ist in der beschrieben Wikipedia-Artikel:

Ein JPEG-Bild besteht aus einer Folge von Segmenten, die jeweils mit einer Markierung beginnen, die jeweils mit einem 0xFF-Byte beginnen, gefolgt von einem Byte, das angibt, um welche Art von Markierung es sich handelt. Einige Marker bestehen nur aus diesen zwei Bytes; anderen folgen zwei Bytes, die die Länge der darauffolgenden markierungsspezifischen Nutzlastdaten angeben.


Eine JPEG-Datei besteht aus einer Folge von Segmenten:

SEGMENT_0
SEGMENT_1
SEGMENT_2
...

Jedes Segment beginnt mit einer 2-Byte-Markierung. Das erste Byte ist 0xFF, das zweite Byte bestimmt den Typ des Segments. Danach folgt eine Codierung der Länge des Segments. Innerhalb des Segments befinden sich Daten, die für diesen Segmenttyp spezifisch sind.

Die Bildbreite und -höhe wird in einem Textsegment gefunden SOFnoder “Beginn des Frames [n]”, wobei “n” eine Zahl ist, die für einen JPEG-Decoder etwas Besonderes bedeutet. Es sollte ausreichen, nur nach a zu suchen SOF0und seine Byte-Bezeichnung ist 0xC0. Sobald Sie diesen Rahmen gefunden haben, können Sie ihn decodieren, um die Bildhöhe und -breite zu ermitteln.

Die Struktur eines Programms, das das tut, was Sie wollen, würde also folgendermaßen aussehen:

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

Dies ist im Wesentlichen die Struktur, die in gefunden wird Michael Petrovs get_jpeg_size() Implementierung.

  • @LionKing, lassen Sie mich wissen, wenn die Erklärung nicht klar ist oder wenn Sie zusätzliche Hilfe benötigen.

    – jxh

    16. August 2013 um 18:18 Uhr

  • Danke, aber ich verstehe es nicht, ich möchte einen sehr einfachen Weg und ein Beispiel, um es zu verstehen.

    – König der Löwen

    16. August 2013 um 23:21 Uhr

  • Ich würde mich sehr über einen Grund für die Ablehnung freuen. Vielen Dank!

    – jxh

    16. August 2013 um 23:56 Uhr

  • Es tut mir so leid, aber dies ist unbeabsichtigt (falsch) aufgetreten, verzeihen Sie mir.

    – König der Löwen

    17. August 2013 um 0:07 Uhr


  • @LionKing: Es gibt wirklich kein Problem. Nur zu Ihrer Information, Down-Votes sind völlig anonym, daher kann ich nicht wissen, wer mich down-voted hat. Ich wollte nur einen Grund, damit ich die Antwort verbessern kann.

    – jxh

    17. August 2013 um 4:12 Uhr

Benutzeravatar von user3614789
Benutzer3614789

dann müssen Sie die Höhen- und Breitenmarkierung von JPEG finden [ffc0].

Nachdem ffc0 im Binärformat gefunden wurde, sind die vier, fünf Bytes hoch und sechs und sieben Bytes breit.

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);

Benutzeravatar von vlzvl
vlzvl

Die Frage ist alt und die anderen Antworten sind richtig, aber ihr Format ist nicht das einfachste. Ich benutze nur getc um schnell die Abmessungen zu erhalten, während irrelevante Markierungen übersprungen werden (es unterstützt auch 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;
     }
  }

  • Sofern mir nichts fehlt, schlagen diese und alle anderen Antworten, die alle Bytes lesen, fehl, wenn das Segment ff c0 oder ff c2 nach einem anderen Segment kommt, in dem die Nutzlast zufällig ff c0 / ff c2 enthält.

    – David S

    29. November 2017 um 3:31 Uhr

  • @ Dave S hat noch nie von Byte Stuffing gehört?

    – Lukas

    7. August 2018 um 23:23 Uhr

  • @Luca Als ich den Wiki-Eintrag in der Spezifikation überflogen habe, habe ich gelesen, dass Segmente nur Byte-Füllung für entropiecodierte Daten durchgeführt haben, sodass ff co / ff c2 in anderen Nutzlasten auftreten könnte.

    – David S

    8. August 2018 um 18:40 Uhr

  • @DaveS Kennen Sie einen anderen zuverlässigeren Weg, um diese Informationen herauszufinden?

    – HRK44

    13. Dezember 2018 um 9:52 Uhr

  • @ HRK44, nur um minimal zu parsen, die Datensatzgrößen zu lesen und an ihnen vorbei zu springen, bis Sie den ff c0-Datensatz sehen.

    – David S

    13. Dezember 2018 um 21:35 Uhr

Benutzeravatar von Apostolos
Apostel

Bildabmessungen in JPEG-Dateien finden Sie wie folgt:

1) Suchen Sie nach FF C0

2) An den Offsets +4 und +6 nach dieser Position sind jeweils Höhe und Breite (Wörter).

In den meisten Fällen sind die absoluten Versätze von Höhe und Breite A3 bzw. A5.

Hier ist ein einfacher Code, den ich geschrieben habe und der zuverlässig zu funktionieren scheint.

#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];
            }

  • Danke, ist das vorherige Beispiel mit C/C++?, was ist Seek, Read Funktionen ? und Was ist der Vorteil dieser Funktion MOTOSHORT ?, auch was ist iHandle variabel?.

    – König der Löwen

    17. August 2013 um 17:17 Uhr


  • Die Such- und Lesefunktionen sind generische Datei-E/A, die in allen Systemen vorhanden sein sollten. MOTOSHORT ist ein Makro (siehe Anfang des Codes), das zum Lesen von Big-Endian-Shorts auf jedem System geeignet ist, unabhängig von der Endianität. Die ihandle-Variable ist das Datei-Handle, von dem angenommen wird, dass es geöffnet wird, bevor die Funktion aufgerufen wird.

    – BitBank

    18. August 2013 um 5:45 Uhr

Benutzeravatar von codegeek
Codefreak

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;
}

  • Danke, ist das vorherige Beispiel mit C/C++?, was ist Seek, Read Funktionen ? und Was ist der Vorteil dieser Funktion MOTOSHORT ?, auch was ist iHandle variabel?.

    – König der Löwen

    17. August 2013 um 17:17 Uhr


  • Die Such- und Lesefunktionen sind generische Datei-E/A, die in allen Systemen vorhanden sein sollten. MOTOSHORT ist ein Makro (siehe Anfang des Codes), das zum Lesen von Big-Endian-Shorts auf jedem System geeignet ist, unabhängig von der Endianität. Die ihandle-Variable ist das Datei-Handle, von dem angenommen wird, dass es geöffnet wird, bevor die Funktion aufgerufen wird.

    – BitBank

    18. August 2013 um 5:45 Uhr

Benutzeravatar von Guilherme Campos Hazan
Guilherme Campos Hazan

Hier ist ein Code, den ich in Java geschrieben habe. Funktioniert gut für JPEGs, die von einer Kamera aufgenommen wurden. Es scannt den gesamten Code, um die größte Bildgröße zu finden. Ich konnte es nicht verbessern, die Längen jedes Blocks zu überspringen, weil es nicht funktioniert. Wenn jemand den Code dafür verbessern könnte, wäre das großartig.

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};
}

1432840cookie-checkWie erhalte ich die Breite/Höhe einer JPEG-Datei, ohne die Bibliothek zu verwenden?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy