diff --git a/pyproject.toml b/pyproject.toml index bb3257de95090401c88389695e18dff861752bc2..579538438beea2e75d64651da8499fe248c2d93b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,38 @@ [build-system] requires = [ - "setuptools>=42", - "wheel", - "setuptools_scm[toml]>3.4", + "setuptools>=60", + "setuptools-scm>=8.0", ] build-backend = "setuptools.build_meta" +[project] +dynamic = ["version"] +name = "zolfa-nd2reader" +authors = [ + {name = "Ruben Verweij", email = "ruben@lighthacking.nl"}, + {name = "Lorenzo Zolfanelli", email = "lorenzo.zolfanelli@espci.psl.eu"} +] +maintainers = [ + {name = "Lorenzo Zolfanelli", email = "lorenzo.zolfanelli@espci.psl.eu"} +] +description = "A tool for reading ND2 files produced by NIS Elements" +readme = "README.md" +classifiers = [ + "Development Status :: 4 - Beta", + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", + "Intended Audience :: Science/Research", + "Topic :: Scientific/Engineering :: Image Processing", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12" +] + +dependencies = [ + "numpy >= 1.14", + "xmltodict >= 0.9.2", + "PIMS >= 0.5.0" +] + [tool.setuptools_scm] write_to = "src/zolfa/nd2reader/version.py" diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 291f4e5a8ce985a08e3deab00aaedbedccc13d9a..0000000000000000000000000000000000000000 --- a/setup.cfg +++ /dev/null @@ -1,42 +0,0 @@ -[metadata] -name = zolfa-nd2reader -version = attr:zolfa.nd2reader.version -description = A tool for reading ND2 files produced by NIS Elements -description-file = README.md -classifiers = - Development Status :: 5 - Production/Stable - Intended Audience :: Science/Research - License :: Freely Distributable - License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+) - Operating System :: POSIX :: Linux - Programming Language :: Python :: 2.7 - Programming Language :: Python :: 3.4 - Topic :: Scientific/Engineering -keywords = - nd2 - nikon - microscopy - NIS Elements -author = Ruben Verweij, Lorenzo Zolfanelli -author_email = dev@zolfa.nl -url = https://projects.lilik.it/zolfa/zolfa-nd2reader - -[options] -packages = find_namespace: -package_dir = - = src -python_requires = >=3.6 -install_requires = - numpy>=1.14 - six>=1.4 - xmltodict>=0.9.2 - PIMS>=0.5.0 -setup_requires = - setuptools_scm - -[options.packages.find] -where = src -include = zolfa.* - -[bdist_wheel] -universal=1 \ No newline at end of file diff --git a/src/zolfa/nd2reader/__init__.py b/src/zolfa/nd2reader/__init__.py index 3b424e162f3706568d8be5223edbb2df48670cc9..9aa0d339234a8587ca6c84110999a38fe26c5824 100644 --- a/src/zolfa/nd2reader/__init__.py +++ b/src/zolfa/nd2reader/__init__.py @@ -2,10 +2,7 @@ from os import path from zolfa.nd2reader.reader import ND2Reader from zolfa.nd2reader.legacy import Nd2 -try: - import importlib.metadata as importlib_metadata -except: - import importlib_metadata +import importlib.metadata as importlib_metadata try: __version__ = importlib_metadata.version(__name__) diff --git a/src/zolfa/nd2reader/artificial.py b/src/zolfa/nd2reader/artificial.py index 05f460944333f7af860444a43cafaecac2a26fe9..8c72ff253c08fd5f130629ce9022832109fc9cd4 100644 --- a/src/zolfa/nd2reader/artificial.py +++ b/src/zolfa/nd2reader/artificial.py @@ -1,6 +1,5 @@ """Functions to create artificial nd2 data for testing purposes """ -import six import numpy as np import struct from zolfa.nd2reader.common import check_or_make_dir @@ -14,18 +13,18 @@ global_labels = ['image_attributes', 'image_text_info', 'image_metadata', 'acquisition_frames', 'lut_data', 'grabber_settings', 'custom_data', 'app_info', 'image_frame_0'] -global_file_labels = ["ImageAttributesLV!", "ImageTextInfoLV!", - "ImageMetadataLV!", "ImageMetadataSeqLV|0!", - "ImageCalibrationLV|0!", "CustomData|X!", "CustomData|Y!", - "CustomData|Z!", "CustomData|RoiMetadata_v1!", - "CustomData|PFS_STATUS!", "CustomData|PFS_OFFSET!", - "CustomData|GUIDStore!", "CustomData|CustomDescriptionV1_0!", - "CustomData|Camera_ExposureTime1!", "CustomData|CameraTemp1!", - "CustomData|AcqTimesCache!", "CustomData|AcqTimes2Cache!", - "CustomData|AcqFramesCache!", "CustomDataVar|LUTDataV1_0!", - "CustomDataVar|GrabberCameraSettingsV1_0!", - "CustomDataVar|CustomDataV2_0!", "CustomDataVar|AppInfo_V1_0!", - "ImageDataSeq|0!"] +global_file_labels = [b"ImageAttributesLV!", b"ImageTextInfoLV!", + b"ImageMetadataLV!", b"ImageMetadataSeqLV|0!", + b"ImageCalibrationLV|0!", b"CustomData|X!", b"CustomData|Y!", + b"CustomData|Z!", b"CustomData|RoiMetadata_v1!", + b"CustomData|PFS_STATUS!", b"CustomData|PFS_OFFSET!", + b"CustomData|GUIDStore!", b"CustomData|CustomDescriptionV1_0!", + b"CustomData|Camera_ExposureTime1!", b"CustomData|CameraTemp1!", + b"CustomData|AcqTimesCache!", b"CustomData|AcqTimes2Cache!", + b"CustomData|AcqFramesCache!", b"CustomDataVar|LUTDataV1_0!", + b"CustomDataVar|GrabberCameraSettingsV1_0!", + b"CustomDataVar|CustomDataV2_0!", b"CustomDataVar|AppInfo_V1_0!", + b"ImageDataSeq|0!"] class ArtificialND2(object): @@ -107,7 +106,7 @@ class ArtificialND2(object): self.raw_text += struct.pack("Q", location) def _get_version_string(self): - return six.b('ND2 FILE SIGNATURE CHUNK NAME01!Ver%s.%s' % self.version) + return b'ND2 FILE SIGNATURE CHUNK NAME01!Ver%d.%d' % self.version def _get_version_byte_length(self): return 16 + len(self._get_version_string()) @@ -125,7 +124,7 @@ class ArtificialND2(object): tuple: (binary data, dictionary data) """ - raw_text = six.b('') + raw_text = b'' labels = global_labels file_labels = global_file_labels @@ -137,19 +136,19 @@ class ArtificialND2(object): version_length = self._get_version_byte_length() # calculate data length - label_length = np.sum([len(six.b(l)) + 16 for l in file_labels]) + label_length = np.sum([len(l) + 16 for l in file_labels]) # write label map cur_pos = version_length + label_length for label, file_label, data in zip(labels, file_labels, file_data): - raw_text += six.b(file_label) + raw_text += file_label data_length = len(data) raw_text += struct.pack('QQ', cur_pos, data_length) locations[label] = (cur_pos, data_length) cur_pos += data_length # write data - raw_text += six.b('').join(file_data) + raw_text += b''.join(file_data) return raw_text, locations, file_data_dict @@ -170,7 +169,7 @@ class ArtificialND2(object): raw_data = struct.pack('I', data) elif isinstance(data, float): raw_data = struct.pack('d', data) - elif isinstance(data, str): + elif isinstance(data, bytes): raw_data = self._str_to_padded_bytes(data) return raw_data @@ -180,14 +179,14 @@ class ArtificialND2(object): return self.data_types['metadata_item'] elif isinstance(data, int): return self.data_types['unsigned_int'] - elif isinstance(data, str): + elif isinstance(data, bytes): return self.data_types['string'] else: return self.data_types['double'] @staticmethod def _str_to_padded_bytes(data): - return six.b('').join([struct.pack('cx', six.b(s)) for s in data]) + struct.pack('xx') + return b''.join([struct.pack('Bx', s) for s in data]) + struct.pack('xx') def _pack_dict_with_metadata(self, data): raw_data = b'' @@ -217,44 +216,44 @@ class ArtificialND2(object): @staticmethod def _get_slx_img_attrib(): - return {'uiWidth': 128, - 'uiWidthBytes': 256, - 'uiHeight': 128, - 'uiComp': 1, - 'uiBpcInMemory': 16, - 'uiBpcSignificant': 12, - 'uiSequenceCount': 70, - 'uiTileWidth': 128, - 'uiTileHeight': 128, - 'eCompression': 2, - 'dCompressionParam': -1.0, - 'ePixelType': 1, - 'uiVirtualComponents': 1 + return {b'uiWidth': 128, + b'uiWidthBytes': 256, + b'uiHeight': 128, + b'uiComp': 1, + b'uiBpcInMemory': 16, + b'uiBpcSignificant': 12, + b'uiSequenceCount': 70, + b'uiTileWidth': 128, + b'uiTileHeight': 128, + b'eCompression': 2, + b'dCompressionParam': -1.0, + b'ePixelType': 1, + b'uiVirtualComponents': 1 } @staticmethod def _get_slx_picture_metadata(): - return {'sPicturePlanes': + return {b'sPicturePlanes': { - 'sPlaneNew': { + b'sPlaneNew': { # channels are numbered a0, a1, ..., aN - 'a0': { - 'sDescription': 'TRITC' - } + b'a0': { + b'sDescription': b'TRITC', } } - } + }, + } def _get_file_data(self, labels): file_data = [ - {'SLxImageAttributes': self._get_slx_img_attrib()}, # ImageAttributesLV!", + {b'SLxImageAttributes': self._get_slx_img_attrib()}, # ImageAttributesLV!", 7, # ImageTextInfoLV!", 7, # ImageMetadataLV!", - {'SLxPictureMetadata': self._get_slx_picture_metadata()}, # ImageMetadataSeqLV|0!", + {b'SLxPictureMetadata': self._get_slx_picture_metadata()}, # ImageMetadataSeqLV|0!", 7, # ImageCalibrationLV|0!", - 7, # CustomData|X!", - 7, # CustomData|Y!", - 7, # CustomData|Z!", + 7., # CustomData|X!", + 7., # CustomData|Y!", + 7., # CustomData|Z!", 7, # CustomData|RoiMetadata_v1!", 7, # CustomData|PFS_STATUS!", 7, # CustomData|PFS_OFFSET!", diff --git a/src/zolfa/nd2reader/common.py b/src/zolfa/nd2reader/common.py index c326bd83635d484b596cac649889e2eb142b187d..db99b52b914b423f5f3ca9f3a1c14ca2eb0fec79 100644 --- a/src/zolfa/nd2reader/common.py +++ b/src/zolfa/nd2reader/common.py @@ -2,7 +2,7 @@ import os import struct import array from datetime import datetime -import six +import io import re from zolfa.nd2reader.exceptions import InvalidVersionError @@ -156,7 +156,7 @@ def _parse_string(data): """ value = data.read(2) # the string ends at the first instance of \x00\x00 - while not value.endswith(six.b("\x00\x00")): + while not value.endswith(b"\x00\x00"): next_data = data.read(2) if len(next_data) == 0: break @@ -275,7 +275,7 @@ def read_metadata(data, count): if data is None: return None - data = six.BytesIO(data) + data = io.BytesIO(data) metadata = {} for _ in range(count): @@ -336,7 +336,7 @@ def get_from_dict_if_exists(key, dictionary, convert_key_to_binary=True): """ if convert_key_to_binary: - key = six.b(key) + key = key.encode('latin-1') if key not in dictionary: return None diff --git a/src/zolfa/nd2reader/common_raw_metadata.py b/src/zolfa/nd2reader/common_raw_metadata.py index 408339fa31ddd19c7fa1fc7265000e47d956cd89..21e7d04e939bd88bb0f9a51f613e9460f416c837 100644 --- a/src/zolfa/nd2reader/common_raw_metadata.py +++ b/src/zolfa/nd2reader/common_raw_metadata.py @@ -1,4 +1,3 @@ -import six import warnings from zolfa.nd2reader.common import get_from_dict_if_exists @@ -11,10 +10,10 @@ def parse_if_not_none(to_check, callback): def parse_dimension_text_line(line): - if six.b("Dimensions:") in line: - entries = line.split(six.b("\r\n")) + if b"Dimensions:" in line: + entries = line.split(b"\r\n") for entry in entries: - if entry.startswith(six.b("Dimensions:")): + if entry.startswith(b"Dimensions:"): return entry return None @@ -41,22 +40,22 @@ def parse_roi_type(type_no): def get_loops_from_data(loop_data): # special ND experiment - if six.b('pPeriod') not in loop_data: + if b'pPeriod' not in loop_data: return [] - if six.b('uiPeriodCount') in loop_data and loop_data[six.b('uiPeriodCount')] > 0: + if b'uiPeriodCount' in loop_data and loop_data[b'uiPeriodCount'] > 0: loops = [] - for i, period in enumerate(loop_data[six.b('pPeriod')]): + for i, period in enumerate(loop_data[b'pPeriod']): # exclude invalid periods - if six.b('pPeriodValid') in loop_data: + if b'pPeriodValid' in loop_data: try: - if loop_data[six.b('pPeriodValid')][i] == 1: - loops.append(loop_data[six.b('pPeriod')][period]) + if loop_data[b'pPeriodValid'][i] == 1: + loops.append(loop_data[b'pPeriod'][period]) except IndexError: continue else: # we can't be sure, append all - loops.append(loop_data[six.b('pPeriod')][period]) + loops.append(loop_data[b'pPeriod'][period]) return [loop_data] diff --git a/src/zolfa/nd2reader/label_map.py b/src/zolfa/nd2reader/label_map.py index dcd2e9cfe6cd4b5cfd82d1bb8cf920dbbbde5a1f..f51d4be973df28f61089541292604bb911ce4c75 100644 --- a/src/zolfa/nd2reader/label_map.py +++ b/src/zolfa/nd2reader/label_map.py @@ -1,4 +1,3 @@ -import six import struct import re @@ -31,7 +30,7 @@ class LabelMap(object): int: The location of the textual image information """ - return self._get_location(six.b("ImageTextInfoLV!")) + return self._get_location(b"ImageTextInfoLV!") @property def image_metadata(self): @@ -41,7 +40,7 @@ class LabelMap(object): int: The location of the image metadata """ - return self._get_location(six.b("ImageMetadataLV!")) + return self._get_location(b"ImageMetadataLV!") @property def image_events(self): @@ -51,7 +50,7 @@ class LabelMap(object): int: The location of the image events """ - return self._get_location(six.b("ImageEventsLV!")) + return self._get_location(b"ImageEventsLV!") @property def image_metadata_sequence(self): @@ -62,7 +61,7 @@ class LabelMap(object): int: The location of the image metadata sequence """ - return self._get_location(six.b("ImageMetadataSeqLV|0!")) + return self._get_location(b"ImageMetadataSeqLV|0!") def get_image_data_location(self, index): """Get the location of the image data @@ -72,7 +71,7 @@ class LabelMap(object): """ if not self._image_data: - regex = re.compile(six.b("""ImageDataSeq\|(\d+)!""")) + regex = re.compile(b"""ImageDataSeq\|(\d+)!""") for match in regex.finditer(self._data): if match: location = self._parse_data_location(match.end()) @@ -87,7 +86,7 @@ class LabelMap(object): int: The location of the image calibration """ - return self._get_location(six.b("ImageCalibrationLV|0!")) + return self._get_location(b"ImageCalibrationLV|0!") @property def image_attributes(self): @@ -97,7 +96,7 @@ class LabelMap(object): int: The location of the image attributes """ - return self._get_location(six.b("ImageAttributesLV!")) + return self._get_location(b"ImageAttributesLV!") @property def x_data(self): @@ -107,7 +106,7 @@ class LabelMap(object): int: The location of the custom x data """ - return self._get_location(six.b("CustomData|X!")) + return self._get_location(b"CustomData|X!") @property def y_data(self): @@ -117,7 +116,7 @@ class LabelMap(object): int: The location of the custom y data """ - return self._get_location(six.b("CustomData|Y!")) + return self._get_location(b"CustomData|Y!") @property def z_data(self): @@ -127,7 +126,7 @@ class LabelMap(object): int: The location of the custom z data """ - return self._get_location(six.b("CustomData|Z!")) + return self._get_location(b"CustomData|Z!") @property def roi_metadata(self): @@ -137,7 +136,7 @@ class LabelMap(object): int: The location of the regions of interest (ROIs) """ - return self._get_location(six.b("CustomData|RoiMetadata_v1!")) + return self._get_location(b"CustomData|RoiMetadata_v1!") @property def pfs_status(self): @@ -147,7 +146,7 @@ class LabelMap(object): int: The location of the perfect focus system (PFS) status """ - return self._get_location(six.b("CustomData|PFS_STATUS!")) + return self._get_location(b"CustomData|PFS_STATUS!") @property def pfs_offset(self): @@ -157,7 +156,7 @@ class LabelMap(object): int: The location of the perfect focus system (PFS) offset """ - return self._get_location(six.b("CustomData|PFS_OFFSET!")) + return self._get_location(b"CustomData|PFS_OFFSET!") @property def guid(self): @@ -167,7 +166,7 @@ class LabelMap(object): int: The location of the image guid """ - return self._get_location(six.b("CustomData|GUIDStore!")) + return self._get_location(b"CustomData|GUIDStore!") @property def description(self): @@ -177,7 +176,7 @@ class LabelMap(object): int: The location of the image description """ - return self._get_location(six.b("CustomData|CustomDescriptionV1_0!")) + return self._get_location(b"CustomData|CustomDescriptionV1_0!") @property def camera_exposure_time(self): @@ -187,7 +186,7 @@ class LabelMap(object): int: The location of the camera exposure time """ - return self._get_location(six.b("CustomData|Camera_ExposureTime1!")) + return self._get_location(b"CustomData|Camera_ExposureTime1!") @property def camera_temp(self): @@ -197,7 +196,7 @@ class LabelMap(object): int: The location of the camera temperature """ - return self._get_location(six.b("CustomData|CameraTemp1!")) + return self._get_location(b"CustomData|CameraTemp1!") @property def acquisition_times(self): @@ -207,7 +206,7 @@ class LabelMap(object): int: The location of the acquisition times, block 1 """ - return self._get_location(six.b("CustomData|AcqTimesCache!")) + return self._get_location(b"CustomData|AcqTimesCache!") @property def acquisition_times_2(self): @@ -217,7 +216,7 @@ class LabelMap(object): int: The location of the acquisition times, block 2 """ - return self._get_location(six.b("CustomData|AcqTimes2Cache!")) + return self._get_location(b"CustomData|AcqTimes2Cache!") @property def acquisition_frames(self): @@ -227,7 +226,7 @@ class LabelMap(object): int: The location of the acquisition frames """ - return self._get_location(six.b("CustomData|AcqFramesCache!")) + return self._get_location(b"CustomData|AcqFramesCache!") @property def lut_data(self): @@ -237,7 +236,7 @@ class LabelMap(object): int: The location of the LUT data """ - return self._get_location(six.b("CustomDataVar|LUTDataV1_0!")) + return self._get_location(b"CustomDataVar|LUTDataV1_0!") @property def grabber_settings(self): @@ -247,7 +246,7 @@ class LabelMap(object): int: The location of the grabber settings """ - return self._get_location(six.b("CustomDataVar|GrabberCameraSettingsV1_0!")) + return self._get_location(b"CustomDataVar|GrabberCameraSettingsV1_0!") @property def custom_data(self): @@ -257,7 +256,7 @@ class LabelMap(object): int: The location of the custom user data """ - return self._get_location(six.b("CustomDataVar|CustomDataV2_0!")) + return self._get_location(b"CustomDataVar|CustomDataV2_0!") @property def app_info(self): @@ -267,4 +266,4 @@ class LabelMap(object): int: The location of the application info metadata """ - return self._get_location(six.b("CustomDataVar|AppInfo_V1_0!")) + return self._get_location(b"CustomDataVar|AppInfo_V1_0!") diff --git a/src/zolfa/nd2reader/parser.py b/src/zolfa/nd2reader/parser.py index e3ceea3cb4ed578744160756393f8f04a1b5c73e..45fcde16ef6ac2550f1c8e2f0ca77aa283aff5cf 100644 --- a/src/zolfa/nd2reader/parser.py +++ b/src/zolfa/nd2reader/parser.py @@ -2,7 +2,6 @@ import struct import array -import six import warnings from pims.base_frames import Frame import numpy as np @@ -18,8 +17,8 @@ class Parser(object): """ CHUNK_HEADER = 0xabeceda - CHUNK_MAP_START = six.b("ND2 FILEMAP SIGNATURE NAME 0001!") - CHUNK_MAP_END = six.b("ND2 CHUNK MAP SIGNATURE 0000001!") + CHUNK_MAP_START = b"ND2 FILEMAP SIGNATURE NAME 0001!" + CHUNK_MAP_END = b"ND2 CHUNK MAP SIGNATURE 0000001!" supported_file_versions = {(3, None): True} diff --git a/src/zolfa/nd2reader/raw_metadata.py b/src/zolfa/nd2reader/raw_metadata.py index 9b2e8e38707a7b6ce0dd1ef1a770f0d30dfd7bad..7ef593dc0a1444d67ba970a33af5db194b3cf17b 100644 --- a/src/zolfa/nd2reader/raw_metadata.py +++ b/src/zolfa/nd2reader/raw_metadata.py @@ -1,6 +1,5 @@ import re import xmltodict -import six import numpy as np import warnings @@ -74,7 +73,7 @@ class RawMetadata(object): def _parse_width_or_height(self, key): try: - length = self.image_attributes[six.b('SLxImageAttributes')][six.b(key)] + length = self.image_attributes[b'SLxImageAttributes'][key.encode('latin-1')] except KeyError: length = None @@ -88,13 +87,13 @@ class RawMetadata(object): def _parse_date(self): try: - return parse_date(self.image_text_info[six.b('SLxImageTextInfo')]) + return parse_date(self.image_text_info[b'SLxImageTextInfo']) except KeyError: return None def _parse_calibration(self): try: - return self.image_calibration.get(six.b('SLxCalibration'), {}).get(six.b('dCalibration')) + return self.image_calibration.get(b'SLxCalibration', {}).get(b'dCalibration') except KeyError: return None @@ -117,7 +116,7 @@ class RawMetadata(object): return [] try: - metadata = self.image_metadata_sequence[six.b('SLxPictureMetadata')][six.b('sPicturePlanes')] + metadata = self.image_metadata_sequence[b'SLxPictureMetadata'][b'sPicturePlanes'] except KeyError: return [] @@ -132,19 +131,19 @@ class RawMetadata(object): # indicates the order in which the channel is stored. So by sorting the dicts alphabetically # we get the correct order. channels = [] - for valid, (label, chan) in zip(validity, sorted(metadata[six.b('sPlaneNew')].items())): + for valid, (label, chan) in zip(validity, sorted(metadata[b'sPlaneNew'].items())): if not valid: continue - if chan[six.b('sDescription')] is not None: - channels.append(chan[six.b('sDescription')].decode("utf8")) + if chan[b'sDescription'] is not None: + channels.append(chan[b'sDescription'].decode("utf8")) else: channels.append('Unknown') return channels def _get_channel_validity_list(self, metadata): try: - validity = self.image_metadata[six.b('SLxExperiment')][six.b('ppNextLevelEx')][six.b('')][0][ - six.b('ppNextLevelEx')][six.b('')][0][six.b('pItemValid')] + validity = self.image_metadata[b'SLxExperiment'][b'ppNextLevelEx'][b''][0][ + b'ppNextLevelEx'][b''][0][b'pItemValid'] except (KeyError, TypeError): # If none of the channels have been deleted, there is no validity list, so we just make one validity = [True for _ in metadata] @@ -220,7 +219,7 @@ class RawMetadata(object): return [] try: - metadata = self.image_metadata_sequence[six.b('SLxPictureMetadata')] + metadata = self.image_metadata_sequence[b'SLxPictureMetadata'] except KeyError: return [] @@ -234,7 +233,7 @@ class RawMetadata(object): return [] try: - metadata = self.image_metadata_sequence[six.b('SLxPictureMetadata')][b'sPicturePlanes'] + metadata = self.image_metadata_sequence[b'SLxPictureMetadata'][b'sPicturePlanes'] except KeyError: return [] @@ -261,12 +260,12 @@ class RawMetadata(object): is always there and in the same exact format, so we just parse that instead. """ - dimension_text = six.b("") + dimension_text = b"" if self.image_text_info is None: return dimension_text try: - textinfo = self.image_text_info[six.b('SLxImageTextInfo')].values() + textinfo = self.image_text_info[b'SLxImageTextInfo'].values() except KeyError: return dimension_text @@ -282,8 +281,7 @@ class RawMetadata(object): if dimension_text is None: return [] - if six.PY3: - dimension_text = dimension_text.decode("utf8") + dimension_text = dimension_text.decode("utf8") match = re.match(pattern, dimension_text) if not match: @@ -301,7 +299,7 @@ class RawMetadata(object): if self.image_attributes is None: return 0 try: - total_images = self.image_attributes[six.b('SLxImageAttributes')][six.b('uiSequenceCount')] + total_images = self.image_attributes[b'SLxImageAttributes'][b'uiSequenceCount'] except KeyError: total_images = None @@ -311,19 +309,19 @@ class RawMetadata(object): """Parse the raw ROI metadata. """ - if self.roi_metadata is None or not six.b('RoiMetadata_v1') in self.roi_metadata: + if self.roi_metadata is None or not b'RoiMetadata_v1' in self.roi_metadata: return - raw_roi_data = self.roi_metadata[six.b('RoiMetadata_v1')] + raw_roi_data = self.roi_metadata[b'RoiMetadata_v1'] - if not six.b('m_vectGlobal_Size') in raw_roi_data: + if not b'm_vectGlobal_Size' in raw_roi_data: return - number_of_rois = raw_roi_data[six.b('m_vectGlobal_Size')] + number_of_rois = raw_roi_data[b'm_vectGlobal_Size'] roi_objects = [] for i in range(number_of_rois): - current_roi = raw_roi_data[six.b('m_vectGlobal_%d' % i)] + current_roi = raw_roi_data[b'm_vectGlobal_%d' % i] roi_objects.append(self._parse_roi(current_roi)) self._metadata_parsed['rois'] = roi_objects @@ -340,17 +338,17 @@ class RawMetadata(object): dict: the parsed ROI metadata """ - number_of_timepoints = raw_roi_dict[six.b('m_vectAnimParams_Size')] + number_of_timepoints = raw_roi_dict[b'm_vectAnimParams_Size'] roi_dict = { "timepoints": [], "positions": [], "sizes": [], - "shape": parse_roi_shape(raw_roi_dict[six.b('m_sInfo')][six.b('m_uiShapeType')]), - "type": parse_roi_type(raw_roi_dict[six.b('m_sInfo')][six.b('m_uiInterpType')]) + "shape": parse_roi_shape(raw_roi_dict[b'm_sInfo'][b'm_uiShapeType']), + "type": parse_roi_type(raw_roi_dict[b'm_sInfo'][b'm_uiInterpType']) } for i in range(number_of_timepoints): - roi_dict = self._parse_vect_anim(roi_dict, raw_roi_dict[six.b('m_vectAnimParams_%d' % i)]) + roi_dict = self._parse_vect_anim(roi_dict, raw_roi_dict[b'm_vectAnimParams_%d' % i]) # convert to NumPy arrays roi_dict["timepoints"] = np.array(roi_dict["timepoints"], dtype=np.float) @@ -371,23 +369,23 @@ class RawMetadata(object): dict: the parsed metadata """ - roi_dict["timepoints"].append(animation_dict[six.b('m_dTimeMs')]) + roi_dict["timepoints"].append(animation_dict[b'm_dTimeMs']) image_width = self._metadata_parsed["width"] * self._metadata_parsed["pixel_microns"] image_height = self._metadata_parsed["height"] * self._metadata_parsed["pixel_microns"] # positions are taken from the center of the image as a fraction of the half width/height of the image - position = np.array((0.5 * image_width * (1 + animation_dict[six.b('m_dCenterX')]), - 0.5 * image_height * (1 + animation_dict[six.b('m_dCenterY')]), - animation_dict[six.b('m_dCenterZ')])) + position = np.array((0.5 * image_width * (1 + animation_dict[b'm_dCenterX']), + 0.5 * image_height * (1 + animation_dict[b'm_dCenterY']), + animation_dict[b'm_dCenterZ'])) roi_dict["positions"].append(position) - size_dict = animation_dict[six.b('m_sBoxShape')] + size_dict = animation_dict[b'm_sBoxShape'] # sizes are fractions of the half width/height of the image - roi_dict["sizes"].append((size_dict[six.b('m_dSizeX')] * 0.25 * image_width, - size_dict[six.b('m_dSizeY')] * 0.25 * image_height, - size_dict[six.b('m_dSizeZ')])) + roi_dict["sizes"].append((size_dict[b'm_dSizeX'] * 0.25 * image_width, + size_dict[b'm_dSizeY'] * 0.25 * image_height, + size_dict[b'm_dSizeZ'])) return roi_dict def _parse_experiment_metadata(self): @@ -399,16 +397,16 @@ class RawMetadata(object): 'loops': [] } - if self.image_metadata is None or six.b('SLxExperiment') not in self.image_metadata: + if self.image_metadata is None or b'SLxExperiment' not in self.image_metadata: return - raw_data = self.image_metadata[six.b('SLxExperiment')] + raw_data = self.image_metadata[b'SLxExperiment'] - if six.b('wsApplicationDesc') in raw_data: - self._metadata_parsed['experiment']['description'] = raw_data[six.b('wsApplicationDesc')].decode('utf8') + if b'wsApplicationDesc' in raw_data: + self._metadata_parsed['experiment']['description'] = raw_data[b'wsApplicationDesc'].decode('utf8') - if six.b('uLoopPars') in raw_data: - self._metadata_parsed['experiment']['loops'] = self._parse_loop_data(raw_data[six.b('uLoopPars')]) + if b'uLoopPars' in raw_data: + self._metadata_parsed['experiment']['loops'] = self._parse_loop_data(raw_data[b'uLoopPars']) def _parse_loop_data(self, loop_data): """Parse the experimental loop data @@ -507,19 +505,19 @@ class RawMetadata(object): events = read_metadata(read_chunk(self._fh, self._label_map.image_events), 1) - if events is None or six.b('RLxExperimentRecord') not in events: + if events is None or b'RLxExperimentRecord' not in events: return - events = events[six.b('RLxExperimentRecord')][six.b('pEvents')] + events = events[b'RLxExperimentRecord'][b'pEvents'] if len(events) == 0: return - for event in events[six.b('')]: + for event in events[b'']: event_info = { - 'index': event[six.b('I')], - 'time': event[six.b('T')], - 'type': event[six.b('M')], + 'index': event[b'I'], + 'time': event[b'T'], + 'type': event[b'M'], } if event_info['type'] in event_names.keys(): event_info['name'] = event_names[event_info['type']] diff --git a/tests/test_artificial.py b/tests/test_artificial.py index 8595d3d676e9289c7510a3ad18fad1bdebef8db5..ab9de287cfd177a79effc369e47d4a03a6a21053 100644 --- a/tests/test_artificial.py +++ b/tests/test_artificial.py @@ -1,6 +1,5 @@ import unittest from os import path -import six import struct from zolfa.nd2reader.artificial import ArtificialND2 diff --git a/tests/test_common.py b/tests/test_common.py index 27c548bb191c92ac44bfa6d0a69237fdd66c648c..b594cb16272d95f704e51b0d239f2faa920468b6 100644 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -2,7 +2,7 @@ import unittest from os import path import array -import six +import io import struct from zolfa.nd2reader.artificial import ArtificialND2 @@ -48,20 +48,20 @@ class TestCommon(unittest.TestCase): def test_parse_date_24(self): date_format = "%m/%d/%Y %H:%M:%S" date = '02/13/2016 23:43:37' - textinfo = {six.b('TextInfoItem9'): six.b(date)} + textinfo = {b'TextInfoItem9': date.encode('latin-1')} result = parse_date(textinfo) self.assertEqual(result.strftime(date_format), date) def test_parse_date_12(self): date_format = "%m/%d/%Y %I:%M:%S %p" date = '02/13/2016 11:43:37 PM' - textinfo = {six.b('TextInfoItem9'): six.b(date)} + textinfo = {b'TextInfoItem9': date.encode('latin-1')} result = parse_date(textinfo) self.assertEqual(result.strftime(date_format), date) def test_parse_date_exception(self): date = 'i am no date' - textinfo = {six.b('TextInfoItem9'): six.b(date)} + textinfo = {b'TextInfoItem9': date.encode('latin-1')} result = parse_date(textinfo) self.assertIsNone(result) @@ -82,7 +82,7 @@ class TestCommon(unittest.TestCase): @staticmethod def _prepare_bin_stream(binary_format, *value): - file = six.BytesIO() + file = io.BytesIO() data = struct.pack(binary_format, *value) file.write(data) file.seek(0) @@ -101,10 +101,10 @@ class TestCommon(unittest.TestCase): file = self._prepare_bin_stream("d", 47.9) self.assertEqual(_parse_double(file), 47.9) - test_string = 'colloid' - file = self._prepare_bin_stream("%ds" % len(test_string), six.b(test_string)) + test_string = b'colloid' + file = self._prepare_bin_stream("%ds" % len(test_string), test_string) parsed = _parse_string(file) - self.assertEqual(parsed, six.b(test_string)) + self.assertEqual(parsed, test_string) test_data = [1, 2, 3, 4, 5] file = self._prepare_bin_stream("Q" + ''.join(['B'] * len(test_data)), len(test_data), *test_data) @@ -113,7 +113,7 @@ class TestCommon(unittest.TestCase): def test_get_from_dict_if_exists(self): test_dict = { - six.b('existing'): 'test', + b'existing': 'test', 'string': 'test2' } @@ -127,7 +127,7 @@ class TestCommon(unittest.TestCase): chunk_location = artificial.locations['image_attributes'][0] chunk_read = read_chunk(fh, chunk_location) - real_data = six.BytesIO(artificial.raw_text) + real_data = io.BytesIO(artificial.raw_text) real_data.seek(chunk_location) diff --git a/tests/test_raw_metadata.py b/tests/test_raw_metadata.py index 8f560921a3cf1e0a7957a2711964c1f521dd1f30..0eddbf6cb51a384410e9d56989f05a0921802d96 100644 --- a/tests/test_raw_metadata.py +++ b/tests/test_raw_metadata.py @@ -1,5 +1,4 @@ import unittest -import six from zolfa.nd2reader.artificial import ArtificialND2 from zolfa.nd2reader.label_map import LabelMap @@ -26,9 +25,9 @@ class TestRawMetadata(unittest.TestCase): self.assertIsNone(parse_roi_type(-1)) def test_parse_dimension_text(self): - line = six.b('Metadata:\r\nDimensions: T(443) x \xce\xbb(1)\r\nCamera Name: Andor Zyla VSC-01537') - self.assertEqual(parse_dimension_text_line(line), six.b('Dimensions: T(443) x \xce\xbb(1)')) - self.assertIsNone(parse_dimension_text_line(six.b('Dim: nothing'))) + line = b'Metadata:\r\nDimensions: T(443) x \xce\xbb(1)\r\nCamera Name: Andor Zyla VSC-01537' + self.assertEqual(parse_dimension_text_line(line), b'Dimensions: T(443) x \xce\xbb(1)') + self.assertIsNone(parse_dimension_text_line(b'Dim: nothing')) def test_parse_z_levels(self): # smokescreen test to check if the fallback to z_coordinates is working @@ -62,7 +61,7 @@ class TestRawMetadata(unittest.TestCase): def _assert_dicts_equal(self, parsed_dict, original_dict): for attribute in original_dict.keys(): - parsed_key = six.b(attribute) + parsed_key = attribute self.assertIn(parsed_key, parsed_dict.keys()) if isinstance(parsed_dict[parsed_key], dict): diff --git a/tests/test_reader.py b/tests/test_reader.py index d01032d08d1c961e2ca69b330c16ef39e4f28860..7a0d5c8c7c68932c0cc86f53b429df5a94a1c920 100644 --- a/tests/test_reader.py +++ b/tests/test_reader.py @@ -17,9 +17,9 @@ class TestReader(unittest.TestCase): self.assertTrue('nd2' in ND2Reader.class_exts()) def cmp_two_readers(self, r1, r2): - attributes = r1.data['image_attributes']['SLxImageAttributes'] - self.assertEqual(r2.metadata['width'], attributes['uiWidth']) - self.assertEqual(r2.metadata['height'], attributes['uiHeight']) + attributes = r1.data['image_attributes'][b'SLxImageAttributes'] + self.assertEqual(r2.metadata['width'], attributes[b'uiWidth']) + self.assertEqual(r2.metadata['height'], attributes[b'uiHeight']) self.assertEqual(r2.metadata['width'], r2.sizes['x']) self.assertEqual(r2.metadata['height'], r2.sizes['y'])