PyOPIA 2.9.3__tar.gz → 2.11.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.
Files changed (36) hide show
  1. {pyopia-2.9.3 → pyopia-2.11.0}/PKG-INFO +14 -6
  2. {pyopia-2.9.3 → pyopia-2.11.0}/README.md +7 -2
  3. pyopia-2.11.0/pyopia/__init__.py +1 -0
  4. pyopia-2.11.0/pyopia/auxillarydata.py +106 -0
  5. {pyopia-2.9.3 → pyopia-2.11.0}/pyopia/cf_metadata.json +8 -13
  6. {pyopia-2.9.3 → pyopia-2.11.0}/pyopia/classify.py +66 -30
  7. pyopia-2.11.0/pyopia/cli.py +571 -0
  8. pyopia-2.11.0/pyopia/exampledata.py +150 -0
  9. {pyopia-2.9.3 → pyopia-2.11.0}/pyopia/instrument/silcam.py +14 -3
  10. {pyopia-2.9.3 → pyopia-2.11.0}/pyopia/io.py +88 -19
  11. pyopia-2.11.0/pyopia/metadata.py +46 -0
  12. pyopia-2.11.0/pyopia/plotting.py +221 -0
  13. pyopia-2.11.0/pyopia/tests/test_auxillarydata.py +102 -0
  14. {pyopia-2.9.3 → pyopia-2.11.0}/pyopia/tests/test_classify.py +88 -63
  15. {pyopia-2.9.3 → pyopia-2.11.0}/pyopia/tests/test_io.py +5 -1
  16. pyopia-2.11.0/pyopia/tests/test_notebooks.py +26 -0
  17. {pyopia-2.9.3 → pyopia-2.11.0}/pyproject.toml +6 -3
  18. pyopia-2.9.3/pyopia/__init__.py +0 -1
  19. pyopia-2.9.3/pyopia/cli.py +0 -309
  20. pyopia-2.9.3/pyopia/exampledata.py +0 -143
  21. pyopia-2.9.3/pyopia/plotting.py +0 -51
  22. pyopia-2.9.3/pyopia/tests/test_notebooks.py +0 -22
  23. {pyopia-2.9.3 → pyopia-2.11.0}/.gitignore +0 -0
  24. {pyopia-2.9.3 → pyopia-2.11.0}/LICENSE +0 -0
  25. {pyopia-2.9.3 → pyopia-2.11.0}/pyopia/background.py +0 -0
  26. {pyopia-2.9.3 → pyopia-2.11.0}/pyopia/instrument/__init__.py +0 -0
  27. {pyopia-2.9.3 → pyopia-2.11.0}/pyopia/instrument/common.py +0 -0
  28. {pyopia-2.9.3 → pyopia-2.11.0}/pyopia/instrument/holo.py +0 -0
  29. {pyopia-2.9.3 → pyopia-2.11.0}/pyopia/instrument/uvp.py +0 -0
  30. {pyopia-2.9.3 → pyopia-2.11.0}/pyopia/pipeline.py +0 -0
  31. {pyopia-2.9.3 → pyopia-2.11.0}/pyopia/process.py +0 -0
  32. {pyopia-2.9.3 → pyopia-2.11.0}/pyopia/simulator/__init__.py +0 -0
  33. {pyopia-2.9.3 → pyopia-2.11.0}/pyopia/simulator/silcam.py +0 -0
  34. {pyopia-2.9.3 → pyopia-2.11.0}/pyopia/statistics.py +0 -0
  35. {pyopia-2.9.3 → pyopia-2.11.0}/pyopia/tests/__init__.py +0 -0
  36. {pyopia-2.9.3 → pyopia-2.11.0}/pyopia/tests/test_pipeline.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyOPIA
3
- Version: 2.9.3
3
+ Version: 2.11.0
4
4
  Summary: A Python Ocean Particle Image Analysis toolbox.
5
5
  Project-URL: Repository, https://github.com/sintef/pyopia
6
6
  Project-URL: Documentation, https://pyopia.readthedocs.io
@@ -8,6 +8,7 @@ Author-email: Emlyn Davies <emlyn.davies@sintef.no>, "Alex Nimmo Smith@plymouth.
8
8
  License-File: LICENSE
9
9
  Keywords: Imaging,Measurement,Ocean,Particles,Size distribution
10
10
  Requires-Python: ~=3.12
11
+ Requires-Dist: click<8.2.0
11
12
  Requires-Dist: cmocean<4,>=3.0.3
