如何在Python中将图像分割成多个片段

时间:2011-05-10 16:37:33

标签: python image split python-imaging-library crop

我正在尝试使用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

但它似乎没有用。它会分割照片,但不是以精确的方式(您可以尝试)。

16 个答案:

答案 0 :(得分:23)

from PIL import Image

def crop(path, input, height, width, k, page, area):
    im = Image.open(input)
    imgwidth, imgheight = im.size
    for i in range(0,imgheight,height):
        for j in range(0,imgwidth,width):
            box = (j, i, j+width, i+height)
            a = im.crop(box)
            try:
                o = a.crop(area)
                o.save(os.path.join(path,"PNG","%s" % page,"IMG-%s.png" % k))
            except:
                pass
            k +=1

答案 1 :(得分:20)

编辑:我相信这个答案错过了将图像切割成列和行中的矩形的意图。这个答案只能分成几行。它看起来像列和行中的其他答案。

比所有这些更简单的是使用其他人发明的轮子:)设置可能涉及更多,但随后可以轻松使用。

这些说明适用于Windows 7;它们可能需要适应其他操作系统。

here获取并安装点子。

下载安装档案,并将其解压缩到根Python安装目录。打开一个控制台并键入(如果我没记错的话):

python get-pip.py install

然后在控制台输入以下命令,通过pip获取并安装image_slicer模块:

python -m pip install image_slicer

将要切片的图像复制到Python根目录中,打开python shell(而不是"命令行"),然后输入以下命令:

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

这个模块的美妙之处在于它

  1. 安装在python中
  2. 可以使用两行代码调用图像分割
  3. 接受任何偶数作为图像切片参数(例如,在此示例中为14)
  4. 使用该参数并自动将给定图像拆分为如此多的切片,并将结果编号的切片自动保存在同一目录中,最后
  5. 具有将图像拼接在一起的功能(我还没有测试过);文件显然必须在测试image_slicer.slice函数后在分割文件中看到的约定后命名。

答案 2 :(得分:10)

  1. crop将更具可重用性 功能,如果你分开 从中裁剪代码 图像保存 码。它也会打电话 签名更简单。
  2. im.crop返回一个 Image._ImageCrop个实例。这样 实例没有保存方法。 相反,你必须粘贴 Image._ImageCrop实例到了 新Image.Image
  3. 您的范围没有权利 步长。 (为什么height-2而不是 height?例如。为何停下来 imgheight-(height/2)?)。
  4. 所以,你可能会尝试这样的事情:

    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)
    

答案 3 :(得分:10)

将图像拆分为MxN像素的图块(假设im为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)]

如果您想将图像分割为四个部分:

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

tiles [0]保持左上方的图块

答案 4 :(得分:7)

作为替代解决方案,我们将通过使用 itertools.product 生成坐标网格来构建图块。我们将忽略边缘上的部分瓦片,只迭代两个区间之间的笛卡尔积, range(0, h-h%d, d) X range(0, w-w%d, d)

给定 filename:图像文件名,d:图块大小,dir_in:包含图像的目录的路径,以及 dir_out:所在目录瓷砖将被输出:

def tile(filename, dir_in, dir_out, d):
    name, ext = os.path.splitext(filename)
    img = Image.open(os.path.join(dir_in, filename))
    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

答案 5 :(得分:1)

这是一个简洁的纯python解决方案,可在python 3和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))

注意:  

  • 将超出原始图像右侧和底部的作物调整为原始图像限制,并且仅包含原始像素。  
  • 通过使用两个chopsize变量并根据上面的代码适当替换chopsize,可以很容易地为w和h选择不同的chopsize。

  • 答案 6 :(得分:1)

    不确定这是否是最有效的答案,但对我有用:

    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
    

    这主要基于DataScienceGuy的答案here

    答案 7 :(得分:1)

    这是一个适用于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
    

    用法:

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

    然后根据指定的X和Y片段将图像裁剪为片段,在我的情况下为5 x 5 = 25片段

    答案 8 :(得分:1)

    这是另一种解决方案,只需使用 NumPy 内置 np.array_split :

    def divide_img_blocks(img, n_blocks=(5, 5)):
        horizontal = np.array_split(img, n_blocks[0])
        splitted_img = [np.array_split(block, n_blocks[1], axis=1) for block in horizontal]
        return np.asarray(splitted_img, dtype=np.ndarray).reshape(n_blocks)
    

    它返回一个 NumPy 数组,其维度为 n_blocks。 数组的每个元素都是一个块,因此要访问每个块并将其保存为图像,您应该编写如下内容:

    result = divide_img_blocks(my_image)
    
    for i in range(result.shape[0]):
        for j in range(result.shape[1]):
            cv2.imwrite(f"my_block_{i}_{j}.jpg", result[i,j])
    

    这个答案非常快,比@Nir 的答案还要快,在贴出来的答案中,它是最干净的。此外,它比建议的包(即 image_slicer)快近三个数量级。

    Time taken by divide_img_blocks: 0.0009832382202148438
    Time taken by Nir answer: 0.002960681915283203
    Time taken by image_slicer.slice: 0.4419238567352295
    

    希望它仍然有用。

    答案 9 :(得分:0)

    这是我的脚本工具,将css-sprit图像拼接成图标非常简洁:

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

    将代码保存到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)
    

    答案 10 :(得分:0)

    我发现skimage.util.view_as_windows或`skimage.util.view_as_blocks更容易,这也允许你配置步骤

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

    答案 11 :(得分:0)

    我尝试了上述解决方案,但有时您只需要自己做即可。 在某些情况下可能会相距一个像素,但总体上效果很好。

    return View('Shop.Cart.favorites'); 
    

    希望有帮助。

    答案 12 :(得分:0)

    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
    

    答案 13 :(得分:0)

    我建议使用 multiprocessing 而不是常规的 for 循环,如下所示:

    from PIL 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)
    
    def til_image(infile):
        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)
    
    from multiprocessing import Pool, cpu_count
    try:
        pool = Pool(cpu_count())
        pool.imap_unordered(tile_image, os.listdir(root), chunksize=4)
    finally:
        pool.close()
    

    答案 14 :(得分:0)

    最简单的方法:

    import image_slicer
    image_slicer.slice('/Address of image for exp/A1.png',16)
    

    此命令将图像分成 16 个切片并将它们保存在输入图像所在的目录中。 你应该先安装 image_slicer:

    pip install image_slicer
    

    答案 15 :(得分:-1)

    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/')