NSFopen 2.2.2__tar.gz → 2.2.4__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 (59) hide show
  1. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/__init__.py +9 -5
  2. nsfopen-2.2.4/NSFopen/_version.py +1 -0
  3. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Force_Spectroscopy/Force_Spectroscopy.py +40 -23
  4. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/process/__init__.py +2 -1
  5. nsfopen-2.2.4/NSFopen/process/colormaps.py +71 -0
  6. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/process/process.py +220 -187
  7. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/read/read.py +3 -6
  8. {nsfopen-2.2.2 → nsfopen-2.2.4/NSFopen.egg-info}/PKG-INFO +1 -1
  9. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen.egg-info/SOURCES.txt +1 -0
  10. {nsfopen-2.2.2/NSFopen.egg-info → nsfopen-2.2.4}/PKG-INFO +1 -1
  11. nsfopen-2.2.2/NSFopen/_version.py +0 -1
  12. {nsfopen-2.2.2 → nsfopen-2.2.4}/LICENSE +0 -0
  13. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Calculate_Roughness/Calculate_Roughness.html +0 -0
  14. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Calculate_Roughness/Calculate_Roughness.ipynb +0 -0
  15. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Calculate_Roughness/Calculate_Roughness.py +0 -0
  16. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Calculate_Roughness/__pycache__/Calculate_Roughness.cpython-310.pyc +0 -0
  17. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Calculate_Roughness/sapphire.nid +0 -0
  18. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Force_Spectroscopy/Force_Spectroscopy.html +0 -0
  19. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Force_Spectroscopy/Force_Spectroscopy.ipynb +0 -0
  20. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Force_Spectroscopy/spectroscopy.nid +0 -0
  21. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Lateral_Force/Lateral Force Processing.html +0 -0
  22. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Lateral_Force/Lateral Force Processing.ipynb +0 -0
  23. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Lateral_Force/Lateral Force Processing.py +0 -0
  24. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Lateral_Force/SBS-PMMA.nid +0 -0
  25. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Plane_Removal/Plane_Removal.html +0 -0
  26. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Plane_Removal/Plane_Removal.ipynb +0 -0
  27. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Plane_Removal/Plane_Removal.py +0 -0
  28. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Plane_Removal/circles.nid +0 -0
  29. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Plane_Removal/circles_holes.nid +0 -0
  30. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Plane_Removal/grid.nid +0 -0
  31. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Plane_Removal/squares_holes.nid +0 -0
  32. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Plotting_Data/MoS2.nid +0 -0
  33. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Plotting_Data/Plotting Data.html +0 -0
  34. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Plotting_Data/Plotting Data.ipynb +0 -0
  35. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Plotting_Data/Plotting Data.py +0 -0
  36. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Sharpen_Image/Sharpen Image.html +0 -0
  37. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Sharpen_Image/Sharpen Image.ipynb +0 -0
  38. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Sharpen_Image/Sharpen Image.py +0 -0
  39. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Sharpen_Image/grid.nid +0 -0
  40. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Sharpen_Image/img.jpg +0 -0
  41. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Spring_Constant/Spring_Constant.html +0 -0
  42. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Spring_Constant/Spring_Constant.ipynb +0 -0
  43. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Spring_Constant/Spring_Constant.py +0 -0
  44. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Spring_Constant/__pycache__/Spring_Constant.cpython-310.pyc +0 -0
  45. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Spring_Constant/__pycache__/__init__.cpython-310.pyc +0 -0
  46. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Spring_Constant/sader.py +0 -0
  47. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Spring_Constant/thermal.nid +0 -0
  48. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/example/Spring_Constant/thermal.png +0 -0
  49. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/read/__init__.py +0 -0
  50. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen/read/read_beta.py +0 -0
  51. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen.egg-info/dependency_links.txt +0 -0
  52. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen.egg-info/entry_points.txt +0 -0
  53. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen.egg-info/not-zip-safe +0 -0
  54. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen.egg-info/requires.txt +0 -0
  55. {nsfopen-2.2.2 → nsfopen-2.2.4}/NSFopen.egg-info/top_level.txt +0 -0
  56. {nsfopen-2.2.2 → nsfopen-2.2.4}/README.md +0 -0
  57. {nsfopen-2.2.2 → nsfopen-2.2.4}/pyproject.toml +0 -0
  58. {nsfopen-2.2.2 → nsfopen-2.2.4}/setup.cfg +0 -0
  59. {nsfopen-2.2.2 → nsfopen-2.2.4}/setup.py +0 -0
