Извлечь миниатюру из файла jpeg

Я хотел бы извлечь миниатюру изображения из JPEG, без какой-либо внешней библиотеки. Я имею в виду, что это не слишком сложно, потому что мне нужно знать, где начинается эскиз и заканчивается в файле, и просто вырезать его. Я изучаю много документации ( т. е.:http://www.media.mit.edu/pia/Research/deepview/exif.html), и попробуйте проанализировать JPEG, но не все ясно. Я попытался шаг за шагом отслеживать байты, но в глубине я запутался. Есть ли хорошая документация, или читаемый источник код для извлечения информации о начальной и конечной позиции эскиза в файле jpeg?

спасибо!

4 ответов


для большинства изображений JPEG, созданных телефонами или цифровыми камерами, миниатюра (если есть) хранится в маркере APP1 (FFE1). Внутри этого сегмента маркера находится файл TIFF, содержащий информацию EXIF для основного изображения и необязательного эскиза, сохраненного в виде сжатого изображения JPEG. Файл TIFF обычно содержит две "страницы", где первая страница-это информация EXIF, а вторая страница-миниатюра, хранящаяся в" старом " формате TIFF type 6. Формат типа 6 - это когда файл JPEG просто хранится как есть внутри обертки TIFF. Если вы хотите, чтобы максимально простой код извлекал миниатюру в виде JFIF, вам нужно будет сделать следующие шаги:

  1. ознакомьтесь с маркерами/тегами JFIF и TIFF. Маркеры JFIF состоят из двух байтов: 0xFF, за которым следует Тип маркера (0xE1 для APP1). За этими двумя байтами следует двухбайтовая длина, хранящаяся в порядке big-endian. Для файлов TIFF обратитесь к ссылке Adobe TIFF 6.0.
  2. поиск файла JPEG для маркера app1 (FFE1) EXIF. Может быть несколько маркеров APP1 и может быть несколько маркеров перед APP1.
  3. маркер APP1, который вы ищете, содержит буквы "EXIF" сразу после поля длины.
  4. найдите "II" или " MM " (6 байт от длины), чтобы указать endianness, используемый в файле TIFF. Второй = Интел = с обратным порядком байтов, мм = Моторола = с обратным порядком байтов.
  5. пропустите теги первой страницы, чтобы найти второй IFD, где изображение сохраняется. Во второй "странице" найдите два тега TIFF, которые указывают на данные JPEG. Тег 0x201 имеет смещение данных JPEG (относительно II / MM), а тег 0x202 имеет длину в байтах.

Exiftool очень способен сделать это быстро и легко:

exiftool -b -ThumbnailImage my_image.jpg > my_thumbnail.jpg

для этой проблемы есть гораздо более простое решение, но я не знаю, насколько оно надежно: начните читать файл JPEG с третьего байта и найдите FFD8 (начало jpeg image marker), затем FFD9 (конец JPEG image marker). Извлеките его и вуаля, это ваш ноготь.

простая реализация JavaScript:

function getThumbnail(file, callback) {
    if (file.type == "image/jpeg") {
        var reader = new FileReader();
        reader.onload = function (e) {
            var array = new Uint8Array(e.target.result),
                start, end;
            for (var i = 2; i < array.length; i++) {
                if (array[i] == 0xFF) {
                    if (!start) {
                        if (array[i + 1] == 0xD8) {
                            start = i;
                        }
                    } else {
                        if (array[i + 1] == 0xD9) {
                            end = i;
                            break;
                        }
                    }
                }
            }
            if (start && end) {
                callback(new Blob([array.subarray(start, end)], {type:"image/jpeg"}));
            } else {
                // TODO scale with canvas
            }
        }
        reader.readAsArrayBuffer(file.slice(0, 50000));
    } else if (file.type.indexOf("image/") === 0) {
        // TODO scale with canvas
    }
}

страница Википедии на JFIF в http://en.wikipedia.org/wiki/JPEG_File_Interchange_Format дает хорошее описание заголовка JPEG (заголовок содержит эскиз в виде несжатого растрового изображения). Это должно дать вам представление о макете и, следовательно, код, необходимый для извлечения информации.

Hexdump заголовка изображения (маленький дисплей endian):

sdk@AndroidDev:~$ head -c 48 stfu.jpg |hexdump
0000000 d8ff e0ff 1000 464a 4649 0100 0101 4800
0000010 4800 0000 e1ff 1600 7845 6669 0000 4d4d
0000020 2a00 0000 0800 0000 0000 0000 feff 1700

магия изображений (байты 1,0), магия заголовка сегмента App0 (байты 3,2), длина заголовка (5,4) Заголовок тип подписи ("jfif имеют\0"||"JFXX\0")(6-10 байт), версия (байт 11,12) плотность единиц (байт 13), x плотность (байт 15,14), г плотность (байт 17,16), ширина миниатюры (19 байт), высота эскиза (18 байт), и, наконец, отдохнуть до заголовка "длина" - это данные эскиза.

из приведенного выше примера видно, что длина заголовка составляет 16 байт (байт 6,5), а версия-01.01 (байт 12,13). Кроме того, поскольку ширина эскиза и высота эскиза равны 0x00, изображение не содержит ноготь большого пальца.