Glymur 0.13.4__tar.gz → 0.13.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. {glymur-0.13.4 → glymur-0.13.6}/CHANGES.txt +8 -0
  2. {glymur-0.13.4 → glymur-0.13.6}/Glymur.egg-info/PKG-INFO +1 -1
  3. {glymur-0.13.4 → glymur-0.13.6}/PKG-INFO +1 -1
  4. {glymur-0.13.4 → glymur-0.13.6}/glymur/codestream.py +75 -1
  5. {glymur-0.13.4 → glymur-0.13.6}/glymur/jp2k.py +2 -38
  6. {glymur-0.13.4 → glymur-0.13.6}/glymur/jp2kr.py +22 -2
  7. {glymur-0.13.4 → glymur-0.13.6}/glymur/tiff.py +1 -1
  8. {glymur-0.13.4 → glymur-0.13.6}/glymur/version.py +1 -1
  9. {glymur-0.13.4 → glymur-0.13.6}/setup.cfg +1 -1
  10. {glymur-0.13.4 → glymur-0.13.6}/tests/test_codestream.py +13 -0
  11. {glymur-0.13.4 → glymur-0.13.6}/tests/test_jp2k.py +2 -2
  12. {glymur-0.13.4 → glymur-0.13.6}/tests/test_jp2kr.py +2 -2
  13. {glymur-0.13.4 → glymur-0.13.6}/tests/test_printing.py +16 -0
  14. {glymur-0.13.4 → glymur-0.13.6}/tests/test_threading.py +3 -3
  15. {glymur-0.13.4 → glymur-0.13.6}/tests/test_tiff2jp2.py +24 -0
  16. {glymur-0.13.4 → glymur-0.13.6}/Glymur.egg-info/SOURCES.txt +0 -0
  17. {glymur-0.13.4 → glymur-0.13.6}/Glymur.egg-info/dependency_links.txt +0 -0
  18. {glymur-0.13.4 → glymur-0.13.6}/Glymur.egg-info/entry_points.txt +0 -0
  19. {glymur-0.13.4 → glymur-0.13.6}/Glymur.egg-info/not-zip-safe +0 -0
  20. {glymur-0.13.4 → glymur-0.13.6}/Glymur.egg-info/requires.txt +0 -0
  21. {glymur-0.13.4 → glymur-0.13.6}/Glymur.egg-info/top_level.txt +0 -0
  22. {glymur-0.13.4 → glymur-0.13.6}/LICENSE.txt +0 -0
  23. {glymur-0.13.4 → glymur-0.13.6}/MANIFEST.in +0 -0
  24. {glymur-0.13.4 → glymur-0.13.6}/README.md +0 -0
  25. {glymur-0.13.4 → glymur-0.13.6}/glymur/__init__.py +0 -0
  26. {glymur-0.13.4 → glymur-0.13.6}/glymur/_iccprofile.py +0 -0
  27. {glymur-0.13.4 → glymur-0.13.6}/glymur/command_line.py +0 -0
  28. {glymur-0.13.4 → glymur-0.13.6}/glymur/config.py +0 -0
  29. {glymur-0.13.4 → glymur-0.13.6}/glymur/core.py +0 -0
  30. {glymur-0.13.4 → glymur-0.13.6}/glymur/data/__init__.py +0 -0
  31. {glymur-0.13.4 → glymur-0.13.6}/glymur/data/goodstuff.j2k +0 -0
  32. {glymur-0.13.4 → glymur-0.13.6}/glymur/data/heliov.jpx +0 -0
  33. {glymur-0.13.4 → glymur-0.13.6}/glymur/data/nemo.jp2 +0 -0
  34. {glymur-0.13.4 → glymur-0.13.6}/glymur/jp2box.py +0 -0
  35. {glymur-0.13.4 → glymur-0.13.6}/glymur/lib/__init__.py +0 -0
  36. {glymur-0.13.4 → glymur-0.13.6}/glymur/lib/openjp2.py +0 -0
  37. {glymur-0.13.4 → glymur-0.13.6}/glymur/lib/tiff.py +0 -0
  38. {glymur-0.13.4 → glymur-0.13.6}/glymur/options.py +0 -0
  39. {glymur-0.13.4 → glymur-0.13.6}/pyproject.toml +0 -0
  40. {glymur-0.13.4 → glymur-0.13.6}/tests/test_callbacks.py +0 -0
  41. {glymur-0.13.4 → glymur-0.13.6}/tests/test_cinema.py +0 -0
  42. {glymur-0.13.4 → glymur-0.13.6}/tests/test_colour_specification_box.py +0 -0
  43. {glymur-0.13.4 → glymur-0.13.6}/tests/test_config.py +0 -0
  44. {glymur-0.13.4 → glymur-0.13.6}/tests/test_geo.py +0 -0
  45. {glymur-0.13.4 → glymur-0.13.6}/tests/test_jp2box.py +0 -0
  46. {glymur-0.13.4 → glymur-0.13.6}/tests/test_jp2box_jpx.py +0 -0
  47. {glymur-0.13.4 → glymur-0.13.6}/tests/test_jp2box_uuid.py +0 -0
  48. {glymur-0.13.4 → glymur-0.13.6}/tests/test_jp2box_xml.py +0 -0
  49. {glymur-0.13.4 → glymur-0.13.6}/tests/test_jp2k_writes.py +0 -0
  50. {glymur-0.13.4 → glymur-0.13.6}/tests/test_libtiff.py +0 -0
  51. {glymur-0.13.4 → glymur-0.13.6}/tests/test_openjp2.py +0 -0
  52. {glymur-0.13.4 → glymur-0.13.6}/tests/test_set_decoded_components.py +0 -0
  53. {glymur-0.13.4 → glymur-0.13.6}/tests/test_slicing.py +0 -0
  54. {glymur-0.13.4 → glymur-0.13.6}/tests/test_warnings.py +0 -0
