Changeset - 0480fc790527
[Not reviewed]
default
0 6 0
Drew Perttula - 8 years ago 2017-05-27 12:22:28
drewp@bigasterisk.com
paint now looks for best match
Ignore-this: 5604a2a2181624a0612dddf1afa32985
6 files changed with 93 insertions and 178 deletions:
0 comments (0 inline, 0 general)
bin/effecteval
Show inline comments
 
@@ -66,13 +66,12 @@ class SongEffects(PrettyErrorHandler, cy
 
        
 
        note = self.get_argument('note', default=None)
 
        if note is not None:
 
            note = URIRef(note)
 

	
 
        log.info("adding to %s", song)
 

	
 
        note, p = yield songNotePatch(self.settings.graph, dropped, song, event, ctx=song, note=note)
 
        
 
        self.settings.graph.patch(p)
 
        self.settings.graph.suggestPrefixes({'song': URIRef(song + '/')})
 
        self.write(json.dumps({'note': note}))
 
        
bin/paintserver
Show inline comments
 
@@ -11,24 +11,34 @@ from greplin import scales
 
import optparse, sys, logging
 
import cyclone.web
 
from rdflib import URIRef
 
from light9.rdfdb import clientsession
 
import light9.paint.solve
 
from lib.cycloneerr import PrettyErrorHandler
 
from light9.namespaces import RDF, L9, DEV
 

	
 

	
 
class Solve(PrettyErrorHandler, cyclone.web.RequestHandler):
 
    def post(self):
 
        painting = json.loads(self.request.body)
 
        reload(light9.paint.solve)
 
        solver = light9.paint.solve.Solver(self.settings.graph)
 
        solver.loadSamples()
 
        with self.settings.stats.solve.time():
 
            out = solver.solve(painting)
 
            layers = solver.simulationLayers(out)
 
        self.write(json.dumps({'layers': layers, 'out': out}))
 
            img = solver.draw(painting)
 
            sample = solver.bestMatch(img)
 
            with self.settings.graph.currentState() as g:
 
                bestPath = 'show/dance2017/cam/test/%s' % g.value(sample, L9['path'])
 
            #out = solver.solve(painting)
 
            #layers = solver.simulationLayers(out)
 
            
 
        self.write(json.dumps({
 
            'bestMatch': {'uri': sample, 'path': bestPath},
 
        #    'layers': layers,
 
        #    'out': out,
 
        }))
 

	
 
class App(object):
 
    def __init__(self, show, session):
 
        self.show = show
 
        self.session = session
 

	
light9/paint/solve.py
Show inline comments
 
@@ -61,47 +61,65 @@ class Solver(object):
 
                
 
                key = (samp, g.value(samp, L9['path']).toPython().encode('utf8'))
 
                self.sampleSettings[key] = DeviceSettings.fromResource(self.graph, samp)
 

	
 
    def _blur(self, img):
 
        return scipy.ndimage.gaussian_filter(img, 10, 0, mode='nearest')
 
                
 
    def draw(self, painting, w, h):
 

	
 
    def draw(self, painting):
 
        return self._draw(painting, 100, 48)
 
        
 
    def _draw(self, painting, w, h):
 
        surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h)
 
        ctx = cairo.Context(surface)
 
        ctx.rectangle(0, 0, w, h)
 
        ctx.fill()
 
        
 
        ctx.set_line_cap(cairo.LINE_CAP_ROUND)
 
        ctx.set_line_width(20)
 
        ctx.set_line_width(20) # ?
 
        for stroke in painting['strokes']:
 
            for pt in stroke['pts']:
 
                op = ctx.move_to if pt is stroke['pts'][0] else ctx.line_to
 
                op(pt[0] / 4, pt[1] / 4) # todo scale
 
                op(pt[0] * w, pt[1] * h)
 

	
 
            r,g,b = parseHex(stroke['color'])
 
            ctx.set_source_rgb(r / 255, g / 255, b / 255)
 
            ctx.stroke()
 
        
 
        #surface.write_to_png('/tmp/surf.png')
 
        surface.write_to_png('/tmp/surf.png')
 
        return numpyFromCairo(surface)
 

	
 

	
 
    def _imgDist(self, a, b):
 
        return numpy.sum(numpy.absolute(a - b), axis=None)
 
        
 
    def bestMatch(self, img):
 
        results = []
 
        for uri, img2 in self.samples.iteritems():
 
            results.append((self._imgDist(img, img2), uri, img2))
 
        results.sort()
 
        print 'results:'
 
        for r in results:
 
            print r
 
        saveNumpy('/tmp/bestsamp.png', results[-1][2])
 
        return results[-1][1]
 
        
 
    def solve(self, painting):
 
        """
 
        given strokes of colors on a photo of the stage, figure out the
 
        best light DeviceSettings to match the image
 
        """
 
        pic0 = self.draw(painting, 100, 48).astype(numpy.float)
 
        pic0 = self.draw(painting).astype(numpy.float)
 
        pic0Blur = self._blur(pic0)
 
        saveNumpy('/tmp/sample_paint_%s.png' % len(painting['strokes']),
 
                  pic0Blur)
 
        sampleDist = {}
 
        for sample, picSample in sorted(self.blurredSamples.items()):
 
            #saveNumpy('/tmp/sample_%s.png' % sample.split('/')[-1],
 
            #          f(picSample))
 
            dist = numpy.sum(numpy.absolute(pic0Blur - picSample), axis=None)
 
            dist = self._imgDist(pic0Blur, picSample)
 
            sampleDist[sample] = dist
 
        results = [(d, uri) for uri, d in sampleDist.items()]
 
        results.sort()
 

	
 
        sample = results[0][1]
 

	
 
