¿Cómo obtener el ancho/alto del archivo jpeg sin usar la biblioteca?

11 minutos de lectura

En primer lugar, quiero decir que intenté muchas veces encontrar la respuesta usando la búsqueda de Google, y encontré muchos resultados, pero no entendí, porque no conozco la idea de leer un archivo binario y convertir el valor obtenido en valor legible.

Lo que intenté hacerlo.

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

Quiero una explicación simple con ejemplos, cómo obtener el ancho/alto del archivo jpeg desde su encabezado y luego convertir ese valor en un valor legible.

  • ¿Tiene los detalles de lo que está contenido en los archivos jpeg? Si es así, inclúyalo en su pregunta. Dudo que su método anterior funcione, ya que generalmente hay un encabezado al principio y luego comienzan los valores de píxeles reales. Si solo necesita la información de alto y ancho, creo que puede obtenerla leyendo solo el encabezado.

    – Shrm

    16 de agosto de 2013 a las 2:01


  • @mishr: estoy hablando de jpeg files en general.

    – Rey Leon

    16 de agosto de 2013 a las 2:04

  • Lo entiendo, pero la pregunta es ¿sabes cuál es el formato de los archivos jpeg? ¿O quieres que te lo encontremos?

    – Shrm

    16 de agosto de 2013 a las 2:06

  • @mishr: Esta es la primera vez que trato con archivos binarios, como jpeg, y no entiendo nada al respecto.

    – Rey Leon

    16 de agosto de 2013 a las 2:10


  • Mira esto fastgraph.com/help/jpeg_header_format.html. Dice que el encabezado contiene la información de ancho y alto en los desplazamientos 2 y 4 respectivamente. Todo lo que necesita hacer es ese punto fread a estas compensaciones usando fseek y leer 2 bytes de cada ubicación. Luego, debe convertir estos bytes en números enteros. Darle una oportunidad.

    – Shrm

    16 de agosto de 2013 a las 2:16


avatar de usuario
jxh

Desafortunadamente, no parece ser simple para JPEG. Deberías mirar la fuente de la jhead herramienta de línea de comandos. Proporciona esta información. Al pasar por la fuente, verás la función ReadJpegSections. Esta función explora todos los segmentos contenidos en el archivo JPEG para extraer la información deseada. El ancho y alto de la imagen se obtiene al procesar los marcos que tienen un SOFn marcador.

Veo que la fuente es de dominio público, así que mostraré el fragmento que obtiene la información de la imagen:

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

Desde el código fuente, me queda claro que no hay un solo “encabezado” con esta información. Tiene que escanear el archivo JPEG, analizando cada segmento, hasta que encuentre el segmento con la información que desea. Esto se describe en el articulo de wikipedia:

Una imagen JPEG consta de una secuencia de segmentos, cada uno de los cuales comienza con un marcador, cada uno de los cuales comienza con un byte 0xFF seguido de un byte que indica qué tipo de marcador es. Algunos marcadores consisten solo en esos dos bytes; otros van seguidos de dos bytes que indican la longitud de los datos de carga útil específicos del marcador que siguen.


Un archivo JPEG consta de una secuencia de segmentos:

SEGMENT_0
SEGMENT_1
SEGMENT_2
...

Cada segmento comienza con un marcador de 2 bytes. El primer byte es 0xFF, el segundo byte determina el tipo del segmento. A esto le sigue una codificación de la longitud del segmento. Dentro del segmento hay datos específicos de ese tipo de segmento.

El ancho y la altura de la imagen se encuentran en un segmento de tipo SOFno “Inicio del cuadro [n]”, donde “n” es un número que significa algo especial para un decodificador JPEG. Debería ser lo suficientemente bueno para buscar solo un SOF0y su designación de byte es 0xC0. Una vez que encuentre este marco, puede decodificarlo para encontrar la altura y el ancho de la imagen.

Entonces, la estructura de un programa para hacer lo que quieres se vería así:

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