@@ -1,3 +1,11 @@
1
+ Aug 19, 2024 - v0.13.6
2
+ Fix tiff2jp2 when tile coverage not square.
3
+
4
+ July 26, 2024 - v0.13.5
5
+ Add support for parsing the CAP segment.
6
+ End official support for OpenJPEG 2.3.x.
7
+ Fix issue preventing API docs from building.
8
+
1
9
  July 4, 2024 - v0.13.4
2
10
  Don't reset openjpeg codec in Jp2k if already set in Jp2kr.
3
11
  Update CI configuration to specify openjpeg versions.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Glymur
3
- Version: 0.13.4
3
+ Version: 0.13.6
4
4
  Home-page: https://github.com/quintusdias/glymur
5
5
  Author: 'John Evans'
6
6
  Author-email: "John Evans" <jevans667cc@proton.me>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Glymur
3
- Version: 0.13.4
3
+ Version: 0.13.6
4
4
  Home-page: https://github.com/quintusdias/glymur
5
5
  Author: 'John Evans'
6
6
  Author-email: "John Evans" <jevans667cc@proton.me>
@@ -189,7 +189,7 @@ class Codestream(object):
189
189
  0xff3f: self._parse_reserved_marker,
190
190
 
191
191
  # 0xff4f: SOC (already encountered by the time we get here)
192
- 0xff50: self._parse_reserved_segment,
192
+ 0xff50: self._parse_cap_segment,
193
193
  0xff51: self._parse_siz_segment,
194
194
  0xff52: self._parse_cod_segment,
195
195
  0xff53: self._parse_coc_segment,
@@ -713,6 +713,37 @@ class Codestream(object):
713
713
 
714
714
  return RGNsegment(crgn, srgn, sprgn, length, offset)
715
715
 