@@ -4,22 +4,26 @@ License - MIT"""
4
4
 
5
5
  import os
6
6
  import pathlib
7
- from . _version import __version__
7
+ from ._version import __version__
8
+
9
+ from .read import read
10
+
11
+ # from . process import process
8
12
 
9
- from . read import read
10
- from . process import process
11
13
 
12
14
  def package_path() -> pathlib.Path:
13
15
  return pathlib.Path(os.path.dirname(os.path.abspath(__file__)))
14
16
 
17
+
15
18
  def example_path() -> pathlib.Path:
16
19
  return package_path() / "example"
17
20
 
18
- def help():
21
+
22
+ def help():
19
23
  print("Nanosurf NID and NHF file reader:")
20
24
  print("-------------------------------")
21
25
  print(f"Installed version: {__version__}")
22
26
  print("\nExamples are stored here:")
23
27
  print(example_path())
24
28
  for example in os.listdir(example_path()):
25
- print(f" {example}")
29
+ print(f" {example}")
@@ -0,0 +1 @@
1
+ __version__ = "2.2.4"
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python
2
2
  # coding: utf-8
3
3
 
4
- # ## This script demonstrates how to open a Nanosurf Force Spectroscopy map and analyze the data using the DMT contact mechanics model
4
+ # ## This script demonstrates how to open a Nanosurf Force Spectroscopy map and analyze the data using the DMT contact mechanics model
5
5
 
6
6
  # In[1]:
7
7
 
@@ -16,23 +16,27 @@ from scipy.optimize import curve_fit
16
16
 
17
17
  from glob import glob
18
18
 
19
- filename = 'spectroscopy.nid'
20
- filename_large = 'spectroscopy_large.nid'
19
+ filename = "spectroscopy.nid"
20
+ filename_large = "spectroscopy_large.nid"
21
21
 
22
22
 
23
23
  # In[2]:
24
24
 
25
25
 
26
- # If USE_LARGE_MAP is True, then it will use the larger force-curve map, downloading if not already locally available
26
+ # If USE_LARGE_MAP is True, then it will use the larger force-curve map, downloading if not already locally available
27
27
  USE_LARGE_MAP = False
28
28
 
29
29
 
30
30
  # In[3]:
31
31
 
32
32
 
33
- if USE_LARGE_MAP and filename_large not in glob('*nid'):
33
+ if USE_LARGE_MAP and filename_large not in glob("*nid"):
34
34
  import urllib.request
35
- urllib.request.urlretrieve("https://www.dropbox.com/s/knzuh588g0uvjfi/spectroscopy_large.nid?dl=1", filename_large)
35
+
36
+ urllib.request.urlretrieve(
37
+ "https://www.dropbox.com/s/knzuh588g0uvjfi/spectroscopy_large.nid?dl=1",
38
+ filename_large,
39
+ )
36
40
  filename = filename_large
37
41
  elif USE_LARGE_MAP:
38
42
  filename = filename_large
@@ -47,20 +51,28 @@ data = afm.data
47
51
  param = afm.param
48
52
 
49
53
  # probe
50
- k = param.Tip.Prop0['Value'] # spring constant
51
- sens = param.Tip.Sensitivity['Value'] # deflection sensitivity (if needed)
54
+ k = param.Tip.Prop0["Value"] # spring constant
55
+ sens = param.Tip.Sensitivity["Value"] # deflection sensitivity (if needed)
52
56
 
53
57
  radius = 10e-9 # tip radius
54
58
  nu = 0.35 # Poisson ratio (between 0.35 and 0.5)
55
59
 
56
- spec_backward = data['Spec']['Backward'] # for DMT model we will use backward modulation
57
- d = spec_backward['Deflection'] # force channel
60
+ direction = "Backward" # Forward or Backward modulation
61
+
62
+ spec_backward = data["Spec"][
63
+ direction
64
+ ] # for DMT model we will use backward modulation
65
+ d = spec_backward["Deflection"] # force channel
58
66
 
59
67
  z_signal = list(spec_backward.index)
60
- z_chans = ['Z-Axis Sensor', 'Z-AxisSensor', 'Z-Axis'] # depending on system, Z-Axis channel may be labeled differently
68
+ z_chans = [
69
+ "Z-Axis Sensor",
70
+ "Z-AxisSensor",
71
+ "Z-Axis",
72
+ ] # depending on system, Z-Axis channel may be labeled differently
61
73
  available_chan = [i for i in z_signal if i in z_chans][0]
62
74
 
63
- z = data['Spec']['Backward'][available_chan] # Z-axis data
75
+ z = data["Spec"][direction][available_chan] # Z-axis data
64
76
 
65
77
  sz = int(param.Spec.maps[4]) # Map size
66
78
 
@@ -72,6 +84,7 @@ extent = param.Spec.maps[:4] # X and Y physical dimensions of map
72
84
 
73
85
  ## Functions
74
86
 
87
+
75
88
  # corrects for any baseline tilt
76
89
  def baseline_correct(x, y, degree=1, start=0, end=0.5):
77
90
  i0 = int(np.floor(len(x) * start))
@@ -90,14 +103,15 @@ def baseline_correct(x, y, degree=1, start=0, end=0.5):
90
103
  def DMT(x, *param):
91
104
  elast, depth, wadh = param
92
105
 
93
- scale = 4./3. * elast * np.sqrt(radius) / (1 - nu**2)
106
+ scale = 4.0 / 3.0 * elast * np.sqrt(radius) / (1 - nu**2)
94
107
  x = x - depth
95
- y = scale * np.sign(x) * np.abs(x)**(3./2.) - wadh
108
+ y = scale * np.sign(x) * np.abs(x) ** (3.0 / 2.0) - wadh
96
109
  return y
97
110
 
98
111
 
99
112
  def DMTfit(*args, maxForce=np.Inf):
100
113
  import time
114
+
101
115
  t = time.time()
102
116
  # args are: [separation, load (force)]
103
117
  if not isinstance(args[0], list):
@@ -108,7 +122,7 @@ def DMTfit(*args, maxForce=np.Inf):
108
122
  # x = x - y / spring_constant # separation
109
123
  lenY = int(np.floor(len(y) * 0.8))
110
124
  a = min(y[lenY:]) # adhesion
111
-
125
+
112
126
  i0 = np.where(y == a)[0][0] # contact point estimator index
113
127
  i1 = np.where(y > maxForce)[0] # max force index
114
128
 
@@ -118,13 +132,15 @@ def DMTfit(*args, maxForce=np.Inf):
118
132
  i1 = i1[0]
119
133
 
120
134
  try:
121
- coeff, var_matrix = curve_fit(DMT, x[i0:i1], y[i0:i1], p0=[1e9, x[i0], -a])
135
+ coeff, var_matrix = curve_fit(
136
+ DMT, x[i0:i1], y[i0:i1], p0=[1e9, x[i0], -a]
137
+ )
122
138
  except RuntimeError:
123
139
  E.append(np.nan)
124
140
  else:
125
141
  E.append(max(coeff[0], 0))
126
142
 
127
- print('Elapsed Time: %3.2f sec\n' % (time.time() - t))
143
+ print("Elapsed Time: %3.2f sec\n" % (time.time() - t))
128
144
  return np.array(E), (coeff, i0, i1)
129
145
 
130
146
 
@@ -135,7 +151,7 @@ def DMTfit(*args, maxForce=np.Inf):
135
151
  dd = [baseline_correct(z_, d_, start=0.5, end=0.8) for z_, d_ in zip(z, d)]
136
152
 
137
153
  # separation
138
- zz = [zz - dd/k for dd, zz in zip(dd, z)]
154
+ zz = [zz - dd / k for dd, zz in zip(dd, z)]
139
155
 
140
156
  # elastic modulus
141
157
  Eb, _ = DMTfit(zz, dd)
@@ -159,14 +175,16 @@ Adh = toMap(adh, sz)
159
175
  # In[7]:
160
176
 
161
177
 
162
- fig, ax = plt.subplots(2, 2, figsize=(10, 8), gridspec_kw={'height_ratios': [2, 1]}, dpi=300)
178
+ fig, ax = plt.subplots(
179
+ 2, 2, figsize=(10, 8), gridspec_kw={"height_ratios": [2, 1]}, dpi=300
180
+ )
163
181
  fig.tight_layout(w_pad=8.0, h_pad=1.0)
164
182
 
165
183
  im = ax.copy()
166
184
 
167
185
  cmap = "afmhot" # colormap
168
- xlbl = 'X [$\mu$m]'
169
- ylbl = 'Y [$\mu$m]'
186
+ xlbl = "X [$\mu$m]"
187
+ ylbl = "Y [$\mu$m]"
170
188
  zlbl = ["Young's Modulus [GPa]", "Adhesion [nN]"]
171
189
 
172
190
  im[0, 0] = ax[0, 0].imshow(Ebim)
@@ -182,8 +200,7 @@ for i in range(2):
182
200
  cb.set_label(zlbl[i])
183
201
 
184
202
  ax[0, i].set(xlabel=xlbl, ylabel=ylbl)
185
- ax[1, i].set(xlabel=zlbl[i], ylabel='Counts')
203
+ ax[1, i].set(xlabel=zlbl[i], ylabel="Counts")
186
204
 
187
205
 
188
206
  plt.show()
189
-
@@ -5,4 +5,5 @@ Created on Sat Oct 12 12:43:59 2019
5
5
  @author: Edward
6
6
  """