@@ -116,13 +134,13 @@ class Solver(object):
 

	
 
        s = DeviceSettings.fromResource(self.graph, sample)
 
        # missing color scale, but it was wrong to operate on all devs at once
 
        return s
 

	
 
    def solveBrute(self, painting):
 
        pic0 = self.draw(painting, 100, 48).astype(numpy.float)
 
        pic0 = self.draw(painting).astype(numpy.float)
 

	
 
        colorSteps = 3
 
        colorStep = 1. / colorSteps
 

	
 
        dims = [
 
            (DEV['aura1'], L9['rx'], [slice(.2, .7+.1, .1)]),
light9/web/paint/paint-elements.coffee
Show inline comments
 
@@ -6,75 +6,102 @@ class Painting
 
    @strokes.push({pts: pts, color: color})
 

	
 
  getDoc: ->
 
    {strokes: @strokes}
 

	
 
class Stroke
 
  constructor: (pos, @color) ->
 
  constructor: (pos, @color, @size) ->
 
    @path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
 
    @path.setAttributeNS(null, 'd', "M #{pos[0]} #{pos[1]}")
 
    @path.setAttributeNS(null, 'd', "M #{pos[0]*@size[0]} #{pos[1]*@size[1]}")
 
    @pts = [pos]
 
    @lastPos = pos
 

	
 
  appendElem: (parent) ->
 
    parent.appendChild(@path)
 
    
 
  move: (pos) ->
 
    if Math.hypot(pos[0] - @lastPos[0], pos[1] - @lastPos[1]) < 30
 
    if Math.hypot(pos[0] - @lastPos[0], pos[1] - @lastPos[1]) < .02
 
      return
 
    @path.attributes.d.value += " L #{pos[0]} #{pos[1]}"
 
    @path.attributes.d.value += " L #{pos[0]*@size[0]} #{pos[1]*@size[1]}"
 
    @pts.push(pos)
 
    @lastPos = pos
 
  
 

	
 
Polymer
 
  is: "light9-paint"
 
  is: "light9-paint-canvas"
 
  behaviors: [ Polymer.IronResizableBehavior ]
 
  listeners: 'iron-resize': 'onResize'
 
  properties: {
 
    layers: { type: Object }
 
    bg: { type: String },
 
    painting: { type: Object } # output
 
  }
 
  ready: ->
 
    @onResize()
 
    @painting = new Painting()
 
    @$.paint.addEventListener('mousedown', @onDown.bind(@))
 
    @$.paint.addEventListener('mousemove', @onMove.bind(@))
 
    @$.paint.addEventListener('mouseup', @onUp.bind(@))
 
    @$.paint.addEventListener('touchstart', @onDown.bind(@))
 
    @$.paint.addEventListener('touchmove', @onMove.bind(@))
 
    @$.paint.addEventListener('touchend', @onUp.bind(@))
 

	
 
  evPos: (ev) ->
 
    return (if ev.touches?.length? then [Math.round(ev.touches[0].clientX),
 
                                         Math.round(ev.touches[0].clientY)] else [ev.x, ev.y]) 
 
    px = (if ev.touches?.length? then [Math.round(ev.touches[0].clientX),
 
                                       Math.round(ev.touches[0].clientY)] else [ev.x, ev.y])
 
    return [px[0] / @size[0], px[1] / @size[1]]
 

	
 
  onDown: (ev) ->
 
    # if it's on an existing one, do selection
 
    @stroke = new Stroke(@evPos(ev), '#aaaaaa')
 
    @stroke = new Stroke(@evPos(ev), '#aaaaaa', @size)
 
    @stroke.appendElem(@$.paint)
 
    @scopeSubtree(@$.paint)
 

	
 
  onMove: (ev) ->
 
    # ..or move selection
 
    return unless @stroke
 
    @stroke.move(@evPos(ev))
 

	
 
  onUp: (ev) ->
 
    return unless @stroke
 
    @painting.addStroke(@stroke.pts, @stroke.color)
 
    @$.solve.body = JSON.stringify(@painting.getDoc())
 
    @$.solve.generateRequest()
 
    @stroke = null
 
    
 
    @notifyPath('painting.strokes.length') # not working
 
    @fire('paintingChanged', @painting)
 

	
 
  onResize: (ev) ->
 
    @$.paint.attributes.viewBox.value = "0 0 #{ev.target.offsetWidth} 500"
 
    @size = [@$.parent.offsetWidth, @$.parent.offsetHeight]
 
    @$.paint.attributes.viewBox.value = "0 0 #{@size[0]} #{@size[1]}"
 

	
 

	
 
Polymer
 
  is: "light9-simulation"
 
  properties: {
 
    layers: { type: Object }
 
    solution: { type: Object }
 
  }
 
  listeners: [
 
    "onLayers(layers)"
 
  ]
 
  ready: ->
 
    null
 
  onLayers: (layers) ->
 
    log('upd', layers)
 
\ No newline at end of file
 
    log('upd', layers)
 

	
 
    
 
Polymer
 
  is: "light9-paint"
 
  properties: {
 
    painting: { type: Object }
 
  }
 

	
 
  ready: () ->
 
    # couldn't make it work to bind to painting's notifyPath events
 
    @$.canvas.addEventListener('paintingChanged', @paintingChanged.bind(@))
 
    @$.solve.addEventListener('response', @onSolve.bind(@))
 
    
 
  paintingChanged: (ev) ->
 
    @painting = ev.detail
 
    @$.solve.body = JSON.stringify(@painting.getDoc())
 
    @$.solve.generateRequest()
 

	
 
  onSolve: (response) ->
 
    console.log(response)
 
\ No newline at end of file
light9/web/paint/paint-elements.html
Show inline comments
 
<link rel="import" href="/lib/polymer/polymer.html">
 
<link rel="import" href="/lib/iron-resizable-behavior/iron-resizable-behavior.html">
 
<link rel="import" href="/lib/iron-ajax/iron-ajax.html">
 
<link rel="import" href="paint-report-elements.html">
 

	
 
<dom-module id="light9-paint">
 
<dom-module id="light9-paint-canvas">
 
  <template>
 
    <style>
 
     :host {
 
         display: block;
 
     }
 
     #parent {
 
         position: relative;
 
         height: 500px;
 
     }
 
     #parent > * {
 
         position: absolute;
 
