如何用Python从1024x720图像中按指定角度提取224x224中心图像块?
嘿,这个需求我之前处理过不少,核心就是要把旋转变换和中心定位结合起来,不然很容易出现提取的块位置不对或者边缘黑边的问题。下面给你详细的解决方案,用OpenCV来实现(处理图像变换效率很高),还有一些避坑要点:
核心思路
- 以指定的中心像素为旋转轴,对原图像进行旋转,同时调整画布尺寸保证旋转后的图像完整显示
- 找到旋转后原中心像素在新图像中的位置(其实就是新画布的中心)
- 以这个新中心为基准,提取224x224的图像块,同时处理边界超出的情况
完整代码实现(OpenCV版本)
import cv2 import math def extract_rotated_patch(img, center_x, center_y, patch_size=224, angle=0): # 获取原图像尺寸:h是高度(行),w是宽度(列) h, w = img.shape[:2] patch_half = patch_size // 2 # 计算旋转后需要的画布尺寸,避免图像被截断 rad = math.radians(angle) cos_rad = abs(math.cos(rad)) sin_rad = abs(math.sin(rad)) new_w = int(h * sin_rad + w * cos_rad) new_h = int(h * cos_rad + w * sin_rad) # 生成旋转矩阵:围绕指定中心旋转,缩放因子为1(不改变大小) rot_matrix = cv2.getRotationMatrix2D((center_x, center_y), angle, 1.0) # 调整平移参数,让旋转后的图像居中显示在新画布上 rot_matrix[0, 2] += (new_w - w) / 2 rot_matrix[1, 2] += (new_h - h) / 2 # 执行旋转操作,用边缘复制填充避免黑边(比补0更自然) rotated_img = cv2.warpAffine( img, rot_matrix, (new_w, new_h), borderMode=cv2.BORDER_REPLICATE ) # 旋转后原中心的位置就是新画布的中心 new_center_x = new_w // 2 new_center_y = new_h // 2 # 计算提取块的边界,确保不超出图像范围 x_start = max(0, new_center_x - patch_half) y_start = max(0, new_center_y - patch_half) x_end = min(new_w, new_center_x + patch_half) y_end = min(new_h, new_center_y + patch_half) # 提取图像块 patch = rotated_img[y_start:y_end, x_start:x_end] # 如果提取的块尺寸不足224x224,自动补0(可选,根据你的需求调整填充方式) if patch.shape[0] != patch_size or patch.shape[1] != patch_size: patch = cv2.copyMakeBorder( patch, top=patch_half - (new_center_y - y_start), bottom=patch_half - (y_end - new_center_y), left=patch_half - (new_center_x - x_start), right=patch_half - (x_end - new_center_x), borderType=cv2.BORDER_CONSTANT, value=[0, 0, 0] ) return patch # ---------------------- 使用示例 ---------------------- if __name__ == "__main__": # 读取你的图像 img = cv2.imread("your_image.jpg") # 已知的中心像素坐标(注意:x是宽方向,y是高方向,对应图像的列和行) center_x = 512 # 示例值,替换成你的实际坐标 center_y = 360 # 示例值,替换成你的实际坐标 # 提取旋转45度的224x224块 rotated_patch = extract_rotated_patch(img, center_x, center_y, patch_size=224, angle=45) # 保存结果 cv2.imwrite("rotated_patch.jpg", rotated_patch) # 显示结果 cv2.imshow("Rotated Patch", rotated_patch) cv2.waitKey(0) cv2.destroyAllWindows()
关键细节说明
- 旋转画布尺寸计算:直接用原图像尺寸旋转会导致边缘被截断,所以我们通过三角函数计算出能完整容纳旋转后图像的最小画布尺寸
- 边缘填充模式:
cv2.BORDER_REPLICATE会复制图像边缘的像素,避免出现生硬的黑边;如果不需要可以换成cv2.BORDER_CONSTANT补固定颜色 - 边界检查:如果原中心靠近原图像边缘,旋转后提取块可能超出新画布范围,用
max/min确保不越界,再补0保证尺寸符合要求
可选:用PIL实现的版本(更简洁)
如果你习惯用PIL库,也可以这样实现:
from PIL import Image import math def extract_rotated_patch_pil(img_path, center_x, center_y, patch_size=224, angle=0): img = Image.open(img_path) patch_half = patch_size // 2 # 计算旋转后的画布尺寸 rad = math.radians(angle) cos_rad = abs(math.cos(rad)) sin_rad = abs(math.sin(rad)) new_w = int(img.height * sin_rad + img.width * cos_rad) new_h = int(img.height * cos_rad + img.width * sin_rad) # 旋转图像,expand=True自动调整画布尺寸 rotated_img = img.rotate( angle, center=(center_x, center_y), expand=True, fillcolor=None # 用边缘像素填充,和OpenCV的BORDER_REPLICATE类似 ) # 计算旋转后原中心的位置(原图像在新画布中的偏移量) offset_x = (new_w - img.width) // 2 offset_y = (new_h - img.height) // 2 new_center_x = center_x + offset_x new_center_y = center_y + offset_y # 裁剪区域 x1, y1 = new_center_x - patch_half, new_center_y - patch_half x2, y2 = new_center_x + patch_half, new_center_y + patch_half # 提取图像块 patch = rotated_img.crop((x1, y1, x2, y2)) # 补全尺寸(如果需要) if patch.size != (patch_size, patch_size): new_patch = Image.new(img.mode, (patch_size, patch_size), (0, 0, 0)) paste_x = max(0, -x1) paste_y = max(0, -y1) new_patch.paste(patch, (paste_x, paste_y)) patch = new_patch return patch # 使用示例 patch = extract_rotated_patch_pil("your_image.jpg", 512, 360, 224, 45) patch.save("rotated_patch_pil.jpg") patch.show()
内容的提问来源于stack exchange,提问作者Rozen Moon
相关产品推荐
相关产品推荐

