Skip to content

Commit

Permalink
Conflict managed
Browse files Browse the repository at this point in the history
  • Loading branch information
jrasky committed Sep 22, 2012
2 parents 14dc947 + fbfac1e commit b3238f7
Show file tree
Hide file tree
Showing 30 changed files with 1,595 additions and 1,792 deletions.
56 changes: 49 additions & 7 deletions geocamTiePoint/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import os
import datetime
import re
import logging
try:
from cStringIO import StringIO
except ImportError:
Expand All @@ -25,7 +26,16 @@
from geocamUtil import anyjson as json
from geocamUtil.models.ExtrasDotField import ExtrasDotField

from geocamTiePoint import quadTree, settings
from geocamTiePoint import quadTree, transform, settings


# poor man's local memory cache for one quadtree tile generator. a
# common access pattern is that the same instance of the app gets
# multiple tile requests on the same quadtree. optimize for that case by
# keeping the generator in memory. note: an alternative approach would
# use the memcached cache, but that would get rid of much of the benefit
# in terms of serialization/deserialization.
cachedGeneratorG = {'key': None, 'value': None}


def getNewImageFileName(instance, filename):
Expand Down Expand Up @@ -115,19 +125,41 @@ def convertImageToRgbaIfNeeded(self, image):
self.imageData.contentType = 'image/png'
self.imageData.save()

def getImage(self):
im = PIL.Image.open(self.imageData.image.file)
self.convertImageToRgbaIfNeeded(im)
return im

def getGeneratorCacheKey(self):
return 'geocamTiePoint.QuadTreeGenerator.%s' % self.id

def getGeneratorWithCache(self):
global cachedGeneratorG
cachedGeneratorCopy = cachedGeneratorG
key = self.getGeneratorCacheKey()
if cachedGeneratorCopy['key'] == key:
logging.debug('getGeneratorWithCache hit %s', key)
result = cachedGeneratorCopy['value']
else:
logging.debug('getGeneratorWithCache miss %s', key)
result = self.getGenerator()
cachedGeneratorG = dict(key=key, value=result)
return result

def getGenerator(self):
image = PIL.Image.open(self.imageData.image.file)
self.convertImageToRgbaIfNeeded(image)
image = self.getImage()

if self.transform:
return (quadTree.WarpedQuadTreeGenerator
(image,
(self.id,
image,
json.loads(self.transform)))
else:
return quadTree.SimpleQuadTreeGenerator(image)
return quadTree.SimpleQuadTreeGenerator(self.id,
image)

def generateExportZip(self, exportName, metaJson):
gen = self.getGenerator()
gen = self.getGeneratorWithCache()
writer = quadTree.ZipWriter(exportName)
gen.writeQuadTree(writer)
writer.writeData('meta.json', dumps(metaJson))
Expand All @@ -152,6 +184,7 @@ class Overlay(models.Model):
alignedQuadTree = models.ForeignKey(QuadTree, null=True, blank=True,
related_name='alignedOverlays',
on_delete=models.SET_NULL)
isPublic = models.BooleanField(default=False)

# extras: a special JSON-format field that holds additional
# schema-free fields in the overlay model. Members of the field can
Expand Down Expand Up @@ -190,7 +223,11 @@ def getJsonDict(self):
'[Y].jpg'])
result['unalignedTilesZoomOffset'] = quadTree.ZOOM_OFFSET
if self.alignedQuadTree is not None:
result['alignedTilesUrl'] = reverse('geocamTiePoint_tile',
if self.isPublic:
urlName = 'geocamTiePoint_publicTile'
else:
urlName = 'geocamTiePoint_tile'
result['alignedTilesUrl'] = reverse(urlName,
args=[str(self.alignedQuadTree.id),
'[ZOOM]',
'[X]',
Expand Down Expand Up @@ -264,3 +301,8 @@ def generateExportZip(self):
(self.getExportName(),
self.getJsonDict()))
return self.alignedQuadTree.exportZip

def updateAlignment(self):
toPts, fromPts = transform.splitPoints(self.extras.points)
tform = transform.getTransform(toPts, fromPts)
self.extras.transform = tform.getJsonDict()
38 changes: 29 additions & 9 deletions geocamTiePoint/optimize.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,23 @@
# All Rights Reserved.
# __END_LICENSE__

"""
Levenberg-Marquardt optimization algorithm. This is a Python adaptation of
the C++ L-M implementation from the NASA Vision Workbench.
"""

import logging

from geocamTiePoint import settings

import numpy
from numpy.linalg import norm

try:
from scipy.optimize import leastsq
HAVE_SCIPY_LEASTSQ = True
except ImportError:
HAVE_SCIPY_LEASTSQ = False

if HAVE_SCIPY_LEASTSQ:
import threading
scipyLeastSqLockG = threading.Lock()

# default arguments
LM_DEFAULT_ABS_TOLERANCE = 1e-16
LM_DEFAULT_REL_TOLERANCE = 1e-16
Expand All @@ -40,7 +45,7 @@ def jacobian(x):
result = numpy.zeros((n, k))
for i in xrange(k):
xp = x.copy()
eps = 1e-7 + 1e-7 * x[i]
eps = 1e-7 + abs(1e-7 * x[i])
xp[i] += eps
yp = f(xp)
result[:, i] = (yp - y) / eps
Expand All @@ -64,6 +69,9 @@ def lm(y, f, x0,
in the neighborhood of x0. The default diff function is simple
subtraction. You can improve numerical stability by providing an
analytical jacobian for f.
This is a Python adaptation of the C++ L-M implementation from the
NASA Vision Workbench.
"""
logger = logging.getLogger('LM')
logger.setLevel(getattr(logging, settings.GEOCAM_TIE_POINT_OPTIMIZE_LOG_LEVEL))
Expand Down Expand Up @@ -106,9 +114,9 @@ def lm(y, f, x0,

J = jacobian(x)

delJ = -1.0 * Rinv * (J.transpose() * error)
delJ = -1.0 * Rinv * J.transpose().dot(error)
# Hessian of cost function (using Gauss-Newton approximation)
hessian = Rinv * (J.transpose() * J)
hessian = Rinv * J.transpose().dot(J)

iterations = 0
normTry = normStart + 1.0
Expand All @@ -121,7 +129,7 @@ def lm(y, f, x0,

# Solve for update
soln, _residues, _rank, _sngVal = numpy.linalg.lstsq(hessianLm, delJ)
deltaX = soln.diagonal()
deltaX = soln

# update parameter vector
xTry = x - deltaX
Expand Down Expand Up @@ -178,6 +186,18 @@ def lm(y, f, x0,
return x, status


def optimize(y, f, x0):
if HAVE_SCIPY_LEASTSQ:
# ack! scipy.optimize.leastsq is not thread-safe
scipyLeastSqLockG.acquire()
x, _cov = leastsq(lambda x: y - f(x), x0)
scipyLeastSqLockG.release()
return x
else:
x, _status = lm(y, f, x0)
return x


def test():
logging.basicConfig(level=logging.DEBUG)

Expand Down
Loading

0 comments on commit b3238f7

Please sign in to comment.