7
7
 
8
- from . process import process
8
+ from . process import process
9
+ from . colormaps import colors
@@ -0,0 +1,71 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Thu May 23 13:04:53 2024
4
+
5
+ @author: nelson
6
+ """
7
+
8
+ import numpy as np
9
+ import pandas as pd
10
+ import os
11
+
12
+ from matplotlib.colors import LinearSegmentedColormap
13
+
14
+
15
+ class colors:
16
+ def __init__(self, name=None, gwy_dir=None, **kwargs):
17
+ self.name = name
18
+ self.gwy_dir = gwy_dir
19
+ if not gwy_dir:
20
+ self.gwy_dir = (
21
+ "C:\\Program Files\\Gwyddion\\share\\gwyddion\\gradients\\"
22
+ )
23
+ custom = ["BlueBlackRed"]
24
+ gwy_list = os.listdir(self.gwy_dir)
25
+
26
+ cmaps = {}
27
+ cmaps["Custom"] = custom
28
+ cmaps["Gwyddion"] = gwy_list
29
+
30
+ if name in gwy_list:
31
+ cmap = self.gwy_cmap()
32
+ elif name in custom:
33
+ cmap = self.custom()
34
+ else:
35
+ print("Name Not Found\n")
36
+ print("Use one of the following choices:\n")
37
+ print({x for v in cmaps.values() for x in v})
38
+ cmap = []
39
+ self.cmap = cmap
40
+ self.cmap_list = cmaps
41
+
42
+ def gwy_cmap(self):
43
+ cmap = pd.read_csv(
44
+ self.gwy_dir + self.name,
45
+ sep=" ",
46
+ header=None,
47
+ skiprows=1,
48
+ index_col=0,
49
+ )
50
+ nodes = cmap.index.values
51
+ colors = cmap.values
52
+
53
+ return LinearSegmentedColormap.from_list(
54
+ self.name, list(zip(nodes, colors))
55
+ )
56
+
57
+ def custom(self):
58
+ if self.name == "BlueBlackRed":
59
+ # Blue-Black-Red
60
+ start = np.array(
61
+ [0.6196078431372549, 0.792156862745098, 0.8823529411764706]
62
+ )
63
+ mid = np.array([0, 0, 0])
64
+ end = np.array(
65
+ [0.9882352941176471, 0.5725490196078431, 0.4470588235294118]
66
+ )
67
+ nodes = [0, 0.5, 1]
68
+
69
+ return LinearSegmentedColormap.from_list(
70
+ self.name, list(zip(nodes, [start, mid, end]))
71
+ )
@@ -11,7 +11,7 @@ import matplotlib.pyplot as plt
11
11
  from mpl_toolkits.mplot3d import Axes3D
12
12
  from matplotlib.ticker import LinearLocator, FormatStrFormatter
13
13
  from matplotlib import cm
14
- from scipy.ndimage import gaussian_filter,median_filter
14
+ from scipy.ndimage import gaussian_filter, median_filter
15
15
  from sklearn.impute import KNNImputer
16
16
  from scipy import stats
17
17
 
@@ -20,24 +20,26 @@ import scipy.stats as ss
20
20
  import os, sys
21
21
 
22
22
 
23
- class process():
23
+ class process:
24
24
  def __init__(self, filename):
25
-
25
+
26
26
  afm = read(filename)
27
27
  self.data = afm.data
28
28
  self.param = afm.param
29
29
 
30
- def flatten(self,
31
- order=1,
32
- direction='Forward',
33
- channel='Z-Axis',
34
- mask=[],
35
- inplace=True):
36
- """ Flattens an image using a nth-order polynomial
37
-
30
+ def flatten(
31
+ self,
32
+ order=1,
33
+ direction="Forward",
34
+ channel="Z-Axis",
35
+ mask=[],
36
+ inplace=True,
37
+ ):
38
+ """Flattens an image using a nth-order polynomial
39
+
38
40
  Parameters
39
41
  ----------
40
-
42
+
41
43
  order : int, default 1
42
44
  Order of the polynomial
43
45
  direction : {'Forward','Backward'}, defulat 'Forward'
@@ -47,14 +49,14 @@ class process():
47
49
  mask : boolean array
48
50
  Mask where True values are ignored in the input array
49
51
  inplace : boolean, default True
50
-
52
+
51
53
  Return
52
54
  ------
53
55
  array or None
54
56
  Flattened image or None if inplace=True
55
-
57
+
56
58
  """
