You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:Python实现PIL图像按M行N列分割并确保指定补丁完全包含在单个区域的通用方案

求助:Python实现PIL图像按M行N列分割并确保指定补丁完全包含在单个区域的通用方案

我来帮你搞定这个问题!你之前的代码只按M、N均匀分割图像,完全没考虑补丁的位置,难怪会出现补丁跨区域的情况。核心思路其实很简单:我们需要调整行和列的分割线,让补丁的整个矩形范围完全落在某一个分割块里,分割线绝对不能穿过补丁区域。

下面我给你一套通用的解决方案,用PIL实现,不管M、N取什么值都能正常工作:

实现思路

  1. 先明确补丁的边界:补丁的顶部是p,底部是p+H;左边是q,右边是q+W。我们要确保这两个垂直/水平区间都完全落在单个分割块中。
  2. 行分割调整:把图像高度分成三部分——补丁上方的区域、补丁区域、补丁下方的区域。上方区域均匀分给k个块(k是均匀分割时补丁所在的行块索引),下方区域均匀分给剩下的M-k-1个块,中间的块就是完整包含补丁的区域。
  3. 列分割调整:和行分割逻辑完全一致,处理水平方向的分割线。
  4. 最后根据调整后的分割点,用PIL的crop方法裁剪出所有区域。

完整代码实现

from PIL import Image

def split_image_with_patch(img, M, N, p, q, H, W):
    # 获取图像的宽和高(注意PIL的size是(宽, 高))
    img_width, img_height = img.size
    patch_y_start = p
    patch_y_end = p + H
    patch_x_start = q
    patch_x_end = q + W

    # 先做边界检查,避免补丁超出图像范围
    if (patch_y_end > img_height or patch_x_end > img_width or
        patch_y_start < 0 or patch_x_start < 0):
        raise ValueError("补丁的位置或尺寸超出了图像的范围,请检查参数!")

    # -------------------------- 处理行分割(垂直方向) --------------------------
    row_splits = [0]
    # 计算均匀分割时,补丁所在的行块索引(从0开始)
    target_row_block = (patch_y_start * M) // img_height

    # 分配补丁上方的k个块(k=target_row_block)
    if target_row_block > 0:
        upper_total_height = patch_y_start
        upper_block_height = upper_total_height // target_row_block
        # 处理无法整除的情况,把剩余像素均匀分配到前面的块
        upper_remaining = upper_total_height % target_row_block

        current_y = 0
        for i in range(target_row_block):
            add_height = upper_block_height
            if i < upper_remaining:
                add_height += 1
            current_y += add_height
            row_splits.append(current_y)

    # 添加补丁的底部位置,作为中间块的结束
    row_splits.append(patch_y_end)

    # 分配补丁下方的剩余块
    lower_block_count = M - target_row_block - 1
    if lower_block_count > 0:
        lower_total_height = img_height - patch_y_end
        lower_block_height = lower_total_height // lower_block_count
        lower_remaining = lower_total_height % lower_block_count

        current_y = patch_y_end
        for i in range(lower_block_count):
            add_height = lower_block_height
            if i < lower_remaining:
                add_height += 1
            current_y += add_height
            row_splits.append(current_y)

    # 确保最后一个分割点刚好是图像高度(避免计算误差)
    if row_splits[-1] != img_height:
        row_splits[-1] = img_height

    # -------------------------- 处理列分割(水平方向) --------------------------
    col_splits = [0]
    # 计算均匀分割时,补丁所在的列块索引(从0开始)
    target_col_block = (patch_x_start * N) // img_width

    # 分配补丁左边的l个块(l=target_col_block)
    if target_col_block > 0:
        left_total_width = patch_x_start
        left_block_width = left_total_width // target_col_block
        left_remaining = left_total_width % target_col_block

        current_x = 0
        for i in range(target_col_block):
            add_width = left_block_width
            if i < left_remaining:
                add_width += 1
            current_x += add_width
            col_splits.append(current_x)

    # 添加补丁的右边界位置,作为中间块的结束
    col_splits.append(patch_x_end)

    # 分配补丁右边的剩余块
    right_block_count = N - target_col_block - 1
    if right_block_count > 0:
        right_total_width = img_width - patch_x_end
        right_block_width = right_total_width // right_block_count
        right_remaining = right_total_width % right_block_count

        current_x = patch_x_end
        for i in range(right_block_count):
            add_width = right_block_width
            if i < right_remaining:
                add_width += 1
            current_x += add_width
            col_splits.append(current_x)

    # 确保最后一个分割点刚好是图像宽度
    if col_splits[-1] != img_width:
        col_splits[-1] = img_width

    # -------------------------- 分割图像并返回结果 --------------------------
    split_regions = []
    for i in range(M):
        row_top = row_splits[i]
        row_bottom = row_splits[i+1]
        for j in range(N):
            col_left = col_splits[j]
            col_right = col_splits[j+1]
            # PIL的crop参数是(left, upper, right, lower),对应x左、y上、x右、y下
            region = img.crop((col_left, row_top, col_right, row_bottom))
            split_regions.append(region)

    # 返回分割后的区域列表,以及行/列分割点(方便你验证结果)
    return split_regions, row_splits, col_splits

代码使用示例

# 加载测试图像
test_img = Image.open("test_image.jpg")
# 按3行1列分割,确保从(120, 0)开始、80x400的补丁完全在一个区域里
regions, row_splits, col_splits = split_image_with_patch(test_img, M=3, N=1, p=120, q=0, H=80, W=400)

# 可以把分割后的区域保存下来查看
for idx, region in enumerate(regions):
    region.save(f"split_region_{idx}.jpg")

# 打印分割点,验证补丁是否在单个块里
print("行分割点:", row_splits)
print("列分割点:", col_splits)

关键说明

  • 这个代码会自动根据补丁位置调整分割线,比如你提到的M=3、N=1的情况,只要补丁参数合法,就能保证补丁完全落在某一个行块里。
  • 代码里做了边界检查,避免补丁超出图像范围导致的错误。
  • 分割时会处理无法均匀分配像素的情况,把剩余的1个像素均匀分配到前面的块里,保证分割后的区域尺寸尽可能均匀。

备注:内容来源于stack exchange,提问作者Mohit Lamba

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.16 11:08:09