PyOPIA 2.6.1__tar.gz → 2.7.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.6.1 → pyopia-2.7.0}/PKG-INFO +1 -1
- pyopia-2.7.0/pyopia/__init__.py +1 -0
- {pyopia-2.6.1 → pyopia-2.7.0}/pyopia/background.py +25 -9
- {pyopia-2.6.1 → pyopia-2.7.0}/pyopia/cli.py +16 -10
- {pyopia-2.6.1 → pyopia-2.7.0}/pyopia/pipeline.py +32 -4
- {pyopia-2.6.1 → pyopia-2.7.0}/pyopia/process.py +1 -1
- {pyopia-2.6.1 → pyopia-2.7.0}/pyopia/statistics.py +1 -1
- pyopia-2.6.1/pyopia/__init__.py +0 -1
- {pyopia-2.6.1 → pyopia-2.7.0}/LICENSE +0 -0
- {pyopia-2.6.1 → pyopia-2.7.0}/README.md +0 -0
- {pyopia-2.6.1 → pyopia-2.7.0}/pyopia/classify.py +0 -0
- {pyopia-2.6.1 → pyopia-2.7.0}/pyopia/exampledata.py +0 -0
- {pyopia-2.6.1 → pyopia-2.7.0}/pyopia/instrument/__init__.py +0 -0
- {pyopia-2.6.1 → pyopia-2.7.0}/pyopia/instrument/common.py +0 -0
- {pyopia-2.6.1 → pyopia-2.7.0}/pyopia/instrument/holo.py +0 -0
- {pyopia-2.6.1 → pyopia-2.7.0}/pyopia/instrument/silcam.py +0 -0
- {pyopia-2.6.1 → pyopia-2.7.0}/pyopia/instrument/uvp.py +0 -0
- {pyopia-2.6.1 → pyopia-2.7.0}/pyopia/io.py +0 -0
- {pyopia-2.6.1 → pyopia-2.7.0}/pyopia/plotting.py +0 -0
- {pyopia-2.6.1 → pyopia-2.7.0}/pyopia/simulator/__init__.py +0 -0
- {pyopia-2.6.1 → pyopia-2.7.0}/pyopia/simulator/silcam.py +0 -0
- {pyopia-2.6.1 → pyopia-2.7.0}/pyopia/tests/__init__.py +0 -0
- {pyopia-2.6.1 → pyopia-2.7.0}/pyopia/tests/test_classify.py +0 -0
- {pyopia-2.6.1 → pyopia-2.7.0}/pyopia/tests/test_notebooks.py +0 -0
- {pyopia-2.6.1 → pyopia-2.7.0}/pyopia/tests/test_pipeline.py +0 -0
- {pyopia-2.6.1 → pyopia-2.7.0}/pyproject.toml +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '2.7.0'
|
|
@@ -95,9 +95,11 @@ def shift_bgstack_fast(bgstack, imbg, imnew):
|
|
|
95
95
|
return bgstack, imbg
|
|
96
96
|
|
|
97
97
|
|
|
98
|
-
def correct_im_accurate(imbg, imraw):
|
|
98
|
+
def correct_im_accurate(imbg, imraw, divide_bg=False):
|
|
99
99
|
'''
|
|
100
|
-
Corrects raw image by subtracting the background and scaling the output
|
|
100
|
+
Corrects raw image by subtracting or dividing the background and scaling the output
|
|
101
|
+
|
|
102
|
+
For dividing method see: https://doi.org/10.1016/j.marpolbul.2016.11.063)
|
|
101
103
|
|
|
102
104
|
There is a small chance of clipping of imc in both crushed blacks and blown
|
|
103
105
|
highlights if the background or raw images are very poorly obtained
|
|
@@ -108,6 +110,9 @@ def correct_im_accurate(imbg, imraw):
|
|
|
108
110
|
background averaged image
|
|
109
111
|
imraw : float64
|
|
110
112
|
raw image
|
|
113
|
+
divide_bg : (bool, optional)
|
|
114
|
+
If True, the correction will be performed by dividing the raw image by the background
|
|
115
|
+
Default to False
|
|
111
116
|
|
|
112
117
|
Returns
|
|
113
118
|
-------
|
|
@@ -115,10 +120,16 @@ def correct_im_accurate(imbg, imraw):
|
|
|
115
120
|
corrected image, same type as input
|
|
116
121
|
'''
|
|
117
122
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
123
|
+
if divide_bg:
|
|
124
|
+
imbg = np.clip(imbg, a_min=1/255, a_max=None) # Clipping the zero_value pixels
|
|
125
|
+
im_corrected = imraw / imbg
|
|
126
|
+
im_corrected += (1 / 2 - np.percentile(im_corrected, 50))
|
|
127
|
+
im_corrected -= im_corrected.min() # Shift the negative values to zero
|
|
128
|
+
im_corrected = np.clip(im_corrected, a_min=0, a_max=1)
|
|
129
|
+
else:
|
|
130
|
+
im_corrected = imraw - imbg
|
|
131
|
+
im_corrected += (1 / 2 - np.percentile(im_corrected, 50))
|
|
132
|
+
im_corrected += 1 - im_corrected.max() # Shift the positive values exceeding unity to one
|
|
122
133
|
|
|
123
134
|
return im_corrected
|
|
124
135
|
|
|
@@ -207,7 +218,7 @@ class CorrectBackgroundAccurate():
|
|
|
207
218
|
----------
|
|
208
219
|
bgshift_function : (string, optional)
|
|
209
220
|
Function used to shift the background. Defaults to passing (i.e. static background)
|
|
210
|
-
Available options are 'accurate', 'fast', or 'pass' to apply a
|
|
221
|
+
Available options are 'accurate', 'fast', or 'pass' to apply a static background correction:
|
|
211
222
|
|
|
212
223
|
:func:`pyopia.background.shift_bgstack_accurate`
|
|
213
224
|
|
|
@@ -220,6 +231,10 @@ class CorrectBackgroundAccurate():
|
|
|
220
231
|
The key in Pipeline.data of the image to be background corrected.
|
|
221
232
|
Defaults to 'imraw'
|
|
222
233
|
|
|
234
|
+
divide_bg : (bool)
|
|
235
|
+
If True, it performs background correction by dividing the raw image by the background.
|
|
236
|
+
Default to False.
|
|
237
|
+
|
|
223
238
|
Returns
|
|
224
239
|
-------
|
|
225
240
|
data : :class:`pyopia.pipeline.Data`
|
|
@@ -256,10 +271,11 @@ class CorrectBackgroundAccurate():
|
|
|
256
271
|
Then you could use :class:`pyopia.pipeline.CorrectBackgroundNone` if you need to instead.
|
|
257
272
|
'''
|
|
258
273
|
|
|
259
|
-
def __init__(self, bgshift_function='pass', average_window=1, image_source='imraw'):
|
|
274
|
+
def __init__(self, bgshift_function='pass', average_window=1, image_source='imraw', divide_bg=False):
|
|
260
275
|
self.bgshift_function = bgshift_function
|
|
261
276
|
self.average_window = average_window
|
|
262
277
|
self.image_source = image_source
|
|
278
|
+
self.divide_bg = divide_bg
|
|
263
279
|
|
|
264
280
|
def _build_background_step(self, data):
|
|
265
281
|
'''Add one layer to the background stack from the raw image in data pipeline, and update the background image.'''
|
|
@@ -284,7 +300,7 @@ class CorrectBackgroundAccurate():
|
|
|
284
300
|
data['skip_next_steps'] = True
|
|
285
301
|
return data
|
|
286
302
|
|
|
287
|
-
data['im_corrected'] = correct_im_accurate(data['imbg'], data[self.image_source])
|
|
303
|
+
data['im_corrected'] = correct_im_accurate(data['imbg'], data[self.image_source], divide_bg=self.divide_bg)
|
|
288
304
|
|
|
289
305
|
match self.bgshift_function:
|
|
290
306
|
case 'pass':
|
|
@@ -15,6 +15,7 @@ import rich.progress
|
|
|
15
15
|
import pandas as pd
|
|
16
16
|
import threading
|
|
17
17
|
|
|
18
|
+
import pyopia
|
|
18
19
|
import pyopia.background
|
|
19
20
|
import pyopia.instrument.silcam
|
|
20
21
|
import pyopia.instrument.holo
|
|
@@ -117,25 +118,27 @@ def generate_config(instrument: str, raw_files: str, model_path: str, outfolder:
|
|
|
117
118
|
|
|
118
119
|
|
|
119
120
|
@app.command()
|
|
120
|
-
def process(config_filename: str, num_chunks: int = 1):
|
|
121
|
+
def process(config_filename: str, num_chunks: int = 1, strategy: str = 'block'):
|
|
121
122
|
'''Run a PyOPIA processing pipeline based on given a config.toml
|
|
122
123
|
|
|
123
124
|
Parameters
|
|
124
125
|
----------
|
|
125
126
|
config_filename : str
|
|
126
|
-
|
|
127
|
+
Config filename
|
|
128
|
+
|
|
127
129
|
numchunks : int, optional
|
|
128
|
-
|
|
130
|
+
Split the dataset into chucks, and process in parallell, by default 1
|
|
129
131
|
|
|
132
|
+
strategy : str, optional
|
|
133
|
+
Strategy to use for chunking dataset, either `block` or `interleave`. Defult: `block`
|
|
130
134
|
'''
|
|
131
|
-
from pyopia.io import load_toml
|
|
132
|
-
from pyopia.pipeline import Pipeline
|
|
133
|
-
|
|
134
135
|
t1 = time.time()
|
|
135
136
|
|
|
136
137
|
with Progress(transient=True) as progress:
|
|
138
|
+
progress.console.print(f"[blue]PYOPIA VERSION {pyopia.__version__}")
|
|
139
|
+
|
|
137
140
|
progress.console.print("[blue]LOAD CONFIG")
|
|
138
|
-
pipeline_config = load_toml(config_filename)
|
|
141
|
+
pipeline_config = pyopia.io.load_toml(config_filename)
|
|
139
142
|
|
|
140
143
|
setup_logging(pipeline_config)
|
|
141
144
|
logger = logging.getLogger('rich')
|
|
@@ -144,11 +147,14 @@ def process(config_filename: str, num_chunks: int = 1):
|
|
|
144
147
|
check_chunks(num_chunks, pipeline_config)
|
|
145
148
|
|
|
146
149
|
progress.console.print("[blue]OBTAIN IMAGE LIST")
|
|
147
|
-
raw_files = pyopia.pipeline.FilesToProcess(pipeline_config['general']['raw_files'])
|
|
148
150
|
conf_corrbg = pipeline_config['steps'].get('correctbackground', dict())
|
|
149
151
|
average_window = conf_corrbg.get('average_window', 0)
|
|
150
152
|
bgshift_function = conf_corrbg.get('bgshift_function', 'pass')
|
|
151
|
-
raw_files.
|
|
153
|
+
raw_files = pyopia.pipeline.FilesToProcess(pipeline_config['general']['raw_files'])
|
|
154
|
+
raw_files.prepare_chunking(num_chunks, average_window, bgshift_function, strategy=strategy)
|
|
155
|
+
|
|
156
|
+
# Write the dataset list of images to a text file
|
|
157
|
+
raw_files.to_filelist_file('filelist.txt')
|
|
152
158
|
|
|
153
159
|
progress.console.print('[blue]PREPARE FOLDERS')
|
|
154
160
|
if 'output' not in pipeline_config['steps']:
|
|
@@ -167,7 +173,7 @@ def process(config_filename: str, num_chunks: int = 1):
|
|
|
167
173
|
progress.console.print("[blue]INITIALISE PIPELINE")
|
|
168
174
|
|
|
169
175
|
def process_file_list(file_list, c):
|
|
170
|
-
processing_pipeline = Pipeline(pipeline_config)
|
|
176
|
+
processing_pipeline = pyopia.pipeline.Pipeline(pipeline_config)
|
|
171
177
|
|
|
172
178
|
with get_custom_progress_bar(f'[blue]Processing progress (chunk {c})', disable=c != 0) as pbar:
|
|
173
179
|
for filename in pbar.track(file_list, description=f'[blue]Processing progress (chunk {c})'):
|
|
@@ -373,25 +373,53 @@ class FilesToProcess:
|
|
|
373
373
|
with open(path_to_filelist, 'w') as fh:
|
|
374
374
|
[fh.writelines(L + '\n') for L in self.files]
|
|
375
375
|
|
|
376
|
-
|
|
376
|
+
with open(path_to_filelist+'.chunks', 'w') as fh:
|
|
377
|
+
for i, chunk in enumerate(self.chunked_files):
|
|
378
|
+
fh.write(f'--- Chunk {i}, N = {len(chunk)} ---\n')
|
|
379
|
+
[fh.writelines(L + '\n') for L in chunk]
|
|
380
|
+
|
|
381
|
+
def prepare_chunking(self, num_chunks, average_window, bgshift_function, strategy='block'):
|
|
382
|
+
'''Chunk the file list and add initial background files to each chunk
|
|
383
|
+
|
|
384
|
+
Parameters
|
|
385
|
+
----------
|
|
386
|
+
num_chunks : int
|
|
387
|
+
Number of chunks to produce (must be at least 1)
|
|
388
|
+
average_window : int
|
|
389
|
+
Number of images to use for background correction
|
|
390
|
+
bgshift_function : str
|
|
391
|
+
Background update strategy, either `pass` (static background) or `accurate`
|
|
392
|
+
strategy : str, optional
|
|
393
|
+
Strategy to use for chunking dataset, either `block` or `interleave`. Defult: `block`
|
|
394
|
+
'''
|
|
377
395
|
if num_chunks > len(self.files) // 2:
|
|
378
396
|
raise RuntimeError('Number of chunks exceeds more than half the number of files to process. Use less chunks.')
|
|
379
|
-
self.chunk_files(num_chunks)
|
|
397
|
+
self.chunk_files(num_chunks, strategy)
|
|
380
398
|
self.build_initial_background_files(average_window=average_window)
|
|
381
399
|
self.insert_bg_files_into_chunks(bgshift_function=bgshift_function)
|
|
382
400
|
|
|
383
|
-
def chunk_files(self, num_chunks: int):
|
|
401
|
+
def chunk_files(self, num_chunks: int, strategy: str = 'block'):
|
|
384
402
|
'''Chunk the file list and create FilesToProcess.chunked_files
|
|
385
403
|
|
|
386
404
|
Parameters
|
|
387
405
|
----------
|
|
388
406
|
num_chunks : int
|
|
389
407
|
number of chunks to produce (must be at least 1)
|
|
408
|
+
strategy : str, optional
|
|
409
|
+
Strategy to use for chunking dataset, either `block` or `interleave`. Defult: `block`
|
|
390
410
|
'''
|
|
391
411
|
if num_chunks < 1:
|
|
392
412
|
raise RuntimeError('You must have at least one chunk')
|
|
393
413
|
chunk_length = int(np.ceil(len(self.files) / num_chunks))
|
|
394
|
-
|
|
414
|
+
if strategy == 'block':
|
|
415
|
+
logging.debug('Chunking file list with strategy: block')
|
|
416
|
+
self.chunked_files = [self.files[i:i + chunk_length] for i in range(0, len(self.files), chunk_length)]
|
|
417
|
+
elif strategy == 'interleave':
|
|
418
|
+
logging.debug('Chunking file list with strategy: interleave')
|
|
419
|
+
self.chunked_files = [self.files[i::num_chunks] for i in range(0, num_chunks)]
|
|
420
|
+
else:
|
|
421
|
+
logging.debug(f'Invalid chunking strategy: {strategy}')
|
|
422
|
+
raise RuntimeError(f'Unknown strategy: {strategy}. Should be either block or interleave')
|
|
395
423
|
|
|
396
424
|
def insert_bg_files_into_chunks(self, bgshift_function='pass'):
|
|
397
425
|
average_window = len(self.background_files)
|
|
@@ -623,7 +623,7 @@ class CalculateImageStats():
|
|
|
623
623
|
|
|
624
624
|
# Add image "global" statistics, separate from the particle stats above (stats)
|
|
625
625
|
image_saturation = np.nan if stats.empty else stats['saturation'].values[0]
|
|
626
|
-
data['image_stats'].loc[data['timestamp'], 'filename'] =
|
|
626
|
+
data['image_stats'].loc[data['timestamp'], 'filename'] = data.get('filename', '')
|
|
627
627
|
data['image_stats'].loc[data['timestamp'], 'particle_count'] = int(stats.shape[0])
|
|
628
628
|
data['image_stats'].loc[data['timestamp'], 'saturation'] = image_saturation
|
|
629
629
|
|
|
@@ -534,7 +534,7 @@ def get_j(dias, number_distribution):
|
|
|
534
534
|
|
|
535
535
|
# use polyfit to obtain the slope of the ditriubtion in log-space (which is
|
|
536
536
|
# assumed near-linear in most parts of the ocean)
|
|
537
|
-
p = np.polyfit(np.log(dias[ind]), np.log(number_distribution[ind]), 1)
|
|
537
|
+
p = np.polyfit(np.log(dias[ind]), np.log(number_distribution[ind], where=number_distribution[ind] > 0), 1)
|
|
538
538
|
junge_slope = p[0]
|
|
539
539
|
return junge_slope
|
|
540
540
|
|
pyopia-2.6.1/pyopia/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '2.6.1'
|
|
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
|