716
+ @classmethod
717
+ def _parse_cap_segment(cls, fptr):
718
+ """Parse the SIZ segment.
719
+
720
+ Parameters
721
+ ----------
722
+ fptr : file
723
+ Open file object.
724
+
725
+ Returns
726
+ -------
727
+ SIZSegment
728
+ The current SIZ segment.
729
+ """
730
+ offset = fptr.tell() - 2
731
+
732
+ read_buffer = fptr.read(2)
733
+ length, = struct.unpack('>H', read_buffer)
734
+ read_buffer = fptr.read(length - 2)
735
+
736
+ pcap, = struct.unpack('>I', read_buffer[:4])
737
+
738
+ n = (length - 6) // 2
739
+ ccap = struct.unpack('>' + 'H' * n, read_buffer[4:])
740
+
741
+ segment = CAPsegment(
742
+ pcap=pcap, ccap=ccap, length=length, offset=offset,
743
+ )
744
+
745
+ return segment
746
+
716
747
  @classmethod
717
748
  def _parse_siz_segment(cls, fptr):
718
749
  """Parse the SIZ segment.
@@ -934,6 +965,49 @@ class Segment(object):
934
965
  return msg
935
966
 
936
967
 
968
+ class CAPsegment(Segment):
969
+ """CAP (Extended Capabilities) segment information.
970
+
971
+ Attributes
972
+ ----------
973
+ marker_id : str
974
+ Identifier for the segment.
975
+ offset : int
976
+ Offset of marker segment in bytes from beginning of file.
977
+ length : int
978
+ Length of marker segment in bytes. This number does not include the
979
+ two bytes constituting the marker.
980
+ pcap : 32-bit unsigned int
981
+ bitmask where the kth 1 represents use of capabilities in Part k of
982
+ ISO/IEC 15444-k.
983
+ ccap : 16-bit unsigned int
984
+ The precise meaning depends on the related parts of ISO/IEC 15444-k.
985
+ """
986
+ def __init__(self, pcap, ccap, length, offset):
987
+ super().__init__(marker_id='CAP')
988
+ self.pcap = pcap
989
+ self.ccap = ccap
990
+ self.length = length
991
+ self.offset = offset
992
+
993
+ def __str__(self):
994
+ msg = Segment.__str__(self)
995
+
996
+ msg += '\n'
997
+
998
+ # have to cast to uint64 because, you know, stupid windows
999
+ parts = [
1000
+ k for k in range(32)
1001
+ if np.bitwise_and(np.uint64(self.pcap), np.uint64(1 << (32 - k)))
1002
+ ]
1003
+
1004
+ for b in parts:
1005
+ msg += f' Pcap: Part {b} (ISO/IEC 15444-{b})\n'
1006
+ msg += f' Ccap: {self.ccap}'
1007
+
1008
+ return msg
1009
+
1010
+
937
1011
  class COCsegment(Segment):
938
1012
  """COC (Coding style Component) segment information.
939
1013
 
@@ -34,13 +34,12 @@ from .lib import openjp2 as opj2
34
34
 
35
35
 
36
36
  class Jp2k(Jp2kr):
37
- """Write JPEG 2000 files.
37
+ """Write JPEG 2000 files (and optionally read them as well).
38
38
 
39
39
  Parameters
40
40
  ----------
41
41
  filename : str or path
42
- The path to JPEG 2000 file. If you are only reading JPEG 2000 files,
43
- this is the only argument you need to supply.
42
+ The path to JPEG 2000 file.
44
43
  data : np.ndarray, optional
45
44
  Image data to be written to file.
46
45
  shape : Tuple[int, int, ...], optional
@@ -105,35 +104,6 @@ class Jp2k(Jp2kr):
105
104
  verbose : bool, optional
106
105
  Print informational messages produced by the OpenJPEG library.
107
106
 
