Mercurial > code > home > repos > light9
diff bin/attic/captureDevice @ 2376:4556eebe5d73
topdir reorgs; let pdm have its src/ dir; separate vite area from light9/
author | drewp@bigasterisk.com |
---|---|
date | Sun, 12 May 2024 19:02:10 -0700 |
parents | bin/captureDevice@8516a39eedc9 |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bin/attic/captureDevice Sun May 12 19:02:10 2024 -0700 @@ -0,0 +1,199 @@ +#!bin/python +""" +Operate a motorized light and take pictures of it in every position. +""" +from rdflib import URIRef +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks, Deferred + +import logging +import optparse +import os +import time +import treq +import cyclone.web, cyclone.websocket, cyclone.httpclient +from light9.metrics import metrics, metricsRoute +from run_local import log +from cycloneerr import PrettyErrorHandler + +from light9.namespaces import L9, RDF +from light9 import networking, showconfig +from rdfdb.syncedgraph import SyncedGraph +from light9.paint.capture import writeCaptureDescription +from light9.effect.settings import DeviceSettings +from light9.collector.collector_client import sendToCollector +from rdfdb.patch import Patch +from light9.zmqtransport import parseJsonMessage + + + +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) + + +def deferSleep(sec): + d = Deferred() + reactor.callLater(sec, d.callback, None) + return d + + +class Capture(object): + firstMoveTime = 3 + settleTime = .5 + + def __init__(self, graph, dev): + self.graph = graph + self.dev = dev + + def steps(a, b, n): + return [round(a + (b - a) * i / n, 5) for i in range(n)] + + startTime = time.time() + self.captureId = 'cap%s' % (int(startTime) - 1495170000) + self.toGather = [] + + #quantum + rxSteps = steps(.06, .952, 10) + rySteps = steps(0.1, .77, 5) + zoomSteps = steps(.12, .85, 3) + # aura + rxSteps = steps(0.15, .95, 10) + rySteps = steps(0, .9, 5) + zoomSteps = steps(.6, .9, 3) + + row = 0 + for ry in rySteps: + xSteps = rxSteps[:] + if row % 2: + xSteps.reverse() + row += 1 + for rx in xSteps: + for zoom in zoomSteps: + self.toGather.append( + DeviceSettings( + graph, + [ + (dev, L9['rx'], rx), + (dev, L9['ry'], ry), + (dev, L9['color'], '#ffffff'), + (dev, L9['zoom'], zoom), + #(dev, L9['focus'], 0.13), + ])) + + self.devTail = dev.rsplit('/')[-1] + self.session = URIRef('/'.join( + [showconfig.showUri(), 'capture', self.devTail, self.captureId])) + self.ctx = URIRef(self.session + '/index') + + self.graph.patch( + Patch(addQuads=[ + (self.session, RDF.type, L9['CaptureSession'], self.ctx), + ])) + + self.numPics = 0 + self.settingsCache = set() + self.step().addErrback(log.error) + + def off(self): + return sendToCollector(client='captureDevice', + session='main', + settings=DeviceSettings(self.graph, [])) + + @inlineCallbacks + def step(self): + if not self.toGather: + yield self.off() + yield deferSleep(1) + reactor.stop() + return + settings = self.toGather.pop() + + log.info('[%s left] move to %r', len(self.toGather), settings) + yield sendToCollector(client='captureDevice', + session='main', + settings=settings) + + yield deferSleep(self.firstMoveTime if self.numPics == + 0 else self.settleTime) + + picId = 'pic%s' % self.numPics + path = '/'.join(['capture', self.devTail, self.captureId, picId + ]) + '.jpg' + uri = URIRef(self.session + '/' + picId) + + yield camera.takePic(uri, os.path.join(showconfig.root(), path)) + self.numPics += 1 + + writeCaptureDescription(self.graph, self.ctx, self.session, uri, + self.dev, path, self.settingsCache, settings) + + reactor.callLater(0, self.step) + + +camera = Camera( + 'http://plus:8200/picamserve/pic?res=1080&resize=800&iso=800&redgain=1.6&bluegain=1.6&shutter=60000&x=0&w=1&y=0&h=.952' +) + + +class Attrs(PrettyErrorHandler, cyclone.web.RequestHandler): + + @metrics('set_attr').time() + def put(self): + client, clientSession, settings, sendTime = parseJsonMessage( + self.request.body) + self.set_status(202) + + +def launch(graph): + + cap = Capture(graph, dev=L9['device/aura5']) + reactor.listenTCP(networking.captureDevice.port, + cyclone.web.Application(handlers=[ + (r'/()', cyclone.web.StaticFileHandler, { + "path": "light9/web", + "default_filename": "captureDevice.html" + }), + metricsRoute(), + ]), + interface='::', + cap=cap) + 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()