57
- data_out = self.data['Image'][direction][channel]
59
+ data_out = self.data["Image"][direction][channel]
58
60
  data_in = data_out.copy()
59
61
  data_in = np.array(data_in, dtype=float)
60
62
  x = np.arange(data_in.shape[1])
@@ -67,83 +69,83 @@ class process():
67
69
  y = np.polyval(p, x)
68
70
  data_out[idx] = out - y
69
71
  if inplace:
70
- self.data['Image'].loc[direction,channel] = data_out
72
+ self.data["Image"].loc[direction, channel] = data_out
71
73
  return None
72
74
  else:
73
75
  return data_out
74
-
75
- def plane_level(self,
76
- direction='Forward',
77
- channel='Z-Axis',
78
- inplace=True):
79
- data_out = self.data['Image'][direction][channel]
76
+
77
+ def plane_level(self, direction="Forward", channel="Z-Axis", inplace=True):
78
+ data_out = self.data["Image"][direction][channel]
80
79
  data_in = data_out.copy()
81
80
  data_in = np.array(data_in, dtype=float)
82
-
81
+
83
82
  # x_size = self.param.X.range[0] * 1e6
84
83
  # y_size = self.param.Y.range[0] * 1e6
85
84
 
86
85
  # xs = np.linspace(0,x_size,data_in.shape[0])
87
86
  # ys = np.linspace(0,y_size,data_in.shape[1])
88
-
89
- xs = np.arange(0,data_in.shape[0])
90
- ys = np.arange(0,data_in.shape[1])
91
- xs,ys = np.meshgrid(xs,ys)
87
+
88
+ xs = np.arange(0, data_in.shape[0])
89
+ ys = np.arange(0, data_in.shape[1])
90
+ xs, ys = np.meshgrid(xs, ys)
92
91
  ones = np.ones(xs.shape)
93
92
 
94
- reshape = (lambda x: np.reshape(x,(len(x)**2,1)))
93
+ reshape = lambda x: np.reshape(x, (len(x) ** 2, 1))
95
94
 
96
- A = np.matrix(np.hstack([reshape(xs),reshape(ys),reshape(ones)]))
95
+ A = np.matrix(np.hstack([reshape(xs), reshape(ys), reshape(ones)]))
97
96
  b = np.matrix(reshape(data_in))
98
97
 
99
98
  fit = (A.T * A).I * A.T * b
100
- plane = np.reshape(A*fit,data_in.shape)
101
- data_out-=plane
99
+ plane = np.reshape(A * fit, data_in.shape)
100
+ data_out -= plane
102
101
  if inplace:
