| #!/usr/bin/env python3 |
| # Copyright 2023 Google LLC |
| # |
| # 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. |
| |
| """Generate ML model inputs from images.""" |
| import argparse |
| import os |
| import struct |
| |
| import numpy as np |
| from PIL import Image |
| from scipy.io import wavfile |
| |
| |
| parser = argparse.ArgumentParser( |
| description='Generate inputs for ML models.') |
| parser.add_argument('--i', dest='input_name', |
| help='Model input image name', required=True) |
| parser.add_argument('--o', dest='output_file', |
| help='Output binary name', required=True) |
| parser.add_argument('--s', |
| dest='input_shape', |
| help='Model input shape (example: "1, 224, 224, 3")', |
| required=True) |
| parser.add_argument('--q', dest='is_quant', action='store_true', |
| help='Indicate it is quant model (default: False)') |
| parser.add_argument('--r', dest='float_input_range', default="-1.0, 1.0", |
| help='Float model input range (default: "-1.0, 1.0")') |
| parser.add_argument('--sgn', dest='is_signed', action='store_true', |
| help='Indicate the input is signed (default: False)') |
| args = parser.parse_args() |
| |
| |
| def write_binary_file(file_path, data, is_quant, is_audio): |
| with open(file_path, "wb+") as file: |
| for d in data: |
| if is_audio: |
| file.write(struct.pack("<h", d)) |
| elif is_quant: |
| file.write(struct.pack("<B", d)) |
| else: |
| file.write(struct.pack("<f", d)) |
| |
| |
| def gen_mlmodel_input(input_name, output_file, input_shape, is_quant, |
| is_signed, float_input_range): |
| if not os.path.exists(input_name): |
| raise RuntimeError(f"Input file {input_name} doesn't exist") |
| if len(input_shape) < 3: |
| raise ValueError("Input shape < 3 dimensions") |
| # convert input shape to a list |
| input_shape = [int(x) for x in args.input_shape.split(',')] |
| input_ext = os.path.splitext(input_name)[1] |
| is_audio = False |
| if (not input_ext) or (input_ext == '.bin') or (input_ext == '.raw'): |
| with open(input_name, mode='rb') as f: |
| data = np.fromfile(f, dtype=np.uint8 if is_quant else np.float32) |
| data = data[:np.prod(input_shape)].reshape(np.prod(input_shape)) |
| elif input_ext == '.wav': |
| is_audio = True |
| _, data = wavfile.read(input_name) |
| data = data[:np.prod(input_shape)].reshape(np.prod(input_shape)) |
| else: |
| resized_img = Image.open(input_name).resize( |
| (input_shape[2], input_shape[1])) |
| data = np.array(resized_img).reshape(np.prod(input_shape)) |
| if not is_quant: |
| float_input_range = [ |
| float(x) for x in args.float_input_range.split(',') |
| ] |
| low = np.min(float_input_range) |
| high = np.max(float_input_range) |
| data = (high - low) * data / 255.0 + low |
| elif is_signed: |
| data -= 128 |
| write_binary_file(output_file, data, is_quant, is_audio) |
| |
| |
| if __name__ == '__main__': |
| gen_mlmodel_input(args.input_name, args.output_file, args.input_shape, |
| args.is_quant, args.is_signed, args.float_input_range) |