Changeset - fc5675f5b756
[Not reviewed]
default
0 3 2
Drew Perttula - 8 years ago 2017-05-19 07:35:29
drewp@bigasterisk.com
captureDevice tool for sweeping through light settings and grabbing pics
Ignore-this: 8f3171e4e29727e0aa78e43edff0a1d9
5 files changed with 181 insertions and 3 deletions:
0 comments (0 inline, 0 general)
bin/captureDevice
Show inline comments
 
new file 100644
 
#!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()
light9/networking.py
Show inline comments
 
@@ -36,6 +36,7 @@ class ServiceAddress(object):
 
    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'])
light9/paint/capture.py
Show inline comments
 
new file 100644
 
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"""
 
        
light9/paint/solve.py
Show inline comments
 
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 numpyFromCairo(surface):
 
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)))
 

	
show/dance2017/networking.n3
Show inline comments
 
@@ -6,6 +6,7 @@ show:dance2017 :networking sh:netHome .
 
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 @@ sh:netHome
 
  :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" .
0 comments (0 inline, 0 general)