103
- self.data['Image'].loc[direction,channel] = data_out
102
+ self.data["Image"].loc[direction, channel] = data_out
104
103
  return None
105
104
  else:
106
105
  return data_out
107
-
108
- def remove_outliers(self,
109
- direction='Forward',
110
- channel='Z-Axis',
111
- q=0.01,
112
- neighbors=5,
113
- inplace=True):
114
- data_out = self.data['Image'][direction][channel]
106
+
107
+ def remove_outliers(
108
+ self,
109
+ direction="Forward",
110
+ channel="Z-Axis",
111
+ q=0.01,
112
+ neighbors=5,
113
+ inplace=True,
114
+ ):
115
+ data_out = self.data["Image"][direction][channel]
115
116
  data_in = data_out.copy()
116
117
  data_in = np.array(data_in, dtype=float)
117
-
118
- if isinstance(q,list):
119
- q0,q1=q
118
+
119
+ if isinstance(q, list):
120
+ q0, q1 = q
120
121
  else:
121
- q0=q1=q
122
-
123
- value = np.quantile(data_in,q=[q0,1-q1])
124
- mask = (data_in<value[0]) | (data_in>value[1])
125
- data_in[mask]=np.nan
122
+ q0 = q1 = q
123
+
124
+ value = np.quantile(data_in, q=[q0, 1 - q1])
125
+ mask = (data_in < value[0]) | (data_in > value[1])
126
+ data_in[mask] = np.nan
126
127
 
127
- data_out=KNNImputer(n_neighbors=neighbors).fit_transform(data_in)
128
+ data_out = KNNImputer(n_neighbors=neighbors).fit_transform(data_in)
128
129
  if inplace:
129
- self.data['Image'].loc[direction,channel] = data_out
130
+ self.data["Image"].loc[direction, channel] = data_out
130
131
  return None
131
132
  else:
132
133
  return data_out
133
-
134
-
135
- def denoise(self,
136
- direction='Forward',
137
- channel='Z-Axis',
138
- function='gaussian',
139
- size=5,
140
- inplace=True,
141
- **kwargs):
142
- """ Applies a 2D filter to an image.
143
-
134
+
135
+ def denoise(
136
+ self,
137
+ direction="Forward",
138
+ channel="Z-Axis",
139
+ function="gaussian",
140
+ size=5,
141
+ inplace=True,
142
+ **kwargs,
143
+ ):
144
+ """Applies a 2D filter to an image.
145
+
144
146
  Parameters
145
147
  ----------
146
-
148
+
147
149
  direction: {'Forward','Backward'}, default 'Forward'
148
150
  Scan direction to use
149
151
  channel: string, default 'Z-Axis'
@@ -156,41 +158,42 @@ class process():
156
158
  Whether to modify the input array or create new one
157
159
  **kwargs:
158
160
  Passed to filters
159
-
161
+
160
162
  Return
161
163
  ------
162
164
  array or None
163
165
  Filtered image or None if inplace=True
164
-
166
+
165
167
  """
166
- data_in = self.data['Image'][direction][channel]
167
- if function == 'median':
168
+ data_in = self.data["Image"][direction][channel]
169
+ if function == "median":
168
170
  data_out = median_filter(data_in, size=size, **kwargs)
169
171
  else:
170
172
  data_out = gaussian_filter(data_in, sigma=size, **kwargs)
171
173
  if inplace:
172
- self.data['Image'].loc[direction,channel] = data_out
174
+ self.data["Image"].loc[direction, channel] = data_out
173
175
  return None
174
176
  else:
175
177
  return data_out
176
-
177
-
178
- def image(self,
179
- direction='Forward',
180
- channel='Z-Axis',
181
- scale=1e9,
182
- cmap=cm.afmhot,
183
- cbar=None,
184
- fontsize=10,
185
- scalebar=None,
186
- theme='light',
187
- title='XXX',
188
- **kwargs):
189
- """ Plots an AFM image
190
-
178
+
179
+ def image(
180
+ self,
181
+ direction="Forward",
182
+ channel="Z-Axis",
183
+ scale=1e9,
184
+ cmap=cm.afmhot,
185
+ cbar=None,
186
+ fontsize=10,
187
+ scalebar=None,
188
+ theme="light",
189
+ title="XXX",
190
+ **kwargs,
191
+ ):
192
+ """Plots an AFM image
193
+
191
194
  Parameters
192
195
  ----------
193
-
196
+
194
197
  direction: {'Forward','Backward'}, default 'Forward'
195
198
  Scan direction to use
196
199
  channel: string, default 'Z-Axis'
@@ -211,126 +214,142 @@ class process():
211
214
  Use either 'light' or 'dark' theme
212
215
  **kwargs:
213
216
  Passed to matplotlib imshow
214
-
217
+
215
218
  Return
216
219
  ------
217
220
  figure
218
221
  matplotlib figure object
219
-
222
+
220
223
  """
221
- d = np.array(self.data['Image'][direction][channel]*scale,dtype=float)
222
- if hasattr(self.param, 'X'):
224
+ d = np.array(
225
+ self.data["Image"][direction][channel] * scale, dtype=float
226
+ )
227
+ if hasattr(self.param, "X"):
223
228
  x = self.param.X.range[0] * 1e6
224
229
  y = self.param.Y.range[0] * 1e6
