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

发布于 2024-11-06 19:19:22 字数 531 浏览 10 评论 0原文

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

但它似乎不起作用。它会分割照片,但不是以精确的方式(你可以尝试一下)。

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).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(24

放手` 2024-11-13 19:19:22

将图像分割为 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] 保存左上角的图块

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

不可一世的女人 2024-11-13 19:19:22

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

给定 filename:图像文件名,d:图块大小,dir_in:包含图像的目录的路径,以及 dir_out:瓦片输出的目录:

from PIL import Image
from itertools import product
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 = 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)

在此处输入图像描述

As an alternative solution, 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 filename: the image file name, d: the tile size, dir_in: the path to the directory containing the image, and dir_out: the directory where tiles will be outputted:

from PIL import Image
from itertools import product
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 = 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

心凉 2024-11-13 19:19:22

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

比所有这些更简单的是使用其他人发明的轮子:) 设置起来可能会更复杂,但使用起来很简单。

这些说明适用于 Windows 7;它们可能需要针对其他操作系统进行调整。

此处获取并安装 pip。

下载安装存档,并将其解压到 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 函数后在分割文件中看到的约定命名。

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.
风吹雪碎 2024-11-13 19:19:22
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
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
不念旧人 2024-11-13 19:19:22
  1. crop 会更可重用
    如果你分开函数
    裁剪代码
    图像保存
    代码。它还会拨打电话
    签名更简单。
  2. im.crop 返回一个
    Image._ImageCrop 实例。这样的
    实例没有保存方法。
    相反,您必须粘贴
    Image._ImageCrop 实例到
    new Image.Image
  3. 您的范围没有正确的
    步长。 (为什么height-2而不是
    高度?例如。为什么停在
    imgheight-(高度/2)?)。

所以,你可以尝试这样的事情:

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)
  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)
不喜欢何必死缠烂打 2024-11-13 19:19:22

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

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

盛装女皇 2024-11-13 19:19:22

这是一个简洁的纯 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。

  • 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.

  • 海拔太高太耀眼 2024-11-13 19:19:22

    我发现 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

    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

    凉薄对峙 2024-11-13 19:19:22

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

    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 答案 此处

    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

    咆哮 2024-11-13 19:19:22

    这是另一个解决方案,仅使用 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 答案更快,@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
    

    希望它仍然有用。

    Here is another solution, just using NumPy built-in 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)
    

    It returns a NumPy array with the dimension passed as n_blocks.
    Each element of the array is a block, so to access each block and save it as an image you should write something like the following:

    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])
    

    This answer is very fast, faster than @Nir answer, which among the posted ones was the cleanest. Additionally is almost three orders of magnitude faster than the suggested package (i.e. 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
    

    Hope it can still be useful.

    能怎样 2024-11-13 19:19:22
    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
    
    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
    
    望喜 2024-11-13 19:19:22

    对于任何寻求简单方法的人来说,这里有一个简单的工作函数,用于将图像分割成 NxN 个部分。

    def slice_image(filename, N):
    
        i = Image.open(filename)
    
        width = i.width
        height = i.height
    
        for x in range(N):
    
            for y in range(N):
    
                index = (x * pieces) + 1 + y
    
                img = i.crop((x * width/N, y * height/N,
                              x * width/N+ width/N, y * height/N+ height/N))
    
                img.save(f"{filename}_sliced_{index}.jpeg")
    

    For anyone looking for a simple approach to this, here is a simple working function for splitting an image into NxN sections.

    def slice_image(filename, N):
    
        i = Image.open(filename)
    
        width = i.width
        height = i.height
    
        for x in range(N):
    
            for y in range(N):
    
                index = (x * pieces) + 1 + y
    
                img = i.crop((x * width/N, y * height/N,
                              x * width/N+ width/N, y * height/N+ height/N))
    
                img.save(f"{filename}_sliced_{index}.jpeg")
    
    层林尽染 2024-11-13 19:19:22

    将图像分割成特定大小的正方形

    我调整了一个解决方案,使其接受特定的图块大小而不是图块数量,因为我需要将图像切割成 32px 正方形的网格。

    参数是image_path图块大小(以像素为单位)。

    我试图使代码尽可能具有可读性。

    # Imports
    from PIL import Image
    import os
    import random
    
    # Function
    def image_to_tiles(im, tile_size = 32):
        """
        Function that splits an image into tiles
        :param im: image: image path
        :param tile_size: width in pixels of a tile
        :return tiles:
        """
        image = Image.open(im)
            
        w = image.width
        h = image.height
        
        row_count = np.int64((h-h%tile_size)/tile_size)
        col_count = np.int64((w-w%tile_size)/tile_size)
        
        n_slices = np.int64(row_count*col_count)
        
        # Image info
        print(f'Image: {im}')
        print(f'Dimensions: w:{w} h:{h}')
        print(f'Tile count: {n_slices}')
    
    
        r = np.linspace(0, w, row_count+1)
        r_tuples = [(np.int64(r[i]), np.int64(r[i])+tile_size) for i in range(0, len(r)-1)]
        q = np.linspace(0, h, col_count+1)
        q_tuples = [(np.int64(q[i]), np.int64(q[i])+tile_size) for i in range(0, len(q)-1)]
        
        #print(f'r_tuples:{r_tuples}\n\nq_tuples:{q_tuples}\n')
        
        tiles = []
        for row in range(row_count):
            for column in range(col_count):
                [y1, y2, x1, x2] = *r_tuples[row], *q_tuples[column]
                x2 = x1+tile_size
                y2 = y1+tile_size
                tile_image = image.crop((x1,y1,x2,y2))
                tile_coords = {'x1':x1,'y1':y1,'x2':x2,'y2':y2}
                tiles.append({'image':tile_image,'coords':tile_coords})
    
        return tiles
    
    # Testing:
    img_path ='/home/user/path/to/image.jpg'
    tiles = image_to_tiles(img_path)
    
    for i in range(20):
        tile = random.choice(tiles)
        tile['image'].show()
    

    Splitting an image into squares of a specific size

    I adapted a solution so that it accepts a specific tile size instead of an amount of tiles because I needed to cut the image up into a grid of 32px squares.

    The parameters are the image_path and the size of the tiles in pixels.

    I tried to make the code as readable as possible.

    # Imports
    from PIL import Image
    import os
    import random
    
    # Function
    def image_to_tiles(im, tile_size = 32):
        """
        Function that splits an image into tiles
        :param im: image: image path
        :param tile_size: width in pixels of a tile
        :return tiles:
        """
        image = Image.open(im)
            
        w = image.width
        h = image.height
        
        row_count = np.int64((h-h%tile_size)/tile_size)
        col_count = np.int64((w-w%tile_size)/tile_size)
        
        n_slices = np.int64(row_count*col_count)
        
        # Image info
        print(f'Image: {im}')
        print(f'Dimensions: w:{w} h:{h}')
        print(f'Tile count: {n_slices}')
    
    
        r = np.linspace(0, w, row_count+1)
        r_tuples = [(np.int64(r[i]), np.int64(r[i])+tile_size) for i in range(0, len(r)-1)]
        q = np.linspace(0, h, col_count+1)
        q_tuples = [(np.int64(q[i]), np.int64(q[i])+tile_size) for i in range(0, len(q)-1)]
        
        #print(f'r_tuples:{r_tuples}\n\nq_tuples:{q_tuples}\n')
        
        tiles = []
        for row in range(row_count):
            for column in range(col_count):
                [y1, y2, x1, x2] = *r_tuples[row], *q_tuples[column]
                x2 = x1+tile_size
                y2 = y1+tile_size
                tile_image = image.crop((x1,y1,x2,y2))
                tile_coords = {'x1':x1,'y1':y1,'x2':x2,'y2':y2}
                tiles.append({'image':tile_image,'coords':tile_coords})
    
        return tiles
    
    # Testing:
    img_path ='/home/user/path/to/image.jpg'
    tiles = image_to_tiles(img_path)
    
    for i in range(20):
        tile = random.choice(tiles)
        tile['image'].show()
    
    别低头,皇冠会掉 2024-11-13 19:19:22

    感谢 @Ivan 教我一些有关 itertools 和网格的知识。来这里是将断层扫描 3D 图像数据(tif 文件)分割成更小的区域以进​​行评估。我将脚本改编为 3D-TIF 文件(使用 tiffile 库)并添加了“居中”方法。因此,图块不是从左上角开始,而是居中并在每个方向的边界处裁剪太小的图块。也许这也对其他人有帮助。

    from itertools import product
    import tifffile as tif
    import numpy as np
    
    path = 'PATH'
    filename= 'FILENAME.tif'
    img = tif.imread(path+filename)
    
    depth, height, width = img.shape
    tilesize = 100
    
    grid = product(range(int((depth%tilesize)/2), int(depth-(depth%tilesize)/2), tilesize),
                   range(int((width%tilesize)/2), int(width-((width%tilesize)/2)), tilesize), 
                   range(int((height%tilesize)/2), int(height-(height%tilesize)/2), tilesize))
    
    for z,y,x in grid:
        crop = img[z:z+tilesize, y:y+tilesize, x:x+tilesize]
        tif.imwrite(path+filename+f'{z:04d}z_{y:04d}y_{x:04d}x.tif', crop, dtype = np.uint8)
    

    Thanks @Ivan for teaching me something about itertools and grids. Came here to split up tomographic 3D image data (tif-files) into smaller regions for evaluation. I adapted the script to 3D-TIF files (using the tiffile library) and added a "centered" approach. So the tiles don't start in the upper-left corner but are centered and crop too small tiles at the borders at each direction. Maybe this also help other people.

    from itertools import product
    import tifffile as tif
    import numpy as np
    
    path = 'PATH'
    filename= 'FILENAME.tif'
    img = tif.imread(path+filename)
    
    depth, height, width = img.shape
    tilesize = 100
    
    grid = product(range(int((depth%tilesize)/2), int(depth-(depth%tilesize)/2), tilesize),
                   range(int((width%tilesize)/2), int(width-((width%tilesize)/2)), tilesize), 
                   range(int((height%tilesize)/2), int(height-(height%tilesize)/2), tilesize))
    
    for z,y,x in grid:
        crop = img[z:z+tilesize, y:y+tilesize, x:x+tilesize]
        tif.imwrite(path+filename+f'{z:04d}z_{y:04d}y_{x:04d}x.tif', crop, dtype = np.uint8)
    
    北方。的韩爷 2024-11-13 19:19:22

    这是我的脚本工具,将 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)
    

    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)
    
    小草泠泠 2024-11-13 19:19:22

    我尝试了上面的解决方案,但有时你必须自己做。
    在某些情况下可能会偏离一个像素,但一般情况下工作正常。

    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
    

    希望有帮助。

    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.

    假情假意假温柔 2024-11-13 19:19:22

    我建议使用多处理而不是常规的 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()
    

    I would suggest to use multiprocessing instead of a regular for loop as follows:

    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()
    
    陌路终见情 2024-11-13 19:19:22

    最简单的方法:

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

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

    pip install image_slicer
    

    the easiest way:

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

    this command splits the image into 16 slices and saves them in the directory that the input image is there.
    you should first install image_slicer:

    pip install image_slicer
    
    万水千山粽是情ミ 2024-11-13 19:19:22

    你可以使用 numpy stride 技巧来实现这一点,但要小心,因为这个函数必须非常小心地使用doc)

    import numpy as np
    from numpy.lib.stride_tricks import as_strided
    
    def img_pieces(img, piece_size):
        height, width, chanels = img.shape
        n_bytes = img.strides[-1]
    
        return np.reshape(
            as_strided(
                img,
                (
                    height // piece_size,
                    width // piece_size,
                    piece_size,
                    piece_size,
                    chanels
                ),
                (
                    n_bytes * chanels * width * piece_size,
                    n_bytes * chanels * piece_size,
                    n_bytes * chanels * width,
                    n_bytes * chanels,
                    n_bytes
                )
            ),
            (
                -1,
                piece_size,
                piece_size,
                chanels
            )
        )
    

    you can use numpy stride tricks to achive this, but be careful, as this function has to be used with extreme care (doc)

    import numpy as np
    from numpy.lib.stride_tricks import as_strided
    
    def img_pieces(img, piece_size):
        height, width, chanels = img.shape
        n_bytes = img.strides[-1]
    
        return np.reshape(
            as_strided(
                img,
                (
                    height // piece_size,
                    width // piece_size,
                    piece_size,
                    piece_size,
                    chanels
                ),
                (
                    n_bytes * chanels * width * piece_size,
                    n_bytes * chanels * piece_size,
                    n_bytes * chanels * width,
                    n_bytes * chanels,
                    n_bytes
                )
            ),
            (
                -1,
                piece_size,
                piece_size,
                chanels
            )
        )
    
    贵在坚持 2024-11-13 19:19:22

    这是我基于 这里,进行一些小的调整(添加频道),它可能会满足您的需求:

    import numpy as np
    
    # Seperate grayscale images to w * h tiles, add padding with zeros if image not scaled
    def to_tiles(arr: np.ndarray, tilesize: tuple[int, int]) -> np.ndarray:
        def f(x: tuple[int, int]) -> tuple[int, int]:
            tmp = list(x)
            if tmp[1] > 0:
                tmp[0] = tmp[0] + 1
            return tuple(tmp)
    
        # # Stride Implementation
        # bytelength = np.int8(np.divide(arr.nbytes, arr.size))
    
        assert arr.ndim == 2, "array must be 2d (grayscale) image"
        a_h, a_w = arr.shape
        h, w = tilesize
        assert a_h > h, "tile height is larger than arr height"
        assert a_w > w, "tile width is larger than arr width"
        row, row_r = f(np.divmod(a_h, h))
        col, col_r = f(np.divmod(a_w, w))
        
        arr = np.pad(
            arr,
            [
                (
                    np.int8(np.ceil(np.divide(h-row_r, 2))) if row_r != 0 else 0,
                    np.int8(np.floor(np.divide(h-row_r, 2))) if row_r != 0 else 0,
                ),
                (
                    np.int8(np.ceil(np.divide(w-col_r, 2))) if col_r != 0 else 0,
                    np.int8(np.floor(np.divide(w-col_r, 2))) if col_r != 0 else 0,
                ),
            ],
            "constant",
            constant_values=(0),
        )
    
        # # Stride Implementation
        # arr = np.lib.stride_tricks.as_strided(
        #     arr, shape=(row, col, h, w), strides=(h*a_w*bytelength, w*bytelength, a_w*bytelength, bytelength)
        # )
    
        arr = arr.reshape(row, h, col, w).swapaxes(1, 2)
        arr = arr.reshape(-1, h, w)
    
        return arr
    

    这是结果的示例。图片来自 FUNSD 数据集。

    Here's my attempt on a grayscale image with only numpy based on the solution from here, with some minor tweaks (adding channels) it might suit your needs:

    import numpy as np
    
    # Seperate grayscale images to w * h tiles, add padding with zeros if image not scaled
    def to_tiles(arr: np.ndarray, tilesize: tuple[int, int]) -> np.ndarray:
        def f(x: tuple[int, int]) -> tuple[int, int]:
            tmp = list(x)
            if tmp[1] > 0:
                tmp[0] = tmp[0] + 1
            return tuple(tmp)
    
        # # Stride Implementation
        # bytelength = np.int8(np.divide(arr.nbytes, arr.size))
    
        assert arr.ndim == 2, "array must be 2d (grayscale) image"
        a_h, a_w = arr.shape
        h, w = tilesize
        assert a_h > h, "tile height is larger than arr height"
        assert a_w > w, "tile width is larger than arr width"
        row, row_r = f(np.divmod(a_h, h))
        col, col_r = f(np.divmod(a_w, w))
        
        arr = np.pad(
            arr,
            [
                (
                    np.int8(np.ceil(np.divide(h-row_r, 2))) if row_r != 0 else 0,
                    np.int8(np.floor(np.divide(h-row_r, 2))) if row_r != 0 else 0,
                ),
                (
                    np.int8(np.ceil(np.divide(w-col_r, 2))) if col_r != 0 else 0,
                    np.int8(np.floor(np.divide(w-col_r, 2))) if col_r != 0 else 0,
                ),
            ],
            "constant",
            constant_values=(0),
        )
    
        # # Stride Implementation
        # arr = np.lib.stride_tricks.as_strided(
        #     arr, shape=(row, col, h, w), strides=(h*a_w*bytelength, w*bytelength, a_w*bytelength, bytelength)
        # )
    
        arr = arr.reshape(row, h, col, w).swapaxes(1, 2)
        arr = arr.reshape(-1, h, w)
    
        return arr
    

    Here's an example of the result. Image from FUNSD dataset.

    南冥有猫 2024-11-13 19:19:22
    def split(img,nbxsplit,nbysplit):
          xdemi=int(img.shape[0]/nbxsplit)
          ydemi=int(img.shape[1]/nbxsplit)
          arr=[]
          for i in range(0,img.shape[0]-xdemi+1,xdemi):
                     for j in range(0,img.shape[1]-ydemi+1,ydemi):
                              arr.append(img[i:i+xdemi][j:j+ydemi])
          a=np.reshape(a,(img.shape[0]-xdemi,img.shape[1]-xdemi))
          return a
    
    def split(img,nbxsplit,nbysplit):
          xdemi=int(img.shape[0]/nbxsplit)
          ydemi=int(img.shape[1]/nbxsplit)
          arr=[]
          for i in range(0,img.shape[0]-xdemi+1,xdemi):
                     for j in range(0,img.shape[1]-ydemi+1,ydemi):
                              arr.append(img[i:i+xdemi][j:j+ydemi])
          a=np.reshape(a,(img.shape[0]-xdemi,img.shape[1]-xdemi))
          return a
    
    烟沫凡尘 2024-11-13 19:19:22

    不确定它是否仍然相关,但我的尝试如下:
    (我假设图像是一个 numpy 数组。我没有使用 Pil 或任何东西,因为我不想有除了 numpy 之外的任何依赖项。)

    def cut_image_grid(image:np.ndarray, grid_size:int=4):
         height, width = image.shape[0], image.shape[1]
         piece_height, piece_width = height//grid_size, width//grid_size
         pieces = []
         for i in range(grid_size):
              for j in range(grid_size):
                   y = i * piece_height
                   x = j * piece_width
                   h = (i+1) * piece_height if i < grid_size else None
                   w = (j+1) * piece_width if j < grid_size else None
                   piece = image[y:h, x:w]
                   pieces.append(piece)
         return np.array(pieces)
    

    作为输入,该函数正在接收一个 numpy 图像和一个整数(您可以也变成元组,但我希望均匀分布的网格单元始终具有相同数量的行和列单元格)。
    首先,代码根据给定的grid_size计算单元格的宽度和高度。之后,代码迭代所有行和列并生成 x、y 坐标,以及用于定义单元格的 x0 和 y0(y+高度,x+宽度)。
    每个单元格都以列表的形式保存为 pieces,然后将其转换为 numpy 数组并返回。

    Not sure if it's still relevant, but my attempt is following:
    (I am assuming the image is a numpy array. I am not using Pil or anything, since i didn't want to have any dependencies other than numpy.)

    def cut_image_grid(image:np.ndarray, grid_size:int=4):
         height, width = image.shape[0], image.shape[1]
         piece_height, piece_width = height//grid_size, width//grid_size
         pieces = []
         for i in range(grid_size):
              for j in range(grid_size):
                   y = i * piece_height
                   x = j * piece_width
                   h = (i+1) * piece_height if i < grid_size else None
                   w = (j+1) * piece_width if j < grid_size else None
                   piece = image[y:h, x:w]
                   pieces.append(piece)
         return np.array(pieces)
    

    As input the function is receiving a numpy image and an integer (which you could also turn into tuples, but i wanted to have evenly spaced grid cells always with same amount of cells row and column wise).
    At first, the code calculates the width and height of the cells based on the given grid_size. After that the code iterates over all rows and columns and generates x, y Coordinates, as well as x0 and y0 (y+height, x+width) for defining the cells.
    Every cell is saved as a list into pieces, which is then transformed into a numpy array and returned.

    杀手六號 2024-11-13 19:19:22

    发布另一个解决方案很尴尬,但还没有找到答案:

    • 只是一个函数,没有保存加载
    • PIL 图像,
    • 没有硬编码的 h/w,只是网格尺寸,因为 args
    • 返回网格也不
    • 是过于复杂,也不使用额外的库
    def ungrid_image(img: PIL.Image, nx=1, ny=1, flatten=False):
        """  Without flatten=True returns row major [tile_j][tile_i]: PIL.Image """
    
        w, h = img.size
        assert w >= nx > 0 and h >= ny > 0
        cw, ch = w // nx, h // ny
    
        if w % nx != 0 or h % ny != 0:
            warn('Unexpected image size for pixel-perfect subdivision. Tiles will match cropped source image.')
    
        def box(i, j):
            # left, top, right, bottom for (i, j) tile
            return i * cw, j * ch, (i + 1) * cw, (j + 1) * ch
    
        res = [[img.crop(box(i, j)) for i in range(nx)] for j in range(ny)]
        return list(itertools.chain.from_iterable(res)) if flatten else res
    

    It's awkward to post another one solution, but have not found an answer for:

    • just a function, without save load
    • PIL image
    • no hardcoded h/w, just grid dimensions as args
    • returns also grid
    • not overcomplicated, nor uses extra libs
    def ungrid_image(img: PIL.Image, nx=1, ny=1, flatten=False):
        """  Without flatten=True returns row major [tile_j][tile_i]: PIL.Image """
    
        w, h = img.size
        assert w >= nx > 0 and h >= ny > 0
        cw, ch = w // nx, h // ny
    
        if w % nx != 0 or h % ny != 0:
            warn('Unexpected image size for pixel-perfect subdivision. Tiles will match cropped source image.')
    
        def box(i, j):
            # left, top, right, bottom for (i, j) tile
            return i * cw, j * ch, (i + 1) * cw, (j + 1) * ch
    
        res = [[img.crop(box(i, j)) for i in range(nx)] for j in range(ny)]
        return list(itertools.chain.from_iterable(res)) if flatten else res
    
    两仪 2024-11-13 19:19:22
    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/')
    
    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/')
    
    ~没有更多了~
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文