108
- Examples
109
- --------
110
- >>> jfile = glymur.data.nemo()
111
- >>> jp2 = glymur.Jp2k(jfile)
112
- >>> jp2.shape
113
- (1456, 2592, 3)
114
- >>> image = jp2[:]
115
- >>> image.shape
116
- (1456, 2592, 3)
117
-
118
- Read a lower resolution thumbnail.
119
-
120
- >>> thumbnail = jp2[::2, ::2]
121
- >>> thumbnail.shape
122
- (728, 1296, 3)
123
-
124
- Make use of OpenJPEG's thread support
125
-
126
- >>> import time
127
- >>> if glymur.version.openjpeg_version >= '2.2.0':
128
- ... jp2file = glymur.data.nemo()
129
- ... jp2 = glymur.Jp2k(jp2file)
130
- ... t0 = time.time(); data = jp2[:]; t1 = time.time()
131
- ... t1 - t0 #doctest: +SKIP
132
- 0.9024193286895752
133
- ... glymur.set_options('lib.num_threads', 4)
134
- ... t0 = time.time(); data = jp2[:]; t1 = time.time()
135
- ... t1 - t0 #doctest: +SKIP
136
- 0.4060473537445068
137
107
  """
138
108
 
139
109
  def __init__(
@@ -800,12 +770,6 @@ class Jp2k(Jp2kr):
800
770
  -------
801
771
  Jp2k
802
772
  Newly wrapped Jp2k object.
803
-
804
- Examples
805
- --------
806
- >>> jfile = glymur.data.goodstuff()
807
- >>> j2k = glymur.Jp2k(jfile)
808
- >>> jp2 = j2k.wrap('jp2_from_j2k.jp2')
809
773
  """
810
774
  if boxes is None:
811
775
  boxes = self._get_default_jp2_boxes()
@@ -55,6 +55,19 @@ class Jp2kr(Jp2kBox):
55
55
  >>> thumbnail = jp2[::2, ::2]
56
56
  >>> thumbnail.shape
57
57
  (728, 1296, 3)
58
+
59
+ Make use of OpenJPEG's thread support
60
+
61
+ >>> import time
62
+ >>> jp2file = glymur.data.nemo()
63
+ >>> jp2 = glymur.Jp2k(jp2file)
64
+ >>> t0 = time.time(); data = jp2[:]; t1 = time.time()
65
+ >>> t1 - t0 #doctest: +SKIP
66
+ 0.9024193286895752
67
+ >>> glymur.set_option('lib.num_threads', 4)
68
+ >>> t0 = time.time(); data = jp2[:]; t1 = time.time()
69
+ >>> t1 - t0 #doctest: +SKIP
70
+ 0.4060473537445068
58
71
  """
59
72
 
60
73
  def __init__(
@@ -233,7 +246,7 @@ class Jp2kr(Jp2kBox):
233
246
 
234
247
  @property
235
248
  def codestream(self):
236
- """Metadata for JP2 or J2K codestream (header only)."""
249
+ """Metadata for JP2 or J2K codestream header."""
237
250
  if self._codestream is None:
238
251
  self._codestream = self.get_codestream(header_only=True)
239
252
  return self._codestream
@@ -523,6 +536,9 @@ class Jp2kr(Jp2kBox):
523
536
  def read(self, **kwargs):
524
537
  """Read a JPEG 2000 image.
525
538
 
539
+ .. deprecated:: 0.13.5
540
+ Use numpy-style slicing instead.
541
+
526
542
  Returns
527
543
  -------
528
544
  img_array : ndarray
@@ -724,7 +740,7 @@ class Jp2kr(Jp2kBox):
724
740
  verbose=False, ignore_pclr_cmap_cdef=False):
725
741
  """Read a JPEG 2000 image.
726
742
 
727
- The only time you should use this method is when the image has
743
+ The only time you should ever use this method is when the image has
728
744
  different subsampling factors across components. Otherwise you should
729
745
  use the read method.
730
746
 
@@ -870,6 +886,10 @@ class Jp2kr(Jp2kBox):
870
886
  def get_codestream(self, header_only=True):
871
887
  """Retrieve codestream.