225
- else:
230
+ elif hasattr(self.param, "image_size_x"):
226
231
  x = self.param.image_size_x * 1e6
227
232
  y = self.param.image_size_y * 1e6
228
-
233
+ else:
234
+ x, y = self.param.rect_axis_range
235
+ x *= 1e6
236
+ y *= 1e6
237
+
229
238
  fig = plt.figure(dpi=300)
230
239
 
231
- if theme=='light':
232
- color = 'k'
240
+ if theme == "light":
241
+ color = "k"
233
242
  else:
234
- color= 'w'
235
- fig.patch.set_facecolor('k')
243
+ color = "w"
244
+ fig.patch.set_facecolor("k")
236
245
 
237
- im = plt.imshow(d, extent=(0, x, 0, y), cmap=cmap, origin='lower', **kwargs)
246
+ im = plt.imshow(
247
+ d, extent=(0, x, 0, y), cmap=cmap, origin="lower", **kwargs
248
+ )
238
249
 
239
250
  for spine in im.axes.spines.values():
240
- spine.set_edgecolor(color)
251
+ spine.set_edgecolor(color)
241
252
 
242
253
  if scalebar:
243
- if isinstance(scalebar,dict):
244
- dx = scalebar['length']
245
- c = scalebar['color']
254
+ if isinstance(scalebar, dict):
255
+ dx = scalebar["length"]
256
+ c = scalebar["color"]
246
257
  else:
247
258
  dx = np.round(x / 5)
248
- c = 'k'
259
+ c = "k"
249
260
 
250
261
  pad = y * 0.05
251
262
  plt.plot([x - dx - pad, x - pad], [pad, pad], linewidth=5, color=c)
252
- txt = plt.text(x - pad - dx / 2, pad + x * 0.03, f'%i $\mu$m' % dx,
253
- horizontalalignment='center',
254
- fontsize=fontsize,
255
- color=c)
256
- if dx<1:
257
- txt.set_text(f'%i nm' % (dx*1e3))
263
+ txt = plt.text(
264
+ x - pad - dx / 2,
265
+ pad + x * 0.03,
266
+ f"%i $\mu$m" % dx,
267
+ horizontalalignment="center",
268
+ fontsize=fontsize,
269
+ color=c,
270
+ )
271
+ if dx < 1:
272
+ txt.set_text(f"%i nm" % (dx * 1e3))
258
273
  plt.xticks([])
259
274
  plt.yticks([])
260
275
  else:
261
- plt.xlabel('X $[\mu{}$m]', fontsize=fontsize, color=color)
262
- plt.ylabel('Y $[\mu{}$m]', fontsize=fontsize, color=color)
276
+ plt.xlabel("X $[\mu{}$m]", fontsize=fontsize, color=color)
277
+ plt.ylabel("Y $[\mu{}$m]", fontsize=fontsize, color=color)
263
278
  plt.xticks(fontsize=fontsize, color=color)
264
279
  plt.yticks(fontsize=fontsize, color=color)
265
280
  im.axes.tick_params(color=color, labelcolor=color)
266
- if title=='XXX':
267
- title = f'{channel} - {direction}'
268
- plt.title(title,fontsize=fontsize, color=color)
281
+ if title == "XXX":
282
+ title = f"{channel} - {direction}"
283
+ plt.title(title, fontsize=fontsize, color=color)
269
284
  if cbar:
270
285
  cb = plt.colorbar(pad=0.02)
271
- cb.set_label(label=cbar,size=fontsize, color=color)
286
+ cb.set_label(label=cbar, size=fontsize, color=color)
272
287
  cb.ax.tick_params(labelsize=fontsize, color=color)
273
288
  cb.ax.yaxis.set_tick_params(color=color)
274
289
  cb.outline.set_edgecolor(color)
275
- plt.setp(plt.getp(cb.ax.axes, 'yticklabels'), color=color)
276
-
290
+ plt.setp(plt.getp(cb.ax.axes, "yticklabels"), color=color)
291
+
277
292
  return fig
278
-
279
- def hist(self,
280
- direction='Forward',
281
- channel='Z-Axis',
282
- bins='sqrt',
283
- scale=1e9,
284
- cmap=cm.afmhot,
285
- xlabel='Z-Axis [nm]',
286
- fontsize=10,
287
- density=False,
288
- theme='light',
289
- **kwargs):
290
-
291
- data = np.array(self.data['Image'][direction][channel]*scale,dtype=float).reshape(-1)
292
- if isinstance(bins,str):
293
- bins = n_bins(data,method=bins)
294
-
293
+
294
+ def hist(
295
+ self,
296
+ direction="Forward",
297
+ channel="Z-Axis",
298
+ bins="sqrt",
299
+ scale=1e9,
300
+ cmap=cm.afmhot,
301
+ xlabel="Z-Axis [nm]",
302
+ fontsize=10,
303
+ density=False,
304
+ theme="light",
305
+ **kwargs,
306
+ ):
307
+
308
+ data = np.array(
309
+ self.data["Image"][direction][channel] * scale, dtype=float
310
+ ).reshape(-1)
311
+ if isinstance(bins, str):
312
+ bins = n_bins(data, method=bins)
313
+
295
314
  fig = plt.figure(dpi=300)
