Full-Stack PHP/JS developer
42,6
рейтинг
29 октября 2013 в 03:34

Разработка → Получаем тип и размеры изображения без скачивания его целиком, используя Python

Возникла задача профильтровать базу данных ссылок на изображения и удалить большие. В результате было найдено решение на Python, которым я поделюсь под хабракатом:

image

Сам скрипт + необходимая для работы библиотека ReseekFile

Вся прелесть решения в том, что для получения информации о типе и размерах изображения(jpg, png, gif) достаточно скачать первых 24 байта файла.

Код анализа этих 24 байт:

    # handle GIFs
    if (size >= 10) and data[:6] in ('GIF87a', 'GIF89a'):
        # Check to see if content_type is correct
        content_type = 'image/gif'
        w, h = struct.unpack("<HH", data[6:10])
        width = int(w)
        height = int(h)

    # See PNG 2. Edition spec (http://www.w3.org/TR/PNG/)
    # Bytes 0-7 are below, 4-byte chunk length, then 'IHDR'
    # and finally the 4-byte width, height
    elif ((size >= 24) and data.startswith('\211PNG\r\n\032\n')
          and (data[12:16] == 'IHDR')):
        content_type = 'image/png'
        w, h = struct.unpack(">LL", data[16:24])
        width = int(w)
        height = int(h)

    # Maybe this is for an older PNG version.
    elif (size >= 16) and data.startswith('\211PNG\r\n\032\n'):
        # Check to see if we have the right content type
        content_type = 'image/png'
        w, h = struct.unpack(">LL", data[8:16])
        width = int(w)
        height = int(h)

    # handle JPEGs
    elif (size >= 2) and data.startswith('\377\330'):
        content_type = 'image/jpeg'
        datastream.seek(0)
        datastream.read(2)
        b = datastream.read(1)
        try:
            while (b and ord(b) != 0xDA):
                while (ord(b) != 0xFF): b = datastream.read(1)
                while (ord(b) == 0xFF): b = datastream.read(1)
                if (ord(b) >= 0xC0 and ord(b) <= 0xC3):
                    datastream.read(3)
                    h, w = struct.unpack(">HH", datastream.read(4))
                    break
                else:
                    datastream.read(int(struct.unpack(">H", datastream.read(2))[0])-2)
                b = datastream.read(1)
            width = int(w)
            height = int(h)
        except struct.error:
            pass
        except ValueError:
            pass


Надеюсь, что этот скрипт найдет себе место в избранном хабраюзеров и послужит когда это будет необходимо :)
@limonte
карма
88,7
рейтинг 42,6
Full-Stack PHP/JS developer
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (14)

  • +9
    Так приятно слышать, что в мире стало еще на одного человека больше, кто знает, что размеры изображения хранятся в начале файла. А еще битрейт mp3 файла бывает там же хранится. В общем, в начале файла много всего интересного обычно, иначе эти форматы не были бы binary safe.
    • +3
      В общем, в начале файла много всего интересного обычно, иначе эти форматы не были бы binary safe.
      Что-то не вижу логики.
  • +5
    буквально на днях делал тоже самое
    чтобы не изобретать велосепид можно использовать PIL

    def _fetch_image(cls, url, user_agent):
            """Return the image by URL downloading as little as possible."""
    
            request = urllib2.Request(url)
            request.add_header('User-Agent', user_agent)
            parser = ImageFile.Parser()
            response = None
            try:
                response = urllib2.urlopen(request)
    
                while True:
                    chunk = response.read(1024)
                    if not chunk:
                        break
    
                    parser.feed(chunk)
                    if parser.image:
                        return parser.image
            except:
                return None
            finally:
                if response:
                    response.close()
    


    подсмотрел здесь github.com/reddit/reddit/blob/master/r2/r2/lib/media.py
    • 0
      в моем случае еще требовались размеры картинки
    • +15
      $ curl -s -r 0-24 http://www.python.org/images/python-logo.gif| file -
      /dev/stdin: GIF image data, version 89a, 211 x 71
      так-то.
  • –17
    запрос HEAD видать придумали трусы…
    • +17
      по HEAD получается размер файла, но не размеры изображения
  • –4
    Сударь, для такой мелочи использовать внешние библиотеки — неуважение к тем, с кем Вы хотите её поделиться. У вас есть два варианта — убрать Reseek или сделать egg.
    • +18
      Три — положить болт на тех, кто думает что им кто-то должен.
  • 0
    хмммм, а еще есть такая хорошая командочка «file», про которую написано «The magic tests are used to check for files with data in particular fixed formats.»
    Причем знает не только 3 вида картинок, а намного больше всего про самые разые форматы файлов.
    • 0
      file не всегда может узнать размеры изображения:

      curl -s -r 0-24 http://img2-1.timeinc.net/ew/dynamic/imgs/110315/Charade_240.jpg | file -
      
      • +1
        Вот еще вариант, детектит размер и не только (не чтоб чего-то доказать, а вдруг кому пригодится).
        Правда оно потребовало чуть больший кусок от файла

        curl -s -r 0-2000 http://img2-1.timeinc.net/ew/dynamic/imgs/110315/Charade_240.jpg | identify -
        


        -=>/tmp/magick-OZeNcjJh JPEG 240x320 240x320+0+0 8-bit DirectClass 2KB 0.000u 0:00.000
        • 0
          Опять не универсальное решение:

          curl -s -r 0-2000 http://digitaldeconstruction.com/wp-content/uploads/2013/02/mma.jpg | identify -
          
  • +1
    А на JS как такое сделать, не подскажете?
    P.S. сразу предупреждаю, что JS я юзаю только в сочетании с Qt C++, и там V8, по идее должно быть доступно все то же, что и в браузере.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.