changeset 1544:fc5675f5b756

captureDevice tool for sweeping through light settings and grabbing pics Ignore-this: 8f3171e4e29727e0aa78e43edff0a1d9
author Drew Perttula <drewp@bigasterisk.com>
date Fri, 19 May 2017 07:35:29 +0000
parents c8cffe82b537
children c7e52977c5ca
files bin/captureDevice light9/networking.py light9/paint/capture.py light9/paint/solve.py show/dance2017/networking.n3
diffstat 5 files changed, 181 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/captureDevice	Fri May 19 07:35:29 2017 +0000
@@ -0,0 +1,137 @@
+#!bin/python
+from __future__ import division
+from rdflib import URIRef, Literal
+from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks, Deferred
+
+import logging
+import optparse
+import os
+import time
+import traceback
+import treq
+import cyclone.web, cyclone.websocket, cyclone.httpclient
+from greplin import scales
+
+from run_local import log
+from lib.cycloneerr import PrettyErrorHandler
+
+from light9.namespaces import L9
+from light9 import networking, showconfig
+from light9.rdfdb.syncedgraph import SyncedGraph
+from light9.paint.capture import writeCaptureDescription
+from light9.greplin_cyclone import StatsForCyclone
+from light9.effect.settings import DeviceSettings
+from light9.effect.sequencer import sendToCollector
+
+stats = scales.collection('/webServer', scales.PmfStat('setAttr'))
+
+class Attrs(PrettyErrorHandler, cyclone.web.RequestHandler):
+    def put(self):
+        with stats.setAttr.time():
+            client, clientSession, settings, sendTime = parseJsonMessage(self.request.body)
+            self.set_status(202)
+
+
+class Camera(object):
+    def __init__(self, imageUrl):
+        self.imageUrl = imageUrl
+    
+    def takePic(self, uri, writePath):
+        log.info('takePic %s', uri)
+        return treq.get(self.imageUrl).addCallbacks(lambda r: self._done(writePath, r), log.error)
+        
+    @inlineCallbacks
+    def _done(self, writePath, response):
+        jpg = yield response.content()
+        try:
+            os.makedirs(os.path.dirname(writePath))
+        except OSError:
+            pass
+        with open(writePath, 'w') as out:
+            out.write(jpg)
+        log.info('wrote %s', writePath)
+
+
+
+settleTime = .5
+
+camera = Camera('http://dash:8200/picamserve/pic?res=480&resize=480&rotation=180&iso=800&redgain=1.6&bluegain=2&shutter=60000')
+
+def launch(graph):
+
+    def steps(a, b, n):
+        return [round(a + (b - a) * i / n, 5) for i in range(n)]
+
+    startTime = time.time()
+    toGather = []
+
+    row = 0
+    for ry in steps(0.85, .92, 6):
+        xSteps = steps(.24, .45, 12)
+        if row % 2:
+            xSteps.reverse()
+        row += 1
+        for rx in xSteps:
+            toGather.append(DeviceSettings(graph, [
+                (L9['device/moving1'], L9['rx'], rx),
+                (L9['device/moving1'], L9['ry'], ry),
+            ]))
+
+    numPics = [0]
+    settingsCache = set()
+    @inlineCallbacks
+    def step():
+        if not toGather:
+            reactor.stop()
+            return
+        settings = toGather.pop()
+        
+        log.info('move to %r', settings)
+        yield sendToCollector(client='captureDevice', session='main', settings=settings)
+        
+        d = Deferred()
+        reactor.callLater(settleTime, d.callback, None)
+        yield d
+        dev = settings.devices()[0]
+
+        devTail = dev.rsplit('/')[-1]
+        captureId = 'cap%s' % (int(startTime) - 1495170000)
+        picId = 'pic%s' % numPics[0]
+        path = '/'.join(['capture', devTail, captureId, picId])
+        ctx = URIRef('/'.join([showconfig.showUri(), 'capture', devTail, captureId, 'index']))
+        uri = URIRef('/'.join([showconfig.showUri(), 'capture', devTail, captureId, picId]))
+                     
+        relOutPath = path + '.jpg'
+        
+        yield camera.takePic(uri, os.path.join(showconfig.root(), relOutPath))
+        numPics[0] += 1
+
+        writeCaptureDescription(graph, ctx, uri, dev, relOutPath, settingsCache, settings)
+        
+        reactor.callLater(0, step)
+    step().addErrback(log.error)
+    
+    reactor.listenTCP(networking.captureDevice.port,
+                      cyclone.web.Application(handlers=[
+                          (r'/()', cyclone.web.StaticFileHandler,
+                           {"path" : "light9/web", "default_filename" : "captureDevice.html"}),
+                          (r'/stats', StatsForCyclone),
+                      ]),
+                      interface='::')
+    log.info('serving http on %s', networking.captureDevice.port)
+    
+def main():
+    parser = optparse.OptionParser()
+    parser.add_option("-v", "--verbose", action="store_true",
+                      help="logging.DEBUG")
+    (options, args) = parser.parse_args()
+    log.setLevel(logging.DEBUG if options.verbose else logging.INFO)
+
+    graph = SyncedGraph(networking.rdfdb.url, "captureDevice")
+
+    graph.initiallySynced.addCallback(lambda _: launch(graph)).addErrback(log.error)
+    reactor.run()
+
+if __name__ == '__main__':
+    main()
--- a/light9/networking.py	Fri May 19 07:33:11 2017 +0000
+++ b/light9/networking.py	Fri May 19 07:35:29 2017 +0000
@@ -36,6 +36,7 @@
     def path(self, more):
         return self.url + str(more)
 
