Получение размера изображения JPEG из его двоичного файла

У меня много файлов jpeg с разным размером изображения. Например, вот первые 640 байтов, полученные с помощью hexdump изображения размером 256 * 384 (пикселей):

0000000: ffd8 ffe0 0010 4a46 4946 0001 0101 0048  ......JFIF.....H
0000010: 0048 0000 ffdb 0043 0003 0202 0302 0203  .H.....C........
0000020: 0303 0304 0303 0405 0805 0504 0405 0a07  ................
0000030: 0706 080c 0a0c 0c0b 0a0b 0b0d 0e12 100d  ................

Я предполагаю, что информация о размере должна быть в этих строках. Но я не могу понять, какие байты правильно указывают размеры. Может ли кто-нибудь помочь мне найти поля, содержащие информацию о размере?


person rajeshsr    schedule 25.03.2010    source источник


Ответы (5)


Согласно разделу Синтаксис и структура страница JPEG в википедии, ширина и высота изображения не сохраняются в самом изображении - или, по крайней мере, нет. таким образом, что это довольно легко найти.


Тем не менее, цитата из FAQ по сжатию изображений JPEG, часть 1/2 :

Тема: [22] Как моя программа может извлекать размеры изображения из файла JPEG?

Заголовок файла JPEG состоит из серии блоков, называемых «маркерами». Высота и ширина изображения сохраняются в маркере типа SOFn (начало кадра, тип N).
Чтобы найти SOFn, вы должны пропустить предыдущие маркеры; вам не нужно знать, что находится в других типах маркеров, просто используйте их длинные слова, чтобы пропустить их.
Минимальная необходимая логика - это, возможно, страница кода C.
(Некоторые люди рекомендуют просто искать пару байтов, представляющую SOFn, не обращая внимания на структуру блока маркера. Это небезопасно, потому что предыдущий маркер может содержать шаблон SOFn либо случайно, либо потому, что он содержит Сжатое изображение эскиза в формате JPEG. Если вы не следуете структуре маркера, вы получите размер эскиза вместо размера основного изображения.)
Пример с подробными комментариями на языке C можно найти в rdjpgcom.c в распределение IJG (см. часть 2, пункт 15).
Perl-код можно найти на wwwis по адресу http://www.tardis.ed.ac.uk/~ark/wwwis/.

(Эээ, эта ссылка кажется неработающей ...)


Вот часть кода C, которая может вам помочь: Расшифровка ширины и высоты Файл JPEG (JFIF)

person Pascal MARTIN    schedule 25.03.2010
comment
Если это так, то как наутилус или другая программа для просмотра изображений определяет разрешение изображения? Кажется, они тоже согласны со значением 256 * 384 для этого изображения. - person rajeshsr; 25.03.2010
comment
Большое тебе спасибо! Теперь я понял. greping 0xFFC0, похоже, работает, в любом случае я понимаю опасность, связанную с этим! Еще раз спасибо! Кстати, это мой первый пост на stackoverflow! Довольно удивлен быстротой и точностью ответа. Всем спасибо! - person rajeshsr; 25.03.2010
comment
Последние две ссылки не работают. - person Millie Smith; 05.10.2015
comment
Последние две ссылки все еще не работают - person Bovaz; 05.08.2016
comment
Вот архив последней ссылки: https://web.archive.org/web/20131016210645/http://www.64lines.com/jpeg-width-height - person Louis Hong; 16.01.2017

Эта функция будет читать свойства JPEG

function jpegProps(data) {          // data is an array of bytes
    var off = 0;
    while(off<data.length) {
        while(data[off]==0xff) off++;
        var mrkr = data[off];  off++;
        
        if(mrkr==0xd8) continue;    // SOI
        if(mrkr==0xd9) break;       // EOI
        if(0xd0<=mrkr && mrkr<=0xd7) continue;
        if(mrkr==0x01) continue;    // TEM
        
        var len = (data[off]<<8) | data[off+1];  off+=2;  
        
        if(mrkr==0xc0) return {
            bpc : data[off],     // precission (bits per channel)
            h   : (data[off+1]<<8) | data[off+2],
            w   : (data[off+3]<<8) | data[off+4],
            cps : data[off+5]    // number of color components
        }
        off+=len-2;
    }
}

 

person Ivan Kuckir    schedule 28.01.2018
comment
Краткое и идеальное решение. - person Farshad Mohajeri; 16.05.2020

Вот как я реализовал это с помощью js. Маркер, который вы ищете, - это маркер Sofn, а псевдокод в основном будет следующим:

  • начать с первого байта
  • начало сегмента всегда будет FF, за которым следует другой байт, указывающий тип маркера (эти 2 байта называются маркером)
  • если этот другой байт - от 01 или от D1 до D9, в этом сегменте нет данных, поэтому переходите к следующему сегменту
  • if that marker is C0 or C2 (or any other Cn, more detail in the comments of the code), thats the Sofn marker you're looking for
    • the following bytes after the marker will be P (1 byte), L (2 bytes), Height (2 bytes), Width (2 bytes) respectively
  • в противном случае следующие два байта, за которыми следует это, будут свойством длины (длина всего сегмента, исключая маркер, 2 байта), используйте это, чтобы перейти к следующему сегменту
  • повторяйте, пока не найдете маркер Sofn
