changeset 1575:0d2247ec8f49

bestMatch works in paintserver Ignore-this: c237f9f9a3dafe3ccfa3087423a5259a
author Drew Perttula <drewp@bigasterisk.com>
date Mon, 29 May 2017 07:59:59 +0000
parents a261a4bc97a0
children ef7ddef3acb5
files .boring bin/paintserver light9/paint/capture.py light9/paint/solve.py light9/paint/solve_test.py light9/rdfdb/currentstategraphapi.py light9/web/paint/paint-report-elements.html
diffstat 7 files changed, 72 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/.boring	Mon May 29 06:09:50 2017 +0000
+++ b/.boring	Mon May 29 07:59:59 2017 +0000
@@ -153,3 +153,4 @@
 
 # temporary!
 rgbled/build-nano328/
+^show/dance..../capture
--- a/bin/paintserver	Mon May 29 06:09:50 2017 +0000
+++ b/bin/paintserver	Mon May 29 07:59:59 2017 +0000
@@ -20,23 +20,26 @@
 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():
-            img = solver.draw(painting)
-            sample = solver.bestMatch(img)
+            img = self.settings.solver.draw(painting)
+            sample, sampleDist = self.settings.solver.bestMatch(img)
             with self.settings.graph.currentState() as g:
-                bestPath = 'show/dance2017/cam/test/%s' % g.value(sample, L9['path'])
+                bestPath = g.value(sample, L9['imagePath']).replace(L9[''], '')
             #out = solver.solve(painting)
             #layers = solver.simulationLayers(out)
             
         self.write(json.dumps({
-            'bestMatch': {'uri': sample, 'path': bestPath},
+            'bestMatch': {'uri': sample, 'path': bestPath, 'dist': sampleDist},
         #    'layers': layers,
         #    'out': out,
         }))
 
+    def reloadSolver(self):
+        reload(light9.paint.solve)
+        self.settings.solver = light9.paint.solve.Solver(self.settings.graph)
+        self.settings.solver.loadSamples()
+
+        
 class App(object):
     def __init__(self, show, session):
         self.show = show
@@ -44,17 +47,22 @@
 
         self.graph = SyncedGraph(networking.rdfdb.url, "paintServer")
         self.graph.initiallySynced.addCallback(self.launch)
+        
+        self.stats = scales.collection('/', scales.PmfStat('solve'),
+                                       )
+       
+    def launch(self, *args):
 
-        self.stats = scales.collection('/',
-                                       scales.PmfStat('solve'),
-                                       )
-    def launch(self, *args):
+        self.solver = light9.paint.solve.Solver(self.graph)
+        self.solver.loadSamples()
+        
         self.cycloneApp = cyclone.web.Application(handlers=[
             (r'/stats', StatsForCyclone),
             (r'/solve', Solve),
         ],
                                                   debug=True,
                                                   graph=self.graph,
+                                                  solver=self.solver,
                                                   stats=self.stats)
         reactor.listenTCP(networking.paintServer.port, self.cycloneApp)
         log.info("listening on %s" % networking.paintServer.port)
--- a/light9/paint/capture.py	Mon May 29 06:09:50 2017 +0000
+++ b/light9/paint/capture.py	Mon May 29 07:59:59 2017 +0000
@@ -15,6 +15,7 @@
         (uri, RDF.type, L9['LightSample'], ctx),
         (uri, L9['imagePath'], URIRef('/'.join([showconfig.showUri(), relOutPath])), ctx),
         ]))
+    graph.suggestPrefixes('cap', URIRef(uri.rsplit('/', 1)[0] + '/'))
     
 class CaptureLoader(object):
     def __init__(self, graph):
--- a/light9/paint/solve.py	Mon May 29 06:09:50 2017 +0000
+++ b/light9/paint/solve.py	Mon May 29 07:59:59 2017 +0000
@@ -47,12 +47,30 @@
 
 def brightest(img):
     return numpy.amax(img, axis=(0, 1))
+    
+class ImageDist(object):
+    def __init__(self, img1):
+        self.a = img1.reshape((-1,))
+        self.d = 255 * 255 * self.a.shape[0]
 
+    def distanceTo(self, img2):
+        b = img2.reshape((-1,))
+        return 1 - numpy.dot(self.a, b) / self.d
 
+class ImageDistAbs(object):
+    def __init__(self, img1):
+        self.a = img1
+        self.maxDist = img1.shape[0] * img1.shape[1] * img1.shape[2] * 255
+
+    def distanceTo(self, img2):
+        return numpy.sum(numpy.absolute(self.a - img2), axis=None) / self.maxDist
+
+        
 class Solver(object):
-    def __init__(self, graph):
+    def __init__(self, graph, imgSize=(100, 75)):
         self.graph = graph
-        self.samples = {} # uri: Image array
+        self.imgSize = imgSize
+        self.samples = {} # uri: Image array (float 0-255)
         self.fromPath = {} # imagePath: image array
         self.blurredSamples = {}
         self.sampleSettings = {} # (uri, path): DeviceSettings
@@ -63,17 +81,18 @@
         with self.graph.currentState() as g:
             for samp in g.subjects(RDF.type, L9['LightSample']):
                 pathUri = g.value(samp, L9['imagePath'])
