【问题标题】:How to divide an image into evenly sized, overlapping if needed, tiles?如何将图像划分为大小均匀、需要时重叠的图块?
【发布时间】:2019-10-15 17:47:55
【问题描述】:

我有一个高度/宽度尺寸分别为 3837x5126 像素的图像。

我的目标是将此图像划分为精确尺寸为 1000x1000 像素的单独图块。如果图像不能被平均分割,则应创建重叠图块,使图块全部为 1000x1000 像素。

这是在 PowerPoint 中创建的 ROUGH 可视化,用于说明我正在尝试做的事情:

我的想法是这样的:

  1. 将图像除以我的理想尺寸 (1000)
  2. 注意其余部分
  3. 通过将余数除以所有除法来创建偏移量
  4. 对于每个除法,减去偏移量
  5. 使用 1000x1000 尺寸创建重叠图块。

这是我目前的代码。

import numpy as np

def get_divisions(num, divisor):
    n = num / divisor
    divisions = math.floor(n)
    remainder = n % 1
    offset = remainder / divisions

    return divisions, offset


def get_tiles(height, width, window_size=1000):
    number_of_rows, row_offset   = get_divisions(height, window_size)
    number_of_columns, col_offet = get_divisions(width, window_size)


    tiles = []
    for row in range(number_of_rows):
        for col in range(number_of_columns):
            if row == 0:
                row_offset = 0

            if col == 0:
                col_offset = 0

            x = (col * window_size) - (col_offset * window_size)
            y = (row * window_size) - (row_offset * window_size)
            w = window_size
            h = window_size

            tiles.append([x, y, h, w])
    return np.array(tiles, dtype="uint32")
height = 3837
width = 5126
get_tiles(height, width)

输出:

array([[   0,    0, 1000, 1000],
       [1000,    0, 1000, 1000],
       [2000,    0, 1000, 1000],
       [3000,    0, 1000, 1000],
       [4000,    0, 1000, 1000],
       [   0, 1000, 1000, 1000],
       [1000, 1000, 1000, 1000],
       [2000, 1000, 1000, 1000],
       [3000, 1000, 1000, 1000],
       [4000, 1000, 1000, 1000],
       [   0, 2000, 1000, 1000],
       [1000, 2000, 1000, 1000],
       [2000, 2000, 1000, 1000],
       [3000, 2000, 1000, 1000],
       [4000, 2000, 1000, 1000]], dtype=uint32)

如您所见,我的[x, y, h, w] 矩阵不正确,因为有一些 x,y 点位于 3837x5126 像素图像之外。

任何帮助将不胜感激!


编辑:

这里是answer @HansHirse gave,并添加了一些测试。

import unittest
import math
import numpy as np

def tile(h, w, tile_width=None, tile_height=None, window_size=100):
    np.seterr(divide='ignore', invalid='ignore')

    if not tile_width:
        tile_width = window_size

    if not tile_height:
        tile_height = window_size

    wTile = tile_width
    hTile = tile_height

    if tile_width > w or tile_height > h:
        raise ValueError("tile dimensions cannot be larger than origin dimensions")

    # Number of tiles
    nTilesX = np.uint8(np.ceil(w / wTile))
    nTilesY = np.uint8(np.ceil(h / hTile))

    # Total remainders
    remainderX = nTilesX * wTile - w
    remainderY = nTilesY * hTile - h

    # Set up remainders per tile
    remaindersX = np.ones((nTilesX-1, 1)) * np.uint16(np.floor(remainderX / (nTilesX-1)))
    remaindersY = np.ones((nTilesY-1, 1)) * np.uint16(np.floor(remainderY / (nTilesY-1)))
    remaindersX[0:np.remainder(remainderX, np.uint16(nTilesX-1))] += 1
    remaindersY[0:np.remainder(remainderY, np.uint16(nTilesY-1))] += 1

    # Initialize array of tile boxes
    tiles = np.zeros((nTilesX * nTilesY, 4), np.uint16)

    k = 0
    x = 0
    for i in range(nTilesX):
        y = 0
        for j in range(nTilesY):
            tiles[k, :] = (x, y, hTile, wTile)
            k += 1
            if j < (nTilesY-1):
                y = y + hTile - remaindersY[j]
        if i < (nTilesX-1):
            x = x + wTile - remaindersX[i]

    return tiles

class TestTilingWithoutRemainders(unittest.TestCase):
    def test_it_returns_tiles_without_overflow(self):
        self.assertEqual(tile(500, 500, window_size=500).tolist(), np.array([[0, 0, 500, 500]], dtype="uint16").tolist())
        self.assertEqual(tile(250, 500, window_size=250).tolist(), np.array(
            [[0, 0, 250, 250], [250, 0, 250, 250]], dtype="uint16"
            ).tolist())

        self.assertEqual(tile(500, 250, window_size=250).tolist(), np.array(
            [[0, 0, 250, 250], [0, 250, 250, 250]], dtype="uint16"
            ).tolist())