12
13
  Requires-Dist: dask>=2024.8.1
13
14
  Requires-Dist: flake8<7,>=6.1.0
@@ -21,14 +22,16 @@ Requires-Dist: matplotlib>=3.7
21
22
  Requires-Dist: myst-nb<0.18,>=0.17.2
22
23
  Requires-Dist: nbclient==0.7
23
24
  Requires-Dist: nbconvert<8,>=7.16.4
24
- Requires-Dist: numpy<2,>=1.24.0
25
+ Requires-Dist: numpy>=1.24.0
25
26
  Requires-Dist: pandas[computation]<3,>=2.1.1
26
27
  Requires-Dist: poetry-version-plugin<0.3,>=0.2.0
28
+ Requires-Dist: pydantic>=2.11.7
27
29
  Requires-Dist: pytest-error-for-skips<3,>=2.0.2
28
30
  Requires-Dist: pytest>=7.2.0
29
31
  Requires-Dist: readthedocs-sphinx-search<0.4,>=0.3.2
30
32
  Requires-Dist: scikit-image<0.25,>=0.24.0
31
33
  Requires-Dist: scipy<2,>=1.11.2
34
+ Requires-Dist: seaborn>=0.13.2
32
35
  Requires-Dist: sphinx-copybutton<0.6,>=0.5.2
33
36
  Requires-Dist: sphinx-rtd-theme>=0.5.0
34
37
  Requires-Dist: sphinx-togglebutton<0.4,>=0.3.2
@@ -40,8 +43,8 @@ Requires-Dist: typer[all]<0.10,>=0.9.0
40
43
  Requires-Dist: urllib3<2.0
41
44
  Requires-Dist: xarray<2024,>=2023.12.0
42
45
  Provides-Extra: classification
43
- Requires-Dist: keras==3.5.0; extra == 'classification'
44
- Requires-Dist: tensorflow<3,>=2.16.2; extra == 'classification'
46
+ Requires-Dist: keras==3.9.1; extra == 'classification'
47
+ Requires-Dist: tensorflow>=2.19.0; extra == 'classification'
45
48
  Description-Content-Type: text/markdown
46
49
 
47
50
  PyOPIA
@@ -52,10 +55,15 @@ A Python Ocean Particle Image Analysis toolbox
52
55
  # Quick tryout of PyOPIA
53
56
 
54
57
  1) Install [uv](https://docs.astral.sh/uv/getting-started/installation)
55
- 2) Run PyOPIA classification tests on database particles
58
+ 2) Initialize PyOPIA project with a small example image dataset and run processing
56
59
  ```bash
57
- uv run --python 3.12 --with git+https://github.com/SINTEF/pyopia --with tensorflow==2.16.2 --with keras==3.5.0 python -m pyopia.tests.test_classify
60
+ uvx --python 3.12 --from pyopia[classification] pyopia --init-project pyopiatest --example-data
61
+ cd pyopiatest
62
+ uvx --python 3.12 --from pyopia[classification] pyopia process config.toml
58
63
  ```
64
+ 3) Inspect the processed particle statistics in the processed/ folder
65
+
66
+ See the documentation for more information on how to install and use PyOPIA.
59
67
 
60
68
  # Documentation:
61
69
 
@@ -6,10 +6,15 @@ A Python Ocean Particle Image Analysis toolbox
6
6
  # Quick tryout of PyOPIA
7
7
 
8
8
  1) Install [uv](https://docs.astral.sh/uv/getting-started/installation)
9
- 2) Run PyOPIA classification tests on database particles
9
+ 2) Initialize PyOPIA project with a small example image dataset and run processing
10
10
  ```bash
11
- uv run --python 3.12 --with git+https://github.com/SINTEF/pyopia --with tensorflow==2.16.2 --with keras==3.5.0 python -m pyopia.tests.test_classify
11
+ uvx --python 3.12 --from pyopia[classification] pyopia --init-project pyopiatest --example-data
12
+ cd pyopiatest
13
+ uvx --python 3.12 --from pyopia[classification] pyopia process config.toml
12
14
  ```
15
+ 3) Inspect the processed particle statistics in the processed/ folder
16
+
17
+ See the documentation for more information on how to install and use PyOPIA.
13
18
 
14
19
  # Documentation:
15
20
 