-                self.samples[samp] = self.fromPath[pathUri] = loadNumpy(pathUri.replace(L9[''], ''))
+                self.samples[samp] = self.fromPath[pathUri] = loadNumpy(pathUri.replace(L9[''], '')).astype(float)
                 self.blurredSamples[samp] = self._blur(self.samples[samp])
                 
                 key = (samp, pathUri)
                 self.sampleSettings[key] = DeviceSettings.fromResource(self.graph, samp)
-
+        log.info('loaded %s samples', len(self.samples))
+                
     def _blur(self, img):
         return scipy.ndimage.gaussian_filter(img, 10, 0, mode='nearest')
 
     def draw(self, painting):
-        return self._draw(painting, 100, 48)
+        return self._draw(painting, self.imgSize[0], self.imgSize[1])
         
     def _draw(self, painting, w, h):
         surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h)
@@ -92,24 +111,25 @@
             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):
         """the one sample that best matches this image"""
+        #img = self._blur(img)
         results = []
-        for uri, img2 in self.samples.iteritems():
-            results.append((self._imgDist(img, img2), uri, img2))
+        dist = ImageDist(img)
+        for uri, img2 in sorted(self.samples.items()):
+            if img.shape != img2.shape:
+                continue
+            results.append((dist.distanceTo(img2), uri, img2))
         results.sort()
-        log.info('results:')
-        for d,u,i in results:
-            log.info('%s %g', u, d)
-        saveNumpy('/tmp/bestsamp.png', results[0][2])
-        return results[0][1]
+        topDist, topUri, topImg = results[0]
+       
+        #saveNumpy('/tmp/best_in.png', img)
+        #saveNumpy('/tmp/best_out.png', topImg)
+        #saveNumpy('/tmp/mult.png', topImg / 255 * img)
+        return topUri, topDist
         
     def solve(self, painting):
         """
--- a/light9/paint/solve_test.py	Mon May 29 06:09:50 2017 +0000
+++ b/light9/paint/solve_test.py	Mon May 29 07:59:59 2017 +0000
@@ -10,7 +10,7 @@
     def setUp(self):
         self.graph = LocalSyncedGraph(files=['show/dance2017/cam/test/lightConfig.n3',
                                              'show/dance2017/cam/test/bg.n3'])
-        self.solver = solve.Solver(self.graph)
+        self.solver = solve.Solver(self.graph, imgSize=(100, 48))
         self.solver.loadSamples()
         self.solveMethod = self.solver.solve
 
@@ -39,7 +39,7 @@
     def setUp(self):
         self.graph = LocalSyncedGraph(files=['show/dance2017/cam/test/lightConfig.n3',
                                              'show/dance2017/cam/test/bg.n3'])
-        self.solver = solve.Solver(self.graph)
+        self.solver = solve.Solver(self.graph, imgSize=(100, 48))
         self.solver.loadSamples()
         
     def testBlack(self):
@@ -79,7 +79,7 @@
     def setUp(self):
         graph = LocalSyncedGraph(files=['show/dance2017/cam/test/lightConfig.n3',
                                         'show/dance2017/cam/test/bg.n3'])
-        self.solver = solve.Solver(graph)
+        self.solver = solve.Solver(graph, imgSize=(100, 48))
         self.solver.loadSamples()
     def test(self):
         out = self.solver.combineImages(layers=[
@@ -94,12 +94,13 @@
     def setUp(self):
         graph = LocalSyncedGraph(files=['show/dance2017/cam/test/lightConfig.n3',
                                         'show/dance2017/cam/test/bg.n3'])
-        self.solver = solve.Solver(graph)
+        self.solver = solve.Solver(graph, imgSize=(100, 48))
         self.solver.loadSamples()
         
     def testRightSide(self):
         drawingOnRight = {"strokes":[{"pts":[[0.875,0.64],[0.854,0.644]],
                                       "color":"#aaaaaa"}]}
         drawImg = self.solver.draw(drawingOnRight)
-        match = self.solver.bestMatch(drawImg)
+        match, dist = self.solver.bestMatch(drawImg)
         self.assertEqual(L9['sample5'], match)
+        self.assertAlmostEqual(0.06678758, dist)
--- a/light9/rdfdb/currentstategraphapi.py	Mon May 29 06:09:50 2017 +0000
+++ b/light9/rdfdb/currentstategraphapi.py	Mon May 29 07:59:59 2017 +0000
@@ -14,6 +14,9 @@
             return getattr(self.graph, attr)
         raise TypeError("can't access %r of read-only graph" % attr)
 
+    def __len__(self):
+        return len(self.graph)
+
 
 class CurrentStateGraphApi(object):
     """
--- a/light9/web/paint/paint-report-elements.html	Mon May 29 06:09:50 2017 +0000
+++ b/light9/web/paint/paint-report-elements.html	Mon May 29 07:59:59 2017 +0000
@@ -22,7 +22,7 @@
         <!-- drag this img to make an effect out of just it -->
         <light9-capture-image name="mac2" path="{{solution.bestMatch.path}}"></light9-capture-image>
 
-        <div>Error: 280844</div>
+        <div>Error: {{solution.bestMatch.dist}}</div>
         
         <light9-device-settings></light9-device-settings>
       </div>
@@ -140,6 +140,10 @@
   <template>
     <style>
      :host { display: block; }
+       img {
+         outline: 1px solid #232323;
+         margin: 5px;
+     }
     </style>
     <div>{{name}}</div>
     <div><img width="100" src="../{{path}}"></div>