385 lines
12 KiB
Python
385 lines
12 KiB
Python
#!/usr/bin/python
|
|
|
|
import cv2
|
|
import argparse
|
|
import sys
|
|
import os
|
|
import time
|
|
import string
|
|
import ConsoleUtils
|
|
|
|
# I like timing things
|
|
_START_TIME = time.time()
|
|
|
|
name = 'OCVResize'
|
|
description = 'A tool for resizing images using the OpenCV library.'
|
|
author = 'Chris Diesch <cdiesch@sequencelogic.net>'
|
|
version = '0.1.0'
|
|
date = '2017/09/25'
|
|
usage = 'imagine-cv [OPTIONS...] -i,--input IN_PATH -o,--output OUT_PATH'
|
|
|
|
args = None
|
|
# Verbosity
|
|
_VERBOSE = None
|
|
_QUIET = None
|
|
# Input type
|
|
_USE_DIR = None
|
|
# Output format
|
|
_OUT_NAME_FORMAT = None
|
|
# Image color
|
|
_G_SCALE = None
|
|
# Image size options
|
|
_NEW_Y = None
|
|
_NEW_X = None
|
|
_IMG_SCALE = None
|
|
_INTER = cv2.INTER_CUBIC
|
|
_INTER_NAME = 'Cubic'
|
|
|
|
# name formatting tags
|
|
_IN_NAME = '${in_file}'
|
|
_Y_VAL = '${new_y}'
|
|
_X_VAL = '${new_x}'
|
|
|
|
parser = argparse.ArgumentParser(prog=name, description=description, add_help=False, usage=usage)
|
|
printer = ConsoleUtils.SLPrinter(name)
|
|
|
|
|
|
# Helper methods
|
|
def _format_name(in_name):
|
|
template_str = string.Template(_OUT_NAME_FORMAT)
|
|
mapping = {'in_file': in_name, 'new_y': _NEW_Y, 'new_x': _NEW_X}
|
|
|
|
result = template_str.safe_substitute(mapping)
|
|
|
|
return result
|
|
|
|
|
|
def _resize_and_save_img(img_file, out_file):
|
|
global _NEW_X, _NEW_Y
|
|
if _VERBOSE:
|
|
print('Reading image from %s' % img_file)
|
|
image = cv2.imread(img_file)
|
|
if _G_SCALE:
|
|
if _VERBOSE:
|
|
print('Converting image to gray scale')
|
|
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
|
if _VERBOSE:
|
|
print('Processing image %s' % img_file)
|
|
|
|
src_y, src_x = image.shape[:2]
|
|
# Get the scale factors
|
|
if _IMG_SCALE == 0:
|
|
x_scale = 1
|
|
y_scale = 1
|
|
|
|
if _NEW_X != 0:
|
|
x_scale = float(float(_NEW_X) / src_x)
|
|
|
|
if _NEW_Y != 0:
|
|
y_scale = float(float(_NEW_Y) / src_y)
|
|
|
|
if x_scale == 1:
|
|
x_scale = y_scale
|
|
_NEW_X = src_x * x_scale
|
|
|
|
elif y_scale == 1:
|
|
y_scale = x_scale
|
|
_NEW_Y = src_y * y_scale
|
|
|
|
else:
|
|
x_scale = _IMG_SCALE
|
|
y_scale = _IMG_SCALE
|
|
_NEW_Y = _IMG_SCALE * src_y
|
|
_NEW_X = _IMG_SCALE * src_x
|
|
# logging
|
|
if _VERBOSE:
|
|
print('Source image size: %dx%d' % (src_y, src_x))
|
|
print('X scale factor: %f' % x_scale)
|
|
print('Y scale factor: %f' % y_scale)
|
|
print('New Size: %dx%d' % (_NEW_Y, _NEW_X))
|
|
|
|
image = cv2.resize(image, None, fx=x_scale, fy=y_scale, interpolation=_INTER)
|
|
|
|
if _VERBOSE:
|
|
print('Saving image to "%s"' % out_file)
|
|
cv2.imwrite(out_file, image)
|
|
|
|
|
|
def _process_images(image_dir, output_dir):
|
|
start_time = time.time()
|
|
images = os.listdir(image_dir)
|
|
num_processed = 0
|
|
num_images = len(images)
|
|
if not _QUIET:
|
|
print('Loaded %d images to process' % num_images)
|
|
for img in images:
|
|
# get the image name
|
|
img_name = _format_name(img)
|
|
img = os.path.join(image_dir, img)
|
|
new_img = os.path.join(output_dir, img_name)
|
|
# resize the image
|
|
_resize_and_save_img(img, new_img)
|
|
# logging...
|
|
if _VERBOSE and num_processed % 10 == 0:
|
|
img_per_sec = float(num_processed/(time.time() - start_time))
|
|
print('Processed %d images (rate: %.4f/s)' % (num_processed, img_per_sec))
|
|
# Increment
|
|
num_processed += 1
|
|
# more logging
|
|
if not _QUIET:
|
|
run_time = time.time() - start_time
|
|
img_per_sec = float(num_images/run_time)
|
|
print('Processed %d images in %.4f seconds (%.2f images/s)' % (num_images, run_time, img_per_sec))
|
|
|
|
|
|
def _process_single_img(input_file, output_file):
|
|
if _VERBOSE:
|
|
print('Processing image "%s"' % input_file)
|
|
start_time = time.time()
|
|
_resize_and_save_img(input_file, output_file)
|
|
run_time = time.time() - start_time
|
|
if not _QUIET:
|
|
print('Processed image in %.4f seconds.' % run_time)
|
|
|
|
|
|
# Main
|
|
def main(input_dir, output):
|
|
if _USE_DIR:
|
|
_process_images(input_dir, output)
|
|
else:
|
|
_process_single_img(input_dir, output)
|
|
|
|
|
|
# Everything below this is application set up/argument processing this code is where the global values referenced
|
|
# above are defined and input arguments are validated.
|
|
|
|
|
|
# Application helpers
|
|
def _print_version():
|
|
printer.write_no_prefix(name)
|
|
printer.write_no_prefix('Version: %s' % version)
|
|
printer.write_no_prefix('Date: %s' % date)
|
|
printer.write_no_prefix('Author: %s' % author)
|
|
|
|
|
|
def _print_help():
|
|
sys.stdout = printer.old_stdout
|
|
print(name)
|
|
print(description)
|
|
print('Usage: %s' % usage)
|
|
print('')
|
|
print('Options')
|
|
print(' Required:')
|
|
print(' -i, --input IN_PATH The path to the input image.')
|
|
print(' -o, --output OUT_PATH The path to save the new image to.')
|
|
print(' NOTE: images will be saved as ".png" files.')
|
|
print('')
|
|
print(' Input/Output:')
|
|
print(' -n, --name-format FORMAT Use this format when naming images '
|
|
'(default: "${input}(${new_h}).jpg").')
|
|
print(' NOTE: --output MUST be a directory when using this option.')
|
|
print(' Valid format tags:')
|
|
print(' - ${input} Replaced with the name of the input file.')
|
|
print(' - ${new_h} Replaced with the new HEIGHT value (pixels).')
|
|
print(' - ${new_w} Replaced with the new WIDTH value (pixels).')
|
|
print('')
|
|
print(' Image Color:')
|
|
print(' -g, --grey-scale Convert the image to grey scale.')
|
|
print('')
|
|
print(' Image Resizing:')
|
|
print(' -s, --scale SCALE Use this value as the scale factor for both X and Y values (float).')
|
|
print(' NOTE: This CANNOT be used with WIDTH or HEIGHT arguments.')
|
|
print(' -w, --img-width WIDTH Set the image width to WIDTH (pixels).')
|
|
print(' If this value is <= 0, the image will maintain it\'s proportions')
|
|
print(' and the new size will be determined by the height argument.')
|
|
print(' -h, --img-height HEIGHT Set the image height to HEIGHT (pixels).')
|
|
print(' If this value is <= 0, the image will maintain it\'s proportions.')
|
|
print(' and the new size will be determined by the width argument.')
|
|
print(' -c, --cubic Use cubic interpolation (default).')
|
|
print(' -l, --linear Use linear interpolation.')
|
|
print(' -a, --area Yse area interpolation.')
|
|
print('')
|
|
print(' Miscellaneous:')
|
|
print(' -h, --help Prints the help message.')
|
|
print(' -V, --version Prints the version information.')
|
|
print(' -v, --verbose Increases the output verbosity (console logging).')
|
|
print(' -q, --quiet Reduces the output verbosity (console logging).')
|
|
print('')
|
|
print('Version Information:')
|
|
print(' Version: %s' % version)
|
|
print(' Date: %s' % date)
|
|
print('')
|
|
print('Author: %s' % author)
|
|
|
|
|
|
def _make_args():
|
|
# Required arguments
|
|
required_args = parser.add_argument_group('Required')
|
|
|
|
required_args.add_argument('-i', '--input', required=True)
|
|
required_args.add_argument('-o', '--output', required=True)
|
|
|
|
# Input/Output arguments
|
|
in_out_args = parser.add_argument_group('Input/Output')
|
|
in_out_args.add_argument('-n', '--name-format', default='${in_file}(${new_y}).jpg')
|
|
|
|
# Color arguments
|
|
color_args = parser.add_argument_group('Image Color')
|
|
color_args.add_argument('-g', '--gray-scale', action='store_true')
|
|
|
|
# Size arguments
|
|
size_args = parser.add_argument_group('Image Resizing')
|
|
|
|
size_args.add_argument('-s', '--scale', type=float, default=0)
|
|
size_args.add_argument('-w', '--img-width', type=int, default=0)
|
|
size_args.add_argument('-H', '--img-height', type=int, default=0)
|
|
inter_arg = size_args.add_mutually_exclusive_group()
|
|
inter_arg.add_argument('-c', '--cubic', action='store_true')
|
|
inter_arg.add_argument('-l', '--linear', action='store_true')
|
|
inter_arg.add_argument('-a', '--area', action='store_true')
|
|
|
|
# Miscellaneous arguments
|
|
misc_args = parser.add_argument_group('Miscellaneous')
|
|
|
|
misc_args.add_argument('-V', '--version', action=ConsoleUtils.CustomPrintAction, print_fn=_print_version)
|
|
misc_args.add_argument('-h', '--help', action=ConsoleUtils.CustomPrintAction, print_fn=_print_help)
|
|
v_or_q = misc_args.add_mutually_exclusive_group()
|
|
v_or_q.add_argument('-v', '--verbose', action="store_true")
|
|
v_or_q.add_argument("-q", "--quiet", action="store_true")
|
|
|
|
|
|
def _show_args():
|
|
if not _QUIET:
|
|
print('Loading image(s) from path: "%s"' % args.input)
|
|
print('Saving output(s) to path: "%s"' % args.output)
|
|
print('Loading from directories: %s' % _USE_DIR)
|
|
print('Using Interpolation method: "%s"' % _INTER_NAME)
|
|
if _NEW_Y != 0:
|
|
print('Result image height: %d' % _NEW_Y)
|
|
if _NEW_X != 0:
|
|
print('Result image width: %d' % _NEW_X)
|
|
if _IMG_SCALE != 0:
|
|
print('Scaling images by factor: %f' % _IMG_SCALE)
|
|
print('Convert to grey scale: %s' % _G_SCALE)
|
|
print('Image name format: "%s"' % _OUT_NAME_FORMAT)
|
|
printer.write_no_prefix('')
|
|
|
|
|
|
def _check_args():
|
|
fatal_error = False
|
|
|
|
if args.scale != 0 and (args.img_width != 0 or args.img_height != 0):
|
|
print('Fatal Error: Values were provided for "--scale" and "--height" or "--width" arguments.')
|
|
print('If you wish to use the "--scale" argument, you cannot use "--height" or "--width".')
|
|
fatal_error = True
|
|
|
|
if not os.path.exists(args.input):
|
|
print('Fatal Error: The given input file does not exist (%s)' % args.input)
|
|
fatal_error = True
|
|
|
|
if not os.path.exists(args.output) and _USE_DIR:
|
|
print('Error: The given output directory does not exist (%s)')
|
|
print('Attempting to create path "%s"')
|
|
try:
|
|
os.makedirs(args.output)
|
|
print('Successfully created output path "%s"' % args.output)
|
|
except BaseException as ex:
|
|
print('Fatal Error: Unable to create output path at "%s"' % args.output)
|
|
print(ex)
|
|
fatal_error = True
|
|
|
|
if args.img_height == 0 and args.img_width == 0 and args.scale == 0:
|
|
print('Fatal Error: No valid resizing value given')
|
|
fatal_error = True
|
|
|
|
if not args.linear and not args.area:
|
|
args.cubic = True
|
|
|
|
if fatal_error:
|
|
printer.write_no_prefix('')
|
|
_print_help()
|
|
print('Exiting...')
|
|
exit(1)
|
|
|
|
|
|
def _set_inter():
|
|
global _INTER, _INTER_NAME
|
|
if args.linear:
|
|
_INTER = cv2.INTER_LINEAR
|
|
_INTER_NAME = 'Linear'
|
|
elif args.area:
|
|
_INTER = cv2.INTER_AREA
|
|
_INTER_NAME = 'Area'
|
|
else:
|
|
_INTER = cv2.INTER_CUBIC
|
|
_INTER_NAME = 'Cubic'
|
|
|
|
|
|
def _set_dir():
|
|
global _USE_DIR
|
|
_USE_DIR = os.path.isdir(args.input)
|
|
|
|
|
|
def _set_in_out():
|
|
global _OUT_NAME_FORMAT
|
|
_OUT_NAME_FORMAT = args.name_format
|
|
|
|
|
|
def _set_verbosity():
|
|
global _VERBOSE, _QUIET
|
|
_VERBOSE = args.verbose
|
|
_QUIET = args.quiet
|
|
|
|
|
|
def _set_size():
|
|
global _NEW_X, _NEW_Y, _IMG_SCALE
|
|
_NEW_Y = args.img_height
|
|
_NEW_X = args.img_width
|
|
_IMG_SCALE = args.scale
|
|
|
|
|
|
def _set_color():
|
|
global _G_SCALE
|
|
_G_SCALE = args.gray_scale
|
|
|
|
|
|
# Setup function
|
|
def setup():
|
|
global args
|
|
print(ConsoleUtils.get_header(name, version, date, author))
|
|
sys.stdout = printer
|
|
_make_args()
|
|
args = parser.parse_args()
|
|
# get some arguments first...
|
|
_set_verbosity()
|
|
_set_dir()
|
|
_set_inter()
|
|
_set_size()
|
|
_set_color()
|
|
_set_in_out()
|
|
# validate the arguments
|
|
_check_args()
|
|
# Show the valid arguments...
|
|
_show_args()
|
|
|
|
|
|
# Cleanup function
|
|
def cleanup(err=False):
|
|
run_time = time.time() - _START_TIME
|
|
if not err:
|
|
print('Successfully Completed in %.4f s' % run_time)
|
|
else:
|
|
print('Application encountered an error %.4f s into processing.' % run_time)
|
|
|
|
|
|
# Executes main
|
|
if __name__ == '__main__':
|
|
exception = False
|
|
setup()
|
|
try:
|
|
main(args.input, args.output)
|
|
except:
|
|
ConsoleUtils.print_exception()
|
|
exception = True
|
|
cleanup(exception)
|