Updated to version 2.0: changed display backend to moderngl_window.

This commit is contained in:
2024-02-10 20:31:06 -07:00
parent ddc97b9233
commit 139cfc6ab7
8 changed files with 514 additions and 17 deletions

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View 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;
}
}

View File

@ -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
View 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

View File

@ -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')

View File

@ -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"},