PyOPIA 1.1.7__tar.gz → 1.1.8__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyOPIA
3
- Version: 1.1.7
3
+ Version: 1.1.8
4
4
  Summary: A Python Ocean Particle Image Analysis toolbox.
5
5
  Home-page: https://github.com/sintef/pyopia
6
6
  Keywords: Ocean,Particles,Imaging,Measurement,Size distribution
@@ -0,0 +1 @@
1
+ __version__ = '1.1.8'
@@ -4,6 +4,33 @@ import os
4
4
  import gdown
5
5
 
6
6
 
7
+ def get_classifier_database_from_pysilcam_blob(download_directory='./'):
8
+ '''Downloads a specified filename from the pysilcam.blob into the working dir. if it doesn't already exist
9
+
10
+ only works for known filenames that are on this blob
11
+
12
+ Parameters
13
+ ----------
14
+ filename : string
15
+ known filename on the blob
16
+
17
+ '''
18
+ if os.path.exists(os.path.join(download_directory)):
19
+ print(download_directory, 'already exists. Returning nothing')
20
+ return download_directory
21
+ os.makedirs(download_directory, exist_ok=False)
22
+ url = 'https://pysilcam.blob.core.windows.net/test-data/silcam_database.zip'
23
+ print('Downloading....')
24
+ urllib.request.urlretrieve(url, download_directory + '/silcam_database.zip')
25
+ print('Unzipping....')
26
+ with zipfile.ZipFile(os.path.join(download_directory, 'silcam_database.zip'), 'r') as zipit:
27
+ zipit.extractall(os.path.join(download_directory, '../'))
28
+ print('Removing zip file')
29
+ os.remove(os.path.join(download_directory, 'silcam_database.zip'))
30
+ print('Done.')
31
+ return download_directory
32
+
33
+
7
34
  def get_file_from_pysilcam_blob(filename, download_directory='./'):