296
-
297
- if theme=='light':
298
- color = 'k'
299
- c_bg = 'w'
315
+
316
+ if theme == "light":
317
+ color = "k"
318
+ c_bg = "w"
300
319
  else:
301
- color= 'w'
302
- c_bg = 'k'
320
+ color = "w"
321
+ c_bg = "k"
303
322
  fig.patch.set_facecolor(c_bg)
304
-
305
-
306
- n, edge, patches = plt.hist(data, bins=bins, density=density, color='green', **kwargs)
323
+
324
+ n, edge, patches = plt.hist(
325
+ data, bins=bins, density=density, color="green", **kwargs
326
+ )
307
327
  bin_centers = 0.5 * (edge[:-1] + edge[1:])
308
-
328
+
309
329
  col = bin_centers - min(bin_centers)
310
330
  col /= max(col)
311
-
331
+
312
332
  for c, p in zip(col, patches):
313
- plt.setp(p, 'facecolor', cmap(c))
314
-
333
+ plt.setp(p, "facecolor", cmap(c))
334
+
315
335
  im = plt.gca()
316
336
  for spine in im.axes.spines.values():
317
- spine.set_edgecolor(color)
318
-
319
- plt.xlabel(xlabel,fontsize=fontsize, color=color)
337
+ spine.set_edgecolor(color)
338
+
339
+ plt.xlabel(xlabel, fontsize=fontsize, color=color)
320
340
  if density:
321
- ylabel='$\\rho$'
341
+ ylabel = "$\\rho$"
322
342
  else:
323
- ylabel='Counts'
324
- plt.ylabel(ylabel,fontsize=fontsize, color=color)
343
+ ylabel = "Counts"
344
+ plt.ylabel(ylabel, fontsize=fontsize, color=color)
325
345
  plt.xticks(fontsize=fontsize, color=color)
326
346
  plt.yticks(fontsize=fontsize, color=color)
327
347
  im.axes.tick_params(color=color, labelcolor=color)
328
-
348
+
329
349
  im.get_children()[-1].set_facecolor(c_bg)
330
-
350
+
331
351
  return fig
332
-
333
-
352
+
334
353
  # def surf(self,
335
354
  # direction='Forward',
336
355
  # channel='Z-Axis',
@@ -374,11 +393,11 @@ class process():
374
393
  def stats(self):
375
394
  # definition of central moment
376
395
  def Mu(x, i):
377
- return np.mean((x-x.mean())**i)
396
+ return np.mean((x - x.mean()) ** i)
378
397
 
379
398
  # mean deviation
380
399
  def Sa_(x):
381
- return np.mean(np.abs(x-x.mean()))
400
+ return np.mean(np.abs(x - x.mean()))
382
401
 
383
402
  # RMS deviation
384
403
  def Sq_(x):
@@ -386,74 +405,88 @@ class process():
386
405
 
387
406
  # Skew
388
407
  def skew_(x):
389
- return Mu(x, 3)/Mu(x, 2)**(3./2.)
408
+ return Mu(x, 3) / Mu(x, 2) ** (3.0 / 2.0)
390
409
 
391
410
  # Kurtosis
392
411
  def kurt_(x):
393
- return Mu(x, 4)/Mu(x, 2)**2 - 3
394
-
412
+ return Mu(x, 4) / Mu(x, 2) ** 2 - 3
395
413
 
396
414
  # stat=[]
397
415
  func = [Sa_, Sq_, skew_, kurt_, np.min, np.max]
398
- fname = ['Sa', 'Sq', 'Ssk', 'Sku', 'Min', 'Max']
416
+ fname = ["Sa", "Sq", "Ssk", "Sku", "Min", "Max"]
399
417
  stats = {}
400
418
  # cnames = []
401
- for d0, _ in self.data['Image'].groupby(level=0):
419
+ for d0, _ in self.data["Image"].groupby(level=0):
402
420
  s = {}
403
- for d1, df in self.data['Image'].groupby(level=1):
421
+ for d1, df in self.data["Image"].groupby(level=1):
404
422
  # cnames.append([d0, d1])
405
423
  s[d1] = {s: f(df[d0][d1]) for s, f in zip(fname, func)}
406
424
  stats[d0] = s
407
425
 
408
426
  return toPandaDF(stats).unstack(level=0)
409
-
427
+
410
428
 
411
429
  def toPandaDF(user_dict):
412
- df = pd.DataFrame.from_dict({(i, j): user_dict[i][j]
413
- for i in user_dict.keys()
414
- for j in user_dict[i].keys()}, orient='index').transpose()
430
+ df = pd.DataFrame.from_dict(
431
+ {
432
+ (i, j): user_dict[i][j]
433
+ for i in user_dict.keys()
434
+ for j in user_dict[i].keys()
435
+ },
436
+ orient="index",
437
+ ).transpose()
415
438
  return df
416
439
 
417
440
 
418
- def n_bins(x,method='sqrt'):
441
+ def n_bins(x, method="sqrt"):
419
442
  n = len(x)
420
- if method=='freedman': # freedman-diaconis rule
421
- h = 2*stats.iqr(x)/np.cbrt(n)
422
- k = int(np.ceil((max(x)-min(x))/h))
423
- elif method=='rice':
443
+ if method == "freedman": # freedman-diaconis rule
444
+ h = 2 * stats.iqr(x) / np.cbrt(n)
445
+ k = int(np.ceil((max(x) - min(x)) / h))
446
+ elif method == "rice":
424
447
  k = int(np.ceil(2 * np.cbrt(n)))
