Beispiel #1
0
def SOS(fp):
    """Return a dict containing SOS header data.

    See ISO/IEC 10918-1 Section B.2.3.

    After returning, `fp` will be positioned at the end of the current marker
    segment.

    Parameters
    ----------
    fp : file-life
        A file-like positioned at the start of the length byte for the current
        marker segment.

    Returns
    -------
    dict
        A dict with keys:

        * ``Ls`` : scan header length
        * ``Ns`` : number of image components in scan
        * ``Csj`` : scan component selector
        * ``Tdj`` : DC entropy coding table destination selector.
        * ``Taj`` : AC entropy coding table destination selector. Set to 0 for
          lossless.
        * ``Ss`` : start of spectral or predictor selection. Shall be 0 for
          sequential DCT, for lossless this is the predictor.
        * ``Se`` : end of spectral selection. Shall be 63 for sequential DCT.
          In lossless this has no meaning and shall be set to 0.
        * ``Ah`` : successive approximation bit position high. In lossless
          this has no meaning and shall be set to 0.
        * ``Al`` : successive approximation bit position low or point transform
          Shall be set to 0 for sequential DCT. In lossless mode specifies
          the point transform Pt.
    """
    (length, nr_components) = unpack('>HB', fp.read(3))

    csj, tdj, taj, tmj = [], [], [], []
    for ii in range(nr_components):
        _cs = unpack('>B', fp.read(1))[0]
        csj.append(_cs)
        _td, _ta = split_byte(fp.read(1))
        tdj.append(_td)
        taj.append(_ta)


    (ss, se) = unpack('>BB', fp.read(2))
    ah, al = split_byte(fp.read(1))

    return {
        'Ls' : length,
        'Ns' : nr_components,
        'Csj' : csj,
        'Tdj' : tdj,
        'Taj' : taj,
        'Ss' : ss,
        'Se' : se,
        'Ah' : ah,
        'Al' : al,
    }
Beispiel #2
0
def EXP(fp):
    """Return a dict containing EXP segment data.

    See ISO/IEC 10918-1 Section B.3.3.

    After returning, `fp` will be positioned at the end of the current marker
    segment.

    Parameters
    ----------
    fp : file-life
        A file-like positioned at the start of the length byte for the current
        marker segment.

    Returns
    -------
    dict
        A dict with keys:

        * ``Le`` : EXP segment length
        * ``Eh`` : expand horizontally
        * ``Ev`` : expand vertically
    """
    length = unpack('>H', fp.read(2))[0]
    eh, ev = split_byte(unpack('>B', fp.read(1))[0])

    return {'Le' : length, 'Eh' : eh, 'Ev' : ev}
Beispiel #3
0
def DHT(fp):
    """Return a dict containing DHT segment data.

    See ISO/IEC 10918-1 Section B.2.4.2.

    After returning, `fp` will be positioned at the end of the current marker
    segment.

    Parameters
    ----------
    fp : file-life
        A file-like positioned at the start of the length byte for the current
        marker segment.

    Returns
    -------
    dict
        A dict with keys:

        * ``Lh`` : Huffman table definition length
        * ``Tc`` : table class (0 for DC or lossless, 1 for AC)
        * ``Th`` : Huffman table destination identifier, one of four possible
          destinations at the decoder into which the table shall be installed
        * ``Li`` : number of Huffman codes of length *i*, equivalent to the
          list *BITS*
        * ``Vij`` : value associated with each Huffman code of length *i*,
          equivalent to *HUFFVAL*
    """
    length = unpack('>H', fp.read(2))[0]
    bytes_to_read = length - 2

    tc, th, li = [], [], []
    vij = {}
    while bytes_to_read > 0:
        _tc, _th = split_byte(fp.read(1))
        tc.append(_tc)
        th.append(_th)

        bytes_to_read -= 1

        # li (BITS) is the number of codes for each code length, from 1 to 16
        _li = unpack('>16B', fp.read(16))
        bytes_to_read -= 16

        # vij is a list of the 8-bit symbols values (HUFFVAL), each of which
        #   is assigned a Huffman code.
        _vij = {}
        for ii in range(16):
            nr = _li[ii]
            if nr:
                _vij[ii + 1] = unpack('>{}B'.format(nr), fp.read(nr))
                bytes_to_read -= nr

        li.append(_li)
        vij[(_tc, _th)] = _vij

    return {'Lh' : length, 'Tc' : tc, 'Th' : th, 'Li' : li, 'Vij' : vij}