@@ -31,15 +35,14 @@
 
      tools:
 
      - hover spot
 
      - paint
 
      - erase
 
    </div>
 
    
 
    <iron-ajax id="solve" method="POST" url="../paintServer/solve"></iron-ajax>
 
    <div id="parent">
 
      <img src="bg2.jpg">
 
      <img src="{{bg}}">
 
      <svg id="paint" viewBox="0 0 500 221">
 
        <defs id="defs12751">
 
          <filter
 
              style="color-interpolation-filters:sRGB"
 
              id="blur"
 
              x="-1.0"
 
@@ -73,163 +76,21 @@
 
                 k4="0"
 
                 ></feComposite> -->
 
          </filter>
 
        </defs>
 
      </svg>
 
    </div>
 

	
 
    
 
    
 
    <light9-simulation layers="{{layers}}"></light9-simulation>
 
  </template>
 
</dom-module>
 

	
 
<!-- merge more with light9-collector-device -->
 
<dom-module id="light9-device-settings">
 
  <template>
 
    <style>
 
     :host {
 
         display: block;
 
         break-inside: avoid-column;
 
         border: 2px solid gray;
 
         padding: 8px;
 
     }
 
     td.nonzero {
 
         background: #310202;
 
         color: #e25757;
 
     }
 
     td.full {
 
         background: #2b0000;
 
         color: red;
 
         font-weight: bold;
 
     }
 
    </style>
 
    <h3>{{label}}</h3>
 
    <table class="borders">
 
      <tr>
 
        <th>device attr</th>
 
        <th>value</th>
 
      </tr>
 
      <template is="dom-repeat" items="{{attrs}}">
 
        <tr>
 
          <td>{{item.attr}}</td>
 
          <td class$="{{item.valClass}}">{{item.val}}</td>
 
        </tr>
 
      </template>
 

	
 
  </template>
 
  <script>
 
   HTMLImports.whenReady(function () {
 
       Polymer({
 
           is: "light9-device-settings",
 
           properties: {
 
               label: {type: String, notify: true},
 
               attrs: {type: Array, notify: true},
 

	
 
           },
 
           ready: function() {
 

	
 
               this.label = "aura2";
 
               this.attrs = [
 
                   {attr: 'rx', val: .03},
 
                   {attr: 'color', val: '#ffe897'},
 
               ];
 
                    
 
           }
 
       });
 
   });
 
  </script>
 
 
 