425
- elif method=='sturges':
426
- k = int(np.ceil(np.log2(n))+1)
427
- elif method=='doane':
448
+ elif method == "sturges":
449
+ k = int(np.ceil(np.log2(n)) + 1)
450
+ elif method == "doane":
428
451
  g = stats.skew(x)
429
- sigma = np.sqrt(6*(n-2)/((n+1)*(n+3)))
430
- k = int(1 + np.log2(n)+np.log2(1+np.abs(g)/sigma))
431
- elif method=='quantile':
432
- k = np.quantile(x,np.arange(0,1.01,0.025))
452
+ sigma = np.sqrt(6 * (n - 2) / ((n + 1) * (n + 3)))
453
+ k = int(1 + np.log2(n) + np.log2(1 + np.abs(g) / sigma))
454
+ elif method == "quantile":
455
+ k = np.quantile(x, np.arange(0, 1.01, 0.025))
433
456
  else:
434
457
  k = int(np.ceil(np.sqrt(n)))
435
458
  return k
436
459
 
437
460
 
438
461
  if __name__ == "__main__":
439
- file='..\\example\\Calculate_Roughness\\sapphire.nid'
440
-
462
+ file = "..\\example\\Calculate_Roughness\\sapphire.nid"
463
+ from colormaps import colors
464
+
465
+ cmap = colors("Sky").cmap
466
+
441
467
  # fig = plt.figure(dpi=300)
442
468
  afm = process(file)
443
469
 
444
- chan = 'Z-Axis'
445
-
470
+ chan = "Z-Axis"
471
+
446
472
  afm.flatten(channel=chan)
447
473
 
448
474
  rms_roughness = afm.stats().Forward[chan].Sq
449
475
  z = afm.data.Image.Forward[chan]
450
- mask = z > 2*rms_roughness
476
+ mask = z > 2 * rms_roughness
451
477
  afm.plane_level()
452
478
  afm.flatten(channel=chan, mask=mask)
453
- fig = afm.image(cbar='Z [nm]', title='', channel=chan, scalebar={'color':'w','length':0.25}, scale=1e9, theme='dark')
479
+ fig = afm.image(
480
+ cbar="Z [nm]",
481
+ title="",
482
+ channel=chan,
483
+ scalebar={"color": "w", "length": 0.25},
484
+ scale=1e9,
485
+ theme="dark",
486
+ cmap=cmap,
487
+ )
454
488
  plt.show()
455
-
456
- afm.hist(scale=1e12,xlabel='Height [pm]',theme='dark')
457
-
458
489
 
459
- print('Average Roughness: %3.2f pm' % (rms_roughness*1e12))
490
+ afm.hist(scale=1e12, xlabel="Height [pm]", theme="dark", cmap=cmap)
491
+
492
+ print("Average Roughness: %3.2f pm" % (rms_roughness * 1e12))
@@ -224,7 +224,8 @@ class nid_read:
224
224
  key = "DataSet\\Calibration\\Scanhead"
225
225
  if key in header:
226
226
  sens = from_array(key, "In5")
227
- cantilever["Sensitivity"] = {"Value": sens / 10, "Unit": "m"}
227
+ max_defl = from_array(key, "In0")
228
+ cantilever["Sensitivity"] = {"Value": sens / max_defl, "Unit": "m"}
228
229
 
229
230
  mapTable = {}
230
231
  key = "DataSet\\SpecInfos"
@@ -641,10 +642,6 @@ def checkFileType(filename, cla):
641
642
 
642
643
  if __name__ == "__main__":
643
644
  # filename='..//example//Plane_Removal//circles.nid'
644
- filename = "image_test.nhf"
645
- afm = read(filename, verbose=True)
646
- print(afm.param)
647
-
648
- filename = "new.nhf"
645
+ filename = "atomic_lattice.nid"
649
646
  afm = read(filename, verbose=True)
650
647
  print(afm.param)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: NSFopen
3
- Version: 2.2.2
3
+ Version: 2.2.4
4
4
  Summary: Python module to open Nanosurf NID and NHF files
5
5
  Author: Nanosurf AG
6
6
  Author-email: scripting@nanosurf.com
@@ -50,6 +50,7 @@ NSFopen/example/Spring_Constant/thermal.png
50
50
  NSFopen/example/Spring_Constant/__pycache__/Spring_Constant.cpython-310.pyc
51
51
  NSFopen/example/Spring_Constant/__pycache__/__init__.cpython-310.pyc
52
52
  NSFopen/process/__init__.py
53
+ NSFopen/process/colormaps.py
53
54
  NSFopen/process/process.py
54
55
  NSFopen/read/__init__.py
55
56
  NSFopen/read/read.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: NSFopen
3
- Version: 2.2.2
3
+ Version: 2.2.4
4
4
  Summary: Python module to open Nanosurf NID and NHF files
5
5
  Author: Nanosurf AG
6
6
  Author-email: scripting@nanosurf.com
@@ -1 +0,0 @@
1
- __version__ = "2.2.2"
File without changes
File without changes
File without changes
File without changes
File without changes