napari-dpr 0.1.0__cp311-cp311-win_amd64.whl
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.
- napari_dpr/__init__.py +9 -0
- napari_dpr/_widget.py +88 -0
- napari_dpr/dpr.py +131 -0
- napari_dpr/dpr_core.cp310-win_amd64.pyd +0 -0
- napari_dpr/dpr_core.cp311-win_amd64.pyd +0 -0
- napari_dpr/dpr_core.cp312-win_amd64.pyd +0 -0
- napari_dpr/dpr_core.cp313-win_amd64.pyd +0 -0
- napari_dpr/dpr_core.cp39-win_amd64.pyd +0 -0
- napari_dpr/dpr_core.cpp +15410 -0
- napari_dpr/dpr_core.pyx +103 -0
- napari_dpr/example.py +46 -0
- napari_dpr/napari.yaml +10 -0
- napari_dpr/run.dpr.py +68 -0
- napari_dpr-0.1.0.dist-info/METADATA +149 -0
- napari_dpr-0.1.0.dist-info/RECORD +19 -0
- napari_dpr-0.1.0.dist-info/WHEEL +5 -0
- napari_dpr-0.1.0.dist-info/entry_points.txt +2 -0
- napari_dpr-0.1.0.dist-info/licenses/LICENSE +21 -0
- napari_dpr-0.1.0.dist-info/top_level.txt +1 -0
napari_dpr/__init__.py
ADDED
napari_dpr/_widget.py
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
"""
|
2
|
+
Napari plugin widget for DPR resolution enhancement.
|
3
|
+
"""
|
4
|
+
from typing import TYPE_CHECKING
|
5
|
+
import numpy as np
|
6
|
+
from magicgui import magic_factory
|
7
|
+
from napari_dpr.dpr_core import apply_dpr
|
8
|
+
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
import napari
|
11
|
+
|
12
|
+
@magic_factory(
|
13
|
+
psf={"widget_type": "FloatSpinBox", "min": 1.0, "max": 10.0, "step": 0.1, "value": 4.0},
|
14
|
+
gain={"widget_type": "FloatSpinBox", "min": 0.1, "max": 10.0, "step": 0.1, "value": 2.0},
|
15
|
+
background={"widget_type": "FloatSpinBox", "min": 0.0, "max": 50.0, "step": 1.0, "value": 10.0},
|
16
|
+
call_button="Enhance Resolution"
|
17
|
+
)
|
18
|
+
def enhance_image(
|
19
|
+
viewer: "napari.viewer.Viewer",
|
20
|
+
image_layer: "napari.layers.Image",
|
21
|
+
psf: float = 4.0,
|
22
|
+
gain: float = 2.0,
|
23
|
+
background: float = 10.0,
|
24
|
+
) -> None:
|
25
|
+
"""
|
26
|
+
Enhance image resolution using DPR.
|
27
|
+
|
28
|
+
Parameters
|
29
|
+
----------
|
30
|
+
viewer : napari.viewer.Viewer
|
31
|
+
Napari viewer instance
|
32
|
+
image_layer : napari.layers.Image
|
33
|
+
Input image layer to enhance
|
34
|
+
psf : float
|
35
|
+
Point spread function size parameter
|
36
|
+
gain : float
|
37
|
+
Gain parameter for enhancement
|
38
|
+
background : float
|
39
|
+
Background subtraction value
|
40
|
+
"""
|
41
|
+
if image_layer is None:
|
42
|
+
raise ValueError("Please select an image layer")
|
43
|
+
|
44
|
+
# Get the image data
|
45
|
+
image_data = image_layer.data
|
46
|
+
|
47
|
+
# Make sure image has the right dimensions and type
|
48
|
+
# DPR expects a 3D array (HEIGHT, WIDTH, TIME/CHANNELS)
|
49
|
+
if image_data.ndim == 2:
|
50
|
+
# Convert 2D image to 3D with one time point
|
51
|
+
image_data = image_data[:, :, np.newaxis]
|
52
|
+
elif image_data.ndim == 3 and image_data.shape[0] < image_data.shape[1]:
|
53
|
+
# If first dimension is smallest, it's probably [TIME, HEIGHT, WIDTH]
|
54
|
+
# We need to transpose to [HEIGHT, WIDTH, TIME]
|
55
|
+
image_data = image_data.transpose([1, 2, 0])
|
56
|
+
elif image_data.ndim > 3:
|
57
|
+
# If 4D or more, take the first 3 dimensions
|
58
|
+
image_data = image_data[:, :, :, 0]
|
59
|
+
|
60
|
+
# Ensure data is float64
|
61
|
+
if image_data.dtype != np.float64:
|
62
|
+
image_data = image_data.astype(np.float64)
|
63
|
+
|
64
|
+
# Apply DPR
|
65
|
+
try:
|
66
|
+
dpr_out, magnified = apply_dpr(image_data, psf=psf, gain=gain, background=background)
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
# Add the magnified original for comparison
|
71
|
+
viewer.add_image(
|
72
|
+
magnified.sum(axis=2), # Sum over the time/channel dimension
|
73
|
+
name=f"{image_layer.name}_magnified",
|
74
|
+
colormap=image_layer.colormap.name,
|
75
|
+
)
|
76
|
+
|
77
|
+
# Add the enhanced image to the viewer
|
78
|
+
viewer.add_image(
|
79
|
+
dpr_out,
|
80
|
+
name=f"{image_layer.name}_DPR_enhanced",
|
81
|
+
colormap=image_layer.colormap.name,
|
82
|
+
)
|
83
|
+
|
84
|
+
except Exception as e:
|
85
|
+
import traceback
|
86
|
+
print(f"Error applying DPR: {e}")
|
87
|
+
print(traceback.format_exc())
|
88
|
+
raise
|
napari_dpr/dpr.py
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
import os, sys, time, numpy as np, scipy.ndimage as ndi
|
2
|
+
from scipy.interpolate import RectBivariateSpline
|
3
|
+
import tifffile as tiff
|
4
|
+
from PIL import Image
|
5
|
+
import matplotlib.pyplot as plt
|
6
|
+
|
7
|
+
## original DPR as shown by https://github.com/biomicroscopy/DPR-Resolution_enhancement_with_deblurring_by_pixel_reassignment
|
8
|
+
|
9
|
+
def dpr_set_parameters(psf, **k): return {'gain': k.get('gain',1), 'background': k.get('background',int(np.ceil(17*psf))), 'temporal': k.get('temporal',None)}
|
10
|
+
|
11
|
+
def dpr_update_single(i, psf, opt):
|
12
|
+
g, r = opt['gain'], int(np.ceil(opt['background']))
|
13
|
+
psf /= 1.6651
|
14
|
+
h, w = i.shape
|
15
|
+
x0, y0 = np.linspace(-.5,.5,w), np.linspace(-.5,.5,h)
|
16
|
+
x, y = np.linspace(-.5,.5,round(5*w/psf)), np.linspace(-.5,.5,round(5*h/psf))
|
17
|
+
sx, sy = np.array([[1,0,-1],[2,0,-2],[1,0,-1]]), np.array([[1,2,1],[0,0,0],[-1,-2,-1]])
|
18
|
+
i = i - i.min()
|
19
|
+
localmin = np.zeros_like(i)
|
20
|
+
i_localmin = np.zeros_like(i)
|
21
|
+
for u in range(h):
|
22
|
+
for v in range(w):
|
23
|
+
sub = i[max(0,u-r):min(h,u+r+1), max(0,v-r):min(w,v+r+1)]
|
24
|
+
localmin[u,v] = sub.min()
|
25
|
+
i_localmin[u,v] = i[u,v] - localmin[u,v]
|
26
|
+
m = RectBivariateSpline(y0,x0,i_localmin)(y,x)
|
27
|
+
m[m<0] = 0
|
28
|
+
m = np.pad(m,10)
|
29
|
+
mag = RectBivariateSpline(y0,x0,i)(y,x)
|
30
|
+
mag[mag<0] = 0
|
31
|
+
mag = np.pad(mag,10)
|
32
|
+
hn, wn = mag.shape
|
33
|
+
norm = m / (ndi.gaussian_filter(m,10)+1e-5)
|
34
|
+
gx = ndi.convolve(norm, sy, mode='reflect') / (norm + 1e-5)
|
35
|
+
gy = ndi.convolve(norm, sx, mode='reflect') / (norm + 1e-5)
|
36
|
+
d = 0.5*g+1
|
37
|
+
dx, dy = d*gx, d*gy
|
38
|
+
dx[np.abs(dx)>10] = 0
|
39
|
+
dy[np.abs(dy)>10] = 0
|
40
|
+
out = np.zeros((hn, wn))
|
41
|
+
for nx in range(10, hn-10):
|
42
|
+
for ny in range(10, wn-10):
|
43
|
+
wx, wy = dx[nx,ny], dy[nx,ny]
|
44
|
+
fx, fy = int(wx), int(wy)
|
45
|
+
sx, sy = int(np.sign(wx)), int(np.sign(wy))
|
46
|
+
w1 = (1-abs(wx-fx))*(1-abs(wy-fy))
|
47
|
+
w2 = (1-abs(wx-fx))*abs(wy-fy)
|
48
|
+
w3 = abs(wx-fx)*(1-abs(wy-fy))
|
49
|
+
w4 = abs(wx-fx)*abs(wy-fy)
|
50
|
+
c1 = [fx, fy]
|
51
|
+
c2 = [fx, fy+sy]
|
52
|
+
c3 = [fx+sx, fy]
|
53
|
+
c4 = [fx+sx, fy+sy]
|
54
|
+
val = mag[nx,ny]
|
55
|
+
out[nx+c1[0], ny+c1[1]] += w1*val
|
56
|
+
out[nx+c2[0], ny+c2[1]] += w2*val
|
57
|
+
out[nx+c3[0], ny+c3[1]] += w3*val
|
58
|
+
out[nx+c4[0], ny+c4[1]] += w4*val
|
59
|
+
return out[10:-10,10:-10], mag[10:-10,10:-10], g, r
|
60
|
+
|
61
|
+
def dpr_stack(s, psf, o):
|
62
|
+
f = s.shape[2]
|
63
|
+
shp = dpr_update_single(s[:,:,0],psf,o)[1].shape
|
64
|
+
out = np.zeros((*shp,f)); mag = np.zeros((*shp,f))
|
65
|
+
for i in range(f):
|
66
|
+
sys.stdout.write(f"\rProcessing {i+1}/{f}"); sys.stdout.flush()
|
67
|
+
o1,o2,_,_ = dpr_update_single(s[:,:,i],psf,o)
|
68
|
+
out[:,:,i], mag[:,:,i] = o1, o2
|
69
|
+
t = o.get('temporal','')
|
70
|
+
if t == 'mean': out = np.mean(out,axis=2)
|
71
|
+
elif t == 'var': out = np.var(out,axis=2)
|
72
|
+
return out, mag
|
73
|
+
|
74
|
+
def load_image_stack(p,n,t):
|
75
|
+
path = os.path.join(p,f'{n}.{t}')
|
76
|
+
if t.lower() == 'tif':
|
77
|
+
d = tiff.imread(path)
|
78
|
+
return np.transpose(d,(1,2,0)) if d.ndim==3 else d
|
79
|
+
return np.array(Image.open(path))
|
80
|
+
|
81
|
+
def save_image(im, p, n, t):
|
82
|
+
os.makedirs(p, exist_ok=True)
|
83
|
+
f = os.path.join(p, f'{n}.{t}')
|
84
|
+
if t.lower()=='tif': tiff.imwrite(f, im)
|
85
|
+
else:
|
86
|
+
if im.dtype != np.uint8:
|
87
|
+
im = ((im-im.min())/(im.max()-im.min())*255).astype(np.uint8)
|
88
|
+
Image.fromarray(im).save(f)
|
89
|
+
|
90
|
+
def process_image(p,n,t,psf,o):
|
91
|
+
s = load_image_stack(p,n,t)
|
92
|
+
out, mag = dpr_stack(s, psf, o)
|
93
|
+
save_image(out, os.path.join(p,'DPR_results'), f'{n}_result', t)
|
94
|
+
return s, out, mag
|
95
|
+
|
96
|
+
def display_images(i,m,o):
|
97
|
+
plt.figure(figsize=(12,4))
|
98
|
+
plt.subplot(1,3,1); plt.imshow(i[...,0] if i.ndim==3 else i, cmap='gray'); plt.title('Initial')
|
99
|
+
plt.subplot(1,3,2); plt.imshow(np.mean(m,axis=2) if m.ndim==3 else m, cmap='gray'); plt.title('Magnified')
|
100
|
+
plt.subplot(1,3,3); plt.imshow(o, cmap='gray'); plt.title('DPR')
|
101
|
+
plt.tight_layout(); plt.show()
|
102
|
+
|
103
|
+
def main():
|
104
|
+
p = r'test_data'
|
105
|
+
f = input("File name [test_image.tif]: ") or "test_image.tif"
|
106
|
+
n,t = f.rsplit('.',1)
|
107
|
+
mode = input("Use default params? y/n/e [y]: ").lower() or 'y'
|
108
|
+
if mode == 'e':
|
109
|
+
print("PSF: blur radius\nGain: enhancement\nBackground: subtraction\nTemporal: mean or var")
|
110
|
+
mode = input("Use default now? y/n [y]: ").lower() or 'y'
|
111
|
+
if mode == 'y': psf,g,bg,tmp = 4,2,10,'mean'
|
112
|
+
else:
|
113
|
+
psf = float(input("PSF [4]: ") or 4)
|
114
|
+
g = float(input("Gain [2]: ") or 2)
|
115
|
+
bg = float(input("Background [10]: ") or 10)
|
116
|
+
tmp = input("Temporal [mean]: ") or 'mean'
|
117
|
+
o = dpr_set_parameters(psf, gain=g, background=bg, temporal=tmp)
|
118
|
+
start = time.time()
|
119
|
+
res = process_image(p,n,t,psf,o)
|
120
|
+
if res:
|
121
|
+
img,dpr,mag = res
|
122
|
+
print(f"\nTime: {time.time()-start:.2f}s")
|
123
|
+
display_images(img,mag,dpr)
|
124
|
+
else: print("Failed.")
|
125
|
+
|
126
|
+
def apply_dpr(im, psf=4, gain=2, background=10, temporal='mean'):
|
127
|
+
if im.ndim == 2: im = im[:,:,np.newaxis]
|
128
|
+
o = dpr_set_parameters(psf, gain=gain, background=background, temporal=temporal)
|
129
|
+
return dpr_stack(im, psf, o)
|
130
|
+
|
131
|
+
if __name__ == '__main__': main()
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|