python利用四个坐标点对图片目标区域最小外接矩形进行裁剪

在图像裁剪操作中,opencv和pillow两个库都具有相应的函数,但是这两个库中的函数仅仅能对与图片平行的矩形进行裁剪操作,如果想要对目标的最小外接矩形进行裁剪该如何操作呢?如下所示:

具体处理该问题的思路如下:

  1. 计算最小外接矩形的四个点的坐标,旋转角度
  2. 将原图像进行旋转,旋转角度为最小外接矩形的角度
  3. 将四个点的坐标进行映射,求出被旋转后图像的四个点的坐标
  4. 利用这四个点对图像进行裁剪

图像原图如下:

1 求出该区域的最小外接矩形,并且得到外接矩形的四个点的坐标和旋转角度。

rect = cv2.minAreaRect(self.contours[0])#rect为[(旋转中心x坐标,旋转中心y坐标),(矩形长,矩形宽),旋转角度]
box_origin = cv2.boxPoints(rect)#box_origin为[(x0,y0),(x1,y1),(x2,y2),(x3,y3)]

2 将原图像进行旋转,旋转角度为最小外接矩形的角度,由于防止旋转后目标区域在图像外面,所以我将图像大小扩大为原来的2倍。

M = cv2.getRotationMatrix2D(rect[0],rect[2],1)
dst = cv2.warpAffine(rotateimg,M,(2*rotateimg.shape[0],2*rotateimg.shape[1]))

3 将原四个点的坐标做映射,映射到旋转后的区域,得到新的四个点的坐标。

#逆时针旋转
def Nrotate(angle,valuex,valuey,pointx,pointy):
      angle = (angle/180)*math.pi
      valuex = np.array(valuex)
      valuey = np.array(valuey)
      nRotatex = (valuex-pointx)*math.cos(angle) - (valuey-pointy)*math.sin(angle) + pointx
      nRotatey = (valuex-pointx)*math.sin(angle) + (valuey-pointy)*math.cos(angle) + pointy
      return (nRotatex, nRotatey)
#顺时针旋转
def Srotate(angle,valuex,valuey,pointx,pointy):
      angle = (angle/180)*math.pi
      valuex = np.array(valuex)
      valuey = np.array(valuey)
      sRotatex = (valuex-pointx)*math.cos(angle) + (valuey-pointy)*math.sin(angle) + pointx
      sRotatey = (valuey-pointy)*math.cos(angle) - (valuex-pointx)*math.sin(angle) + pointy
      return (sRotatex,sRotatey)
#将四个点做映射
def rotatecordiate(angle,rectboxs,pointx,pointy):
      output = []
      for rectbox in rectboxs:
        if angle>0:
          output.append(Srotate(angle,rectbox[0],rectbox[1],pointx,pointy))
        else:
          output.append(Nrotate(-angle,rectbox[0],rectbox[1],pointx,pointy))
      return output
box = rotatecordiate(rect[2],box_origin,rect[0][0],rect[0][1])

4 利用四个点坐标进行裁剪,如2中图所示,图像经过旋转后已经变为和图片没有旋转角的图像,经过实验观察旋转后的坐标还是有细微差别,但误差已经在千分点甚至万分点左右,对我们裁剪造成的影响可以忽略不计。

def imagecrop(image,box):
      xs = [x[1] for x in box]
      ys = [x[0] for x in box]
      print(xs)
      print(min(xs),max(xs),min(ys),max(ys))
      cropimage = image[min(xs):max(xs),min(ys):max(ys)]
      print(cropimage.shape)
      cv2.imwrite('cropimage.png',cropimage)
      return cropimage
imagecrop(dst,np.int0(box))

到此,利用4个坐标点对图像进行裁剪操作已经完成。

  • 24
    点赞
  • 117
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
可以通过旋转卡壳算法来找到最小矩形,具体步骤如下: 1. 找到四个点中最左边的点,记作P0。 2. 以P0为起点,找到在其左侧的点中最远的点,记作P1。 3. 以P1为起点,找到在其左侧的点中最远的点,记作P2。 4. 以P2为起点,找到在其左侧的点中最远的点,记作P3。 5. 对于每个点Pi,计算其与P(i-1)和P(i+1)的夹角θi。 6. 对于每个点Pi,计算其到P(i-1)和P(i+1)所确定的直线的距离di。 7. 对于每个点Pi,计算其到直线P(i-1)P(i+1)的距离ei。 8. 对于每个点Pi,计算其在θi+π/2方向上的投影长度fi。 9. 对于每个点Pi,计算其到以P(i-1)P(i+1)为对角线的矩形的最小距离gi。 10. 对于每个点Pi,计算其到以P(i-1)P(i+1)为对角线的矩形的最大距离hi。 11. 对于每个点Pi,计算其到以P(i-1)P(i+1)为对角线的矩形的面积Ai。 12. 对所有点的Ai求和,得到最小矩形的面积。 13. 最小矩形的长和宽可以通过P0P2和P1P3的长度计算得到。 示例代码: import math def distance(p1, p2): return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) def cross(p1, p2, p3): return (p2[0] - p1[0]) * (p3[1] - p1[1]) - (p3[0] - p1[0]) * (p2[1] - p1[1]) def rotate_calipers(points): n = len(points) P = [i for i in range(n)] hull = [] hull.append(min(P, key=lambda p: points[p][0])) i, j = hull[0], (hull[0] + 1) % n while True: hull.append(j) for k in range(n): if cross(points[i], points[j], points[k]) < 0: j = k if j == hull[0]: break i = hull[-1] m = len(hull) p = 0 q = 1 if m > 1 else 0 r = 1 if m > 2 else 0 min_area = float('inf') while True: while True: while distance(points[hull[p]], points[hull[q]]) <= distance(points[hull[q]], points[hull[(r + 1) % m]]): r = (r + 1) % m if distance(points[hull[p]], points[hull[q]]) <= distance(points[hull[q]], points[hull[r]]): break if p == 0 and q == 1 and r == m - 1: break q = (q + 1) % m d = distance(points[hull[p]], points[hull[q]]) e = distance(points[hull[q]], points[hull[r]]) f = math.sqrt(d ** 2 + e ** 2) area = d * e if area < min_area: min_area = area len1 = d len2 = e angle = math.atan2(points[hull[q]][1] - points[hull[p]][1], points[hull[q]][0] - points[hull[p]][0]) rect_center = ((points[hull[p]][0] + points[hull[q]][0] + points[hull[r]][0]) / 3, (points[hull[p]][1] + points[hull[q]][1] + points[hull[r]][1]) / 3) p = (p + 1) % m if p == q: q = (q + 1) % m if q == r: r = (r + 1) % m if p == 0: break return rect_center, len1, len2, angle points = [(0, 0), (2, 0), (2, 2), (0, 2)] rect_center, len1, len2, angle = rotate_calipers(points) print(rect_center, len1, len2, angle)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值