Beispiel #4
0
def DQT(fp):
    """Return a dict containing DQT segment data.

    See ISO/IEC 10918-1 Section B.2.4.1.

    After returning, `fp` will be positioned at the end of the current marker
    segment.

    Parameters
    ----------
    fp : file-life
        A file-like positioned at the start of the length byte for the current
        marker segment.

    Returns
    -------
    dict
        A dict with keys:

        * ``Lq`` : quantization table definition length
        * ``Pq`` : quantization table element precision
        * ``Tq`` : quantization table destination identifier
        * ``Qk`` : quantization table element
    """
    # length is 2 + sum(t=1, N) of (65 + 64 * Pq(t))
    length = unpack('>H', fp.read(2))[0]
    bytes_to_read = length - 2

    pq, tq, qk = [], [], []
    while bytes_to_read > 0:
        precision, table_id = split_byte(fp.read(1))
        bytes_to_read -= 1
        pq.append(precision)
        tq.append(table_id)

        if precision not in (0, 1):
            raise ValueError(
                "JPEG 10918 - DQT: invalid precision '{}'".format(precision)
            )

        # If Pq is 0, Qk is 8-bit, if Pq is 1, Qk is 16-bit
        Q_k = []
        for ii in range(64):
            if precision == 0:
                Q_k.append(unpack('>B', fp.read(1))[0])
                bytes_to_read -= 1
            elif precision == 1:
                Q_k.append(unpack('>H', fp.read(2))[0])
                bytes_to_read -= 2

        qk.append(Q_k)

    return {'Lq' : length, 'Pq' : pq, 'Tq' : tq, 'Qk' : qk}
Beispiel #5
0
 def test_splitting(self):
     """Test splitting a byte."""
     ref = {
         b'\x00': (0, 0),
         b'\x0f': (0, 15),
         b'\xf0': (15, 0),
         b'\x55': (5, 5),
         b'\xaa': (10, 10),
         b'\x18': (1, 8),
         b'\x81': (8, 1),
     }
     for byte, out in ref.items():
         assert out == split_byte(byte)
Beispiel #6
0
def DAC(fp):
    """Return a dict containing DAC segment data.

    See ISO/IEC 10918-1 Section B.2.4.3.

    After returning, `fp` will be positioned at the end of the current marker
    segment.

    Parameters
    ----------
    fp : file-life
        A file-like positioned at the start of the length byte for the current
        marker segment.

    Returns
    -------
    dict
        A dict with keys:

        * ``La`` : arithmetic coding conditioning definition length
        * ``Tc`` : table class (0 for DC or lossless, 1 for AC)
        * ``Tb`` : arithmetic coding conditioning table destination identifier
        * ``Cs`` : conditioning table value
    """
    length = unpack('>H', fp.read(2))[0]
    bytes_to_read = length - 2

    tc, tb, cs = [], [], []
    while bytes_to_read > 0:
        _tc, _tb = split_byte(fp.read(1))
        cs.append(unpack('>B', fp.read(1))[0])
        tc.append(_tc)
        tb.append(_tb)

        bytes_to_read -= 2

    return {'La' : length, 'Tc' : tc, 'Tb' : tb, 'Cs' : cs}
Beispiel #7
0
 def test_empty_byte_raises(self):
     """Test that an empty `byte` value raises an exception."""
     msg = r"ord\(\) expected a character"
     with pytest.raises(TypeError, match=msg):
         split_byte(b'')
Beispiel #8
0
def SOF(fp):
    """Return a dict containing SOF header data.

    See ISO/IEC 10918-1 Section B.2.2.

    After returning, `fp` will be positioned at the end of the current marker
    segment.

    Parameters
    ----------
    fp : file-life
        A file-like positioned at the start of the length byte for the current
        marker segment.

    Returns
    -------
    dict
        A dict with keys:

        * ``Lf`` : frame header length
        * ``P`` : sample precision
        * ``Y`` : number of lines
        * ``X`` : number of samples per line
        * ``Nf`` : number of image components in frame
        * ``Ci`` : component identifier
        * ``Hi`` : horizontal sampling factor
        * ``Vi`` : vertical sampling factor
        * ``Tqi`` : quantization table destination selector
    """
    (length,
     precision,
     nr_lines,
     samples_per_line,
     nr_components) = unpack('>HBHHB', fp.read(8))

    component_id = {}
    #horizontal_sampling_factor = []
    #vertical_sampling_factor = []
    #quantisation_selector = []
    for ii in range(nr_components):
        _ci = unpack('>B', fp.read(1))[0]
        _hor, _vert = split_byte(fp.read(1))
        #horizontal_sampling_factor.append(_hor)
        #vertical_sampling_factor.append(_vert)
        _tqi = unpack('>B', fp.read(1))[0]
        #quantisation_selector.append(_tqi)

        component_id[_ci] = {
            'Hi' : _hor,
            'Vi' : _vert,
            'Tqi' : _tqi,
        }

    return {
        'Lf' : length,
        'P' : precision,
        'Y' : nr_lines,
        'X' : samples_per_line,
        'Nf' : nr_components,
        'Ci' : component_id,
    }