function getJpgSize(hexArr) {
  let i = 0;
  let marker = '';

  while (i < hexArr.length) {
    //ff always start a marker,
    //something's really wrong if the first btye isn't ff
    if (hexArr[i] !== 'ff') {
      console.log(i);
      throw new Error('aaaaaaa');
    }

    //get the second byte of the marker, which indicates the marker type
    marker = hexArr[++i];

    //these are segments that don't have any data stored in it, thus only 2 bytes
    //01 and D1 through D9
    if (marker === '01' || (!isNaN(parseInt(marker[1])) && marker[0] === 'd')) {
      i++;
      continue;
    }

    /*
    sofn marker: https://www.w3.org/Graphics/JPEG/itu-t81.pdf pg 36
      INFORMATION TECHNOLOGY –
      DIGITAL COMPRESSION AND CODING
      OF CONTINUOUS-TONE STILL IMAGES –
      REQUIREMENTS AND GUIDELINES

    basically, sofn (start of frame, type n) segment contains information
    about the characteristics of the jpg

    the marker is followed by:
      - Lf [frame header length], two bytes
      - P [sample precision], one byte
      - Y [number of lines in the src img], two bytes, which is essentially the height
      - X [number of samples per line], two bytes, which is essentially the width 
      ... [other parameters]

    sofn marker codes: https://www.digicamsoft.com/itu/itu-t81-36.html
    apparently there are other sofn markers but these two the most common ones
    */
    if (marker === 'c0' || marker === 'c2') {
      break;
    }
    //2 bytes specifying length of the segment (length excludes marker)
    //jumps to the next seg
    i += parseInt(hexArr.slice(i + 1, i + 3).join(''), 16) + 1;
  }
  const size = {
    height: parseInt(hexArr.slice(i + 4, i + 6).join(''), 16),
    width: parseInt(hexArr.slice(i + 6, i + 8).join(''), 16),
  };
  return size;
}
person charri    schedule 19.08.2020

Если вы работаете в системе Linux и имеете под рукой PHP, варианты этого скрипта php могут дать то, что вы ищете:

#! /usr/bin/php -q
<?php

if (file_exists($argv[1]) ) {

    $targetfile = $argv[1];

    // get info on uploaded file residing in the /var/tmp directory:
    $safefile       = escapeshellcmd($targetfile);
    $getinfo        = `/usr/bin/identify $safefile`;
    $imginfo        = preg_split("/\s+/",$getinfo);
    $ftype          = strtolower($imginfo[1]);
    $fsize          = $imginfo[2];

    switch($fsize) {
        case 0:
            print "FAILED\n";
            break;
        default:
            print $safefile.'|'.$ftype.'|'.$fsize."|\n";
    }
}

// eof

хост> imageinfo 009140_DJI_0007.JPG

009140_DJI_0007.JPG | jpeg | 4000x3000 |

(Выводит имя файла, тип файла, размеры файла в формате с разделителями-вертикальными чертами)

На странице руководства:

Для получения дополнительных сведений о команде 'identify' укажите в браузере [...] http://www.imagemagick.org/script/identify.php.

person Bud Hovell    schedule 22.01.2019

Я преобразовал код CPP из верхнего ответа в скрипт Python.

"""
Source: https://stackoverflow.com/questions/2517854/getting-image-size-of-jpeg-from-its-binary#:~:text=The%20header%20of%20a%20JPEG,Of%20Frame%2C%20type%20N).
"""
def get_jpeg_size(data):
   """
   Gets the JPEG size from the array of data passed to the function, file reference: http:#www.obrador.com/essentialjpeg/headerinfo.htm
   """
   data_size=len(data)
   #Check for valid JPEG image
   i=0   # Keeps track of the position within the file
   if(data[i] == 0xFF and data[i+1] == 0xD8 and data[i+2] == 0xFF and data[i+3] == 0xE0): 
   # Check for valid JPEG header (null terminated JFIF)
      i += 4
      if(data[i+2] == ord('J') and data[i+3] == ord('F') and data[i+4] == ord('I') and data[i+5] == ord('F') and data[i+6] == 0x00):
         #Retrieve the block length of the first block since the first block will not contain the size of file
         block_length = data[i] * 256 + data[i+1]
         while (i<data_size):
            i+=block_length               #Increase the file index to get to the next block
            if(i >= data_size): return False;   #Check to protect against segmentation faults
            if(data[i] != 0xFF): return False;   #Check that we are truly at the start of another block
            if(data[i+1] == 0xC0):          #0xFFC0 is the "Start of frame" marker which contains the file size
               #The structure of the 0xFFC0 block is quite simple [0xFFC0][ushort length][uchar precision][ushort x][ushort y]
               height = data[i+5]*256 + data[i+6];
               width = data[i+7]*256 + data[i+8];
               return height, width
            else:
               i+=2;                              #Skip the block marker
               block_length = data[i] * 256 + data[i+1]   #Go to the next block
         return False                   #If this point is reached then no size was found
      else:
         return False                  #Not a valid JFIF string
   else:
      return False                     #Not a valid SOI header




with open('path/to/file.jpg','rb') as handle:
   data = handle.read()

h, w = get_jpeg_size(data)
print(s)
person Gajraj Singh Chouhan    schedule 07.06.2020