| #!/usr/bin/env python3 |
| # SPDX-License-Identifier: Apache-2.0 |
| # ----------------------------------------------------------------------------- |
| # Copyright 2020 Arm Limited |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy |
| # of the License at: |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations |
| # under the License. |
| # ----------------------------------------------------------------------------- |
| """ |
| The ``astc_image_info`` utility provides basic image query capabilities. It is |
| a modal command line utility, exposing multiple available operators. |
| |
| * ``info``: Query structural information about the image, such as image |
| dimensions, number of color channels, and the min/max of each channel. |
| * ``color``: Query the stored color value at a specific pixel coordinate, and |
| print the result in a variety of different formats. |
| |
| Both modes allow multiple images to be specified on the command line. |
| """ |
| |
| import argparse |
| import sys |
| |
| from PIL import Image |
| |
| |
| def main_color(args): |
| """ |
| Main function for the "color" mode. |
| |
| This mode prints the color at a specific pixel coordinate in each image. |
| The color value is printed in a variety of color formats (decimal, HTML |
| string, float). |
| |
| Args: |
| args (Namespace): The parsed command line arguments. |
| |
| Returns: |
| int: The process return code. |
| """ |
| retCode = 0 |
| |
| for i, image in enumerate(args.images): |
| if i != 0: |
| print("") |
| |
| img = Image.open(image.name) |
| x = args.location[0] |
| y = args.location[1] |
| |
| print(image.name) |
| print("=" * len(image.name)) |
| |
| if (x >= img.size[0]) or (y >= img.size[1]): |
| print("- ERROR: location out-of-bounds [%ux%u]" % img.size) |
| retCode = 1 |
| else: |
| color = img.getpixel((x, y)) |
| |
| # Print byte values |
| print("+ Byte: %s" % str(color)) |
| |
| # Print hex values |
| fmtString = "+ Hex: #" + ("%02X" * len(color)) |
| print(fmtString % color) |
| |
| # Print float values |
| parts = ["%g"] * len(color) |
| parts = ", ".join(parts) |
| fmtString = "+ Float: (" + parts + ")" |
| print(fmtString % tuple(float(x)/255.0 for x in color)) |
| |
| return retCode |
| |
| |
| def main_info(args): |
| """ |
| Main function for the "info" mode. |
| |
| This mode prints the basic metadata of an image: |
| |
| - the overall image size. |
| - the number of color channels. |
| - the min/max value in each color channel. |
| |
| Args: |
| args (Namespace): The parsed command line arguments. |
| |
| Returns: |
| int: The process return code. |
| """ |
| for i, image in enumerate(args.images): |
| if i != 0: |
| print("") |
| |
| img = Image.open(image.name) |
| minmax = img.getextrema() |
| |
| print(image.name) |
| print("=" * len(image.name)) |
| print("+ Size: %ux%u" % (img.size[0], img.size[1])) |
| print("+ Channels: %s" % ("".join(img.getbands()))) |
| for j, channel in enumerate(img.getbands()): |
| print(" + %s: %u - %u" % (channel, *minmax[j])) |
| |
| return 0 |
| |
| |
| def parse_loc(value): |
| """ |
| Command line argument parser for position arguments. |
| |
| Args: |
| value (str): The command line argument string to parse. Must be of the |
| form <int>x<int>", where both integers must be zero or positive. |
| |
| Returns: |
| list(int, int): The parsed location. |
| |
| Raises: |
| ArgumentTypeError: The value is not a valid location. |
| """ |
| error = argparse.ArgumentTypeError("%s is an invalid location" % value) |
| svalue = value.split("x") |
| |
| if len(svalue) != 2: |
| raise error |
| |
| try: |
| ivalue = [int(x) for x in svalue if int(x) >= 0] |
| except ValueError: |
| raise error |
| |
| if len(ivalue) != len(svalue): |
| raise error |
| |
| return ivalue |
| |
| |
| def parse_command_line(): |
| """ |
| Parse the command line. |
| |
| Returns: |
| Namespace: The parsed command line container. |
| """ |
| parser = argparse.ArgumentParser() |
| subparsers = parser.add_subparsers( |
| title="Operations") |
| |
| # Create the parser for the "pipette" command |
| parserA = subparsers.add_parser( |
| "color", |
| help="Print color at given coordinate") |
| |
| parserA.set_defaults(func=main_color) |
| |
| parserA.add_argument( |
| "location", metavar="loc", type=parse_loc, |
| help="The location spec XxY") |
| |
| parserA.add_argument( |
| "images", metavar="image", nargs="+", type=argparse.FileType("r"), |
| help="The images to query") |
| |
| # Create the parser for the "size" command |
| parserB = subparsers.add_parser( |
| "info", |
| help="Print image metadata info") |
| |
| parserB.set_defaults(func=main_info) |
| |
| parserB.add_argument( |
| "images", metavar="image", nargs="+", type=argparse.FileType("r"), |
| help="The images to query") |
| |
| # Cope with the user failing to specify any sub-command. Note on Python 3.8 |
| # we could use required=True on the add_subparsers call, but we cannot do |
| # this on 3.6 which is our current min-spec. |
| args = parser.parse_args() |
| if not hasattr(args, "func"): |
| parser.print_help() |
| return None |
| |
| return args |
| |
| |
| def main(): |
| """ |
| The main function. |
| |
| Returns: |
| int: The process return code. |
| """ |
| args = parse_command_line() |
| if args: |
| return args.func(args) |
| |
| return 0 |
| |
| |
| if __name__ == "__main__": |
| sys.exit(main()) |