[python] How to Split Image Into Multiple Pieces in Python

I'm trying to split a photo into multiple pieces using PIL.

def crop(Path,input,height,width,i,k,x,y,page):
    im = Image.open(input)
    imgwidth = im.size[0]
    imgheight = im.size[1]
    for i in range(0,imgheight-height/2,height-2):
        print i
        for j in range(0,imgwidth-width/2,width-2):
            print j
            box = (j, i, j+width, i+height)
            a = im.crop(box)
            a.save(os.path.join(Path,"PNG","%s" % page,"IMG-%s.png" % k))
            k +=1

but it doesn't seem to be working. It splits the photo but not in an exact way (you can try it).

This question is related to python image split python-imaging-library crop

The answer is


Edit: I believe this answer missed the intent to cut an image into rectangles in columns and rows. This answer cuts only into rows. It looks like other answers cut in columns and rows.

Simpler than all these is to use a wheel someone else invented :) It may be more involved to set up, but then it's a snap to use.

These instructions are for Windows 7; they may need to be adapted for other OSs.

Get and install pip from here.

Download the install archive, and extract it to your root Python installation directory. Open a console and type (if I recall correctly):

python get-pip.py install

Then get and install the image_slicer module via pip, by entering the following command at the console:

python -m pip install image_slicer

Copy the image you want to slice into the Python root directory, open a python shell (not the "command line"), and enter these commands:

import image_slicer
image_slicer.slice('huge_test_image.png', 14)

