structured-optics 0.4__tar.gz → 0.4.1__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.
- {structured_optics-0.4 → structured_optics-0.4.1}/PKG-INFO +3 -3
- {structured_optics-0.4 → structured_optics-0.4.1}/README.md +1 -1
- {structured_optics-0.4 → structured_optics-0.4.1}/pyproject.toml +2 -2
- structured_optics-0.4.1/structured_optics/slmdisplay.py +160 -0
- {structured_optics-0.4 → structured_optics-0.4.1}/structured_optics/struct_opt.py +91 -24
- {structured_optics-0.4 → structured_optics-0.4.1}/structured_optics/utils.py +27 -0
- {structured_optics-0.4 → structured_optics-0.4.1}/structured_optics.egg-info/PKG-INFO +3 -3
- {structured_optics-0.4 → structured_optics-0.4.1}/structured_optics.egg-info/requires.txt +1 -1
- structured_optics-0.4/structured_optics/slmdisplay.py +0 -85
- {structured_optics-0.4 → structured_optics-0.4.1}/LICENSE +0 -0
- {structured_optics-0.4 → structured_optics-0.4.1}/setup.cfg +0 -0
- {structured_optics-0.4 → structured_optics-0.4.1}/setup.py +0 -0
- {structured_optics-0.4 → structured_optics-0.4.1}/structured_optics/__init__.py +0 -0
- {structured_optics-0.4 → structured_optics-0.4.1}/structured_optics/algebra_utils.py +0 -0
- {structured_optics-0.4 → structured_optics-0.4.1}/structured_optics/hologram.py +0 -0
- {structured_optics-0.4 → structured_optics-0.4.1}/structured_optics/modes.py +0 -0
- {structured_optics-0.4 → structured_optics-0.4.1}/structured_optics/polarization.py +0 -0
- {structured_optics-0.4 → structured_optics-0.4.1}/structured_optics/prop_methods.py +0 -0
- {structured_optics-0.4 → structured_optics-0.4.1}/structured_optics.egg-info/SOURCES.txt +0 -0
- {structured_optics-0.4 → structured_optics-0.4.1}/structured_optics.egg-info/dependency_links.txt +0 -0
- {structured_optics-0.4 → structured_optics-0.4.1}/structured_optics.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: structured_optics
|
|
3
|
-
Version: 0.4
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Package for Structured Light simulation
|
|
5
5
|
Author: Altilano C. Barbosa
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -9,7 +9,7 @@ Description-Content-Type: text/markdown
|
|
|
9
9
|
License-File: LICENSE
|
|
10
10
|
Requires-Dist: numpy
|
|
11
11
|
Requires-Dist: scipy
|
|
12
|
-
Requires-Dist:
|
|
12
|
+
Requires-Dist: screeninfo
|
|
13
13
|
Dynamic: license-file
|
|
14
14
|
|
|
15
15
|
# structured-optics
|
|
@@ -75,7 +75,7 @@ Soon...
|
|
|
75
75
|
- Python 3.9+
|
|
76
76
|
- numpy
|
|
77
77
|
- scipy
|
|
78
|
-
-
|
|
78
|
+
- screeninfo
|
|
79
79
|
|
|
80
80
|
|
|
81
81
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "structured_optics"
|
|
7
|
-
version = "0.4"
|
|
7
|
+
version = "0.4.1"
|
|
8
8
|
description = "Package for Structured Light simulation"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "BSD-3-Clause"
|
|
@@ -16,5 +16,5 @@ authors = [
|
|
|
16
16
|
dependencies = [
|
|
17
17
|
"numpy",
|
|
18
18
|
"scipy",
|
|
19
|
-
"
|
|
19
|
+
"screeninfo"
|
|
20
20
|
]
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""import sys
|
|
2
|
+
import numpy as np
|
|
3
|
+
from PyQt6.QtWidgets import QApplication, QLabel
|
|
4
|
+
from PyQt6.QtGui import QImage, QPixmap
|
|
5
|
+
from PyQt6.QtCore import Qt
|
|
6
|
+
|
|
7
|
+
class My_SLM(QLabel):
|
|
8
|
+
def __init__(self, screen_index=0):
|
|
9
|
+
self.app = QApplication.instance() or QApplication(sys.argv)
|
|
10
|
+
super().__init__()
|
|
11
|
+
|
|
12
|
+
screens = self.app.screens()
|
|
13
|
+
if screen_index >= len(screens):
|
|
14
|
+
raise ValueError("Invalid screen index")
|
|
15
|
+
|
|
16
|
+
screen = screens[screen_index]
|
|
17
|
+
|
|
18
|
+
# Force native window creation (CRITICAL on Linux)
|
|
19
|
+
self.setAttribute(Qt.WidgetAttribute.WA_NativeWindow)
|
|
20
|
+
|
|
21
|
+
self.setWindowFlags(
|
|
22
|
+
Qt.WindowType.FramelessWindowHint
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
self.show() # must come before windowHandle()
|
|
26
|
+
|
|
27
|
+
self.windowHandle().setScreen(screen)
|
|
28
|
+
geo = screen.geometry()
|
|
29
|
+
|
|
30
|
+
# Store logical screen size
|
|
31
|
+
self.width = geo.width()
|
|
32
|
+
self.height = geo.height()
|
|
33
|
+
self.setGeometry(geo)
|
|
34
|
+
|
|
35
|
+
self.showFullScreen()
|
|
36
|
+
|
|
37
|
+
self._img_ref = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def update_image(self, img: np.ndarray):
|
|
41
|
+
|
|
42
|
+
#img:
|
|
43
|
+
# - (H, W) uint8 → grayscale
|
|
44
|
+
# - (H, W, 3) uint8 → RGB
|
|
45
|
+
|
|
46
|
+
if img.dtype != np.uint8:
|
|
47
|
+
raise TypeError("Image must be uint8")
|
|
48
|
+
|
|
49
|
+
self._img_ref = img # keep reference alive
|
|
50
|
+
|
|
51
|
+
if img.ndim == 2:
|
|
52
|
+
h, w = img.shape
|
|
53
|
+
stride = img.strides[0]
|
|
54
|
+
|
|
55
|
+
qimg = QImage(
|
|
56
|
+
img.data,
|
|
57
|
+
w,
|
|
58
|
+
h,
|
|
59
|
+
stride,
|
|
60
|
+
QImage.Format.Format_Grayscale8
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
elif img.ndim == 3 and img.shape[2] == 3:
|
|
64
|
+
h, w, _ = img.shape
|
|
65
|
+
stride = img.strides[0]
|
|
66
|
+
|
|
67
|
+
qimg = QImage(
|
|
68
|
+
img.data,
|
|
69
|
+
w,
|
|
70
|
+
h,
|
|
71
|
+
stride,
|
|
72
|
+
QImage.Format.Format_RGB888
|
|
73
|
+
)
|
|
74
|
+
else:
|
|
75
|
+
raise ValueError("Image must be (H,W) or (H,W,3)")
|
|
76
|
+
|
|
77
|
+
self.setPixmap(QPixmap.fromImage(qimg))
|
|
78
|
+
self.app.processEvents()
|
|
79
|
+
|
|
80
|
+
def close(self):
|
|
81
|
+
if not self.isVisible():
|
|
82
|
+
return
|
|
83
|
+
self.hide()
|
|
84
|
+
self.setParent(None) # detach from Qt ownership
|
|
85
|
+
self.deleteLater() """
|
|
86
|
+
|
|
87
|
+
import tkinter as tk
|
|
88
|
+
import numpy as np
|
|
89
|
+
from screeninfo import get_monitors
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class My_SLM:
|
|
93
|
+
"""
|
|
94
|
+
Fullscreen numpy-array display on a chosen physical monitor.
|
|
95
|
+
Uses plain tkinter (stdlib) - the image is shown as ordinary
|
|
96
|
+
CPU-composited window content, the same way PyQt6's QLabel/QPixmap
|
|
97
|
+
did. Unlike an OpenGL surface (pyglet/GLFW), this never becomes a
|
|
98
|
+
GPU flip-model surface, so it doesn't trigger the driver-level
|
|
99
|
+
scanout switch that causes the display to briefly flash/resync.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
def __init__(self, screen_index: int = 0):
|
|
103
|
+
monitors = get_monitors()
|
|
104
|
+
if screen_index >= len(monitors):
|
|
105
|
+
raise ValueError("Invalid screen index")
|
|
106
|
+
|
|
107
|
+
m = monitors[screen_index]
|
|
108
|
+
self.width, self.height = m.width, m.height
|
|
109
|
+
|
|
110
|
+
self.root = tk.Tk()
|
|
111
|
+
self.root.overrideredirect(True) # borderless
|
|
112
|
+
self.root.geometry(f"{self.width}x{self.height}+{m.x}+{m.y}")
|
|
113
|
+
self.root.attributes("-topmost", True)
|
|
114
|
+
self.root.configure(bg="black")
|
|
115
|
+
|
|
116
|
+
self.label = tk.Label(self.root, bd=0, highlightthickness=0, bg="black")
|
|
117
|
+
self.label.pack(fill="both", expand=True)
|
|
118
|
+
|
|
119
|
+
self.root.update_idletasks()
|
|
120
|
+
self.root.update()
|
|
121
|
+
|
|
122
|
+
self._photo_ref = None # keep the PhotoImage alive (Tk drops it otherwise)
|
|
123
|
+
self._img_ref = None
|
|
124
|
+
|
|
125
|
+
def update_image(self, img: np.ndarray):
|
|
126
|
+
"""
|
|
127
|
+
img:
|
|
128
|
+
- (H, W) uint8 -> grayscale
|
|
129
|
+
- (H, W, 3) uint8 -> RGB
|
|
130
|
+
"""
|
|
131
|
+
if img.dtype != np.uint8:
|
|
132
|
+
raise TypeError("Image must be uint8")
|
|
133
|
+
|
|
134
|
+
img = np.ascontiguousarray(img)
|
|
135
|
+
self._img_ref = img # keep a reference alive
|
|
136
|
+
|
|
137
|
+
if img.ndim == 2:
|
|
138
|
+
h, w = img.shape
|
|
139
|
+
header = f"P5\n{w} {h}\n255\n".encode()
|
|
140
|
+
elif img.ndim == 3 and img.shape[2] == 3:
|
|
141
|
+
h, w, _ = img.shape
|
|
142
|
+
header = f"P6\n{w} {h}\n255\n".encode()
|
|
143
|
+
else:
|
|
144
|
+
raise ValueError("Image must be (H,W) or (H,W,3)")
|
|
145
|
+
|
|
146
|
+
# Build a raw PGM/PPM blob in memory - Tk's built-in photo image
|
|
147
|
+
# reader parses this directly in C, so no PIL/Pillow dependency
|
|
148
|
+
# and no per-pixel Python loop.
|
|
149
|
+
data = header + img.tobytes()
|
|
150
|
+
photo = tk.PhotoImage(data=data)
|
|
151
|
+
|
|
152
|
+
self._photo_ref = photo
|
|
153
|
+
self.label.configure(image=photo)
|
|
154
|
+
|
|
155
|
+
self.root.update()
|
|
156
|
+
|
|
157
|
+
def close(self):
|
|
158
|
+
self.root.destroy()
|
|
159
|
+
|
|
160
|
+
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
1
|
import numpy as np
|
|
4
|
-
|
|
2
|
+
import copy
|
|
3
|
+
from scipy import fft
|
|
5
4
|
from contextlib import contextmanager
|
|
6
5
|
from structured_optics.prop_methods import *
|
|
7
|
-
import copy
|
|
8
6
|
from structured_optics.utils import *
|
|
9
7
|
from structured_optics.modes import *
|
|
10
8
|
from structured_optics.algebra_utils import *
|
|
@@ -13,6 +11,7 @@ from structured_optics.polarization import *
|
|
|
13
11
|
|
|
14
12
|
|
|
15
13
|
|
|
14
|
+
|
|
16
15
|
class Beam():
|
|
17
16
|
"""
|
|
18
17
|
Represents a monochromatic paraxial optical beam on a discretized transverse grid.
|
|
@@ -908,25 +907,73 @@ class Beam():
|
|
|
908
907
|
return p[0]
|
|
909
908
|
return p
|
|
910
909
|
|
|
911
|
-
|
|
910
|
+
|
|
911
|
+
def center_mass(self, pol_index=None) -> tuple:
|
|
912
|
+
"""
|
|
913
|
+
Compute the intensity-weighted center of mass of the field in physical x and y coordinates.
|
|
914
|
+
|
|
915
|
+
Parameters
|
|
916
|
+
----------
|
|
917
|
+
pol_index : int, optional
|
|
918
|
+
Polarization component to use; None uses total intensity.
|
|
919
|
+
|
|
920
|
+
Returns
|
|
921
|
+
-------
|
|
922
|
+
tuple (x_cm, y_cm)
|
|
923
|
+
center-of-mass coordinates in physical units. """
|
|
924
|
+
I = self.int_profile(pol_index)
|
|
925
|
+
|
|
926
|
+
x = np.asarray(self.x)
|
|
927
|
+
y = np.asarray(self.y)
|
|
928
|
+
if x.ndim == 2:
|
|
929
|
+
x = x[0, :] if x.shape[0] == 1 else x[0, :]
|
|
930
|
+
if y.ndim == 2:
|
|
931
|
+
y = y[:, 0] if y.shape[1] == 1 else y[:, 0]
|
|
932
|
+
# Total intensity
|
|
933
|
+
norm = np.sum(I)
|
|
934
|
+
|
|
935
|
+
if norm == 0:
|
|
936
|
+
raise ValueError("Cannot calculate center of mass of zero intensity.")
|
|
937
|
+
|
|
938
|
+
# Intensity marginalized along each direction
|
|
939
|
+
Ix = np.sum(I, axis=0)
|
|
940
|
+
Iy = np.sum(I, axis=1)
|
|
941
|
+
|
|
942
|
+
# Physical center of mass
|
|
943
|
+
x_cm = np.sum(x * Ix) / norm
|
|
944
|
+
y_cm = np.sum(y * Iy) / norm
|
|
945
|
+
|
|
946
|
+
return x_cm, y_cm
|
|
947
|
+
|
|
948
|
+
def std(self, pol_index=None):
|
|
912
949
|
"""
|
|
913
|
-
|
|
914
|
-
|
|
950
|
+
Calculate the intensity-weighted spatial standard deviation
|
|
951
|
+
along x and y.
|
|
915
952
|
|
|
916
|
-
Parameters
|
|
917
|
-
----------
|
|
918
|
-
pol_index : int, optional
|
|
919
|
-
Polarization component to use; None uses total intensity.
|
|
953
|
+
Parameters
|
|
954
|
+
----------
|
|
955
|
+
pol_index : int, optional
|
|
956
|
+
Polarization component to use; None uses total intensity.
|
|
920
957
|
|
|
921
958
|
Returns
|
|
922
959
|
-------
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
960
|
+
sigma_x, sigma_y : float
|
|
961
|
+
Intensity-weighted spatial standard deviations.
|
|
962
|
+
"""
|
|
963
|
+
I = self.int_profile(pol_index)
|
|
964
|
+
x = np.asarray(self.x)
|
|
965
|
+
y = np.asarray(self.y)
|
|
966
|
+
x = x[0, :] if x.ndim == 2 else x
|
|
967
|
+
y = y[:, 0] if y.ndim == 2 else y
|
|
968
|
+
norm = np.sum(I)
|
|
969
|
+
Ix = np.sum(I, axis=0)
|
|
970
|
+
Iy = np.sum(I, axis=1)
|
|
971
|
+
x_cm, y_cm = self.center_mass(pol_index)
|
|
972
|
+
var_x = np.sum(Ix * (x - x_cm)**2) / norm
|
|
973
|
+
var_y = np.sum(Iy * (y - y_cm)**2) / norm
|
|
974
|
+
return np.sqrt(var_x), np.sqrt(var_y)
|
|
975
|
+
|
|
976
|
+
def radial_std(self, pol_index=None) -> float:
|
|
930
977
|
"""
|
|
931
978
|
Calculate the intensity-weighted radial standard deviation of the field
|
|
932
979
|
about its center of mass.
|
|
@@ -941,12 +988,9 @@ class Beam():
|
|
|
941
988
|
float
|
|
942
989
|
Intensity-weighted radial standard deviation.
|
|
943
990
|
"""
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
y0 = self.y[int(round(c[0])), 0]
|
|
948
|
-
r2 = (self.x - x0)**2 + (self.y - y0)**2
|
|
949
|
-
return np.sqrt(np.average(r2, weights=I))
|
|
991
|
+
sx, sy = self.std(pol_index)
|
|
992
|
+
|
|
993
|
+
return np.sqrt(sx**2 + sy**2)
|
|
950
994
|
|
|
951
995
|
def section(self, ang_min:float, ang_max:float, pol_index:int=0) -> np.ndarray:
|
|
952
996
|
"""
|
|
@@ -1040,6 +1084,29 @@ class Beam():
|
|
|
1040
1084
|
A new, spatially cropped Beam.
|
|
1041
1085
|
"""
|
|
1042
1086
|
return get_crop(self, center, std, window)
|
|
1087
|
+
|
|
1088
|
+
def apply_zernike(self, n: int, m: int, strength: float = 1) -> object:
|
|
1089
|
+
"""
|
|
1090
|
+
Apply a zernike polynomial of indexes n,m where (n-m)%2 == 0 and m <=n. Action normalized by waist.
|
|
1091
|
+
Used to perform aberration correction.
|
|
1092
|
+
|
|
1093
|
+
Parameters
|
|
1094
|
+
----------
|
|
1095
|
+
n : int
|
|
1096
|
+
Zernike Polynomial radial degree.
|
|
1097
|
+
m : int
|
|
1098
|
+
Zernike Polynomial azimuthal degree.
|
|
1099
|
+
strength: float, optional
|
|
1100
|
+
Intensity of the aberration correction normalized by waist. Default equals one waist.
|
|
1101
|
+
|
|
1102
|
+
Returns
|
|
1103
|
+
-------
|
|
1104
|
+
Beam
|
|
1105
|
+
self, with zernike polynomial applyed to field phase.
|
|
1106
|
+
|
|
1107
|
+
"""
|
|
1108
|
+
self.field = apply_zernike(self.x, self.y, self.waist, self.field, n, m, strenght= strength)
|
|
1109
|
+
return self
|
|
1043
1110
|
|
|
1044
1111
|
|
|
1045
1112
|
|
|
@@ -3,6 +3,7 @@ from scipy import ndimage
|
|
|
3
3
|
from scipy.special import hermite, genlaguerre, jv, j0, j1, kv, jn_zeros
|
|
4
4
|
from scipy.optimize import brentq
|
|
5
5
|
from functools import wraps
|
|
6
|
+
import math
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
|
|
@@ -471,3 +472,29 @@ def inv_J1(A, a=None, n=10000):
|
|
|
471
472
|
return np.interp(a * A, y, x)
|
|
472
473
|
|
|
473
474
|
|
|
475
|
+
|
|
476
|
+
#utils for aberration correction
|
|
477
|
+
|
|
478
|
+
def radial_poly(n, m, r):
|
|
479
|
+
s = 0
|
|
480
|
+
if (n - m) % 2 == 0 and m <= n:
|
|
481
|
+
for k in range((n - m) // 2 + 1):
|
|
482
|
+
s += (-1)**k * math.factorial(n-k) / (math.factorial(k) * math.factorial((n + m) // 2 - k)
|
|
483
|
+
* math.factorial((n + m) // 2 - k))* r**(n - 2*k)
|
|
484
|
+
elif (n - m) % 2 == 1 and m <= n:
|
|
485
|
+
raise ValueError("Warning: (n - m) is odd, Zernike Polynomial is zero.")
|
|
486
|
+
else:
|
|
487
|
+
raise ValueError("Invalid n and m values for Zernike polynomials.")
|
|
488
|
+
return s
|
|
489
|
+
|
|
490
|
+
def zernike(n, m, r, phi):
|
|
491
|
+
if m >= 0:
|
|
492
|
+
return radial_poly(n, m, r) * np.cos(m*phi)
|
|
493
|
+
else:
|
|
494
|
+
return radial_poly(n, -m, r) * np.sin(-m*phi)
|
|
495
|
+
|
|
496
|
+
def apply_zernike(x, y, waist, desired, n, m, strength):
|
|
497
|
+
r = np.sqrt(x**2 + y**2)/waist
|
|
498
|
+
phi = np.arctan2(y, x)
|
|
499
|
+
phase = strength * zernike(n, m, r, phi)
|
|
500
|
+
return desired*np.exp(1j*phase)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: structured_optics
|
|
3
|
-
Version: 0.4
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Package for Structured Light simulation
|
|
5
5
|
Author: Altilano C. Barbosa
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -9,7 +9,7 @@ Description-Content-Type: text/markdown
|
|
|
9
9
|
License-File: LICENSE
|
|
10
10
|
Requires-Dist: numpy
|
|
11
11
|
Requires-Dist: scipy
|
|
12
|
-
Requires-Dist:
|
|
12
|
+
Requires-Dist: screeninfo
|
|
13
13
|
Dynamic: license-file
|
|
14
14
|
|
|
15
15
|
# structured-optics
|
|
@@ -75,7 +75,7 @@ Soon...
|
|
|
75
75
|
- Python 3.9+
|
|
76
76
|
- numpy
|
|
77
77
|
- scipy
|
|
78
|
-
-
|
|
78
|
+
- screeninfo
|
|
79
79
|
|
|
80
80
|
|
|
81
81
|
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
import numpy as np
|
|
3
|
-
from PyQt6.QtWidgets import QApplication, QLabel
|
|
4
|
-
from PyQt6.QtGui import QImage, QPixmap
|
|
5
|
-
from PyQt6.QtCore import Qt
|
|
6
|
-
|
|
7
|
-
class My_SLM(QLabel):
|
|
8
|
-
def __init__(self, screen_index=0):
|
|
9
|
-
self.app = QApplication.instance() or QApplication(sys.argv)
|
|
10
|
-
super().__init__()
|
|
11
|
-
|
|
12
|
-
screens = self.app.screens()
|
|
13
|
-
if screen_index >= len(screens):
|
|
14
|
-
raise ValueError("Invalid screen index")
|
|
15
|
-
|
|
16
|
-
screen = screens[screen_index]
|
|
17
|
-
|
|
18
|
-
# Force native window creation (CRITICAL on Linux)
|
|
19
|
-
self.setAttribute(Qt.WidgetAttribute.WA_NativeWindow)
|
|
20
|
-
|
|
21
|
-
self.setWindowFlags(
|
|
22
|
-
Qt.WindowType.FramelessWindowHint
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
self.show() # must come before windowHandle()
|
|
26
|
-
|
|
27
|
-
self.windowHandle().setScreen(screen)
|
|
28
|
-
geo = screen.geometry()
|
|
29
|
-
|
|
30
|
-
# Store logical screen size
|
|
31
|
-
self.width = geo.width()
|
|
32
|
-
self.height = geo.height()
|
|
33
|
-
self.setGeometry(geo)
|
|
34
|
-
|
|
35
|
-
self.showFullScreen()
|
|
36
|
-
|
|
37
|
-
self._img_ref = None
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def update_image(self, img: np.ndarray):
|
|
41
|
-
"""
|
|
42
|
-
img:
|
|
43
|
-
- (H, W) uint8 → grayscale
|
|
44
|
-
- (H, W, 3) uint8 → RGB
|
|
45
|
-
"""
|
|
46
|
-
if img.dtype != np.uint8:
|
|
47
|
-
raise TypeError("Image must be uint8")
|
|
48
|
-
|
|
49
|
-
self._img_ref = img # keep reference alive
|
|
50
|
-
|
|
51
|
-
if img.ndim == 2:
|
|
52
|
-
h, w = img.shape
|
|
53
|
-
stride = img.strides[0]
|
|
54
|
-
|
|
55
|
-
qimg = QImage(
|
|
56
|
-
img.data,
|
|
57
|
-
w,
|
|
58
|
-
h,
|
|
59
|
-
stride,
|
|
60
|
-
QImage.Format.Format_Grayscale8
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
elif img.ndim == 3 and img.shape[2] == 3:
|
|
64
|
-
h, w, _ = img.shape
|
|
65
|
-
stride = img.strides[0]
|
|
66
|
-
|
|
67
|
-
qimg = QImage(
|
|
68
|
-
img.data,
|
|
69
|
-
w,
|
|
70
|
-
h,
|
|
71
|
-
stride,
|
|
72
|
-
QImage.Format.Format_RGB888
|
|
73
|
-
)
|
|
74
|
-
else:
|
|
75
|
-
raise ValueError("Image must be (H,W) or (H,W,3)")
|
|
76
|
-
|
|
77
|
-
self.setPixmap(QPixmap.fromImage(qimg))
|
|
78
|
-
self.app.processEvents()
|
|
79
|
-
|
|
80
|
-
def close(self):
|
|
81
|
-
if not self.isVisible():
|
|
82
|
-
return
|
|
83
|
-
self.hide()
|
|
84
|
-
self.setParent(None) # detach from Qt ownership
|
|
85
|
-
self.deleteLater()
|
|
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
|
{structured_optics-0.4 → structured_optics-0.4.1}/structured_optics.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|