Cutting one image into multiple images using the Python Image Library
Asked Answered
P

6

13

I need to cut this image into three parts using PIL and pick the middle part. How do I do it?

http://thedilbertstore.com/images/periodic_content/dilbert/dt110507dhct.jpg

Packer answered 19/5, 2011 at 13:14 Comment(0)
C
4

If the boxes are not known on before hand I would run a simple edge finding filter over the image (both x and y directions) to find the boundaries of the box.

A simple approach would be:

  1. Run horizontal edge filter over image. You now have an image where each pixel describes the changes in intensity left and right of that pixel. I.e. it will "find" vertical lines.
  2. For each column in the horizontal-edge-image get the average absolute magnitude of its rows. In the resulting 1 x WIDTH sized array you will find the vertical lines at the positions of highest value. Since the lines are more than one pixel wide yo might have to be a bit clever here.
  3. Do the same for the other axis to find the horizontal lines.

You could do some pre processing by first extracting only pixels that are black (or near black) if you believe that the borders of the boxes will always be black. But I doubt it'd be necessary since the above method should be very stable.

Corpus answered 19/5, 2011 at 14:40 Comment(2)
I need to automated this, sometimes the lines are hand-drawn, but there are lines for sure. Your answer is great, but I am wondering if you have any snippet of code to post here? or link to a site where this is done? The size of each box (sub-image) may not be fixed, so auto detecting the boxes is what I am looking for? I want to give an image of any size, with boxes in it and get individual images. So the algorithm has to make best guess on finding the vertical, horizontal lines. Thx a bunch.Packer
Sorry, I don't have any code lying around. But it seems like PIL has what is needed: I am guessing the FIND_EDGES filter at this page pythonware.com/library/pil/handbook/imagefilter.htm would be of use. Unfortunately it seems like this ould get the edges for both X and Y simultaneously. But you can define your own filter kernel. For horizontal try [-1, 0, 1] and for vertical, the same but as a column vector instead of a row vector.Robot
O
31

Say you have a really long picture like this.

Picture

And now you want to slice it up into smaller vertical bits, because it is so long.

Here is a Python script that will do that. This was useful to me for in preparing very long images for LaTeX docs.

from __future__ import division
import Image
import math
import os

def long_slice(image_path, out_name, outdir, slice_size):
    """slice an image into parts slice_size tall"""
    img = Image.open(image_path)
    width, height = img.size
    upper = 0
    left = 0
    slices = int(math.ceil(height/slice_size))

    count = 1
    for slice in range(slices):
        #if we are at the end, set the lower bound to be the bottom of the image
        if count == slices:
            lower = height
        else:
            lower = int(count * slice_size)  
        #set the bounding box! The important bit     
        bbox = (left, upper, width, lower)
        working_slice = img.crop(bbox)
        upper += slice_size
        #save the slice
        working_slice.save(os.path.join(outdir, "slice_" + out_name + "_" + str(count)+".png"))
        count +=1

if __name__ == '__main__':
    #slice_size is the max height of the slices in pixels
    long_slice("longcat.jpg","longcat", os.getcwd(), 300)

This is is the output

Picture


Picture


Picture

Obsecrate answered 10/1, 2013 at 7:4 Comment(0)
V
11

I wanted to up-vote Gourneau's solution, but lack the sufficient reputation. However, I figured I would post the code that I developed as a result of his answer just in case it might be helpful to somebody else. I also added the ability to iterate through a file structure, and choose an image width.

import Image
import os

# Set the root directory
rootdir = 'path/to/your/file/directory'

def long_slice(image_path, out_name, outdir, sliceHeight, sliceWidth):
    img = Image.open(image_path) # Load image
    imageWidth, imageHeight = img.size # Get image dimensions
    left = 0 # Set the left-most edge
    upper = 0 # Set the top-most edge
    while (left < imageWidth):
        while (upper < imageHeight):
            # If the bottom and right of the cropping box overruns the image.
            if (upper + sliceHeight > imageHeight and \
                left + sliceWidth > imageWidth):
                bbox = (left, upper, imageWidth, imageHeight)
            # If the right of the cropping box overruns the image
            elif (left + sliceWidth > imageWidth):
                bbox = (left, upper, imageWidth, upper + sliceHeight)
            # If the bottom of the cropping box overruns the image
            elif (upper + sliceHeight > imageHeight):
                bbox = (left, upper, left + sliceWidth, imageHeight)
            # If the entire cropping box is inside the image,
            # proceed normally.
            else:
                bbox = (left, upper, left + sliceWidth, upper + sliceHeight)
            working_slice = img.crop(bbox) # Crop image based on created bounds
            # Save your new cropped image.
            working_slice.save(os.path.join(outdir, 'slice_' + out_name + \
                '_' + str(upper) + '_' + str(left) + '.jpg'))
            upper += sliceHeight # Increment the horizontal position
        left += sliceWidth # Increment the vertical position
        upper = 0

if __name__ == '__main__':
    # Iterate through all the files in a set of directories.
    for subdir, dirs, files in os.walk(rootdir):
        for file in files:
            long_slice(subdir + '/' + file, 'longcat', subdir, 128, 128)
Valois answered 9/4, 2014 at 13:59 Comment(0)
H
7

For this particular image you would do

import Image
i = Image.open('dt110507dhct.jpg')
frame2 = i.crop(((275, 0, 528, 250)))
frame2.save('dt110507dhct_frame2.jpg')
Haricot answered 19/5, 2011 at 13:53 Comment(0)
C
4

If the boxes are not known on before hand I would run a simple edge finding filter over the image (both x and y directions) to find the boundaries of the box.

A simple approach would be:

  1. Run horizontal edge filter over image. You now have an image where each pixel describes the changes in intensity left and right of that pixel. I.e. it will "find" vertical lines.
  2. For each column in the horizontal-edge-image get the average absolute magnitude of its rows. In the resulting 1 x WIDTH sized array you will find the vertical lines at the positions of highest value. Since the lines are more than one pixel wide yo might have to be a bit clever here.
  3. Do the same for the other axis to find the horizontal lines.

You could do some pre processing by first extracting only pixels that are black (or near black) if you believe that the borders of the boxes will always be black. But I doubt it'd be necessary since the above method should be very stable.

Corpus answered 19/5, 2011 at 14:40 Comment(2)
I need to automated this, sometimes the lines are hand-drawn, but there are lines for sure. Your answer is great, but I am wondering if you have any snippet of code to post here? or link to a site where this is done? The size of each box (sub-image) may not be fixed, so auto detecting the boxes is what I am looking for? I want to give an image of any size, with boxes in it and get individual images. So the algorithm has to make best guess on finding the vertical, horizontal lines. Thx a bunch.Packer
Sorry, I don't have any code lying around. But it seems like PIL has what is needed: I am guessing the FIND_EDGES filter at this page pythonware.com/library/pil/handbook/imagefilter.htm would be of use. Unfortunately it seems like this ould get the edges for both X and Y simultaneously. But you can define your own filter kernel. For horizontal try [-1, 0, 1] and for vertical, the same but as a column vector instead of a row vector.Robot
S
3

Look at the crop() method of PIL

http://effbot.org/imagingbook/image.htm

(requires knowledge of the bounding box of the image...assuming that the image has the same dimensions every day you should be able to determine the bounding box once and use it for all the time).

Saccharate answered 19/5, 2011 at 13:24 Comment(0)
G
0
  1. Load the Image
  2. Get the Size
  3. Use the Crop method
  4. Save the middle image
Goldin answered 19/5, 2011 at 13:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.