7
\$\begingroup\$

I'm developing an application that will hide information inside the quantization tables of JPEG files. It's called DQTsteg, if you want to know more about what I'm attempting to do.

Right now I'm developing the most basic component of this system, which is the part that reads quantization tables from files. So it'd be a good thing if this code were as fast as it can possibly be, as it'll be a very frequent operation.

Here's the whole code on GitHub, fewer than 93 lines long.

I have noticed that sometimes urllib.requests sometimes are somewhat slow (above one second).

I've seen some people on Stack Overflow saying that some servers don't support byte by byte requests through urllib.request (you either download the whole file or you don't). I don't know whether these people are right or not, but is there any case where this urllib.request could go wrong (and then I'd either be denied access to the target file or I'd loop endlessly over the first few bytes, or anything of this sort)?

Any suggestions are welcome. Here follows the code:

#!/usr/bin/env python

"""
QT_reader.py

Functions for reading quantization tables from files and URLs.

Below are some global variables defined according to the JPEG Standard.

"""

DEFINE_QUANTIZATION_TABLE = b'\xff\xdb'
SINGLE_TABLE_PAYLOAD_DATA = b'\x00\x43'
DOUBLE_TABLE_PAYLOAD_DATA = b'\x00\x84'

def load_file(jpgpath):
    """
    Loads a file from jpgpath. Direct acess not intended; use QT_get_single() or QT_get_all().
    Args:
        jpgpath (str): file path.
    Returns:
        jpgpath (object): file object.
    """
    if load_file.status == "web":
        import urllib.request
        jpgpath = urllib.request.urlopen(jpgpath)
    if load_file.status == "file":
        jpgpath = open(jpgpath,"rb")
    return jpgpath


def QT_get_single(jpgpath, status="file"):
    """
    Reads a single quantization table from file.
    Args:
        jpgpath (str): file path, local or URL.
        status (str): set to "file" for local files or set to "web" for URLs.
    Returns:
        tuple: A tuple with .tell() position for the DQT on [0] and the QT table itself on [1] as a bytearray.
    """
    if QT_get_single.status != "off":
        if status == "web":
            load_file.status = "web"
        elif status == "file":
            load_file.status = "file"
        load_file.jpgpath = jpgpath
        jpgpath = load_file(load_file.jpgpath)
    while True:
        while True:
            QT_buffer = jpgpath.read(2)
            if QT_buffer == DEFINE_QUANTIZATION_TABLE: break
            if QT_buffer == b'':
                raise EOFError("Reached end of file.")
        DQT_position = jpgpath.tell()
        QT_buffer = jpgpath.read(2)
        if QT_buffer == DOUBLE_TABLE_PAYLOAD_DATA:
            QT_buffer = jpgpath.read(130)
            break
        elif QT_buffer == SINGLE_TABLE_PAYLOAD_DATA:
            QT_buffer = jpgpath.read(65)
            break
        else:
            continue
    return ( DQT_position, bytearray(QT_buffer) )

def QT_get_all(jpgpath, status="file",stop=-1):
    """
    Reads multiple quantization tables from file. Reads all QTs by default.
    Args:
        jpgpath (str): file path, local or URL.
        status (str): set to "file" for local files or set to "web" for URLs.
        stop (int): Stop after retrieving the nth table. One retrieves one; two retrieves two; zero or default retrieves all.
    Returns:
        list: A list of QT_get_single() tuples.
    """
    QT_tables = []
    counter = 0

    load_file.status = status
    load_file.jpgpath = jpgpath
    jpgpath = load_file(load_file.jpgpath)

    QT_get_single.jpgpath = jpgpath
    QT_get_single.status = "off"

    while True:
        try:
            QT_buffer = QT_get_single(QT_get_single.jpgpath,QT_get_single.status)
            QT_tables.append(QT_buffer)
            counter += 1
        except: break
        if counter == stop: break
    return QT_tables
\$\endgroup\$
  • \$\begingroup\$ It might be worth while doing from urrlib import request rather than import urrlib.request because it will probably make the other bit look nicer as you just have to call request... rather than urrlib.request... \$\endgroup\$ – 13ros27 Dec 21 '17 at 16:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.