212 lines
7.5 KiB
ReStructuredText
212 lines
7.5 KiB
ReStructuredText
------------
|
|
How do I...?
|
|
------------
|
|
|
|
|
|
Read the lowest resolution thumbnail?
|
|
=====================================
|
|
Printing the Jp2k object should reveal the number of resolutions (look in the
|
|
COD segment section), but you can take a shortcut by supplying -1 as the
|
|
resolution level. ::
|
|
|
|
>>> import glymur
|
|
>>> file = glymur.data.nemo()
|
|
>>> j = glymur.Jp2k(file)
|
|
>>> thumbnail = j.read(rlevel=-1)
|
|
|
|
Display metadata?
|
|
=================
|
|
There are two ways. From the unix command line, the script *jp2dump* is
|
|
available. ::
|
|
|
|
$ jp2dump /path/to/glymur/installation/data/nemo.jp2
|
|
|
|
From within Python, it is as simple as printing the Jp2k object, i.e. ::
|
|
|
|
>>> from glymur import Jp2k
|
|
>>> file = glymur.data.nemo()
|
|
>>> j = Jp2k(file)
|
|
>>> print(j)
|
|
|
|
This prints the metadata found in the JP2 boxes, but in the case of the
|
|
codestream box, only the main header is printed. It is possible to print
|
|
**only** the codestream information as well, i.e. ::
|
|
|
|
>>> print(j.get_codestream())
|
|
|
|
Add XML Metadata?
|
|
=================
|
|
An existing raw codestream (or JP2 file) can be wrapped (re-wrapped) in a
|
|
user-defined set of JP2 boxes. To get just a minimal JP2 jacket on the
|
|
codestream provided by `goodstuff.j2k` (a file consisting of a raw codestream),
|
|
you can use the **wrap** method with no box argument: ::
|
|
|
|
>>> import glymur
|
|
>>> jfile = glymur.data.goodstuff()
|
|
>>> j2k = glymur.Jp2k(jfile)
|
|
>>> jp2 = j2k.wrap("newfile.jp2")
|
|
>>> print(jp2)
|
|
File: newfile.jp2
|
|
JPEG 2000 Signature Box (jP ) @ (0, 12)
|
|
Signature: 0d0a870a
|
|
File Type Box (ftyp) @ (12, 20)
|
|
Brand: jp2
|
|
Compatibility: ['jp2 ']
|
|
JP2 Header Box (jp2h) @ (32, 45)
|
|
Image Header Box (ihdr) @ (40, 22)
|
|
Size: [800 480 3]
|
|
Bitdepth: 8
|
|
Signed: False
|
|
Compression: wavelet
|
|
Colorspace Unknown: False
|
|
Colour Specification Box (colr) @ (62, 15)
|
|
Method: enumerated colorspace
|
|
Precedence: 0
|
|
Colorspace: sRGB
|
|
Contiguous Codestream Box (jp2c) @ (77, 115228)
|
|
Main header:
|
|
.
|
|
. (truncated)
|
|
.
|
|
|
|
The raw codestream was wrapped in a JP2 jacket with four boxes in the outer
|
|
layer (the signature, file type, JP2 header, and contiguous codestream), with
|
|
two additional boxes (image header and color specification) contained in the
|
|
JP2 header superbox.
|
|
|
|
XML boxes are not in the minimal set of box requirements for the JP2 format, so
|
|
in order to add an XML box into the mix, we'll need to specify all of the
|
|
boxes. If you already have a JP2 jacket in place, you can just reuse it,
|
|
though. Take the following example content in an XML file `favorites.xml` : ::
|
|
|
|
<?xml version="1.0"?>
|
|
<favorite_things>
|
|
<category>Light Ale</category>
|
|
</favorite_things>
|
|
|
|
and add it after the JP2 header box, but before the codestream box ::
|
|
|
|
>>> boxes = jp2.box # The box attribute is the list of JP2 boxes
|
|
>>> xmlbox = glymur.jp2box.XMLBox(file='favorites.xml')
|
|
>>> boxes.insert(3, xmlbox)
|
|
>>> jp2_xml = jp2.wrap("newfile_with_xml.jp2", boxes=boxes)
|
|
>>> print(jp2_xml)
|
|
File: newfile_with_xml.jp2
|
|
JPEG 2000 Signature Box (jP ) @ (0, 12)
|
|
Signature: 0d0a870a
|
|
File Type Box (ftyp) @ (12, 20)
|
|
Brand: jp2
|
|
Compatibility: ['jp2 ']
|
|
JP2 Header Box (jp2h) @ (32, 45)
|
|
Image Header Box (ihdr) @ (40, 22)
|
|
Size: [800 480 3]
|
|
Bitdepth: 8
|
|
Signed: False
|
|
Compression: wavelet
|
|
Colorspace Unknown: False
|
|
Colour Specification Box (colr) @ (62, 15)
|
|
Method: enumerated colorspace
|
|
Precedence: 0
|
|
Colorspace: sRGB
|
|
XML Box (xml ) @ (77, 76)
|
|
<favorite_things>
|
|
<category>Light Ale</category>
|
|
</favorite_things>
|
|
|
|
Contiguous Codestream Box (jp2c) @ (153, 115236)
|
|
Main header:
|
|
.
|
|
. (truncated)
|
|
.
|
|
|
|
Create an image with an alpha layer?
|
|
====================================
|
|
|
|
OpenJPEG can create JP2 files with more than 3 components, but by default, any
|
|
extra components are not described as such. In order to do so,
|
|
we need to rewrap such an image in a set of boxes that includes a channel
|
|
definition box.
|
|
|
|
This example is based on SciPy example code found at
|
|
http://scipy-lectures.github.io/advanced/image_processing/#basic-manipulations .
|
|
Instead of a circular mask, however, we'll make it an ellipse since the source
|
|
image isn't square.
|
|
|
|
>>> import numpy as np
|
|
>>> import glymur
|
|
>>> from glymur import Jp2k
|
|
>>> rgb = Jp2k(glymur.data.goodstuff()).read()
|
|
>>> lx, ly = rgb.shape[0:2]
|
|
>>> X, Y = np.ogrid[0:lx, 0:ly]
|
|
>>> mask = ly**2*(X - lx / 2) ** 2 + lx**2*(Y - ly / 2) ** 2 > (lx * ly / 2)**2
|
|
>>> alpha = 255 * np.ones((lx, ly, 1), dtype=np.uint8)
|
|
>>> alpha[mask] = 0
|
|
>>> rgba = np.concatenate((rgb, alpha), axis=2)
|
|
>>> jp2 = Jp2k('tmp.jp2', 'wb')
|
|
>>> jp2.write(rgba)
|
|
|
|
Next we need to specify what types of channels we have.
|
|
The first three channels are color channels, but we identify the fourth as
|
|
an alpha channel::
|
|
|
|
>>> from glymur.core import COLOR, OPACITY
|
|
>>> ctype = [COLOR, COLOR, COLOR, OPACITY]
|
|
|
|
And finally we have to specify just exactly how each channel is to be
|
|
interpreted. The color channels are straightforward, they correspond to R-G-B,
|
|
but the alpha (or opacity) channel in this case is to be applied against the
|
|
entire image (it is possible to apply an alpha channel to a single color
|
|
channel, but we aren't doing that). ::
|
|
|
|
>>> from glymur.core import RED, GREEN, BLUE, WHOLE_IMAGE
|
|
>>> asoc = [RED, GREEN, BLUE, WHOLE_IMAGE]
|
|
>>> cdef = glymur.jp2box.ChannelDefinitionBox(channel_type=ctype, association=asoc)
|
|
>>> print(cdef)
|
|
Channel Definition Box (cdef) @ (0, 0)
|
|
Channel 0 (color) ==> (1)
|
|
Channel 1 (color) ==> (2)
|
|
Channel 2 (color) ==> (3)
|
|
Channel 3 (opacity) ==> (whole image)
|
|
|
|
It's easiest to take the existing jp2 jacket and just add the channel
|
|
definition box in the appropriate spot. The channel definition box **must**
|
|
go into the jp2 header box, and then we can rewrap the image. ::
|
|
|
|
>>> boxes = jp2.box # The box attribute is the list of JP2 boxes
|
|
>>> boxes[2].box.append(cdef)
|
|
>>> jp2_rgba = jp2.wrap("goodstuff_rgba.jp2", boxes=boxes)
|
|
|
|
Here's how the Preview application on the mac shows the RGBA image.
|
|
|
|
.. image:: goodstuff_alpha.png
|
|
|
|
|
|
Work with XMP UUIDs?
|
|
====================
|
|
The example JP2 file shipped with glymur has an XMP UUID. ::
|
|
|
|
>>> import glymur
|
|
>>> j = glymur.Jp2k(glymur.data.nemo())
|
|
>>> print(j.box[4])
|
|
UUID Box (uuid) @ (715, 2412)
|
|
UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)
|
|
UUID Data:
|
|
<ns0:xmpmeta xmlns:ns0="adobe:ns:meta/" xmlns:ns2="http://ns.adobe.com/xap/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" ns0:xmptk="XMP Core 4.4.0-Exiv2">
|
|
<rdf:RDF>
|
|
<rdf:Description ns2:CreatorTool="glymur" rdf:about="" />
|
|
</rdf:RDF>
|
|
</ns0:xmpmeta>
|
|
|
|
Since the UUID data in this case is returned as an ElementTree instance, one can
|
|
use ElementTree to access the data. For example, to extract the
|
|
**CreatorTool** attribute value, the following would work::
|
|
|
|
>>> xmp = j.box[4].data
|
|
>>> ns0 = '{http://www.w3.org/1999/02/22-rdf-syntax-ns#}'
|
|
>>> ns1 = '{http://ns.adobe.com/xap/1.0/}'
|
|
>>> name = '{0}RDF/{0}Description'.format(ns0)
|
|
>>> elt = xmp.find(name)
|
|
>>> elt
|
|
<Element '{http://www.w3.org/1999/02/22-rdf-syntax-ns#}Description' at 0xb4baa93c>
|
|
>>> elt.attrib['{0}CreatorTool'.format(ns1)]
|
|
'glymur'
|