• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# SPDX-License-Identifier: Apache-2.0
3# -----------------------------------------------------------------------------
4# Copyright 2020 Arm Limited
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may not
7# use this file except in compliance with the License. You may obtain a copy
8# of the License at:
9#
10#     http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17# -----------------------------------------------------------------------------
18"""
19The ``astc_image_info`` utility provides basic image query capabilities. It is
20a modal command line utility, exposing multiple available operators.
21
22* ``info``: Query structural information about the image, such as image
23      dimensions, number of color channels, and the min/max of each channel.
24* ``color``: Query the stored color value at a specific pixel coordinate, and
25      print the result in a variety of different formats.
26
27Both modes allow multiple images to be specified on the command line.
28"""
29
30import argparse
31import sys
32
33from PIL import Image
34
35
36def main_color(args):
37    """
38    Main function for the "color" mode.
39
40    This mode prints the color at a specific pixel coordinate in each image.
41    The color value is printed in a variety of color formats (decimal, HTML
42    string, float).
43
44    Args:
45        args (Namespace): The parsed command line arguments.
46
47    Returns:
48        int: The process return code.
49    """
50    retCode = 0
51
52    for i, image in enumerate(args.images):
53        if i != 0:
54            print("")
55
56        img = Image.open(image.name)
57        x = args.location[0]
58        y = args.location[1]
59
60        print(image.name)
61        print("=" * len(image.name))
62
63        if (x >= img.size[0]) or (y >= img.size[1]):
64            print("- ERROR: location out-of-bounds [%ux%u]" % img.size)
65            retCode = 1
66        else:
67            color = img.getpixel((x, y))
68
69            # Print byte values
70            print("+ Byte: %s" % str(color))
71
72            # Print hex values
73            fmtString = "+ Hex: #" + ("%02X" * len(color))
74            print(fmtString % color)
75
76            # Print float values
77            parts = ["%g"] * len(color)
78            parts = ", ".join(parts)
79            fmtString = "+ Float: (" + parts + ")"
80            print(fmtString % tuple(float(x)/255.0 for x in color))
81
82    return retCode
83
84
85def main_info(args):
86    """
87    Main function for the "info" mode.
88
89    This mode prints the basic metadata of an image:
90
91        - the overall image size.
92        - the number of color channels.
93        - the min/max value in each color channel.
94
95    Args:
96        args (Namespace): The parsed command line arguments.
97
98    Returns:
99        int: The process return code.
100    """
101    for i, image in enumerate(args.images):
102        if i != 0:
103            print("")
104
105        img = Image.open(image.name)
106        minmax = img.getextrema()
107
108        print(image.name)
109        print("=" * len(image.name))
110        print("+ Size: %ux%u" % (img.size[0], img.size[1]))
111        print("+ Channels: %s" % ("".join(img.getbands())))
112        for j, channel in enumerate(img.getbands()):
113            print("  + %s: %u - %u" % (channel, *minmax[j]))
114
115    return 0
116
117
118def parse_loc(value):
119    """
120    Command line argument parser for position arguments.
121
122    Args:
123        value (str): The command line argument string to parse. Must be of the
124            form <int>x<int>", where both integers must be zero or positive.
125
126    Returns:
127        list(int, int): The parsed location.
128
129    Raises:
130        ArgumentTypeError: The value is not a valid location.
131    """
132    error = argparse.ArgumentTypeError("%s is an invalid location" % value)
133    svalue = value.split("x")
134
135    if len(svalue) != 2:
136        raise error
137
138    try:
139        ivalue = [int(x) for x in svalue if int(x) >= 0]
140    except ValueError:
141        raise error
142
143    if len(ivalue) != len(svalue):
144        raise error
145
146    return ivalue
147
148
149def parse_command_line():
150    """
151    Parse the command line.
152
153    Returns:
154        Namespace: The parsed command line container.
155    """
156    parser = argparse.ArgumentParser()
157    subparsers = parser.add_subparsers(
158        title="Operations")
159
160    # Create the parser for the "pipette" command
161    parserA = subparsers.add_parser(
162        "color",
163        help="Print color at given coordinate")
164
165    parserA.set_defaults(func=main_color)
166
167    parserA.add_argument(
168        "location", metavar="loc", type=parse_loc,
169        help="The location spec XxY")
170
171    parserA.add_argument(
172        "images", metavar="image", nargs="+", type=argparse.FileType("r"),
173        help="The images to query")
174
175    # Create the parser for the "size" command
176    parserB = subparsers.add_parser(
177        "info",
178        help="Print image metadata info")
179
180    parserB.set_defaults(func=main_info)
181
182    parserB.add_argument(
183        "images", metavar="image", nargs="+", type=argparse.FileType("r"),
184        help="The images to query")
185
186    # Cope with the user failing to specify any sub-command. Note on Python 3.8
187    # we could use required=True on the add_subparsers call, but we cannot do
188    # this on 3.6 which is our current min-spec.
189    args = parser.parse_args()
190    if not hasattr(args, "func"):
191        parser.print_help()
192        return None
193
194    return args
195
196
197def main():
198    """
199    The main function.
200
201    Returns:
202        int: The process return code.
203    """
204    args = parse_command_line()
205    if args:
206        return args.func(args)
207
208    return 0
209
210
211if __name__ == "__main__":
212    sys.exit(main())
213