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