+captureDevice = ServiceAddress(L9['captureDevice'])
 curveCalc = ServiceAddress(L9['curveCalc'])
 dmxServer = ServiceAddress(L9['dmxServer'])
 dmxServerZmq = ServiceAddress(L9['dmxServerZmq'])
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/light9/paint/capture.py	Fri May 19 07:35:29 2017 +0000
@@ -0,0 +1,34 @@
+import os
+from rdflib import URIRef
+from light9 import showconfig
+from light9.rdfdb.patch import Patch
+from light9.namespaces import L9
+from light9.paint.solve import loadNumPy
+
+def writeCaptureDescription(graph, ctx, uri, dev, relOutPath, settingsSubgraphCache, settings):
+    settings.addStatements(
+        uri, ctx=ctx,
+        settingRoot=URIRef('/'.join([showconfig.showUri(), 'capture', dev.rsplit('/')[1]])),
+        settingsSubgraphCache=settingsSubgraphCache)
+    graph.patch(Patch(addQuads=[
+        (dev, L9['capture'], uri, ctx),
+        (uri, L9['imagePath'], URIRef('/'.join([showconfig.showUri(), relOutPath])), ctx),
+        ]))
+    
+class CaptureLoader(object):
+    def __init__(self, graph):
+        self.graph = graph
+        
+    def loadImage(self, pic, thumb=(100, 100)):
+        ip = self.graph.value(pic, L9['imagePath'])
+        if not ip.startswith(showconfig.show()):
+            raise ValueError(repr(ip))
+        diskPath = os.path.join(showconfig.root(), ip[len(self.show):])
+        return loadNumPy(diskPath, thumb)
+        
+    def devices(self):
+        """devices for which we have any captured data"""
+
+    def capturedSettings(self, device):
+        """list of (pic, settings) we know for this device"""
+        
--- a/light9/paint/solve.py	Fri May 19 07:33:11 2017 +0000
+++ b/light9/paint/solve.py	Fri May 19 07:35:29 2017 +0000
@@ -1,7 +1,6 @@
 from __future__ import division
 from light9.namespaces import RDF, L9, DEV
 from PIL import Image
-import decimal
 import numpy
 import scipy.misc, scipy.ndimage, scipy.optimize
 import cairo
@@ -18,6 +17,11 @@
 def numpyFromPil(img):
     return scipy.misc.fromimage(img, mode='RGB').transpose((1, 0, 2))
 
+def loadNumpy(path, thumb=(100, 100)):
+    img = Image.open(path)
+    img.thumbnail(thumb)
+    return numpyFromPil(img)
+
 def saveNumpy(path, img):
     scipy.misc.imsave(path, img.transpose((1, 0, 2)))
 
--- a/show/dance2017/networking.n3	Fri May 19 07:33:11 2017 +0000
+++ b/show/dance2017/networking.n3	Fri May 19 07:35:29 2017 +0000
@@ -6,6 +6,7 @@
 sh:netHome
   :webServer        <http://dash:8200/>;
   :patchReceiverUpdateHost "dash";
+  :captureDevice    <http://dash:8215/>;
   :curveCalc        <http://dash:8201/>;
   :collector        <http://dash:8202/>;
   :collectorZmq     <http://dash:8203/>;
@@ -14,13 +15,14 @@
   :keyboardComposer <http://dash:8205/>;
   :musicPlayer      <http://dash:8206/>;
   :oscDmxServer     <udp://dash:8207/>;
-  :paintServer      <http://dash:8213/>;
-  :picamserve       <http://10.2.0.5:8208/>;
+  :paintServer      <http://dash:8214/>;
+  :picamserve       <http://10.1.0.127:8208/>;
   :rdfdb            <http://dash:8209/>;
   :subComposer      <http://dash:8210/>;
   :subServer        <http://dash:8211/>;
   :vidref           <http://dash:8212/> .
 
+:captureDevice    :urlPath "captureDevice" .
 :curveCalc        :urlPath "curveCalc" .
 :dmxServer        :urlPath "dmxServer" .
 :effectEval       :urlPath "effectEval" .