The beauty of this module is that it

  1. Is installed in python
  2. Can invoke an image split with two lines of code
  3. Accepts any even number as an image slice parameter (e.g. 14 in this example)
  4. Takes that parameter and automagically splits the given image into so many slices, and auto-saves the resultant numbered tiles in the same directory, and finally
  5. Has a function to stitch the image tiles back together (which I haven't yet tested); files apparently must be named after the convention which you will see in the split files after testing the image_slicer.slice function.

Here is a concise, pure-python solution that works in both python 3 and 2:

from PIL import Image

infile = '20190206-135938.1273.Easy8thRunnersHopefully.jpg'
chopsize = 300

img = Image.open(infile)
width, height = img.size

# Save Chops of original image
for x0 in range(0, width, chopsize):
   for y0 in range(0, height, chopsize):
      box = (x0, y0,
             x0+chopsize if x0+chopsize <  width else  width - 1,
             y0+chopsize if y0+chopsize < height else height - 1)
      print('%s %s' % (infile, box))
      img.crop(box).save('zchop.%s.x%03d.y%03d.jpg' % (infile.replace('.jpg',''), x0, y0))

Notes:

  • The crops that go over the right and bottom of the original image are adjusted to the original image limit and contain only the original pixels.
  • It's easy to choose a different chopsize for w and h by using two chopsize vars and replacing chopsize as appropriate in the code above.


  • import cv2
    
    def crop_image(image_path, output_path):
        im =  cv2.imread(os.listdir()[2])
        imgheight=im.shape[0]
        imgwidth=im.shape[1]
    
        y1 = 0
        M = 2000
        N = 2000
        for y in range(0,imgheight,M):
            for x in range(0, imgwidth, N):
                y1 = y + M
                x1 = x + N
                tiles = im[y:y+M,x:x+N]
                if tiles.shape[0] < 100 or  tiles.shape[1]<100:
                    continue
    
                cv2.rectangle(im, (x, y), (x1, y1), (0, 255, 0))
                cv2.imwrite(output_path +  str(x) + '_' + str(y)+"{}.png".format(image_path),tiles)
    crop_image(os.listdir()[2], './cutted/')
    

    This is my script tools, it is very sample to splite css-sprit image into icons:

    Usage: split_icons.py img dst_path width height
    Example: python split_icons.py icon-48.png gtliu 48 48
    

    Save code into split_icons.py :

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import os
    import sys
    import glob
    from PIL import Image
    
    def Usage():
        print '%s img dst_path width height' % (sys.argv[0])
        sys.exit(1)
    
    if len(sys.argv) != 5:
        Usage()
    
    src_img = sys.argv[1]
    dst_path = sys.argv[2]
    
    if not os.path.exists(sys.argv[2]) or not os.path.isfile(sys.argv[1]):
        print 'Not exists', sys.argv[2], sys.argv[1]
        sys.exit(1)
    
    w, h = int(sys.argv[3]), int(sys.argv[4])
    im = Image.open(src_img)
    im_w, im_h = im.size
    print 'Image width:%d height:%d  will split into (%d %d) ' % (im_w, im_h, w, h)
    w_num, h_num = int(im_w/w), int(im_h/h)
    
    for wi in range(0, w_num):
        for hi in range(0, h_num):
            box = (wi*w, hi*h, (wi+1)*w, (hi+1)*h)
            piece = im.crop(box)
            tmp_img = Image.new('L', (w, h), 255)
            tmp_img.paste(piece)
            img_path = os.path.join(dst_path, "%d_%d.png" % (wi, hi))
            tmp_img.save(img_path)
    

    Not sure if this is the most efficient answer, but it works for me:

    import os
    import glob
    from PIL import Image
    Image.MAX_IMAGE_PIXELS = None # to avoid image size warning
    
    imgdir = "/path/to/image/folder"
    # if you want file of a specific extension (.png):
    filelist = [f for f in glob.glob(imgdir + "**/*.png", recursive=True)]
    savedir = "/path/to/image/folder/output"
    
    start_pos = start_x, start_y = (0, 0)
    cropped_image_size = w, h = (500, 500)
    
    for file in filelist:
        img = Image.open(file)
        width, height = img.size
    
        frame_num = 1
        for col_i in range(0, width, w):
            for row_i in range(0, height, h):
                crop = img.crop((col_i, row_i, col_i + w, row_i + h))
                name = os.path.basename(file)
                name = os.path.splitext(name)[0]
                save_to= os.path.join(savedir, name+"_{:03}.png")
                crop.save(save_to.format(frame_num))
                frame_num += 1
    

    This is mostly based on DataScienceGuy answer here


    1. crop would be a more reusable function if you separate the cropping code from the image saving code. It would also make the call signature simpler.
    2. im.crop returns a Image._ImageCrop instance. Such instances do not have a save method. Instead, you must paste the Image._ImageCrop instance onto a new Image.Image
    3. Your ranges do not have the right step sizes. (Why height-2 and not height? for example. Why stop at imgheight-(height/2)?).

    So, you might try instead something like this:

    import Image
    import os
    
    def crop(infile,height,width):
        im = Image.open(infile)
        imgwidth, imgheight = im.size
        for i in range(imgheight//height):
            for j in range(imgwidth//width):
                box = (j*width, i*height, (j+1)*width, (i+1)*height)
                yield im.crop(box)
    
    if __name__=='__main__':
        infile=...
        height=...
        width=...
        start_num=...
        for k,piece in enumerate(crop(infile,height,width),start_num):
            img=Image.new('RGB', (height,width), 255)
            img.paste(piece)
            path=os.path.join('/tmp',"IMG-%s.png" % k)
            img.save(path)
    

    Splitting image to tiles of MxN pixels (assuming im is numpy.ndarray):

    tiles = [im[x:x+M,y:y+N] for x in range(0,im.shape[0],M) for y in range(0,im.shape[1],N)]
    

    In the case you want to split the image to four pieces:

    M = im.shape[0]//2
    N = im.shape[1]//2
    

    tiles[0] holds the upper left tile


    I find it easier to skimage.util.view_as_windows or `skimage.util.view_as_blocks which also allows you to configure the step

    http://scikit-image.org/docs/dev/api/skimage.util.html?highlight=view_as_windows#skimage.util.view_as_windows


    Here is a late answer that works with Python 3

    from PIL import Image
    import os
    
    def imgcrop(input, xPieces, yPieces):
        filename, file_extension = os.path.splitext(input)
        im = Image.open(input)
        imgwidth, imgheight = im.size
        height = imgheight // yPieces
        width = imgwidth // xPieces
        for i in range(0, yPieces):
            for j in range(0, xPieces):
                box = (j * width, i * height, (j + 1) * width, (i + 1) * height)
                a = im.crop(box)
                try:
                    a.save("images/" + filename + "-" + str(i) + "-" + str(j) + file_extension)
                except:
                    pass
    

    Usage:

    imgcrop("images/testing.jpg", 5, 5)
    

    Then the images will be cropped into pieces according to the specified X and Y pieces, in my case 5 x 5 = 25 pieces


    I tried the solutions above, but sometimes you just gotta do it yourself. Might be off by a pixel in some cases but works fine in general.

    import matplotlib.pyplot as plt
    import numpy as np
    def image_to_tiles(im, number_of_tiles = 4, plot=False):
        """
        Function that splits SINGLE channel images into tiles
        :param im: image: single channel image (NxN matrix)
        :param number_of_tiles: squared number
        :param plot:
        :return tiles:
        """
        n_slices = np.sqrt(number_of_tiles)
        assert int(n_slices + 0.5) ** 2 == number_of_tiles, "Number of tiles is not a perfect square"
    
        n_slices = n_slices.astype(np.int)
        [w, h] = cropped_npy.shape
    
        r = np.linspace(0, w, n_slices+1)
        r_tuples = [(np.int(r[i]), np.int(r[i+1])) for i in range(0, len(r)-1)]
        q = np.linspace(0, h, n_slices+1)
        q_tuples = [(np.int(q[i]), np.int(q[i+1])) for i in range(0, len(q)-1)]
    
        tiles = []
        for row in range(n_slices):
            for column in range(n_slices):
                [x1, y1, x2, y2] = *r_tuples[row], *q_tuples[column] 
                tiles.append(im[x1:y1, x2:y2])
    
        if plot:
            fig, axes = plt.subplots(n_slices, n_slices, figsize=(10,10))
            c = 0
            for row in range(n_slices):
                for column in range(n_slices):
                    axes[row,column].imshow(tiles[c])
                    axes[row,column].axis('off')
                    c+=1
    
        return tiles
    

    Hope it helps.


    As an alternative to other solutions, we will construct the tiles by generating a grid of coordinates using itertools.product. We will ignore partial tiles on the edges, only iterating through the Cartesian product between the two intervals, i.e. range(0, h-h%d, d) X range(0, w-w%d, d).

    Given fp: the file name to the image, d: the tile size, opt.path: the path to the directory containing the images, and opt.out: is the directory where tiles will be outputted:

    def tile(filename, dir_in, dir_out, d):
        name, ext = os.path.splitext(filename)
        img = Image.open(os.path.join(dir_in, fp))
        w, h = img.size
        
        grid = list(product(range(0, h-h%d, d), range(0, w-w%d, d)))
        for i, j in grid:
            box = (j, i, j+d, i+d)
            out = os.path.join(dir_out, f'{name}_{i}_{j}{ext}')
            img.crop(box).save(out)
    

    enter image description here


    import os
    import sys
    from PIL import Image
    
    savedir = r"E:\new_mission _data\test"
    filename = r"E:\new_mission _data\test\testing1.png"
    img = Image.open(filename)
    width, height = img.size
    start_pos = start_x, start_y = (0, 0)
    cropped_image_size = w, h = (1024,1024)
    
    frame_num = 1
    for col_i in range(0, width, w):
        for row_i in range(0, height, h):
            crop = img.crop((col_i, row_i, col_i + w, row_i + h))
            save_to= os.path.join(savedir, "testing_{:02}.png")
            crop.save(save_to.format(frame_num))
            frame_num += 1
    

    Examples related to python

    programming a servo thru a barometer Is there a way to view two blocks of code from the same file simultaneously in Sublime Text? python variable NameError Why my regexp for hyphenated words doesn't work? Comparing a variable with a string python not working when redirecting from bash script is it possible to add colors to python output? Get Public URL for File - Google Cloud Storage - App Engine (Python) Real time face detection OpenCV, Python xlrd.biffh.XLRDError: Excel xlsx file; not supported Could not load dynamic library 'cudart64_101.dll' on tensorflow CPU-only installation

    Examples related to image

    Reading images in python Numpy Resize/Rescale Image Convert np.array of type float64 to type uint8 scaling values Extract a page from a pdf as a jpeg How do I stretch an image to fit the whole background (100% height x 100% width) in Flutter? Angular 4 img src is not found How to make a movie out of images in python Load local images in React.js How to install "ifconfig" command in my ubuntu docker image? How do I display local image in markdown?

    Examples related to split

    Parameter "stratify" from method "train_test_split" (scikit Learn) Pandas split DataFrame by column value How to split large text file in windows? Attribute Error: 'list' object has no attribute 'split' Split function in oracle to comma separated values with automatic sequence How would I get everything before a : in a string Python Split String by delimiter position using oracle SQL JavaScript split String with white space Split a String into an array in Swift? Split pandas dataframe in two if it has more than 10 rows

    Examples related to python-imaging-library

    How do I install PIL/Pillow for Python 3.6? TypeError: Image data can not convert to float Combine several images horizontally with Python Generate random colors (RGB) How to show PIL Image in ipython notebook Why can't Python import Image from PIL? Importing images from a directory (Python) to list or dictionary ImportError: No module named Image Installing PIL with pip Image.open() cannot identify image file - Python?

    Examples related to crop

    Crop image to specified size and picture location How to "crop" a rectangular image into a square with CSS? How to crop an image using PIL? Android Crop Center of Bitmap How to Split Image Into Multiple Pieces in Python Crop image in PHP What's the algorithm to calculate aspect ratio?