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