PyOPIA 2.4.6__tar.gz → 2.5.0__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.
- {pyopia-2.4.6 → pyopia-2.5.0}/PKG-INFO +1 -1
- pyopia-2.5.0/pyopia/__init__.py +1 -0
- {pyopia-2.4.6 → pyopia-2.5.0}/pyopia/instrument/silcam.py +115 -2
- {pyopia-2.4.6 → pyopia-2.5.0}/pyopia/io.py +33 -6
- pyopia-2.4.6/pyopia/__init__.py +0 -1
- {pyopia-2.4.6 → pyopia-2.5.0}/LICENSE +0 -0
- {pyopia-2.4.6 → pyopia-2.5.0}/README.md +0 -0
- {pyopia-2.4.6 → pyopia-2.5.0}/pyopia/background.py +0 -0
- {pyopia-2.4.6 → pyopia-2.5.0}/pyopia/classify.py +0 -0
- {pyopia-2.4.6 → pyopia-2.5.0}/pyopia/cli.py +0 -0
- {pyopia-2.4.6 → pyopia-2.5.0}/pyopia/exampledata.py +0 -0
- {pyopia-2.4.6 → pyopia-2.5.0}/pyopia/instrument/__init__.py +0 -0
- {pyopia-2.4.6 → pyopia-2.5.0}/pyopia/instrument/common.py +0 -0
- {pyopia-2.4.6 → pyopia-2.5.0}/pyopia/instrument/holo.py +0 -0
- {pyopia-2.4.6 → pyopia-2.5.0}/pyopia/instrument/uvp.py +0 -0
- {pyopia-2.4.6 → pyopia-2.5.0}/pyopia/pipeline.py +0 -0
- {pyopia-2.4.6 → pyopia-2.5.0}/pyopia/plotting.py +0 -0
- {pyopia-2.4.6 → pyopia-2.5.0}/pyopia/process.py +0 -0
- {pyopia-2.4.6 → pyopia-2.5.0}/pyopia/simulator/__init__.py +0 -0
- {pyopia-2.4.6 → pyopia-2.5.0}/pyopia/simulator/silcam.py +0 -0
- {pyopia-2.4.6 → pyopia-2.5.0}/pyopia/statistics.py +0 -0
- {pyopia-2.4.6 → pyopia-2.5.0}/pyopia/tests/__init__.py +0 -0
- {pyopia-2.4.6 → pyopia-2.5.0}/pyopia/tests/test_classify.py +0 -0
- {pyopia-2.4.6 → pyopia-2.5.0}/pyopia/tests/test_notebooks.py +0 -0
- {pyopia-2.4.6 → pyopia-2.5.0}/pyopia/tests/test_pipeline.py +0 -0
- {pyopia-2.4.6 → pyopia-2.5.0}/pyproject.toml +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '2.5.0'
|
|
@@ -51,6 +51,117 @@ def load_mono8(filename):
|
|
|
51
51
|
return img
|
|
52
52
|
|
|
53
53
|
|
|
54
|
+
def load_bayer_rgb8(filename):
|
|
55
|
+
'''load an RG8 .bsilc file from disc and convert it to RGB image
|
|
56
|
+
|
|
57
|
+
Assumes 8-bit Bayer-RG (Red-Green) image in range 0-255
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
filename : string
|
|
62
|
+
filename to load
|
|
63
|
+
|
|
64
|
+
Returns
|
|
65
|
+
-------
|
|
66
|
+
array
|
|
67
|
+
raw image float between 0-1
|
|
68
|
+
'''
|
|
69
|
+
img_bayer = np.load(filename, allow_pickle=False).astype(np.int16)
|
|
70
|
+
|
|
71
|
+
# Check the image dimension
|
|
72
|
+
image_shape = np.shape(img_bayer)
|
|
73
|
+
if len(image_shape) > 2:
|
|
74
|
+
if image_shape[2] == 1:
|
|
75
|
+
img_bayer = img_bayer[:, :, 0]
|
|
76
|
+
else:
|
|
77
|
+
raise RuntimeError('Invalid image dimension')
|
|
78
|
+
|
|
79
|
+
M, N = img_bayer.shape[:2] # Number of pixels in image height and width
|
|
80
|
+
img_bayer_min, img_bayer_max = np.min(img_bayer), np.max(img_bayer)
|
|
81
|
+
|
|
82
|
+
# img is a reconstructed RGB image
|
|
83
|
+
img = np.zeros((M, N, 3), dtype=np.uint8)
|
|
84
|
+
img[0:M:2, 0:N:2, 0] = img_bayer[0:M:2, 0:N:2] # Red pixels
|
|
85
|
+
img[0:M:2, 1:N:2, 1] = img_bayer[0:M:2, 1:N:2] # Green pixels on the first row
|
|
86
|
+
img[1:M:2, 0:N:2, 1] = img_bayer[1:M:2, 0:N:2] # Green pixels on the second row
|
|
87
|
+
img[1:M:2, 1:N:2, 2] = img_bayer[1:M:2, 1:N:2] # Blue pixels
|
|
88
|
+
|
|
89
|
+
# Boundary pixels interpolation in the first and last rows
|
|
90
|
+
# ***Red pixels
|
|
91
|
+
img[0, 2:N-1:2, 1] = (img_bayer[1, 2:N-1:2] + img_bayer[0, 1:N-2:2] + img_bayer[0, 3:N:2] + 1) // 3 # Interpolated G
|
|
92
|
+
img[0, 2:N-1:2, 2] = (img_bayer[1, 1:N-2:2] + img_bayer[1, 3:N:2] + 1) // 2 # Interpolated B
|
|
93
|
+
# ***Green pixels (odd columns)
|
|
94
|
+
img[0, 1:N-2:2, 0] = (img_bayer[0, 0:N-3:2] + img_bayer[0, 2:N-1:2] + 1) // 2 # Interpolated R
|
|
95
|
+
img[0, 1:N-2:2, 2] = img_bayer[1, 1:N-2:2] # Interpolated B
|
|
96
|
+
# ***Blue pixels
|
|
97
|
+
img[M-1, 1:N-2:2, 1] = (img_bayer[M-2, 1:N-2:2]
|
|
98
|
+
+ img_bayer[M-1, 0:N-3:2] + img_bayer[M-1, 2:N-1:2] + 1) // 3 # Interpolated G
|
|
99
|
+
img[M-1, 1:N-2:2, 0] = (img_bayer[M-2, 0:N-3:2] + img_bayer[M-2, 2:N-1:2] + 1) // 2 # Interpolated R
|
|
100
|
+
# ***Green pixels (even columns)
|
|
101
|
+
img[M-1, 2:N-1:2, 2] = (img_bayer[M-1, 1:N-2:2] + img_bayer[M-1, 3:N:2] + 1) // 2 # Interpolated B
|
|
102
|
+
img[M-1, 2:N-1:2, 0] = img_bayer[M-2, 2:N-1:2] # Interpolated R
|
|
103
|
+
|
|
104
|
+
# Boundary pixels interpolation in the first and last cols
|
|
105
|
+
# ***Red pixels
|
|
106
|
+
img[2:M-1:2, 0, 1] = (img_bayer[3:M:2, 0] + img_bayer[1:M-2:2, 0] + img_bayer[2:M-1:2, 1] + 1) // 3 # Interpolated G
|
|
107
|
+
img[2:M-1:2, 0, 2] = (img_bayer[3:M:2, 1] + img_bayer[1:M-2:2, 1] + 1) // 2 # Interpolated B
|
|
108
|
+
# ***Green pixels (odd columns)
|
|
109
|
+
img[1:M-2:2, 0, 0] = (img_bayer[0:M-3:2, 0] + img_bayer[2:M-1:2, 0] + 1) // 2 # Interpolated R
|
|
110
|
+
img[1:M-2:2, 0, 2] = img_bayer[1:M-2:2, 1] # Interpolated B
|
|
111
|
+
# ***Blue pixels
|
|
112
|
+
img[1:M-2:2, N-1, 1] = (img_bayer[0:M-3:2, N-1]
|
|
113
|
+
+ img_bayer[2:M-1:2, N-1] + img_bayer[1:M-2:2, N-2] + 1) // 3 # Interpolated G
|
|
114
|
+
img[1:M-2:2, N-1, 0] = (img_bayer[0:M-3:2, N-2] + img_bayer[2:M-1:2, N-2] + 1) // 2 # Interpolated R
|
|
115
|
+
# ***Green pixels (even columns)
|
|
116
|
+
img[2:M-1:2, N-1, 0] = img_bayer[2:M-1:2, N-2] # Interpolated R
|
|
117
|
+
img[2:M-1:2, N-1, 2] = (img_bayer[1:M-2:2, N-1]+img_bayer[3:M:2, N-1] + 1)//2 # Interpolated B
|
|
118
|
+
|
|
119
|
+
# Corner pixels interpolation
|
|
120
|
+
# *** top-left
|
|
121
|
+
img[0, 0, 1] = (img_bayer[1, 0] + img_bayer[0, 1] + 1) // 2 # Interpolated G
|
|
122
|
+
img[0, 0, 2] = img_bayer[1, 1] # Interpolated B
|
|
123
|
+
# *** top-right
|
|
124
|
+
img[0, N-1, 0] = img_bayer[0, N-2] # Interpolated R
|
|
125
|
+
img[0, N-1, 2] = img_bayer[1, N-1] # Interpolated B
|
|
126
|
+
# *** bottom-left
|
|
127
|
+
img[M-1, 0, 0] = img_bayer[M-2, 0] # Interpolated R
|
|
128
|
+
img[M-1, 0, 2] = img_bayer[M-1, 1] # Interpolated B
|
|
129
|
+
# *** bottom-right
|
|
130
|
+
img[M-1, N-1, 1] = (img_bayer[M-2, N-1] + img_bayer[M-1, N-2] + 1) // 2 # Interpolated G
|
|
131
|
+
img[M-1, N-1, 0] = img_bayer[M-2, N-2] # Interpolated R
|
|
132
|
+
|
|
133
|
+
# Internal pixels interpolation
|
|
134
|
+
# ***G pixel on odd row, even column
|
|
135
|
+
img[1:M-2:2, 2:N-1:2, 0] = (img_bayer[0:M-3:2, 2:N-1:2] + img_bayer[2:M-1:2, 2:N-1:2] + 1) // 2 # Interpolated R
|
|
136
|
+
img[1:M-2:2, 2:N-1:2, 2] = (img_bayer[1:M-2:2, 1:N-2:2] + img_bayer[1:M-2:2, 3:N:2] + 1) // 2 # Interpolated B
|
|
137
|
+
# ***G pixel on even row, odd column
|
|
138
|
+
img[2:M-1:2, 1:N-2:2, 0] = (img_bayer[2:M-1:2, 0:N-3:2] + img_bayer[2:M-1:2, 2:N-1:2] + 1) // 2 # Interpolated R
|
|
139
|
+
img[2:M-1:2, 1:N-2:2, 2] = (img_bayer[1:M-2:2, 1:N-2:2] + img_bayer[3:M:2, 1:N-2:2] + 1) // 2 # Interpolated B
|
|
140
|
+
# ***R pixel
|
|
141
|
+
img[2:M-1:2, 2:N-1:2, 1] = (img_bayer[1:M-2:2, 2:N-1:2]
|
|
142
|
+
+ img_bayer[3:M:2, 2:N-1:2] + img_bayer[2:M-1:2, 1:N-2:2]
|
|
143
|
+
+ img_bayer[2:M-1:2, 3:N:2] + 2) // 4 # Interpolated G
|
|
144
|
+
img[2:M-1:2, 2:N-1:2, 2] = (img_bayer[1:M-2:2, 1:N-2:2]
|
|
145
|
+
+ img_bayer[3:M:2, 1:N-2:2] + img_bayer[3:M:2, 3:N:2]
|
|
146
|
+
+ img_bayer[1:M-2:2, 3:N:2] + 2) // 4 # Interpolated B
|
|
147
|
+
# ***B pixel
|
|
148
|
+
img[1:M-2:2, 1:N-2:2, 0] = (img_bayer[0:M-3:2, 0:N-3:2]
|
|
149
|
+
+ img_bayer[2:M-1:2, 0:N-3:2] + img_bayer[2:M-1:2, 2:N-1:2]
|
|
150
|
+
+ img_bayer[0:M-3:2, 2:N-1:2] + 2) // 4 # Interpolated R
|
|
151
|
+
img[1:M-2:2, 1:N-2:2, 1] = (img_bayer[0:M-3:2, 1:N-2:2]
|
|
152
|
+
+ img_bayer[2:M-1:2, 1:N-2:2] + img_bayer[1:M-2:2, 0:N-3:2]
|
|
153
|
+
+ img_bayer[1:M-2:2, 2:N-1:2] + 2) // 4 # Interpolated G
|
|
154
|
+
img_min, img_max = np.min(img), np.max(img)
|
|
155
|
+
|
|
156
|
+
if img_min < 0 or img_max > 255 or (img_max - img_bayer_max) != 0 or (img_min - img_bayer_min) != 0:
|
|
157
|
+
raise ValueError(
|
|
158
|
+
"The converted RGB image is not suitable for further analysis. Check the pixel intensity ranges of the input image."
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
img = img.astype(np.float64) / 255
|
|
162
|
+
return img
|
|
163
|
+
|
|
164
|
+
|
|
54
165
|
def load_rgb8(filename):
|
|
55
166
|
'''load an RGB .silc file from disc
|
|
56
167
|
|
|
@@ -102,13 +213,14 @@ class SilCamLoad():
|
|
|
102
213
|
Parameters
|
|
103
214
|
----------
|
|
104
215
|
image_format : str, optional
|
|
105
|
-
Image file format. Can be either 'infer', 'rgb8' or 'mono8', by default 'infer'.
|
|
216
|
+
Image file format. Can be either 'infer', 'rgb8', 'bayer_rg8' or 'mono8', by default 'infer'.
|
|
106
217
|
|
|
107
218
|
Note
|
|
108
219
|
----
|
|
109
220
|
'infer' uses the file extension to determine the image format using the following convention:
|
|
110
221
|
- '.silc' for RGB8
|
|
111
222
|
- '.msilc' for MONO8
|
|
223
|
+
- '.bsilc' for BAYER_RG8
|
|
112
224
|
- '.bmp' for using skimage.io.imread
|
|
113
225
|
|
|
114
226
|
Returns
|
|
@@ -125,9 +237,10 @@ class SilCamLoad():
|
|
|
125
237
|
self.image_format = image_format
|
|
126
238
|
self.extension_load = {'.silc': load_rgb8,
|
|
127
239
|
'.msilc': load_mono8,
|
|
240
|
+
'.bsilc': load_bayer_rgb8,
|
|
128
241
|
'.bmp': skimage.io.imread}
|
|
129
242
|
self.format_load = {'RGB8': load_rgb8,
|
|
130
|
-
'MONO8': load_mono8}
|
|
243
|
+
'MONO8': load_mono8, 'BAYER_RG8': load_bayer_rgb8}
|
|
131
244
|
|
|
132
245
|
def __call__(self, data):
|
|
133
246
|
data['timestamp'] = timestamp_from_filename(data['filename'])
|
|
@@ -85,14 +85,39 @@ def write_stats(stats,
|
|
|
85
85
|
elif not append:
|
|
86
86
|
datafilename += ('-Image-D' +
|
|
87
87
|
str(xstats['timestamp'][0].values).replace('-', '').replace(':', '').replace('.', '-'))
|
|
88
|
-
encoding =
|
|
88
|
+
encoding = setup_xstats_encoding(xstats)
|
|
89
89
|
xstats.to_netcdf(datafilename + '-STATS.nc', encoding=encoding, engine=NETCDF_ENGINE, format='NETCDF4')
|
|
90
90
|
|
|
91
|
-
# If we have image statistics, add
|
|
91
|
+
# If we have image statistics (summary data for each raw image), add the image_stats a group
|
|
92
92
|
if image_stats is not None:
|
|
93
93
|
image_stats.to_xarray().to_netcdf(datafilename + '-STATS.nc', group='image_stats', mode='a', engine=NETCDF_ENGINE)
|
|
94
94
|
|
|
95
95
|
|
|
96
|
+
def setup_xstats_encoding(xstats, string_vars=['export name', 'holo_filename']):
|
|
97
|
+
'''Setup encoding for writing to NetCDF, where string variables are explicitly defined as string types
|
|
98
|
+
|
|
99
|
+
Notes
|
|
100
|
+
-----
|
|
101
|
+
Setting up encoding like this for xstats is needed because default behaviour is to set everything as float if there is no
|
|
102
|
+
value, so in a situation where the first image contains no particles we must ensure that string variables are set as string
|
|
103
|
+
types.
|
|
104
|
+
|
|
105
|
+
Parameters
|
|
106
|
+
----------
|
|
107
|
+
xstats : xarray.Dataset
|
|
108
|
+
Xarray version of stats dataframe, including metadata
|
|
109
|
+
string_vars : list, optional
|
|
110
|
+
list of string columns in xstats, by default ['export name', 'holo_filename']
|
|
111
|
+
|
|
112
|
+
Returns
|
|
113
|
+
-------
|
|
114
|
+
encoding : dict
|
|
115
|
+
'encoding' input argument to be given to xstats.to_netcdf()
|
|
116
|
+
'''
|
|
117
|
+
encoding = {k: {'dtype': 'str'} for k in string_vars if k in xstats.data_vars}
|
|
118
|
+
return encoding
|
|
119
|
+
|
|
120
|
+
|
|
96
121
|
def make_xstats(stats, toml_steps):
|
|
97
122
|
'''Converts a stats dataframe into xarray DataSet, with metadata
|
|
98
123
|
|
|
@@ -225,10 +250,12 @@ def merge_and_save_mfdataset(path_to_data, prefix='*'):
|
|
|
225
250
|
output_name = os.path.join(path_to_data, prefix_out)
|
|
226
251
|
|
|
227
252
|
logging.info(f'writing {output_name}')
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
253
|
+
# save the particle statistics (xstats) to NetCDF
|
|
254
|
+
encoding = setup_xstats_encoding(xstats)
|
|
255
|
+
xstats.to_netcdf(output_name + '-STATS.nc', encoding=encoding, engine=NETCDF_ENGINE, format='NETCDF4')
|
|
256
|
+
# if summary data for each raw image are available (image_stats), save this into the image_stats group
|
|
257
|
+
if image_stats is not None:
|
|
258
|
+
image_stats.to_netcdf(output_name + '-STATS.nc', group='image_stats', mode='a', engine=NETCDF_ENGINE)
|
|
232
259
|
logging.info(f'writing {output_name} done.')
|
|
233
260
|
|
|
234
261
|
|
pyopia-2.4.6/pyopia/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '2.4.6'
|
|
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
|