Initial commit.
This commit is contained in:
parent
9fbcec53e1
commit
e056e898cc
32 changed files with 16874 additions and 0 deletions
0
CHANGES.txt
Normal file
0
CHANGES.txt
Normal file
14
MANIFEST
Normal file
14
MANIFEST
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# file GENERATED by distutils, do NOT edit
|
||||
README
|
||||
setup.py
|
||||
jptools/__init__.py
|
||||
jptools/codestream.py
|
||||
jptools/core.py
|
||||
jptools/jp2box.py
|
||||
jptools/jp2k.py
|
||||
jptools/data/emneck.jp2
|
||||
jptools/test/__init__.py
|
||||
jptools/test/test_codestream.py
|
||||
jptools/test/test_conformance.py
|
||||
jptools/test/test_jp2k.py
|
||||
jptools/test/test_printing.py
|
||||
9
README
Normal file
9
README
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
JPTOOLS
|
||||
=======
|
||||
|
||||
JPTOOLS provides a suite of JPEG2000 tools, including a bridge from Python to
|
||||
the OpenJPEG library.
|
||||
|
||||
Please read the docs.
|
||||
=====================
|
||||
Yes. Please.
|
||||
4
TODO
Normal file
4
TODO
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
Test -c options with multiple values.
|
||||
Test -b options with h*w>4096
|
||||
Test -b options with h or w > 1024
|
||||
Test -b options with h or w < 4
|
||||
15
bin/jp2dump
Executable file
15
bin/jp2dump
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import glymur
|
||||
|
||||
description='Print JPEG2000 metadata.'
|
||||
parser = argparse.ArgumentParser(description=description)
|
||||
parser.add_argument('-c', '--codestream', help='dump codestream',
|
||||
action='store_true')
|
||||
parser.add_argument('filename')
|
||||
args = parser.parse_args()
|
||||
|
||||
filename = args.filename
|
||||
glymur.jp2dump(args.filename, codestream=args.codestream)
|
||||
153
docs/Makefile
Normal file
153
docs/Makefile
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/glymur.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/glymur.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/glymur"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/glymur"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
190
docs/make.bat
Normal file
190
docs/make.bat
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
@ECHO OFF
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set BUILDDIR=build
|
||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source
|
||||
set I18NSPHINXOPTS=%SPHINXOPTS% source
|
||||
if NOT "%PAPER%" == "" (
|
||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
||||
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
if "%1" == "help" (
|
||||
:help
|
||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
||||
echo. html to make standalone HTML files
|
||||
echo. dirhtml to make HTML files named index.html in directories
|
||||
echo. singlehtml to make a single large HTML file
|
||||
echo. pickle to make pickle files
|
||||
echo. json to make JSON files
|
||||
echo. htmlhelp to make HTML files and a HTML help project
|
||||
echo. qthelp to make HTML files and a qthelp project
|
||||
echo. devhelp to make HTML files and a Devhelp project
|
||||
echo. epub to make an epub
|
||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
||||
echo. text to make text files
|
||||
echo. man to make manual pages
|
||||
echo. texinfo to make Texinfo files
|
||||
echo. gettext to make PO message catalogs
|
||||
echo. changes to make an overview over all changed/added/deprecated items
|
||||
echo. linkcheck to check all external links for integrity
|
||||
echo. doctest to run all doctests embedded in the documentation if enabled
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "clean" (
|
||||
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
|
||||
del /q /s %BUILDDIR%\*
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "html" (
|
||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "dirhtml" (
|
||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "singlehtml" (
|
||||
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pickle" (
|
||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the pickle files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "json" (
|
||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the JSON files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "htmlhelp" (
|
||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
||||
.hhp project file in %BUILDDIR%/htmlhelp.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "qthelp" (
|
||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
||||
.qhcp project file in %BUILDDIR%/qthelp, like this:
|
||||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\glymur.qhcp
|
||||
echo.To view the help file:
|
||||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\glymur.ghc
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "devhelp" (
|
||||
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "epub" (
|
||||
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The epub file is in %BUILDDIR%/epub.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latex" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "text" (
|
||||
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The text files are in %BUILDDIR%/text.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "man" (
|
||||
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The manual pages are in %BUILDDIR%/man.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "texinfo" (
|
||||
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "gettext" (
|
||||
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "changes" (
|
||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.The overview file is in %BUILDDIR%/changes.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "linkcheck" (
|
||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Link check complete; look for any errors in the above output ^
|
||||
or in %BUILDDIR%/linkcheck/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "doctest" (
|
||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Testing of doctests in the sources finished, look at the ^
|
||||
results in %BUILDDIR%/doctest/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
:end
|
||||
5
docs/source/api.rst
Normal file
5
docs/source/api.rst
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
API
|
||||
===
|
||||
|
||||
.. autoclass:: glymur.Jp2k
|
||||
:members:
|
||||
248
docs/source/conf.py
Normal file
248
docs/source/conf.py
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# glymur documentation build configuration file, created by
|
||||
# sphinx-quickstart on Sun May 26 11:29:54 2013.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest',
|
||||
'sphinx.ext.autosummary',
|
||||
'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'numpydoc']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'glymur'
|
||||
copyright = u'2013, John Evans'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.1'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.1.0.a1'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = []
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'default'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'glymurdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'glymur.tex', u'glymur Documentation',
|
||||
u'John Evans', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output --------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'glymur', u'glymur Documentation',
|
||||
[u'John Evans'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output ------------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'glymur', u'glymur Documentation',
|
||||
u'John Evans', 'glymur', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#texinfo_show_urls = 'footnote'
|
||||
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {'http://docs.python.org/': None}
|
||||
24
docs/source/how_do_i.rst
Normal file
24
docs/source/how_do_i.rst
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
************
|
||||
How do I...?
|
||||
************
|
||||
|
||||
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.::
|
||||
|
||||
>>> import pkg_resources
|
||||
>>> from glymur import Jp2k
|
||||
>>> file = pkg_resources.resource_filename(glymur.__name__, "data/nemo.jp2")
|
||||
>>> j = Jp2k(file)
|
||||
>>> print(j)
|
||||
|
||||
The primary emphasis is on JP2 metadata, but it is possible to
|
||||
display raw codestream as well::
|
||||
|
||||
>>> print(j.get_codestream())
|
||||
|
||||
25
docs/source/index.rst
Normal file
25
docs/source/index.rst
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
.. glymur documentation master file, created by
|
||||
sphinx-quickstart on Thu Jan 24 16:44:44 2013.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to glymur's documentation!
|
||||
===================================
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
introduction
|
||||
api
|
||||
how_do_i
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
||||
187
docs/source/introduction.rst
Normal file
187
docs/source/introduction.rst
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
=========================================
|
||||
glymur: a Python interface for JPEG 2000
|
||||
=========================================
|
||||
|
||||
**glymur** contains a Python interface to the OpenJPEG library
|
||||
which allows linux and mac users to read and write JPEG 2000 files. For more
|
||||
information about OpenJPEG, please consult http://www.openjpeg.org. **glymur**
|
||||
should be considered to be alpha-quality software.
|
||||
|
||||
**glymur** tries to support reading (including all metadata) and writing of
|
||||
JP2 and J2C files. Writing J2C/JP2 files is currently limited to images that
|
||||
can fit in memory, however.
|
||||
|
||||
There is some very limited support for reading JPX
|
||||
metadata. For instance, **asoc** and **labl** boxes are recognized, so GMLJP2
|
||||
metadata can be retrieved from such JPX files.
|
||||
|
||||
------------
|
||||
Requirements
|
||||
------------
|
||||
**glymur** works on Python 2.7 and 3.3. Python 3.3 is strongly recommended.
|
||||
|
||||
OpenJPEG
|
||||
========
|
||||
OpenJPEG must be built as a shared library. In addition, you
|
||||
currently must compile OpenJPEG from the developmental source that
|
||||
you can retrieve via subversion. As of this time of writing, svn
|
||||
revision 2345 works. In addition, you should also retrieve their test data, as
|
||||
you will need it when running **glymur**'s test suite.
|
||||
|
||||
Earlier versions of OpenJPEG through the 2.0 official release are not supported.
|
||||
|
||||
Be sure to have the following ports/RPMs/debs installed.
|
||||
|
||||
* gcc
|
||||
* gcc-c++
|
||||
* cmake
|
||||
|
||||
You should build OpenJPEG with testing turned on. Consult the OpenJPEG
|
||||
documentation on how to do this. If you use linux, make sure that you
|
||||
have the following development packages installed
|
||||
|
||||
* zlib-devel
|
||||
* png-devel
|
||||
* libtiff-devel
|
||||
* lcms2-devel
|
||||
|
||||
OS
|
||||
==
|
||||
|
||||
Mac OS X
|
||||
--------
|
||||
All the necessary packages are available to use **glymur** with Python 3.3 via
|
||||
MacPorts. A minimal set of ports includes
|
||||
|
||||
* python33
|
||||
* py33-numpy
|
||||
* py33-distribute
|
||||
|
||||
To run all the testing, one of the following combinations of ports must
|
||||
additionally be installed:
|
||||
|
||||
* py33-scikit-image and either py33-Pillow or freeimage
|
||||
* py33-matplotlib and py33-Pillow
|
||||
|
||||
Linux
|
||||
-----
|
||||
|
||||
Fedora 18
|
||||
'''''''''
|
||||
Fedora 18 ships with Python 3.3, so all the necessary RPMs are available to
|
||||
meet the minimal set of requirements.
|
||||
|
||||
* python3
|
||||
* python3-numpy
|
||||
* python3-setuptools
|
||||
* python3-matplotlib (for running tests)
|
||||
* python3-matplotlib-tk (or whichever matplotlib backend you prefer)
|
||||
|
||||
A few tests still will not run, however, unless one of the following
|
||||
combinations of RPMs / Python packages is installed.
|
||||
|
||||
* scikit-image and either Pillow or freeimage
|
||||
* matplotlib and Pillow
|
||||
|
||||
The 2nd route is probably the easiest, so go ahead and install **Pillow**
|
||||
via **pip** since **Pillow** is not yet available in Fedora 18 default
|
||||
repositories::
|
||||
|
||||
$ yum install python3-devel # pip needs this in order to compile Pillow
|
||||
$ yum install python3-pip # First one must install pip of course!
|
||||
$ pip-python3 install Pillow --user
|
||||
$ export PYTHONPATH=$HOME/.local/lib/python3.3/site-packages:$PYTHONPATH
|
||||
|
||||
Fedora 17
|
||||
'''''''''
|
||||
Fedora 17 ships with Python 3.2 and 2.7, so these steps detail working with
|
||||
2.7.
|
||||
|
||||
Required RPMs include::
|
||||
|
||||
* python
|
||||
* python-mock
|
||||
* python-pip
|
||||
* python-setuptools
|
||||
* numpy
|
||||
|
||||
In addition, you must install **contextlib2** via pip.
|
||||
|
||||
A few tests still will not run, however, unless one of the following
|
||||
combinations of RPMs / Python packages is installed.
|
||||
|
||||
* scikit-image and either Pillow or freeimage
|
||||
* matplotlib and Pillow
|
||||
|
||||
**scikit-image** is not available in the Fedora 17 default repositories, but
|
||||
it may be installed via **pip**::
|
||||
|
||||
$ yum install Cython # pip needs this in order to compile scikit-image
|
||||
$ yum install python-devel # pip needs this in order to compile scikit-image
|
||||
$ yum install freeimage # scikit-image uses this as a backend
|
||||
$ yum install scipy # needed by scikit-image
|
||||
$ pip-python install scikit-image --user
|
||||
$ pip-python install contextlib2 --user
|
||||
$ export PYTHONPATH=$HOME/.local/lib/python2.7/site-packages:$PYTHONPATH
|
||||
|
||||
Windows
|
||||
-------
|
||||
Not currently supported.
|
||||
|
||||
------------------------------------
|
||||
Installation, Testing, Configuration
|
||||
------------------------------------
|
||||
|
||||
From this point forward, python3 will be referred to as just "python".
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
In addition to merely installing **glymur**, you should adjust your **$PATH**
|
||||
environment variable in order to be able to use the **jp2dump** script from
|
||||
the unix command line.
|
||||
|
||||
::
|
||||
|
||||
$ python setup.py install --prefix=/install/path
|
||||
$ export PYTHONPATH=/install/path/lib/python3.3/site-packages
|
||||
$ export PATH=/install/path/bin:$PATH
|
||||
|
||||
|
||||
Configuration
|
||||
=============
|
||||
glymur uses **ctypes** (for the moment) to access the openjp2 library, and
|
||||
because **ctypes** access libraries in a platform-dependent manner, it is
|
||||
recommended that you create a configuration file to help glymur properly find
|
||||
the openjp2 library. You may create the configuration file as follows::
|
||||
|
||||
$ mkdir ~/.glymur
|
||||
$ cd ~/.glymur
|
||||
$ cat > glymurrc << EOF
|
||||
> [library]
|
||||
> openjp2: /opt/openjp2-svn/lib/libopenjp2.so
|
||||
> EOF
|
||||
|
||||
That assumes, of course, that you've installed OpenJPEG into /opt/openjp2-svn.
|
||||
|
||||
|
||||
Testing
|
||||
=======
|
||||
In order to run all of the test suite, you will first need the OpenJPEG test
|
||||
data that you previously retrieved.
|
||||
Then you should set the **OPJ_DATA_ROOT** environment variable to
|
||||
point to this directory, e.g.::
|
||||
|
||||
$ cd /somewhere/outside/the/glymur/unpacking/directory
|
||||
$ svn co http://openjpeg.googlecode.com/svn/data
|
||||
$ export OPJ_DATA_ROOT=`pwd`/data
|
||||
|
||||
The test suite may then be run with::
|
||||
|
||||
$ cd /back/to/glymur/unpacking/directory
|
||||
$ python -m unittest discover
|
||||
|
||||
At the moment, the development version of the library prints quite a few
|
||||
warnings to stderr, which you may ignore. There are also more skipped tests
|
||||
on Python 2.7 than on Python 3.3. The important thing will be whether
|
||||
or not any test errors are reported at the end.
|
||||
99
glymur/__init__.py
Normal file
99
glymur/__init__.py
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
"""glymur - read, write, and interrogate JPEG 2000 files
|
||||
"""
|
||||
|
||||
|
||||
def _glymurrc_fname():
|
||||
"""Return the path to the configuration file.
|
||||
|
||||
Search order:
|
||||
1) current working directory
|
||||
2) environ var GLYMURCONFIGDIR
|
||||
3) HOME/.glymur/glymurrc
|
||||
"""
|
||||
|
||||
# Current directory.
|
||||
fname = os.path.join(os.getcwd(), 'glymurrc')
|
||||
if os.path.exists(fname):
|
||||
return fname
|
||||
|
||||
# environ var GLYMURCONFIGDIR
|
||||
if 'GLYMURCONFIGDIR' in os.environ:
|
||||
path = os.environ['GLYMURCONFIGDIR']
|
||||
if os.path.exists(path):
|
||||
fname = os.path.join(path, 'glymurrc')
|
||||
if os.path.exists(fname):
|
||||
return fname
|
||||
|
||||
# HOME/.glymur/jptoolrsc
|
||||
confdir = _get_configdir()
|
||||
if confdir is not None:
|
||||
fname = os.path.join(_get_configdir(), 'glymurrc')
|
||||
if os.path.exists(fname):
|
||||
return fname
|
||||
|
||||
# didn't find a configuration file.
|
||||
return None
|
||||
|
||||
|
||||
def _config():
|
||||
"""Read configuration file.
|
||||
|
||||
Based on matplotlib.
|
||||
"""
|
||||
filename = _glymurrc_fname()
|
||||
if filename is not None:
|
||||
# Read the configuration file for the library location.
|
||||
parser = ConfigParser()
|
||||
parser.read(filename)
|
||||
try:
|
||||
libopenjp2_path = parser.get('library', 'openjp2')
|
||||
if not os.path.exists(libopenjp2_path):
|
||||
msg = 'OpenJP2 library path specified by configuration file '
|
||||
msg += 'does not exist.'
|
||||
raise ImportError(msg)
|
||||
except NoOptionError as e:
|
||||
msg = "Error parsing configuration file '{0}': {1}"
|
||||
msg = msg.format(fname, e.message)
|
||||
warnings.warn(msg, UserWarning)
|
||||
except:
|
||||
raise
|
||||
else:
|
||||
# No help from the config file, try to find it ourselves.
|
||||
from ctypes.util import find_library
|
||||
libopenjp2_path = find_library('openjp2')
|
||||
if libopenjp2_path is None:
|
||||
raise ImportError('OpenJP2 library not found.')
|
||||
|
||||
_OPENJP2 = ctypes.CDLL(libopenjp2_path)
|
||||
return _OPENJP2
|
||||
|
||||
|
||||
def _get_configdir():
|
||||
"""Return string representing the configuration directory.
|
||||
|
||||
Default is HOME/.glymur. You can override this with the GLYMURCONFIGDIR
|
||||
environment variable.
|
||||
"""
|
||||
|
||||
if 'GLYMURCONFIGDIR' in os.environ:
|
||||
return os.environ['GLYMURCONFIGDIR']
|
||||
|
||||
if 'HOME' in os.environ:
|
||||
return os.path.join(os.environ['HOME'], '.glymur')
|
||||
|
||||
import sys
|
||||
if sys.hexversion <= 0x03000000:
|
||||
from ConfigParser import SafeConfigParser as ConfigParser
|
||||
from ConfigParser import NoOptionError
|
||||
else:
|
||||
from configparser import ConfigParser
|
||||
from configparser import NoOptionError
|
||||
import ctypes
|
||||
import os
|
||||
|
||||
_OPENJP2 = _config()
|
||||
|
||||
from .jp2k import Jp2k
|
||||
from .jp2dump import jp2dump
|
||||
|
||||
from . import test
|
||||
1714
glymur/codestream.py
Normal file
1714
glymur/codestream.py
Normal file
File diff suppressed because it is too large
Load diff
123
glymur/core.py
Normal file
123
glymur/core.py
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
# Progression order
|
||||
LRCP = 0
|
||||
RLCP = 1
|
||||
RPCL = 2
|
||||
PCRL = 3
|
||||
CPRL = 4
|
||||
|
||||
_progression_order_display = {
|
||||
LRCP: 'LRCP',
|
||||
RLCP: 'RLCP',
|
||||
RPCL: 'RPCL',
|
||||
PCRL: 'PCRL',
|
||||
CPRL: 'CPRL'}
|
||||
|
||||
progression_order = {
|
||||
'LRCP': LRCP,
|
||||
'RLCP': RLCP,
|
||||
'RPCL': RPCL,
|
||||
'PCRL': PCRL,
|
||||
'CPRL': CPRL}
|
||||
|
||||
WAVELET_TRANSFORM_9x7_IRREVERSIBLE = 0
|
||||
WAVELET_TRANSFORM_5x3_REVERSIBLE = 1
|
||||
|
||||
_wavelet_transform_display = {
|
||||
WAVELET_TRANSFORM_9x7_IRREVERSIBLE: '9-7 irreversible',
|
||||
WAVELET_TRANSFORM_5x3_REVERSIBLE: '5-3 reversible'}
|
||||
|
||||
ENUMERATED_COLORSPACE = 1
|
||||
RESTRICTED_ICC_PROFILE = 2
|
||||
ANY_ICC_PROFILE = 3
|
||||
VENDOR_COLOR_METHOD = 4
|
||||
|
||||
_method_display = {
|
||||
ENUMERATED_COLORSPACE: 'enumerated colorspace',
|
||||
RESTRICTED_ICC_PROFILE: 'restricted ICC profile',
|
||||
ANY_ICC_PROFILE: 'any ICC profile',
|
||||
VENDOR_COLOR_METHOD: 'vendor color method'}
|
||||
|
||||
_ = {1: 'accurately represents correct colorspace definition',
|
||||
2: 'approximates correct colorspace definition, exceptional quality',
|
||||
3: 'approximates correct colorspace definition, reasonable quality',
|
||||
4: 'approximates correct colorspace definition, poor quality'}
|
||||
_approximation_display = _
|
||||
|
||||
# Registration values for comment markers.
|
||||
RCME_BINARY = 0 # binary value comments
|
||||
RCME_ISO_8859_1 = 1 # comments in latin-1 codec
|
||||
|
||||
# enumerated colorspaces
|
||||
CMYK = 12
|
||||
SRGB = 16
|
||||
GREYSCALE = 17
|
||||
YCC = 18
|
||||
E_SRGB = 20
|
||||
ROMM_RGB = 21
|
||||
|
||||
_colorspace_map_display = {
|
||||
CMYK: 'CMYK',
|
||||
SRGB: 'sRGB',
|
||||
GREYSCALE: 'greyscale',
|
||||
YCC: 'YCC',
|
||||
E_SRGB: 'e-sRGB',
|
||||
ROMM_RGB: 'ROMM-RGB'}
|
||||
|
||||
# enumerated color channel types
|
||||
_COLOR = 0
|
||||
_OPACITY = 1
|
||||
_PRE_MULTIPLIED_OPACITY = 2
|
||||
_UNSPECIFIED = 65535
|
||||
_color_type_map_display = {
|
||||
_COLOR: 'color',
|
||||
_OPACITY: 'opacity',
|
||||
_PRE_MULTIPLIED_OPACITY: 'pre-multiplied opacity',
|
||||
_UNSPECIFIED: 'unspecified'}
|
||||
|
||||
# enumerated color channel associations
|
||||
_rgb_colorspace = {"R": 1, "G": 2, "B": 3}
|
||||
_greyscale_colorspace = {"Y": 1}
|
||||
_ycbcr_colorspace = {"Y": 1, "Cb": 2, "Cr": 3}
|
||||
_colorspace = {SRGB: _rgb_colorspace,
|
||||
GREYSCALE: _greyscale_colorspace,
|
||||
YCC: _ycbcr_colorspace,
|
||||
E_SRGB: _rgb_colorspace,
|
||||
ROMM_RGB: _rgb_colorspace}
|
||||
|
||||
# How to display the codestream profile.
|
||||
_capabilities_display = {
|
||||
0: '2',
|
||||
1: '0',
|
||||
2: '1',
|
||||
3: '3'}
|
||||
|
||||
# Reader requirements
|
||||
RREQ_UNRESTRICTED_JPEG2000_PART_1 = 5
|
||||
RREQ_UNRESTRICTED_JPEG2000_PART_2 = 6
|
||||
RREQ_CMYK_ENUMERATED_COLORSPACE = 55
|
||||
RREQ_CMYK_ENUMERATED_COLORSPACE = 55
|
||||
RREQ_E_SRGB_ENUMERATED_COLORSPACE = 60
|
||||
RREQ_ROMM_RGB_ENUMERATED_COLORSPACE = 61
|
||||
_reader_requirements_display = {
|
||||
0: 'File not completely understood',
|
||||
1: 'Deprecated',
|
||||
2: 'Contains multiple composition layers',
|
||||
3: 'Deprecated',
|
||||
4: 'JPEG 2000 Part 1 Profile 1 codestream',
|
||||
RREQ_UNRESTRICTED_JPEG2000_PART_1:
|
||||
'Unrestricted JPEG 2000 Part 1 codestream, ITU-T Rec. T.800 '
|
||||
+ '| ISO/IEC 15444-1',
|
||||
RREQ_UNRESTRICTED_JPEG2000_PART_2:
|
||||
'Unrestricted JPEG 2000 Part 2 codestream',
|
||||
7: 'JPEG codestream as defined in ISO/IEC 10918-1',
|
||||
8: 'Deprecated',
|
||||
9: 'Non-premultiplied opacity channel',
|
||||
10: 'Premultiplied opacity channel',
|
||||
12: 'Deprecated',
|
||||
18: 'Deprecated',
|
||||
43: '(Deprecated) compositing layer uses restricted ICC profile',
|
||||
44: 'Compositing layer uses Any ICC profile',
|
||||
45: 'Deprecated',
|
||||
RREQ_CMYK_ENUMERATED_COLORSPACE: 'CMYK enumerated colorspace',
|
||||
RREQ_E_SRGB_ENUMERATED_COLORSPACE: 'e-sRGB enumerated colorspace',
|
||||
RREQ_ROMM_RGB_ENUMERATED_COLORSPACE: 'ROMM_RGB enumerated colorspace'}
|
||||
BIN
glymur/data/nemo.jp2
Normal file
BIN
glymur/data/nemo.jp2
Normal file
Binary file not shown.
1763
glymur/jp2box.py
Normal file
1763
glymur/jp2box.py
Normal file
File diff suppressed because it is too large
Load diff
22
glymur/jp2dump.py
Normal file
22
glymur/jp2dump.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
"""
|
||||
Entry point for jp2dump script.
|
||||
"""
|
||||
|
||||
from .jp2k import Jp2k
|
||||
|
||||
|
||||
def jp2dump(filename, codestream=False):
|
||||
"""Prints JPEG2000 metadata.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filename : string
|
||||
The input JPEG2000 file.
|
||||
codestream : optional, logical scalar
|
||||
Whether or not to dump codestream contents.
|
||||
"""
|
||||
j = Jp2k(filename)
|
||||
if codestream:
|
||||
print(j.get_codestream(header_only=False))
|
||||
else:
|
||||
print(j)
|
||||
671
glymur/jp2k.py
Normal file
671
glymur/jp2k.py
Normal file
|
|
@ -0,0 +1,671 @@
|
|||
"""Access to JPEG2000 files.
|
||||
|
||||
License: MIT
|
||||
"""
|
||||
import sys
|
||||
if sys.hexversion >= 0x03030000:
|
||||
from contextlib import ExitStack
|
||||
else:
|
||||
from contextlib2 import ExitStack
|
||||
import ctypes
|
||||
import os
|
||||
import struct
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
|
||||
from .codestream import Codestream
|
||||
from .core import progression_order
|
||||
from .jp2box import Jp2kBox
|
||||
from .lib import openjp2 as opj2
|
||||
|
||||
_cspace_map = {'rgb': opj2._CLRSPC_SRGB,
|
||||
'gray': opj2._CLRSPC_GRAY,
|
||||
'grey': opj2._CLRSPC_GRAY,
|
||||
'ycc': opj2._CLRSPC_YCC}
|
||||
|
||||
# Setup the default callback handlers. See the callback functions subsection
|
||||
# in the ctypes section of the Python documentation for a solid explanation of
|
||||
# what's going on here.
|
||||
_CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_char_p, ctypes.c_void_p)
|
||||
|
||||
|
||||
def _default_error_handler(msg, client_data):
|
||||
msg = "OpenJPEG library error: {0}".format(msg.decode('utf-8').rstrip())
|
||||
opj2._set_error_message(msg)
|
||||
|
||||
|
||||
def _default_info_handler(msg, client_data):
|
||||
print("[INFO] {0}".format(msg.decode('utf-8').rstrip()))
|
||||
|
||||
|
||||
def _default_warning_handler(library_msg, client_data):
|
||||
library_msg = library_msg.decode('utf-8').rstrip()
|
||||
msg = "OpenJPEG library warning: {0}".format(library_msg)
|
||||
warnings.warn(msg)
|
||||
|
||||
_error_callback = _CMPFUNC(_default_error_handler)
|
||||
_info_callback = _CMPFUNC(_default_info_handler)
|
||||
_warning_callback = _CMPFUNC(_default_warning_handler)
|
||||
|
||||
|
||||
class Jp2k(Jp2kBox):
|
||||
"""JPEG 2000 file.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
filename : str
|
||||
The path to the JPEG 2000 file.
|
||||
mode : str
|
||||
The mode used to open the file.
|
||||
box : sequence
|
||||
List of top-level boxes in the file. Each box may in turn contain
|
||||
its own list of boxes. Will be empty if the file consists only of a
|
||||
raw codestream.
|
||||
"""
|
||||
|
||||
def __init__(self, filename, mode='rb'):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
filename : str or file
|
||||
The path to JPEG 2000 file.
|
||||
mode : str, optional
|
||||
The mode used to open the file.
|
||||
"""
|
||||
self.filename = filename
|
||||
self.mode = mode
|
||||
self.box = []
|
||||
self.offset = 0
|
||||
|
||||
# Parse the file for JP2/JPX contents only if we are reading it.
|
||||
if mode == 'rb':
|
||||
self._parse()
|
||||
|
||||
def __str__(self):
|
||||
metadata = ['File: ' + os.path.basename(self.filename)]
|
||||
if len(self.box) > 0:
|
||||
for box in self.box:
|
||||
metadata.append(box.__str__())
|
||||
else:
|
||||
c = self.get_codestream()
|
||||
metadata.append(c.__str__())
|
||||
return '\n'.join(metadata)
|
||||
|
||||
def _parse(self):
|
||||
"""Parses the JPEG 2000 file.
|
||||
|
||||
Raises
|
||||
------
|
||||
IOError
|
||||
The file was not JPEG 2000.
|
||||
"""
|
||||
stat = os.stat(self.filename)
|
||||
self.length = stat.st_size
|
||||
self._file_size = stat.st_size
|
||||
|
||||
with open(self.filename, 'rb') as f:
|
||||
|
||||
# Make sure we have a JPEG2000 file. It could be either JP2 or
|
||||
# J2C. Check for J2C first, single box in that case.
|
||||
buffer = f.read(2)
|
||||
signature, = struct.unpack('>H', buffer)
|
||||
if signature == 0xff4f:
|
||||
self._codec_format = opj2._CODEC_J2K
|
||||
# That's it, we're done. The codestream object is only
|
||||
# produced upon explicit request.
|
||||
return
|
||||
|
||||
self._codec_format = opj2._CODEC_JP2
|
||||
|
||||
# Should be JP2.
|
||||
# First 4 bytes should be 12, the length of the 'jP ' box.
|
||||
# 2nd 4 bytes should be the box ID ('jP ').
|
||||
# 3rd 4 bytes should be the box signature (13, 10, 135, 10).
|
||||
f.seek(0)
|
||||
buffer = f.read(12)
|
||||
values = struct.unpack('>I4s4B', buffer)
|
||||
L = values[0]
|
||||
T = values[1]
|
||||
signature = values[2:]
|
||||
if L != 12 or T != b'jP ' or signature != (13, 10, 135, 10):
|
||||
msg = '{0} is not a JPEG 2000 file.'.format(self.filename)
|
||||
raise IOError(msg)
|
||||
|
||||
# Back up and start again, we know we have a superbox (box of
|
||||
# boxes) here.
|
||||
f.seek(0)
|
||||
self.box = self._parse_superbox(f)
|
||||
|
||||
def write(self, data, cratios=None, eph=False, psnr=None, numres=None,
|
||||
cbsize=None, psizes=None, grid_offset=None, sop=False,
|
||||
subsam=None, tilesize=None, prog=None, modesw=None,
|
||||
colorspace=None, verbose=False):
|
||||
"""Write image data to a JP2/JPX/J2k file. Intended usage of the
|
||||
various parameters follows that of OpenJPEG's opj_compress utility.
|
||||
|
||||
This method can only be used to create JPEG 2000 images that can fit
|
||||
in memory.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data : array
|
||||
Image data to be written to file.
|
||||
callbacks : bool, optional
|
||||
If true, enable default info handler such that INFO messages
|
||||
produced by the OpenJPEG library are output to the console. By
|
||||
default, OpenJPEG warning and error messages are captured by
|
||||
Python's own warning and error mechanisms.
|
||||
cbsize : tuple, optional
|
||||
Code block size (DY, DX).
|
||||
colorspace : str, optional
|
||||
Either 'rgb' or 'gray'.
|
||||
cratios : sequence, optional
|
||||
Compression ratios for successive layers.
|
||||
eph : bool, optional
|
||||
If true, write SOP marker after each header packet.
|
||||
grid_offset : tuple, optional
|
||||
Offset (DY, DX) of the origin of the image in the reference grid.
|
||||
modesw : int, optional
|
||||
Mode switch.
|
||||
1 = BYPASS(LAZY)
|
||||
2 = RESET
|
||||
4 = RESTART(TERMALL)
|
||||
8 = VSC
|
||||
16 = ERTERM(SEGTERM)
|
||||
32 = SEGMARK(SEGSYM)
|
||||
numres : int, optional
|
||||
Number of resolutions.
|
||||
prog : str, optional
|
||||
Progression order, one of "LRCP" "RLCP", "RPCL", "PCRL", "CPRL".
|
||||
psnr : list, optional
|
||||
Different PSNR for successive layers.
|
||||
psizes : list, optional
|
||||
List of precinct sizes.
|
||||
sop : bool, optional
|
||||
If true, write SOP marker before each packet.
|
||||
subsam : tuple, optional
|
||||
Subsampling factors (dy, dx).
|
||||
tilesize : tuple, optional
|
||||
Numeric tuple specifying tile size in terms of (numrows, numcols),
|
||||
not (X, Y).
|
||||
verbose : bool, optional
|
||||
Print informational messages produced by the OpenJPEG library.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import numpy as np
|
||||
>>> import matplotlib as mpl
|
||||
>>> import matplotlib.pyplot as plt
|
||||
>>> import os
|
||||
>>> file = os.path.join(mpl.get_data_path(), 'sample_data', 'lena.png')
|
||||
>>> data = plt.imread(file)
|
||||
>>> data = data * 255
|
||||
>>> from tempfile import NamedTemporaryFile
|
||||
>>> tfile = NamedTemporaryFile(suffix='.jp2', delete=False)
|
||||
>>> from glymur import Jp2k
|
||||
>>> j = Jp2k(tfile.name, mode='wb')
|
||||
>>> j.write(data.astype(np.uint8))
|
||||
"""
|
||||
|
||||
cparams = opj2._set_default_encoder_parameters()
|
||||
|
||||
outfile = self.filename.encode()
|
||||
n = opj2._PATH_LEN - len(outfile)
|
||||
outfile += b'0' * n
|
||||
cparams.outfile = outfile
|
||||
|
||||
if self.filename[-4:].lower() == '.jp2':
|
||||
codec_fmt = opj2._CODEC_JP2
|
||||
else:
|
||||
codec_fmt = opj2._CODEC_J2K
|
||||
|
||||
cparams.cod_format = codec_fmt
|
||||
|
||||
# Set defaults to lossless to begin.
|
||||
cparams.tcp_rates[0] = 0
|
||||
cparams.tcp_numlayers = 1
|
||||
cparams.cp_disto_alloc = 1
|
||||
|
||||
if cbsize is not None:
|
||||
w = cbsize[1]
|
||||
h = cbsize[0]
|
||||
if h * w > 4096 or h < 4 or w < 4:
|
||||
msg = "Code block area cannot exceed 4096. "
|
||||
msg += "Code block height and width must be larger than 4."
|
||||
raise RuntimeError(msg)
|
||||
cparams.cblockw_init = w
|
||||
cparams.cblockh_init = h
|
||||
|
||||
if cratios is not None:
|
||||
cparams.tcp_numlayers = len(cratios)
|
||||
for j, cratio in enumerate(cratios):
|
||||
cparams.tcp_rates[j] = cratio
|
||||
cparams.cp_disto_alloc = 1
|
||||
|
||||
if eph:
|
||||
cparams.csty |= 0x04
|
||||
|
||||
if grid_offset is not None:
|
||||
cparams.image_offset_x0 = grid_offset[1]
|
||||
cparams.image_offset_y0 = grid_offset[0]
|
||||
|
||||
if modesw is not None:
|
||||
for x in range(6):
|
||||
if modesw & (1 << x):
|
||||
cparams.mode |= (1 << x)
|
||||
|
||||
if numres is not None:
|
||||
cparams.numresolution = numres
|
||||
|
||||
if prog is not None:
|
||||
prog = prog.upper()
|
||||
cparams.prog_order = progression_order[prog]
|
||||
|
||||
if psnr is not None:
|
||||
cparams.tcp_numlayers = len(psnr)
|
||||
for j, snr_layer in enumerate(psnr):
|
||||
cparams.tcp_distoratio[j] = snr_layer
|
||||
cparams.cp_fixed_quality = 1
|
||||
|
||||
if psizes is not None:
|
||||
for j, precinct in enumerate(psizes):
|
||||
cparams.prcw_init[j] = precinct[0]
|
||||
cparams.prch_init[j] = precinct[1]
|
||||
cparams.csty |= 0x01
|
||||
cparams.res_spec = len(psizes)
|
||||
|
||||
if sop:
|
||||
cparams.csty |= 0x02
|
||||
|
||||
if subsam is not None:
|
||||
cparams.subsampling_dy = subsam[0]
|
||||
cparams.subsampling_dx = subsam[1]
|
||||
|
||||
if tilesize is not None:
|
||||
cparams.cp_tdx = tilesize[1]
|
||||
cparams.cp_tdy = tilesize[0]
|
||||
cparams.tile_size_on = opj2._TRUE
|
||||
|
||||
if cratios is not None and psnr is not None:
|
||||
msg = "Cannot specify cratios and psnr together."
|
||||
raise RuntimeError(msg)
|
||||
|
||||
if data.ndim == 2:
|
||||
numrows, numcols = data.shape
|
||||
data = data.reshape(numrows, numcols, 1)
|
||||
elif data.ndim == 3:
|
||||
pass
|
||||
else:
|
||||
msg = "{0}D imagery is not allowed.".format(data.ndim)
|
||||
raise IOError(msg)
|
||||
|
||||
numrows, numcols, num_comps = data.shape
|
||||
|
||||
if colorspace is None:
|
||||
if data.shape[2] == 1 or data.shape[2] == 2:
|
||||
colorspace = opj2._CLRSPC_GRAY
|
||||
else:
|
||||
# No YCC unless specifically told to do so.
|
||||
colorspace = opj2._CLRSPC_SRGB
|
||||
else:
|
||||
if codec_fmt == opj2._CODEC_J2K:
|
||||
raise IOError('Do not specify a colorspace with J2K.')
|
||||
colorspace = colorspace.lower()
|
||||
if colorspace not in ('rgb', 'grey', 'gray'):
|
||||
msg = 'Invalid colorspace "{0}"'.format(colorspace)
|
||||
raise IOError(msg)
|
||||
elif colorspace == 'rgb' and data.shape[2] < 3:
|
||||
msg = 'RGB colorspace requires at least 3 components.'
|
||||
raise IOError(msg)
|
||||
else:
|
||||
colorspace = _cspace_map[colorspace]
|
||||
|
||||
if data.dtype == np.uint8:
|
||||
comp_prec = 8
|
||||
elif data.dtype == np.uint16:
|
||||
comp_prec = 16
|
||||
else:
|
||||
raise RuntimeError("unhandled datatype")
|
||||
|
||||
comptparms = (opj2._image_comptparm_t * num_comps)()
|
||||
for j in range(num_comps):
|
||||
comptparms[j].dx = cparams.subsampling_dx
|
||||
comptparms[j].dy = cparams.subsampling_dy
|
||||
comptparms[j].w = numcols
|
||||
comptparms[j].h = numrows
|
||||
comptparms[j].x0 = cparams.image_offset_x0
|
||||
comptparms[j].y0 = cparams.image_offset_y0
|
||||
comptparms[j].prec = comp_prec
|
||||
comptparms[j].bpp = comp_prec
|
||||
comptparms[j].sgnd = 0
|
||||
|
||||
image = opj2._image_create(comptparms, colorspace)
|
||||
|
||||
# set image offset and reference grid
|
||||
image.contents.x0 = cparams.image_offset_x0
|
||||
image.contents.y0 = cparams.image_offset_y0
|
||||
image.contents.x1 = (image.contents.x0 +
|
||||
(numcols - 1) * cparams.subsampling_dx + 1)
|
||||
image.contents.y1 = (image.contents.y0 +
|
||||
(numrows - 1) * cparams.subsampling_dy + 1)
|
||||
|
||||
# Stage the image data to the openjpeg data structure.
|
||||
for k in range(0, num_comps):
|
||||
layer = np.ascontiguousarray(data[:, :, k], dtype=np.int32)
|
||||
dest = image.contents.comps[k].data
|
||||
src = layer.ctypes.data
|
||||
ctypes.memmove(dest, src, layer.nbytes)
|
||||
|
||||
# set multi-component transform?
|
||||
if image.contents.numcomps == 3:
|
||||
cparams.tcp_mct = 1
|
||||
else:
|
||||
cparams.tcp_mct = 0
|
||||
|
||||
codec = opj2._create_compress(codec_fmt)
|
||||
|
||||
if verbose:
|
||||
opj2._set_info_handler(codec, _info_callback)
|
||||
else:
|
||||
opj2._set_info_handler(codec, None)
|
||||
|
||||
opj2._set_warning_handler(codec, _warning_callback)
|
||||
opj2._set_error_handler(codec, _error_callback)
|
||||
opj2._setup_encoder(codec, cparams, image)
|
||||
strm = opj2._stream_create_default_file_stream_v3(self.filename, False)
|
||||
opj2._start_compress(codec, image, strm)
|
||||
opj2._encode(codec, strm)
|
||||
opj2._end_compress(codec, strm)
|
||||
opj2._stream_destroy_v3(strm)
|
||||
opj2._destroy_codec(codec)
|
||||
opj2._image_destroy(image)
|
||||
|
||||
self._parse()
|
||||
|
||||
def read(self, reduce=0, layer=0, area=None, tile=None, verbose=False):
|
||||
"""Read a JPEG 2000 image.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
layer : int, optional
|
||||
Number of quality layer to decode.
|
||||
reduce : int, optional
|
||||
Factor by which to reduce output resolution.
|
||||
area : tuple, optional
|
||||
Specifies decoding image area,
|
||||
(first_row, first_col, last_row, last_col)
|
||||
tile : int, optional
|
||||
Number of tile to decode.
|
||||
verbose : bool, optional
|
||||
Print informational messages produced by the OpenJPEG library.
|
||||
|
||||
Returns
|
||||
-------
|
||||
result : array
|
||||
The image data.
|
||||
|
||||
Raises
|
||||
------
|
||||
IOError
|
||||
If the image has differing subsample factors.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import glymur
|
||||
>>> import pkg_resources as pkg
|
||||
>>> jfile = pkg.resource_filename(glymur.__name__, "data/nemo.jp2")
|
||||
>>> jp = glymur.Jp2k(jfile)
|
||||
>>> data = jp.read(reduce=1)
|
||||
"""
|
||||
# Check for differing subsample factors.
|
||||
codestream = self.get_codestream(header_only=True)
|
||||
dxs = np.array(codestream.segment[1].XRsiz)
|
||||
dys = np.array(codestream.segment[1].YRsiz)
|
||||
if np.any(dxs - dxs[0]) or np.any(dys - dys[0]):
|
||||
msg = "Components must all have the same subsampling factors."
|
||||
raise IOError(msg)
|
||||
|
||||
data = self._read_common(reduce=reduce,
|
||||
layer=layer,
|
||||
area=area,
|
||||
tile=tile,
|
||||
verbose=verbose,
|
||||
as_bands=False)
|
||||
|
||||
if data.shape[2] == 1:
|
||||
data = data.view()
|
||||
data.shape = data.shape[0:2]
|
||||
|
||||
return data
|
||||
|
||||
def _read_common(self, reduce=0, layer=0, area=None, tile=None,
|
||||
verbose=False, as_bands=False):
|
||||
"""Read a JPEG 2000 image.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
layer : int, optional
|
||||
Number of quality layer to decode.
|
||||
reduce : int, optional
|
||||
Factor by which to reduce output resolution.
|
||||
area : tuple, optional
|
||||
Specifies decoding image area,
|
||||
(first_row, first_col, last_row, last_col)
|
||||
tile : int, optional
|
||||
Number of tile to decode.
|
||||
verbose : bool, optional
|
||||
Print informational messages produced by the OpenJPEG library.
|
||||
as_bands : bool, optional
|
||||
If true, return the individual 2D components in a list.
|
||||
|
||||
Returns
|
||||
-------
|
||||
data : list or array
|
||||
The individual image components or a single array.
|
||||
"""
|
||||
dparam = opj2._set_default_decoder_parameters()
|
||||
|
||||
infile = self.filename.encode()
|
||||
nelts = opj2._PATH_LEN - len(infile)
|
||||
infile += b'0' * nelts
|
||||
dparam.infile = infile
|
||||
|
||||
dparam.decod_format = self._codec_format
|
||||
|
||||
dparam.cp_layer = layer
|
||||
dparam.cp_reduce = reduce
|
||||
if area is not None:
|
||||
if area[0] < 0 or area[1] < 0:
|
||||
msg = "Upper left corner coordinates must be nonnegative: {0}"
|
||||
msg = msg.format(area)
|
||||
raise IOError(msg)
|
||||
if area[2] <= 0 or area[3] <= 0:
|
||||
msg = "Lower right corner coordinates must be positive: {0}"
|
||||
msg = msg.format(area)
|
||||
raise IOError(msg)
|
||||
dparam.DA_y0 = area[0]
|
||||
dparam.DA_x0 = area[1]
|
||||
dparam.DA_y1 = area[2]
|
||||
dparam.DA_x1 = area[3]
|
||||
|
||||
if tile is not None:
|
||||
dparam.tile_index = tile
|
||||
dparam.nb_tile_to_decode = 1
|
||||
|
||||
with ExitStack() as stack:
|
||||
stream = opj2._stream_create_default_file_stream_v3(self.filename,
|
||||
True)
|
||||
stack.callback(opj2._stream_destroy_v3, stream)
|
||||
codec = opj2._create_decompress(self._codec_format)
|
||||
stack.callback(opj2._destroy_codec, codec)
|
||||
|
||||
opj2._set_error_handler(codec, _error_callback)
|
||||
opj2._set_warning_handler(codec, _warning_callback)
|
||||
if verbose:
|
||||
opj2._set_info_handler(codec, _info_callback)
|
||||
else:
|
||||
opj2._set_info_handler(codec, None)
|
||||
|
||||
opj2._setup_decoder(codec, dparam)
|
||||
image = opj2._read_header(stream, codec)
|
||||
stack.callback(opj2._image_destroy, image)
|
||||
|
||||
if dparam.nb_tile_to_decode:
|
||||
opj2._get_decoded_tile(codec, stream, image, dparam.tile_index)
|
||||
else:
|
||||
opj2._set_decode_area(codec, image,
|
||||
dparam.DA_x0, dparam.DA_y0,
|
||||
dparam.DA_x1, dparam.DA_y1)
|
||||
opj2._decode(codec, stream, image)
|
||||
opj2._end_decompress(codec, stream)
|
||||
|
||||
component = image.contents.comps[0]
|
||||
if component.sgnd:
|
||||
if component.prec <= 8:
|
||||
dtype = np.int8
|
||||
elif component.prec <= 16:
|
||||
dtype = np.int16
|
||||
else:
|
||||
raise RuntimeError("Unhandled precision, datatype")
|
||||
else:
|
||||
if component.prec <= 8:
|
||||
dtype = np.uint8
|
||||
elif component.prec <= 16:
|
||||
dtype = np.uint16
|
||||
else:
|
||||
raise RuntimeError("Unhandled precision, datatype")
|
||||
|
||||
if as_bands:
|
||||
data = []
|
||||
else:
|
||||
nrows = image.contents.comps[0].h
|
||||
ncols = image.contents.comps[0].w
|
||||
ncomps = image.contents.numcomps
|
||||
data = np.zeros((nrows, ncols, ncomps), dtype)
|
||||
|
||||
for k in range(image.contents.numcomps):
|
||||
component = image.contents.comps[k]
|
||||
nrows = component.h
|
||||
ncols = component.w
|
||||
|
||||
if nrows == 0 or ncols == 0:
|
||||
# Letting this situation continue would segfault
|
||||
# Python.
|
||||
msg = "Component {0} has dimensions {1} x {2}"
|
||||
msg = msg.format(k, nrows, ncols)
|
||||
raise IOError(msg)
|
||||
|
||||
addr = ctypes.addressof(component.data.contents)
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
x = np.ctypeslib.as_array(
|
||||
(ctypes.c_int32 * nrows * ncols).from_address(addr))
|
||||
if as_bands:
|
||||
data.append(np.reshape(x.astype(dtype), (nrows, ncols)))
|
||||
else:
|
||||
data[:, :, k] = np.reshape(x.astype(dtype), (nrows, ncols))
|
||||
|
||||
return data
|
||||
|
||||
def read_bands(self, reduce=0, layer=0, area=None, tile=None,
|
||||
verbose=False):
|
||||
"""Read a JPEG 2000 image.
|
||||
|
||||
The only time you should use this method is when the image has
|
||||
different subsampling factors across components. Otherwise you should
|
||||
use the read method.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
layer : int, optional
|
||||
Number of quality layer to decode.
|
||||
reduce : int, optional
|
||||
Factor by which to reduce output resolution.
|
||||
area : tuple, optional
|
||||
Specifies decoding image area,
|
||||
(first_row, first_col, last_row, last_col)
|
||||
tile : int, optional
|
||||
Number of tile to decode.
|
||||
verbose : bool, optional
|
||||
Print informational messages produced by the OpenJPEG library.
|
||||
|
||||
Returns
|
||||
-------
|
||||
lst : list
|
||||
The individual image components.
|
||||
|
||||
See also
|
||||
--------
|
||||
read : read JPEG 2000 image
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import glymur
|
||||
>>> import pkg_resources as pkg
|
||||
>>> jfile = pkg.resource_filename(glymur.__name__, "data/nemo.jp2")
|
||||
>>> jp = glymur.Jp2k(jfile)
|
||||
>>> components_lst = jp.read_bands(reduce=1)
|
||||
"""
|
||||
lst = self._read_common(reduce=reduce,
|
||||
layer=layer,
|
||||
area=area,
|
||||
tile=tile,
|
||||
verbose=verbose,
|
||||
as_bands=True)
|
||||
|
||||
return lst
|
||||
|
||||
def get_codestream(self, header_only=True):
|
||||
"""Returns a codestream object.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
header_only : bool, optional
|
||||
If True, only marker segments in the main header are parsed.
|
||||
Supplying False may impose a large performance penalty.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Object describing the codestream syntax.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import glymur
|
||||
>>> import pkg_resources as pkg
|
||||
>>> jfile = pkg.resource_filename(glymur.__name__, "data/nemo.jp2")
|
||||
>>> jp = glymur.Jp2k(jfile)
|
||||
>>> codestream = jp.get_codestream()
|
||||
>>> print(codestream.segment[1])
|
||||
SIZ marker segment @ (87, 47)
|
||||
Profile: 2
|
||||
Reference Grid Height, Width: (1456 x 2592)
|
||||
Vertical, Horizontal Reference Grid Offset: (0 x 0)
|
||||
Reference Tile Height, Width: (512 x 512)
|
||||
Vertical, Horizontal Reference Tile Offset: (0 x 0)
|
||||
Bitdepth: (8, 8, 8)
|
||||
Signed: (False, False, False)
|
||||
Vertical, Horizontal Subsampling: ((1, 1), (1, 1), (1, 1))
|
||||
|
||||
Raises
|
||||
------
|
||||
IOError
|
||||
If the file is JPX with more than one codestream.
|
||||
"""
|
||||
with open(self.filename, 'rb') as fp:
|
||||
if self._codec_format == opj2._CODEC_J2K:
|
||||
codestream = Codestream(fp, header_only=header_only)
|
||||
else:
|
||||
box = [x for x in self.box if x.id == 'jp2c']
|
||||
if len(box) != 1:
|
||||
msg = "JP2 files must have a single codestream."
|
||||
raise RuntimeError(msg)
|
||||
fp.seek(box[0].offset)
|
||||
buffer = fp.read(8)
|
||||
(L, T) = struct.unpack('>I4s', buffer)
|
||||
if L == 1:
|
||||
# Seek past the XL field.
|
||||
buffer = fp.read(8)
|
||||
codestream = Codestream(fp, header_only=header_only)
|
||||
|
||||
return codestream
|
||||
3
glymur/lib/__init__.py
Normal file
3
glymur/lib/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
"""This package organizes individual libraries employed by glymur."""
|
||||
from . import openjp2
|
||||
from . import test
|
||||
1309
glymur/lib/openjp2.py
Normal file
1309
glymur/lib/openjp2.py
Normal file
File diff suppressed because it is too large
Load diff
1
glymur/lib/test/__init__.py
Normal file
1
glymur/lib/test/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from .test_openjp2 import TestOpenJP2 as openjp2
|
||||
400
glymur/lib/test/test_openjp2.py
Normal file
400
glymur/lib/test/test_openjp2.py
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
import doctest
|
||||
import os
|
||||
import pkg_resources
|
||||
import shutil
|
||||
import struct
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import numpy as np
|
||||
|
||||
import glymur
|
||||
|
||||
|
||||
# Doc tests should be run as well.
|
||||
def load_tests(loader, tests, ignore):
|
||||
tests.addTests(doctest.DocTestSuite('glymur.lib.openjp2'))
|
||||
return tests
|
||||
|
||||
|
||||
class TestOpenJP2(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_set_default_encoder_parameters(self):
|
||||
cparams = glymur.lib.openjp2._set_default_encoder_parameters()
|
||||
|
||||
self.assertEqual(cparams.res_spec, 0)
|
||||
self.assertEqual(cparams.cblockw_init, 64)
|
||||
self.assertEqual(cparams.cblockh_init, 64)
|
||||
self.assertEqual(cparams.numresolution, 6)
|
||||
self.assertEqual(cparams.subsampling_dx, 1)
|
||||
self.assertEqual(cparams.subsampling_dy, 1)
|
||||
self.assertEqual(cparams.mode, 0)
|
||||
self.assertEqual(cparams.prog_order, glymur.lib.openjp2.LRCP)
|
||||
self.assertEqual(cparams.roi_shift, 0)
|
||||
self.assertEqual(cparams.cp_tx0, 0)
|
||||
self.assertEqual(cparams.cp_ty0, 0)
|
||||
|
||||
self.assertEqual(cparams.irreversible, 0)
|
||||
|
||||
def test_set_default_decoder_parameters(self):
|
||||
dparams = glymur.lib.openjp2._set_default_decoder_parameters()
|
||||
|
||||
self.assertEqual(dparams.DA_x0, 0)
|
||||
self.assertEqual(dparams.DA_y0, 0)
|
||||
self.assertEqual(dparams.DA_x1, 0)
|
||||
self.assertEqual(dparams.DA_y1, 0)
|
||||
|
||||
def tile_macro(self, codec, stream, imagep, tidx):
|
||||
# called only by j2k_random_tile_access
|
||||
glymur.lib.openjp2._get_decoded_tile(codec, stream, imagep, tidx)
|
||||
for j in range(imagep.contents.numcomps):
|
||||
self.assertIsNotNone(imagep.contents.comps[j].data)
|
||||
|
||||
def j2k_random_tile_access(self, filename, codec_format=None):
|
||||
# called by the test_rtaX methods
|
||||
dparam = glymur.lib.openjp2._set_default_decoder_parameters()
|
||||
|
||||
infile = filename.encode()
|
||||
nelts = glymur.lib.openjp2._PATH_LEN - len(infile)
|
||||
infile += b'0' * nelts
|
||||
dparam.infile = infile
|
||||
|
||||
dparam.decod_format = codec_format
|
||||
|
||||
codec = glymur.lib.openjp2._create_decompress(codec_format)
|
||||
|
||||
glymur.lib.openjp2._set_info_handler(codec, None)
|
||||
glymur.lib.openjp2._set_warning_handler(codec, None)
|
||||
glymur.lib.openjp2._set_error_handler(codec, None)
|
||||
|
||||
x = (filename, True)
|
||||
stream = glymur.lib.openjp2._stream_create_default_file_stream_v3(*x)
|
||||
|
||||
glymur.lib.openjp2._setup_decoder(codec, dparam)
|
||||
image = glymur.lib.openjp2._read_header(stream, codec)
|
||||
|
||||
cstr_info = glymur.lib.openjp2._get_cstr_info(codec)
|
||||
|
||||
tile_ul = 0
|
||||
tile_ur = cstr_info.contents.tw - 1
|
||||
tile_lr = cstr_info.contents.tw * cstr_info.contents.th - 1
|
||||
tile_ll = tile_lr - cstr_info.contents.tw
|
||||
|
||||
self.tile_macro(codec, stream, image, tile_ul)
|
||||
self.tile_macro(codec, stream, image, tile_ur)
|
||||
self.tile_macro(codec, stream, image, tile_lr)
|
||||
self.tile_macro(codec, stream, image, tile_ll)
|
||||
|
||||
glymur.lib.openjp2._destroy_cstr_info(cstr_info)
|
||||
|
||||
glymur.lib.openjp2._end_decompress(codec, stream)
|
||||
glymur.lib.openjp2._destroy_codec(codec)
|
||||
glymur.lib.openjp2._stream_destroy_v3(stream)
|
||||
glymur.lib.openjp2._image_destroy(image)
|
||||
|
||||
def tile_decoder(self, x0=None, y0=None, x1=None, y1=None, filename=None,
|
||||
codec_format=None):
|
||||
x = (filename, True)
|
||||
stream = glymur.lib.openjp2._stream_create_default_file_stream_v3(*x)
|
||||
dparam = glymur.lib.openjp2._set_default_decoder_parameters()
|
||||
|
||||
dparam.decod_format = codec_format
|
||||
|
||||
# Do not use layer decoding limitation.
|
||||
dparam.cp_layer = 0
|
||||
|
||||
# do not use resolution reductions.
|
||||
dparam.cp_reduce = 0
|
||||
|
||||
codec = glymur.lib.openjp2._create_decompress(codec_format)
|
||||
|
||||
glymur.lib.openjp2._set_info_handler(codec, None)
|
||||
glymur.lib.openjp2._set_warning_handler(codec, None)
|
||||
glymur.lib.openjp2._set_error_handler(codec, None)
|
||||
|
||||
glymur.lib.openjp2._setup_decoder(codec, dparam)
|
||||
image = glymur.lib.openjp2._read_header(stream, codec)
|
||||
glymur.lib.openjp2._set_decode_area(codec, image, x0, y0, x1, y1)
|
||||
|
||||
data = np.zeros((1150, 2048, 3), dtype=np.uint8)
|
||||
while True:
|
||||
rargs = glymur.lib.openjp2._read_tile_header(codec, stream)
|
||||
tidx = rargs[0]
|
||||
sz = rargs[1]
|
||||
go_on = rargs[-1]
|
||||
if not go_on:
|
||||
break
|
||||
glymur.lib.openjp2._decode_tile_data(codec, tidx, data, sz, stream)
|
||||
|
||||
glymur.lib.openjp2._end_decompress(codec, stream)
|
||||
glymur.lib.openjp2._destroy_codec(codec)
|
||||
glymur.lib.openjp2._stream_destroy_v3(stream)
|
||||
glymur.lib.openjp2._image_destroy(image)
|
||||
|
||||
def tile_encoder(self, num_comps=None, tile_width=None, tile_height=None,
|
||||
filename=None, codec=None, comp_prec=None,
|
||||
image_width=None, image_height=None,
|
||||
irreversible=None):
|
||||
num_tiles = (image_width / tile_width) * (image_height / tile_height)
|
||||
tile_size = tile_width * tile_height * num_comps * comp_prec / 8
|
||||
|
||||
data = np.random.random((tile_height, tile_width, num_comps))
|
||||
data = (data * 255).astype(np.uint8)
|
||||
|
||||
l_param = glymur.lib.openjp2._set_default_encoder_parameters()
|
||||
|
||||
l_param.tcp_numlayers = 1
|
||||
l_param.cp_fixed_quality = 1
|
||||
l_param.tcp_distoratio[0] = 20
|
||||
|
||||
# position of the tile grid aligned with the image
|
||||
l_param.cp_tx0 = 0
|
||||
l_param.cp_ty0 = 0
|
||||
|
||||
# tile size, we are using tile based encoding
|
||||
l_param.tile_size_on = 1
|
||||
l_param.cp_tdx = tile_width
|
||||
l_param.cp_tdy = tile_height
|
||||
|
||||
# use irreversible encoding
|
||||
l_param.irreversible = irreversible
|
||||
|
||||
l_param.numresolution = 6
|
||||
|
||||
l_param.prog_order = glymur.lib.openjp2.LRCP
|
||||
|
||||
l_params = (glymur.lib.openjp2._image_comptparm_t * num_comps)()
|
||||
for j in range(num_comps):
|
||||
l_params[j].dx = 1
|
||||
l_params[j].dy = 1
|
||||
l_params[j].h = image_height
|
||||
l_params[j].w = image_width
|
||||
l_params[j].sgnd = 0
|
||||
l_params[j].prec = comp_prec
|
||||
l_params[j].x0 = 0
|
||||
l_params[j].y0 = 0
|
||||
|
||||
codec = glymur.lib.openjp2._create_compress(codec)
|
||||
|
||||
glymur.lib.openjp2._set_info_handler(codec, None)
|
||||
glymur.lib.openjp2._set_warning_handler(codec, None)
|
||||
glymur.lib.openjp2._set_error_handler(codec, None)
|
||||
|
||||
cspace = glymur.lib.openjp2._CLRSPC_SRGB
|
||||
l_image = glymur.lib.openjp2._image_tile_create(l_params, cspace)
|
||||
|
||||
l_image.contents.x0 = 0
|
||||
l_image.contents.y0 = 0
|
||||
l_image.contents.x1 = image_width
|
||||
l_image.contents.y1 = image_height
|
||||
l_image.contents.color_space = glymur.lib.openjp2._CLRSPC_SRGB
|
||||
|
||||
glymur.lib.openjp2._setup_encoder(codec, l_param, l_image)
|
||||
|
||||
x = (filename, False)
|
||||
stream = glymur.lib.openjp2._stream_create_default_file_stream_v3(*x)
|
||||
glymur.lib.openjp2._start_compress(codec, l_image, stream)
|
||||
|
||||
for j in np.arange(num_tiles):
|
||||
glymur.lib.openjp2._write_tile(codec, j, data, tile_size, stream)
|
||||
|
||||
glymur.lib.openjp2._end_compress(codec, stream)
|
||||
glymur.lib.openjp2._stream_destroy_v3(stream)
|
||||
glymur.lib.openjp2._destroy_codec(codec)
|
||||
glymur.lib.openjp2._image_destroy(l_image)
|
||||
|
||||
def tte0_setup(self, filename):
|
||||
kwargs = {'filename': filename,
|
||||
'codec': glymur.lib.openjp2._CODEC_J2K,
|
||||
'comp_prec': 8,
|
||||
'irreversible': 1,
|
||||
'num_comps': 3,
|
||||
'image_height': 200,
|
||||
'image_width': 200,
|
||||
'tile_height': 100,
|
||||
'tile_width': 100}
|
||||
self.tile_encoder(**kwargs)
|
||||
|
||||
def test_tte0(self):
|
||||
# Runs test designated tte0 in OpenJPEG test suite.
|
||||
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
|
||||
self.tte0_setup(tfile.name)
|
||||
|
||||
def test_ttd0(self):
|
||||
# Runs test designated ttd0 in OpenJPEG test suite.
|
||||
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
|
||||
|
||||
# Produce the tte0 output file for ttd0 input.
|
||||
self.tte0_setup(tfile.name)
|
||||
|
||||
kwargs = {'x0': 0,
|
||||
'y0': 0,
|
||||
'x1': 1000,
|
||||
'y1': 1000,
|
||||
'filename': tfile.name,
|
||||
'codec_format': glymur.lib.openjp2._CODEC_J2K}
|
||||
self.tile_decoder(**kwargs)
|
||||
|
||||
def tte1_setup(self, filename):
|
||||
kwargs = {'filename': filename,
|
||||
'codec': glymur.lib.openjp2._CODEC_J2K,
|
||||
'comp_prec': 8,
|
||||
'irreversible': 1,
|
||||
'num_comps': 3,
|
||||
'image_height': 256,
|
||||
'image_width': 256,
|
||||
'tile_height': 128,
|
||||
'tile_width': 128}
|
||||
self.tile_encoder(**kwargs)
|
||||
|
||||
def test_tte1(self):
|
||||
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
|
||||
# Runs test designated tte1 in OpenJPEG test suite.
|
||||
self.tte1_setup(tfile.name)
|
||||
|
||||
def test_ttd1(self):
|
||||
# Runs test designated ttd1 in OpenJPEG test suite.
|
||||
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
|
||||
|
||||
# Produce the tte0 output file for ttd0 input.
|
||||
self.tte1_setup(tfile.name)
|
||||
|
||||
kwargs = {'x0': 0,
|
||||
'y0': 0,
|
||||
'x1': 128,
|
||||
'y1': 128,
|
||||
'filename': tfile.name,
|
||||
'codec_format': glymur.lib.openjp2._CODEC_J2K}
|
||||
self.tile_decoder(**kwargs)
|
||||
|
||||
def test_rta1(self):
|
||||
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
|
||||
# Runs test designated rta1 in OpenJPEG test suite.
|
||||
self.tte1_setup(tfile.name)
|
||||
|
||||
kwargs = {'codec_format': glymur.lib.openjp2._CODEC_J2K}
|
||||
self.j2k_random_tile_access(tfile.name, **kwargs)
|
||||
|
||||
def tte2_setup(self, filename):
|
||||
kwargs = {'filename': filename,
|
||||
'codec': glymur.lib.openjp2._CODEC_JP2,
|
||||
'comp_prec': 8,
|
||||
'irreversible': 1,
|
||||
'num_comps': 3,
|
||||
'image_height': 256,
|
||||
'image_width': 256,
|
||||
'tile_height': 128,
|
||||
'tile_width': 128}
|
||||
self.tile_encoder(**kwargs)
|
||||
|
||||
def test_tte2(self):
|
||||
# Runs test designated tte2 in OpenJPEG test suite.
|
||||
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
|
||||
self.tte2_setup(tfile.name)
|
||||
|
||||
def test_ttd2(self):
|
||||
# Runs test designated ttd2 in OpenJPEG test suite.
|
||||
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
|
||||
# Produce the tte0 output file for ttd0 input.
|
||||
self.tte2_setup(tfile.name)
|
||||
|
||||
kwargs = {'x0': 0,
|
||||
'y0': 0,
|
||||
'x1': 128,
|
||||
'y1': 128,
|
||||
'filename': tfile.name,
|
||||
'codec_format': glymur.lib.openjp2._CODEC_JP2}
|
||||
self.tile_decoder(**kwargs)
|
||||
|
||||
def test_rta2(self):
|
||||
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
|
||||
# Runs test designated rta2 in OpenJPEG test suite.
|
||||
self.tte2_setup(tfile.name)
|
||||
|
||||
kwargs = {'codec_format': glymur.lib.openjp2._CODEC_JP2}
|
||||
self.j2k_random_tile_access(tfile.name, **kwargs)
|
||||
|
||||
def tte3_setup(self, filename):
|
||||
kwargs = {'filename': filename,
|
||||
'codec': glymur.lib.openjp2._CODEC_J2K,
|
||||
'comp_prec': 8,
|
||||
'irreversible': 1,
|
||||
'num_comps': 1,
|
||||
'image_height': 256,
|
||||
'image_width': 256,
|
||||
'tile_height': 128,
|
||||
'tile_width': 128}
|
||||
self.tile_encoder(**kwargs)
|
||||
|
||||
def test_tte3(self):
|
||||
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
|
||||
# Runs test designated tte3 in OpenJPEG test suite.
|
||||
self.tte3_setup(tfile.name)
|
||||
|
||||
def test_rta3(self):
|
||||
# Runs test designated rta3 in OpenJPEG test suite.
|
||||
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
|
||||
self.tte3_setup(tfile.name)
|
||||
|
||||
kwargs = {'codec_format': glymur.lib.openjp2._CODEC_J2K}
|
||||
self.j2k_random_tile_access(tfile.name, **kwargs)
|
||||
|
||||
def tte4_setup(self, filename):
|
||||
kwargs = {'filename': filename,
|
||||
'codec': glymur.lib.openjp2._CODEC_J2K,
|
||||
'comp_prec': 8,
|
||||
'irreversible': 0,
|
||||
'num_comps': 1,
|
||||
'image_height': 256,
|
||||
'image_width': 256,
|
||||
'tile_height': 128,
|
||||
'tile_width': 128}
|
||||
self.tile_encoder(**kwargs)
|
||||
|
||||
def test_tte4(self):
|
||||
# Runs test designated tte4 in OpenJPEG test suite.
|
||||
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
|
||||
self.tte4_setup(tfile.name)
|
||||
|
||||
def test_rta4(self):
|
||||
# Runs test designated rta4 in OpenJPEG test suite.
|
||||
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
|
||||
self.tte4_setup(tfile.name)
|
||||
|
||||
kwargs = {'codec_format': glymur.lib.openjp2._CODEC_J2K}
|
||||
self.j2k_random_tile_access(tfile.name, **kwargs)
|
||||
|
||||
def tte5_setup(self, filename):
|
||||
kwargs = {'filename': filename,
|
||||
'codec': glymur.lib.openjp2._CODEC_J2K,
|
||||
'comp_prec': 8,
|
||||
'irreversible': 0,
|
||||
'num_comps': 1,
|
||||
'image_height': 512,
|
||||
'image_width': 512,
|
||||
'tile_height': 256,
|
||||
'tile_width': 256}
|
||||
self.tile_encoder(**kwargs)
|
||||
|
||||
def test_tte5(self):
|
||||
# Runs test designated tte5 in OpenJPEG test suite.
|
||||
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
|
||||
self.tte5_setup(tfile.name)
|
||||
|
||||
def test_rta5(self):
|
||||
# Runs test designated rta5 in OpenJPEG test suite.
|
||||
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
|
||||
self.tte5_setup(tfile.name)
|
||||
|
||||
kwargs = {'codec_format': glymur.lib.openjp2._CODEC_J2K}
|
||||
self.j2k_random_tile_access(tfile.name, **kwargs)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
7
glymur/test/__init__.py
Normal file
7
glymur/test/__init__.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
from .test_callbacks import TestCallbacks as callbacks
|
||||
from .test_codestream import TestCodestream as codestream
|
||||
from .test_jp2k import TestJp2k as jp2k
|
||||
from .test_printing import TestPrinting as printing
|
||||
from .test_opj_suite import TestSuite as suite
|
||||
from .test_opj_suite_write import TestSuiteWrite as suitew
|
||||
from .test_opj_suite_neg import TestSuiteNegative as suiteneg
|
||||
72
glymur/test/test_callbacks.py
Normal file
72
glymur/test/test_callbacks.py
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import os
|
||||
import pkg_resources
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
import warnings
|
||||
|
||||
if sys.hexversion < 0x03000000:
|
||||
from StringIO import StringIO
|
||||
else:
|
||||
from io import StringIO
|
||||
|
||||
import glymur
|
||||
|
||||
try:
|
||||
data_root = os.environ['OPJ_DATA_ROOT']
|
||||
except KeyError:
|
||||
data_root = None
|
||||
except:
|
||||
raise
|
||||
|
||||
|
||||
class TestCallbacks(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
# Save sys.stdout.
|
||||
self.stdout = sys.stdout
|
||||
sys.stdout = StringIO()
|
||||
self.jp2file = pkg_resources.resource_filename(glymur.__name__,
|
||||
"data/nemo.jp2")
|
||||
|
||||
def tearDown(self):
|
||||
# Restore stdout.
|
||||
sys.stdout = self.stdout
|
||||
|
||||
def test_info_callback_on_write(self):
|
||||
# Verify the messages printed when writing an image in verbose mode.
|
||||
j = glymur.Jp2k(self.jp2file)
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
tiledata = j.read(tile=0)
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
j = glymur.Jp2k(tfile.name, 'wb')
|
||||
j.write(tiledata, verbose=True)
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
expected = '[INFO] tile number 1 / 1'
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_info_warning_callbacks_on_read(self):
|
||||
# Verify that we get the expected stdio output when our internal info
|
||||
# callback handler is enabled.
|
||||
j = glymur.Jp2k(self.jp2file)
|
||||
d = j.read(reduce=3, verbose=True, area=(0, 0, 512, 1024))
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
|
||||
lines = ['[INFO] Start to read j2k main header (85).',
|
||||
'[INFO] Main header has been correctly decoded.',
|
||||
'[INFO] Setting decoding area to 0,0,1024,512',
|
||||
'[INFO] Header of tile 0 / 17 has been read.',
|
||||
'[INFO] Tile 1/18 has been decoded.',
|
||||
'[INFO] Image data has been updated with tile 1.',
|
||||
'[INFO] Header of tile 1 / 17 has been read.',
|
||||
'[INFO] Tile 2/18 has been decoded.',
|
||||
'[INFO] Image data has been updated with tile 2.',
|
||||
'[INFO] Stream reached its end !']
|
||||
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
92
glymur/test/test_codestream.py
Normal file
92
glymur/test/test_codestream.py
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
import os
|
||||
import struct
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import numpy as np
|
||||
import pkg_resources
|
||||
|
||||
from glymur import Jp2k
|
||||
import glymur
|
||||
|
||||
try:
|
||||
data_root = os.environ['OPJ_DATA_ROOT']
|
||||
except KeyError:
|
||||
data_root = None
|
||||
except:
|
||||
raise
|
||||
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
class TestCodestream(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.jp2file = pkg_resources.resource_filename(glymur.__name__,
|
||||
"data/nemo.jp2")
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_reserved_marker_segment(self):
|
||||
# Some marker segments were reserved in FCD15444-1. Since that
|
||||
# standard is old, some of them may have come into use.
|
||||
#
|
||||
# Let's inject a reserved marker segment into a file that
|
||||
# we know something about to make sure we can still parse it.
|
||||
filename = os.path.join(data_root, 'input/conformance/p0_01.j2k')
|
||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||
with open(filename, 'rb') as ifile:
|
||||
# Everything up until the first QCD marker.
|
||||
buffer = ifile.read(45)
|
||||
tfile.write(buffer)
|
||||
|
||||
# Write the new marker segment, 0xff6f = 65391
|
||||
buffer = struct.pack('>HHB', int(65391), int(3), int(0))
|
||||
tfile.write(buffer)
|
||||
|
||||
# Get the rest of the input file.
|
||||
buffer = ifile.read()
|
||||
tfile.write(buffer)
|
||||
tfile.flush()
|
||||
|
||||
j = Jp2k(tfile.name)
|
||||
c = j.get_codestream()
|
||||
|
||||
self.assertEqual(c.segment[2].id, '0xff6f')
|
||||
self.assertEqual(c.segment[2].length, 3)
|
||||
self.assertEqual(c.segment[2].data, b'\x00')
|
||||
|
||||
@unittest.skipIf(sys.hexversion < 0x03020000,
|
||||
"Uses features introduced in 3.2.")
|
||||
def test_unknown_marker_segment(self):
|
||||
# Let's inject a marker segment whose marker does not appear to
|
||||
# be valid. We still parse the file, but warn about the offending
|
||||
# marker.
|
||||
filename = os.path.join(data_root, 'input/conformance/p0_01.j2k')
|
||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||
with open(filename, 'rb') as ifile:
|
||||
# Everything up until the first QCD marker.
|
||||
buffer = ifile.read(45)
|
||||
tfile.write(buffer)
|
||||
|
||||
# Write the new marker segment, 0xff79 = 65401
|
||||
buffer = struct.pack('>HHB', int(65401), int(3), int(0))
|
||||
tfile.write(buffer)
|
||||
|
||||
# Get the rest of the input file.
|
||||
buffer = ifile.read()
|
||||
tfile.write(buffer)
|
||||
tfile.flush()
|
||||
|
||||
with self.assertWarns(UserWarning) as cw:
|
||||
j = Jp2k(tfile.name)
|
||||
c = j.get_codestream()
|
||||
|
||||
self.assertEqual(c.segment[2].id, '0xff79')
|
||||
self.assertEqual(c.segment[2].length, 3)
|
||||
self.assertEqual(c.segment[2].data, b'\x00')
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
602
glymur/test/test_jp2k.py
Normal file
602
glymur/test/test_jp2k.py
Normal file
|
|
@ -0,0 +1,602 @@
|
|||
import contextlib
|
||||
import ctypes
|
||||
import doctest
|
||||
import imp
|
||||
import os
|
||||
import re
|
||||
import struct
|
||||
import sys
|
||||
import tempfile
|
||||
import uuid
|
||||
import unittest
|
||||
if sys.hexversion <= 0x03030000:
|
||||
from mock import patch
|
||||
else:
|
||||
from unittest.mock import patch
|
||||
import warnings
|
||||
from xml.etree import cElementTree as ET
|
||||
|
||||
import numpy as np
|
||||
import pkg_resources
|
||||
|
||||
from glymur import Jp2k
|
||||
from glymur.lib import openjp2 as opj2
|
||||
import glymur
|
||||
|
||||
try:
|
||||
data_root = os.environ['OPJ_DATA_ROOT']
|
||||
except KeyError:
|
||||
data_root = None
|
||||
except:
|
||||
raise
|
||||
|
||||
|
||||
# Doc tests should be run as well.
|
||||
def load_tests(loader, tests, ignore):
|
||||
tests.addTests(doctest.DocTestSuite('glymur.jp2k'))
|
||||
return tests
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def chdir(dirname=None):
|
||||
curdir = os.getcwd()
|
||||
try:
|
||||
if dirname is not None:
|
||||
os.chdir(dirname)
|
||||
yield
|
||||
finally:
|
||||
os.chdir(curdir)
|
||||
|
||||
|
||||
class TestJp2k(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.jp2file = pkg_resources.resource_filename(glymur.__name__,
|
||||
"data/nemo.jp2")
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
@unittest.skipIf(sys.hexversion < 0x03020000,
|
||||
"Uses features introduced in 3.2.")
|
||||
def test_invalid_xml_box(self):
|
||||
# Should be able to recover from xml box with bad xml.
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
with open(self.jp2file, 'rb') as ifile:
|
||||
# Everything up until the jp2c box.
|
||||
buffer = ifile.read(77)
|
||||
tfile.write(buffer)
|
||||
|
||||
# Write the xml box with bad xml
|
||||
# Length = 28, id is 'xml '.
|
||||
buffer = struct.pack('>I4s', int(28), b'xml ')
|
||||
tfile.write(buffer)
|
||||
|
||||
buffer = '<test>this is a test'
|
||||
buffer = buffer.encode()
|
||||
tfile.write(buffer)
|
||||
|
||||
# Get the rest of the input file.
|
||||
buffer = ifile.read()
|
||||
tfile.write(buffer)
|
||||
tfile.flush()
|
||||
|
||||
with self.assertWarns(UserWarning) as cw:
|
||||
jp2k = Jp2k(tfile.name)
|
||||
|
||||
self.assertEqual(jp2k.box[3].id, 'xml ')
|
||||
self.assertEqual(jp2k.box[3].offset, 77)
|
||||
self.assertEqual(jp2k.box[3].length, 28)
|
||||
self.assertIsNone(jp2k.box[3].xml)
|
||||
|
||||
def test_bad_area_parameter(self):
|
||||
# Verify that we error out appropriately if given a bad area parameter.
|
||||
j = Jp2k(self.jp2file)
|
||||
with self.assertRaises(IOError):
|
||||
# Start corner must be >= 0
|
||||
d = j.read(area=(-1, -1, 1, 1))
|
||||
with self.assertRaises(IOError):
|
||||
# End corner must be > 0
|
||||
d = j.read(area=(10, 10, 0, 0))
|
||||
with self.assertRaises(IOError):
|
||||
# End corner must be >= start corner
|
||||
d = j.read(area=(10, 10, 8, 8))
|
||||
|
||||
def test_reduce_too_high(self):
|
||||
# Verify that we error out appropriately if not given a JPEG 2000 file.
|
||||
j = Jp2k(self.jp2file)
|
||||
with self.assertRaises(IOError):
|
||||
d = j.read(reduce=6)
|
||||
|
||||
def test_not_JPEG2000(self):
|
||||
# Verify that we error out appropriately if not given a JPEG 2000 file.
|
||||
filename = pkg_resources.resource_filename(glymur.__name__, "jp2k.py")
|
||||
with self.assertRaises(IOError):
|
||||
jp2k = Jp2k(filename)
|
||||
|
||||
def test_file_not_present(self):
|
||||
# Verify that we error out appropriately if not given an existing file
|
||||
# at all.
|
||||
if sys.hexversion < 0x03030000:
|
||||
error = OSError
|
||||
else:
|
||||
error = IOError
|
||||
with self.assertRaises(error):
|
||||
filename = 'this file does not actually exist on the file system.'
|
||||
jp2k = Jp2k(filename)
|
||||
|
||||
def test_nemo_tile(self):
|
||||
# Issue 134, trouble reading first nemo tile.
|
||||
j = Jp2k(self.jp2file)
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
tiledata = j.read(tile=0)
|
||||
subsetdata = j.read(area=(0, 0, 512, 512))
|
||||
np.testing.assert_array_equal(tiledata, subsetdata)
|
||||
|
||||
def test_jp2_boxes(self):
|
||||
# Verify the boxes of a JP2 file.
|
||||
jp2k = Jp2k(self.jp2file)
|
||||
|
||||
# top-level boxes
|
||||
self.assertEqual(len(jp2k.box), 4)
|
||||
|
||||
self.assertEqual(jp2k.box[0].id, 'jP ')
|
||||
self.assertEqual(jp2k.box[0].offset, 0)
|
||||
self.assertEqual(jp2k.box[0].length, 12)
|
||||
self.assertEqual(jp2k.box[0].longname, 'JPEG 2000 Signature')
|
||||
|
||||
self.assertEqual(jp2k.box[1].id, 'ftyp')
|
||||
self.assertEqual(jp2k.box[1].offset, 12)
|
||||
self.assertEqual(jp2k.box[1].length, 20)
|
||||
self.assertEqual(jp2k.box[1].longname, 'File Type')
|
||||
|
||||
self.assertEqual(jp2k.box[2].id, 'jp2h')
|
||||
self.assertEqual(jp2k.box[2].offset, 32)
|
||||
self.assertEqual(jp2k.box[2].length, 45)
|
||||
self.assertEqual(jp2k.box[2].longname, 'JP2 Header')
|
||||
|
||||
self.assertEqual(jp2k.box[3].id, 'jp2c')
|
||||
self.assertEqual(jp2k.box[3].offset, 77)
|
||||
self.assertEqual(jp2k.box[3].length, 1133427)
|
||||
|
||||
# jp2h super box
|
||||
self.assertEqual(len(jp2k.box[2].box), 2)
|
||||
|
||||
self.assertEqual(jp2k.box[2].box[0].id, 'ihdr')
|
||||
self.assertEqual(jp2k.box[2].box[0].offset, 40)
|
||||
self.assertEqual(jp2k.box[2].box[0].length, 22)
|
||||
self.assertEqual(jp2k.box[2].box[0].longname, 'Image Header')
|
||||
self.assertEqual(jp2k.box[2].box[0].height, 1456)
|
||||
self.assertEqual(jp2k.box[2].box[0].width, 2592)
|
||||
self.assertEqual(jp2k.box[2].box[0].num_components, 3)
|
||||
self.assertEqual(jp2k.box[2].box[0].bits_per_component, 8)
|
||||
self.assertEqual(jp2k.box[2].box[0].signed, False)
|
||||
self.assertEqual(jp2k.box[2].box[0].compression, 7)
|
||||
self.assertEqual(jp2k.box[2].box[0].cspace_unknown, False)
|
||||
self.assertEqual(jp2k.box[2].box[0].ip_provided, False)
|
||||
|
||||
self.assertEqual(jp2k.box[2].box[1].id, 'colr')
|
||||
self.assertEqual(jp2k.box[2].box[1].offset, 62)
|
||||
self.assertEqual(jp2k.box[2].box[1].length, 15)
|
||||
self.assertEqual(jp2k.box[2].box[1].longname, 'Colour Specification')
|
||||
self.assertEqual(jp2k.box[2].box[1].precedence, 0)
|
||||
self.assertEqual(jp2k.box[2].box[1].approximation, 0)
|
||||
self.assertEqual(jp2k.box[2].box[1].color_space, glymur.core.SRGB)
|
||||
self.assertIsNone(jp2k.box[2].box[1].icc_profile)
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_j2k_box(self):
|
||||
# Verify that a J2K file has no boxes.
|
||||
filename = os.path.join(data_root, 'input/conformance/p0_01.j2k')
|
||||
jp2k = Jp2k(filename)
|
||||
self.assertEqual(len(jp2k.box), 0)
|
||||
|
||||
def test_64bit_XL_field(self):
|
||||
# Verify that boxes with the XL field are properly read.
|
||||
# Don't have such a file on hand, so we create one. Copy our example
|
||||
# file, but making the codestream have a 64-bit XL field.
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
with open(self.jp2file, 'rb') as ifile:
|
||||
# Everything up until the jp2c box.
|
||||
buffer = ifile.read(77)
|
||||
tfile.write(buffer)
|
||||
|
||||
# The L field must be 1 in order to signal the presence of the
|
||||
# XL field. The actual length of the jp2c box increased by 8
|
||||
# (8 bytes for the XL field).
|
||||
L = 1
|
||||
T = b'jp2c'
|
||||
XL = 1133427 + 8
|
||||
buffer = struct.pack('>I4sQ', int(L), T, XL)
|
||||
tfile.write(buffer)
|
||||
|
||||
# Get the rest of the input file (minus the 8 bytes for L and
|
||||
# T.
|
||||
ifile.seek(8, 1)
|
||||
buffer = ifile.read()
|
||||
tfile.write(buffer)
|
||||
tfile.flush()
|
||||
|
||||
jp2k = Jp2k(tfile.name)
|
||||
|
||||
self.assertEqual(jp2k.box[3].id, 'jp2c')
|
||||
self.assertEqual(jp2k.box[3].offset, 77)
|
||||
self.assertEqual(jp2k.box[3].length, 1133427 + 8)
|
||||
|
||||
def test_L_is_zero(self):
|
||||
# Verify that boxes with the L field as zero are correctly read.
|
||||
# This should only happen in the last box of a JPEG 2000 file.
|
||||
# Our example image has its last box at byte 588458.
|
||||
baseline_jp2 = Jp2k(self.jp2file)
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
with open(self.jp2file, 'rb') as ifile:
|
||||
# Everything up until the jp2c box.
|
||||
buffer = ifile.read(588458)
|
||||
tfile.write(buffer)
|
||||
|
||||
L = 0
|
||||
T = b'uuid'
|
||||
buffer = struct.pack('>I4s', int(L), T)
|
||||
tfile.write(buffer)
|
||||
|
||||
# Get the rest of the input file (minus the 8 bytes for L and
|
||||
# T.
|
||||
ifile.seek(8, 1)
|
||||
buffer = ifile.read()
|
||||
tfile.write(buffer)
|
||||
tfile.flush()
|
||||
|
||||
new_jp2 = Jp2k(tfile.name)
|
||||
|
||||
# The top level boxes in each file should match.
|
||||
for j in range(len(baseline_jp2.box)):
|
||||
self.assertEqual(new_jp2.box[j].id,
|
||||
baseline_jp2.box[j].id)
|
||||
self.assertEqual(new_jp2.box[j].offset,
|
||||
baseline_jp2.box[j].offset)
|
||||
self.assertEqual(new_jp2.box[j].length,
|
||||
baseline_jp2.box[j].length)
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_read_differing_subsamples(self):
|
||||
# Verify that we error out appropriately if we use the read method
|
||||
# on an image with differing subsamples
|
||||
#
|
||||
# Issue 86.
|
||||
filename = os.path.join(data_root, 'input/conformance/p0_05.j2k')
|
||||
j = Jp2k(filename)
|
||||
with self.assertRaises(IOError):
|
||||
j.read()
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_empty_box_with_j2k(self):
|
||||
# Verify that the list of boxes in a J2C/J2K file is present, but
|
||||
# empty.
|
||||
filename = os.path.join(data_root, 'input/conformance/p0_05.j2k')
|
||||
j = Jp2k(filename)
|
||||
self.assertEqual(j.box, [])
|
||||
|
||||
def test_code_block_height_different_than_width(self):
|
||||
# Verify that we can set a code block size where height does not equal
|
||||
# width.
|
||||
data = np.zeros((128, 128), dtype=np.uint8)
|
||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
|
||||
# The code block dimensions are given as rows x columns.
|
||||
j.write(data, cbsize=(16, 32))
|
||||
|
||||
c = j.get_codestream()
|
||||
|
||||
# Code block size is reported as XY in the codestream.
|
||||
self.assertEqual(tuple(c.segment[2].SPcod[5:7]), (3, 2))
|
||||
|
||||
def test_negative_too_many_dimensions(self):
|
||||
# OpenJP2 only allows 2D or 3D images.
|
||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
with self.assertRaises(IOError) as ce:
|
||||
data = np.zeros((128, 128, 2, 2), dtype=np.uint8)
|
||||
j.write(data)
|
||||
|
||||
def test_unrecognized_jp2_colorspace(self):
|
||||
# We only allow RGB and GRAYSCALE.
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
with self.assertRaises(IOError) as ce:
|
||||
data = np.zeros((128, 128, 3), dtype=np.uint8)
|
||||
j.write(data, colorspace='cmyk')
|
||||
|
||||
def test_2D_rgb(self):
|
||||
# RGB must have at least 3 components.
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
with self.assertRaises(IOError) as ce:
|
||||
data = np.zeros((128, 128, 2), dtype=np.uint8)
|
||||
j.write(data, colorspace='rgb')
|
||||
|
||||
def test_colorspace_with_j2k(self):
|
||||
# Specifying a colorspace with J2K does not make sense.
|
||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
with self.assertRaises(IOError) as ce:
|
||||
data = np.zeros((128, 128, 3), dtype=np.uint8)
|
||||
j.write(data, colorspace='rgb')
|
||||
|
||||
def test_specify_rgb(self):
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
data = np.zeros((128, 128, 3), dtype=np.uint8)
|
||||
j.write(data, colorspace='rgb')
|
||||
self.assertEqual(j.box[2].box[1].color_space, glymur.core.SRGB)
|
||||
|
||||
def test_specify_gray(self):
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
data = np.zeros((128, 128), dtype=np.uint8)
|
||||
j.write(data, colorspace='gray')
|
||||
self.assertEqual(j.box[2].box[1].color_space,
|
||||
glymur.core.GREYSCALE)
|
||||
|
||||
def test_specify_grey(self):
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
data = np.zeros((128, 128), dtype=np.uint8)
|
||||
j.write(data, colorspace='grey')
|
||||
self.assertEqual(j.box[2].box[1].color_space,
|
||||
glymur.core.GREYSCALE)
|
||||
|
||||
def test_grey_with_extra_component(self):
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
data = np.zeros((128, 128, 2), dtype=np.uint8)
|
||||
j.write(data)
|
||||
self.assertEqual(j.box[2].box[0].height, 128)
|
||||
self.assertEqual(j.box[2].box[0].width, 128)
|
||||
self.assertEqual(j.box[2].box[0].num_components, 2)
|
||||
self.assertEqual(j.box[2].box[1].color_space,
|
||||
glymur.core.GREYSCALE)
|
||||
|
||||
def test_grey_with_two_extra_components(self):
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
data = np.zeros((128, 128, 3), dtype=np.uint8)
|
||||
j.write(data, colorspace='gray')
|
||||
self.assertEqual(j.box[2].box[0].height, 128)
|
||||
self.assertEqual(j.box[2].box[0].width, 128)
|
||||
self.assertEqual(j.box[2].box[0].num_components, 3)
|
||||
self.assertEqual(j.box[2].box[1].color_space,
|
||||
glymur.core.GREYSCALE)
|
||||
|
||||
def test_rgb_with_extra_component(self):
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
data = np.zeros((128, 128, 4), dtype=np.uint8)
|
||||
j.write(data)
|
||||
self.assertEqual(j.box[2].box[0].height, 128)
|
||||
self.assertEqual(j.box[2].box[0].width, 128)
|
||||
self.assertEqual(j.box[2].box[0].num_components, 4)
|
||||
self.assertEqual(j.box[2].box[1].color_space, glymur.core.SRGB)
|
||||
|
||||
def test_specify_ycc(self):
|
||||
# We don't support writing YCC at the moment.
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
with self.assertRaises(IOError) as ce:
|
||||
data = np.zeros((128, 128, 3), dtype=np.uint8)
|
||||
j.write(data, colorspace='ycc')
|
||||
|
||||
def test_uinf_ulst_url_boxes(self):
|
||||
# Verify that we can read UINF, ULST, and URL boxes. I don't have
|
||||
# easy access to such a file, and there's no such file in the
|
||||
# openjpeg repository, so I'll fake one.
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
with open(self.jp2file, 'rb') as ifile:
|
||||
# Everything up until the jp2c box.
|
||||
buffer = ifile.read(77)
|
||||
tfile.write(buffer)
|
||||
|
||||
# Write the UINF superbox
|
||||
# Length = 50, id is uinf.
|
||||
buffer = struct.pack('>I4s', int(50), b'uinf')
|
||||
tfile.write(buffer)
|
||||
|
||||
# Write the ULST box.
|
||||
# Length is 26, 1 UUID, hard code that UUID as zeros.
|
||||
buffer = struct.pack('>I4sHIIII', int(26), b'ulst', int(1),
|
||||
int(0), int(0), int(0), int(0))
|
||||
tfile.write(buffer)
|
||||
|
||||
# Write the URL box.
|
||||
# Length is 16, version is one byte, flag is 3 bytes, url
|
||||
# is the rest.
|
||||
buffer = struct.pack('>I4sBBBB',
|
||||
int(16), b'url ',
|
||||
int(0), int(0), int(0), int(0))
|
||||
tfile.write(buffer)
|
||||
buffer = struct.pack('>ssss', b'a', b'b', b'c', b'd')
|
||||
tfile.write(buffer)
|
||||
|
||||
# Get the rest of the input file.
|
||||
buffer = ifile.read()
|
||||
tfile.write(buffer)
|
||||
tfile.flush()
|
||||
|
||||
jp2k = Jp2k(tfile.name)
|
||||
|
||||
self.assertEqual(jp2k.box[3].id, 'uinf')
|
||||
self.assertEqual(jp2k.box[3].offset, 77)
|
||||
self.assertEqual(jp2k.box[3].length, 50)
|
||||
|
||||
self.assertEqual(jp2k.box[3].box[0].id, 'ulst')
|
||||
self.assertEqual(jp2k.box[3].box[0].offset, 85)
|
||||
self.assertEqual(jp2k.box[3].box[0].length, 26)
|
||||
ulst = []
|
||||
ulst.append(uuid.UUID('00000000-0000-0000-0000-000000000000'))
|
||||
self.assertEqual(jp2k.box[3].box[0].ulst, ulst)
|
||||
|
||||
self.assertEqual(jp2k.box[3].box[1].id, 'url ')
|
||||
self.assertEqual(jp2k.box[3].box[1].offset, 111)
|
||||
self.assertEqual(jp2k.box[3].box[1].length, 16)
|
||||
self.assertEqual(jp2k.box[3].box[1].version, 0)
|
||||
self.assertEqual(jp2k.box[3].box[1].flag, (0, 0, 0))
|
||||
self.assertEqual(jp2k.box[3].box[1].URL, 'abcd')
|
||||
|
||||
def test_xml_box_with_trailing_nulls(self):
|
||||
# ElementTree does not like trailing null chars after valid XML
|
||||
# text.
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
with open(self.jp2file, 'rb') as ifile:
|
||||
# Everything up until the jp2c box.
|
||||
buffer = ifile.read(77)
|
||||
tfile.write(buffer)
|
||||
|
||||
# Write the xml box
|
||||
# Length = 36, id is 'xml '.
|
||||
buffer = struct.pack('>I4s', int(36), b'xml ')
|
||||
tfile.write(buffer)
|
||||
|
||||
buffer = '<test>this is a test</test>' + chr(0)
|
||||
buffer = buffer.encode()
|
||||
tfile.write(buffer)
|
||||
|
||||
# Get the rest of the input file.
|
||||
buffer = ifile.read()
|
||||
tfile.write(buffer)
|
||||
tfile.flush()
|
||||
|
||||
jp2k = Jp2k(tfile.name)
|
||||
|
||||
self.assertEqual(jp2k.box[3].id, 'xml ')
|
||||
self.assertEqual(jp2k.box[3].offset, 77)
|
||||
self.assertEqual(jp2k.box[3].length, 36)
|
||||
|
||||
def test_asoc_label_box(self):
|
||||
# Construct a fake file with an asoc and a label box, as
|
||||
# OpenJPEG doesn't have such a file.
|
||||
data = Jp2k(self.jp2file).read(reduce=3)
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
j.write(data)
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile2:
|
||||
|
||||
# Offset of the codestream is where we start.
|
||||
buffer = tfile.read(77)
|
||||
tfile2.write(buffer)
|
||||
|
||||
# read the rest of the file, it's the codestream.
|
||||
codestream = tfile.read()
|
||||
|
||||
# Write the asoc superbox.
|
||||
# Length = 36, id is 'asoc'.
|
||||
buffer = struct.pack('>I4s', int(56), b'asoc')
|
||||
tfile2.write(buffer)
|
||||
|
||||
# Write the contained label box
|
||||
buffer = struct.pack('>I4s', int(13), b'lbl ')
|
||||
tfile2.write(buffer)
|
||||
tfile2.write('label'.encode())
|
||||
|
||||
# Write the xml box
|
||||
# Length = 36, id is 'xml '.
|
||||
buffer = struct.pack('>I4s', int(35), b'xml ')
|
||||
tfile2.write(buffer)
|
||||
|
||||
buffer = '<test>this is a test</test>'
|
||||
buffer = buffer.encode()
|
||||
tfile2.write(buffer)
|
||||
|
||||
# Now append the codestream.
|
||||
tfile2.write(codestream)
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
jasoc = Jp2k(tfile2.name)
|
||||
self.assertEqual(jasoc.box[3].id, 'asoc')
|
||||
self.assertEqual(jasoc.box[3].box[0].id, 'lbl ')
|
||||
self.assertEqual(jasoc.box[3].box[0].label, 'label')
|
||||
self.assertEqual(jasoc.box[3].box[1].id, 'xml ')
|
||||
|
||||
def test_openjpeg_library_message(self):
|
||||
# Verify the error message produced by the openjpeg library.
|
||||
# This will confirm that the error callback mechanism is working.
|
||||
with open(self.jp2file, 'rb') as fp:
|
||||
data = fp.read()
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
tfile.write(data[0:129])
|
||||
|
||||
# Make the DY bytes of the SIZ segment zero. That means that
|
||||
# a subsampling factor is zero, which is illegal.
|
||||
tfile.write(b'\x00')
|
||||
tfile.write(data[130:132])
|
||||
tfile.write(b'\x00')
|
||||
tfile.write(data[134:136])
|
||||
tfile.write(b'\x00')
|
||||
|
||||
tfile.write(data[136:])
|
||||
tfile.flush()
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
j = Jp2k(tfile.name)
|
||||
regexp = re.compile(r'''OpenJPEG\slibrary\serror:\s+
|
||||
Invalid\svalues\sfor\scomp\s=\s0\s+
|
||||
:\sdx=1\sdy=0''', re.VERBOSE)
|
||||
if sys.hexversion < 0x03020000:
|
||||
with self.assertRaisesRegexp(IOError, regexp) as ce:
|
||||
d = j.read(reduce=3)
|
||||
else:
|
||||
with self.assertRaisesRegex(IOError, regexp) as ce:
|
||||
d = j.read(reduce=3)
|
||||
|
||||
@unittest.skipIf(sys.hexversion < 0x03020000,
|
||||
"Uses features introduced in 3.2.")
|
||||
def test_config_file_via_environ(self):
|
||||
"""Verify that we can read a configuration file set via environ var."""
|
||||
with tempfile.TemporaryDirectory() as tdir:
|
||||
filename = os.path.join(tdir, 'glymurrc')
|
||||
with open(filename, 'wb') as tfile:
|
||||
tfile.write('[library]\n'.encode())
|
||||
line = 'openjp2: {0}\n'.format(glymur._OPENJP2._name)
|
||||
tfile.write(line.encode())
|
||||
tfile.flush()
|
||||
with patch.dict('os.environ', {'GLYMURCONFIGDIR': tdir}):
|
||||
imp.reload(glymur)
|
||||
j = Jp2k(self.jp2file)
|
||||
|
||||
@unittest.skipIf(sys.hexversion < 0x03020000,
|
||||
"Uses features introduced in 3.2.")
|
||||
def test_config_file_via_environ_is_wrong(self):
|
||||
# A non-existant library location should be rejected.
|
||||
with tempfile.TemporaryDirectory() as tdir:
|
||||
fname = os.path.join(tdir, 'glymurrc')
|
||||
with open(fname, 'w') as fp:
|
||||
with tempfile.NamedTemporaryFile(suffix='.dylib') as tfile:
|
||||
fp.write('[library]\n')
|
||||
fp.write('openjp2: {0}.not.there\n'.format(tfile.name))
|
||||
fp.flush()
|
||||
with patch.dict('os.environ', {'GLYMURCONFIGDIR': tdir}):
|
||||
# Misconfigured new configuration file should
|
||||
# be rejected.
|
||||
with self.assertRaises(ImportError) as ce:
|
||||
imp.reload(glymur)
|
||||
|
||||
@unittest.skipIf(sys.hexversion < 0x03020000,
|
||||
"Uses features introduced in 3.2.")
|
||||
def test_missing_config_file_via_environ(self):
|
||||
# Verify that we error out properly if the configuration file
|
||||
# specified via environment variable is not found.
|
||||
with tempfile.TemporaryDirectory() as tdir:
|
||||
with patch.dict('os.environ', {'GLYMURCONFIGDIR': tdir}):
|
||||
# Misconfigured new configuration file should
|
||||
# be rejected.
|
||||
with self.assertRaises(ImportError) as ce:
|
||||
imp.reload(glymur)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
7364
glymur/test/test_opj_suite.py
Normal file
7364
glymur/test/test_opj_suite.py
Normal file
File diff suppressed because it is too large
Load diff
154
glymur/test/test_opj_suite_neg.py
Normal file
154
glymur/test/test_opj_suite_neg.py
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
"""
|
||||
The tests here do not correspond directly to the OpenJPEG test suite, but
|
||||
seem like logical negative tests to add.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ..lib import openjp2 as opj2
|
||||
|
||||
# Need some combination of matplotlib, PIL, or scikits-image for reading
|
||||
# other image formats.
|
||||
no_read_backend = False
|
||||
msg = "Either scikit-image with the freeimage backend or matplotlib "
|
||||
msg += "with the PIL backend must be available in order to run the "
|
||||
msg += "tests in this suite."
|
||||
no_read_backend_msg = msg
|
||||
try:
|
||||
import skimage.io
|
||||
try:
|
||||
skimage.io.use_plugin('freeimage')
|
||||
from skimage.io import imread
|
||||
except ImportError:
|
||||
try:
|
||||
skimage.io.use_plugin('PIL')
|
||||
from skimage.io import imread
|
||||
except ImportError:
|
||||
raise
|
||||
except ImportError:
|
||||
try:
|
||||
from PIL import Image
|
||||
from matplotlib.pyplot import imread
|
||||
except ImportError:
|
||||
no_read_backend = True
|
||||
|
||||
from glymur import Jp2k
|
||||
import glymur
|
||||
|
||||
try:
|
||||
data_root = os.environ['OPJ_DATA_ROOT']
|
||||
except KeyError:
|
||||
data_root = None
|
||||
except:
|
||||
raise
|
||||
|
||||
|
||||
def read_image(infile):
|
||||
# PIL issues warnings which we do not care about, so suppress them.
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
data = imread(infile)
|
||||
return data
|
||||
|
||||
|
||||
@unittest.skipIf(no_read_backend, no_read_backend_msg)
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
class TestSuiteNegative(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_negative_psnr_with_cratios(self):
|
||||
# Using psnr with cratios options is not allowed.
|
||||
# Not an OpenJPEG test, but close.
|
||||
infile = os.path.join(data_root, 'input/nonregression/Bretagne1.ppm')
|
||||
data = read_image(infile)
|
||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
with self.assertRaises(RuntimeError):
|
||||
j.write(data, psnr=[30, 35, 40], cratios=[2, 3, 4])
|
||||
|
||||
@unittest.skipIf(sys.hexversion < 0x03020000,
|
||||
"Uses features introduced in 3.2.")
|
||||
def test_NR_MarkerIsNotCompliant_j2k_dump(self):
|
||||
# SOT marker gives bad offset.
|
||||
relpath = 'input/nonregression/MarkerIsNotCompliant.j2k'
|
||||
jfile = os.path.join(data_root, relpath)
|
||||
jp2k = Jp2k(jfile)
|
||||
with self.assertWarns(UserWarning) as cw:
|
||||
c = jp2k.get_codestream(header_only=False)
|
||||
|
||||
# Verify that the last segment returned in the codestream is SOD,
|
||||
# not EOC. Codestream parsing should stop when we try to jump to
|
||||
# the end of SOT.
|
||||
self.assertEqual(c.segment[-1].id, 'SOD')
|
||||
|
||||
@unittest.skipIf(sys.hexversion < 0x03020000,
|
||||
"Uses features introduced in 3.2.")
|
||||
def test_NR_illegalcolortransform_dump(self):
|
||||
# SOT marker gives bad offset.
|
||||
relpath = 'input/nonregression/illegalcolortransform.j2k'
|
||||
jfile = os.path.join(data_root, relpath)
|
||||
jp2k = Jp2k(jfile)
|
||||
with self.assertWarns(UserWarning) as cw:
|
||||
c = jp2k.get_codestream(header_only=False)
|
||||
|
||||
# Verify that the last segment returned in the codestream is SOD,
|
||||
# not EOC. Codestream parsing should stop when we try to jump to
|
||||
# the end of SOT.
|
||||
self.assertEqual(c.segment[-1].id, 'SOD')
|
||||
|
||||
@unittest.skipIf(sys.hexversion < 0x03020000,
|
||||
"Uses features introduced in 3.2.")
|
||||
def test_NR_Cannotreaddatawithnosizeknown_j2k(self):
|
||||
# SOT marker gives bad offset.
|
||||
relpath = 'input/nonregression/Cannotreaddatawithnosizeknown.j2k'
|
||||
jfile = os.path.join(data_root, relpath)
|
||||
jp2k = Jp2k(jfile)
|
||||
with self.assertWarns(UserWarning) as cw:
|
||||
c = jp2k.get_codestream(header_only=False)
|
||||
|
||||
# Verify that the last segment returned in the codestream is SOD,
|
||||
# not EOC. Codestream parsing should stop when we try to jump to
|
||||
# the end of SOT.
|
||||
self.assertEqual(c.segment[-1].id, 'SOD')
|
||||
|
||||
def test_code_block_dimensions(self):
|
||||
# opj_compress doesn't allow the dimensions of a codeblock
|
||||
# to be too small or too big, so neither will we.
|
||||
data = np.zeros((256, 256), dtype=np.uint8)
|
||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
|
||||
# opj_compress doesn't allow code block area to exceed 4096.
|
||||
with self.assertRaises(RuntimeError) as cr:
|
||||
j.write(data, cbsize=(256, 256))
|
||||
|
||||
# opj_compress doesn't allow either dimension to be less than 4.
|
||||
with self.assertRaises(RuntimeError) as cr:
|
||||
j.write(data, cbsize=(2048, 2))
|
||||
with self.assertRaises(RuntimeError) as cr:
|
||||
j.write(data, cbsize=(2, 2048))
|
||||
|
||||
@unittest.skipIf(sys.hexversion < 0x03020000,
|
||||
"Uses features introduced in 3.2.")
|
||||
def test_exceeded_box(self):
|
||||
# Verify that a warning is issued if we read past the end of a box
|
||||
# This file has a palette (pclr) box whose length is impossibly
|
||||
# short.
|
||||
infile = os.path.join(data_root,
|
||||
'input/nonregression/mem-b2ace68c-1381.jp2')
|
||||
with self.assertWarns(UserWarning) as cw:
|
||||
j = Jp2k(infile)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
809
glymur/test/test_opj_suite_write.py
Normal file
809
glymur/test/test_opj_suite_write.py
Normal file
|
|
@ -0,0 +1,809 @@
|
|||
"""
|
||||
The tests defined here roughly correspond to what is in the OpenJPEG test
|
||||
suite.
|
||||
"""
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ..lib import openjp2 as opj2
|
||||
|
||||
# Need some combination of matplotlib, PIL, or scikits-image for reading
|
||||
# other image formats.
|
||||
no_read_backend = False
|
||||
msg = "Either scikit-image with the freeimage backend or matplotlib "
|
||||
msg += "with the PIL backend must be available in order to run the "
|
||||
msg += "tests in this suite."
|
||||
no_read_backend_msg = msg
|
||||
try:
|
||||
import skimage.io
|
||||
try:
|
||||
skimage.io.use_plugin('freeimage')
|
||||
from skimage.io import imread
|
||||
except ImportError:
|
||||
try:
|
||||
skimage.io.use_plugin('PIL')
|
||||
from skimage.io import imread
|
||||
except ImportError:
|
||||
raise
|
||||
except ImportError:
|
||||
try:
|
||||
from PIL import Image
|
||||
from matplotlib.pyplot import imread
|
||||
except ImportError:
|
||||
no_read_backend = True
|
||||
|
||||
from glymur import Jp2k
|
||||
import glymur
|
||||
|
||||
try:
|
||||
data_root = os.environ['OPJ_DATA_ROOT']
|
||||
except KeyError:
|
||||
data_root = None
|
||||
except:
|
||||
raise
|
||||
|
||||
|
||||
def read_image(infile):
|
||||
# PIL issues warnings which we do not care about, so suppress them.
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
data = imread(infile)
|
||||
return data
|
||||
|
||||
|
||||
@unittest.skipIf(no_read_backend, no_read_backend_msg)
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
class TestSuiteWrite(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_NR_ENC_Bretagne1_ppm_1_encode(self):
|
||||
# NR-ENC-Bretagne1.ppm-1-encode
|
||||
infile = os.path.join(data_root, 'input/nonregression/Bretagne1.ppm')
|
||||
data = read_image(infile)
|
||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
j.write(data, cratios=[200, 100, 50])
|
||||
|
||||
# Should be three layers.
|
||||
c = j.get_codestream()
|
||||
|
||||
# SIZ: Image and tile size
|
||||
# Profile: "0" means profile 2
|
||||
self.assertEqual(c.segment[1].Rsiz, 0)
|
||||
# Reference grid size
|
||||
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz),
|
||||
(640, 480))
|
||||
# Reference grid offset
|
||||
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), (0, 0))
|
||||
# Tile size
|
||||
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
|
||||
(640, 480))
|
||||
# Tile offset
|
||||
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
|
||||
(0, 0))
|
||||
# bitdepth
|
||||
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
|
||||
# signed
|
||||
self.assertEqual(c.segment[1]._signed, (False, False, False))
|
||||
# subsampling
|
||||
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)),
|
||||
[(1, 1)] * 3)
|
||||
|
||||
# COD: Coding style default
|
||||
self.assertFalse(c.segment[2].Scod & 2) # no sop
|
||||
self.assertFalse(c.segment[2].Scod & 4) # no eph
|
||||
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
|
||||
self.assertEqual(c.segment[2]._layers, 3) # layers = 3
|
||||
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
|
||||
self.assertEqual(c.segment[2].SPcod[4], 5) # levels
|
||||
self.assertEqual(tuple(c.segment[2]._code_block_size),
|
||||
(64, 64)) # cblksz
|
||||
# Selective arithmetic coding bypass
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
|
||||
# Reset context probabilities
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x02)
|
||||
# Termination on each coding pass
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
|
||||
# Vertically causal context
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
|
||||
# Predictable termination
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
|
||||
# Segmentation symbols
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
|
||||
self.assertEqual(c.segment[2].SPcod[8],
|
||||
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
|
||||
self.assertEqual(len(c.segment[2].SPcod), 9)
|
||||
|
||||
def test_NR_ENC_Bretagne1_ppm_2_encode(self):
|
||||
# NR-ENC-Bretagne1.ppm-2-encode
|
||||
infile = os.path.join(data_root, 'input/nonregression/Bretagne1.ppm')
|
||||
data = read_image(infile)
|
||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
j.write(data, psnr=[30, 35, 40], numres=2)
|
||||
|
||||
# Should be three layers.
|
||||
c = j.get_codestream()
|
||||
|
||||
# SIZ: Image and tile size
|
||||
# Profile: "0" means profile 2
|
||||
self.assertEqual(c.segment[1].Rsiz, 0)
|
||||
# Reference grid size
|
||||
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz),
|
||||
(640, 480))
|
||||
# Reference grid offset
|
||||
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), (0, 0))
|
||||
# Tile size
|
||||
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
|
||||
(640, 480))
|
||||
# Tile offset
|
||||
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
|
||||
(0, 0))
|
||||
# bitdepth
|
||||
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
|
||||
# signed
|
||||
self.assertEqual(c.segment[1]._signed, (False, False, False))
|
||||
# subsampling
|
||||
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)),
|
||||
[(1, 1)] * 3)
|
||||
|
||||
# COD: Coding style default
|
||||
self.assertFalse(c.segment[2].Scod & 2) # no sop
|
||||
self.assertFalse(c.segment[2].Scod & 4) # no eph
|
||||
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
|
||||
self.assertEqual(c.segment[2]._layers, 3) # layers = 3
|
||||
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
|
||||
self.assertEqual(c.segment[2].SPcod[4], 1) # levels
|
||||
self.assertEqual(tuple(c.segment[2]._code_block_size),
|
||||
(64, 64)) # cblksz
|
||||
# Selective arithmetic coding bypass
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
|
||||
# Reset context probabilities
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x02)
|
||||
# Termination on each coding pass
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
|
||||
# Vertically causal context
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
|
||||
# Predictable termination
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
|
||||
# Segmentation symbols
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
|
||||
self.assertEqual(c.segment[2].SPcod[8],
|
||||
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
|
||||
self.assertEqual(len(c.segment[2].SPcod), 9)
|
||||
|
||||
def test_NR_ENC_Bretagne1_ppm_3_encode(self):
|
||||
# NR-ENC-Bretagne1.ppm-3-encode
|
||||
infile = os.path.join(data_root, 'input/nonregression/Bretagne1.ppm')
|
||||
data = read_image(infile)
|
||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
j.write(data, psnr=[30, 35, 40], cbsize=(16, 16),
|
||||
psizes=[(64, 64)])
|
||||
|
||||
# Should be three layers.
|
||||
c = j.get_codestream()
|
||||
|
||||
# SIZ: Image and tile size
|
||||
# Profile: "0" means profile 2
|
||||
self.assertEqual(c.segment[1].Rsiz, 0)
|
||||
# Reference grid size
|
||||
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz),
|
||||
(640, 480))
|
||||
# Reference grid offset
|
||||
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), (0, 0))
|
||||
# Tile size
|
||||
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
|
||||
(640, 480))
|
||||
# Tile offset
|
||||
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
|
||||
(0, 0))
|
||||
# bitdepth
|
||||
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
|
||||
# signed
|
||||
self.assertEqual(c.segment[1]._signed, (False, False, False))
|
||||
# subsampling
|
||||
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)),
|
||||
[(1, 1)] * 3)
|
||||
|
||||
# COD: Coding style default
|
||||
self.assertFalse(c.segment[2].Scod & 2) # no sop
|
||||
self.assertFalse(c.segment[2].Scod & 4) # no eph
|
||||
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
|
||||
self.assertEqual(c.segment[2]._layers, 3) # layers = 3
|
||||
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
|
||||
self.assertEqual(c.segment[2].SPcod[4], 5) # levels
|
||||
self.assertEqual(tuple(c.segment[2]._code_block_size),
|
||||
(16, 16)) # cblksz
|
||||
# Selective arithmetic coding bypass
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
|
||||
# Reset context probabilities
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x02)
|
||||
# Termination on each coding pass
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
|
||||
# Vertically causal context
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
|
||||
# Predictable termination
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
|
||||
# Segmentation symbols
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
|
||||
self.assertEqual(c.segment[2].SPcod[8],
|
||||
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
|
||||
self.assertEqual(c.segment[2]._precinct_size,
|
||||
[(2, 2), (4, 4), (8, 8), (16, 16), (32, 32),
|
||||
(64, 64)])
|
||||
|
||||
def test_NR_ENC_Bretagne2_ppm_4_encode(self):
|
||||
infile = os.path.join(data_root, 'input/nonregression/Bretagne2.ppm')
|
||||
data = read_image(infile)
|
||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
j.write(data,
|
||||
psizes=[(128, 128)] * 3,
|
||||
cratios=[100, 20, 2],
|
||||
tilesize=(480, 640),
|
||||
cbsize=(32, 32))
|
||||
|
||||
# Should be three layers.
|
||||
c = j.get_codestream()
|
||||
|
||||
# SIZ: Image and tile size
|
||||
# Profile: "0" means profile 2
|
||||
self.assertEqual(c.segment[1].Rsiz, 0)
|
||||
# Reference grid size
|
||||
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz),
|
||||
(data.shape[1], data.shape[0]))
|
||||
# Reference grid offset
|
||||
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), (0, 0))
|
||||
# Tile size. Reported as XY, not RC.
|
||||
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
|
||||
(640, 480))
|
||||
# Tile offset
|
||||
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
|
||||
(0, 0))
|
||||
# bitdepth
|
||||
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
|
||||
# signed
|
||||
self.assertEqual(c.segment[1]._signed, (False, False, False))
|
||||
# subsampling
|
||||
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)),
|
||||
[(1, 1)] * 3)
|
||||
|
||||
# COD: Coding style default
|
||||
self.assertFalse(c.segment[2].Scod & 2) # no sop
|
||||
self.assertFalse(c.segment[2].Scod & 4) # no eph
|
||||
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
|
||||
self.assertEqual(c.segment[2]._layers, 3) # layers = 3
|
||||
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
|
||||
self.assertEqual(c.segment[2].SPcod[4], 5) # levels
|
||||
self.assertEqual(tuple(c.segment[2]._code_block_size),
|
||||
(32, 32)) # cblksz
|
||||
# Selective arithmetic coding bypass
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
|
||||
# Reset context probabilities
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x02)
|
||||
# Termination on each coding pass
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
|
||||
# Vertically causal context
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
|
||||
# Predictable termination
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
|
||||
# Segmentation symbols
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
|
||||
self.assertEqual(c.segment[2].SPcod[8],
|
||||
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
|
||||
self.assertEqual(c.segment[2]._precinct_size,
|
||||
[(16, 16), (32, 32), (64, 64)] + [(128, 128)] * 3)
|
||||
|
||||
def test_NR_ENC_Bretagne2_ppm_5_encode(self):
|
||||
# NR-ENC-Bretagne2.ppm-4-encode
|
||||
infile = os.path.join(data_root, 'input/nonregression/Bretagne2.ppm')
|
||||
data = read_image(infile)
|
||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
j.write(data, tilesize=(127, 127), prog="PCRL")
|
||||
|
||||
c = j.get_codestream()
|
||||
|
||||
# SIZ: Image and tile size
|
||||
# Profile: "0" means profile 2
|
||||
self.assertEqual(c.segment[1].Rsiz, 0)
|
||||
# Reference grid size
|
||||
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz),
|
||||
(data.shape[1], data.shape[0]))
|
||||
# Reference grid offset
|
||||
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), (0, 0))
|
||||
# Tile size
|
||||
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
|
||||
(127, 127))
|
||||
# Tile offset
|
||||
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
|
||||
(0, 0))
|
||||
# bitdepth
|
||||
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
|
||||
# signed
|
||||
self.assertEqual(c.segment[1]._signed, (False, False, False))
|
||||
# subsampling
|
||||
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)),
|
||||
[(1, 1)] * 3)
|
||||
|
||||
# COD: Coding style default
|
||||
self.assertFalse(c.segment[2].Scod & 2) # no sop
|
||||
self.assertFalse(c.segment[2].Scod & 4) # no eph
|
||||
self.assertEqual(c.segment[2].SPcod[0], glymur.core.PCRL)
|
||||
self.assertEqual(c.segment[2]._layers, 1) # layers = 1
|
||||
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
|
||||
self.assertEqual(c.segment[2].SPcod[4], 5) # levels
|
||||
self.assertEqual(tuple(c.segment[2]._code_block_size),
|
||||
(64, 64)) # cblksz
|
||||
# Selective arithmetic coding bypass
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
|
||||
# Reset context probabilities
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x02)
|
||||
# Termination on each coding pass
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
|
||||
# Vertically causal context
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
|
||||
# Predictable termination
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
|
||||
# Segmentation symbols
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
|
||||
self.assertEqual(c.segment[2].SPcod[8],
|
||||
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
|
||||
self.assertEqual(len(c.segment[2].SPcod), 9)
|
||||
|
||||
def test_NR_ENC_Bretagne2_ppm_6_encode(self):
|
||||
# NR-ENC-Bretagne2.ppm-6-encode
|
||||
infile = os.path.join(data_root, 'input/nonregression/Bretagne2.ppm')
|
||||
data = read_image(infile)
|
||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
j.write(data, subsam=(2, 2), sop=True)
|
||||
|
||||
c = j.get_codestream(header_only=False)
|
||||
|
||||
# SIZ: Image and tile size
|
||||
# Profile: "0" means profile 2
|
||||
self.assertEqual(c.segment[1].Rsiz, 0)
|
||||
# Reference grid size
|
||||
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz),
|
||||
(5183, 3887))
|
||||
# Reference grid offset
|
||||
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), (0, 0))
|
||||
# Tile size
|
||||
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
|
||||
(5183, 3887))
|
||||
# Tile offset
|
||||
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
|
||||
(0, 0))
|
||||
# bitdepth
|
||||
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
|
||||
# signed
|
||||
self.assertEqual(c.segment[1]._signed, (False, False, False))
|
||||
# subsampling
|
||||
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)),
|
||||
[(2, 2)] * 3)
|
||||
|
||||
# COD: Coding style default
|
||||
self.assertTrue(c.segment[2].Scod & 2) # sop
|
||||
self.assertFalse(c.segment[2].Scod & 4) # no eph
|
||||
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
|
||||
self.assertEqual(c.segment[2]._layers, 1) # layers = 1
|
||||
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
|
||||
self.assertEqual(c.segment[2].SPcod[4], 5) # levels
|
||||
self.assertEqual(tuple(c.segment[2]._code_block_size),
|
||||
(64, 64)) # cblksz
|
||||
# Selective arithmetic coding bypass
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
|
||||
# Reset context probabilities
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x02)
|
||||
# Termination on each coding pass
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
|
||||
# Vertically causal context
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
|
||||
# Predictable termination
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
|
||||
# Segmentation symbols
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
|
||||
self.assertEqual(c.segment[2].SPcod[8],
|
||||
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
|
||||
self.assertEqual(len(c.segment[2].SPcod), 9)
|
||||
|
||||
# 18 SOP segments.
|
||||
nsops = [x.Nsop for x in c.segment if x.id == 'SOP']
|
||||
self.assertEqual(nsops, list(range(18)))
|
||||
|
||||
def test_NR_ENC_Bretagne2_ppm_7_encode(self):
|
||||
infile = os.path.join(data_root, 'input/nonregression/Bretagne2.ppm')
|
||||
data = read_image(infile)
|
||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
j.write(data, modesw=38, eph=True)
|
||||
|
||||
c = j.get_codestream(header_only=False)
|
||||
|
||||
# SIZ: Image and tile size
|
||||
# Profile: "0" means profile 2
|
||||
self.assertEqual(c.segment[1].Rsiz, 0)
|
||||
# Reference grid size
|
||||
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz),
|
||||
(2592, 1944))
|
||||
# Reference grid offset
|
||||
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), (0, 0))
|
||||
# Tile size
|
||||
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
|
||||
(2592, 1944))
|
||||
# Tile offset
|
||||
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
|
||||
(0, 0))
|
||||
# bitdepth
|
||||
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
|
||||
# signed
|
||||
self.assertEqual(c.segment[1]._signed, (False, False, False))
|
||||
# subsampling
|
||||
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)),
|
||||
[(1, 1)] * 3)
|
||||
|
||||
# COD: Coding style default
|
||||
self.assertFalse(c.segment[2].Scod & 2) # no sop
|
||||
self.assertTrue(c.segment[2].Scod & 4) # eph
|
||||
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
|
||||
self.assertEqual(c.segment[2]._layers, 1) # layers = 1
|
||||
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
|
||||
self.assertEqual(c.segment[2].SPcod[4], 5) # levels
|
||||
self.assertEqual(tuple(c.segment[2]._code_block_size),
|
||||
(64, 64)) # cblksz
|
||||
# Selective arithmetic coding BYPASS
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
|
||||
# RESET context probabilities (RESET)
|
||||
self.assertTrue(c.segment[2].SPcod[7] & 0x02)
|
||||
# Termination on each coding pass, RESTART(TERMALL)
|
||||
self.assertTrue(c.segment[2].SPcod[7] & 0x04)
|
||||
# Vertically causal context (VSC)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
|
||||
# Predictable termination, ERTERM(SEGTERM)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
|
||||
# Segmentation symbols, SEGMARK(SEGSYSM)
|
||||
self.assertTrue(c.segment[2].SPcod[7] & 0x0020)
|
||||
self.assertEqual(c.segment[2].SPcod[8],
|
||||
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
|
||||
self.assertEqual(len(c.segment[2].SPcod), 9)
|
||||
|
||||
# 18 EPH segments.
|
||||
ephs = [x for x in c.segment if x.id == 'EPH']
|
||||
self.assertEqual(len(ephs), 18)
|
||||
|
||||
def test_NR_ENC_Bretagne2_ppm_8_encode(self):
|
||||
infile = os.path.join(data_root, 'input/nonregression/Bretagne2.ppm')
|
||||
data = read_image(infile)
|
||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
j.write(data, grid_offset=[300, 150], cratios=[800])
|
||||
|
||||
c = j.get_codestream(header_only=False)
|
||||
|
||||
# SIZ: Image and tile size
|
||||
# Profile: "0" means profile 2
|
||||
self.assertEqual(c.segment[1].Rsiz, 0)
|
||||
# Reference grid size
|
||||
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz),
|
||||
(2742, 2244))
|
||||
# Reference grid offset
|
||||
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz),
|
||||
(150, 300))
|
||||
# Tile size
|
||||
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
|
||||
(2742, 2244))
|
||||
# Tile offset
|
||||
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
|
||||
(0, 0))
|
||||
# bitdepth
|
||||
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
|
||||
# signed
|
||||
self.assertEqual(c.segment[1]._signed, (False, False, False))
|
||||
# subsampling
|
||||
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)),
|
||||
[(1, 1)] * 3)
|
||||
|
||||
# COD: Coding style default
|
||||
self.assertFalse(c.segment[2].Scod & 2) # no sop
|
||||
self.assertFalse(c.segment[2].Scod & 4) # no eph
|
||||
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
|
||||
self.assertEqual(c.segment[2]._layers, 1) # layers = 1
|
||||
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
|
||||
self.assertEqual(c.segment[2].SPcod[4], 5) # levels
|
||||
self.assertEqual(tuple(c.segment[2]._code_block_size),
|
||||
(64, 64)) # cblksz
|
||||
# Selective arithmetic coding BYPASS
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
|
||||
# RESET context probabilities (RESET)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x02)
|
||||
# Termination on each coding pass, RESTART(TERMALL)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
|
||||
# Vertically causal context (VSC)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
|
||||
# Predictable termination, ERTERM(SEGTERM)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
|
||||
# Segmentation symbols, SEGMARK(SEGSYSM)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
|
||||
self.assertEqual(c.segment[2].SPcod[8],
|
||||
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
|
||||
self.assertEqual(len(c.segment[2].SPcod), 9)
|
||||
|
||||
def test_NR_ENC_Cevennes1_bmp_9_encode(self):
|
||||
infile = os.path.join(data_root, 'input/nonregression/Cevennes1.bmp')
|
||||
data = read_image(infile)
|
||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
j.write(data, cratios=[800])
|
||||
|
||||
c = j.get_codestream(header_only=False)
|
||||
|
||||
# SIZ: Image and tile size
|
||||
# Profile: "0" means profile 2
|
||||
self.assertEqual(c.segment[1].Rsiz, 0)
|
||||
# Reference grid size
|
||||
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz),
|
||||
(2592, 1944))
|
||||
# Reference grid offset
|
||||
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), (0, 0))
|
||||
# Tile size
|
||||
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
|
||||
(2592, 1944))
|
||||
# Tile offset
|
||||
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
|
||||
(0, 0))
|
||||
# bitdepth
|
||||
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
|
||||
# signed
|
||||
self.assertEqual(c.segment[1]._signed, (False, False, False))
|
||||
# subsampling
|
||||
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)),
|
||||
[(1, 1)] * 3)
|
||||
|
||||
# COD: Coding style default
|
||||
self.assertFalse(c.segment[2].Scod & 2) # no sop
|
||||
self.assertFalse(c.segment[2].Scod & 4) # no eph
|
||||
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
|
||||
self.assertEqual(c.segment[2]._layers, 1) # layers = 1
|
||||
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
|
||||
self.assertEqual(c.segment[2].SPcod[4], 5) # levels
|
||||
self.assertEqual(tuple(c.segment[2]._code_block_size),
|
||||
(64, 64)) # cblksz
|
||||
# Selective arithmetic coding BYPASS
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
|
||||
# RESET context probabilities (RESET)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x02)
|
||||
# Termination on each coding pass, RESTART(TERMALL)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
|
||||
# Vertically causal context (VSC)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
|
||||
# Predictable termination, ERTERM(SEGTERM)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
|
||||
# Segmentation symbols, SEGMARK(SEGSYSM)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
|
||||
self.assertEqual(c.segment[2].SPcod[8],
|
||||
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
|
||||
self.assertEqual(len(c.segment[2].SPcod), 9)
|
||||
|
||||
def test_NR_ENC_Cevennes2_ppm_10_encode(self):
|
||||
infile = os.path.join(data_root, 'input/nonregression/Cevennes2.ppm')
|
||||
data = read_image(infile)
|
||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
j.write(data, cratios=[50])
|
||||
|
||||
c = j.get_codestream(header_only=False)
|
||||
|
||||
# SIZ: Image and tile size
|
||||
# Profile: "0" means profile 2
|
||||
self.assertEqual(c.segment[1].Rsiz, 0)
|
||||
# Reference grid size
|
||||
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz),
|
||||
(640, 480))
|
||||
# Reference grid offset
|
||||
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), (0, 0))
|
||||
# Tile size
|
||||
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
|
||||
(640, 480))
|
||||
# Tile offset
|
||||
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
|
||||
(0, 0))
|
||||
# bitdepth
|
||||
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
|
||||
# signed
|
||||
self.assertEqual(c.segment[1]._signed, (False, False, False))
|
||||
# subsampling
|
||||
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)),
|
||||
[(1, 1)] * 3)
|
||||
|
||||
# COD: Coding style default
|
||||
self.assertFalse(c.segment[2].Scod & 2) # no sop
|
||||
self.assertFalse(c.segment[2].Scod & 4) # no eph
|
||||
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
|
||||
self.assertEqual(c.segment[2]._layers, 1) # layers = 1
|
||||
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
|
||||
self.assertEqual(c.segment[2].SPcod[4], 5) # levels
|
||||
self.assertEqual(tuple(c.segment[2]._code_block_size),
|
||||
(64, 64)) # cblksz
|
||||
# Selective arithmetic coding BYPASS
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
|
||||
# RESET context probabilities (RESET)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x02)
|
||||
# Termination on each coding pass, RESTART(TERMALL)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
|
||||
# Vertically causal context (VSC)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
|
||||
# Predictable termination, ERTERM(SEGTERM)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
|
||||
# Segmentation symbols, SEGMARK(SEGSYSM)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
|
||||
self.assertEqual(c.segment[2].SPcod[8],
|
||||
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
|
||||
self.assertEqual(len(c.segment[2].SPcod), 9)
|
||||
|
||||
def test_NR_ENC_Rome_bmp_11_encode(self):
|
||||
infile = os.path.join(data_root, 'input/nonregression/Rome.bmp')
|
||||
data = read_image(infile)
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
jp2 = Jp2k(tfile.name, 'wb')
|
||||
jp2.write(data, psnr=[30, 35, 50], prog='LRCP', numres=3)
|
||||
|
||||
ids = [box.id for box in jp2.box]
|
||||
lst = ['jP ', 'ftyp', 'jp2h', 'jp2c']
|
||||
self.assertEqual(ids, lst)
|
||||
|
||||
ids = [box.id for box in jp2.box[2].box]
|
||||
self.assertEqual(ids, ['ihdr', 'colr'])
|
||||
|
||||
# Signature box. Check for corruption.
|
||||
self.assertEqual(jp2.box[0].signature, (13, 10, 135, 10))
|
||||
|
||||
# File type box.
|
||||
self.assertEqual(jp2.box[1].brand, 'jp2 ')
|
||||
self.assertEqual(jp2.box[1].minor_version, 0)
|
||||
self.assertEqual(jp2.box[1].compatibility_list[0], 'jp2 ')
|
||||
|
||||
# Jp2 Header
|
||||
# Image header
|
||||
self.assertEqual(jp2.box[2].box[0].height, 480)
|
||||
self.assertEqual(jp2.box[2].box[0].width, 640)
|
||||
self.assertEqual(jp2.box[2].box[0].num_components, 3)
|
||||
self.assertEqual(jp2.box[2].box[0].bits_per_component, 8)
|
||||
self.assertEqual(jp2.box[2].box[0].signed, False)
|
||||
self.assertEqual(jp2.box[2].box[0].compression, 7) # wavelet
|
||||
self.assertEqual(jp2.box[2].box[0].cspace_unknown, False)
|
||||
self.assertEqual(jp2.box[2].box[0].ip_provided, False)
|
||||
|
||||
# Jp2 Header
|
||||
# Colour specification
|
||||
self.assertEqual(jp2.box[2].box[1].method, 1)
|
||||
self.assertEqual(jp2.box[2].box[1].precedence, 0)
|
||||
self.assertEqual(jp2.box[2].box[1].approximation, 0)
|
||||
self.assertIsNone(jp2.box[2].box[1].icc_profile)
|
||||
self.assertEqual(jp2.box[2].box[1].color_space, glymur.core.SRGB)
|
||||
|
||||
c = jp2.box[3].main_header
|
||||
|
||||
# SIZ: Image and tile size
|
||||
# Profile: "0" means profile 2
|
||||
self.assertEqual(c.segment[1].Rsiz, 0)
|
||||
# Reference grid size
|
||||
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz),
|
||||
(640, 480))
|
||||
# Reference grid offset
|
||||
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz),
|
||||
(0, 0))
|
||||
# Tile size
|
||||
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
|
||||
(640, 480))
|
||||
# Tile offset
|
||||
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
|
||||
(0, 0))
|
||||
# bitdepth
|
||||
self.assertEqual(c.segment[1]._bitdepth, (8, 8, 8))
|
||||
# signed
|
||||
self.assertEqual(c.segment[1]._signed, (False, False, False))
|
||||
# subsampling
|
||||
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)),
|
||||
[(1, 1)] * 3)
|
||||
|
||||
# COD: Coding style default
|
||||
self.assertFalse(c.segment[2].Scod & 2) # no sop
|
||||
self.assertFalse(c.segment[2].Scod & 4) # no eph
|
||||
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
|
||||
self.assertEqual(c.segment[2]._layers, 3) # layers = 3
|
||||
self.assertEqual(c.segment[2].SPcod[3], 1) # mct
|
||||
self.assertEqual(c.segment[2].SPcod[4], 2) # levels
|
||||
self.assertEqual(tuple(c.segment[2]._code_block_size),
|
||||
(64, 64)) # cblksz
|
||||
# Selective arithmetic coding BYPASS
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
|
||||
# RESET context probabilities (RESET)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x02)
|
||||
# Termination on each coding pass, RESTART(TERMALL)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
|
||||
# Vertically causal context (VSC)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
|
||||
# Predictable termination, ERTERM(SEGTERM)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
|
||||
# Segmentation symbols, SEGMARK(SEGSYSM)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
|
||||
self.assertEqual(c.segment[2].SPcod[8],
|
||||
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
|
||||
self.assertEqual(len(c.segment[2].SPcod), 9)
|
||||
|
||||
@unittest.skip("Known failure in openjpeg test suite.")
|
||||
def test_NR_ENC_random_issue_0005_tif_12_encode(self):
|
||||
# opj_decompress has trouble reading it, but that is not an issue here.
|
||||
# The nature of the image itself seems to give the compressor trouble.
|
||||
infile = os.path.join(data_root,
|
||||
'input/nonregression/random-issue-0005.tif')
|
||||
data = read_image(infile)
|
||||
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
|
||||
j = Jp2k(tfile.name, 'wb')
|
||||
j.write(data)
|
||||
|
||||
c = j.get_codestream(header_only=False)
|
||||
|
||||
# SIZ: Image and tile size
|
||||
# Profile: "0" means profile 2
|
||||
self.assertEqual(c.segment[1].Rsiz, 0)
|
||||
# Reference grid size
|
||||
self.assertEqual((c.segment[1].Xsiz, c.segment[1].Ysiz),
|
||||
(1024, 1024))
|
||||
# Reference grid offset
|
||||
self.assertEqual((c.segment[1].XOsiz, c.segment[1].YOsiz), (0, 0))
|
||||
# Tile size
|
||||
self.assertEqual((c.segment[1].XTsiz, c.segment[1].YTsiz),
|
||||
(1024, 1024))
|
||||
# Tile offset
|
||||
self.assertEqual((c.segment[1].XTOsiz, c.segment[1].YTOsiz),
|
||||
(0, 0))
|
||||
# bitdepth
|
||||
self.assertEqual(c.segment[1]._bitdepth, (16,))
|
||||
# signed
|
||||
self.assertEqual(c.segment[1]._signed, (False,))
|
||||
# subsampling
|
||||
self.assertEqual(list(zip(c.segment[1].XRsiz, c.segment[1].YRsiz)),
|
||||
[(1, 1)])
|
||||
|
||||
# COD: Coding style default
|
||||
self.assertFalse(c.segment[2].Scod & 2) # no sop
|
||||
self.assertFalse(c.segment[2].Scod & 4) # no eph
|
||||
self.assertEqual(c.segment[2].SPcod[0], glymur.core.LRCP)
|
||||
self.assertEqual(c.segment[2]._layers, 1) # layers = 1
|
||||
self.assertEqual(c.segment[2].SPcod[3], 0) # mct
|
||||
self.assertEqual(c.segment[2].SPcod[4], 5) # levels
|
||||
self.assertEqual(tuple(c.segment[2]._code_block_size),
|
||||
(64, 64)) # cblksz
|
||||
# Selective arithmetic coding BYPASS
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x01)
|
||||
# RESET context probabilities (RESET)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x02)
|
||||
# Termination on each coding pass, RESTART(TERMALL)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x04)
|
||||
# Vertically causal context (VSC)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x08)
|
||||
# Predictable termination, ERTERM(SEGTERM)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0010)
|
||||
# Segmentation symbols, SEGMARK(SEGSYSM)
|
||||
self.assertFalse(c.segment[2].SPcod[7] & 0x0020)
|
||||
self.assertEqual(c.segment[2].SPcod[8],
|
||||
glymur.core.WAVELET_TRANSFORM_5x3_REVERSIBLE)
|
||||
self.assertEqual(len(c.segment[2].SPcod), 9)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
767
glymur/test/test_printing.py
Normal file
767
glymur/test/test_printing.py
Normal file
|
|
@ -0,0 +1,767 @@
|
|||
import os
|
||||
import pkg_resources
|
||||
import struct
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
if sys.hexversion < 0x03000000:
|
||||
from StringIO import StringIO
|
||||
else:
|
||||
from io import StringIO
|
||||
|
||||
import glymur
|
||||
|
||||
try:
|
||||
data_root = os.environ['OPJ_DATA_ROOT']
|
||||
except KeyError:
|
||||
data_root = None
|
||||
except:
|
||||
raise
|
||||
|
||||
|
||||
class TestPrinting(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
# Save sys.stdout.
|
||||
self.stdout = sys.stdout
|
||||
sys.stdout = StringIO()
|
||||
self.jp2file = pkg_resources.resource_filename(glymur.__name__,
|
||||
"data/nemo.jp2")
|
||||
|
||||
# Save the output of dumping nemo.jp2 for more than one test.
|
||||
lines = ['File: nemo.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: [1456 2592 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, 1133427)',
|
||||
' Main header:',
|
||||
' SOC marker segment @ (85, 0)',
|
||||
' SIZ marker segment @ (87, 47)',
|
||||
' Profile: 2',
|
||||
' Reference Grid Height, Width: (1456 x 2592)',
|
||||
' Vertical, Horizontal Reference Grid Offset: '
|
||||
+ '(0 x 0)',
|
||||
' Reference Tile Height, Width: (512 x 512)',
|
||||
' Vertical, Horizontal Reference Tile Offset: '
|
||||
+ '(0 x 0)',
|
||||
' Bitdepth: (8, 8, 8)',
|
||||
' Signed: (False, False, False)',
|
||||
' Vertical, Horizontal Subsampling: '
|
||||
+ '((1, 1), (1, 1), (1, 1))',
|
||||
' COD marker segment @ (136, 12)',
|
||||
' Coding style:',
|
||||
' Entropy coder, without partitions',
|
||||
' SOP marker segments: False',
|
||||
' EPH marker segments: False',
|
||||
' Coding style parameters:',
|
||||
' Progression order: LRCP',
|
||||
' Number of layers: 3',
|
||||
' Multiple component transformation usage: '
|
||||
+ 'reversible',
|
||||
' Number of resolutions: 6',
|
||||
' Code block height, width: (64 x 64)',
|
||||
' Wavelet transform: 5-3 reversible',
|
||||
' Precinct size: default, 2^15 x 2^15',
|
||||
' Code block context:',
|
||||
' Selective arithmetic coding bypass: '
|
||||
+ 'False',
|
||||
' Reset context probabilities on '
|
||||
+ 'coding pass boundaries: False',
|
||||
' Termination on each coding pass: False',
|
||||
' Vertically stripe causal context: '
|
||||
+ 'False',
|
||||
' Predictable termination: False',
|
||||
' Segmentation symbols: False',
|
||||
' QCD marker segment @ (150, 19)',
|
||||
' Quantization style: no quantization, '
|
||||
+ '2 guard bits',
|
||||
' Step size: [(0, 8), (0, 9), (0, 9), '
|
||||
+ '(0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), '
|
||||
+ '(0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), '
|
||||
+ '(0, 10)]']
|
||||
self.expectedNemo = '\n'.join(lines)
|
||||
|
||||
def tearDown(self):
|
||||
# Restore stdout.
|
||||
sys.stdout = self.stdout
|
||||
|
||||
def test_jp2dump(self):
|
||||
glymur.jp2dump(self.jp2file)
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
self.assertEqual(actual, self.expectedNemo)
|
||||
|
||||
def test_COC_segment(self):
|
||||
j = glymur.Jp2k(self.jp2file)
|
||||
codestream = j.get_codestream(header_only=False)
|
||||
print(codestream.segment[5])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
|
||||
lines = ['COC marker segment @ (183, 9)',
|
||||
' Associated component: 1',
|
||||
' Coding style for this component: '
|
||||
+ 'Entropy coder, PARTITION = 0',
|
||||
' Coding style parameters:',
|
||||
' Number of resolutions: 6',
|
||||
' Code block height, width: (64 x 64)',
|
||||
' Wavelet transform: 5-3 reversible',
|
||||
' Code block context:',
|
||||
' Selective arithmetic coding bypass: False',
|
||||
' Reset context probabilities '
|
||||
+ 'on coding pass boundaries: False',
|
||||
' Termination on each coding pass: False',
|
||||
' Vertically stripe causal context: False',
|
||||
' Predictable termination: False',
|
||||
' Segmentation symbols: False']
|
||||
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_COD_segment(self):
|
||||
j = glymur.Jp2k(self.jp2file)
|
||||
codestream = j.get_codestream()
|
||||
print(codestream.segment[2])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
|
||||
lines = ['COD marker segment @ (136, 12)',
|
||||
' Coding style:',
|
||||
' Entropy coder, without partitions',
|
||||
' SOP marker segments: False',
|
||||
' EPH marker segments: False',
|
||||
' Coding style parameters:',
|
||||
' Progression order: LRCP',
|
||||
' Number of layers: 3',
|
||||
' Multiple component transformation usage: '
|
||||
+ 'reversible',
|
||||
' Number of resolutions: 6',
|
||||
' Code block height, width: (64 x 64)',
|
||||
' Wavelet transform: 5-3 reversible',
|
||||
' Precinct size: default, 2^15 x 2^15',
|
||||
' Code block context:',
|
||||
' Selective arithmetic coding bypass: False',
|
||||
' Reset context probabilities on coding '
|
||||
+ 'pass boundaries: False',
|
||||
' Termination on each coding pass: False',
|
||||
' Vertically stripe causal context: False',
|
||||
' Predictable termination: False',
|
||||
' Segmentation symbols: False']
|
||||
|
||||
expected = '\n'.join(lines)
|
||||
self.actual = actual
|
||||
self.expected = expected
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_CRG(self):
|
||||
filename = os.path.join(data_root, 'input/conformance/p0_03.j2k')
|
||||
j = glymur.Jp2k(filename)
|
||||
codestream = j.get_codestream()
|
||||
print(codestream.segment[-5])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
lines = ['CRG marker segment at (87, 6)',
|
||||
' Vertical, Horizontal offset: (0.50, 1.00)']
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_RGN(self):
|
||||
filename = os.path.join(data_root, 'input/conformance/p0_03.j2k')
|
||||
j = glymur.Jp2k(filename)
|
||||
codestream = j.get_codestream(header_only=False)
|
||||
print(codestream.segment[12])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
lines = ['RGN marker segment @ (310, 5)',
|
||||
' Associated component: 0',
|
||||
' ROI style: 0',
|
||||
' Parameter: 7']
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_SOP(self):
|
||||
filename = os.path.join(data_root, 'input/conformance/p0_03.j2k')
|
||||
j = glymur.Jp2k(filename)
|
||||
codestream = j.get_codestream(header_only=False)
|
||||
print(codestream.segment[-2])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
lines = ['SOP marker segment @ (12836, 4)',
|
||||
' Nsop: 15']
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_CME(self):
|
||||
# Test printing a CME or comment marker segment.
|
||||
filename = os.path.join(data_root, 'input/conformance/p0_02.j2k')
|
||||
j = glymur.Jp2k(filename)
|
||||
codestream = j.get_codestream()
|
||||
# 2nd to last segment in the main header
|
||||
print(codestream.segment[-2])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
lines = ['CME marker segment @ (85, 45)',
|
||||
' "Creator: AV-J2K (c) 2000,2001 Algo Vision"']
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_EOC_segment(self):
|
||||
j = glymur.Jp2k(self.jp2file)
|
||||
codestream = j.get_codestream(header_only=False)
|
||||
print(codestream.segment[-1])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
|
||||
lines = ['EOC marker segment @ (1133502, 0)']
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_PLT_segment(self):
|
||||
filename = os.path.join(data_root, 'input/conformance/p0_07.j2k')
|
||||
j = glymur.Jp2k(filename)
|
||||
codestream = j.get_codestream(header_only=False)
|
||||
print(codestream.segment[49935])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
|
||||
lines = ['PLT marker segment @ (7871146, 38)',
|
||||
' Index: 0',
|
||||
' Iplt: [9, 122, 19, 30, 27, 9, 41, 62, 18, 29, 261,'
|
||||
+ ' 55, 82, 299, 93, 941, 951, 687, 1729, 1443, 1008, 2168,'
|
||||
+ ' 2188, 2223]']
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_POD_segment(self):
|
||||
filename = os.path.join(data_root, 'input/conformance/p0_13.j2k')
|
||||
j = glymur.Jp2k(filename)
|
||||
codestream = j.get_codestream()
|
||||
print(codestream.segment[8])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
|
||||
lines = ['POD marker segment @ (878, 20)',
|
||||
' Progression change 0:',
|
||||
' Resolution index start: 0',
|
||||
' Component index start: 0',
|
||||
' Layer index end: 1',
|
||||
' Resolution index end: 33',
|
||||
' Component index end: 128',
|
||||
' Progression order: RLCP',
|
||||
' Progression change 1:',
|
||||
' Resolution index start: 0',
|
||||
' Component index start: 128',
|
||||
' Layer index end: 1',
|
||||
' Resolution index end: 33',
|
||||
' Component index end: 257',
|
||||
' Progression order: CPRL']
|
||||
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_PPM_segment(self):
|
||||
filename = os.path.join(data_root, 'input/conformance/p1_03.j2k')
|
||||
j = glymur.Jp2k(filename)
|
||||
codestream = j.get_codestream()
|
||||
print(codestream.segment[9])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
|
||||
lines = ['PPM marker segment @ (213, 43712)',
|
||||
' Index: 0',
|
||||
' Data: 43709 uninterpreted bytes']
|
||||
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_PPT_segment(self):
|
||||
filename = os.path.join(data_root, 'input/conformance/p1_06.j2k')
|
||||
j = glymur.Jp2k(filename)
|
||||
codestream = j.get_codestream(header_only=False)
|
||||
print(codestream.segment[6])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
|
||||
lines = ['PPT marker segment @ (155, 109)',
|
||||
' Index: 0',
|
||||
' Packet headers: 106 uninterpreted bytes']
|
||||
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_QCC_segment(self):
|
||||
j = glymur.Jp2k(self.jp2file)
|
||||
codestream = j.get_codestream(header_only=False)
|
||||
print(codestream.segment[6])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
|
||||
lines = ['QCC marker segment @ (194, 20)',
|
||||
' Associated Component: 1',
|
||||
' Quantization style: no quantization, 2 guard bits',
|
||||
' Step size: [(0, 8), (0, 9), (0, 9), (0, 10), (0, 9), '
|
||||
+ '(0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), '
|
||||
+ '(0, 10), (0, 9), (0, 9), (0, 10)]']
|
||||
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_QCD_segment_5x3_transform(self):
|
||||
j = glymur.Jp2k(self.jp2file)
|
||||
codestream = j.get_codestream()
|
||||
print(codestream.segment[3])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
|
||||
lines = ['QCD marker segment @ (150, 19)',
|
||||
' Quantization style: no quantization, 2 guard bits',
|
||||
' Step size: [(0, 8), (0, 9), (0, 9), (0, 10), (0, 9), '
|
||||
+ '(0, 9), (0, 10), (0, 9), (0, 9), (0, 10), (0, 9), '
|
||||
+ '(0, 9), (0, 10), (0, 9), (0, 9), (0, 10)]']
|
||||
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_SIZ_segment(self):
|
||||
j = glymur.Jp2k(self.jp2file)
|
||||
codestream = j.get_codestream()
|
||||
print(codestream.segment[1])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
|
||||
lines = ['SIZ marker segment @ (87, 47)',
|
||||
' Profile: 2',
|
||||
' Reference Grid Height, Width: (1456 x 2592)',
|
||||
' Vertical, Horizontal Reference Grid Offset: (0 x 0)',
|
||||
' Reference Tile Height, Width: (512 x 512)',
|
||||
' Vertical, Horizontal Reference Tile Offset: (0 x 0)',
|
||||
' Bitdepth: (8, 8, 8)',
|
||||
' Signed: (False, False, False)',
|
||||
' Vertical, Horizontal Subsampling: '
|
||||
+ '((1, 1), (1, 1), (1, 1))']
|
||||
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_SOC_segment(self):
|
||||
j = glymur.Jp2k(self.jp2file)
|
||||
codestream = j.get_codestream()
|
||||
print(codestream.segment[0])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
|
||||
lines = ['SOC marker segment @ (85, 0)']
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_SOD_segment(self):
|
||||
j = glymur.Jp2k(self.jp2file)
|
||||
codestream = j.get_codestream(header_only=False)
|
||||
print(codestream.segment[9])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
|
||||
lines = ['SOD marker segment @ (249, 0)']
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_SOT_segment(self):
|
||||
j = glymur.Jp2k(self.jp2file)
|
||||
codestream = j.get_codestream(header_only=False)
|
||||
print(codestream.segment[4])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
|
||||
lines = ['SOT marker segment @ (171, 10)',
|
||||
' Tile part index: 0',
|
||||
' Tile part length: 78629',
|
||||
' Tile part instance: 0',
|
||||
' Number of tile parts: 1']
|
||||
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_TLM_segment(self):
|
||||
filename = os.path.join(data_root, 'input/conformance/p0_15.j2k')
|
||||
j = glymur.Jp2k(filename)
|
||||
codestream = j.get_codestream()
|
||||
print(codestream.segment[10])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
|
||||
lines = ['TLM marker segment @ (268, 28)',
|
||||
' Index: 0',
|
||||
' Tile number: (0, 1, 2, 3)',
|
||||
' Length: (4267, 2117, 4080, 2081)']
|
||||
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_entire_file(self):
|
||||
j = glymur.Jp2k(self.jp2file)
|
||||
print(j)
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
self.assertEqual(actual, self.expectedNemo)
|
||||
|
||||
def test_codestream(self):
|
||||
j = glymur.Jp2k(self.jp2file)
|
||||
print(j.get_codestream())
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
|
||||
lst = ['Codestream:',
|
||||
' SOC marker segment @ (85, 0)',
|
||||
' SIZ marker segment @ (87, 47)',
|
||||
' Profile: 2',
|
||||
' Reference Grid Height, Width: (1456 x 2592)',
|
||||
' Vertical, Horizontal Reference Grid Offset: (0 x 0)',
|
||||
' Reference Tile Height, Width: (512 x 512)',
|
||||
' Vertical, Horizontal Reference Tile Offset: (0 x 0)',
|
||||
' Bitdepth: (8, 8, 8)',
|
||||
' Signed: (False, False, False)',
|
||||
' Vertical, Horizontal Subsampling: '
|
||||
+ '((1, 1), (1, 1), (1, 1))',
|
||||
' COD marker segment @ (136, 12)',
|
||||
' Coding style:',
|
||||
' Entropy coder, without partitions',
|
||||
' SOP marker segments: False',
|
||||
' EPH marker segments: False',
|
||||
' Coding style parameters:',
|
||||
' Progression order: LRCP',
|
||||
' Number of layers: 3',
|
||||
' Multiple component transformation usage: '
|
||||
+ 'reversible',
|
||||
' Number of resolutions: 6',
|
||||
' Code block height, width: (64 x 64)',
|
||||
' Wavelet transform: 5-3 reversible',
|
||||
' Precinct size: default, 2^15 x 2^15',
|
||||
' Code block context:',
|
||||
' Selective arithmetic coding bypass: False',
|
||||
' Reset context probabilities on '
|
||||
+ 'coding pass boundaries: False',
|
||||
' Termination on each coding pass: False',
|
||||
' Vertically stripe causal context: False',
|
||||
' Predictable termination: False',
|
||||
' Segmentation symbols: False',
|
||||
' QCD marker segment @ (150, 19)',
|
||||
' Quantization style: no quantization, '
|
||||
+ '2 guard bits',
|
||||
' Step size: [(0, 8), (0, 9), (0, 9), '
|
||||
+ '(0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), '
|
||||
+ '(0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), '
|
||||
+ '(0, 10)]']
|
||||
expected = '\n'.join(lst)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_xml(self):
|
||||
filename = os.path.join(data_root, 'input/conformance/file1.jp2')
|
||||
j = glymur.Jp2k(filename)
|
||||
print(j.box[2])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
|
||||
lines = ['XML Box (xml ) @ (36, 439)',
|
||||
' <ns0:IMAGE_CREATION '
|
||||
+ 'xmlns:ns0="http://www.jpeg.org/jpx/1.0/xml" '
|
||||
+ 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '
|
||||
+ 'xsi:schemaLocation="http://www.jpeg.org/jpx/1.0/xml '
|
||||
+ 'http://www.jpeg.org/metadata/15444-2.xsd">',
|
||||
|
||||
' <ns0:GENERAL_CREATION_INFO>',
|
||||
' <ns0:CREATION_TIME>'
|
||||
+ '2001-11-01T13:45:00.000-06:00'
|
||||
+ '</ns0:CREATION_TIME>',
|
||||
|
||||
' <ns0:IMAGE_SOURCE>'
|
||||
+ 'Professional 120 Image'
|
||||
+ '</ns0:IMAGE_SOURCE>',
|
||||
|
||||
' </ns0:GENERAL_CREATION_INFO>',
|
||||
' </ns0:IMAGE_CREATION>']
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_component_definition(self):
|
||||
filename = os.path.join(data_root, 'input/conformance/file2.jp2')
|
||||
j = glymur.Jp2k(filename)
|
||||
print(j.box[2].box[2])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
lines = ['Component Definition Box (cdef) @ (81, 28)',
|
||||
' Component 0 (color) ==> (3)',
|
||||
' Component 1 (color) ==> (2)',
|
||||
' Component 2 (color) ==> (1)']
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_component_mapping(self):
|
||||
filename = os.path.join(data_root, 'input/conformance/file9.jp2')
|
||||
j = glymur.Jp2k(filename)
|
||||
print(j.box[2].box[2])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
lines = ['Component Mapping Box (cmap) @ (848, 20)',
|
||||
' Component 0 ==> palette column 0',
|
||||
' Component 0 ==> palette column 1',
|
||||
' Component 0 ==> palette column 2']
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_palette(self):
|
||||
filename = os.path.join(data_root, 'input/conformance/file9.jp2')
|
||||
j = glymur.Jp2k(filename)
|
||||
print(j.box[2].box[1])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
lines = ['Palette Box (pclr) @ (66, 782)',
|
||||
' Size: (256 x 3)']
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_palette(self):
|
||||
filename = os.path.join(data_root, 'input/conformance/file7.jp2')
|
||||
j = glymur.Jp2k(filename)
|
||||
print(j.box[2])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
lines = ['Reader Requirements Box (rreq) @ (44, 24)',
|
||||
' Standard Features:',
|
||||
' Feature 005: '
|
||||
+ 'Unrestricted JPEG 2000 Part 1 codestream, '
|
||||
+ 'ITU-T Rec. T.800 | ISO/IEC 15444-1',
|
||||
' Feature 060: e-sRGB enumerated colorspace',
|
||||
|
||||
' Feature 043: '
|
||||
+ '(Deprecated) '
|
||||
+ 'compositing layer uses restricted ICC profile',
|
||||
|
||||
' Vendor Features:']
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_CRG(self):
|
||||
filename = os.path.join(data_root, 'input/conformance/p0_03.j2k')
|
||||
j = glymur.Jp2k(filename)
|
||||
codestream = j.get_codestream()
|
||||
print(codestream.segment[6])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
lines = ['CRG marker segment @ (87, 6)',
|
||||
' Vertical, Horizontal offset: (0.50, 1.00)']
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_differing_subsamples(self):
|
||||
# Issue 86.
|
||||
filename = os.path.join(data_root, 'input/conformance/p0_05.j2k')
|
||||
j = glymur.Jp2k(filename)
|
||||
codestream = j.get_codestream()
|
||||
print(codestream.segment[1])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
lines = ['SIZ marker segment @ (2, 50)',
|
||||
' Profile: 0',
|
||||
' Reference Grid Height, Width: (1024 x 1024)',
|
||||
' Vertical, Horizontal Reference Grid Offset: (0 x 0)',
|
||||
' Reference Tile Height, Width: (1024 x 1024)',
|
||||
' Vertical, Horizontal Reference Tile Offset: (0 x 0)',
|
||||
' Bitdepth: (8, 8, 8, 8)',
|
||||
' Signed: (False, False, False, False)',
|
||||
' Vertical, Horizontal Subsampling: '
|
||||
+ '((1, 1), (1, 1), (2, 2), (2, 2))']
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_palette_box(self):
|
||||
# Verify that palette (pclr) boxes are printed without error.
|
||||
filename = os.path.join(data_root, 'input/conformance/file9.jp2')
|
||||
j = glymur.Jp2k(filename)
|
||||
print(j.box[2].box[1])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
lines = ['Palette Box (pclr) @ (66, 782)',
|
||||
' Size: (256 x 3)']
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_asoc_label_box(self):
|
||||
# Construct a fake file with an asoc and a label box, as
|
||||
# OpenJPEG doesn't have such a file.
|
||||
data = glymur.Jp2k(self.jp2file).read(reduce=3)
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
j = glymur.Jp2k(tfile.name, 'wb')
|
||||
j.write(data)
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile2:
|
||||
|
||||
# Offset of the codestream is where we start.
|
||||
buffer = tfile.read(77)
|
||||
tfile2.write(buffer)
|
||||
|
||||
# read the rest of the file, it's the codestream.
|
||||
codestream = tfile.read()
|
||||
|
||||
# Write the asoc superbox.
|
||||
# Length = 36, id is 'asoc'.
|
||||
buffer = struct.pack('>I4s', int(56), b'asoc')
|
||||
tfile2.write(buffer)
|
||||
|
||||
# Write the contained label box
|
||||
buffer = struct.pack('>I4s', int(13), b'lbl ')
|
||||
tfile2.write(buffer)
|
||||
tfile2.write('label'.encode())
|
||||
|
||||
# Write the xml box
|
||||
# Length = 36, id is 'xml '.
|
||||
buffer = struct.pack('>I4s', int(35), b'xml ')
|
||||
tfile2.write(buffer)
|
||||
|
||||
buffer = '<test>this is a test</test>'
|
||||
buffer = buffer.encode()
|
||||
tfile2.write(buffer)
|
||||
|
||||
# Now append the codestream.
|
||||
tfile2.write(codestream)
|
||||
|
||||
jasoc = glymur.Jp2k(tfile2.name)
|
||||
print(jasoc.box[3])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
lines = ['Association Box (asoc) @ (77, 56)',
|
||||
' Label Box (lbl ) @ (85, 13)',
|
||||
' Label: label',
|
||||
' XML Box (xml ) @ (98, 35)',
|
||||
' <test>this is a test</test>']
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_less_common_boxes(self):
|
||||
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
|
||||
with open(self.jp2file, 'rb') as ifile:
|
||||
# Everything up until the jp2c box.
|
||||
buffer = ifile.read(77)
|
||||
tfile.write(buffer)
|
||||
|
||||
# Write the UINF superbox
|
||||
# Length = 50, id is uinf.
|
||||
buffer = struct.pack('>I4s', int(50), b'uinf')
|
||||
tfile.write(buffer)
|
||||
|
||||
# Write the ULST box.
|
||||
# Length is 26, 1 UUID, hard code that UUID as zeros.
|
||||
buffer = struct.pack('>I4sHIIII', int(26), b'ulst', int(1),
|
||||
int(0), int(0), int(0), int(0))
|
||||
tfile.write(buffer)
|
||||
|
||||
# Write the URL box.
|
||||
# Length is 16, version is one byte, flag is 3 bytes, url
|
||||
# is the rest.
|
||||
buffer = struct.pack('>I4sBBBB',
|
||||
int(16), b'url ',
|
||||
int(0), int(0), int(0), int(0))
|
||||
tfile.write(buffer)
|
||||
buffer = struct.pack('>ssss', b'a', b'b', b'c', b'd')
|
||||
tfile.write(buffer)
|
||||
|
||||
# Start the resolution superbox.
|
||||
buffer = struct.pack('>I4s', int(44), b'res ')
|
||||
tfile.write(buffer)
|
||||
|
||||
# Write the capture resolution box.
|
||||
buffer = struct.pack('>I4sHHHHBB',
|
||||
int(18), b'resc',
|
||||
int(1), int(1), int(1), int(1),
|
||||
int(0), int(1))
|
||||
tfile.write(buffer)
|
||||
|
||||
# Write the display resolution box.
|
||||
buffer = struct.pack('>I4sHHHHBB',
|
||||
int(18), b'resd',
|
||||
int(1), int(1), int(1), int(1),
|
||||
int(1), int(0))
|
||||
tfile.write(buffer)
|
||||
|
||||
# Get the rest of the input file.
|
||||
buffer = ifile.read()
|
||||
tfile.write(buffer)
|
||||
tfile.flush()
|
||||
|
||||
jp2k = glymur.Jp2k(tfile.name)
|
||||
print(jp2k.box[3])
|
||||
print(jp2k.box[4])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
lines = ['UUIDInfo Box (uinf) @ (77, 50)',
|
||||
' UUID List Box (ulst) @ (85, 26)',
|
||||
' UUID[0]: 00000000-0000-0000-0000-000000000000',
|
||||
' Data Entry URL Box (url ) @ (111, 16)',
|
||||
' Version: 0',
|
||||
' Flag: 0 0 0',
|
||||
' URL: "abcd"',
|
||||
'Resolution Box (res ) @ (127, 44)',
|
||||
' Capture Resolution Box (resc) @ (135, 18)',
|
||||
' VCR: 1.0',
|
||||
' HCR: 10.0',
|
||||
' Display Resolution Box (resd) @ (153, 18)',
|
||||
' VDR: 10.0',
|
||||
' HDR: 1.0']
|
||||
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_jpx_approximation_with_icc_profile(self):
|
||||
# ICC profiles may be used in JP2, but the approximation field should
|
||||
# be zero unless we have jpx. This file does both.
|
||||
filename = os.path.join(data_root, 'input/nonregression/text_GBR.jp2')
|
||||
j = glymur.Jp2k(filename)
|
||||
|
||||
print(j.box[3].box[1])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
lines = ['Colour Specification Box (colr) @ (179, 1339)',
|
||||
' Method: any ICC profile',
|
||||
' Precedence: 2',
|
||||
' Approximation: accurately represents '
|
||||
+ 'correct colorspace definition',
|
||||
' ICC Profile: 1328 bytes']
|
||||
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@unittest.skipIf(data_root is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_uuid(self):
|
||||
# UUID box
|
||||
filename = os.path.join(data_root, 'input/nonregression/text_GBR.jp2')
|
||||
j = glymur.Jp2k(filename)
|
||||
|
||||
print(j.box[4])
|
||||
actual = sys.stdout.getvalue().strip()
|
||||
lines = ['UUID Box (uuid) @ (1544, 25)',
|
||||
' UUID: 3a0d0218-0ae9-4115-b376-4bca41ce0e71',
|
||||
' UUID Data: 1 bytes']
|
||||
|
||||
expected = '\n'.join(lines)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
28
setup.py
Normal file
28
setup.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
from distutils.core import setup
|
||||
|
||||
kwargs = {'name': 'glymur',
|
||||
'version': '0.1.0',
|
||||
'description': 'Tools for manipulating JPEG2000 files',
|
||||
'long_description': open('README').read(),
|
||||
'author': 'John Evans',
|
||||
'author_email': 'johnevans938 at gmail dot com',
|
||||
'url': 'https://bitbucket.org/johnevans007/jptools',
|
||||
'packages': ['glymur', 'glymur.test', 'glymur.lib',
|
||||
'glymur.lib.test'],
|
||||
'package_data': {'glymur': ['data/*.jp2']},
|
||||
'scripts': ['bin/jp2dump'],
|
||||
'license': 'LICENSE.txt',
|
||||
'platforms': ['darwin']}
|
||||
clssfrs = ["Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3.2",
|
||||
"Programming Language :: Python :: 3.3",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Development Status :: 1 - Alpha",
|
||||
"Operating System :: MacOS",
|
||||
"Operating System :: POSIX :: Linux",
|
||||
"Intended Audience :: Scientific/Research",
|
||||
"Intended Audience :: Information Technology",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules"]
|
||||
kwargs['classifiers'] = clssfrs
|
||||
setup(**kwargs)
|
||||
Loading…
Add table
Add a link
Reference in a new issue