Updated to version 2.0: changed display backend to moderngl_window.
This commit is contained in:
@ -4,7 +4,7 @@ Display any array, webcam, or video file.
|
||||
display is a function that displays these in their own windows.
|
||||
"""
|
||||
|
||||
__version__ = "1.3.1"
|
||||
__version__ = "2.0.0"
|
||||
|
||||
from .window.subscriber_windows import display, breakpoint_display, read_updates, publish_updates
|
||||
from . import effects
|
||||
|
@ -18,10 +18,12 @@ try:
|
||||
|
||||
using_pyv4l2cam = True
|
||||
except ImportError:
|
||||
warnings.warn("Could not import PyV4L2Cam on linux. Camera capture will be slow.")
|
||||
warnings.warn(
|
||||
"To install, run: pip install git+https://github.com/simleek/PyV4L2Cam.git"
|
||||
)
|
||||
pass
|
||||
# while this is still good for raspberry pi, OpenCV tends to be faster for normal computers.
|
||||
#warnings.warn("Could not import PyV4L2Cam on linux. Camera capture will be slow.")
|
||||
#warnings.warn(
|
||||
# "To install, run: pip install git+https://github.com/simleek/PyV4L2Cam.git"
|
||||
#)
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
369
displayarray/window/mglwindow.py
Normal file
369
displayarray/window/mglwindow.py
Normal file
File diff suppressed because it is too large
Load Diff
109
displayarray/window/pyr_quads.frag
Normal file
109
displayarray/window/pyr_quads.frag
Normal file
@ -0,0 +1,109 @@
|
||||
#version 430
|
||||
|
||||
struct TexLevel {
|
||||
int startIdx;
|
||||
int width;
|
||||
int height;
|
||||
vec4 rect; // 4 float representing position on triangle
|
||||
};
|
||||
|
||||
layout(std430, binding = 0) buffer InputBuffer {
|
||||
float inputImage[];
|
||||
};
|
||||
|
||||
layout(std430, binding = 1) buffer TexData {
|
||||
int channels;
|
||||
int levels;
|
||||
TexLevel texLevels[];
|
||||
};
|
||||
|
||||
layout(std430, binding=2) buffer UserInput {
|
||||
vec2 iMouse;
|
||||
};
|
||||
|
||||
layout(std430, binding=3) buffer UserOutput {
|
||||
int hit_level;
|
||||
vec2 hit_pos;
|
||||
};
|
||||
|
||||
layout(origin_upper_left, pixel_center_integer) in vec4 gl_FragCoord;
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
float bilinearInterpolation(float x, float y, float bottomLeft, float bottomRight, float topLeft, float topRight) {
|
||||
float left = mix(topLeft, bottomLeft, y);
|
||||
float right = mix(topRight, bottomRight, y);
|
||||
return mix(left, right, x);
|
||||
}
|
||||
|
||||
void main() {
|
||||
int our_level = -1;
|
||||
float y_current = -1;
|
||||
float x_current = -1;
|
||||
|
||||
for(int i=0;i<levels;i++){
|
||||
if(gl_FragCoord.x>=texLevels[i].rect.x &&
|
||||
gl_FragCoord.y>=texLevels[i].rect.y &&
|
||||
gl_FragCoord.x<texLevels[i].rect.z &&
|
||||
gl_FragCoord.y<texLevels[i].rect.w
|
||||
){
|
||||
our_level = i;
|
||||
//don't break. All shader instances should get same execution, and this puts later textures on top.
|
||||
}
|
||||
}
|
||||
|
||||
if(our_level!=-1) {
|
||||
|
||||
int levelWidth = texLevels[our_level].width;
|
||||
int levelHeight = texLevels[our_level].height;
|
||||
|
||||
y_current = int(levelHeight * (gl_FragCoord.y - texLevels[our_level].rect.y) / (texLevels[our_level].rect.w - texLevels[our_level].rect.y));
|
||||
x_current = int(levelWidth * (gl_FragCoord.x - texLevels[our_level].rect.x) / (texLevels[our_level].rect.z - texLevels[our_level].rect.x));
|
||||
|
||||
int topLeftIdx = texLevels[our_level].startIdx + int(floor(x_current) * texLevels[our_level].height * channels + floor(y_current) * channels);
|
||||
int topRightIdx = topLeftIdx + texLevels[our_level].height * channels;
|
||||
int bottomLeftIdx = topLeftIdx + channels;
|
||||
int bottomRightIdx = topRightIdx + channels;
|
||||
|
||||
//leave this for visual debugging
|
||||
out_color = vec4(float(y_current)/float(levelHeight), float(x_current)/float(levelWidth), 0.0, 1.0);
|
||||
out_color.x = bilinearInterpolation(
|
||||
fract(x_current),
|
||||
fract(y_current),
|
||||
inputImage[bottomLeftIdx],
|
||||
inputImage[bottomRightIdx],
|
||||
inputImage[topLeftIdx],
|
||||
inputImage[topRightIdx]
|
||||
);
|
||||
if (channels > 1) {
|
||||
out_color.y = bilinearInterpolation(
|
||||
fract(x_current),
|
||||
fract(y_current),
|
||||
inputImage[bottomLeftIdx+1],
|
||||
inputImage[bottomRightIdx+1],
|
||||
inputImage[topLeftIdx+1],
|
||||
inputImage[topRightIdx+1]
|
||||
);
|
||||
}
|
||||
if(channels>2){
|
||||
out_color.z = bilinearInterpolation(
|
||||
fract(x_current),
|
||||
fract(y_current),
|
||||
inputImage[bottomLeftIdx+2],
|
||||
inputImage[bottomRightIdx+2],
|
||||
inputImage[topLeftIdx+2],
|
||||
inputImage[topRightIdx+2]
|
||||
);
|
||||
}
|
||||
// currently only supporting 3 channels at most.
|
||||
}else{
|
||||
// nice white background. ( ∩´ ᐜ `∩)
|
||||
out_color = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
}
|
||||
|
||||
if(distance(iMouse, gl_FragCoord.xy)==0){
|
||||
hit_level = our_level;
|
||||
//hit_pos = vec2(x_current, y_current);
|
||||
hit_pos = iMouse;
|
||||
}
|
||||
|
||||
}
|
@ -35,6 +35,7 @@ try:
|
||||
except:
|
||||
warnings.warn("Could not import ZMQ and tensorcom. Cannot send messages between programs.")
|
||||
|
||||
from . import mglwindow
|
||||
|
||||
class SubscriberWindows(object):
|
||||
"""Windows that subscribe to updates to cameras, videos, and arrays."""
|
||||
@ -61,6 +62,7 @@ class SubscriberWindows(object):
|
||||
self.ctx = None
|
||||
self.sock_list: List[zmq.Socket] = []
|
||||
self.top_list: List[bytes] = []
|
||||
self.displayer = mglwindow.MglWindow()
|
||||
|
||||
if callbacks is None:
|
||||
callbacks = []
|
||||
@ -75,10 +77,10 @@ class SubscriberWindows(object):
|
||||
|
||||
def __bool__(self):
|
||||
self.update()
|
||||
return not self.exited
|
||||
return not self.exited and not self.displayer.window.is_closing
|
||||
|
||||
def __iter__(self):
|
||||
while not self.exited:
|
||||
while not self.exited and not self.displayer.window.is_closing:
|
||||
self.update()
|
||||
yield self.frames
|
||||
|
||||
@ -93,15 +95,15 @@ class SubscriberWindows(object):
|
||||
uid = uid_for_source(name)
|
||||
self.source_names.append(uid)
|
||||
self.input_vid_global_names.append(uid)
|
||||
self.input_cams.append(name)
|
||||
self.input_cams.append(uid)
|
||||
return self
|
||||
|
||||
def add_window(self, name):
|
||||
"""Add another window for this class to display sources with. The name will be the title."""
|
||||
self.window_names.append(name)
|
||||
cv2.namedWindow(name + " (press ESC to quit)")
|
||||
m = WeakMethod(self.handle_mouse)
|
||||
cv2.setMouseCallback(name + " (press ESC to quit)", m)
|
||||
#cv2.namedWindow(name + " (press ESC to quit)")
|
||||
#m = WeakMethod(self.handle_mouse)
|
||||
#cv2.setMouseCallback(name + " (press ESC to quit)", m)
|
||||
return self
|
||||
|
||||
def add_callback(self, callback):
|
||||
@ -160,7 +162,7 @@ class SubscriberWindows(object):
|
||||
else:
|
||||
if len(self.window_names) <= win_num:
|
||||
self.add_window(f"{prepend_name}{win_num}")
|
||||
cv2.imshow(
|
||||
self.displayer.imshow(
|
||||
self.window_names[win_num] + " (press ESC to quit)", f[i]
|
||||
)
|
||||
win_num += 1
|
||||
@ -179,7 +181,7 @@ class SubscriberWindows(object):
|
||||
else:
|
||||
if len(self.window_names) <= win_num:
|
||||
self.add_window(f"{prepend_name} {win_num}")
|
||||
cv2.imshow(
|
||||
self.displayer.imshow(
|
||||
self.window_names[win_num] + " (press ESC to quit)", frames[f]
|
||||
)
|
||||
win_num += 1
|
||||
@ -233,6 +235,7 @@ class SubscriberWindows(object):
|
||||
self.__check_too_many_channels()
|
||||
if not self.silent:
|
||||
self.display_frames(self.frames)
|
||||
self.displayer.update()
|
||||
|
||||
def update(self, arr: Union[List[np.ndarray], np.ndarray] = None, id: Union[List[str],str, List[int], int, None] = None):
|
||||
"""Update window frames once. Optionally add a new input and input id."""
|
||||
@ -292,6 +295,7 @@ class SubscriberWindows(object):
|
||||
self.__stop_all_cams()
|
||||
for t in self.close_threads:
|
||||
t.join()
|
||||
self.displayer.window.close()
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
@ -310,7 +314,7 @@ class SubscriberWindows(object):
|
||||
sub_cmd = window_commands.win_cmd_sub()
|
||||
msg_cmd = ""
|
||||
key = ""
|
||||
while msg_cmd != "quit" and key != "quit":
|
||||
while msg_cmd != "quit" and key != "quit" and (not self.displayer.window.is_closing):
|
||||
msg_cmd, key = self.update()
|
||||
sub_cmd.release()
|
||||
window_commands.quit(force_all_read=False)
|
||||
|
8
examples/looping/cam.py
Normal file
8
examples/looping/cam.py
Normal file
@ -0,0 +1,8 @@
|
||||
from displayarray import display
|
||||
import numpy as np
|
||||
|
||||
arr = np.random.normal(0.5, 0.1, (100, 100, 5))
|
||||
|
||||
with display(0, size=(-1,-1)) as displayer:
|
||||
while displayer:
|
||||
pass
|
@ -1,9 +1,14 @@
|
||||
from displayarray import display
|
||||
import numpy as np
|
||||
|
||||
arr = np.random.normal(0.5, 0.1, (100, 100, 5))
|
||||
arr = np.random.normal(0.5, 0.1, (100, 100, 3))
|
||||
arr2 = np.random.normal(0.5, 0.1, (200, 200, 3))
|
||||
arr3 = np.random.normal(0.5, 0.1, (300, 300, 3))
|
||||
|
||||
with display(arr) as displayer:
|
||||
while displayer:
|
||||
arr[:] += np.random.normal(0.001, 0.0005, (100, 100, 5))
|
||||
arr[:] += np.random.normal(0.001, 0.0005, (100, 100, 3))
|
||||
arr %= 1.0
|
||||
displayer.update(arr2, '2')
|
||||
displayer.update(arr3, '3')
|
||||
|
||||
|
2
setup.py
2
setup.py
@ -23,7 +23,7 @@ if os.path.exists(readme_path):
|
||||
setup(
|
||||
long_description=readme,
|
||||
name="displayarray",
|
||||
version="1.3.1",
|
||||
version="2.0.0",
|
||||
description="Tool for displaying numpy arrays.",
|
||||
python_requires="==3.*,>=3.7.0",
|
||||
project_urls={"repository": "https://github.com/simleek/displayarray"},
|
||||
|
Reference in New Issue
Block a user