Source code for vtool.image

# -*- coding: utf-8 -*-
# LICENCE
from __future__ import absolute_import, division, print_function, unicode_literals
import six
import os
from os.path import exists, join  # NOQA
from os.path import splitext
from six.moves import zip, map, range  # NOQA
import numpy as np
from PIL import Image
from .util_math import TAU
from vtool import exif
import utool as ut
import ubelt as ub


try:
    import cv2

    CV2_INTERPOLATION_TYPES = {
        'nearest': cv2.INTER_NEAREST,
        'linear': cv2.INTER_LINEAR,
        'area': cv2.INTER_AREA,
        'cubic': cv2.INTER_CUBIC,
        'lanczos': cv2.INTER_LANCZOS4,
    }

    CV2_BORDER_TYPES = {
        'constant': cv2.BORDER_CONSTANT,
        'replicate': cv2.BORDER_REPLICATE,
        'reflect': cv2.BORDER_REFLECT,
        'wrap': cv2.BORDER_WRAP,
        'reflect101': cv2.BORDER_REFLECT101,
        'tranparent': cv2.BORDER_TRANSPARENT,
        'isolated': cv2.BORDER_ISOLATED,
    }

    CV2_WARP_KWARGS = {
        'flags': CV2_INTERPOLATION_TYPES['lanczos'],
        'borderMode': cv2.BORDER_CONSTANT,
    }

    try:
        IMREAD_COLOR = cv2.IMREAD_COLOR
    except AttributeError:
        from distutils.version import LooseVersion

        cv2_version = LooseVersion(cv2.__version__)
        print('UNKNOWN cv2_version = {!r}'.format(cv2_version))
        assert cv2_version.version[0] <= 2
        IMREAD_COLOR = cv2.CV_LOAD_IMAGE_COLOR
except ImportError:
    print('ERROR: import cv2 is failing!')
    cv2 = ut.DynStruct()
    cv2.BORDER_CONSTANT = None
    cv2.INTER_LANCZOS4 = None
    cv2.CV_AA = None
    cv2.FONT_HERSHEY_SIMPLEX = None
    cv2.INTER_NEAREST = None


try:
    LINE_AA = cv2.LINE_AA
except AttributeError:
    LINE_AA = cv2.CV_AA

# cv2.BORDER_CONSTANT     cv2.BORDER_REFLECT      cv2.BORDER_REPLICATE
# cv2.BORDER_DEFAULT      cv2.BORDER_REFLECT101   cv2.BORDER_TRANSPARENT
# cv2.BORDER_ISOLATED     cv2.BORDER_REFLECT_101  cv2.BORDER_WRAP


EXIF_TAG_GPS = 'GPSInfo'
EXIF_TAG_DATETIME = 'DateTimeOriginal'


# References:
# http://docs.opencv.org/trunk/doc/py_tutorials/py_gui/py_image_display/py_image_display.html
# cv2.IMREAD_COLOR
# cv2.IMREAD_GRAYSCALE
# cv2.IMREAD_UNCHANGED


def _rectify_border_mode(border_mode, default=cv2.BORDER_CONSTANT):
    """Converts argument to cv2 style"""
    if border_mode is None:
        return default
    elif isinstance(border_mode, six.text_type):
        return CV2_BORDER_TYPES[border_mode]
    else:
        return border_mode


def _rectify_interpolation(interp, default=cv2.INTER_LANCZOS4):
    """
    Converts interpolation into flags suitable cv2 functions

    Args:
        interp (int or str): string or cv2-style interpolation type
        default (int): cv2 interpolation flag to use if `interp` is None

    Returns:
        int: flag specifying interpolation type that can be passed to
           functions like cv2.resize, cv2.warpAffine, etc...
    """
    if interp is None:
        return default
    elif isinstance(interp, six.text_type):
        try:
            return CV2_INTERPOLATION_TYPES[interp]
        except KeyError:
            print(
                'Valid values for interpolation are {}'.format(
                    list(CV2_INTERPOLATION_TYPES.keys())
                )
            )
            raise
    else:
        return interp


