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.
- {pyopia-1.1.7 → pyopia-1.1.8}/PKG-INFO +1 -1
- pyopia-1.1.8/pyopia/__init__.py +1 -0
- {pyopia-1.1.7 → pyopia-1.1.8}/pyopia/exampledata.py +28 -0
- pyopia-1.1.8/pyopia/tests/test_classify.py +212 -0
- pyopia-1.1.7/pyopia/__init__.py +0 -1
- {pyopia-1.1.7 → pyopia-1.1.8}/LICENSE +0 -0
- {pyopia-1.1.7 → pyopia-1.1.8}/README.md +0 -0
- {pyopia-1.1.7 → pyopia-1.1.8}/pyopia/background.py +0 -0
- {pyopia-1.1.7 → pyopia-1.1.8}/pyopia/classify.py +0 -0
- {pyopia-1.1.7 → pyopia-1.1.8}/pyopia/cli.py +0 -0
- {pyopia-1.1.7 → pyopia-1.1.8}/pyopia/instrument/__init__.py +0 -0
- {pyopia-1.1.7 → pyopia-1.1.8}/pyopia/instrument/holo.py +0 -0
- {pyopia-1.1.7 → pyopia-1.1.8}/pyopia/instrument/silcam.py +0 -0
- {pyopia-1.1.7 → pyopia-1.1.8}/pyopia/io.py +0 -0
- {pyopia-1.1.7 → pyopia-1.1.8}/pyopia/pipeline.py +0 -0
- {pyopia-1.1.7 → pyopia-1.1.8}/pyopia/plotting.py +0 -0
- {pyopia-1.1.7 → pyopia-1.1.8}/pyopia/process.py +0 -0
- {pyopia-1.1.7 → pyopia-1.1.8}/pyopia/statistics.py +0 -0
- {pyopia-1.1.7 → pyopia-1.1.8}/pyopia/tests/__init__.py +0 -0
- {pyopia-1.1.7 → pyopia-1.1.8}/pyopia/tests/test_pipeline.py +0 -0
- {pyopia-1.1.7 → pyopia-1.1.8}/pyproject.toml +0 -0
|
@@ -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()
|
pyopia-1.1.7/pyopia/__init__.py
DELETED
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|