8
35
  '''Downloads a specified filename from the pysilcam.blob into the working dir. if it doesn't already exist
9
36
 
@@ -19,6 +46,7 @@ def get_file_from_pysilcam_blob(filename, download_directory='./'):
19
46
  return filename
20
47
  url = 'https://pysilcam.blob.core.windows.net/test-data/' + filename
21
48
  urllib.request.urlretrieve(url, os.path.join(download_directory, filename))
49
+ return download_directory
22
50
 
23
51
 
24
52
  def get_example_silc_image(download_directory='./'):
@@ -0,0 +1,212 @@
1
+ '''
2
+ A high level test for the basic processing pipeline.
3
+
4
+ '''
5
+
6
+ from glob import glob
7
+ import tempfile
8
+ import os
9
+
10
+ import pyopia.exampledata as exampledata
11
+ import pyopia.io
12
+ import pyopia.classify
13
+ import pyopia.pipeline
14
+ import pyopia.process
15
+ import pyopia.statistics
16
+ import pyopia.background # noqa: F401
17
+ import pandas as pd
18
+ import skimage.io
19
+ import numpy as np
20
+ import pyopia.instrument.silcam
21
+
22
+
23
+ def test_match_to_database():
24
+ '''
25
+ Basic check of classification prediction against the training database.
26
+ Therefore, if correct positive matches are not high percentages, then something is wrong with the prediction.
27
+
28
+ @todo include more advanced testing of the classification feks. assert values in a confusion matrix.
29
+ '''
30
+
31
+ # location of the training data
32
+ with tempfile.TemporaryDirectory() as tempdir:
33
+ # location of the training data
34
+ database_path = os.path.join(tempdir, 'silcam_classification_database')
35
+
36
+ exampledata.get_classifier_database_from_pysilcam_blob(database_path)
37
+ os.makedirs(os.path.join(tempdir, 'model'), exist_ok=True)
38
+ model_path = exampledata.get_example_model(os.path.join(tempdir, 'model'))
39
+
40
+ # Load the trained tensorflow model and class names
41
+ cl = pyopia.classify.Classify(model_path)
42
+ class_labels = cl.class_labels
43
+
44
+ # class_labels should match the training data
45
+ classes = sorted(glob(os.path.join(database_path, '*')))
46
+
47
+ # @todo write a quick check that classes and class_labels agree before doing the proper test.
48
+
49
+ def correct_positives(category):
50
+ '''
51
+ calculate the percentage positive matches for a given category
52
+ '''
53
+ print('Checking', category)
54
+ # list the files in this category of the training data
55
+ files = glob(os.path.join(database_path, category, '*.tiff'))
56
+
57
+ assert len(files) > 50, 'less then 50 files in test data.'
58
+
59
+ # start a counter of incorrectly classified images
60
+ failed = 0
61
+ time_limit = len(files) * 0.01
62
+ t1 = pd.Timestamp.now()
63
+
64
+ # loop through the database images
65
+ for file in files:
66
+ img = skimage.io.imread(file) # load ROI
67
+ prediction = cl.proc_predict(img) # run prediction from silcam_classify
68
+
69
+ ind = np.argmax(prediction) # find the highest score
70
+
71
+ # check if the highest score matches the correct category
72
+ if not class_labels[ind] == category:
73
+ # if not, the add to the failure count
74
+ failed += 1
75
+
76
+ # turn failed count into a success percent
77
+ success = 100 - (failed / len(files)) * 100
78
+
79
+ t2 = pd.Timestamp.now()
80
+ td = t2 - t1
81
+ assert td < pd.to_timedelta(time_limit, 's'), 'Processing time too long.'
82
+
83
+ return success
84
+
85
+ # loop through each category and calculate the success percentage
86
+ for cat in classes:
87
+ name = os.path.split(cat)[-1]
88
+ success = correct_positives(name)
89
+ print(name, success)
90
+ assert success > 96, (name + ' was poorly classified at only ' + str(success) + 'percent.')
91
+
92
+
93
+ def test_pipeline_classification():
94
+ with tempfile.TemporaryDirectory() as tempdir:
95
+ # location of the training data
96
+ database_path = os.path.join(tempdir, 'silcam_classification_database')
97
+
98
+ exampledata.get_classifier_database_from_pysilcam_blob(database_path)
99
+ os.makedirs(os.path.join(tempdir, 'model'), exist_ok=True)
100
+ model_path = exampledata.get_example_model(os.path.join(tempdir, 'model'))
101
+
102
+ # Load the trained tensorflow model and class names
103
+ cl = pyopia.classify.Classify(model_path)
104
+
105
+ def get_good_roi(category):
106
+ '''
107
+ calculate the percentage positive matches for a given category
108
+ '''
109
+
110
+ # list the files in this category of the training data
111
+ files = sorted(glob(os.path.join(database_path, category, '*.tiff')))
112
+
113
+ # loop through the database images
114
+ for file in files:
115
+ img = skimage.io.imread(file) # load ROI
116
+ prediction = cl.proc_predict(img) # run prediction from silcam_classify
117
+
118
+ if np.max(prediction) < 0.96:
119
+ continue
120
+
121
+ ind = np.argmax(prediction) # find the highest score
122
+
123
+ # check if the highest score matches the correct category
124
+ if cl.class_labels[ind] == category:
125
+ return img, category
126
+
127
+ cl = pyopia.classify.Classify(model_path)
128
+ canvas = np.ones((2048, 2448, 3), np.float64)
129
+
130
+ rc_shift = int(2048/len(cl.class_labels)/1.5)
131
+ rc = rc_shift
132
+
133
+ classes = sorted(glob(os.path.join(database_path, '*')))
134
+ print(classes)
135
+
136
+ categories = []
137
+
138
+ for cat in classes:
139
+ name = os.path.split(cat)[-1]
140
+ img, category = get_good_roi(name)
141
+ categories.append(category)
142
+ img_shape = np.shape(img)
143
+ rc += rc_shift
144
+ canvas[rc:rc + img_shape[0], rc: rc + img_shape[1], :] = np.float64(img)/255
145
+
146
+ settings = {'general': {'raw_files': None,
147
+ 'pixel_size': 24},
148
+ 'steps': {'note': 'non-standard pipeline.'}
149
+ }
150
+
151
+ # Initialise the pipeline class without running anything
152
+ MyPipeline = pyopia.pipeline.Pipeline(settings=settings, initial_steps='')
153
+
154
+ # Get the example trained model
155
+ model_path = pyopia.exampledata.get_example_model(os.getcwd())
156
+
157
+ # Add the classifier step description to settings (i.e. metadata)
158
+ MyPipeline.settings['steps'].update({'classifier':
159
+ {'pipeline_class': 'pyopia.classify.Classify',
160
+ 'model_path': model_path}})
161
+
162
+ # Execute the classifier step we defined above
163
+ MyPipeline.run_step('classifier')
164
+ # This is the same as running:
165
+ # MyPipeline.data['cl'] = pyopia.classify.Classify(model_path=model_path)
166
+ # Note: the classifier step is special in that it's output is specifically data['cl'], rather than other new keys in data
167
+
168
+ MyPipeline.data['imraw'] = canvas
169
+ MyPipeline.data['timestamp'] = pd.Timestamp.now()
170
+
171
+ # Add the imageprep step description
172
+ MyPipeline.settings['steps'].update({'imageprep':
173
+ {'pipeline_class': 'pyopia.instrument.silcam.ImagePrep',
174
+ 'image_level': 'imraw'}})
175
+ # Run the step
176
+ MyPipeline.run_step('imageprep')
177
+ # This is the same as running:
178
+ # ImagePrep = pyopia.instrument.silcam.ImagePrep(image_level='imraw')
179
+ # MyPipeline.data = ImagePrep(MyPipeline.data)
180
+
181
+ # Add the segmentation step description
182
+ MyPipeline.settings['steps'].update({'segmentation':
183
+ {'pipeline_class': 'pyopia.process.Segment',
184
+ 'threshold': 1}})
185
+ # Run the step
186
+ MyPipeline.run_step('segmentation')
187
+ # This is the same as running:
188
+ # Segment = pyopia.process.Segment(threshold=settings['steps']['segmentation']['threshold'])
189
+ # data = Segment(data)
190
+
191
+ # Add the segmentation step description
192
+ MyPipeline.settings['steps'].update({'statextract':
193
+ {'pipeline_class': 'pyopia.process.CalculateStats'}}
194
+ )
195
+
196
+ # Run the step
197
+ MyPipeline.run_step('statextract')
198
+ # This is the same as running:
199
+ # CalculateStats = pyopia.process.CalculateStats()
200
+ # data = CalculateStats(data)
201
+
202
+ stats = pyopia.statistics.add_best_guesses_to_stats(MyPipeline.data['stats'])
203
+
204
+ out = [x[12:] for x in stats['best guess'].values]
205
+ assert categories == out, 'Classes returned from classifier do not match what was given to the pipeline'
206
+ print('classes input', categories)
207
+ print('classes measured', out)
208
+
209
+
210
+ if __name__ == "__main__":
211
+ test_match_to_database()
212
+ test_pipeline_classification()
@@ -1 +0,0 @@
1
- __version__ = '1.1.7'
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