class TestTilingWithRemainders(unittest.TestCase):
    def test_it_returns_tiles_with_overflow(self):
        self.assertEqual(tile(500, 501, window_size=500).tolist(), np.array(
            [[0, 0, 500, 500], [1, 0, 500, 500]], dtype="uint16"
            ).tolist())


        self.assertEqual(tile(251, 250, window_size=250).tolist(), np.array(
            [[0, 0, 250, 250], [0, 1, 250, 250]], dtype="uint16"
            ).tolist())


class TestTilingWithInvalidWindowSizes(unittest.TestCase):
    def test_it_raises_an_error_with_invalid_tile_height(self):
        self.assertRaises(ValueError, tile, 500, 500, tile_height=50000000)

    def test_it_raises_an_error_with_invalid_tile_width(self):
        self.assertRaises(ValueError, tile, 500, 500, tile_width=50000000)

    def test_it_raises_an_error_with_invalid_window_size(self):
        self.assertRaises(ValueError, tile, 500, 500, window_size=50000000)


【问题讨论】:

  • 不确定你的可视化有多粗糙,但我认为你不能用 5 个边 1,000 的正方形覆盖 5,126 像素 - 你需要 6 个。同样,你不能用 3 个正方形覆盖 3,837 像素的高度边 1,000 - 你需要 4。
  • @MarkSetchell 它非常粗糙。在 PowerPoint 中创建。我现在编辑了我的问题以强调这一点。很抱歉造成混乱。

标签: python opencv image-processing


【解决方案1】:

我的解决方案来了。不幸的是,我的代码不是基于你的,但我希望你仍然可以遵循它。主要方法与您在问题中描述的完全相同。

我们看一下代码:

import cv2
import numpy as np

# Some image; get width and height
image = cv2.resize(cv2.imread('path/to/your/image.png'), (512, 383))
h, w = image.shape[:2]

# Tile parameters
wTile = 100
hTile = 100

# Number of tiles
nTilesX = np.uint8(np.ceil(w / wTile))
nTilesY = np.uint8(np.ceil(h / hTile))

# Total remainders
remainderX = nTilesX * wTile - w
remainderY = nTilesY * hTile - h

# Set up remainders per tile
remaindersX = np.ones((nTilesX-1, 1)) * np.uint16(np.floor(remainderX / (nTilesX-1)))
remaindersY = np.ones((nTilesY-1, 1)) * np.uint16(np.floor(remainderY / (nTilesY-1)))
remaindersX[0:np.remainder(remainderX, np.uint16(nTilesX-1))] += 1
remaindersY[0:np.remainder(remainderY, np.uint16(nTilesY-1))] += 1

# Initialize array of tile boxes
tiles = np.zeros((nTilesX * nTilesY, 4), np.uint16)

# Determine proper tile boxes
k = 0
x = 0
for i in range(nTilesX):
    y = 0
    for j in range(nTilesY):
        tiles[k, :] = (x, y, hTile, wTile)
        k += 1
        if (j < (nTilesY-1)):
            y = y + hTile - remaindersY[j]
    if (i < (nTilesX-1)):
        x = x + wTile - remaindersX[i]

print(tiles)

对于尺寸为[512, 383] 和平铺大小[100, 100] 的图像,tiles 数组如下所示:

[[  0   0 100 100]
 [  0  94 100 100]
 [  0 188 100 100]
 [  0 283 100 100]
 [ 82   0 100 100]
 [ 82  94 100 100]
 [ 82 188 100 100]
 [ 82 283 100 100]
 [164   0 100 100]
 [164  94 100 100]
 [164 188 100 100]
 [164 283 100 100]
 [246   0 100 100]
 [246  94 100 100]
 [246 188 100 100]
 [246 283 100 100]
 [329   0 100 100]
 [329  94 100 100]
 [329 188 100 100]
 [329 283 100 100]
 [412   0 100 100]
 [412  94 100 100]
 [412 188 100 100]
 [412 283 100 100]]

您将获得 24 个图块,每个图块的大小为 [100, 100],并且可能有最相似的重叠。

如果我们切换到一些尺寸为[500, 300] 的图像,我们会得到这些tiles

[[  0   0 100 100]
 [  0 100 100 100]
 [  0 200 100 100]
 [100   0 100 100]
 [100 100 100 100]
 [100 200 100 100]
 [200   0 100 100]
 [200 100 100 100]
 [200 200 100 100]
 [300   0 100 100]
 [300 100 100 100]
 [300 200 100 100]
 [400   0 100 100]
 [400 100 100 100]
 [400 200 100 100]]

如您所见,只剩下 15 个图块,而且完全没有重叠。

希望有帮助!

【讨论】:

  • 轰隆隆!我已经对此进行了测试,它完全有效。如果有人感兴趣,我会将测试放在问题的底部。谢谢@HansHirse! +1
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-16
  • 1970-01-01
  • 1970-01-01
  • 2014-01-03
  • 1970-01-01
相关资源
最近更新 更多