123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- """
- Image non-geospatial operations and manipulations
- Note: Functions in this module are experimental and are not considered
- a stable API, i.e. may change in future releases of GRASS GIS.
- It heavily relies on PIL but unlike PIL, the functions operate on
- files instead of PIL Image objects (which are used internally).
- These functions are convenient for post-processing outputs from GRASS
- modules, e.g. after rendering. However, if you have multiple operations
- you may want to consider using PIL directly for efficiency (to avoid
- writing and reading from the files).
- Usage
- =====
- Use keyword arguments for all parameters other than those for input,
- output, and format. All function provide reasonable defaults if possible,
- but note that they may not be applicable to you case or when developing
- a general tool.
- >>> import grass.imaging.operations as iop
- >>> # replace white color in the image by 100% transparency
- >>> iop.change_rbg_to_transparent("map.png", color=(255, 255, 255))
- >>> # crop the image in place
- >>> iop.crop_image("map.png")
- >>> # create a new image with inverted colors of the original image
- >>> iop.invert_image_colors("map.png", "map_inverted.png")
- >>> # create a thumbnail of the original image
- >>> iop.thumbnail_image("map.png", "map_thumbnail.png", size=(64, 64))
- Error handling
- ==============
- When PIL or a required submodule is not available, a RuntimeError
- exception is raised with a message mentioning the missing dependency.
- Additionally, any of the exceptions raised by PIL may be raised too,
- for example, when the file is not found.
- Authors, copyright and license
- ==============================
- (C) 2018 by Vaclav Petras and the GRASS Development Team
- This program is free software under the GNU General Public
- License (>=v2). Read the file COPYING that comes with GRASS
- for details.
- .. sectionauthor:: Vaclav Petras <wenzeslaus gmail com>
- """
- # import similar to what is in visvis
- try:
- import PIL
- from PIL import Image
- try:
- import PIL.ImageOps as ImageOps
- except ImportError:
- ImageOps = None
- except ImportError:
- PIL = None
- def crop_image(input_file, output_file=None, format=None):
- """Crop to non-zero area of the image
- :param input_file: Name of the file to manipulate
- :param output_file: filename for the new file (same as input by default)
- :param format: format to be used new file (if different from extension)
- """
- if PIL is None:
- raise RuntimeError(_("Install PIL or Pillow to use this function"))
- if not output_file:
- output_file = input_file
- img = Image.open(input_file)
- box = img.getbbox()
- cropped_image = img.crop(box)
- cropped_image.save(output_file, format)
- def thumbnail_image(input_file, output_file=None, size=(200, 200), format=None):
- """Create a thumbnail of an image
- The image aspect ratio is kept and its height and width are adjusted
- accordingly to fit the ``size`` parameter.
- :param input_file: Name of the file to manipulate
- :param size: Size of the new image in pixels as tuple
- :param output_file: filename for the new file (same as input by default)
- :param format: format to be used new file (if different from extension)
- """
- if PIL is None:
- raise RuntimeError(_("Install PIL or Pillow to use this function"))
- if not output_file:
- output_file = input_file
- img = Image.open(input_file)
- img.thumbnail(size, Image.ANTIALIAS)
- img.save(output_file, format)
- def change_rbg_to_transparent(
- input_file, output_file=None, color="white", alpha=0, format=None
- ):
- """Make a specified RGB color in the image transparent
- The color is specified as a RGB tuple (triplet) or string 'white'
- or 'black'. Note that GRASS color names are not supported.
- The white (255, 255, 255) is replaced by default but each application
- is encouraged to consider color to use and explicitly specify it.
- :param input_file: Name of the file to manipulate
- :param color: Color to be replaced by transparency (tuple of three ints)
- :param alpha: Level of opacity (0 fully transparent, 255 fully opaque)
- :param output_file: filename for the new file (same as input by default)
- :param format: format to be used new file (if different from extension)
- """
- if PIL is None:
- raise RuntimeError(_("Install PIL or Pillow to use this function"))
- if color == "white":
- rgb = (255, 255, 255)
- elif color == "black":
- rgb = (0, 0, 0)
- else:
- rgb = color # pylint: disable=redefined-variable-type
- if not output_file:
- output_file = input_file
- img = Image.open(input_file)
- img = img.convert("RGBA")
- old_data = img.getdata()
- new_data = []
- for item in old_data:
- if item[0] == rgb[0] and item[1] == rgb[1] and item[2] == rgb[2]:
- new_data.append((rgb[0], rgb[1], rgb[2], alpha))
- else:
- new_data.append(item)
- img.putdata(new_data)
- img.save(output_file, format)
- def invert_image_colors(input_file, output_file=None, format=None):
- """Invert colors in the image
- The alpha channel, if present, is untouched by this function.
- :param input_file: Name of the file to manipulate
- :param output_file: filename for the new file (same as input by default)
- :param format: format to be used new file (if different from extension)
- """
- if PIL is None:
- raise RuntimeError(_("Install PIL or Pillow to use this function"))
- if ImageOps is None:
- raise RuntimeError(
- _(
- "Install a newer version of PIL or Pillow to"
- " use this function (missing ImageOps module)"
- )
- )
- if not output_file:
- output_file = input_file
- original_img = Image.open(input_file)
- # according to documentation (3.0.x) the module can work only on RGB
- # so we need to specifically take care of transparency if present
- if original_img.mode == "RGBA":
- # split into bands
- red1, green1, blue1, alpha = original_img.split()
- rgb_img = Image.merge("RGB", (red1, green1, blue1))
- # invert RGB
- inverted_rgb_img = ImageOps.invert(rgb_img)
- # put back the original alpha
- red2, green2, blue2 = inverted_rgb_img.split()
- new_image = Image.merge("RGBA", (red2, green2, blue2, alpha))
- else:
- new_image = ImageOps.invert(original_img)
- new_image.save(output_file, format)
|