@@ -0,0 +1 @@
1
+ __version__ = "2.11.0"
@@ -0,0 +1,106 @@
1
+ import pandas as pd
2
+ import logging
3
+ import xarray as xr
4
+
5
+ logger = logging.getLogger()
6
+
7
+ AUXILLARY_DATA_FILE_TEMPLATE = """% COMMENT LINE: PLEASE UPDATE THIS FILE WITH PROJECT RELEVANT DATA. EACH COLUMN WILL BECOME A NETCDF VARIABLE.
8
+ % COMMENT LINE: ONE LINE PER MEASUREMENT, TIME IS INTERPOLATED TO IMAGE DATA TIMES IN PYOPIA. FOLLOWING LINES ARE UNITS, DESCRIPTION AND VARIABLE NAME.
9
+ ,metres,degC
10
+ Time of measurement,Depth at measurement location,Temperature at measurement location
11
+ time,depth,temperature
12
+ 2022-06-08T18:40:00.00000,0.0,5.0
13
+ 2022-06-08T18:41:00.00000,5.0,6.0
14
+ 2022-06-08T18:42:00.00000,10.0,7.0
15
+ 2022-06-08T18:43:00.00000,20.0,8.0
16
+ """ # noqa: E501
17
+
18
+
19
+ class AuxillaryData:
20
+ """
21
+ Handle auxillary data for PyOPIA particle statistics file.
22
+
23
+ Auxillary data variables may include (image) depth, longitude, latitude, etc.
24
+ This class parses a well-defined defined .csv input format, see example below.
25
+
26
+ Parameters
27
+ ----------
28
+ auxillary_data_path : str
29
+ Path to auxillary data files .csv creates by the enduser
30
+
31
+
32
+ Example of auxillary data file
33
+ ------------------------------
34
+ % COMMENT LINE:
35
+ % COMMENT LINE:
36
+ ,m,degC,psu
37
+ Time of measurement,Depth at measurement location,Temperature at measurement location
38
+ time,depth,temperature,salinity
39
+ 2025-03-19T16:59:29.950729,0.0,5.0,34
40
+ 2025-03-19T17:59:29.950729,0.0,5.0,34
41
+
42
+
43
+ Note
44
+ ----
45
+ Each column will become a netCDF variable
46
+ The two first lines are comments, and are ignored
47
+ Note that the third and fourth rows contain units and description for each variable
48
+ """
49
+
50
+ def __init__(self, auxillary_data_path=None):
51
+ self.auxillary_data_path = auxillary_data_path
52
+
53
+ # Create empty dataframe for cases where no file was specified, or an error occured reading it
54
+ self.auxillary_data = pd.DataFrame(index=pd.Index([], name="time")).to_xarray()
55
+ if auxillary_data_path is not None:
56
+ try:
57
+ self.auxillary_data = self.load_auxillary_data(auxillary_data_path)
58
+ except RuntimeError as e:
59
+ print(f"Failed to load auxillary data from file: {self.auxillary_data}")
60
+ logging.error(
61
+ f"Failed to load auxillary data from file: {self.auxillary_data}"
62
+ )
63
+ logging.error(e)
64
+
65
+ def load_auxillary_data(self, auxillary_data_path):
66
+ """Load and format uxillary data from .csv file"""
67
+
68
+ # Load in the auxillary data file
69
+ auxillary_data = pd.read_csv(auxillary_data_path, skiprows=4)
70
+
71
+ # Load units and description rows
72
+ units = pd.read_csv(auxillary_data_path, skiprows=2, nrows=0).columns
73
+ long_names = pd.read_csv(auxillary_data_path, skiprows=3, nrows=0).columns
74
+
75
+ # Set time as the index and make sure its type is datetime64[ns]
76
+ auxillary_data["time"] = auxillary_data["time"].astype("datetime64[ns]")
77
+ auxillary_data = auxillary_data.set_index("time")
78
+
79
+ # Transform into xarray, add units
80
+ auxillary_data = auxillary_data.to_xarray()
81
+ for i, col in enumerate(auxillary_data.data_vars): # Iterate over each column
82
+ auxillary_data[col].attrs["units"] = units[i + 1]
83
+ auxillary_data[col].attrs["long_name"] = long_names[i + 1]
84
+
85
+ logging.info(auxillary_data)
86
+
87
+ return auxillary_data
88
+
89
+ def add_auxillary_data_to_xstats(self, xstats):
90
+ """Add auxillary data to a PyOPIA xstats object"""
91
+ logging.info("Adding auxillary data to xstats and storing to new file")
92
+
93
+ # Add each auxillary data variable to xstats, interpolated to xstats times
94
+ for (
95
+ data_var
96
+ ) in self.auxillary_data.data_vars: # Iterate over each data variable
97
+ xstats[data_var] = xr.DataArray(
98
+ data=self.auxillary_data[data_var]
99
+ .astype(float)
100
+ .interp(time=xstats["timestamp"]),
101
+ dims=xstats.dims,
102
+ coords=xstats.coords,
103
+ attrs=self.auxillary_data[data_var].attrs,
104
+ )
105
+
106
+ return xstats
@@ -1,49 +1,49 @@
1
1
  {"major_axis_length": {
2
2
  "standard_name": "major_axis_length",
3
3
  "long_name": "The length of the major axis of the ellipse that has the same normalized second central moments as the region",
4
- "units": "micrometer",
4
+ "units": "Pixels",
5
5
  "calculation_method": "Computed using skimage.measure.regionprops (axis_major_length)",
6
6
  "pyopia_process_level": 1},
7
7
  "minor_axis_length": {
8
8
  "standard_name": "minor_axis_length",
9
9
  "long_name": "The length of the minor axis of the ellipse that has the same normalized second central moments as the region",
10
- "units": "micrometer",
10
+ "units": "Pixels",
11
11
  "calculation_method": "Computed using skimage.measure.regionprops (axis_minor_length)",
12
12
  "pyopia_process_level": 1},
13
13
  "equivalent_diameter": {
14
14
  "standard_name": "equivalent_circular_diameter",
15
15
  "long_name": "Diameter of a circle with the same area as the particle",
16
- "units": "micrometer",
16
+ "units": "Pixels",
17
17
  "calculation_method": "Computed using skimage.measure.regionprops (equivalent_diameter)",
18
18
  "pyopia_process_level": 1},
19
19
  "minr": {
20
20
  "standard_name": "minimum_row_index",
21
21
  "long_name": "Minimum row index of the particle bounding box",
22
- "units": "pixels",
22
+ "units": "Pixels",
23
23
  "calculation_method": "Extracted from skimage.measure.regionprops (bbox[0])",
24
24
  "pyopia_process_level": 1},
25
25
  "maxr": {
26
26
  "standard_name": "maximum_row_index",
27
27
  "long_name": "Maximum row index of the particle bounding box",
28
- "units": "pixels",
28
+ "units": "Pixels",
29
29
  "calculation_method": "Extracted from skimage.measure.regionprops (bbox[2])",
30
30
  "pyopia_process_level": 1},
31
31
  "minc": {
32
32
  "standard_name": "minimum_column_index",
33
33
  "long_name": "Minimum column index of the particle bounding box",
34
- "units": "pixels",
34
+ "units": "Pixels",
35
35
  "calculation_method": "Extracted from skimage.measure.regionprops (bbox[1])",
36
36
  "pyopia_process_level": 1},
37
37
  "maxc": {
38
38
  "standard_name": "maximum_column_index",
39
39
  "long_name": "Maximum column index of the particle bounding box",
40
- "units": "pixels",
40
+ "units": "Pixels",
41
41
  "calculation_method": "Extracted from skimage.measure.regionprops (bbox[3])",
42
42
  "pyopia_process_level": 1},
43
43
  "saturation": {
44
44
  "standard_name": "image_saturation",
45
45
  "long_name": "Percentage saturation of the image",
46
- "units": "percent",
46
+ "units": "Percent",
47
47
  "calculation_method": "Computed as the percentage of the image covered by particles relative to the maximum acceptable coverage",
48
48
  "pyopia_process_level": 1},
49
49
  "index": {
@@ -58,11 +58,6 @@
58
58
  "units": "",
59
59
  "calculation_method": "Generated during particle export",
60
60
  "pyopia_process_level": 1},
61
- "time": {
62
- "standard_name": "time",
63
- "long_name": "Time of particle observation",
64
- "calculation_method": "Extracted from the timestamp of the observation",
65
- "pyopia_process_level": 0},
66
61
  "timestamp": {
67
62
  "standard_name": "timestamp",
68
63
  "long_name": "Timestamp of particle observation",
@@ -1,11 +1,14 @@
1
- '''
1
+ """
2
2
  Module containing tools for classifying particle ROIs
3
- '''
3
+ """
4
4
 
5
5
  import os
6
+ import hashlib
6
7
  import numpy as np
7
8
  import pandas as pd
8
9
  import logging
10
+ from skimage.exposure import rescale_intensity
11
+
9
12
  logger = logging.getLogger()
10
13
 
11
14
  # import tensorflow here. It must be imported on the processor where it will be used!
@@ -15,16 +18,16 @@ try:
15
18
  from tensorflow import keras
16
19
  import tensorflow as tf
17
20
  except ImportError:
18
- info_str = 'ERROR: Could not import Keras. Classify will not work'
19
- info_str += ' until you install tensorflow.\n'
20
- info_str += 'Use: pip install pyopia[classification]\n'
21
- info_str += ' or: pip install pyopia[classification-arm64]'
22
- info_str += ' for tensorflow-macos (silicon chips)'
21
+ info_str = "ERROR: Could not import Keras. Classify will not work"
22
+ info_str += " until you install tensorflow.\n"
23
+ info_str += "Use: pip install pyopia[classification]\n"
24
+ info_str += " or: pip install pyopia[classification-arm64]"
25
+ info_str += " for tensorflow-macos (silicon chips)"
23
26
  raise ImportError(info_str)
24
27
 
25
28
 
26
- class Classify():
27
- '''
29
+ class Classify:
30
+ """
28
31
  A classifier class for PyOPIA workflow.
29
32
  This is intended as a parent class that can be used as a template for flexible classification methods
30
33
 
@@ -32,6 +35,10 @@ class Classify():
32
35
  ----------
33
36
  model_path : str
34
37
  path to particle-classifier e.g. '/testdata/model_name/particle_classifier.keras'
38
+ normalize_intensity : bool
39
+ Scale input image intensity to [0-1] range before classification
40
+ correct_whitebalance : bool
41
+ Perform whitebalance correction before classification
35
42
 
36
43
  Example
37
44
  -------
@@ -71,23 +78,36 @@ class Classify():
71
78
  import pyopia.exampledata
72
79
  model_path = exampledata.get_example_model()
73
80
 
74
- '''
75
- def __init__(self, model_path=None):
81
+ """
82
+
83
+ def __init__(
84
+ self,
85
+ model_path=None,
86
+ normalize_intensity=True,
87
+ correct_whitebalance=False,
88
+ ):
76
89
  self.model_path = model_path
77
90
  self.load_model()
78
91
 
79
92
  # Get config for image resizing from the model
80
- _, self.img_height, self.img_width, _ = self.model.get_config()['layers'][0]['config']['batch_shape']
81
- self.pad_to_aspect_ratio = getattr(self.model.layers[0], 'pad_to_aspect_ratio', False)
93
+ _, self.img_height, self.img_width, _ = self.model.get_config()["layers"][0][
94
+ "config"
95
+ ]["batch_shape"]
96
+ self.pad_to_aspect_ratio = getattr(
97
+ self.model.layers[0], "pad_to_aspect_ratio", False
98
+ )
82
99
 
83
100
  # Enable this to perform whitebalance correction in the preprocessing step
84
- self.correct_whitebalance = False
101
+ self.correct_whitebalance = correct_whitebalance
102
+
103
+ # Enable this to rescale intensity in the preprocessing step
104
+ self.normalize_intensity = normalize_intensity
85
105
 
86
106
  def __call__(self):
87
107
  return self
88
108
 
89
109
  def load_model(self):
90
- '''
110
+ """
91
111
  Load a trained Keras model into the Classify class.
92
112
 
93
113
  Parameters
@@ -96,34 +116,41 @@ class Classify():
96
116
  loaded Keras model
97
117
  class_names: list
98
118
  names for the model output classes
99
- '''
119
+ """
100
120
  model_path = self.model_path
101
121
 
102
- os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
122
+ os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
103
123
  keras.backend.clear_session()
104
124
 
105
125
  # Instantiate Keras model from file
106
126
  path, filename = os.path.split(model_path)
107
127
  self.model = keras.models.load_model(model_path)
108
128
 
129
+ # Create a hash of the model weights file
130
+ with open(model_path, "rb") as f:
131
+ digest = hashlib.file_digest(f, "sha256")
132
+ self.model_hash = digest.hexdigest()
133
+
109
134
  # Try to create model output class name list from last model layer name
110
135
  class_labels = None
111
136
  try:
112
- class_labels = self.model.layers[-1].name.split('.')
137
+ class_labels = self.model.layers[-1].name.split(".")
113
138
  except: # noqa E722
114
- logger.info('Could not get class names from model layer name, reverting to old method with header file.')
139
+ logger.info(
140
+ "Could not get class names from model layer name, reverting to old method with header file."
141
+ )
115
142
 
116
143
  # If we could not create correct class names above, revert to old header file method
117
144
  expected_class_number = self.model.layers[-1].output.shape[1]
118
145
  if class_labels is None or len(class_labels) != expected_class_number:
119
- header = pd.read_csv(os.path.join(path, 'header.tfl.txt'))
146
+ header = pd.read_csv(os.path.join(path, "header.tfl.txt"))
120
147
  class_labels = header.columns
121
148
 
122
149
  self.class_labels = class_labels
123
150
  logger.info(self.class_labels)
124
151
 
125
152
  def preprocessing(self, img_input):
126
- '''
153
+ """
127
154
  Preprocess ROI ready for prediction. example here based on the pysilcam network setup
128
155
 
129
156
  Parameters
@@ -135,7 +162,7 @@ class Classify():
135
162
  -------
136
163
  img_preprocessed : float
137
164
  A particle ROI with range 0.-255., corrected and preprocessed, ready for prediction
138
- '''
165
+ """
139
166
 
140
167
  whitebalanced = img_input.astype(np.float64)
141
168
 
@@ -143,10 +170,16 @@ class Classify():
143
170
  if self.correct_whitebalance:
144
171
  p = 99
145
172
  for c in range(3):
146
- whitebalanced[:, :, c] += (p/100) - np.percentile(whitebalanced[:, :, c], p)
173
+ whitebalanced[:, :, c] += (p / 100) - np.percentile(
174
+ whitebalanced[:, :, c], p
175
+ )
147
176
  whitebalanced[whitebalanced > 1] = 1
148
177
  whitebalanced[whitebalanced < 0] = 0
149
178
 
179
+ # Rescale intensity to 0-1 range
180
+ if self.normalize_intensity:
181
+ whitebalanced = rescale_intensity(whitebalanced)
182
+
150
183
  # convert back to 0-255 scaling (because of this layer in the network:
151
184
  # layers.Rescaling(1./255, input_shape=(img_height, img_width, 3)))
152
185
  # This is useful because it allows training to use tf.keras.utils.image_dataset_from_directory,
@@ -154,9 +187,12 @@ class Classify():
154
187
  img = keras.utils.img_to_array(whitebalanced * 255)
155
188
 
156
189
  # resize to match the dimentions expected by the network
157
- img = tf.image.resize(img, [self.img_height, self.img_width],
158
- method=tf.image.ResizeMethod.BILINEAR,
159
- preserve_aspect_ratio=self.pad_to_aspect_ratio)
190
+ img = tf.image.resize(
191
+ img,
192
+ [self.img_height, self.img_width],
193
+ method=tf.image.ResizeMethod.BILINEAR,
194
+ preserve_aspect_ratio=self.pad_to_aspect_ratio,
195
+ )
160
196
 
161
197
  img_array = tf.keras.utils.img_to_array(img)
162
198
  img_preprocessed = tf.expand_dims(img_array, 0) # Create a batch
@@ -164,7 +200,7 @@ class Classify():
164
200
 
165
201
  @tf.function
166
202
  def predict(self, img_preprocessed):
167
- '''
203
+ """
168
204
  Use tensorflow model to classify particles. example here based on the pysilcam network setup.
169
205
 
170
206
  Parameters
@@ -177,14 +213,14 @@ class Classify():
177
213
  -------
178
214
  prediction : array
179
215
  The probability of the roi belonging to each class
180
- '''
216
+ """
181
217
 
182
218
  prediction = self.model(img_preprocessed, training=False)
183
219
  prediction = tf.nn.softmax(prediction[0])
184
220
  return prediction
185
221
 
186
222
  def proc_predict(self, img_input):
187
- '''
223
+ """
188
224
  Run pre-processing (:meth:`Classify.preprocessing`) and prediction (:meth:`Classify.predict`)
189
225
  using tensorflow model to classify particles. example here based on the pysilcam network setup.
190
226
 
@@ -197,7 +233,7 @@ class Classify():
197
233
  -------
198
234
  prediction : array
199
235
  The probability of the roi belonging to each class
200
- '''
236
+ """
201
237
  img_preprocessed = self.preprocessing(img_input)
202
238
  prediction = self.predict(img_preprocessed)
203
239