Source code for vtool.chip

# -*- coding: utf-8 -*-
# LICENCE
from __future__ import absolute_import, division, print_function
import numpy as np
import numpy.linalg as npl
from vtool import linalg as ltool
from vtool import image as gtool
import utool as ut

try:
    import cv2
except ImportError:
    print('ERROR: import cv2 is failing!')
    cv2 = ut.DynStruct()
    cv2.INTER_LANCZOS4 = None


[docs]def get_image_to_chip_transform(bbox, chipsz, theta): """ transforms image space into chipspace Args: bbox - bounding box of chip in image space chipsz - size of the chip theta - rotation of the bounding box Ignore: >>> # https://groups.google.com/forum/#!topic/sympy/k1HnZK_bNNA >>> from vtool.patch import * # NOQA >>> import sympy >>> import sympy.abc >>> theta = sympy.abc.theta >>> >>> x, y, w, h, target_area = sympy.symbols('x y w h, a') >>> gx, gy = sympy.symbols('gx, gy') >>> >>> round = sympy.floor # hack >>> >>> ht = sympy.sqrt(target_area * h / w) >>> wt = w * ht / h >>> cw_, ch_ = round(wt), round(ht) >>> >>> from vtool import ltool >>> T1 = ltool.translation_mat3x3(tx1, ty1, dtype=None) >>> S = ltool.scale_mat3x3(sx, sy, dtype=None) >>> R = ltool.rotation_mat3x3(-theta, sympy.sin, sympy.cos) >>> T2 = ltool.translation_mat3x3(tx2, ty2, dtype=None) >>> >>> def add_matmul_hold_prop(mat): >>> #import functools >>> mat = sympy.Matrix(mat) >>> def matmul_hold(other, hold=False): >>> new = sympy.MatMul(mat, other, hold=hold) >>> add_matmul_hold_prop(new) >>> return new >>> setattr(mat, 'matmul_hold', matmul_hold) >>> return mat >>> >>> T1 = add_matmul_hold_prop(T1) >>> T2 = add_matmul_hold_prop(T2) >>> R = add_matmul_hold_prop(R) >>> S = add_matmul_hold_prop(S) >>> >>> C = T2.multiply(R.multiply(S.multiply(T1))) >>> sympy.simplify(C) """ (x, y, w, h) = bbox (cw_, ch_) = chipsz tx1 = -(x + (w / 2.0)) ty1 = -(y + (h / 2.0)) sx = cw_ / w sy = ch_ / h tx2 = cw_ / 2.0 ty2 = ch_ / 2.0 # Translate from bbox center to (0, 0) T1 = ltool.translation_mat3x3(tx1, ty1) # Scale to chip height S = ltool.scale_mat3x3(sx, sy) # Rotate to chip orientation R = ltool.rotation_mat3x3(-theta) # Translate from (0, 0) to chip center T2 = ltool.translation_mat3x3(tx2, ty2) # Merge into single transformation (operate left-to-right aka data on left) C = T2.dot(R.dot(S.dot(T1))) return C
def _get_chip_to_image_transform(bbox, chipsz, theta): """ transforms chip space into imgspace bbox - bounding box of chip in image space chipsz - size of the chip theta - rotation of the bounding box """ C = get_image_to_chip_transform(bbox, chipsz, theta) invC = npl.inv(C) return invC
[docs]def extract_chip_from_gpath( gfpath, bbox, theta, new_size, interpolation=cv2.INTER_LANCZOS4 ): imgBGR = gtool.imread(gfpath) # Read parent image chipBGR = extract_chip_from_img(imgBGR, bbox, theta, new_size, interpolation) return chipBGR
[docs]def extract_chip_into_square(imgBGR, bbox, theta, target_size): bbox_size = bbox[2:4] unpadded_dsize, ratio = gtool.resized_dims_and_ratio(bbox_size, target_size) chipBGR = extract_chip_from_img(imgBGR, bbox, theta, unpadded_dsize) chipBGR_square = gtool.embed_in_square_image(chipBGR, target_size) return chipBGR_square
[docs]def extract_chip_from_gpath_into_square(args): gfpath, bbox, theta, target_size = args imgBGR = gtool.imread(gfpath) # Read parent image return extract_chip_into_square(imgBGR, bbox, theta, target_size)
[docs]def extract_chip_from_img( imgBGR, bbox, theta, new_size, interpolation=cv2.INTER_LANCZOS4 ): """Crops chip from image ; Rotates and scales; ibs.show_annot_image(aid)[0].pt_save_and_view() Args: gfpath (str): bbox (tuple): xywh theta (float): new_size (tuple): wy Returns: ndarray: chipBGR CommandLine: python -m vtool.chip --test-extract_chip_from_img python -m vtool.chip --test-extract_chip_from_img --show Example: >>> # ENABLE_DOCTEST >>> from vtool.chip import * # NOQA >>> # build test data >>> imgBGR = gtool.imread(ut.grab_test_imgpath('carl.jpg')) >>> bbox = (100, 3, 100, 100) >>> theta = 0.0 >>> new_size = (58, 34) >>> # execute function >>> chipBGR = extract_chip_from_img(imgBGR, bbox, theta, new_size) >>> # verify results >>> assert chipBGR.shape[0:2] == new_size[::-1], 'did not resize correctly' >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> pt.imshow(chipBGR) >>> pt.show_if_requested() """ # THE CULPRIT FOR MULTIPROCESSING FREEZES flags = interpolation # if True: M = get_image_to_chip_transform(bbox, new_size, theta) # Build transformation chipBGR = cv2.warpAffine( imgBGR, M[0:2], tuple(new_size), flags=flags, borderMode=cv2.BORDER_CONSTANT ) # else: # # if theta == 0, not sure if this is better. Certainly not more general # x, y, w, h = bbox # roiBGR = imgBGR[y:y + h, x:x + w, :] # chipBGR = cv2.resize(roiBGR, tuple(new_size), interpolation=interpolation) # chipBGR = gtool.warpAffine(imgBGR, M, new_size) # Rotate and scale return chipBGR
[docs]def gridsearch_chipextract(): r""" CommandLine: xdoctest -m ~/code/vtool/vtool/chip.py gridsearch_chipextract --show Example: >>> # DISABLE_DOCTEST >>> # GRIDSEARCH >>> from vtool.chip import * # NOQA >>> gridsearch_chipextract() >>> ut.show_if_requested() """ import cv2 test_func = extract_chip_from_img if False: gpath = ut.grab_test_imgpath('carl.jpg') bbox = (100, 3, 100, 100) theta = 0.0 new_size = (58, 34) else: gpath = '/media/raid/work/GZ_Master1/_ibsdb/images/1524525d-2131-8770-d27c-3a5f9922e9e9.jpg' bbox = (450, 373, 2062, 1124) theta = 0.0 old_size = bbox[2:4] # target_area = 700 ** 2 target_area = 1200 ** 2 new_size = ScaleStrat.area(target_area, old_size) print('old_size = %r' % (old_size,)) print('new_size = %r' % (new_size,)) # new_size = (677, 369) imgBGR = gtool.imread(gpath) args = (imgBGR, bbox, theta, new_size) param_info = ut.ParamInfoList( 'extract_params', [ ut.ParamInfo( 'interpolation', cv2.INTER_LANCZOS4, varyvals=[ cv2.INTER_LANCZOS4, cv2.INTER_CUBIC, cv2.INTER_LINEAR, cv2.INTER_NEAREST, # cv2.INTER_AREA ], ) ], ) show_func = None # Generalize import wbia.plottool as pt pt.imshow(imgBGR) # HACK cfgdict_list, cfglbl_list = param_info.get_gridsearch_input(defaultslice=slice(0, 10)) fnum = pt.ensure_fnum(None) if show_func is None: show_func = pt.imshow lbl = ut.get_funcname(test_func) cfgresult_list = [ test_func(*args, **cfgdict) for cfgdict in ut.ProgressIter(cfgdict_list, lbl=lbl) ] onclick_func = None ut.interact_gridsearch_result_images( show_func, cfgdict_list, cfglbl_list, cfgresult_list, fnum=fnum, figtitle=lbl, unpack=False, max_plots=25, onclick_func=onclick_func, ) pt.iup()
[docs]class ScaleStrat(object): """ Scaling strategies """
[docs] @staticmethod def maxwh(target, orig_wh, tol=0): r""" The maximum dimension becomes target Args: target (int): target size Example: >>> # ENABLE_DOCTEST >>> import utool as ut >>> ut.assert_eq(ScaleStrat.maxwh(800, (190, 220)), (691, 800)) >>> ut.assert_eq(ScaleStrat.maxwh(800, (220, 190)), (800, 691)) """ max_idx = np.argmax(orig_wh) orig_dim_size = orig_wh[max_idx] low, high = (target - tol, target + tol) if low <= orig_dim_size and orig_dim_size <= high: new_size = orig_wh else: scale_factor = target / orig_dim_size wt = int(round(orig_wh[0] * scale_factor)) ht = int(round(orig_wh[1] * scale_factor)) new_size = (wt, ht) return new_size
[docs] @staticmethod def width(target, orig_wh, tol=0): r""" The width becomes target Args: target (int): target size Example: >>> # ENABLE_DOCTEST >>> import utool as ut >>> ut.assert_eq(ScaleStrat.width(800, (190, 220)), (800, 926)) >>> ut.assert_eq(ScaleStrat.width(800, (220, 190)), (800, 691)) """ orig_dim_size = orig_wh[0] low, high = (target - tol, target + tol) if low <= orig_dim_size and orig_dim_size <= high: new_size = orig_wh else: scale_factor = target / orig_dim_size wt = int(round(orig_wh[0] * scale_factor)) ht = int(round(orig_wh[1] * scale_factor)) new_size = (wt, ht) return new_size
[docs] @staticmethod def area(target, orig_wh, tol=0): r""" The area becomes target Args: target (int): target size Example: >>> # ENABLE_DOCTEST >>> import utool as ut >>> ut.assert_eq(ScaleStrat.area(800 ** 2, (190, 220)), (743, 861)) >>> ut.assert_eq(ScaleStrat.area(800 ** 2, (220, 190)), (861, 743)) """ w, h = orig_wh area = w * h low, high = (target - tol, target + tol) if low <= area and area <= high: new_size = orig_wh else: ht = np.sqrt(target * h / w) wt = w * ht / h new_size = (int(round(wt)), int(round(ht))) return new_size
[docs]def get_scaled_size_with_dlen(target_dlen, w, h): r""" returns new_size which scales (w, h) as close to target_dlen as possible and maintains aspect ratio """ # ht = np.sqrt(target_area * h / w) # wt = w * ht / h # new_size = (int(round(wt)), int(round(ht))) raise NotImplementedError()
# return new_size
[docs]def compute_chip( gfpath, bbox, theta, new_size, filter_list=[], interpolation=cv2.INTER_LANCZOS4 ): r"""Extracts a chip and applies filters DEPRICATE Args: gfpath (str): image file path string bbox (tuple): bounding box in the format (x, y, w, h) theta (float): angle in radians new_size (tuple): must maintain the same aspect ratio or else you will get weirdness filter_list (list): Returns: ndarray: chipBGR - cropped image CommandLine: python -m vtool.chip --test-compute_chip --show Example: >>> # DISABLE_DOCTEST >>> from vtool.chip import * # NOQA >>> from vtool.util_math import TAU >>> # build test data >>> gfpath = ut.grab_test_imgpath('carl.jpg') >>> bbox = (100, 3, 100, 100) >>> theta = TAU / 8 >>> new_size = (32, 32) >>> filter_list = [] >>> # execute function >>> chipBGR = compute_chip(gfpath, bbox, theta, new_size, filter_list) >>> # verify results >>> assert chipBGR.shape[0:2] == new_size[::-1], 'did not resize correctly' >>> # xdoctest: +REQUIRES(--show) >>> import wbia.plottool as pt >>> import vtool as vt >>> pt.imshow(vt.draw_verts(vt.imread(gfpath), vt.scaled_verts_from_bbox(bbox, theta, 1, 1)), pnum=(1, 2, 1)) >>> pt.imshow(chipBGR, pnum=(1, 2, 2)) >>> pt.show_if_requested() """ chipBGR = extract_chip_from_gpath(gfpath, bbox, theta, new_size, interpolation) chipBGR = apply_filter_funcs(chipBGR, filter_list) return chipBGR
[docs]def apply_filter_funcs(chipBGR, filter_funcs): """applies a list of preprocessing filters to a chip DEPRICATE """ chipBGR_ = chipBGR for func in filter_funcs: chipBGR_ = func(chipBGR) return chipBGR_
[docs]def get_extramargin_measures(bbox_gs, new_size, halfoffset_ms=(64, 64)): r""" Computes a detection chip with a bit of spatial context so the detection algorithm doesn't clip boundaries Returns: mbbox_gs, margin_size - margin bounding box in image size, size of entire margined chip, CommandLine: python -m vtool.chip --test-get_extramargin_measures --show Example: >>> # ENABLE_DOCTEST >>> from vtool.chip import * # NOQA >>> gfpath = ut.grab_test_imgpath('carl.jpg') >>> bbox_gs = [40, 40, 150, 150] >>> theta = .15 * (np.pi * 2) >>> new_size = (150, 150) >>> halfoffset_ms = (32, 32) >>> mbbox_gs, margin_size = get_extramargin_measures(bbox_gs, new_size, halfoffset_ms) >>> # xdoctest: +REQUIRES(--show) >>> testshow_extramargin_info(gfpath, bbox_gs, theta, new_size, halfoffset_ms, mbbox_gs, margin_size) """ # _ex denotes an expanded version # There are three spaces we are working in here # chip _cs (the space of the original chip) # margin _ms (the margin chip has the scale of chip space with padding) # imagespace _gs (the space using in bbox_gs specification) x_gs, y_gs, w_gs, h_gs = bbox_gs if w_gs == 0 or h_gs == 0: raise ValueError('Bounding box has no area') w_cs, h_cs = new_size # Extra margin in chip space xo_ms, yo_ms = halfoffset_ms # Compute size of margin chip mw, mh = (w_cs + (2 * xo_ms), h_cs + (2 * yo_ms)) margin_size = (mw, mh) # Get the conversion from chip to image space sx, sy = (w_gs / w_cs, h_gs / h_cs) # Convert the chip offsets to image space halfoffset_gs = ((sx * xo_ms), (sy * yo_ms)) xo_gs, yo_gs = halfoffset_gs # Find the size of the expanded margin bbox in image space mbbox_gs = (x_gs - xo_gs, y_gs - yo_gs, w_gs + (2 * xo_gs), h_gs + (2 * yo_gs)) return mbbox_gs, margin_size
[docs]def testshow_extramargin_info( gfpath, bbox_gs, theta, new_size, halfoffset_ms, mbbox_gs, margin_size ): import wbia.plottool as pt import vtool as vt imgBGR = vt.imread(gfpath) chipBGR = compute_chip(gfpath, bbox_gs, theta, new_size, []) mchipBGR = compute_chip(gfpath, mbbox_gs, theta, margin_size, []) # index = 0 w_cs, h_cs = new_size xo_ms, yo_ms = halfoffset_ms bbox_ms = [xo_ms, yo_ms, w_cs, h_cs] verts_gs = vt.scaled_verts_from_bbox(bbox_gs, theta, 1, 1) expanded_verts_gs = vt.scaled_verts_from_bbox(mbbox_gs, theta, 1, 1) expanded_verts_ms = vt.scaled_verts_from_bbox(bbox_ms, 0, 1, 1) # topheavy imgBGR = vt.draw_verts(imgBGR, verts_gs) imgBGR = vt.draw_verts(imgBGR, expanded_verts_gs) mchipBGR = vt.draw_verts(mchipBGR, expanded_verts_ms) fnum = 1 pt.imshow(imgBGR, pnum=(1, 3, 1), fnum=fnum, title='original image') pt.gca().set_xlabel(str(imgBGR.shape)) pt.imshow(chipBGR, pnum=(1, 3, 2), fnum=fnum, title='original chip') pt.gca().set_xlabel(str(chipBGR.shape)) pt.imshow( mchipBGR, pnum=(1, 3, 3), fnum=fnum, title='scaled chip with expanded margin.\n(orig margin drawn in orange)', ) pt.gca().set_xlabel(str(mchipBGR.shape)) pt.show_if_requested()
# pt.imshow(chipBGR) if __name__ == '__main__': """ CommandLine: xdoctest -m vtool.chip """ import xdoctest xdoctest.doctest_module(__file__)