Finalized lens work. Started initial unit tests.

This commit is contained in:
simleek
2019-10-08 22:32:59 -07:00
parent 7b632a604d
commit 60a29ddd9c
42 changed files with 376 additions and 224 deletions
-2
View File
@@ -1,2 +0,0 @@
# redirection, so we can use subtree like pip
from displayarray import frame_publising, subscriber_window
+1 -1
View File
@@ -6,4 +6,4 @@ display is a function that displays these in their own windows.
__version__ = "0.6.6"
from .subscriber_window.subscriber_windows import display
from .window.subscriber_windows import display, breakpoint_display
+3 -3
View File
@@ -1,4 +1,4 @@
from displayarray.subscriber_window import window_commands
from displayarray.window import window_commands
import numpy as np
from typing import Union
@@ -13,7 +13,7 @@ def global_cv_display_callback(frame: np.ndarray, cam_id: Union[int, str]):
:param cam_id: The video or image source
:type cam_id: Union[int, str]
"""
from displayarray.subscriber_window import SubscriberWindows
from displayarray.window import SubscriberWindows
SubscriberWindows.FRAME_DICT[str(cam_id) + "frame"] = frame
@@ -23,7 +23,7 @@ class function_display_callback(object): # NOSONAR
Used for running arbitrary functions on pixels.
>>> import random
>>> from displayarray.webcam_pub import VideoHandlerThread
>>> from displayarray.frame import VideoHandlerThread
>>> img = np.zeros((300, 300, 3))
>>> def fun(array, coords, finished):
... r,g,b = random.random()/20.0, random.random()/20.0, random.random()/20.0
+6 -1
View File
@@ -1,9 +1,10 @@
import numpy as np
from ..input import mouse_loop
import cv2
class Crop(object):
"""A callback class that will return the input array cropped to the output size. N-dimensional."""
def __init__(self, output_size=(64, 64, 3), center=None):
self.output_size = output_size
self.center = center
@@ -12,6 +13,7 @@ class Crop(object):
self.input_size = None
def __call__(self, arr):
"""Crop the input array to the specified output size. output is centered on self.center point on input."""
if self.center is None:
self.input_size = arr.shape
self.center = [int(arr.shape[x]) // 2 for x in range(arr.ndim)]
@@ -35,6 +37,8 @@ class Crop(object):
return out_array.astype(arr.dtype)
def enable_mouse_control(self):
"""Move the mouse to move where the crop is from on the original image"""
@mouse_loop
def m_loop(me):
if self.center is None:
@@ -44,3 +48,4 @@ class Crop(object):
1]
self.mouse_control = m_loop
return self
+5 -3
View File
@@ -1,9 +1,6 @@
import numpy as np
from ..input import mouse_loop
import cv2
from ..util.simple_unique_count import uniquecount
class ControllableLens(object):
@@ -35,6 +32,10 @@ class Barrel(ControllableLens):
self.mouse_control = None
def enable_mouse_control(self):
"""
Move the mouse to center the image, scroll to increase/decrease barrel, ctrl+scroll to increase/decrease zoom
"""
@mouse_loop
def m_loop(me):
self.center[:] = [me.y, me.x]
@@ -51,6 +52,7 @@ class Barrel(ControllableLens):
self.barrel_power /= 1.1
self.mouse_control = m_loop
return self
def __call__(self, arr):
zoom_out = 1.0 / self.zoom
@@ -4,7 +4,7 @@ import time
import cv2
import numpy as np
from displayarray.frame_publising import subscriber_dictionary
from displayarray.frame import subscriber_dictionary
from .np_to_opencv import NpCam
from displayarray.uid import uid_for_source
@@ -12,10 +12,10 @@ from typing import Union, Tuple
def pub_cam_loop(
cam_id: Union[int, str],
request_size: Tuple[int, int] = (1280, 720),
high_speed: bool = False,
fps_limit: float = 240,
cam_id: Union[int, str],
request_size: Tuple[int, int] = (1280, 720),
high_speed: bool = False,
fps_limit: float = 240,
) -> bool:
"""
Publish whichever camera you select to CVCams.<cam_id>.Vid.
@@ -81,10 +81,10 @@ def pub_cam_loop(
def pub_cam_thread(
cam_id: Union[int, str],
request_ize: Tuple[int, int] = (1280, 720),
high_speed: bool = False,
fps_limit: float = 240,
cam_id: Union[int, str],
request_ize: Tuple[int, int] = (1280, 720),
high_speed: bool = False,
fps_limit: float = 240,
) -> threading.Thread:
"""Run pub_cam_loop in a new thread."""
t = threading.Thread(
@@ -5,9 +5,9 @@ import numpy as np
from displayarray.callbacks import global_cv_display_callback
from displayarray.uid import uid_for_source
from displayarray.frame_publising import subscriber_dictionary
from displayarray.frame_publising.frame_publishing import pub_cam_thread
from displayarray.subscriber_window import window_commands
from displayarray.frame import subscriber_dictionary
from displayarray.frame.frame_publishing import pub_cam_thread
from displayarray.window import window_commands
FrameCallable = Callable[[np.ndarray], Optional[np.ndarray]]
@@ -85,7 +85,7 @@ class VideoHandlerThread(threading.Thread):
:param callbacks: List of callbacks to be run on frames before displaying to the screen.
"""
from displayarray.subscriber_window import SubscriberWindows
from displayarray.window import SubscriberWindows
if callbacks is None:
callbacks = []
+1 -1
View File
@@ -1,4 +1,4 @@
from displayarray.subscriber_window import window_commands
from displayarray.window import window_commands
import threading
import time
@@ -8,11 +8,11 @@ from localpubsub import NoData
from displayarray.callbacks import global_cv_display_callback
from displayarray.uid import uid_for_source
from displayarray.frame_publising import subscriber_dictionary
from displayarray.frame_publising.frame_update_thread import FrameCallable
from displayarray.frame_publising.frame_update_thread import VideoHandlerThread
from displayarray.frame import subscriber_dictionary
from displayarray.frame.frame_update_thread import FrameCallable
from displayarray.frame.frame_update_thread import VideoHandlerThread
from displayarray.input import MouseEvent
from displayarray.subscriber_window import window_commands
from displayarray.window import window_commands
import weakref
@@ -63,12 +63,18 @@ class SubscriberWindows(object):
self.update()
return not self.exited
def block(self):
self.loop()
for ct in self.close_threads:
ct.join()
def add_source(self, name):
"""Add another source for this class to display."""
uid = uid_for_source(name)
self.source_names.append(uid)
self.input_vid_global_names.append(uid + "frame")
self.input_cams.append(name)
return self
def add_window(self, name):
"""Add another window for this class to display sources with. The name will be the title."""
@@ -76,6 +82,7 @@ class SubscriberWindows(object):
cv2.namedWindow(name + " (press ESC to quit)")
m = WeakMethod(self.handle_mouse)
cv2.setMouseCallback(name + " (press ESC to quit)", m)
return self
def del_window(self, name):
cv2.setMouseCallback(name + " (press ESC to quit)", lambda *args: None)
@@ -83,6 +90,7 @@ class SubscriberWindows(object):
def add_callback(self, callback):
"""Add a callback for this class to apply to videos."""
self.callbacks.append(callback)
return self
def __stop_all_cams(self):
for c in self.source_names:
@@ -143,17 +151,15 @@ class SubscriberWindows(object):
if self.input_vid_global_names[i] in self.FRAME_DICT and not isinstance(
self.FRAME_DICT[self.input_vid_global_names[i]], NoData
):
if (
len(self.callbacks) > 0
and self.callbacks[i % len(self.callbacks)] is not None
):
self.frames = self.callbacks[i % len(self.callbacks)](
self.FRAME_DICT[self.input_vid_global_names[i]]
)
else:
self.frames = self.FRAME_DICT[self.input_vid_global_names[i]]
self.frames = self.FRAME_DICT[self.input_vid_global_names[i]]
if isinstance(self.frames, np.ndarray) and len(self.frames.shape) <= 3:
self.frames = [self.frames]
if len(self.callbacks) > 0:
for c in self.callbacks:
for f in range(len(self.frames)):
frame = c(self.frames[f])
if frame is not None:
self.frames[f] = frame
win_num = self._display_frames(self.frames, win_num)
def update(self, arr=None, id=None):
@@ -175,6 +181,7 @@ class SubscriberWindows(object):
key = ""
while msg_cmd != "quit" and key != "quit" and len(self.frames) == 0:
msg_cmd, key = self.update()
return self
def end(self):
"""Close all threads. Should be used with non-blocking mode."""
@@ -279,3 +286,7 @@ def display(
s = SubscriberWindows(window_names=window_names, video_sources=vids)
s.close_threads = vid_threads
return s
def breakpoint_display(*args, **kwargs):
return display(*args, **kwargs, blocking=True)
+2 -1
View File
@@ -1 +1,2 @@
# Fractal test is from: https://www.youtube.com/watch?v=WgXQ59rg0GM
# Fractal test is from: https://www.youtube.com/watch?v=WgXQ59rg0GM
import os
+9
View File
@@ -0,0 +1,9 @@
from displayarray import display
import numpy as np
def black_and_white(arr):
return (np.sum(arr, axis=-1) / 3).astype(np.uint8)
display(0, callbacks=black_and_white, blocking=True)
+15
View File
@@ -0,0 +1,15 @@
from displayarray import display
import math as m
from examples.videos import test_video
def forest_color(arr):
forest_color.i += 1
arr[..., 0] = (m.sin(forest_color.i * (2 * m.pi) * .4 / 360) * 255 + arr[..., 0]) % 255
arr[..., 1] = (m.sin((forest_color.i * (2 * m.pi) * .5 + 45) / 360) * 255 + arr[..., 1]) % 255
arr[..., 2] = (m.cos(forest_color.i * (2 * m.pi) * .3 / 360) * 255 + arr[..., 2]) % 255
forest_color.i = 0
display(test_video, callbacks=forest_color, blocking=True, fps_limit=120)
+12
View File
@@ -0,0 +1,12 @@
from displayarray import display
import numpy as np
arr = np.random.normal(0.5, 0.1, (500, 500, 3))
def fix_arr_cv(arr_in):
arr_in[:] += np.random.normal(0.01, 0.005, (500, 500, 3))
arr_in %= 1.0
display(arr, callbacks=fix_arr_cv, blocking=True)
+51
View File
@@ -0,0 +1,51 @@
from displayarray import breakpoint_display
import numpy as np
center = (75, 450)
zoom = .5
zoom_out = 1.0 / zoom
arr = np.random.uniform(0, 1, (300, 600, 3))
breakpoint_display(arr)
y = np.arange(arr.shape[0])
x = np.arange(arr.shape[1])
y_ = (y - center[0]) * zoom_out / arr.shape[0]
x_ = (x - center[1]) * zoom_out / arr.shape[1]
p = np.array(np.meshgrid(x_, y_))
breakpoint_display(p[0] + .5)
breakpoint_display(p[1] + .5)
barrel_power = 1.5
theta = np.arctan2(p[1], p[0])
breakpoint_display((theta + (np.pi / 2.0)) / (np.pi / 2.0))
radius = np.linalg.norm(p, axis=0)
print(radius.shape)
breakpoint_display(radius)
radius = pow(radius, barrel_power)
breakpoint_display(radius)
print(len(x))
x_new = 0.5 * (radius * np.cos(theta) + 1)
breakpoint_display(x_new)
x_new = np.clip(x_new * len(x), 0, len(x) - 1)
breakpoint_display(x_new / float(len(x)))
y_new = 0.5 * (radius * np.sin(theta) + 1)
breakpoint_display(y_new)
y_new = np.clip(y_new * len(y), 0, len(y) - 1)
p = np.array(np.meshgrid(y, x)).astype(np.uint32)
p_new = np.array((y_new, x_new)).astype(np.uint32)
brr = arr.copy()
brr[p[0], p[1], :] = np.swapaxes(arr[p_new[0], p_new[1], :], 0, 1)
breakpoint_display(brr)
crr = np.zeros_like(arr)
crr[p_new[0], p_new[1], :] = np.swapaxes(arr[p[0], p[1], :], 0, 1)
breakpoint_display(crr)
+4
View File
@@ -0,0 +1,4 @@
from displayarray import display
import numpy as np
display(np.random.normal(0.5, 0.1, (500, 500, 3))).block()
@@ -4,8 +4,12 @@ from displayarray import display
def mandel(height=240, width=320, itermax=255, y_min=-1.8, y_max=.6, x_min=-1.6, x_max=1.6):
"""
Generate a view of the mandlebrot fractal
source: https://thesamovar.wordpress.com/2009/03/22/fast-fractals-with-python-and-numpy/
>>> img = mandel()
>>> center = (0, -np.pi/2)
>>> center = (0, -1.78)
>>> length = 3.2
>>> d = display(img)
>>> while d:
@@ -15,15 +19,6 @@ def mandel(height=240, width=320, itermax=255, y_min=-1.8, y_max=.6, x_min=-1.6,
... x_min = center[0]-length/2.0
... x_max = center[0]+length/2.0
... img[...] = mandel(y_min=y_min, y_max=y_max, x_min=x_min, x_max=x_max)
:param height:
:param width:
:param itermax:
:param y_min:
:param y_max:
:param x_min:
:param x_max:
:return:
"""
ix, iy = np.mgrid[0:height, 0:width]
@@ -39,7 +34,7 @@ def mandel(height=240, width=320, itermax=255, y_min=-1.8, y_max=.6, x_min=-1.6,
break
z = z * z
z = z + c
rem = abs(z) > 2.0
rem = abs(z) > 4.0
img[ix[rem], iy[rem]] = i + 1
rem = ~rem
z = z[rem]
+7
View File
@@ -0,0 +1,7 @@
from displayarray.effects import crop
from displayarray import display
from examples.videos import test_video
# Move the mouse to move where the crop is from on the original image
display(test_video).add_callback(crop.Crop().enable_mouse_control()).block()
+9
View File
@@ -0,0 +1,9 @@
from displayarray.effects import lens
from displayarray import display
from examples.videos import test_video
# Move the mouse to center the image, scroll to increase/decrease barrel, ctrl+scroll to increase/decrease zoom
m = lens.Barrel(use_bleed=False)
m.enable_mouse_control()
display(test_video, callbacks=m, blocking=True)
+7
View File
@@ -0,0 +1,7 @@
from displayarray.effects import lens
from displayarray import display
from examples.videos import test_video
m = lens.Mustache()
m.enable_mouse_control()
display(test_video, callbacks=m, blocking=True)
+13
View File
@@ -0,0 +1,13 @@
from displayarray.effects import crop, lens
from displayarray import display
from examples.videos import test_video
# Move the mouse to center the image, scroll to increase/decrease barrel, ctrl+scroll to increase/decrease zoom
d = display(test_video) \
.add_callback(crop.Crop()) \
.add_callback(lens.Barrel().enable_mouse_control()) \
.wait_for_init()
while d:
print(d.frames[0].shape)
+10
View File
@@ -0,0 +1,10 @@
from displayarray.effects import crop, lens
from displayarray import display
from examples.videos import test_video
# Move the mouse to move where the crop is from on the original image
display(test_video) \
.add_callback(crop.Crop()) \
.add_callback(lens.Barrel().enable_mouse_control()) \
.block()
+9
View File
@@ -0,0 +1,9 @@
from displayarray import display
import numpy as np
arr = np.random.normal(0.5, 0.1, (100, 100, 3))
with display(arr) as displayer:
while displayer:
arr[:] += np.random.normal(0.001, 0.0005, (100, 100, 3))
arr %= 1.0
@@ -0,0 +1,41 @@
from displayarray import display
import numpy as np
from tensorflow.keras import layers, models
import tensorflow as tf
from examples.videos import test_video_2
for gpu in tf.config.experimental.list_physical_devices("GPU"):
tf.compat.v2.config.experimental.set_memory_growth(gpu, True)
displayer = display(test_video_2)
displayer.wait_for_init()
autoencoder = models.Sequential()
autoencoder.add(
layers.Conv2D(
20, (3, 3), activation="sigmoid", input_shape=displayer.frames[0].shape
)
)
autoencoder.add(
layers.Conv2D(
20, (3, 3), activation="sigmoid", input_shape=displayer.frames[0].shape
)
)
autoencoder.add(layers.Conv2DTranspose(3, (3, 3), activation="sigmoid"))
autoencoder.add(layers.Conv2DTranspose(3, (3, 3), activation="sigmoid"))
autoencoder.compile(loss="mse", optimizer="adam")
while displayer:
displayer.update()
grab = tf.convert_to_tensor(
next(iter(displayer.FRAME_DICT.values()))[np.newaxis, ...].astype(np.float32) / 255.0
)
grab_noise = tf.convert_to_tensor(
((next(iter(displayer.FRAME_DICT.values()))[np.newaxis, ...].astype(
np.float32) + np.random.uniform(0, 255, grab.shape)) / 2)
/ 255.0
)
displayer.update((grab_noise.numpy()[0] * 255.0).astype(np.uint8), "uid for grab noise")
autoencoder.fit(grab_noise, grab, steps_per_epoch=1, epochs=1)
output_image = autoencoder.predict(grab, steps=1)
displayer.update((output_image[0] * 255.0).astype(np.uint8), "uid for autoencoder output")
-171
View File
@@ -1,171 +0,0 @@
import unittest as ut
class TestSubWin(ut.TestCase):
def test_display_numpy(self):
from displayarray import display
import numpy as np
display(np.random.normal(0.5, 0.1, (500, 500, 3)))
def test_display_numpy_callback(self):
from displayarray import display
import numpy as np
arr = np.random.normal(0.5, 0.1, (500, 500, 3))
def fix_arr_cv(arr_in):
arr_in[:] += np.random.normal(0.01, 0.005, (500, 500, 3))
arr_in %= 1.0
display(arr, callbacks=fix_arr_cv, blocking=True)
def test_display_numpy_loop(self):
from displayarray import display
import numpy as np
arr = np.random.normal(0.5, 0.1, (100, 100, 3))
with display(arr) as displayer:
while displayer:
arr[:] += np.random.normal(0.001, 0.0005, (100, 100, 3))
arr %= 1.0
def test_display_camera(self):
from displayarray import display
import numpy as np
def black_and_white(arr):
return (np.sum(arr, axis=-1) / 3).astype(np.uint8)
display(0, callbacks=black_and_white, blocking=True)
def test_display_video(self):
from displayarray import display
import math as m
def forest_color(arr):
forest_color.i += 1
arr[..., 0] = (m.sin(forest_color.i * (2 * m.pi) * .4 / 360) * 255 + arr[..., 0]) % 255
arr[..., 1] = (m.sin((forest_color.i * (2 * m.pi) * .5 + 45) / 360) * 255 + arr[..., 1]) % 255
arr[..., 2] = (m.cos(forest_color.i * (2 * m.pi) * .3 / 360) * 255 + arr[..., 2]) % 255
forest_color.i = 0
display("fractal test.mp4", callbacks=forest_color, blocking=True, fps_limit=120)
def test_display_tensorflow(self):
from displayarray import display
import numpy as np
from tensorflow.keras import layers, models
import tensorflow as tf
for gpu in tf.config.experimental.list_physical_devices("GPU"):
tf.compat.v2.config.experimental.set_memory_growth(gpu, True)
displayer = display("fractal test.mp4")
displayer.wait_for_init()
autoencoder = models.Sequential()
autoencoder.add(
layers.Conv2D(
20, (3, 3), activation="sigmoid", input_shape=displayer.frames[0].shape
)
)
autoencoder.add(
layers.Conv2D(
20, (3, 3), activation="sigmoid", input_shape=displayer.frames[0].shape
)
)
autoencoder.add(layers.Conv2DTranspose(3, (3, 3), activation="sigmoid"))
autoencoder.add(layers.Conv2DTranspose(3, (3, 3), activation="sigmoid"))
autoencoder.compile(loss="mse", optimizer="adam")
while displayer:
grab = tf.convert_to_tensor(
displayer.FRAME_DICT["fractal test.mp4frame"][np.newaxis, ...].astype(np.float32)
/ 255.0
)
grab_noise = tf.convert_to_tensor(
(((displayer.FRAME_DICT["fractal test.mp4frame"][np.newaxis, ...].astype(
np.float32) + np.random.uniform(0, 255, grab.shape)) / 2) % 255)
/ 255.0
)
displayer.update((grab_noise.numpy()[0] * 255.0).astype(np.uint8), "uid for grab noise")
autoencoder.fit(grab_noise, grab, steps_per_epoch=1, epochs=1)
output_image = autoencoder.predict(grab, steps=1)
displayer.update((output_image[0] * 255.0).astype(np.uint8), "uid for autoencoder output")
def test_lens_construction(self):
from displayarray import display
import numpy as np
center = (75, 450)
zoom = .5
zoom_out = 1.0 / zoom
arr = np.random.uniform(0, 1, (300, 600, 3))
display(arr, blocking=True)
y = np.arange(arr.shape[0])
x = np.arange(arr.shape[1])
y_ = (y - center[0]) * zoom_out / arr.shape[0]
x_ = (x - center[1]) * zoom_out / arr.shape[1]
p = np.array(np.meshgrid(x_, y_))
display(p[0] + .5, blocking=True)
display(p[1] + .5, blocking=True)
barrel_power = 1.5
theta = np.arctan2(p[1], p[0])
display((theta + (np.pi / 2.0)) / (np.pi / 2.0), blocking=True)
radius = np.linalg.norm(p, axis=0)
print(radius.shape)
display(radius, blocking=True)
radius = pow(radius, barrel_power)
display(radius, blocking=True)
print(len(x))
x_new = 0.5 * (radius * np.cos(theta) + 1)
display(x_new, blocking=True)
x_new = np.clip(x_new * len(x), 0, len(x) - 1)
display(x_new / float(len(x)), blocking=True)
y_new = 0.5 * (radius * np.sin(theta) + 1)
display(y_new, blocking=True)
y_new = np.clip(y_new * len(y), 0, len(y) - 1)
p = np.array(np.meshgrid(y, x)).astype(np.uint32)
p_new = np.array((y_new, x_new)).astype(np.uint32)
brr = arr.copy()
brr[p[0], p[1], :] = np.swapaxes(arr[p_new[0], p_new[1], :], 0, 1)
display(brr, blocking=True)
crr = np.zeros_like(arr)
crr[p_new[0], p_new[1], :] = np.swapaxes(arr[p[0], p[1], :], 0, 1)
display(crr, blocking=True)
def test_display_mustache_lens(self):
from displayarray.effects import lens
from displayarray import display
m = lens.Mustache()
m.enable_mouse_control()
display("fractal test.mp4", callbacks=m, blocking=True)
def test_display_barrel_lens(self):
from displayarray.effects import lens
from displayarray import display
m = lens.Barrel(use_bleed=False)
m.enable_mouse_control()
display("fractal test.mp4", callbacks=m, blocking=True)
def test_crop(self):
from displayarray.effects import crop
from displayarray import display
m = crop.Crop()
m.enable_mouse_control()
display("fractal test.mp4", callbacks=m, blocking=True)

Some files were not shown because too many files have changed in this diff Show More