[docs]def montage(img_list, dsize, rng=np.random, method='random', return_debug=False): """ Creates a montage / collage from a set of images CommandLine: python -m vtool.image --exec-montage:0 --show python -m vtool.image --exec-montage:1 Example: >>> # SLOW_DOCTEST >>> # xdoctest: +SKIP >>> from vtool.image import * # NOQA >>> img_list0 = testdata_imglist() >>> img_list1 = [resize_to_maxdims(img, (256, 256)) for img in img_list0] >>> num = 4 >>> img_list = list(ub.flatten([img_list1] * num)) >>> dsize = (700, 700) >>> rng = np.random.RandomState(42) >>> method = 'unused' >>> #method = 'random' >>> dst, debug_info = montage(img_list, dsize, rng, method=method, >>> return_debug=True) >>> place_img = debug_info.get('place_img_', np.ones((2, 2))) >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> pt.imshow(dst, pnum=(1, 2, 1)) >>> pt.imshow(place_img / place_img.max(), pnum=(1, 2, 2)) >>> ut.show_if_requested() Example: >>> # SLOW_DOCTEST >>> # xdoctest: +SKIP >>> import wbia >>> import random >>> from os.path import join, expanduser, abspath >>> from vtool.image import * # NOQA >>> ibs = wbia.opendb('GZC') >>> gid_list0 = ibs.get_valid_gids() >>> img_list = [] >>> for i in range(6000): >>> print(i) >>> try: >>> gid = random.choice(gid_list0) >>> image = ibs.get_images(gid) >>> image = resize_to_maxdims(image, (512, 512)) >>> img_list.append(image) >>> except Exception: >>> pass >>> dsize = (19200, 10800) >>> rng = np.random.RandomState(42) >>> dst = montage(img_list, dsize, rng) >>> filepath = abspath(expanduser(join('~', 'Desktop', 'montage.jpg'))) >>> print('Writing to: %r' % (filepath, )) >>> imwrite(filepath, dst) """ import vtool as vt channels = 3 shape = tuple(dsize[::-1]) + (channels,) dst = np.zeros(shape, dtype=np.uint8) use_placement_prob = method == 'unused' if use_placement_prob: # TODO: place images in places that other images have not been placed yet place_img = np.ones(shape[0:2], dtype=np.float) # place_img[ # place_img = vt.gaussian_patch(shape[0:2], np.array(shape[0:2]) * .1) # place_img = vt.gaussian_patch(shape[0:2], np.array(shape[0:2]) * .3) temp_img = np.ones(shape[0:2], dtype=np.float) # Enumerate valid 2d locations xy_locs_ = np.meshgrid( np.arange(place_img.shape[1]), np.arange(place_img.shape[0]) ) xy_locs = np.vstack((xy_locs_[0].flatten(), xy_locs_[1].flatten())).T for img in img_list: # np.ones(img.shape, dtype=np.uint8) * 255 # vt.warp_patch_onto_kpts() w, h = vt.get_size(img) qw = w / 3.0 qh = h / 3.0 tx_pdf = (-qw, dsize[0] + qw) ty_pdf = (-qh, dsize[1] + qh) # txy_pdf = [] if use_placement_prob: # Enumerate probability of valid 2d locations # Renomralize place image np.divide(place_img, place_img.sum(), out=place_img) # import utool # utool.embed() if True: window_frac = 0.125 w = h = int(round(min(dst.shape[0:2]) * window_frac)) element = cv2.getStructuringElement(cv2.MORPH_CROSS, (w, h)) place_img_ = place_img place_img_ = cv2.erode(place_img_, element) np.divide(place_img_, place_img_.sum(), out=place_img_) xy_prob = place_img_.flatten() else: xy_prob = place_img.flatten() xy_loc_idxs = np.arange(len(xy_locs)) # np.ones(img.shape, dtype=np.uint8) * 255 # overwrite tx_pdf with a better value idx = rng.choice(xy_loc_idxs, size=1, replace=True, p=xy_prob) tx, ty = xy_locs[idx][0] tx_pdf = (tx, tx) ty_pdf = (ty, ty) Aff = vt.random_affine_transform( tx_pdf=tx_pdf, ty_pdf=ty_pdf, theta_pdf=(-TAU / 32, TAU / 32), rng=rng ) cv2.warpAffine( img, Aff[0:2], dsize, dst=dst, flags=cv2.INTER_LANCZOS4, borderMode=cv2.BORDER_TRANSPARENT, ) if use_placement_prob: # Denote that an image has been placed here. patch = vt.gaussian_patch(img.shape[0:2], np.array(img.shape[0:2]) * 0.3) # np.add(patch, min(0, patch.min()), out=patch) np.divide(patch, patch.max(), out=patch) np.subtract(1, patch, out=patch) # patch[:] = 0 # Reset temp image temp_img[:] = 1 # Align patch with placement image cv2.warpAffine( patch, Aff[0:2], dsize, dst=temp_img, flags=cv2.INTER_LANCZOS4, borderMode=cv2.BORDER_TRANSPARENT, ) # Renormalize image # np.add(temp_img, min(0, temp_img.min()), out=temp_img) # np.divide(temp_img, temp_img.max(), out=temp_img) np.clip(temp_img, 0, 1, out=temp_img) # Blend with placement probability image np.multiply(temp_img, place_img, out=place_img) # (255 - get_pixel_dist(dst, 0)) / 255 if return_debug: debug_info = {} if use_placement_prob: debug_info['place_img'] = place_img debug_info['place_img_'] = place_img_ return dst, debug_info return dst
[docs]def imread( img_fpath, grayscale=False, orient=False, flags=None, force_pil=None, delete_if_corrupted=False, **kwargs ): r""" Wrapper around the opencv imread function. Handles remote uris. Args: img_fpath (str): file path string grayscale (bool): (default = False) orient (bool): (default = False) flags (None): opencv flags (default = None) force_pil (bool): (default = None) delete_if_corrupted (bool): (default = False) Returns: ndarray: imgBGR CommandLine: python -m vtool.image --test-imread python -m vtool.image --test-imread:1 python -m vtool.image --test-imread:2 References: http://docs.opencv.org/modules/core/doc/utility_and_system_functions_and_macros.html#error http://stackoverflow.com/questions/23572241/cv2-threshold-error-210 Example: >>> # ENABLE_DOCTEST >>> from vtool.image import * # NOQA >>> img_fpath = ut.grab_test_imgpath('carl.jpg') >>> imgBGR1 = imread(img_fpath, grayscale=False) >>> imgBGR2 = imread(img_fpath, grayscale=True) >>> imgBGR3 = imread(img_fpath, orient=True) >>> assert imgBGR1.shape == (250, 300, 3) >>> assert imgBGR2.shape == (250, 300) >>> # assert np.all(imgBGR1 == imgBGR3) >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> pt.imshow(imgBGR1, pnum=(2, 2, 1)) >>> pt.imshow(imgBGR2, pnum=(2, 2, 2)) >>> pt.imshow(imgBGR3, pnum=(2, 2, 3)) >>> ut.show_if_requested() Example: >>> # ENABLE_DOCTEST >>> from vtool.image import * # NOQA >>> img_url = 'http://images.summitpost.org/original/769474.JPG' >>> img_fpath = ut.grab_file_url(img_url) >>> imgBGR1 = imread(img_url) >>> imgBGR2 = imread(img_fpath) >>> #imgBGR2 = imread(img_fpath, force_pil=False, flags=cv2.IMREAD_UNCHANGED) >>> print('imgBGR.shape = %r' % (imgBGR1.shape,)) >>> print('imgBGR2.shape = %r' % (imgBGR2.shape,)) >>> result = str(imgBGR1.shape) >>> diff_pxls = imgBGR1 != imgBGR2 >>> num_diff_pxls = diff_pxls.sum() >>> print(result) >>> print('num_diff_pxls=%r/%r' % (num_diff_pxls, diff_pxls.size)) >>> assert num_diff_pxls == 0 >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> diffMag = np.linalg.norm(imgBGR2 / 255. - imgBGR1 / 255., axis=2) >>> pt.imshow(imgBGR1, pnum=(1, 3, 1)) >>> pt.imshow(diffMag / diffMag.max(), pnum=(1, 3, 2)) >>> pt.imshow(imgBGR2, pnum=(1, 3, 3)) >>> ut.show_if_requested() (2736, 3648, 3) Example: >>> # ENABLE_DOCTEST >>> from vtool.image import * # NOQA >>> url = 'http://www.sherv.net/cm/emo/funny/2/big-dancing-banana-smiley-emoticon.gif' >>> img_fpath = ut.grab_file_url(url) >>> delete_if_corrupted = False >>> grayscale = False >>> imgBGR = imread(img_fpath, grayscale=grayscale) >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> pt.imshow(imgBGR) >>> ut.show_if_requested() """ path, ext = splitext(img_fpath) orient_ = 'auto' if orient in ['auto', 'on', True] else False use_pil = orient_ or ext.lower() == '.gif' or force_pil is True if img_fpath.startswith('http://') or img_fpath.startswith('https://'): imgBGR = imread_remote_url( img_fpath, grayscale=grayscale, orient=orient, use_pil=use_pil, flags=flags ) elif img_fpath.startswith('s3://'): imgBGR = imread_remote_s3( img_fpath, grayscale=grayscale, orient=orient, use_pil=use_pil, flags=flags ) else: try: if use_pil: # If we want to open with auto orient, only open once with PIL # Otherwise, open with OpenCV (faster) and reorient if given # the known orientation of the image # pil_img = Image.open(img_fpath) # print("USE PIL") with Image.open(img_fpath) as pil_img: imgBGR = _fix_orient_pil_img( pil_img, grayscale=grayscale, orient=orient_, **kwargs ) # with Image.open(img_fpath) as pil_img: # breaks? # pil_img.close() # breaks? else: # print("USE OPENCV") if flags is None: flags = cv2.IMREAD_GRAYSCALE if grayscale else IMREAD_COLOR # TODO cv2.IMREAD_UNCHANGED imgBGR = cv2.imread(img_fpath, flags=flags) except cv2.error as cv2ex: ut.printex(cv2ex, 'opencv error', iswarning=True) # print('cv2error dict = ' + ub.repr2(cv2ex.__dict__)) # print('cv2error dirlist = ' + ub.repr2(dir(cv2ex))) # print('cv2error args = ' + repr(cv2ex.args)) # print('cv2error message = ' + repr(cv2ex.message)) # cv2error args = # ('c:/Users/joncrall/code/opencv/modules/core/src/alloc.cpp:52: error: # (-4) Failed to allocate 22311168 bytes in function # OutOfMemoryError\n',) # cv2error message = #'c:/Users/joncrall/code/opencv/modules/core/src/alloc.cpp:52: error: # (-4) #Failed to allocate 22311168 bytes in function # OutOfMemoryError\n' imgBGR = None # ismem_error = cv2ex.message.find('error: (-4)') > -1 ismem_error = cv2ex.message.find('OutOfMemoryError') > -1 if ismem_error: raise MemoryError('Memory Error while reading img_fpath=%s' % img_fpath) except Exception as ex: ut.printex(ex, iswarning=True) imgBGR = None if imgBGR is None: # if not exists(img_fpath): if not ut.checkpath(img_fpath, verbose=True): raise IOError('cannot read img_fpath=%s does not exist.' % img_fpath) else: if not os.access(img_fpath, os.R_OK): raise PermissionError( 'cannot read img_fpath={} access denied.'.format(img_fpath) ) if delete_if_corrupted: # Probably should depricate this. A bit out of scope msg = ( 'Cannot read corrupted img_fpath=%s, requires deletion.' % img_fpath ) print('[vt.imread] deleting corrupted image') ut.delete(img_fpath) else: msg = ( 'Cannot read img_fpath=%s, ' 'seems corrupted or memory error.' % img_fpath ) print('[vt.imread] ' + msg) raise IOError(msg) if not isinstance(orient, bool) and orient in exif.ORIENTATION_DICT: if False: print('[vt.imread] Applying orientation %r' % (orient,)) imgBGR = _fix_orientation(imgBGR, orient) return imgBGR
[docs]def imread_remote_s3(img_fpath, **kwargs): import io try: s3_dict = ut.s3_str_decode_to_dict(img_fpath) contents = ut.read_s3_contents(**s3_dict) # btyedata = np.asarray(bytearray(contents), dtype=np.uint8) # imgBGR = cv2.imdecode(btyedata, -1) with io.BytesIO(contents) as image_stream: imgBGR = _imread_bytesio(image_stream, **kwargs) # with Image.open(image_stream) as pil_img: # imgBGR = _fix_orient_pil_img(pil_img, **kwargs) except AttributeError: pass return imgBGR
[docs]def imread_remote_url(img_url, **kwargs): from six.moves import urllib import io print('USE PIL REMOTE') addinfourl = urllib.request.urlopen(img_url) try: # image_file = io.BytesIO(addinfourl.read()) # pil_img = Image.open(image_file) with io.BytesIO(addinfourl.read()) as image_stream: imgBGR = _imread_bytesio(image_stream, **kwargs) # nparr = np.fromstring(image_stream.getvalue(), np.uint8) # imgBGR = cv2.imdecode(nparr, cv2.IMREAD_COLOR) # cv2.IMREAD_COLOR in OpenCV 3.1 # imgBGR = cv2.imread(image_file) # with Image.open(image_file) as pil_img: # imgBGR = _fix_orient_pil_img(pil_img, **kwargs) except IOError: pass finally: addinfourl.close() return imgBGR
def _imread_bytesio(image_stream, use_pil=False, flags=None, **kwargs): if use_pil: with Image.open(image_stream) as pil_img: imgBGR = _fix_orient_pil_img(pil_img, **kwargs) else: if flags is None: grayscale = kwargs.get('grayscale', False) flags = cv2.IMREAD_GRAYSCALE if grayscale else IMREAD_COLOR nparr = np.fromstring(image_stream.getvalue(), np.uint8) imgBGR = cv2.imdecode(nparr, flags=flags) # cv2.IMREAD_COLOR in OpenCV 3.1 return imgBGR def _fix_orient_pil_img(pil_img, grayscale=False, orient=False, **kwargs): if orient == 'auto': exif_dict = exif.get_exif_dict(pil_img) orient = exif.get_orientation(exif_dict, **kwargs) # if pil_img.format in ['MPO', 'GIF']: np_img = np.array(pil_img.convert('RGB')) # else: # np_img = np.array(pil_img) if grayscale: imgBGR = cv2.cvtColor(np_img, cv2.COLOR_RGB2GRAY) else: imgBGR = cv2.cvtColor(np_img, cv2.COLOR_RGB2BGR) if not isinstance(orient, bool) and orient in exif.ORIENTATION_DICT: imgBGR = _fix_orientation(imgBGR, orient, **kwargs) return imgBGR def _fix_orientation(imgBGR, orient, fallback=True): assert not isinstance(orient, bool) and orient in exif.ORIENTATION_DICT orient_ = exif.ORIENTATION_DICT[orient] if orient_ == exif.ORIENTATION_000: return imgBGR dsize_h, dsize_w = imgBGR.shape[:2] if orient_ in [exif.ORIENTATION_090, exif.ORIENTATION_270]: dsize = ( dsize_h, dsize_w, ) else: dsize = ( dsize_w, dsize_h, ) # FIXME; rotation changes the shape of the images # rotate_image does not do this, it must incorrectly clip areas. # TODO 90 degree optimizations if orient_ == exif.ORIENTATION_090: # return np.rot90(imgBGR, k=1) return rotate_image(imgBGR, TAU * 0.25, dsize=dsize) elif orient_ == exif.ORIENTATION_180: # return np.rot90(imgBGR, k=2) return rotate_image(imgBGR, TAU * 0.50, dsize=dsize) elif orient_ == exif.ORIENTATION_270: # return np.rot90(imgBGR, k=3) return rotate_image(imgBGR, TAU * 0.75, dsize=dsize) elif fallback: return imgBGR else: raise IOError('Could not fix the image orientation')
[docs]def imwrite(img_fpath, imgBGR, fallback=False): """ References: http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html Args: img_fpath (str): file path string imgBGR (ndarray[uint8_t, ndim=2]): image data in opencv format (blue, green, red) fallback (bool): (default = False) CommandLine: python -m vtool.image --exec-imwrite Example: >>> # ENABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> import utool as ut >>> img_fpath1 = ut.grab_test_imgpath('zebra.png') >>> imgBGR = vt.imread(img_fpath1) >>> img_dpath = ub.ensure_app_cache_dir('vtool', 'testwrite') >>> img_fpath2 = ut.unixjoin(img_dpath, 'zebra.png') >>> fallback = False >>> imwrite(img_fpath2, imgBGR, fallback=fallback) >>> imgBGR2 = vt.imread(img_fpath2) >>> assert np.all(imgBGR2 == imgBGR) """ try: cv2.imwrite(img_fpath, imgBGR) except Exception as ex: if fallback: try: imwrite_fallback(img_fpath, imgBGR) except Exception: pass msg = '[vt.image] ERROR writing: %s' % (img_fpath,) ut.printex(ex, msg, keys=['imgBGR.shape']) raise
[docs]def imwrite_fallback(img_fpath, imgBGR): try: import matplotlib.image as mpl_image imgRGB = cv2.cvtColor(imgBGR, cv2.COLOR_BGR2RGB) mpl_image.imsave(img_fpath, imgRGB) return None except Exception as ex: msg = '[vt.image] FALLBACK ERROR writing: %s' % (img_fpath,) ut.printex(ex, msg, keys=['imgBGR.shape']) raise
[docs]def get_size(img): """Returns the image size in (width, height)""" wh = img.shape[0:2][::-1] return wh
[docs]def get_num_channels(img): """Returns the number of color channels""" ndims = len(img.shape) if ndims == 2: nChannels = 1 elif ndims == 3 and img.shape[2] == 3: nChannels = 3 elif ndims == 3 and img.shape[2] == 4: nChannels = 4 elif ndims == 3 and img.shape[2] == 1: nChannels = 1 else: raise ValueError( 'Cannot determine number of channels ' 'for img.shape={}'.format(img.shape) ) return nChannels
[docs]def subpixel_values(img, pts): """ References: stackoverflow.com/uestions/12729228/simple-efficient-binlinear-interpolation-of-images-in-numpy-and-python SeeAlso: cv2.getRectSubPix(image, patchSize, center[, patch[, patchType]]) """ # Image info nChannels = get_num_channels(img) height, width = img.shape[0:2] # Subpixel locations to sample ptsT = pts.T x = ptsT[0] y = ptsT[1] # Get quantized pixel locations near subpixel pts x0 = np.floor(x).astype(int) x1 = x0 + 1 y0 = np.floor(y).astype(int) y1 = y0 + 1 # Make sure the values do not go past the boundary x0 = np.clip(x0, 0, width - 1) x1 = np.clip(x1, 0, width - 1) y0 = np.clip(y0, 0, height - 1) y1 = np.clip(y1, 0, height - 1) # Find bilinear weights wa = (x1 - x) * (y1 - y) wb = (x1 - x) * (y - y0) wc = (x - x0) * (y1 - y) wd = (x - x0) * (y - y0) if nChannels != 1: wa = np.array([wa] * nChannels).T wb = np.array([wb] * nChannels).T wc = np.array([wc] * nChannels).T wd = np.array([wd] * nChannels).T # Sample values Ia = img[y0, x0] Ib = img[y1, x0] Ic = img[y0, x1] Id = img[y1, x1] # Perform the bilinear interpolation subpxl_vals = (wa * Ia) + (wb * Ib) + (wc * Ic) + (wd * Id) return subpxl_vals
[docs]def open_image_size(image_fpath): """ Gets image size from an image on disk Args: image_fpath (str): Returns: tuple: size (width, height) CommandLine: python -m vtool.image --test-open_image_size Doctest: >>> from vtool.image import * # NOQA >>> image_fpath = ut.grab_test_imgpath('patsy.jpg') >>> size = open_image_size(image_fpath) >>> result = ('size = %s' % (str(size),)) >>> print(result) size = (800, 441) Ignore: >>> # Confirm that Image.open is a lazy load >>> import vtool as vt >>> import utool as ut >>> import timeit >>> setup = ut.codeblock( >>> ''' >>> from PIL import Image >>> import utool as ut >>> import vtool as vt >>> image_fpath = ut.grab_test_imgpath('patsy.jpg') >>> ''' >>> ) >>> t1 = timeit.timeit('Image.open(image_fpath)', setup, number=100) >>> t2 = timeit.timeit('Image.open(image_fpath).size', setup, number=100) >>> t3 = timeit.timeit('vt.open_image_size(image_fpath)', setup, number=100) >>> t4 = timeit.timeit('vt.imread(image_fpath).shape', setup, number=100) >>> t5 = timeit.timeit('Image.open(image_fpath).getdata()', setup, number=100) >>> print('t1 = %r' % (t1,)) >>> print('t2 = %r' % (t2,)) >>> print('t3 = %r' % (t3,)) >>> print('t4 = %r' % (t4,)) >>> print('t5 = %r' % (t5,)) >>> assert t2 < t5 >>> assert t3 < t4 """ try: pil_img = Image.open(image_fpath) size = pil_img.size except IOError as ex: print('ERROR: Failed open image size') ut.checkpath(image_fpath, verbose=True) ut.printex(ex, 'ERROR: Failed open image size') raise return size
[docs]def cvt_BGR2L(imgBGR): imgLAB = cv2.cvtColor(imgBGR, cv2.COLOR_BGR2LAB) imgL = imgLAB[:, :, 0] return imgL
[docs]def cvt_BGR2RGB(imgBGR): imgRGB = cv2.cvtColor(imgBGR, cv2.COLOR_BGR2RGB) return imgRGB
[docs]def warpAffine(img, Aff, dsize, assume_float01=True): """ dsize = (width, height) of return image Args: img (ndarray[uint8_t, ndim=2]): image data Aff (ndarray): affine matrix dsize (tuple): width, height Returns: ndarray: warped_img CommandLine: python -m vtool.image --test-warpAffine --show Example: >>> # DISABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> img_fpath = ut.grab_test_imgpath('carl.jpg') >>> img = vt.imread(img_fpath) >>> Aff = vt.rotation_mat3x3(TAU / 8) >>> dsize = vt.get_size(img) >>> warped_img = warpAffine(img, Aff, dsize) >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> pt.imshow(warped_img) >>> ut.show_if_requested() Ignore: >>> import skimage.transform >>> %timeit cv2.warpAffine(img, Aff[0:2], tuple(dsize), **CV2_WARP_KWARGS) >>> 100 loops, best of 3: 7.95 ms per loop >>> skimage.transform.AffineTransform >>> tf = skimage.transform.AffineTransform(rotation=TAU / 8) >>> Aff_ = tf.params >>> out = skimage.transform._warps_cy._warp_fast(img[:, :, 0], Aff_, output_shape=dsize, mode='constant', order=1) >>> %timeit skimage.transform._warps_cy._warp_fast(img[:, :, 0], Aff_, output_shape=dsize, mode='constant', order=1) >>> 100 loops, best of 3: 5.74 ms per loop >>> %timeit cv2.warpAffine(img[:, :, 0], Aff[0:2], tuple(dsize), **CV2_WARP_KWARGS) >>> 100 loops, best of 3: 5.13 ms per loop >>> CONCLUSION, cv2 transforms are better """ warped_img = cv2.warpAffine(img, Aff[0:2], tuple(dsize), **CV2_WARP_KWARGS) if assume_float01 and img.dtype.kind == 'f': # Ensure that image intensity doesnt go out of range warped_img = warped_img.clip(0, 1) return warped_img
[docs]def warpHomog(img, Homog, dsize, assume_float01=True): """ dsize = (width, height) of return image Example: >>> img = np.random.rand(224, 224) >>> Homog = np.random.rand(3, 3) >>> dsize = (128, 128) >>> warped_img = warpHomog(img, Homog, dsize) """ warped_img = cv2.warpPerspective(img, Homog, tuple(dsize), **CV2_WARP_KWARGS) if assume_float01 and img.dtype.kind == 'f': # Ensure that image intensity doesnt go out of range warped_img = warped_img.clip(0, 1) return warped_img
[docs]def resize(img, dsize, interpolation=None): interpolation = _rectify_interpolation(interpolation) return cv2.resize(img, dsize, interpolation=interpolation)
[docs]def resize_mask(mask, chip, interpolation=None): dsize = get_size(chip) return resize(mask, dsize, interpolation)
[docs]def resize_image_by_scale(img, scale, interpolation=None): interpolation = _rectify_interpolation(interpolation) dsize, tonew_sf = get_round_scaled_dsize(get_size(img), scale) new_img = cv2.resize(img, dsize, interpolation=interpolation) return new_img
[docs]def resized_dims_and_ratio(img_size, max_dsize): """ returns resized dimensions to get ``img_size`` to fit into ``max_dsize`` FIXME: Should specifying a None force the use of the original dim? Args: img_size (tuple): max_dsize (tuple): Returns: tuple: (dsize, ratio) CommandLine: python -m vtool.image resized_dims_and_ratio --show Example: >>> # ENABLE_DOCTEST >>> from vtool.image import * # NOQA >>> img_size = (200, 100) >>> max_dsize = (150, 150) >>> (dsize, ratio) = resized_dims_and_ratio(img_size, max_dsize) >>> result = ('(dsize, ratio) = %s' % (ub.repr2((dsize, ratio), nl=0),)) >>> print(result) (dsize, ratio) = ((150, 75), 0.75) Example: >>> # ENABLE_DOCTEST >>> from vtool.image import * # NOQA >>> img_size = (200, 100) >>> max_dsize = (5000, 1000) >>> (dsize, ratio) = resized_dims_and_ratio(img_size, max_dsize) >>> result = ('(dsize, ratio) = %s' % (ub.repr2((dsize, ratio), nl=0),)) >>> print(result) (dsize, ratio) = ((2000, 1000), 10.0) Example: >>> # ENABLE_DOCTEST >>> from vtool.image import * # NOQA >>> img_size = (200, 100) >>> max_dsize = (5000, None) >>> (dsize, ratio) = resized_dims_and_ratio(img_size, max_dsize) >>> result = ('(dsize, ratio) = %s' % (ub.repr2((dsize, ratio), nl=0),)) >>> print(result) (dsize, ratio) = ((200, 100), 1.0) Example: >>> # ENABLE_DOCTEST >>> from vtool.image import * # NOQA >>> img_size = (200, 100) >>> max_dsize = (None, None) >>> (dsize, ratio) = resized_dims_and_ratio(img_size, max_dsize) >>> result = ('(dsize, ratio) = %s' % (ub.repr2((dsize, ratio), nl=0),)) >>> print(result) (dsize, ratio) = ((200, 100), 1.0) """ # if isinstance(max_dsize, (tuple, list, np.ndarray)): max_width, max_height = max_dsize width, height = img_size if False: if max_width is not None and max_height is not None: ratio = min(max_width / width, max_height / height) elif max_width is not None: ratio = max_width / width elif max_width is not None: ratio = max_height / height else: ratio = 1.0 else: if max_width is None: max_width = width if max_height is None: max_height = height ratio = min(max_width / width, max_height / height) dsize = (int(round(width * ratio)), int(round(height * ratio))) return dsize, ratio
[docs]def resized_clamped_thumb_dims(img_size, max_dsize): dsize_, ratio = resized_dims_and_ratio(img_size, max_dsize) dsize = img_size if ratio > 1 else dsize_ sx = dsize[0] / img_size[0] sy = dsize[1] / img_size[1] return dsize, sx, sy
[docs]def pad_image_ondisk( img_fpath, pad_, out_fpath=None, value=0, borderType=cv2.BORDER_CONSTANT, **kwargs ): r""" Returns: str: out_fpath - file path string CommandLine: python -m vtool.image pad_image_ondisk Example: >>> # DISABLE_DOCTEST >>> from vtool.image import * # NOQA >>> img_fpath = ut.get_argval('--fpath', type_=str) >>> pad_ = '?' >>> out_fpath = None >>> value = 0 >>> borderType = 0 >>> out_fpath = pad_image_ondisk(img_fpath, pad_, out_fpath, value, borderType) >>> result = ('out_fpath = %s' % (ub.repr2(out_fpath),)) >>> print(result) """ imgBGR = imread(img_fpath) imgBGR2 = cv2.copyMakeBorder( imgBGR, pad_, pad_, pad_, pad_, borderType=cv2.BORDER_CONSTANT, value=value ) imgBGR2[:pad_, :] = value imgBGR2[-pad_:, :] = value imgBGR2[:, :pad_] = value imgBGR2[:, -pad_:] = value if out_fpath is None: out_fpath = ut.augpath(img_fpath, '_pad=%r' % (pad_)) imwrite(out_fpath, imgBGR2) return out_fpath
[docs]def pad_image(imgBGR, pad_, value=0, borderType=cv2.BORDER_CONSTANT): imgBGR2 = cv2.copyMakeBorder( imgBGR, pad_, pad_, pad_, pad_, borderType=cv2.BORDER_CONSTANT, value=value ) return imgBGR2
[docs]def get_pixel_dist(img, pixel, channel=None): """ pixel = fillval isfill = mask2d """ if not isinstance(pixel, np.ndarray): if isinstance(pixel, (list, tuple)): pixel = np.array(pixel) else: pixel = np.array([pixel]) mask2d = np.abs(img - pixel[None, None, :]) if len(img.shape) > 2: if channel is None: mask2d = np.sum(mask2d, axis=2) else: mask2d = mask2d[:, :, channel] return mask2d
[docs]def make_white_transparent(imgBGR): r""" Args: imgBGR (ndarray[uint8_t, ndim=2]): image data (blue, green, red) Returns: ndarray: imgBGRA CommandLine: python -m vtool.image make_white_transparent --show Example: >>> # DISABLE_DOCTEST >>> from vtool.image import * # NOQA >>> imgBGR = imread(ut.get_argval('--fpath', type_=str)) >>> imgBGRA = make_white_transparent(imgBGR) >>> result = ('imgBGRA = %s' % (ub.repr2(imgBGRA),)) >>> print(result) >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> ut.show_if_requested() """ import vtool as vt dist = vt.get_pixel_dist(imgBGR, [255, 255, 255]) # grayflags = np.logical_and(imgBGR[:, :, 0] == imgBGR[:, :, 1], imgBGR[:, :, 1] == imgBGR[:, :, 2]) # dist = dist / dist.max() imgBGRA = cv2.cvtColor(imgBGR, cv2.COLOR_BGR2BGRA) # imgBGRA[:, :, 3] = np.maximum(np.round(dist * 255), ~grayflags * 255) # imgBGRA[:, :, 3] = np.round(dist * 255) imgBGRA[:, :, 3] = np.round(dist / 3) return imgBGRA
[docs]def crop_out_imgfill(img, fillval=None, thresh=0, channel=None): r""" Crops image to remove fillval Args: img (ndarray[uint8_t, ndim=2]): image data fillval (None): (default = None) thresh (int): (default = 0) Returns: ndarray: cropped_img CommandLine: python -m vtool.image --exec-crop_out_imgfill Example: >>> # ENABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> img = vt.get_stripe_patch() >>> img = (img * 255).astype(np.uint8) >>> print(img) >>> img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) >>> fillval = np.array([25, 25, 25]) >>> thresh = 0 >>> cropped_img = crop_out_imgfill(img, fillval, thresh) >>> cropped_img2 = cv2.cvtColor(cropped_img, cv2.COLOR_RGB2GRAY) >>> result = ('cropped_img2 = \n%s' % (str(cropped_img2),)) >>> print(result) Example: >>> # ENABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> img = vt.get_stripe_patch() >>> img = (img * 255).astype(np.uint8) >>> print(img) >>> fillval = 25 >>> thresh = 0 >>> cropped_img = crop_out_imgfill(img, fillval, thresh) >>> result = ('cropped_img = \n%s' % (str(cropped_img),)) >>> print(result) """ import vtool as vt if fillval is None: fillval = np.array([255] * get_num_channels(img)) # for colored images # with ut.embed_on_exception_context: pixel = fillval dist = get_pixel_dist(img, pixel, channel=channel) isfill = dist <= thresh # isfill should just be 2D # Fix shape that comes back as (1, W, H) if len(isfill.shape) == 3 and isfill.shape[0] == 1: if np.all(np.greater(isfill.shape[1:2], [4, 4])): isfill = isfill[0] rowslice, colslice = vt.get_crop_slices(isfill) cropped_img = img[rowslice, colslice] return cropped_img
[docs]def clipwhite_ondisk(fpath_in, fpath_out=None, verbose=ut.NOT_QUIET): r""" Strips white borders off an image on disk Args: fpath_in (str): fpath_out (None): (default = None) verbose (bool): verbosity flag(default = True) Returns: str: fpath_out CommandLine: python -m vtool.image clipwhite_ondisk """ import vtool as vt if fpath_out is None: fpath_out = ut.augpath(fpath_in, '_clipwhite') img = vt.imread(fpath_in, flags=cv2.IMREAD_UNCHANGED) if verbose: print('[clipwhite] img.shape = %r' % (img.shape,)) cropped_img = clipwhite(img) if verbose: print('[clipwhite] cropped_img.shape = %r' % (cropped_img.shape,)) vt.imwrite(fpath_out, cropped_img) return fpath_out
[docs]def clipwhite(img): """ Strips white borders off an image """ nChannels = get_num_channels(img) # thresh = 128 thresh = 64 if nChannels == 4: # alpha thresh = 12 fillval = 0 channel = 3 # imgBGRA # Clip alpha first cropped_img = crop_out_imgfill(img, fillval, thresh=thresh, channel=channel) # Clip white next fillval = np.array([255] * nChannels) cropped_img = crop_out_imgfill(cropped_img, fillval=fillval, thresh=thresh) # cropped_img = crop_out_imgfill(img, fillval=fillval, thresh=thresh) else: fillval = np.array([255] * nChannels) cropped_img = crop_out_imgfill(img, fillval=fillval, thresh=thresh) return cropped_img
[docs]def rotate_image_ondisk(img_fpath, theta, out_fpath=None, **kwargs): r""" Rotates an image on disk Args: img_fpath (?): theta (?): out_fpath (None): CommandLine: python -m vtool.image --test-rotate_image_ondisk Example: >>> # DISABLE_DOCTEST >>> from vtool.image import * # NOQA >>> # build test data >>> img_fpath = ut.grab_test_imgpath('star.png') >>> theta = TAU * 3 / 8 >>> # execute function >>> out_fpath = None >>> out_fpath_ = rotate_image_ondisk(img_fpath, theta, out_fpath) >>> print(out_fpath_) >>> if ut.get_argflag('--show') or ut.inIPython(): >>> import wbia.plottool as pt >>> pt.imshow(out_fpath_, pnum=(1, 1, 1)) >>> pt.show_if_requested() """ img = imread(img_fpath) imgR = rotate_image(img, theta, **kwargs) out_fpath_ = ( ut.augpath(img_fpath, augsuf='_theta=%r' % (theta)) if out_fpath is None else out_fpath ) imwrite(out_fpath_, imgR) return out_fpath_
[docs]def rotate_image(img, theta, border_mode=None, interpolation=None, dsize=None): r""" Rotates an image around its center Args: img (ndarray[uint8_t, ndim=2]): image data theta (?): CommandLine: python -m vtool.image --test-rotate_image Example: >>> # ENABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> # build test data >>> img = vt.get_test_patch('star2') >>> theta = TAU / 16.0 >>> # execute function >>> imgR = rotate_image(img, theta) >>> if ut.get_argflag('--show') or ut.inIPython(): >>> import wbia.plottool as pt >>> pt.imshow(img * 255, pnum=(1, 2, 1)) >>> pt.imshow(imgR * 255, pnum=(1, 2, 2)) >>> pt.show_if_requested() """ from vtool import linalg as ltool border_mode = _rectify_border_mode(border_mode) interpolation = _rectify_interpolation(interpolation) bbox0 = [0, 0, img.shape[1], img.shape[0]] if dsize is None: dsize = [img.shape[1], img.shape[0]] bbox1 = bbox0 else: bbox1 = [0, 0, dsize[0], dsize[1]] R = ltool.rotation_around_bbox_mat3x3(theta, bbox0, bbox1=bbox1) imgR = cv2.warpAffine( img, R[0:2], tuple(dsize), borderMode=border_mode, flags=interpolation ) return imgR
[docs]def shear(img, x_shear, y_shear, dsize=None, **kwargs): r""" Args: img (ndarray[uint8_t, ndim=2]): image data x_shear (?): y_shear (?): dsize (tuple): width, height CommandLine: python -m vtool.image --test-shear --show Example: >>> # DISABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> img_fpath = ut.grab_test_imgpath('carl.jpg') >>> img = vt.imread(img_fpath) >>> x_shear = 0.05 >>> y_shear = -0.05 >>> dsize = None >>> imgSh = shear(img, x_shear, y_shear, dsize) >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> pt.imshow(imgSh) >>> ut.show_if_requested() """ from vtool import linalg as ltool if dsize is None: dsize = get_size(img) shear_mat3x3 = ltool.shear_mat3x3(x_shear, y_shear) warp_kwargs = CV2_WARP_KWARGS.copy() warp_kwargs.update(kwargs) imgSh = cv2.warpAffine(img, shear_mat3x3[0:2], tuple(dsize), **warp_kwargs) return imgSh
[docs]def affine_warp_around_center( img, sx=1, sy=1, theta=0, shear=0, tx=0, ty=0, dsize=None, borderMode=cv2.BORDER_CONSTANT, flags=cv2.INTER_LANCZOS4, out=None, **kwargs ): r""" CommandLine: python -m vtool.image --test-affine_warp_around_center --show Example: >>> # DISABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> img_fpath = ut.grab_test_imgpath('carl.jpg') >>> img = vt.imread(img_fpath) / 255.0 >>> img = img.astype(np.float32) >>> dsize = (1000, 1000) >>> shear = .2 >>> theta = np.pi / 4 >>> tx = 0 >>> ty = 100 >>> sx = 1.5 >>> sy = 1.0 >>> borderMode = cv2.BORDER_CONSTANT >>> flags = cv2.INTER_LANCZOS4 >>> img_warped = affine_warp_around_center(img, sx=sx, sy=sy, ... theta=theta, shear=shear, tx=tx, ty=ty, dsize=dsize, ... borderMode=borderMode, flags=flags, borderValue=(.5, .5, .5)) >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> pt.imshow((img_warped * 255.0).astype(np.uint8)) >>> ut.show_if_requested() """ from vtool import linalg as ltool if dsize is None: dsize = (img.shape[1], img.shape[0]) else: dsize = tuple(dsize) w2, h2 = dsize h1, w1 = img.shape[0:2] y1, x1 = h1 / 2.0, w1 / 2.0 y2, x2 = h2 / 2.0, w2 / 2.0 # MOVE AFFINE AROUND w.r.t new dsize Aff = ltool.affine_around_mat3x3(x1, y1, sx, sy, theta, shear, tx, ty, x2, y2) img_warped = cv2.warpAffine( img, Aff[0:2], dsize, dst=out, borderMode=borderMode, flags=flags, **kwargs ) # Fix grayscale channel issues if len(img.shape) == 3 and len(img_warped.shape) == 2: img_warped.shape = img_warped.shape + (1,) return img_warped
[docs]def get_round_scaled_dsize(dsize, scale): """ Returns an integer size and scale that best approximates the floating point scale on the original size Args: dsize (tuple): original width height scale (float or tuple): desired floating point scale factor """ try: sx, sy = scale except TypeError: sx = sy = scale w, h = dsize new_w = int(round(w * sx)) new_h = int(round(h * sy)) new_scale = new_w / w, new_h / h new_dsize = (new_w, new_h) return new_dsize, new_scale
[docs]def rectify_to_square(img, extreme='max'): h, w = img.shape[0:2] if w == h: return img else: extreme_fn = {'max': max, 'min': min}[extreme] d = extreme_fn(w, h) return resize(img, (d, d))
[docs]def rectify_to_float01(img, dtype=np.float32): """Ensure that an image is encoded using a float properly""" if img.dtype.kind in ('i', 'u'): assert img.max() <= 255 img_ = img.astype(dtype) / 255.0 else: img_ = img.astype(dtype) return img_
[docs]def rectify_to_uint8(img): """Ensure that an image is encoded in uint8 properly""" if img.dtype.kind in ('f'): if img.max() <= 1.0 or img.min() >= 0.0: raise ValueError( 'Bad input image. Stats={}'.format( ub.repr2(ut.get_stats(img.ravel()), precision=2) ) ) img_ = (img * 255.0).astype(np.uint8) else: img_ = img return img_
[docs]def make_channels_comparable(img1, img2): """ Broadcasts image arrays so they can have elementwise operations applied CommandLine: python -m vtool.image make_channels_comparable Example: >>> # DISABLE_DOCTEST >>> from vtool.image import * # NOQA >>> wh_basis = [(5, 5), (3, 5), (5, 3), (1, 1), (1, 3), (3, 1)] >>> for w, h in wh_basis: >>> shape_basis = [(w, h), (w, h, 1), (w, h, 3)] >>> # Test all permutations of shap inputs >>> for shape1, shape2 in ut.product(shape_basis, shape_basis): >>> print('* input shapes: %r, %r' % (shape1, shape2)) >>> img1 = np.empty(shape1) >>> img2 = np.empty(shape2) >>> img1, img2 = make_channels_comparable(img1, img2) >>> print('... output shapes: %r, %r' % (img1.shape, img2.shape)) >>> elem = (img1 + img2) >>> print('... elem(+) shape: %r' % (elem.shape,)) >>> assert elem.size == img1.size, 'outputs should have same size' >>> assert img1.size == img2.size, 'new imgs should have same size' >>> print('--------') """ import vtool as vt # w1, h1 = get_size(img1) # w2, h2 = get_size(img2) # if not (w1 == w2 and h1 == h2): # raise AssertionError( # 'Images must have same size, %r, %r' % ((w1, h1), (w2, h2))) if img1.shape != img2.shape: c1 = vt.get_num_channels(img1) c2 = vt.get_num_channels(img2) if len(img1.shape) == 2 and len(img2.shape) == 2: raise AssertionError('UNREACHABLE: Both are 2-grayscale') elif len(img1.shape) == 3 and len(img2.shape) == 2: # Image 2 is grayscale if c1 == 3: img2 = np.tile(img2[..., None], 3) else: img2 = img2[..., None] elif len(img1.shape) == 2 and len(img2.shape) == 3: # Image 1 is grayscale if c2 == 3: img1 = np.tile(img1[..., None], 3) else: img1 = img1[..., None] elif len(img1.shape) == 3 and len(img2.shape) == 3: # Both images have 3 dims. # Check if either have color, then check for alpha if c1 == 1 and c2 == 1: raise AssertionError('UNREACHABLE: Both are 3-grayscale') elif c1 == 3 and c2 == 3: raise AssertionError('UNREACHABLE: Both are 3-color') elif c1 == 1 and c2 == 3: img1 = np.tile(img1, 3) elif c1 == 3 and c2 == 1: img2 = np.tile(img2, 3) elif c1 == 3 and c2 == 4: # raise NotImplementedError('alpha not handled yet') # assumes img1 is in 0:1 format img1 = np.dstack((img1, np.ones(img1.shape[0:2]))) elif c1 == 4 and c2 == 3: # assumes img1 is in 0:1 format img2 = np.dstack((img2, np.ones(img2.shape[0:2]))) # raise NotImplementedError('alpha not handled yet') elif c1 == 1 and c2 == 4: img1 = np.dstack((np.tile(img1, 3), np.ones(img1.shape[0:2]))) elif c1 == 4 and c2 == 1: img2 = np.dstack((np.tile(img2, 3), np.ones(img2.shape[0:2]))) else: raise AssertionError( 'Unknown shape case: %r, %r' % (img1.shape, img2.shape) ) else: raise AssertionError('Unknown shape case: %r, %r' % (img1.shape, img2.shape)) return img1, img2
def _lookup_colorspace_code(colorspace, src_colorspace='BGR'): src_colorspace = src_colorspace.upper() colorspace = colorspace.upper() prefix = 'COLOR_' + src_colorspace + '2' valid_dst_colorspaces = [ key.replace(prefix, '') for key in cv2.__dict__.keys() if key.startswith(prefix) ] if colorspace not in valid_dst_colorspaces: raise NotImplementedError('unknown colorspace = %r' % (colorspace,)) else: key = prefix + colorspace code = cv2.__dict__[key] return code
[docs]def convert_colorspace(img, colorspace, src_colorspace='BGR'): r""" Converts colorspace of img. Convinience function around cv2.cvtColor Args: img (ndarray[uint8_t, ndim=2]): image data colorspace (str): RGB, LAB, etc src_colorspace (unicode): (default = u'BGR') Returns: ndarray[uint8_t, ndim=2]: img - image data CommandLine: python -m vtool.image convert_colorspace --show Example: >>> # DISABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> img_fpath = ut.grab_test_imgpath('zebra.png') >>> img_fpath = ut.grab_file_url('http://itsnasb.com/wp-content/uploads/2013/03/lisa-frank-logo1.jpg') >>> img_fpath = ut.grab_test_imgpath('carl.jpg') >>> img = vt.imread(img_fpath) >>> img_float = vt.rectify_to_float01(img, np.float32) >>> colorspace = 'LAB' >>> src_colorspace = 'BGR' >>> imgLAB = convert_colorspace(img, colorspace, src_colorspace) >>> imgL = imgLAB[:, :, 0] >>> fillL = imgL.mean() >>> fillAB = 0 if ut.is_float(img) else 128 >>> imgAB_LAB = vt.embed_channels(imgLAB[:, :, 1:3], (1, 2), fill=fillL) >>> imgA_LAB = vt.embed_channels(imgLAB[:, :, 1], (1,), fill=(fillL, fillAB)) >>> imgB_LAB = vt.embed_channels(imgLAB[:, :, 2], (2,), fill=(fillL, fillAB)) >>> imgAB_BGR = convert_colorspace(imgAB_LAB, src_colorspace, colorspace) >>> imgA_BGR = convert_colorspace(imgA_LAB, src_colorspace, colorspace) >>> imgB_BGR = convert_colorspace(imgB_LAB, src_colorspace, colorspace) >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> #imgAB_HSV = convert_colorspace(convert_colorspace(imgAB_LAB, 'LAB', 'BGR'), 'BGR', 'HSV') >>> imgAB_HSV = convert_colorspace(img, 'HSV', 'BGR') >>> imgAB_HSV[:, :, 1:3] = .6 if ut.is_float(img) else 128 >>> imgCOLOR_BRG = convert_colorspace(imgAB_HSV, 'BGR', 'HSV') >>> pt.imshow(img, pnum=(3, 4, 1), title='input') >>> pt.imshow(imgL, pnum=(3, 4, 2), title='L (lightness)') >>> pt.imshow((imgLAB[:, :, 1]), pnum=(3, 4, 3), title='A (grayscale)') >>> pt.imshow((imgLAB[:, :, 2]), pnum=(3, 4, 4), title='B (grayscale)') >>> pt.imshow(imgCOLOR_BRG, pnum=(3, 4, 5), title='Hue') >>> pt.imshow(imgAB_BGR, pnum=(3, 4, 6), title='A+B (color overlay)') >>> pt.imshow(imgA_BGR, pnum=(3, 4, 7), title='A (Red-Green)') >>> pt.imshow(imgB_BGR, pnum=(3, 4, 8), title='B (Blue-Yellow)') >>> rgblind_LAB = vt.embed_channels(imgLAB[:, :, (0, 2)], (0, 2), fill=fillAB) >>> rgblind_BRG = convert_colorspace(rgblind_LAB, src_colorspace, colorspace) >>> byblind_LAB = vt.embed_channels(imgLAB[:, :, (0, 1)], (0, 1), fill=fillAB) >>> byblind_BGR = convert_colorspace(byblind_LAB, src_colorspace, colorspace) >>> pt.imshow(byblind_BGR, title='colorblind B-Y', pnum=(3, 4, 11)) >>> pt.imshow(rgblind_BRG, title='colorblind R-G', pnum=(3, 4, 12)) >>> ut.show_if_requested() """ src_colorspace = src_colorspace.upper() colorspace = colorspace.upper() if colorspace == src_colorspace: return img # FIXME copy? code = _lookup_colorspace_code(colorspace, src_colorspace) img2 = cv2.cvtColor(img, code) return img2
[docs]def convert_image_list_colorspace(image_list, colorspace, src_colorspace='BGR'): """ converts a list of images from <src_colorspace> to <colorspace> """ src_colorspace = src_colorspace.upper() colorspace = colorspace.upper() if colorspace == src_colorspace: return image_list code = _lookup_colorspace_code(colorspace, src_colorspace) if isinstance(image_list, np.ndarray): # Be more efficient if using numpy arrays if colorspace == 'GRAY' and src_colorspace != 'GRAY': image_list2 = np.empty(image_list.shape[0:3], dtype=image_list.dtype) else: image_list2 = np.empty(image_list.shape, dtype=image_list.dtype) for index in range(len(image_list2)): cv2.cvtColor(image_list[index], code, dst=image_list2[index]) else: # If python list use comprehension image_list2 = [cv2.cvtColor(img, code) for img in image_list] return image_list2
[docs]def padded_resize(img, target_size=(64, 64), interpolation=None): r""" makes the image resize to the target size and pads the rest of the area with a fill value Args: img (ndarray[uint8_t, ndim=2]): image data target_size (tuple): CommandLine: python -m vtool.image --test-padded_resize --show Example: >>> # ENABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> imgA = vt.imread(ut.grab_test_imgpath('carl.jpg')) >>> imgB = vt.imread(ut.grab_test_imgpath('ada.jpg')) >>> imgC = vt.imread(ut.grab_test_imgpath('carl.jpg'), grayscale=True) >>> #target_size = (64, 64) >>> target_size = (1024, 1024) >>> img3_list = [padded_resize(img, target_size) for img in [imgA, imgB, imgC]] >>> # verify results >>> assert ut.allsame([vt.get_size(img3) for img3 in img3_list]) >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> pnum_ = pt.make_pnum_nextgen(1, 3) >>> pt.imshow(img3_list[0], pnum=pnum_()) >>> pt.imshow(img3_list[1], pnum=pnum_()) >>> pt.imshow(img3_list[2], pnum=pnum_()) >>> ut.show_if_requested() """ interpolation = _rectify_interpolation(interpolation) img2 = resize_to_maxdims(img, target_size, interpolation=interpolation) dsize2 = get_size(img2) if dsize2 != target_size: img3 = embed_in_square_image(img2, target_size) else: img3 = img2 return img3
[docs]def embed_in_square_image( img, target_size, img_origin=(0.5, 0.5), target_origin=(0.5, 0.5) ): r""" Embeds an image in the center of an empty image Args: img (ndarray[uint8_t, ndim=2]): image data target_size (tuple): offset (tuple): position of Returns: ndarray: img_sqare CommandLine: python -m vtool.image embed_in_square_image --show Example: >>> # DISABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> img_fpath = ut.grab_test_imgpath('carl.jpg') >>> img = vt.imread(img_fpath) >>> target_size = tuple(np.array(vt.get_size(img)) * 3) >>> img_origin = (.5, .5) >>> target_origin = (.5, .5) >>> img_square = embed_in_square_image(img, target_size, img_origin, target_origin) >>> assert img_square.sum() == img.sum() >>> assert vt.get_size(img_square) == target_size >>> img_origin = (0, 0) >>> target_origin = (0, 0) >>> img_square2 = embed_in_square_image(img, target_size, img_origin, target_origin) >>> assert img_square.sum() == img.sum() >>> assert vt.get_size(img_square) == target_size >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> pt.imshow(img_square, pnum=(1, 2, 1)) >>> pt.imshow(img_square2, pnum=(1, 2, 2)) >>> ut.show_if_requested() """ # Allocate large image num_channels = get_num_channels(img) target_shape = ( target_size[::-1] if num_channels == 1 else tuple(target_size)[::-1] + (num_channels,) ) img_sqare = np.zeros(target_shape, dtype=img.dtype) # Determine slice of target shape that places img_origin at target_origin target_rc = np.array(target_shape[0:2]) img_rc = np.array(img.shape[0:2]) img_origin_abs = np.array(img_origin)[::-1] * img_rc target_origin_abs = np.array(target_origin)[::-1] * target_rc # img_left_rc = img_rc - img_origin_abs # img_right_rc = img_origin_abs # TODO: allow image to hang off edge # print('img_rc = %r' % (img_rc,)) # print('img_origin = %r' % (img_origin,)) # print('img_origin_abs = %r' % (img_origin_abs,)) # print('target_rc = %r' % (target_rc,)) # print('target_origin = %r' % (target_origin,)) # print('target_origin_abs = %r' % (target_origin_abs,)) # # Find start slice in the target image target_diff = np.floor(target_origin_abs - img_origin_abs) target_rc_start = np.maximum(target_diff, 0).astype(np.int) img_rc_start = (-(target_diff - target_rc_start)).astype(np.int) img_clip_rc_low = img_rc - img_rc_start end_hang = np.maximum((target_rc_start + img_clip_rc_low) - target_rc, 0) img_clip_rc = img_clip_rc_low - end_hang img_rc_end = (img_rc_start + img_clip_rc).astype(np.int) target_rc_end = (target_rc_start + img_clip_rc).astype(np.int) img_rc_slice = [slice(b, e) for (b, e) in zip(img_rc_start, img_rc_end)] target_rc_slice = [slice(b, e) for (b, e) in zip(target_rc_start, target_rc_end)] # embed image at position img_sqare[target_rc_slice[0], target_rc_slice[1]] = img[ img_rc_slice[0], img_rc_slice[1] ] # target_rc_overhang = target_rc_start - target_rc_hangstart ##-np.minimum(np.floor(img_origin_abs - target_origin_abs), 0) ##img_origin_abs - target_rc_overhang # # Find start slice in the given image # img_rc_start = img_rc - target_rc_overhang # cliped_img_rc = img_rc - img_rc_start # (target_rc_start + cliped_img_rc) - target_rc # image_rc_start = np.maximum(target_rc_overhang, 0) # image_rc_end = img_rc - image_rc_start # image_rc_start = np.maximum(-target_rc_start, 0) # image_rc_end = img_rc - image_rc_start if False: rc_diff = target_rc - img_rc # amount of extra space in target rc_start = np.floor(rc_diff / 2) rc_end = [None if e == 0 else e for e in (rc_start - rc_diff)] rc_slice = [slice(b, e) for (b, e) in zip(rc_start, rc_end)] # embed image at center img_sqare[rc_slice[0], rc_slice[1]] = img return img_sqare
def _trimread(gpath): """Try an imread""" try: return imread(gpath) except Exception: return None
[docs]def get_scale_factor(src_img, dst_img): """returns scale factor from one image to the next""" src_h, src_w = src_img.shape[0:2] dst_h, dst_w = dst_img.shape[0:2] sx = dst_w / src_w sy = dst_h / src_h return (sx, sy)
[docs]def resize_to_maxdims_ondisk(img_fpath, max_dsize, out_fpath=None): r""" Args: img_fpath (str): file path string max_dsize (?): out_fpath (str): file path string(default = None) CommandLine: python -m vtool.image resize_to_maxdims_ondisk --fpath ~/latex/crall-candidacy-2015/figures3/knormA.png --dsize=417,None python -m vtool.image resize_to_maxdims_ondisk --fpath ~/latex/crall-candidacy-2015/figures3/knormB.png --dsize=417,None python -m vtool.image resize_to_maxdims_ondisk --fpath ~/latex/crall-candidacy-2015/figures3/knormC.png --dsize=417,None python -m vtool.image resize_to_maxdims_ondisk --fpath ~/latex/crall-candidacy-2015/figures3/knormD.png --dsize=417,None python -m vtool.image resize_to_maxdims_ondisk --fpath ~/latex/crall-candidacy-2015/figures3/knormE.png --dsize=417,None python -m vtool.image resize_to_maxdims_ondisk --fpath ~/latex/crall-candidacy-2015/figures3/knormF.png --dsize=417,None python -m vtool.image resize_to_maxdims_ondisk --fpath ~/latex/crall-candidacy-2015/figures3/knormG.png --dsize=417,None python -m vtool.image resize_to_maxdims_ondisk --fpath ~/latex/crall-candidacy-2015/figures3/knormH.png --dsize=417,None python -m vtool.image resize_to_maxdims_ondisk --fpath ~/latex/crall-candidacy-2015/figures3/knormI.png --dsize=417,None python -m vtool.image resize_to_maxdims_ondisk --fpath ~/latex/crall-candidacy-2015/figures3/knormJ.png --dsize=417,None Example: >>> # DISABLE_DOCTEST >>> from vtool.image import * # NOQA >>> img_fpath = ut.get_argval('--fpath') >>> max_dsize = ut.get_argval('--dsize', type_=list) >>> out_fpath = None >>> resize_to_maxdims_ondisk(img_fpath, max_dsize, out_fpath) """ img = imread(img_fpath, flags=cv2.IMREAD_UNCHANGED) img2 = resize_to_maxdims(img, max_dsize) out_fpath_ = ( ut.augpath(img_fpath, '_max_dsize=%r' % (max_dsize)) if out_fpath is None else out_fpath ) imwrite(out_fpath_, img2)
[docs]def resize_to_maxdims(img, max_dsize=(64, 64), interpolation=None): r""" Args: img (ndarray[uint8_t, ndim=2]): image data max_dsize (tuple): interpolation (long): CommandLine: python -m vtool.image --test-resize_to_maxdims --show Example: >>> # ENABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> img_fpath = ut.grab_test_imgpath('carl.jpg') >>> img = vt.imread(img_fpath) >>> max_dsize = (1024, 1024) >>> img2 = resize_to_maxdims(img, max_dsize) >>> print('img.shape = %r' % (img.shape,)) >>> print('img2.shape = %r' % (img2.shape,)) >>> # verify results >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> pt.imshow(img2) >>> ut.show_if_requested() """ img_size = get_size(img) dsize, ratio = resized_dims_and_ratio(img_size, max_dsize) interpolation = _rectify_interpolation(interpolation) return cv2.resize(img, dsize, interpolation=interpolation)
[docs]def resize_thumb(img, max_dsize=(64, 64), interpolation=None): """ Resize an image such that its max width or height is: CommandLine: python -m vtool.image --test-resize_thumb --show Example: >>> # ENABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> # build test data >>> img_fpath = ut.grab_test_imgpath('carl.jpg') >>> img = vt.imread(img_fpath) >>> max_dsize = (64, 64) >>> # execute function >>> img2 = resize_thumb(img, max_dsize) >>> print('img.shape = %r' % (img.shape,)) >>> print('img2.shape = %r' % (img2.shape,)) >>> # verify results >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> pt.imshow(img2) >>> ut.show_if_requested() """ height, width = img.shape[0:2] img_size = (width, height) dsize, ratio = resized_dims_and_ratio(img_size, max_dsize) interpolation = _rectify_interpolation(interpolation) if ratio > 1: return cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # return cvt_BGR2RGB(img) else: return cv2.resize(img, dsize, interpolation=interpolation)
[docs]def find_pixel_value_index(img, pixel): r""" Args: img (ndarray[uint8_t, ndim=2]): image data pixel (ndarray or scalar): CommandLine: python -m vtool.util_math --test-find_pixel_value_index References: http://stackoverflow.com/questions/21407815/get-column-row-index-from-numpy-array-that-meets-a-boolean-condition Example: >>> # ENABLE_DOCTEST >>> from vtool.image import * # NOQA >>> # build test data >>> img = np.random.rand(10, 10, 3) + 1.0 >>> pixel = np.array([0, 0, 0]) >>> img[5, 5, :] = pixel >>> img[2, 3, :] = pixel >>> img[1, 1, :] = pixel >>> img[0, 0, :] = pixel >>> img[2, 0, :] = pixel >>> # execute function >>> result = find_pixel_value_index(img, pixel) >>> # verify results >>> print(result) [[0 0] [1 1] [2 0] [2 3] [5 5]] """ mask2d = get_pixel_dist(img, pixel) == 0 pixel_locs = np.column_stack(np.where(mask2d)) return pixel_locs
[docs]def draw_text( img, text, org, textcolor_rgb=[0, 0, 0], fontScale=1, thickness=2, fontFace=cv2.FONT_HERSHEY_SIMPLEX, lineType=LINE_AA, bottomLeftOrigin=False, ): """ CommandLine: python -m vtool.image --test-draw_text:0 --show python -m vtool.image --test-draw_text:1 --show Example: >>> # DISABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> font_names = sorted([key for key in cv2.__dict__.keys() if key.startswith('FONT_H')]) >>> text = 'opencv' >>> img = np.zeros((400, 1024), dtype=np.uint8) >>> thickness = 2 >>> fontScale = 1.0 >>> lineType = 4 >>> lineType = 8 >>> lineType = cv2.CV_AA >>> for count, font_name in enumerate(font_names, start=1): >>> print(font_name) >>> fontFace = cv2.__dict__[font_name] >>> org = (10, count * 45) >>> text = 'opencv - ' + font_name >>> vt.draw_text(img, text, org, ... fontFace=fontFace, textcolor_rgb=[255, 255, 255], ... fontScale=fontScale, thickness=thickness) >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> pt.imshow(img) >>> ut.show_if_requested() Example: >>> # DISABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> font_names = sorted([key for key in cv2.__dict__.keys() if key.startswith('FONT_H')]) >>> text = 'opencv' >>> img = np.zeros((400, 1024, 3), dtype=np.uint8) >>> img[:200, :512, 0] = 255 >>> img[200:, 512:, 2] = 255 >>> thickness = 2 >>> fontScale = 1.0 >>> lineType = 4 >>> lineType = 8 >>> lineType = cv2.CV_AA >>> for count, font_name in enumerate(font_names, start=1): >>> print(font_name) >>> fontFace = cv2.__dict__[font_name] >>> org = (10, count * 45) >>> text = 'opencv - ' + font_name >>> vt.draw_text(img, text, org, ... fontFace=fontFace, textcolor_rgb=[255, 255, 255], ... fontScale=fontScale, thickness=thickness) >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> pt.imshow(img) >>> ut.show_if_requested() where each of the font IDs can be combined with FONT_ITALIC to get the slanted letters. """ if len(textcolor_rgb) == 4: # remove alpha textcolor_rgb = textcolor_rgb[:3] textcolor_bgr = textcolor_rgb[::-1] text_pt, text_sz = cv2.getTextSize(text, fontFace, fontScale, thickness) text_w, text_h = text_pt out = cv2.putText( img, text, org, fontFace, fontScale, textcolor_bgr, thickness, lineType, bottomLeftOrigin, ) return out
# def testing(img): # r""" # Args: # img (ndarray[uint8_t, ndim=2]): image data # # CommandLine: # python -m vtool.image --test-testing --show # # Example: # >>> # DISABLE_DOCTEST # >>> from vtool.image import * # NOQA # >>> import vtool as vt # >>> img_fpath = ut.grab_test_imgpath('carl.jpg') # >>> img = vt.imread(img_fpath) # >>> img2 = testing(img) # >>> # xdoctest: +REQUIRES(--show) # >>> import wbia.plottool as pt # >>> pt.imshow(img, pnum=(1, 2, 1)) # >>> pt.imshow(img2, pnum=(1, 2, 2)) # >>> ut.show_if_requested() # """ # img = img.astype(np.float32) # mask = np.ones(img.shape, dtype=np.uint8) # img2 = img.copy() # alpha = 2.2 # beta = 50.0 # cv2.illuminationChange(img, mask, img2, alpha, beta) # #ut.embed() # return img2
[docs]def perlin_noise(size, scale=32.0, rng=np.random): """ References: http://www.siafoo.net/snippet/229 CommandLine: python -m vtool.image perlin_noise --show Example: >>> # ENABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> #size = (64, 64) >>> size = (256, 256) >>> #scale = 32.0 >>> scale = 64.0 >>> img = perlin_noise(size, scale) >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> pt.imshow(img, pnum=(1, 1, 1)) >>> ut.show_if_requested() """ # from PIL import Image class PerlinNoiseGenerator(object): def __init__(self, size=None, n=None): n = n if n is not None else 256 self.size = size if size is not None else (256, 256) self.order = len(self.size) # Generate WAY more numbers than we need # because we are throwing out all the numbers not inside a unit # sphere. Something of a hack but statistically speaking # it should work fine... or crash. G = (rng.uniform(size=2 * self.order * n) * 2 - 1).reshape(-1, self.order) # GAH! How do I generalize this?! # length = hypot(G[:,i] for i in range(self.order)) if self.order == 1: length = G[:, 0] elif self.order == 2: length = np.hypot(G[:, 0], G[:, 1]) elif self.order == 3: length = np.hypot(G[:, 0], G[:, 1], G[:, 2]) self.G = (G[length < 1] / (length[length < 1])[:, np.newaxis])[ :n, ] self.P = np.arange(n, dtype=np.int32) rng.shuffle(self.P) self.idx_ar = ( np.indices(2 * np.ones(self.order, dtype=np.int8), dtype=np.int8) .reshape(self.order, -1) .T ) self.drop = np.poly1d((-6, 15, -10, 0, 0, 1.0)) def noise(self, coords): ijk = (np.floor(coords) + self.idx_ar).astype(np.int8) uvw = coords - ijk indexes = self.P[ijk[:, :, self.order - 1]] for i in range(self.order - 1): indexes = self.P[(ijk[:, :, i] + indexes) % len(self.P)] gradiens = self.G[indexes % len(self.G)] # gradiens = self.G[(ijk[:,:, 0] + indexes) % len(self.G)] res = ( self.drop(np.abs(uvw)).prod(axis=2) * np.prod([gradiens, uvw], axis=0).sum(axis=2) ).sum(axis=1) res[res > 1.0] = 1.0 res[res < -1.0] = -1.0 return ((res + 1) * 128).astype(np.int8) def getData(self, scale=32.0): return self.noise(np.indices(self.size).reshape(self.order, 1, -1).T / scale) def getImage(self, scale=32.0): return Image.frombuffer( 'L', self.size[:2], self.getData(scale)[: self.size[0] * self.size[1]], 'raw', 'L', 0, 1, ) def saveImage(self, fileName, scale=32.0): im = self.getImage(scale) im.save(fileName) self = PerlinNoiseGenerator(size=size) pil_img = self.getImage(scale=scale) img = np.array(pil_img.getdata()).reshape(size) return img
# STACK IMAGES STUFF
[docs]def testdata_imglist(): # build test data import vtool as vt img1 = vt.imread(ut.grab_test_imgpath('carl.jpg')) img2 = vt.imread(ut.grab_test_imgpath('astro.png')) img3 = vt.imread(ut.grab_test_imgpath('ada.jpg')) img4 = vt.imread(ut.grab_test_imgpath('jeff.png')) img5 = vt.imread(ut.grab_test_imgpath('star.png')) img_list = [img1, img2, img3, img4, img5] return img_list
[docs]def stack_image_list_special( img1, img_list, num=1, vert=True, use_larger=True, initial_sf=None, interpolation=None ): r""" # TODO: add initial scale down factor? CommandLine: python -m vtool.image --test-stack_image_list_special --show Example: >>> # ENABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> img_list_ = testdata_imglist() >>> img1 = img_list_[0] >>> img_list = img_list_[1:] >>> vert = True >>> return_offset = True >>> use_larger = False >>> num_bot = 1 >>> initial_sf = None >>> initial_sf = .5 >>> imgB, offset_list, sf_list = stack_image_list_special(img1, img_list, num_bot, vert, use_larger, initial_sf) >>> # xdoctest: +REQUIRES(--show) >>> wh_list = np.array([vt.get_size(img1)] + list(map(vt.get_size, img_list))) >>> wh_list_ = wh_list * sf_list >>> import wbia.plottool as pt >>> pt.imshow(imgB) >>> print('imgB.shape = %r' % (imgB.shape,)) >>> for offset, wh, color in zip(offset_list, wh_list_, pt.distinct_colors(len(offset_list))): ... pt.draw_bbox((offset[0], offset[1], wh[0], wh[1]), bbox_color=color) >>> ut.show_if_requested() """ import vtool as vt interpolation = _rectify_interpolation(interpolation) # img2 = img_list[0] img_list2 = img_list[:num] img_list3 = img_list[num:] # img_list_ = img_list[1:] # interpolation = cv2.INTER_NEAREST stack_kw = dict( modifysize=True, return_sf=True, use_larger=use_larger, interpolation=interpolation, ) if vert is None: vert_, _ = ut.get_argval('--vert', return_was_specified=True) if _: vert = not vert_ else: if len(img_list) > 0: vert = not infer_vert(img1, img_list[0], vert)[0] else: # HACK FIXME move flag setting to viz_matches or experiment drawing vert = True offset_list1 = [(0, 0)] if initial_sf is None: initial_sf = 1.0 sf_list1 = [(initial_sf, initial_sf)] img1_ = img1 else: dsize, initial_sf_ = vt.get_round_scaled_dsize(vt.get_size(img1), initial_sf) sf_list1 = [initial_sf_] img1_ = cv2.resize(img1, dsize, interpolation=interpolation) # stack the bottom images img_stack2, offset_list2, sf_list2 = stack_image_list( img_list2, vert=not vert, return_offset=True, **stack_kw ) # stack the top images img_stack3, offset_list3, sf_list3 = stack_image_list( img_list3, vert=vert, return_offset=True, **stack_kw ) # stack img1_ and the first stack imgL, offset_listL, sf_listL = stack_multi_images( img1_, img_stack2, offset_list1, sf_list1, offset_list2, sf_list2, vert=vert, interpolation=interpolation, ) # stack the output and the second stack img, offset_list, sf_list = stack_multi_images( imgL, img_stack3, offset_listL, sf_listL, offset_list3, sf_list3, vert=not vert ) return img, offset_list, sf_list
# Combine the stacks
[docs]def stack_multi_images( img1, img2, offset_list1, sf_list1, offset_list2, sf_list2, vert=True, use_larger=False, modifysize=True, interpolation=None, ): """combines images that are already stacked""" interpolation = _rectify_interpolation(interpolation, default=cv2.INTER_NEAREST) if img1 is None: return img2, offset_list2, sf_list2 if img2 is None: return img1, offset_list1, sf_list1 # combine with the main image imgB, offset_tup, sf_tup = stack_images( img1, img2, vert=vert, use_larger=use_larger, modifysize=modifysize, return_sf=True, interpolation=interpolation, ) # combine the offsets def mult_tuplelist(tuple_list, scale_xy): return [(tup[0] * scale_xy[0], tup[1] * scale_xy[1]) for tup in tuple_list] def add_tuplelist(tuple_list, offset_xy): return [(tup[0] + offset_xy[0], tup[1] + offset_xy[1]) for tup in tuple_list] offset_list1_ = add_tuplelist(mult_tuplelist(offset_list1, sf_tup[0]), offset_tup[0]) offset_list2_ = add_tuplelist(mult_tuplelist(offset_list2, sf_tup[1]), offset_tup[1]) sf_list1_ = mult_tuplelist(sf_list1, sf_tup[0]) sf_list2_ = mult_tuplelist(sf_list2, sf_tup[1]) offset_listB = offset_list1_ + offset_list2_ sf_listB = sf_list1_ + sf_list2_ # offset_listB, sf_listB = combine_offset_lists([offset_list1, # offset_list2], [sf_list1, sf_list2], offset_tup, sf_tup) return imgB, offset_listB, sf_listB
[docs]def stack_multi_images2( multiimg_list, offsets_list, sfs_list, vert=True, modifysize=True ): r""" Args: multiimg_list (list): offset_lists (?): sfs_list (?): vert (bool): Returns: tuple: (stacked_img, stacked_img, stacked_sfs) CommandLine: python -m vtool.image --test-stack_multi_images2 --show Example: >>> # DISABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> img_list = testdata_imglist() >>> img_stack1, offset_list1, sf_list1 = stack_image_list(img_list[::-1], vert=True, return_info=True, modifysize=True) >>> img_stack2, offset_list2, sf_list2 = stack_image_list(img_list, vert=True, return_info=True, modifysize=True) >>> img_stack3, offset_list3, sf_list3 = stack_image_list(img_list, vert=True, return_info=True, modifysize=False) >>> multiimg_list = [img_stack1, img_stack2, img_stack3] >>> offsets_list = [offset_list1, offset_list2, offset_list3] >>> sfs_list = [sf_list1, sf_list2, sf_list3] >>> vert = False >>> tup = stack_multi_images2(multiimg_list, offsets_list, sfs_list, vert) >>> (stacked_img, stacked_offsets, stacked_sfs) = tup >>> result = ut.remove_doublspaces(ub.repr2(np.array(stacked_offsets).T, precision=2, with_dtype=True, linewidth=10000)).replace(' ,', ',') >>> print(result) >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> pt.imshow(stacked_img) >>> wh_list = np.array([vt.get_size(img) for img in img_list[::-1] + img_list + img_list]) >>> wh_list_ = wh_list * stacked_sfs >>> for offset, wh, color in zip(stacked_offsets, wh_list_, pt.distinct_colors(len(stacked_offsets))): ... pt.draw_bbox((offset[0], offset[1], wh[0], wh[1]), bbox_color=color) >>> ut.show_if_requested() np.array([[ 0., 0., 0., 0., 0., 512., 512., 512., 512., 512., 1024., 1024., 1024., 1024., 1024. ], [ 0., 512.12, 1024.25, 1827., 2339., 0., 427., 939., 1742., 2254., 0., 373.18, 1137.45, 2073.38, 2670.47]], dtype=np.float64) """ stacked_img, offset_tups, sf_tups = stack_image_list( multiimg_list, return_sf=True, return_offset=True, vert=vert, modifysize=modifysize, ) stacked_offsets, stacked_sfs = combine_offset_lists( offsets_list, sfs_list, offset_tups, sf_tups ) return stacked_img, stacked_offsets, stacked_sfs
[docs]def combine_offset_lists(offsets_list, sfs_list, offset_tups, sf_tups): """Helper for stacking""" # combine the offsets import operator from six.moves import reduce assert len(offsets_list) == len(offset_tups) assert len(sfs_list) == len(sf_tups) assert len(sfs_list) == len(offsets_list) def mult_tuplelist(tuple_list, scale_xy): return [(tup[0] * scale_xy[0], tup[1] * scale_xy[1]) for tup in tuple_list] def add_tuplelist(tuple_list, offset_xy): return [(tup[0] + offset_xy[0], tup[1] + offset_xy[1]) for tup in tuple_list] offset_lists_ = [ add_tuplelist(mult_tuplelist(offsets, sf_tups[ix]), offset_tups[ix]) for ix, offsets in enumerate(offsets_list) ] sf_lists_ = [mult_tuplelist(sfs, sf_tups[ix]) for ix, sfs in enumerate(sfs_list)] offset_listB = reduce(operator.add, offset_lists_) sf_listB = reduce(operator.add, sf_lists_) return offset_listB, sf_listB
[docs]def stack_square_images(img_list, return_info=False, **kwargs): r""" Args: img_list (list): Returns: ndarray: CommandLine: python -m vtool.image --test-stack_square_images Example: >>> # DISABLE_DOCTEST >>> from vtool.image import * # NOQA >>> img_list = '?' >>> result = stack_square_images(img_list) >>> print(result) """ if len(img_list) == 0: raise IndexError('no images to stack') if len(img_list) == 1: return img_list[0] num_vert = int(np.ceil(np.sqrt(len(img_list)))) num_horiz = int(np.ceil(len(img_list) / float(num_vert))) stacked_info_list = [ stack_image_list(imgs, vert=True, return_offset=True, return_sf=True, **kwargs) for imgs in list(ut.ichunks(img_list, num_horiz)) ] vert_patches = ut.get_list_column(stacked_info_list, 0) bigpatch, bigoffsets, bigsfs = stack_image_list( vert_patches, vert=False, return_offset=True, return_sf=True, **kwargs ) if return_info: sfs_list = ut.get_list_column(stacked_info_list, 2) offsets_list = ut.get_list_column(stacked_info_list, 1) offset_listB, sf_listB = combine_offset_lists( offsets_list, sfs_list, bigoffsets, bigsfs ) return bigpatch, offset_listB, sf_listB else: return bigpatch
[docs]def stack_image_list( img_list, return_offset=False, return_sf=False, return_info=False, **kwargs ): r""" CommandLine: python -m vtool.image --test-stack_image_list --show Example: >>> # ENABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> # build test data >>> img_list = testdata_imglist() >>> vert = False >>> return_offset = True >>> modifysize = True >>> return_sf=True >>> kwargs = dict(modifysize=modifysize, vert=vert, use_larger=False) >>> # execute function >>> imgB, offset_list, sf_list = stack_image_list(img_list, return_offset=return_offset, return_sf=return_sf, **kwargs) >>> # verify results >>> result = ub.repr2(np.array(offset_list).T, precision=2, with_dtype=True) >>> print(result) >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> pt.imshow(imgB) >>> wh_list = np.array([vt.get_size(img) for img in img_list]) >>> wh_list_ = wh_list * sf_list >>> for offset, wh, color in zip(offset_list, wh_list_, pt.distinct_colors(len(offset_list))): ... pt.draw_bbox((offset[0], offset[1], wh[0], wh[1]), bbox_color=color) >>> pt.show_if_requested() >>> #wh1 = img1.shape[0:2][::-1] >>> #wh2 = img2.shape[0:2][::-1] >>> #pt.draw_bbox((0, 0) + wh1, bbox_color=(1, 0, 0)) >>> #pt.draw_bbox((woff, hoff) + wh2, bbox_color=(0, 1, 0)) np.array([[ 0. , 76.96, 141.08, 181.87, 246. ], [ 0. , 0. , 0. , 0. , 0. ]], dtype=np.float64) """ if return_info: return_sf = True return_offset = True if len(img_list) == 0: imgB = None offset_list = [] sf_list = [] else: imgB = img_list[0] offset_list = [(0, 0)] sf_list = np.full((len(img_list), 2), np.nan) # sf_list = [(1., 1.)] sf_list[0, :] = (1, 1) # sf_list = np for count, img2 in enumerate(img_list[1:], start=1): out_ = stack_images(imgB, img2, return_sf=return_sf, **kwargs) if return_sf: imgB, offset_tup, sf_tup = out_ offset2 = offset_tup[1] # need to modify scales of previous images sf1, sf2 = sf_tup # sf_list = [np.multiply(sf, sf1) for sf in sf_list] offset_list = [ (sf1[0] * offset[0], sf1[1] * offset[1]) for offset in offset_list ] sf_list[:count, :] *= sf1 sf_list[count, :] = sf2 else: imgB, woff, hoff = out_ offset2 = (woff, hoff) offset_list.append(offset2) if return_offset: if return_sf: return imgB, offset_list, sf_list else: return imgB, offset_list else: return imgB
[docs]def embed_channels(img, input_channels=(0,), nchannels=3, fill=0): r""" Args: img (ndarray[uint8_t, ndim=2]): image data input_channels (tuple): (default = (0,)) nchannels (int): (default = 3) CommandLine: python -m vtool.image embed_channels --show Example: >>> # DISABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> # Embed a (N,M,2) image into an (N,M,3) image >>> img_fpath = ut.grab_test_imgpath('carl.jpg') >>> img = vt.imread(img_fpath).T[1:3].T >>> input_channels = (1, 2) >>> nchannels = 3 >>> newimg = embed_channels(img, input_channels, nchannels) >>> assert newimg.shape[-1] == 3 >>> assert np.all(newimg[:, :, input_channels] == img) """ import vtool as vt new_shape = img.shape[0:2] + (nchannels,) if not isinstance(fill, tuple): fill = (fill,) newimg = np.empty(new_shape, dtype=img.dtype) fill_dims = np.setdiff1d(np.arange(nchannels), input_channels) for dim, val in zip(fill_dims, fill): newimg[:, :, dim] = val # newimg[:, :, tuple(fill_dims.tolist())] = vt.atleast_nd([fill], 3, True) newimg[:, :, input_channels] = vt.atleast_nd(img, 3) return newimg
[docs]def ensure_4channel(img): assert len(img.shape) == 3 if len(img.shape) == 3 and img.shape[2] == 4: img_alpha = img return img_alpha h, w = img.shape[0:2] if img.dtype.kind in {'i', 'u'}: alpha = np.full((h, w, 1), fill_value=255, dtype=img.dtype) elif img.dtype.kind == 'f': alpha = np.full((h, w, 1), fill_value=1, dtype=img.dtype) else: raise NotImplementedError('kind={}'.format(img.dtype.kind)) if img.shape[2] == 1: img_alpha = np.dstack([img, img, img, alpha]) elif img.shape[2] == 3: img_alpha = np.dstack([img, alpha]) else: raise NotImplementedError('shape={}'.format(img.shape)) # print('img_alpha.shape = {!r}'.format(img_alpha.shape)) return img_alpha
[docs]def ensure_3channel(patch): r""" DEPRICATE IN FAVOR OF atleast_3channels? Ensures that there are 3 channels in the image Args: patch (ndarray[N, M, ...]): the image Returns: ndarray: [N, M, 3] CommandLine: python -m vtool.image --exec-ensure_3channel --show Example: >>> # ENABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> patch1 = vt.imread(ut.grab_test_imgpath('astro.png'))[0:512, 0:500, :] >>> patch2 = vt.imread(ut.grab_test_imgpath('ada.jpg'))[:, :, 0:1] >>> patch3 = vt.imread(ut.grab_test_imgpath('jeff.png'))[0:390, 0:400, 0] >>> res1 = ensure_3channel(patch1) >>> res2 = ensure_3channel(patch2) >>> res3 = ensure_3channel(patch3) >>> assert res1.shape[0:2] == patch1.shape[0:2], 'failed test1' >>> assert res2.shape[0:2] == patch2.shape[0:2], 'failed test2' >>> assert res3.shape[0:2] == patch3.shape[0:2], 'failed test3' >>> assert res1.shape[-1] == 3 >>> assert res2.shape[-1] == 3 >>> assert res3.shape[-1] == 3 """ # TODO: should this use atleast_nd as a subroutine? # res = vt.atleast_nd(patch, 3) # if res.shape[-1] == 1: # res = np.tile(res, 3) # import utool # utool.embed() if len(patch.shape) == 2: res = np.tile(patch[:, :, None], 3) elif len(patch.shape) == 3 and patch.shape[-1] == 1: res = np.tile(patch, 3) else: res = patch.copy() return res
[docs]def infer_vert(img1, img2, vert): """which is the better stack dimension""" (h1, w1) = img1.shape[0:2] # get chip dimensions (h2, w2) = img2.shape[0:2] woff, hoff = 0, 0 vert_wh = max(w1, w2), h1 + h2 horiz_wh = w1 + w2, max(h1, h2) if vert is None: # Display the orientation with the better (closer to 1) aspect ratio vert_ar = max(vert_wh) / min(vert_wh) horiz_ar = max(horiz_wh) / min(horiz_wh) vert = vert_ar < horiz_ar if vert: wB, hB = vert_wh hoff = h1 else: wB, hB = horiz_wh woff = w1 return vert, h1, h2, w1, w2, wB, hB, woff, hoff
[docs]def stack_images( img1, img2, vert=None, modifysize=False, return_sf=False, use_larger=True, interpolation=None, white_background=False, overlap=0, ): r""" Args: img1 (ndarray[uint8_t, ndim=2]): image data img2 (ndarray[uint8_t, ndim=2]): image data CommandLine: python -m vtool.image --test-stack_images --show Example: >>> # ENABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> # build test data >>> img1 = vt.imread(ut.grab_test_imgpath('carl.jpg')) >>> img2 = vt.imread(ut.grab_test_imgpath('astro.png')) >>> vert = True >>> modifysize = False >>> # execute function >>> return_sf = True >>> #(imgB, woff, hoff) = stack_images(img1, img2, vert, modifysize, return_sf=return_sf) >>> overlap = 100 >>> imgB, offset2, sf_tup = stack_images(img1, img2, vert, modifysize, >>> return_sf=return_sf, >>> overlap=overlap) >>> woff, hoff = offset2 >>> # verify results >>> result = str((imgB.shape, woff, hoff)) >>> print(result) >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> pt.imshow(imgB) >>> wh1 = np.multiply(vt.get_size(img1), sf_tup[0]) >>> wh2 = np.multiply(vt.get_size(img2), sf_tup[1]) >>> pt.draw_bbox((0, 0, wh1[0], wh1[1]), bbox_color=(1, 0, 0)) >>> pt.draw_bbox((woff[1], hoff[1], wh2[0], wh2[0]), bbox_color=(0, 1, 0)) >>> pt.show_if_requested() ((662, 512, 3), (0.0, 0.0), (0, 150)) """ import operator import vtool as vt interpolation = _rectify_interpolation(interpolation, default=cv2.INTER_NEAREST) # TODO: move this to the same place I'm doing the color gradient nChannels1 = vt.get_num_channels(img1) nChannels2 = vt.get_num_channels(img2) if nChannels1 == 1 and nChannels2 == 3: img1 = vt.atleast_3channels(img1, copy=False) if nChannels1 == 3 and nChannels2 == 1: img2 = vt.atleast_3channels(img2, copy=False) nChannels1 = vt.get_num_channels(img1) nChannels2 = vt.get_num_channels(img2) assert nChannels1 == nChannels2 # TODO: allow for some overlap / blending of the images vert, h1, h2, w1, w2, wB, hB, woff, hoff = infer_vert(img1, img2, vert) if overlap: if vert: hB -= overlap else: wB -= overlap # Rectify both images to they are the same dimension if modifysize: side_index = 1 if vert else 0 # Compre the lengths of the width and height (length1, length2) = (img1.shape[side_index], img2.shape[side_index]) comp_ = operator.lt if use_larger else operator.gt if comp_(length1, length2): tonew_sf2 = (1.0, 1.0) scale = length2 / length1 dsize, tonew_sf1 = vt.get_round_scaled_dsize(vt.get_size(img1), scale) img1 = cv2.resize(img1, dsize, interpolation=interpolation) elif comp_(length2, length1): tonew_sf1 = (1.0, 1.0) scale = length1 / length2 dsize, tonew_sf2 = vt.get_round_scaled_dsize(vt.get_size(img2), scale) img2 = cv2.resize(img2, dsize, interpolation=interpolation) else: tonew_sf1 = (1.0, 1.0) tonew_sf2 = (1.0, 1.0) vert, h1, h2, w1, w2, wB, hB, woff, hoff = infer_vert(img1, img2, vert) else: tonew_sf1 = (1.0, 1.0) tonew_sf2 = (1.0, 1.0) # Do image concatentation dtype = img1.dtype assert img1.dtype == img2.dtype, 'img1.dtype=%r, img2.dtype=%r' % ( img1.dtype, img2.dtype, ) # if False: # if nChannels1 == 3 or len(img1.shape) > 2: # # Allocate new image for both # imgB = np.zeros((hB, wB, nChannels1), dtype) # # Insert the images # imgB[0:h1, 0:w1, :] = img1 # imgB[hoff:(hoff + h2), woff:(woff + w2), :] = img2 # elif nChannels1 == 1: # # Allocate new image for both # imgB = np.zeros((hB, wB), dtype) # # Insert the images # imgB[0:h1, 0:w1] = img1 # imgB[hoff:(hoff + h2), woff:(woff + w2)] = img2 # else: if nChannels1 == 3 or len(img1.shape) > 2: newshape = (hB, wB, nChannels1) else: newshape = (hB, wB) # Allocate new image for both imgB = np.zeros(newshape, dtype=dtype) if white_background: if dtype == np.uint8: imgB += 255 else: imgB += 1.0 if overlap: if vert: hoff -= overlap else: woff -= overlap # Insert the images imgB[0:h1, 0:w1] = img1 imgB[hoff : (hoff + h2), woff : (woff + w2)] = img2 # Blend the overlapping part if vert: part1 = img1[-overlap:, :] part2 = imgB[hoff : (hoff + overlap), 0:w1] alpha = vt.gradient_fill(part1.shape[0:2], vert=vert) imgB[hoff : (hoff + overlap), 0:w1] = vt.blend_images_average( part1, part2, alpha=alpha ) else: part1 = img1[:, -overlap:] part2 = imgB[0:h1, woff : (woff + overlap)] alpha = vt.gradient_fill(part1.shape[0:2], vert=vert) imgB[0:h1, woff : (woff + overlap)] = vt.blend_images_average( part1, part2, alpha=alpha ) else: # Insert the images imgB[0:h1, 0:w1] = img1 imgB[hoff : (hoff + h2), woff : (woff + w2)] = img2 # return if return_sf: offset1 = (0.0, 0.0) offset2 = (woff, hoff) offset_tup = (offset1, offset2) sf_tup = (tonew_sf1, tonew_sf2) return imgB, offset_tup, sf_tup else: return imgB, woff, hoff
[docs]def stack_image_recurse( img_list1, img_list2=None, vert=True, modifysize=False, return_offsets=False, interpolation=None, ): r""" TODO: return offsets as well Args: img_list1 (list): img_list2 (list): vert (bool): Returns: ndarray: None CommandLine: python -m vtool.image --test-stack_image_recurse --show Example: >>> # DISABLE_DOCTEST >>> from vtool.image import * # NOQA >>> import vtool as vt >>> # build test data >>> img1 = vt.imread(ut.grab_test_imgpath('carl.jpg')) >>> img2 = vt.imread(ut.grab_test_imgpath('astro.png')) >>> img3 = vt.imread(ut.grab_test_imgpath('ada.jpg')) >>> img4 = vt.imread(ut.grab_test_imgpath('jeff.png')) >>> img5 = vt.imread(ut.grab_test_imgpath('star.png')) >>> img_list1 = [img1, img2, img3, img4, img5] >>> img_list2 = None >>> vert = True >>> # execute function >>> imgB = stack_image_recurse(img_list1, img_list2, vert) >>> # verify results >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> imshow(imgB) >>> #wh1 = img1.shape[0:2][::-1] >>> #wh2 = img2.shape[0:2][::-1] >>> #pt.draw_bbox((0, 0) + wh1, bbox_color=(1, 0, 0)) >>> #pt.draw_bbox((woff, hoff) + wh2, bbox_color=(0, 1, 0)) >>> pt.show_if_requested() """ interpolation = _rectify_interpolation(interpolation, default=cv2.INTER_NEAREST) if img_list2 is None: # Initialization and error checking if len(img_list1) == 0: return None if len(img_list1) == 1: return img_list1[0] return stack_image_recurse( img_list1[0::2], img_list1[1::2], vert=vert, modifysize=modifysize, interpolation=interpolation, ) if len(img_list1) == 1: # Left base case img1 = img_list1[0] else: # Left recurse img1 = stack_image_recurse( img_list1[0::2], img_list1[1::2], vert=not vert, modifysize=modifysize, interpolation=interpolation, ) if len(img_list2) == 1: # Right base case img2 = img_list2[0] else: # Right Recurse img2 = stack_image_recurse( img_list2[0::2], img_list2[1::2], vert=not vert, modifysize=modifysize, interpolation=interpolation, ) if return_offsets: raise NotImplementedError('finishme') # imgB, offset_list, sf_list = stack_multi_images(img1, img2, # offset_list1, sf_list1, offset_list2, sf_list2, vert=vert) else: imgB, offset_tup, sf_tup = stack_images( img1, img2, vert=vert, return_sf=True, modifysize=modifysize, interpolation=interpolation, ) (woff, hoff) = offset_tup[1] return imgB
# /STACK IMAGES STUFF
[docs]def filterflags_valid_images( gpaths, valid_formats=None, invalid_formats=None, verbose=True ): r""" Flags images with a format that disagrees with its extension Args: gpaths (list): list of image paths valid_formats (None): (default = None) invalid_formats (None): (default = None) verbose (bool): verbosity flag(default = True) Returns: list: isvalid_flags CommandLine: python -m vtool.image filterflags_valid_images --show Notes: An MPO (Multi Picture Object) file is a stereoscopic image and contains two JPG images side-by-side, and allows them to be viewed as a single 3D image. Example: >>> # ENABLE_DOCTEST >>> from vtool.image import * # NOQA >>> gpaths = [ut.grab_test_imgpath('carl.jpg'), >>> ut.grab_test_imgpath('astro.png')] >>> flags = filterflags_valid_images(gpaths) >>> assert all(flags) """ from PIL import Image from os.path import splitext # import operator # import itertools as it # These are exact aliases img_format_alias_dict = { 'JPG': 'JPEG', 'TIF': 'TIFF', } # These aliases are not exact but generally fine # acceptable_alias = { # 'MPO': 'JPEG' # } def get_image_format_from_extension(gpath): gname, ext = splitext(gpath) ext_format = ext[1:].upper() ext_format = img_format_alias_dict.get(ext_format, ext_format) return ext_format def get_image_format_from_pil(gpath): try: pil_image = Image.open(gpath) pil_format = pil_image.format except IOError: pil_format = None # if pil_format == 'MPO': # print(pil_image.n_frames) return pil_format # def read_frames(gpath): # from PIL import Image, ImageSequence # import vtool as vt # import cv2 # #pil_image.n_frames # pil_image = Image.open(gpath) # sequence = [] # for frame in ImageSequence.Iterator(pil_image): # print('frame = %r' % (frame,)) # #img = np.asarray(frame) # rgb_pil = frame.convert('RGB') # img = np.array(rgb_pil) # img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) # sequence.append(img) # stack = vt.stack_square_images(sequence) # import wbia.plottool as pt # pt.qt4ensure() # pt.imshow(stack) # ## btyedata = np.asarray(bytearray(contents), dtype=np.uint8) # #print('frame = %r' % (frame,)) # #frame.save("frame%d.png" % index) # #index = index + 1 # pass def check_agrees(ext_format, pil_format): # pil_format_ = acceptable_alias.get(pil_format, pil_format) return pil_format == ext_format pil_foramt_list = [ get_image_format_from_pil(gpath) for gpath in ut.ProgIter(gpaths, lbl='check image pil-format', enabled=verbose) ] ext_format_list = [ get_image_format_from_extension(gpath) for gpath in ut.ProgIter(gpaths, lbl='check image ext-format', enabled=verbose) ] # agree_flags = list(it.starmap(operator.eq, zip(ext_format_list, # pil_foramt_list))) agree_flags = [check_agrees(e, p) for e, p, in zip(ext_format_list, pil_foramt_list)] valid_flags = agree_flags if valid_formats is not None: # explicitly mark valids valid_flags = ut.and_lists( valid_flags, [format_ in valid_formats for format_ in ext_format_list], [format_ in valid_formats for format_ in pil_foramt_list], ) if invalid_formats is not None: invalid_fmt_flags = ut.or_lists( [format_ in invalid_formats for format_ in ext_format_list], [format_ in invalid_formats for format_ in pil_foramt_list], ) valid_flags = ut.and_lists(valid_flags, ut.not_list(invalid_fmt_flags)) if verbose > 0: # Inspect invalid items invalid_flags = ut.not_list(valid_flags) fmt_list = list(zip(ext_format_list, pil_foramt_list)) invalid_fmt_list = ut.compress(fmt_list, invalid_flags) invalid_fmt_hist = ut.dict_hist(invalid_fmt_list) print('The following {(ext,pil): count} formats are invalid') print(ut.repr3(invalid_fmt_hist)) print('Total Invalid Files %r' % (sum(invalid_fmt_hist.values()),)) # Inspect valid items valid_fmt_list = ut.compress(fmt_list, valid_flags) valid_fmt_hist = ut.dict_hist(valid_fmt_list) print('The following {(ext,pil): count} formats are valid') print(ut.repr3(valid_fmt_hist)) print('Total Valid Files %r' % (sum(valid_fmt_hist.values()),)) if invalid_formats is not None: invalid_fmt_flags if verbose > 1: num_examples = 3 print('Examples of invalid files:') invalid_gpaths = ut.compress(gpaths, invalid_flags) grouped_invalids = ut.group_items(invalid_gpaths, invalid_fmt_list) for key in invalid_fmt_hist.keys(): val = grouped_invalids[key] print(key) print(ut.indentjoin(val[0:num_examples])[1:]) print('\nExamples of valid files:') valid_gpaths = ut.compress(gpaths, valid_flags) grouped_valids = ut.group_items(valid_gpaths, valid_fmt_list) for key in valid_fmt_hist.keys(): val = grouped_valids[key] print(key) print(ut.indentjoin(val[0:num_examples])[1:]) return valid_flags
if __name__ == '__main__': """ CommandLine: xdoctest -m vtool.image """ import xdoctest xdoctest.doctest_module(__file__)