</dom-module>
 

	
 
<dom-module id="light9-capture-image">
 
<dom-module id="light9-paint">
 
  <template>
 
    <style>
 
     :host { display: block; }
 
    </style>
 
    <div>{{name}}</div>
 
    <div><img width="100" src="../{{path}}"></div>
 
  </template>
 
  <script>
 
   HTMLImports.whenReady(function () {
 
       Polymer({
 
           is: "light9-capture-image",
 
           properties: {
 
               name: { type: String },
 
               path: { type: String },
 
           }
 
       });
 
   });
 
  </script>
 
</dom-module>
 

	
 
<dom-module id="light9-simulation">
 
  <template>
 
    <style>
 
     #solutions { display: flex; margin: 20px; }
 
     #single-light { margin-right: 70px; }
 
     #multi-light {}
 
     #breakdown { position: relative; }
 
     #sources { display: flex; }
 
     #solution { display: flex; margin-top: 80px; }
 
     #connectors { position: absolute; width: 100%; height: 100%; }
 
     #connectors path { stroke: #615c54; stroke-width: 3px; }
 
    </style>
 

	
 
    <div id="solutions">
 
      <div id="single-light">
 
        <div>Single pic best match:</div>
 

	
 
        <light9-capture-image name="mac2" path="show/dance2017/capture/moving1/cap258592/pic1.jpg"></light9-capture-image>
 
    <light9-paint-canvas id="canvas" bg="bg2.jpg" painting="{{painting}}"></light9-paint-canvas>
 

	
 
        <div>Error: 280844</div>
 
        
 
        <div>DeviceSettings</div>
 
        <light9-device-settings></light9-device-settings>
 
      </div>
 

	
 
      <!-- existing effect best match? -->
 
      
 
      <div id="multi-light">
 
        Created from multiple lights:
 

	
 
        <div id="breakdown">
 
          <svg id="connectors">
 
            <g>
 
              <path d="M 112,241 L 150,280"></path>
 
              <path d="M 332,241 L 150,280"></path>
 
              <path d="M 532,241 L 150,280"></path>
 
              <path d="M 732,241 L 150,280"></path>
 
            </g>
 
            
 
          </svg>
 
          <div id="sources">
 
            <div>
 
              <light9-capture-image name="aura1" path="show/dance2017/capture/moving1/cap258592/pic1.jpg"></light9-capture-image>
 
              <light9-device-settings></light9-device-settings>
 
            </div>
 
            <div>
 
              <light9-capture-image name="aura2" path="show/dance2017/capture/moving1/cap258592/pic1.jpg"></light9-capture-image>
 
              <light9-device-settings></light9-device-settings>
 
            </div>
 
            <div>
 
              <light9-capture-image name="aura3" path="show/dance2017/capture/moving1/cap258592/pic1.jpg"></light9-capture-image>
 
              <light9-device-settings></light9-device-settings>
 
            </div>
 
            <div>
 
              <light9-capture-image name="aura4" path="show/dance2017/capture/moving1/cap258592/pic1.jpg"></light9-capture-image>
 
              <light9-device-settings></light9-device-settings>
 
            </div>
 
          </div>
 
          
 
          <div id="solution">
 
            <div> <div>combined</div><div><img width="150" src="../show/dance2017/capture/moving1/cap258592/pic1.jpg"></div><div>error 9980</div></div>
 
            <div> <div>residual</div><div><img width="150" src="../show/dance2017/capture/moving1/cap258592/pic1.jpg"></div></div>
 
          </div>
 
        </div>
 

	
 
        Save as effect named <input> <button>Save</button>
 
      </div>
 

	
 
    <iron-ajax id="solve" method="POST" url="../paintServer/solve" last-response="{{solve}}"></iron-ajax>
 
    
 
    <light9-simulation solution="{{solve}}" layers="{{layers}}"></light9-simulation>
 
  </template>
 
</dom-module>
 

	
 
<script src="paint-elements.js"></script>
light9/web/paint/paint-report-elements.html
Show inline comments
 
@@ -17,13 +17,13 @@
 

	
 
    <div id="solutions">
 
      <div id="single-light">
 
        <div>Single pic best match:</div>
 

	
 
        <!-- drag this img to make an effect out of just it -->
 
        <light9-capture-image name="mac2" path="show/dance2017/capture/moving1/cap258592/pic1.jpg"></light9-capture-image>
 
        <light9-capture-image name="mac2" path="{{solution.bestMatch.path}}"></light9-capture-image>
 

	
 
        <div>Error: 280844</div>
 
        
 
        <light9-device-settings></light9-device-settings>
 
      </div>
 

	
0 comments (0 inline, 0 general)