872
888
 
889
+ This differs from the codestream property in that segment
890
+ metadata that lies past the end of the codestream header
891
+ can be retrieved.
892
+
873
893
  Parameters
874
894
  ----------
875
895
  header_only : bool, optional
@@ -862,7 +862,7 @@ class Tiff2Jp2k(object):
862
862
  partial_jp2_tile_rows = (self.imageheight / jth) != (self.imageheight // jth) # noqa : E501
863
863
  partial_jp2_tile_cols = (self.imagewidth / jtw) != (self.imagewidth // jtw) # noqa : E501
864
864
 
865
- num_jp2k_tile_rows = int(np.ceil(self.imagewidth / jtw))
865
+ num_jp2k_tile_rows = int(np.ceil(self.imageheight / jth))
866
866
  num_jp2k_tile_cols = int(np.ceil(self.imagewidth / jtw))
867
867
 
868
868
  jp2k_tile_row = int(np.ceil(jp2k_tile_num // num_jp2k_tile_cols))
@@ -20,7 +20,7 @@ from .lib import tiff
20
20
 
21
21
  # Do not change the format of this next line! Doing so risks breaking
22
22
  # setup.py
23
- version = "0.13.4"
23
+ version = "0.13.6"
24
24
 
25
25
  version_tuple = parse(version).release
26
26
 
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = Glymur
3
- version = 0.13.4
3
+ version = 0.13.6
4
4
  author = 'John Evans'
5
5
  author_email = "John Evans" <jevans667cc@proton.me>
6
6
  license = 'MIT'
@@ -30,6 +30,19 @@ class TestSuite(fixtures.TestCommon):
30
30
  self.p1_06 = ir.files('tests.data').joinpath('p1_06.j2k')
31
31
  self.issue142 = ir.files('tests.data').joinpath('issue142.j2k')
32
32
  self.edf_c2_1178956 = ir.files('tests.data').joinpath('edf_c2_1178956.jp2') # noqa : E501
33
+ self.htj2k = ir.files('tests.data').joinpath('oj-ht-byte.jph')
34
+
35
+ def test_cap_marker_segment(self):
36
+ """
37
+ SCENARIO: the file has a CAP marker segment for the 3rd segment
38
+
39
+ EXPECTED RESULT: the segment metadata is verified
40
+ """
41
+ j = Jp2k(self.htj2k)
42
+ cap = j.codestream.segment[2]
43
+
44
+ self.assertEqual(cap.pcap, 131072)
45
+ self.assertEqual(cap.ccap, (3,))
33
46
 
34
47
  def test_unrecognized_marker(self):
35
48
  """
@@ -29,8 +29,8 @@ from . import fixtures
29
29
 
30
30
 
31
31
  @unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
32
- @unittest.skipIf(glymur.version.openjpeg_version < '2.3.0',
33
- "Requires as least v2.3.0")
32
+ @unittest.skipIf(glymur.version.openjpeg_version < '2.4.0',
33
+ "Requires as least v2.4.0")
34
34
  class TestJp2k(fixtures.TestCommon):
35
35
  """These tests should be run by just about all configuration."""
36
36
 
@@ -29,8 +29,8 @@ from . import fixtures
29
29
 
30
30
 
31
31
  @unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
32
- @unittest.skipIf(glymur.version.openjpeg_version < '2.3.0',
33
- "Requires as least v2.3.0")
32
+ @unittest.skipIf(glymur.version.openjpeg_version < '2.4.0',
33
+ "Requires as least v2.4.0")
34
34
  class TestJp2kr(fixtures.TestCommon):
35
35
  """These tests should be run by just about all configuration."""
36
36
 
@@ -42,6 +42,22 @@ class TestPrinting(fixtures.TestCommon):
42
42
  super().tearDown()
43
43
  glymur.reset_option('all')
44
44
 
45
+ def test_cap_segment(self):
46
+ """
47
+ Scenario: Print a CAP segment
48
+
49
+ Expected Result: segment is verified
50
+ """
51
+ htj2k_file = ir.files('tests.data').joinpath('oj-ht-byte.jph')
52
+ j = glymur.Jp2kr(htj2k_file)
53
+ actual = str(j.codestream.segment[2])
54
+ expected = (
55
+ 'CAP marker segment @ (467, 8)\n'
56
+ ' Pcap: Part 15 (ISO/IEC 15444-15)\n'
57
+ ' Ccap: (3,)'
58
+ )
59
+ self.assertEqual(actual, expected)
60
+
45
61
  def test_empty_file(self):
46
62
  """
47
63
  SCENARIO: Print the file after with object is constructed, but
@@ -22,8 +22,8 @@ from . import fixtures
22
22
 
23
23
  @unittest.skipIf(os.cpu_count() < 2, "makes no sense if 2 cores not there")
24
24
  @unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
25
- @unittest.skipIf(glymur.version.openjpeg_version < '2.3.0',
26
- "Requires as least v2.3.0")
25
+ @unittest.skipIf(glymur.version.openjpeg_version < '2.4.0',
26
+ "Requires as least v2.4.0")
27
27
  class TestSuite(fixtures.TestCommon):
28
28
  """Test behavior when multiple threads are possible."""
29
29
 
@@ -79,7 +79,7 @@ class TestSuite(fixtures.TestCommon):
79
79
  glymur.set_option('lib.num_threads', 2)
80
80
 
81
81
  @unittest.skipIf(
82
- glymur.version.openjpeg_version < '2.4.0', "Requires as least v2.3.0"
82
+ glymur.version.openjpeg_version < '2.4.0', "Requires as least v2.4.0"
83
83
  )
84
84
  def test_threads_write_support__ge_2p4(self):
85
85
  """
@@ -43,6 +43,7 @@ class TestSuite(fixtures.TestCommon):
43
43
  cls.ycbcr_bg = root.joinpath('ycbcr_bg.tif')
44
44
  cls.ycbcr_stripped = root.joinpath('ycbcr_stripped.tif')
45
45
  cls.stripped = root.joinpath('stripped.tif')
46
+ cls.moon63 = root.joinpath('moon63.tif')
46
47
 
47
48
  test_tiff_dir = tempfile.mkdtemp()
48
49
  cls.test_tiff_path = pathlib.Path(test_tiff_dir)
@@ -1919,3 +1920,26 @@ class TestSuite(fixtures.TestCommon):
1919
1920
  expected = expected[:, :, :3]
1920
1921
 
1921
1922
  np.testing.assert_array_equal(actual, expected)
1923
+
1924
+ def test_unevenly_tiled(self):
1925
+ """
1926
+ SCENARIO: Convert monochromatic TIFF file to JP2. The TIFF is tiled
1927
+ 2x3, and the 2nd row of tiles consists of partial tiles.
1928
+
1929
+ EXPECTED RESULT: The JP2 file validates
1930
+ """
1931
+ with Tiff2Jp2k(
1932
+ self.moon63, self.temp_jp2_filename, tilesize=(32, 32)
1933
+ ) as j:
1934
+ j.run()
1935
+
1936
+ jp2 = Jp2k(self.temp_jp2_filename)
1937
+ actual = jp2[:]
1938
+ expected = skimage.io.imread(self.moon63)
1939
+ np.testing.assert_array_equal(actual, expected)
1940
+
1941
+ c = jp2.get_codestream()
1942
+ self.assertEqual(c.segment[1].xsiz, 96)
1943
+ self.assertEqual(c.segment[1].ysiz, 63)
1944
+ self.assertEqual(c.segment[1].xtsiz, 32)
1945
+ self.assertEqual(c.segment[1].ytsiz, 32)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes