diff --git a/floppies/franc/.DS_Store b/floppies/franc/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/floppies/franc/.DS_Store differ diff --git a/floppies/franc/LICENSE b/floppies/franc/LICENSE new file mode 100644 index 0000000..bc8fde7 --- /dev/null +++ b/floppies/franc/LICENSE @@ -0,0 +1 @@ +FLOPPYLEFT - 2017 diff --git a/floppies/franc/README b/floppies/franc/README new file mode 100644 index 0000000..aec7f25 --- /dev/null +++ b/floppies/franc/README @@ -0,0 +1,7 @@ +Author: Slavoj Žižek +Date: 1989 +Title: The Sublime Object of Floppy + +Description: + +And so on, and so on, and so on. diff --git a/floppies/franc/common.py b/floppies/franc/common.py new file mode 100755 index 0000000..eec5e5a --- /dev/null +++ b/floppies/franc/common.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python + +''' +This module contais some common routines used by other samples. +''' + +import numpy as np +import cv2 +import os +from contextlib import contextmanager +import itertools as it + +image_extensions = ['.bmp', '.jpg', '.jpeg', '.png', '.tif', '.tiff', '.pbm', '.pgm', '.ppm'] + +class Bunch(object): + def __init__(self, **kw): + self.__dict__.update(kw) + def __str__(self): + return str(self.__dict__) + +def splitfn(fn): + path, fn = os.path.split(fn) + name, ext = os.path.splitext(fn) + return path, name, ext + +def anorm2(a): + return (a*a).sum(-1) +def anorm(a): + return np.sqrt( anorm2(a) ) + +def homotrans(H, x, y): + xs = H[0, 0]*x + H[0, 1]*y + H[0, 2] + ys = H[1, 0]*x + H[1, 1]*y + H[1, 2] + s = H[2, 0]*x + H[2, 1]*y + H[2, 2] + return xs/s, ys/s + +def to_rect(a): + a = np.ravel(a) + if len(a) == 2: + a = (0, 0, a[0], a[1]) + return np.array(a, np.float64).reshape(2, 2) + +def rect2rect_mtx(src, dst): + src, dst = to_rect(src), to_rect(dst) + cx, cy = (dst[1] - dst[0]) / (src[1] - src[0]) + tx, ty = dst[0] - src[0] * (cx, cy) + M = np.float64([[ cx, 0, tx], + [ 0, cy, ty], + [ 0, 0, 1]]) + return M + + +def lookat(eye, target, up = (0, 0, 1)): + fwd = np.asarray(target, np.float64) - eye + fwd /= anorm(fwd) + right = np.cross(fwd, up) + right /= anorm(right) + down = np.cross(fwd, right) + R = np.float64([right, down, fwd]) + tvec = -np.dot(R, eye) + return R, tvec + +def mtx2rvec(R): + w, u, vt = cv2.SVDecomp(R - np.eye(3)) + p = vt[0] + u[:,0]*w[0] # same as np.dot(R, vt[0]) + c = np.dot(vt[0], p) + s = np.dot(vt[1], p) + axis = np.cross(vt[0], vt[1]) + return axis * np.arctan2(s, c) + +def draw_str(dst, (x, y), s): + cv2.putText(dst, s, (x+1, y+1), cv2.FONT_HERSHEY_PLAIN, 1.0, (0, 0, 0), thickness = 2, lineType=cv2.CV_AA) + cv2.putText(dst, s, (x, y), cv2.FONT_HERSHEY_PLAIN, 1.0, (255, 255, 255), lineType=cv2.CV_AA) + +class Sketcher: + def __init__(self, windowname, dests, colors_func): + self.prev_pt = None + self.windowname = windowname + self.dests = dests + self.colors_func = colors_func + self.dirty = False + self.show() + cv2.setMouseCallback(self.windowname, self.on_mouse) + + def show(self): + cv2.imshow(self.windowname, self.dests[0]) + + def on_mouse(self, event, x, y, flags, param): + pt = (x, y) + if event == cv2.EVENT_LBUTTONDOWN: + self.prev_pt = pt + if self.prev_pt and flags & cv2.EVENT_FLAG_LBUTTON: + for dst, color in zip(self.dests, self.colors_func()): + cv2.line(dst, self.prev_pt, pt, color, 5) + self.dirty = True + self.prev_pt = pt + self.show() + else: + self.prev_pt = None + + +# palette data from matplotlib/_cm.py +_jet_data = {'red': ((0., 0, 0), (0.35, 0, 0), (0.66, 1, 1), (0.89,1, 1), + (1, 0.5, 0.5)), + 'green': ((0., 0, 0), (0.125,0, 0), (0.375,1, 1), (0.64,1, 1), + (0.91,0,0), (1, 0, 0)), + 'blue': ((0., 0.5, 0.5), (0.11, 1, 1), (0.34, 1, 1), (0.65,0, 0), + (1, 0, 0))} + +cmap_data = { 'jet' : _jet_data } + +def make_cmap(name, n=256): + data = cmap_data[name] + xs = np.linspace(0.0, 1.0, n) + channels = [] + eps = 1e-6 + for ch_name in ['blue', 'green', 'red']: + ch_data = data[ch_name] + xp, yp = [], [] + for x, y1, y2 in ch_data: + xp += [x, x+eps] + yp += [y1, y2] + ch = np.interp(xs, xp, yp) + channels.append(ch) + return np.uint8(np.array(channels).T*255) + +def nothing(*arg, **kw): + pass + +def clock(): + return cv2.getTickCount() / cv2.getTickFrequency() + +@contextmanager +def Timer(msg): + print msg, '...', + start = clock() + try: + yield + finally: + print "%.2f ms" % ((clock()-start)*1000) + +class StatValue: + def __init__(self, smooth_coef = 0.5): + self.value = None + self.smooth_coef = smooth_coef + def update(self, v): + if self.value is None: + self.value = v + else: + c = self.smooth_coef + self.value = c * self.value + (1.0-c) * v + +class RectSelector: + def __init__(self, win, callback): + self.win = win + self.callback = callback + cv2.setMouseCallback(win, self.onmouse) + self.drag_start = None + self.drag_rect = None + def onmouse(self, event, x, y, flags, param): + x, y = np.int16([x, y]) # BUG + if event == cv2.EVENT_LBUTTONDOWN: + self.drag_start = (x, y) + if self.drag_start: + if flags & cv2.EVENT_FLAG_LBUTTON: + xo, yo = self.drag_start + x0, y0 = np.minimum([xo, yo], [x, y]) + x1, y1 = np.maximum([xo, yo], [x, y]) + self.drag_rect = None + if x1-x0 > 0 and y1-y0 > 0: + self.drag_rect = (x0, y0, x1, y1) + else: + rect = self.drag_rect + self.drag_start = None + self.drag_rect = None + if rect: + self.callback(rect) + def draw(self, vis): + if not self.drag_rect: + return False + x0, y0, x1, y1 = self.drag_rect + cv2.rectangle(vis, (x0, y0), (x1, y1), (0, 255, 0), 2) + return True + @property + def dragging(self): + return self.drag_rect is not None + + +def grouper(n, iterable, fillvalue=None): + '''grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx''' + args = [iter(iterable)] * n + return it.izip_longest(fillvalue=fillvalue, *args) + +def mosaic(w, imgs): + '''Make a grid from images. + + w -- number of grid columns + imgs -- images (must have same size and format) + ''' + imgs = iter(imgs) + img0 = imgs.next() + pad = np.zeros_like(img0) + imgs = it.chain([img0], imgs) + rows = grouper(w, imgs, pad) + return np.vstack(map(np.hstack, rows)) + +def getsize(img): + h, w = img.shape[:2] + return w, h + +def mdot(*args): + return reduce(np.dot, args) + +def draw_keypoints(vis, keypoints, color = (0, 255, 255)): + for kp in keypoints: + x, y = kp.pt + cv2.circle(vis, (int(x), int(y)), 2, color) diff --git a/floppies/franc/common.pyc b/floppies/franc/common.pyc new file mode 100644 index 0000000..809781f Binary files /dev/null and b/floppies/franc/common.pyc differ diff --git a/floppies/franc/main.pd b/floppies/franc/main.pd new file mode 100644 index 0000000..63b9ed9 --- /dev/null +++ b/floppies/franc/main.pd @@ -0,0 +1,66 @@ +#N canvas 136 23 897 545 10; +#X floatatom 166 153 5 0 0 0 - - -; +#X floatatom 282 159 5 0 0 0 - - -; +#X obj 54 -92 tgl 15 0 empty empty empty 17 7 0 10 -262130 -1 -1 1 +1; +#X msg 54 -54 \; pd dsp \$1; +#X obj 267 -7 OSC/unpackOSC; +#X obj 267 -46 iemnet/udpreceive 9001; +#X obj 267 33 OSC/routeOSC /dot; +#X obj 190 121 unpack f f; +#N canvas 0 22 450 278 (subpatch) 0; +#X array waveform 11 float 1; +#A 0 -1 -1 -1 -1 -1 -1 -1 -0.333333 0 0.333333 -1; +#X coords 0 1 10 -1 200 140 1 0 0; +#X restore 494 188 graph; +#X obj 224 247 tabwrite waveform; +#X obj 264 189 t b f; +#X obj 225 220 f; +#X obj 198 354 tabread4~ waveform; +#X obj 232 401 dac~; +#X obj 488 116 tabread waveform; +#X obj 543 23 + 1; +#X obj 505 21 i; +#X obj 504 -39 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 +1; +#X msg 534 78 0; +#X floatatom 483 72 5 0 0 0 - - -; +#X floatatom 424 254 5 0 0 0 - - -; +#X obj 535 54 select 10; +#X obj 204 325 *~ 10; +#X obj 416 207 + 1; +#X obj 506 -9 metro 100; +#X obj 196 295 osc~; +#X obj 364 370 line~; +#X msg 364 342 \$1 10; +#X obj 337 295 expr 100 + (100 * $f1); +#X connect 2 0 3 0; +#X connect 4 0 6 0; +#X connect 5 0 4 0; +#X connect 6 0 7 0; +#X connect 7 0 0 0; +#X connect 7 0 10 0; +#X connect 7 1 1 0; +#X connect 7 1 11 1; +#X connect 10 0 11 0; +#X connect 10 1 9 1; +#X connect 11 0 9 0; +#X connect 12 0 13 0; +#X connect 12 0 13 1; +#X connect 14 0 23 0; +#X connect 15 0 16 1; +#X connect 16 0 15 0; +#X connect 16 0 19 0; +#X connect 16 0 14 0; +#X connect 16 0 21 0; +#X connect 17 0 24 0; +#X connect 18 0 16 1; +#X connect 21 0 18 0; +#X connect 22 0 12 0; +#X connect 23 0 20 0; +#X connect 23 0 28 0; +#X connect 24 0 16 0; +#X connect 25 0 22 0; +#X connect 27 0 26 0; +#X connect 28 0 27 0; +#X connect 28 0 25 0; diff --git a/floppies/franc/main.py b/floppies/franc/main.py new file mode 100644 index 0000000..6f912c7 --- /dev/null +++ b/floppies/franc/main.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python + +import numpy as np +import cv2, math +import video + +help_message = ''' +USAGE: opt_flow.py [] + +Keys: + 1 - toggle HSV flow visualization + 2 - toggle glitch + +''' + +# def draw_flow(img, flow, step=4): # size grid +# h, w = img.shape[:2] +# y, x = np.mgrid[step/2:h:step, step/2:w:step].reshape(2,-1) +# fx, fy = flow[y,x].T +# lines = np.vstack([x, y, x+fx, y+fy]).T.reshape(-1, 2, 2) +# lines = np.int32(lines + 0.5) +# vis = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) +# cv2.polylines(vis, lines, 0, (0, 0, 255)) # BGR +# for (x1, y1), (x2, y2) in lines: +# cv2.circle(vis, (x1, y1), 1, (0, 255, 0), -1) +# return vis + +import OSC +# from pythonosc import osc_message_builder +# from pythonosc import udp_client +import time + +def send_flow0(img, flow, step=4): # size grid + h, w = img.shape[:2] + y, x = np.mgrid[step/2:h:step, step/2:w:step].reshape(2,-1) + fx, fy = flow[y,x].T + #print "fx, fy", fx, fy + lines = np.vstack([x, y, x+fx, y+fy]).T.reshape(-1, 2, 2) + lines = np.int32(lines + 0.5) + vis = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) + + + flines = [] + for (x1, y1), (x2, y2) in lines: + # print ("y1", y1) + if (x1 == 38 or x1 == 46 or x1 == 54 or x1 == 62 or x1 == 70 or x1 == 78 or x1 == 86 or x1 == 94 or x1 == 102 or x1 == 110 or x1 == 118) and y1 in range(38, 90, 8): + flines.append(((x1,y1),(x2,y2))) + normx = x1 / 8 - 4 + normy = 1 - ((y1 / 8 - 4) / 3.0) + dx = x2-x1 + dy = y2 - y1 + m = int(math.sqrt( (dx*dx) + (dy*dy) )) + if m>2: + print ("dot", (normx, normy)) + msg = OSC.OSCMessage() + msg.setAddress("/dot") + #msg.append(dx) + #msg.append(dy) + #msg.append(m) + msg.append(normx) + msg.append(normy) + client.send(msg) + # client.send_message("/franc", m) + + + # for (x1, y1), (x2, y2) in lines: + # # print ("y1", y1) + # if (y1 == 38 or y1 == 46 or y1 == 54 or y1 == 70 or y1 == 86) and x1 in range(38, 118, 8): + # flines.append(((x1,y1),(x2,y2))) + # dx = x2-x1 + # dy = y2 - y1 + # m = int(math.sqrt( (dx*dx) + (dy*dy) )) + # if m>2: + # print ("x", (dx, dy, m, x1, y1)) + # msg = OSC.OSCMessage() + # msg.setAddress("/x") + # msg.append(dx) + # msg.append(dy) + # msg.append(m) + # msg.append(x1) + # msg.append(y1) + # client.send(msg) + + + # Here goes BPM + + + + + # for (x1, y1), (x2, y2) in lines: + # # print ("y1", y1) + # if (y1 == 10 or y1 == 110) and x1 in range(90, 150, 4): + # flines.append(((x1,y1),(x2,y2))) + # dx = x2-x1 + # dy = y2 - y1 + # m = int(math.sqrt( (dx*dx) + (dy*dy) )) + # if m>2: + # print ("l", (dx, dy, m, x1, y1)) + # msg = OSC.OSCMessage() + # msg.setAddress("/left") + # msg.append(dx) + # msg.append(dy) + # msg.append(m) + # msg.append(x1) + # msg.append(y1) + # client.send(msg) + + + flines = np.int32(flines) + cv2.polylines(vis, flines, 0, (0, 40, 255)) # BGR + for (x1, y1), (x2, y2) in flines: + cv2.circle(vis, (x1, y1), 1, (0, 255, 0), -1) + return vis + + flines = np.int32(flines) + cv2.polylines(vis, flines, 0, (0, 40, 255)) # BGR + for (x1, y1), (x2, y2) in flines: + cv2.circle(vis, (x1, y1), 1, (0, 255, 0), -1) + return vis + + # cv2.rectangle(img, pt1, pt2, color[, thickness[, lineType[, shift]]]) + +if __name__ == '__main__': + import sys + print help_message + try: fn = sys.argv[1] + except: fn = 0 + + + # connect to pd + # Init OSC + client = OSC.OSCClient() + client.connect(('127.0.0.1', 9001)) # first argument is the IP of the host, second argument is the port to use + #data="hello" + # client = udp_client.SimpleUDPClient("127.0.0.1", 9001) + + # connect camera + # cam = video.create_capture(fn) + cam = video.create_capture("0:size=160x120") #canvas size in pixels + ret, prev = cam.read() + prevgray = cv2.cvtColor(prev, cv2.COLOR_BGR2GRAY) + cur_glitch = prev.copy() + + while True: + # print "GRAB FRAME" + ret, img = cam.read() + gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + flow = cv2.calcOpticalFlowFarneback(prevgray, gray, 0.5, 3, 15, 3, 5, 1.2, 0) + prevgray = gray + + cv2.imshow('flow', send_flow0(gray, flow)) + + ch = 0xFF & cv2.waitKey(5) + if ch == 27: + break + cv2.destroyAllWindows() diff --git a/floppies/franc/noweb/index.html b/floppies/franc/noweb/index.html new file mode 100644 index 0000000..bc6fe43 --- /dev/null +++ b/floppies/franc/noweb/index.html @@ -0,0 +1 @@ +GREAT JOB! diff --git a/floppies/franc/video.py b/floppies/franc/video.py new file mode 100755 index 0000000..7e90ded --- /dev/null +++ b/floppies/franc/video.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python + +''' +Video capture sample. + +Sample shows how VideoCapture class can be used to acquire video +frames from a camera of a movie file. Also the sample provides +an example of procedural video generation by an object, mimicking +the VideoCapture interface (see Chess class). + +'create_capture' is a convinience function for capture creation, +falling back to procedural video in case of error. + +Usage: + video.py [--shotdir ] [source0] [source1] ...' + + sourceN is an + - integer number for camera capture + - name of video file + - synth: for procedural video + +Synth examples: + synth:bg=../cpp/lena.jpg:noise=0.1 + synth:class=chess:bg=../cpp/lena.jpg:noise=0.1:size=640x480 + +Keys: + ESC - exit + SPACE - save current frame to directory + +''' + +import numpy as np +import cv2 +from time import clock +from numpy import pi, sin, cos +import common + +class VideoSynthBase(object): + def __init__(self, size=None, noise=0.0, bg = None, **params): + self.bg = None + self.frame_size = (640, 480) + if bg is not None: + self.bg = cv2.imread(bg, 1) + h, w = self.bg.shape[:2] + self.frame_size = (w, h) + + if size is not None: + w, h = map(int, size.split('x')) + self.frame_size = (w, h) + self.bg = cv2.resize(self.bg, self.frame_size) + + self.noise = float(noise) + + def render(self, dst): + pass + + def read(self, dst=None): + w, h = self.frame_size + + if self.bg is None: + buf = np.zeros((h, w, 3), np.uint8) + else: + buf = self.bg.copy() + + self.render(buf) + + if self.noise > 0.0: + noise = np.zeros((h, w, 3), np.int8) + cv2.randn(noise, np.zeros(3), np.ones(3)*255*self.noise) + buf = cv2.add(buf, noise, dtype=cv2.CV_8UC3) + return True, buf + + def isOpened(self): + return True + +class Chess(VideoSynthBase): + def __init__(self, **kw): + super(Chess, self).__init__(**kw) + + w, h = self.frame_size + + self.grid_size = sx, sy = 10, 7 + white_quads = [] + black_quads = [] + for i, j in np.ndindex(sy, sx): + q = [[j, i, 0], [j+1, i, 0], [j+1, i+1, 0], [j, i+1, 0]] + [white_quads, black_quads][(i + j) % 2].append(q) + self.white_quads = np.float32(white_quads) + self.black_quads = np.float32(black_quads) + + fx = 0.9 + self.K = np.float64([[fx*w, 0, 0.5*(w-1)], + [0, fx*w, 0.5*(h-1)], + [0.0,0.0, 1.0]]) + + self.dist_coef = np.float64([-0.2, 0.1, 0, 0]) + self.t = 0 + + def draw_quads(self, img, quads, color = (0, 255, 0)): + img_quads = cv2.projectPoints(quads.reshape(-1, 3), self.rvec, self.tvec, self.K, self.dist_coef) [0] + img_quads.shape = quads.shape[:2] + (2,) + for q in img_quads: + cv2.fillConvexPoly(img, np.int32(q*4), color, cv2.CV_AA, shift=2) + + def render(self, dst): + t = self.t + self.t += 1.0/30.0 + + sx, sy = self.grid_size + center = np.array([0.5*sx, 0.5*sy, 0.0]) + phi = pi/3 + sin(t*3)*pi/8 + c, s = cos(phi), sin(phi) + ofs = np.array([sin(1.2*t), cos(1.8*t), 0]) * sx * 0.2 + eye_pos = center + np.array([cos(t)*c, sin(t)*c, s]) * 15.0 + ofs + target_pos = center + ofs + + R, self.tvec = common.lookat(eye_pos, target_pos) + self.rvec = common.mtx2rvec(R) + + self.draw_quads(dst, self.white_quads, (245, 245, 245)) + self.draw_quads(dst, self.black_quads, (10, 10, 10)) + + +classes = dict(chess=Chess) + +presets = dict( + empty = 'synth:', + lena = 'synth:bg=../cpp/lena.jpg:noise=0.1', + chess = 'synth:class=chess:bg=../cpp/lena.jpg:noise=0.1:size=640x480' +) + + +def create_capture(source = 0, fallback = presets['chess']): + '''source: or '||synth [:= [:...]]' + ''' + source = str(source).strip() + chunks = source.split(':') + # hanlde drive letter ('c:', ...) + if len(chunks) > 1 and len(chunks[0]) == 1 and chunks[0].isalpha(): + chunks[1] = chunks[0] + ':' + chunks[1] + del chunks[0] + + source = chunks[0] + try: source = int(source) + except ValueError: pass + params = dict( s.split('=') for s in chunks[1:] ) + + cap = None + if source == 'synth': + Class = classes.get(params.get('class', None), VideoSynthBase) + try: cap = Class(**params) + except: pass + else: + cap = cv2.VideoCapture(source) + if 'size' in params: + w, h = map(int, params['size'].split('x')) + cap.set(cv2.cv.CV_CAP_PROP_FRAME_WIDTH, w) + cap.set(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT, h) + if cap is None or not cap.isOpened(): + print 'Warning: unable to open video source: ', source + if fallback is not None: + return create_capture(fallback, None) + return cap + +if __name__ == '__main__': + import sys + import getopt + + print __doc__ + + args, sources = getopt.getopt(sys.argv[1:], '', 'shotdir=') + args = dict(args) + shotdir = args.get('--shotdir', '.') + if len(sources) == 0: + sources = [ 0 ] + + caps = map(create_capture, sources) + shot_idx = 0 + while True: + imgs = [] + for i, cap in enumerate(caps): + ret, img = cap.read() + imgs.append(img) + cv2.imshow('capture %d' % i, img) + ch = 0xFF & cv2.waitKey(1) + if ch == 27: + break + if ch == ord(' '): + for i, img in enumerate(imgs): + fn = '%s/shot_%d_%03d.bmp' % (shotdir, i, shot_idx) + cv2.imwrite(fn, img) + print fn, 'saved' + shot_idx += 1 + cv2.destroyAllWindows() diff --git a/floppies/franc/video.pyc b/floppies/franc/video.pyc new file mode 100644 index 0000000..24e2adb Binary files /dev/null and b/floppies/franc/video.pyc differ