My facebook app lets the user upload an image, select what the image is (eyes, nose, mouth or other body part) and then can combine by selecting three random images by category and that's works fine and the code looks ok and readable though not very advanced:
class CyberFazeHandler(BaseHandler):
def get_random_image(self, category):
fileinfos = FileInfo.all().filter("category =", category)
return fileinfos[random.randint(0, fileinfos.count()-1)]
def get(self):
eyes_image = self.get_random_image(category="eyes")
nose_image = self.get_random_image(category="nose")
mouth_image = self.get_random_image(category="mouth")
eyes_data = None
try:
eyes_data = blobstore.fetch_data(eyes_image.blob.key(), 0, 50000)
except Exception, e:
self.set_message(type=u'error',
content=u'Could not find eyes data for file '+str(eyes_image.key().id())+' (' + unicode(e) + u')')
eyes_img = None
try:
eyes_img = images.Image(image_data=eyes_data)
...now I just fetch 3 random images and then combine then in the template:
<a href="/file/{{eyes_image.key.id}}"><img src="{{eyes_url}}"></a><br>
<a href="/file/{{nose_image.key.id}}"><img src="{{nose_url}}"></a><br>
<a href="/file/{{mouth_image.key.id}}"><img src="{{mouth_url}}"></a>
Could this be improved by sending a composite image combining the three images into one? With the advantage that everything on the image will load at the same time and that it will be saved already if a randomization comes up next time the result is already saved. What do you think?
Thank you (the application is apps.facebook.com/cyberfaze you may inspect I did for fun and learning)
The entire class is
class CyberFazeHandler(BaseHandler):
def get_random_image(self, category):
fileinfos = FileInfo.all().filter("category =", category)
return fileinfos[random.randint(0, fileinfos.count()-1)] #optimize
def get(self):
eyes_image = self.get_random_image(category="eyes")
nose_image = self.get_random_image(category="nose")
mouth_image = self.get_random_image(category="mouth")
eyes_data = None
try:
eyes_data = blobstore.fetch_data(eyes_image.blob.key(), 0, 50000)
except Exception, e:
self.set_message(type=u'error',
content=u'Could not find eyes data for file '+str(eyes_image.key().id())+' (' + unicode(e) + u')')
eyes_img = None
try:
eyes_img = images.Image(image_data=eyes_data)
except Exception, e:
self.set_message(type=u'error',
content=u'Could not find eyes img for file '+str(eyes_image.key().id())+' (' + unicode(e) + u')')
nose_data = None
try:
nose_data = blobstore.fetch_data(nose_image.blob.key(), 0, 50000)
except Exception, e:
self.set_message(type=u'error',
content=u'Could not find nose data for file '+str(nose_image.key().id())+' (' + unicode(e) + u')')
nose_img = None
try:
nose_img = images.Image(image_data=nose_data)
except Exception, e:
self.set_message(type=u'error',
content=u'Could not find nose img for file '+str(nose_image.key().id())+' (' + unicode(e) + u')')
mouth_data = None
try:
mouth_data = blobstore.fetch_data(mouth_image.blob.key(), 0, 50000)
except Exception, e:
self.set_message(type=u'error',
content=u'Could not find mouth data for file '+str(eyes_image.key().id())+' (' + unicode(e) + u')')
mouth_img = None
try:
mouth_img = images.Image(image_data=mouth_data)
except Exception, e:
self.set_message(type=u'error',
content=u'Could not find mouth img for file '+str(mouth_image.key().id())+' (' + unicode(e) + u')')
minimum = min(int(eyes_img.width), int(nose_img.width), int(mouth_img.width))
eyes_url = images.get_serving_url(str(eyes_image.blob.key()), size=minimum)
nose_url = images.get_serving_url(str(nose_image.blob.key()), size=minimum)
mouth_url = images.get_serving_url(str(mouth_image.blob.key()), size=minimum)
self.render(u'cyberfaze', minimum=minimum, eyes_image=eyes_image, eyes_url=eyes_url, nose_image=nose_image, nose_url=nose_url, mouth_image=mouth_image, mouth_url=mouth_url, form_url = blobstore.create_upload_url('/upload'),)
After rewrite it's working like said:
class CyberFazeHandler(BaseHandler):
def get_random_image(self, category):
q = FileInfo.all()
q.filter('category =', category)
q.filter('randomvalue >=', random.random())
return q.get()
def get_random_image_legacy(self, category):
fileinfos = FileInfo.all().filter('category =', category)
return fileinfos[random.randint(0, fileinfos.count() - 1)]
def get(self):
eyes_image = self.get_random_image(category='eyes')
if not eyes_image:
logging.debug("getting eyes failed, trying legacy method")
eyes_image = self.get_random_image_legacy(category='eyes')
nose_image = self.get_random_image(category='nose')
if not nose_image:
nose_image = self.get_random_image_legacy(category='nose')
mouth_image = self.get_random_image(category='mouth')
if not mouth_image:
mouth_image = self.get_random_image_legacy(category='mouth')
eyes_data = None
try:
eyes_data = blobstore.fetch_data(eyes_image.blob.key(), 0,
50000)
except Exception, e:
self.set_message(type=u'error',
content=u'Could not find eyes data for file '
+ str(eyes_image.key().id()) + ' ('
+ unicode(e) + u')')
eyes_img = None
try:
eyes_img = images.Image(image_data=eyes_data)
except Exception, e:
self.set_message(type=u'error',
content=u'Could not find eyes img for file '
+ str(eyes_image.key().id()) + ' ('
+ unicode(e) + u')')
nose_data = None
try:
nose_data = blobstore.fetch_data(nose_image.blob.key(), 0,
50000)
except Exception, e:
self.set_message(type=u'error',
content=u'Could not find nose data for file '
+ str(nose_image.key().id()) + ' ('
+ unicode(e) + u')')
nose_img = None
try:
nose_img = images.Image(image_data=nose_data)
except Exception, e:
self.set_message(type=u'error',
content=u'Could not find nose img for file '
+ str(nose_image.key().id()) + ' ('
+ unicode(e) + u')')
mouth_data = None
try:
mouth_data = blobstore.fetch_data(mouth_image.blob.key(),
0, 50000)
except Exception, e:
self.set_message(type=u'error',
content=u'Could not find mouth data for file '
+ str(eyes_image.key().id()) + ' ('
+ unicode(e) + u')')
mouth_img = None
try:
mouth_img = images.Image(image_data=mouth_data)
except Exception, e:
self.set_message(type=u'error',
content=u'Could not find mouth img for file '
+ str(mouth_image.key().id()) + ' ('
+ unicode(e) + u')')
minimum = min(int(eyes_img.width), int(nose_img.width),
int(mouth_img.width))
eyes_url = images.get_serving_url(str(eyes_image.blob.key()),
size=minimum)
nose_url = images.get_serving_url(str(nose_image.blob.key()),
size=minimum)
mouth_url = images.get_serving_url(str(mouth_image.blob.k开发者_Go百科ey()),
size=minimum)
self.render(
u'cyberfaze',
minimum=minimum,
eyes_image=eyes_image,
eyes_url=eyes_url,
nose_image=nose_image,
nose_url=nose_url,
mouth_image=mouth_image,
mouth_url=mouth_url,
form_url=blobstore.create_upload_url('/upload'),
)
Which is more efficient depends on how it'll be used. If the user will be loading a lot of these mashups, it makes more sense to send them as separate images, because there will be fewer images for the browser to cache (a+b+c images instead of a*b*c).
Your code has a much more egregious performance issue, however:
def get_random_image(self, category):
fileinfos = FileInfo.all().filter("category =", category)
return fileinfos[random.randint(0, fileinfos.count()-1)]
Every time you call this function, it will perform a count operation, which is O(n) with the number of FileInfo
entities, then perform an offset query, which is O(n) with the offset. This is extremely slow and inefficient, and will get more so as you increase the number of images.
If you expect the set of images to be small (less than a few thousand) and fairly constant, simply store them in code, which will be faster than any other option. If the set is larger, or changes at runtime, assign a random value between 0 and 1 to each entity, and use a query like this to retrieve a randomly selected one:
q = FileInfo.all()
q.filter('category =', category)
q.filter('random >=', random.random())
return q.get()
精彩评论