thirdparty/google_appengine/google/appengine/api/images/__init__.py
changeset 2273 e4cb9c53db3e
parent 1278 a7766286a7be
child 3031 7678f72140e6
equal deleted inserted replaced
2272:26491ee91e33 2273:e4cb9c53db3e
    40 JPEG = images_service_pb.OutputSettings.JPEG
    40 JPEG = images_service_pb.OutputSettings.JPEG
    41 PNG = images_service_pb.OutputSettings.PNG
    41 PNG = images_service_pb.OutputSettings.PNG
    42 
    42 
    43 OUTPUT_ENCODING_TYPES = frozenset([JPEG, PNG])
    43 OUTPUT_ENCODING_TYPES = frozenset([JPEG, PNG])
    44 
    44 
       
    45 TOP_LEFT = images_service_pb.CompositeImageOptions.TOP_LEFT
       
    46 TOP_CENTER = images_service_pb.CompositeImageOptions.TOP
       
    47 TOP_RIGHT = images_service_pb.CompositeImageOptions.TOP_RIGHT
       
    48 CENTER_LEFT = images_service_pb.CompositeImageOptions.LEFT
       
    49 CENTER_CENTER = images_service_pb.CompositeImageOptions.CENTER
       
    50 CENTER_RIGHT = images_service_pb.CompositeImageOptions.RIGHT
       
    51 BOTTOM_LEFT = images_service_pb.CompositeImageOptions.BOTTOM_LEFT
       
    52 BOTTOM_CENTER = images_service_pb.CompositeImageOptions.BOTTOM
       
    53 BOTTOM_RIGHT = images_service_pb.CompositeImageOptions.BOTTOM_RIGHT
       
    54 
       
    55 ANCHOR_TYPES = frozenset([TOP_LEFT, TOP_CENTER, TOP_RIGHT, CENTER_LEFT,
       
    56                           CENTER_CENTER, CENTER_RIGHT, BOTTOM_LEFT,
       
    57                           BOTTOM_CENTER, BOTTOM_RIGHT])
       
    58 
    45 MAX_TRANSFORMS_PER_REQUEST = 10
    59 MAX_TRANSFORMS_PER_REQUEST = 10
       
    60 
       
    61 MAX_COMPOSITES_PER_REQUEST = 16
    46 
    62 
    47 
    63 
    48 class Error(Exception):
    64 class Error(Exception):
    49   """Base error class for this module."""
    65   """Base error class for this module."""
    50 
    66 
   294 
   310 
   295     if not width and not height:
   311     if not width and not height:
   296       raise BadRequestError("At least one of width or height must be > 0.")
   312       raise BadRequestError("At least one of width or height must be > 0.")
   297 
   313 
   298     if width > 4000 or height > 4000:
   314     if width > 4000 or height > 4000:
   299       raise BadRequestError("Both width and height must be < 4000.")
   315       raise BadRequestError("Both width and height must be <= 4000.")
   300 
   316 
   301     self._check_transform_limits()
   317     self._check_transform_limits()
   302 
   318 
   303     transform = images_service_pb.Transform()
   319     transform = images_service_pb.Transform()
   304     transform.set_width(width)
   320     transform.set_width(width)
   422 
   438 
   423     This is similar to the "I'm Feeling Lucky" button in Picasa.
   439     This is similar to the "I'm Feeling Lucky" button in Picasa.
   424 
   440 
   425     Raises:
   441     Raises:
   426       BadRequestError if MAX_TRANSFORMS_PER_REQUEST transforms have already
   442       BadRequestError if MAX_TRANSFORMS_PER_REQUEST transforms have already
   427       been requested for this image.
   443         been requested for this image.
   428     """
   444     """
   429     self._check_transform_limits()
   445     self._check_transform_limits()
   430     transform = images_service_pb.Transform()
   446     transform = images_service_pb.Transform()
   431     transform.set_autolevels(True)
   447     transform.set_autolevels(True)
   432 
   448 
   509     """Gets the height of the image."""
   525     """Gets the height of the image."""
   510     if self._height is None:
   526     if self._height is None:
   511       self._update_dimensions()
   527       self._update_dimensions()
   512     return self._height
   528     return self._height
   513 
   529 
       
   530   def histogram(self):
       
   531     """Calculates the histogram of the image.
       
   532 
       
   533     Returns: 3 256-element lists containing the number of occurences of each
       
   534     value of each color in the order RGB. As described at
       
   535     http://en.wikipedia.org/wiki/Color_histogram for N = 256. i.e. the first
       
   536     value of the first list contains the number of pixels with a red value of
       
   537     0, the second the number with a red value of 1.
       
   538 
       
   539     Raises:
       
   540       NotImageError when the image data given is not an image.
       
   541       BadImageError when the image data given is corrupt.
       
   542       LargeImageError when the image data given is too large to process.
       
   543       Error when something unknown, but bad, happens.
       
   544     """
       
   545     request = images_service_pb.ImagesHistogramRequest()
       
   546     response = images_service_pb.ImagesHistogramResponse()
       
   547 
       
   548     request.mutable_image().set_content(self._image_data)
       
   549     try:
       
   550       apiproxy_stub_map.MakeSyncCall("images",
       
   551                                      "Histogram",
       
   552                                      request,
       
   553                                      response)
       
   554     except apiproxy_errors.ApplicationError, e:
       
   555       if (e.application_error ==
       
   556           images_service_pb.ImagesServiceError.NOT_IMAGE):
       
   557         raise NotImageError()
       
   558       elif (e.application_error ==
       
   559             images_service_pb.ImagesServiceError.BAD_IMAGE_DATA):
       
   560         raise BadImageError()
       
   561       elif (e.application_error ==
       
   562             images_service_pb.ImagesServiceError.IMAGE_TOO_LARGE):
       
   563         raise LargeImageError()
       
   564       else:
       
   565         raise Error()
       
   566     histogram = response.histogram()
       
   567     return [histogram.red_list(),
       
   568             histogram.green_list(),
       
   569             histogram.blue_list()]
       
   570 
   514 
   571 
   515 def resize(image_data, width=0, height=0, output_encoding=PNG):
   572 def resize(image_data, width=0, height=0, output_encoding=PNG):
   516   """Resize a given image file maintaining the aspect ratio.
   573   """Resize a given image file maintaining the aspect ratio.
   517 
   574 
   518   If both width and height are specified, the more restricting of the two
   575   If both width and height are specified, the more restricting of the two
   629       for more details.
   686       for more details.
   630   """
   687   """
   631   image = Image(image_data)
   688   image = Image(image_data)
   632   image.im_feeling_lucky()
   689   image.im_feeling_lucky()
   633   return image.execute_transforms(output_encoding=output_encoding)
   690   return image.execute_transforms(output_encoding=output_encoding)
       
   691 
       
   692 def composite(inputs, width, height, color=0, output_encoding=PNG):
       
   693   """Composite one or more images onto a canvas.
       
   694 
       
   695   Args:
       
   696     inputs: a list of tuples (image_data, x_offset, y_offset, opacity, anchor)
       
   697     where
       
   698       image_data: str, source image data.
       
   699       x_offset: x offset in pixels from the anchor position
       
   700       y_offset: y offset in piyels from the anchor position
       
   701       opacity: opacity of the image specified as a float in range [0.0, 1.0]
       
   702       anchor: anchoring point from ANCHOR_POINTS. The anchor point of the image
       
   703       is aligned with the same anchor point of the canvas. e.g. TOP_RIGHT would
       
   704       place the top right corner of the image at the top right corner of the
       
   705       canvas then apply the x and y offsets.
       
   706     width: canvas width in pixels.
       
   707     height: canvas height in pixels.
       
   708     color: canvas background color encoded as a 32 bit unsigned int where each
       
   709     color channel is represented by one byte in order ARGB.
       
   710     output_encoding: a value from OUTPUT_ENCODING_TYPES.
       
   711 
       
   712   Returns:
       
   713       str, image data of the composited image.
       
   714 
       
   715   Raises:
       
   716     TypeError If width, height, color, x_offset or y_offset are not of type
       
   717     int or long or if opacity is not a float
       
   718     BadRequestError If more than MAX_TRANSFORMS_PER_REQUEST compositions have
       
   719     been requested, if the canvas width or height is greater than 4000 or less
       
   720     than or equal to 0, if the color is invalid or if for any composition
       
   721     option, the opacity is outside the range [0,1] or the anchor is invalid.
       
   722   """
       
   723   if (not isinstance(width, (int, long)) or
       
   724       not isinstance(height, (int, long)) or
       
   725       not isinstance(color, (int, long))):
       
   726     raise TypeError("Width, height and color must be integers.")
       
   727   if output_encoding not in OUTPUT_ENCODING_TYPES:
       
   728     raise BadRequestError("Output encoding type '%s' not in recognized set "
       
   729                           "%s" % (output_encoding, OUTPUT_ENCODING_TYPES))
       
   730 
       
   731   if not inputs:
       
   732     raise BadRequestError("Must provide at least one input")
       
   733   if len(inputs) > MAX_COMPOSITES_PER_REQUEST:
       
   734     raise BadRequestError("A maximum of %d composition operations can be"
       
   735                           "performed in a single request" %
       
   736                           MAX_COMPOSITES_PER_REQUEST)
       
   737 
       
   738   if width <= 0 or height <= 0:
       
   739     raise BadRequestError("Width and height must be > 0.")
       
   740   if width > 4000 or height > 4000:
       
   741     raise BadRequestError("Width and height must be <= 4000.")
       
   742 
       
   743   if color > 0xffffffff or color < 0:
       
   744     raise BadRequestError("Invalid color")
       
   745   if color >= 0x80000000:
       
   746     color -= 0x100000000
       
   747 
       
   748   image_map = {}
       
   749 
       
   750   request = images_service_pb.ImagesCompositeRequest()
       
   751   response = images_service_pb.ImagesTransformResponse()
       
   752   for (image, x, y, opacity, anchor) in inputs:
       
   753     if not image:
       
   754       raise BadRequestError("Each input must include an image")
       
   755     if (not isinstance(x, (int, long)) or
       
   756         not isinstance(y, (int, long)) or
       
   757         not isinstance(opacity, (float))):
       
   758       raise TypeError("x_offset, y_offset must be integers and opacity must"
       
   759                       "be a float")
       
   760     if x > 4000 or x < -4000:
       
   761       raise BadRequestError("xOffsets must be in range [-4000, 4000]")
       
   762     if y > 4000 or y < -4000:
       
   763       raise BadRequestError("yOffsets must be in range [-4000, 4000]")
       
   764     if opacity < 0 or opacity > 1:
       
   765       raise BadRequestError("Opacity must be in the range 0.0 to 1.0")
       
   766     if anchor not in ANCHOR_TYPES:
       
   767       raise BadRequestError("Anchor type '%s' not in recognized set %s" %
       
   768                             (anchor, ANCHOR_TYPES))
       
   769     if image not in image_map:
       
   770       image_map[image] = request.image_size()
       
   771       request.add_image().set_content(image)
       
   772 
       
   773     option = request.add_options()
       
   774     option.set_x_offset(x)
       
   775     option.set_y_offset(y)
       
   776     option.set_opacity(opacity)
       
   777     option.set_anchor(anchor)
       
   778     option.set_source_index(image_map[image])
       
   779 
       
   780   request.mutable_canvas().mutable_output().set_mime_type(output_encoding)
       
   781   request.mutable_canvas().set_width(width)
       
   782   request.mutable_canvas().set_height(height)
       
   783   request.mutable_canvas().set_color(color)
       
   784 
       
   785   try:
       
   786     apiproxy_stub_map.MakeSyncCall("images",
       
   787                                    "Composite",
       
   788                                    request,
       
   789                                    response)
       
   790   except apiproxy_errors.ApplicationError, e:
       
   791     if (e.application_error ==
       
   792         images_service_pb.ImagesServiceError.BAD_TRANSFORM_DATA):
       
   793       raise BadRequestError()
       
   794     elif (e.application_error ==
       
   795           images_service_pb.ImagesServiceError.NOT_IMAGE):
       
   796       raise NotImageError()
       
   797     elif (e.application_error ==
       
   798           images_service_pb.ImagesServiceError.BAD_IMAGE_DATA):
       
   799       raise BadImageError()
       
   800     elif (e.application_error ==
       
   801           images_service_pb.ImagesServiceError.IMAGE_TOO_LARGE):
       
   802       raise LargeImageError()
       
   803     elif (e.application_error ==
       
   804           images_service_pb.ImagesServiceError.UNSPECIFIED_ERROR):
       
   805       raise TransformationError()
       
   806     else:
       
   807       raise Error()
       
   808 
       
   809   return response.image().content()
       
   810 
       
   811 
       
   812 def histogram(image_data):
       
   813   """Calculates the histogram of the given image.
       
   814 
       
   815   Args:
       
   816     image_data: str, source image data.
       
   817   Returns: 3 256-element lists containing the number of occurences of each
       
   818   value of each color in the order RGB.
       
   819 
       
   820   Raises:
       
   821     NotImageError when the image data given is not an image.
       
   822     BadImageError when the image data given is corrupt.
       
   823     LargeImageError when the image data given is too large to process.
       
   824     Error when something unknown, but bad, happens.
       
   825   """
       
   826   image = Image(image_data)
       
   827   return image.histogram()