Esta es esencialmente la estructura que se encuentra en de Michael Petrov get_jpeg_size() implementación.

  • @LionKing, avíseme si la explicación no es clara o si necesita ayuda adicional.

    – jxh

    16 de agosto de 2013 a las 18:18

  • Gracias, pero no lo entiendo, quiero una forma y un ejemplo muy simple para poder entenderlo.

    – Rey Leon

    16 de agosto de 2013 a las 23:21

  • Apreciaría mucho una razón para el voto negativo. ¡Gracias!

    – jxh

    16 de agosto de 2013 a las 23:56

  • Lo siento mucho, pero esto ocurrió sin querer (incorrectamente), perdóname.

    – Rey Leon

    17 de agosto de 2013 a las 0:07


  • @LionKing: Realmente no hay problema. Solo para su información, los votos negativos son totalmente anónimos, por lo que no hay forma de que yo sepa quién me votó negativo. Solo quería una razón para poder mejorar la respuesta.

    – jxh

    17 de agosto de 2013 a las 4:12

avatar de usuario
usuario3614789

entonces tienes que encontrar el marcador de altura y ancho de jpeg que es [ffc0].

después de encontrar ffc0 en formato binario, los cuatro, cinco bytes son altos y seis y siete bytes son anchos.

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

avatar de usuario
vlzvl

la pregunta es antigua y las otras respuestas son correctas pero su formato no es el más fácil. solo uso getc para obtener rápidamente las dimensiones, mientras se saltan los marcadores irrelevantes (también admite JPEG progresivos):

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

  • A menos que me esté perdiendo algo, esta y todas las demás respuestas que leen todos los bytes fallarán si el segmento ff c0 o ff c2 viene después de algún otro segmento donde la carga útil contiene ff c0 / ff c2.

    – David S.

    29 de noviembre de 2017 a las 3:31

  • @Dave S nunca escuchó sobre el relleno de bytes?

    – Lucas

    7 de agosto de 2018 a las 23:23

  • @Luca Cuando hojeé la entrada de wiki en la especificación, leí que decía que los segmentos solo rellenaban bytes para datos codificados en entropía, por lo que ff co / ff c2 podría ocurrir en otras cargas útiles.

    – David S.

    08/08/2018 a las 18:40

  • @DaveS ¿Conoce alguna otra forma más confiable de averiguar esta información?

    – HRK44

    13 de diciembre de 2018 a las 9:52

  • @HRK44 solo para hacer un análisis mínimo, leer los tamaños de registro y saltarlos hasta que vea el registro ff c0.

    – David S.

    13 dic 2018 a las 21:35

avatar de usuario
Apóstoles

Las dimensiones de la imagen en los archivos JPEG se pueden encontrar de la siguiente manera:

1) buscar FF C0

2) En las compensaciones +4 y +6 después de esta ubicación están el alto y el ancho (palabras), respectivamente.

En la mayoría de los casos, los desplazamientos absolutos de alto y ancho son A3 y A5, respectivamente.

Aquí hay un código simple que escribí que parece funcionar de manera confiable.

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

  • Gracias, es el ejemplo anterior usando C/C++?, que es Seek, Read ¿Funciones?, y ¿Cuál es el beneficio de esta función? MOTOSHORT ?, también que es iHandle variable ?.

    – Rey Leon

    17 de agosto de 2013 a las 17:17


  • Las funciones de búsqueda y lectura son E/S de archivos genéricos que deberían existir en todos los sistemas. El MOTOSHORT es una macro (vea la parte superior del código) que es conveniente para leer cortos Big Endian en cualquier sistema, independientemente de su endianess. La variable ihandle es el identificador de archivo que se supone que se abre antes de llamar a la función.

    – BitBank

    18 de agosto de 2013 a las 5:45

avatar de usuario
códigogeek

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

  • Gracias, es el ejemplo anterior usando C/C++?, que es Seek, Read ¿Funciones?, y ¿Cuál es el beneficio de esta función? MOTOSHORT ?, también que es iHandle variable ?.

    – Rey Leon

    17 de agosto de 2013 a las 17:17


  • Las funciones de búsqueda y lectura son E/S de archivos genéricos que deberían existir en todos los sistemas. El MOTOSHORT es una macro (vea la parte superior del código) que es conveniente para leer cortos Big Endian en cualquier sistema, independientemente de su endianess. La variable ihandle es el identificador de archivo que se supone que se abre antes de llamar a la función.

    – BitBank

    18 de agosto de 2013 a las 5:45

avatar de usuario
Guilherme Campos Hazán

Aquí hay un código que escribí en Java. Funciona bien para jpegs tomados de una cámara. Escanea todo el código para encontrar el tamaño de imagen más grande. No pude mejorarlo para saltar las longitudes de cada bloque porque no funciona. Si alguien puede mejorar el código para hacer eso, sería genial.

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

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad