:py:mod:`kwimage.im_stack` ========================== .. py:module:: kwimage.im_stack .. autoapi-nested-parse:: Stack images Module Contents --------------- Functions ~~~~~~~~~ .. autoapisummary:: kwimage.im_stack.stack_images kwimage.im_stack.stack_images_grid kwimage.im_stack._stack_two_images kwimage.im_stack._efficient_rectangle_packing .. py:function:: stack_images(images, axis=0, resize=None, interpolation=None, overlap=0, return_info=False, bg_value=None) Make a new image with the input images side-by-side :Parameters: * **images** (*Iterable[ndarray[ndim=2]]*) -- image data * **axis** (*int*) -- axis to stack on (either 0 or 1) * **resize** (*int, str, or None*) -- if None image sizes are not modified, otherwise resize resize can be either 0 or 1. We resize the `resize`-th image to match the `1 - resize`-th image. Can also be strings "larger" or "smaller". * **interpolation** (*int or str*) -- string or cv2-style interpolation type. only used if resize or overlap > 0 * **overlap** (*int*) -- number of pixels to overlap. Using a negative number results in a border. * **return_info** (*bool*) -- if True, returns transforms (scales and translations) to map from original image to its new location. :returns: an image of stacked images side by side OR Tuple[ndarray, List]: where the first item is the aformentioned stacked image and the second item is a list of transformations for each input image mapping it to its location in the returned image. :rtype: ndarray .. rubric:: Example >>> import kwimage >>> img1 = kwimage.grab_test_image('carl', space='rgb') >>> img2 = kwimage.grab_test_image('astro', space='rgb') >>> images = [img1, img2] >>> imgB, transforms = stack_images(images, axis=0, resize='larger', >>> overlap=-10, return_info=True) >>> print('imgB.shape = {}'.format(imgB.shape)) >>> # xdoctest: +REQUIRES(--show) >>> import kwplot >>> import kwimage >>> kwplot.autompl() >>> kwplot.imshow(imgB, colorspace='rgb') >>> wh1 = np.multiply(img1.shape[0:2][::-1], transforms[0].scale) >>> wh2 = np.multiply(img2.shape[0:2][::-1], transforms[1].scale) >>> xoff1, yoff1 = transforms[0].translation >>> xoff2, yoff2 = transforms[1].translation >>> xywh1 = (xoff1, yoff1, wh1[0], wh1[1]) >>> xywh2 = (xoff2, yoff2, wh2[0], wh2[1]) >>> kwplot.draw_boxes(kwimage.Boxes([xywh1], 'xywh'), color=(1.0, 0, 0)) >>> kwplot.draw_boxes(kwimage.Boxes([xywh2], 'xywh'), color=(1.0, 0, 0)) >>> kwplot.show_if_requested() ((662, 512, 3), (0.0, 0.0), (0, 150)) .. py:function:: stack_images_grid(images, chunksize=None, axis=0, overlap=0, return_info=False, bg_value=None) Stacks images in a grid. Optionally return transforms of original image positions in the output image. :Parameters: * **images** (*Iterable[ndarray[ndim=2]]*) -- image data * **chunksize** (*int, default=None*) -- number of rows per column or columns per row depending on the value of `axis`. If unspecified, computes this as `int(sqrt(len(images)))`. * **axis** (*int, default=0*) -- If 0, chunksize is columns per row. If 1, chunksize is rows per column. * **overlap** (*int*) -- number of pixels to overlap. Using a negative number results in a border. * **return_info** (*bool*) -- if True, returns transforms (scales and translations) to map from original image to its new location. :returns: an image of stacked images in a grid pattern OR Tuple[ndarray, List]: where the first item is the aformentioned stacked image and the second item is a list of transformations for each input image mapping it to its location in the returned image. :rtype: ndarray .. py:function:: _stack_two_images(img1, img2, axis=0, resize=None, interpolation=None, overlap=0, bg_value=None) :returns: imgB, offset_tup, sf_tup :rtype: Tuple[ndarray, Tuple, Tuple] Ignore: import xinspect globals().update(xinspect.get_func_kwargs(_stack_two_images)) resize = 1 overlap = -10 .. py:function:: _efficient_rectangle_packing() .. rubric:: References https://en.wikipedia.org/wiki/Packing_problems https://github.com/Penlect/rectangle-packer https://github.com/secnot/rectpack https://stackoverflow.com/questions/1213394/what-algorithm-can-be-used-for-packing-rectangles-of-different-sizes-into-the-sm https://www.codeproject.com/Articles/210979/Fast-optimizing-rectangle-packing-algorithm-for-bu Requires: pip install rectangle-packer Ignore: >>> import kwimage >>> anchors = anchors=[[1, 1], [3 / 4, 1], [1, 3 / 4]] >>> boxes = kwimage.Boxes.random(num=100, anchors=anchors).scale((100, 100)).to_xywh() >>> # Create a bunch of rectangles (width, height) >>> sizes = boxes.data[:, 2:4].astype(int).tolist() >>> import rpack >>> positions = rpack.pack(sizes) >>> boxes.data[:, 0:2] = positions >>> boxes = boxes.scale(0.95, about='center') >>> # xdoctest: +REQUIRES(--show) >>> import kwplot >>> kwplot.autompl() >>> boxes.draw() >>> # The result will be a list of (x, y) positions: >>> positions images = [kwimage.grab_test_image(key) for key in kwimage.grab_test_image.keys()] images = [kwimage.imresize(g, max_dim=256) for g in images] sizes = [g.shape[0:2][::-1] for g in images] import rpack positions = rpack.pack(sizes) !pip install rectpack import rectpack bin_width = 512 packer = rectpack.newPacker(rotation=False) for rid, (w, h) in enumerate(sizes): packer.add_rect(w, h, rid=rid) max_w, max_h = np.array(sizes).sum(axis=0) # f = max_w / bin_width avail_height = max_h packer.add_bin(bin_width, avail_height) packer.pack() packer[0] all_rects = packer.rect_list() all_rects = np.array(all_rects) rids = all_rects[:, 5] tl_x = all_rects[:, 1] tl_y = all_rects[:, 2] w = all_rects[:, 3] h = all_rects[:, 4] ltrb = kwimage.Boxes(all_rects[:, 1:5], 'xywh').to_ltrb() canvas_w, canvas_h = ltrb.data[:, 2:4].max(axis=0) canvas = np.zeros((canvas_h, canvas_w), dtype=np.float32) for b, x, y, w, h, rid in all_rects: img = images[rid] img = kwimage.ensure_float01(img) canvas, img = kwimage.make_channels_comparable(canvas, img) canvas[y: y + h, x: x + w] = img kwplot.imshow(canvas)