旋转图像并裁剪掉黑色边框

2023-11-25

我的应用程序:我正在尝试旋转图像(使用 OpenCV 和 Python)

Rotating Images

目前我已经开发了下面的代码,它旋转输入图像,用黑色边框填充它,给我 A。我想要的是 B - 旋转图像内最大可能的区域裁剪窗口。我将其称为轴对齐的有界框。

这本质上是相同的旋转和裁剪,但是我无法得到该问题的答案。此外,该答案显然仅适用于方形图像。我的图像是矩形的。

给A的代码:

import cv2
import numpy as np


def getTranslationMatrix2d(dx, dy):
    """
    Returns a numpy affine transformation matrix for a 2D translation of
    (dx, dy)
    """
    return np.matrix([[1, 0, dx], [0, 1, dy], [0, 0, 1]])


def rotateImage(image, angle):
    """
    Rotates the given image about it's centre
    """

    image_size = (image.shape[1], image.shape[0])
    image_center = tuple(np.array(image_size) / 2)

    rot_mat = np.vstack([cv2.getRotationMatrix2D(image_center, angle, 1.0), [0, 0, 1]])
    trans_mat = np.identity(3)

    w2 = image_size[0] * 0.5
    h2 = image_size[1] * 0.5

    rot_mat_notranslate = np.matrix(rot_mat[0:2, 0:2])

    tl = (np.array([-w2, h2]) * rot_mat_notranslate).A[0]
    tr = (np.array([w2, h2]) * rot_mat_notranslate).A[0]
    bl = (np.array([-w2, -h2]) * rot_mat_notranslate).A[0]
    br = (np.array([w2, -h2]) * rot_mat_notranslate).A[0]

    x_coords = [pt[0] for pt in [tl, tr, bl, br]]
    x_pos = [x for x in x_coords if x > 0]
    x_neg = [x for x in x_coords if x < 0]

    y_coords = [pt[1] for pt in [tl, tr, bl, br]]
    y_pos = [y for y in y_coords if y > 0]
    y_neg = [y for y in y_coords if y < 0]

    right_bound = max(x_pos)
    left_bound = min(x_neg)
    top_bound = max(y_pos)
    bot_bound = min(y_neg)

    new_w = int(abs(right_bound - left_bound))
    new_h = int(abs(top_bound - bot_bound))
    new_image_size = (new_w, new_h)

    new_midx = new_w * 0.5
    new_midy = new_h * 0.5

    dx = int(new_midx - w2)
    dy = int(new_midy - h2)

    trans_mat = getTranslationMatrix2d(dx, dy)
    affine_mat = (np.matrix(trans_mat) * np.matrix(rot_mat))[0:2, :]
    result = cv2.warpAffine(image, affine_mat, new_image_size, flags=cv2.INTER_LINEAR)

    return result

该解决方案/实现背后的数学相当于这个类似问题的解决方案,但公式被简化并避免了奇点。这是具有相同接口的Python代码largest_rotated_rect来自其他解决方案,但在几乎所有情况下都给出更大的面积(始终是经过验证的最佳方案):

def rotatedRectWithMaxArea(w, h, angle):
  """
  Given a rectangle of size wxh that has been rotated by 'angle' (in
  radians), computes the width and height of the largest possible
  axis-aligned rectangle (maximal area) within the rotated rectangle.
  """
  if w <= 0 or h <= 0:
    return 0,0

  width_is_longer = w >= h
  side_long, side_short = (w,h) if width_is_longer else (h,w)

  # since the solutions for angle, -angle and 180-angle are all the same,
  # if suffices to look at the first quadrant and the absolute values of sin,cos:
  sin_a, cos_a = abs(math.sin(angle)), abs(math.cos(angle))
  if side_short <= 2.*sin_a*cos_a*side_long or abs(sin_a-cos_a) < 1e-10:
    # half constrained case: two crop corners touch the longer side,
    #   the other two corners are on the mid-line parallel to the longer line
    x = 0.5*side_short
    wr,hr = (x/sin_a,x/cos_a) if width_is_longer else (x/cos_a,x/sin_a)
  else:
    # fully constrained case: crop touches all 4 sides
    cos_2a = cos_a*cos_a - sin_a*sin_a
    wr,hr = (w*cos_a - h*sin_a)/cos_2a, (h*cos_a - w*sin_a)/cos_2a

  return wr,hr

以下是该函数与其他解决方案的比较:

>>> wl,hl = largest_rotated_rect(1500,500,math.radians(20))
>>> print (wl,hl),', area=',wl*hl
(828.2888697391496, 230.61639227890998) , area= 191016.990904
>>> wm,hm = rotatedRectWithMaxArea(1500,500,math.radians(20))
>>> print (wm,hm),', area=',wm*hm
(730.9511000407718, 266.044443118978) , area= 194465.478358

带角度angle in [0,pi/2[旋转图像的边界框(宽度w, 高度h) 具有以下尺寸:

  • width w_bb = w*cos_a + h*sin_a
  • height h_bb = w*sin_a + h*cos_a

If w_r, h_r是计算出的裁剪图像的最佳宽度和高度,则边界框的插图为:

  • 水平方向:(w_bb-w_r)/2
  • 垂直方向:(h_bb-h_r)/2

Proof:

Looking for the axis aligned rectangle between two parallel lines that has maximal area is an optimization problem with one parameter, e.g. x as in this figure: animated parameter

Let s表示两条平行线之间的距离(它将成为旋转矩形的短边)。然后是侧面a, b所追求的矩形的比例与x, s-x,即 x = a sin α 和 (s-x) = b cos α:

enter image description here

所以面积最大化a*b意味着最大化x*(s-x)。由于直角三角形的“高度定理”,我们知道x*(s-x) = p*q = h*h。因此最大面积达到x = s-x = s/2,即平行线之间的两个角E、G在中线上:

enter image description here

仅当最大矩形适合旋转矩形时,此解决方案才有效。因此对角线EG不得长于另一边l旋转后的矩形。自从

EG = AF + DH = s/2*(cot α + tan α) = s/(2sin αcos α) = s/sin 2*α

我们有条件 s ≤ lsin 2α,其中 s 和 l 是旋转矩形的短边和长边。

如果 s > lsin 2α 参数x必须小于(小于 s/2)并且 st。所需矩形的所有角都位于旋转矩形的一侧。这导致方程

x*cot α + (s-x)*tan α = l

给出 x = sin α*(l余弦α-ssin α)/cos 2*α。从 a = x/sin α 和 b = (s-x)/cos α 我们得到上面使用的公式。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

旋转图像并裁剪掉黑色边框 的相关文章

随机推荐

  • 如何使用 Webpack 和 Angular2 包含外部 css 文件?

    我正在尝试使用 Webpack 添加对 Angular2 中 CSS 文件的外部引用 我的CSS定义为 test css loader style loader css loader 在我的 webpack config js 文件中 在打
  • Ruby:将转义字符串写入 YAML

    下列 require yaml test I m a b d string File open test yaml w do out out write test to yaml end 输出 this is a b d string 我怎
  • Lucene.Net 写/读同步

    我可以写 与IndexWriter 在打开阅读时将新文档放入索引 使用IndexReader 或者我必须在写作之前关闭阅读 我可以阅读 搜索文档吗 使用IndexReader 在索引中 当它打开用于写入时 与IndexWriter 或者我必
  • 扭曲应用程序的 Web 界面

    我有一个用 Twisted 编写的应用程序 我想添加一个 Web 界面来控制和监视它 我需要大量的动态页面来显示当前状态和配置 因此我希望有一个框架至少提供一种具有继承和一些基本路由的模板语言 因为我正在使用 Twisted 无论如何我想使
  • Firebase 更改显示在谷歌登录警报上的应用程序名称?

    我有一个 firebase 项目 但不知何故我输错了应用程序名称 有没有办法更改谷歌登录警报上显示的应用程序名称 您应该更改项目中的产品名称
  • 如何在 ASP.NET Core 中的自定义 TagHelper 中渲染 Razor 模板?

    我正在创建一个自定义 HTML 标记帮助程序 public class CustomTagHelper TagHelper HtmlAttributeName asp for public ModelExpression DataModel
  • 如何删除名称以“-”开头的文件[重复]

    这个问题在这里已经有答案了 在脚本中出现错误后 我最终得到了一个名称以破折号开头的文件 myfile txt 到目前为止我尝试过 rm myfile txt rm illegal option m usage rm f i dPRrvW f
  • 使用 this-> 访问成员是否有任何开销?

    当访问某个类的成员时 我可以使用例如 this gt myVar 10 或者我可以写 myVar 10 我喜欢用this gt 因为它显式声明该变量是此类的成员 但是与仅使用变量名本身相比 它是否会导致任何开销 作为替代方案 我可以向变量添
  • 从 data.frame 或 data.table 构建方形邻接矩阵

    我正在尝试建立一个方形邻接matrix from a data table 这是我已经拥有的可重现的示例 require data table require plyr require reshape2 Build a mock data
  • Locale.ITALY 和 Locale.ITALIAN 有什么不同

    和有什么区别Locale国家和语言 例如Locale ITALY and Locale ITALIAN 我在哪里可以找到其他语言环境的所有这些差异 我应该什么时候使用每一个 是否可以开发我们所需的语言环境 如何 Locale ITALIAN
  • Laravel 4 Illuminate \ Database \ Eloquent \ MassAssignmentException 错误

    嘿 我已经在那里搜索了很多答案 但无法解决这个问题 这是我的迁移代码
  • 在 ASP.NET c# 中查找日期 10 月的最后一个星期日

    嗨 有没有办法找出 ASP NET C 中十月最后一个星期日的日期 我正在使用 net 2 0 不需要为此运行循环 private static DateTime GetLastWeekdayOfMonth DateTime date Da
  • 在 .NET 中创建和部署 ActiveX 控件

    由于显然没有可以接受位图粘贴的 Flash 控件 我想考虑自己写一个 但我不想使用 Flash 所以我考虑使用 NET 现在我相信可以在浏览器中下载并运行的本机代码控件的正确术语是 ActiveX 控件 所以我的问题是 我可以用 NET 创
  • FBSDK 空登录视图

    自从升级到最新的 Xcode 后 我在尝试通过 FBSDK 登录时遇到了一些问题FBSDKLoginManager Safari 中的登录窗口会弹出 但它保持白色 空视图 没有导航项或内容 控制台返回以下内容 ViewService 无法获
  • 将分组 zscore 列添加到 pandas 数据框中

    我可以将一列插入到数据框中 对另一列进行 z 评分 如下所示 1 df insert
  • Rails:从控制器调用另一个控制器操作

    我需要从控制器 B 调用控制器 A 中的创建操作 原因是当我从控制器 B 调用时 我需要以不同的方式重定向 可以在 Rails 中完成吗 要使用另一个控制器 请执行以下操作 def action that calls one from an
  • 点击按钮时忽略 UIGestureRecognizer

    我设置了一个手势识别器 以便在点击屏幕时我的工具栏会向下滑动 当我点击栏上的按钮时 即算作一次点击 在这些情况下如何取消手势 Thanks 您可以查看 Simple GestureRecognizers 示例项目 http develope
  • 两个字符串之间的所有公共子字符串

    我正在使用 C 来查找两个字符串之间的所有公共子字符串 例如 如果输入是 S1 需要电子邮件方面的帮助 S2 需要电子邮件帮助 输出应该是 需要帮助电子邮件 下面的代码返回最长的公共子字符串 但我希望我的代码返回所有公共子字符串 任何帮助深
  • 像 Whatsapp 一样快速视频压缩

    我需要加快 Android 应用程序中的视频压缩速度 我使用的是 FFMPEG 压缩 80MB 视频需要 3 分钟 有谁知道更好的解决方案 我正在使用的命令是 data data com moymer app bin ffmpeg y i
  • 旋转图像并裁剪掉黑色边框

    我的应用程序 我正在尝试旋转图像 使用 OpenCV 和 Python 目前我已经开发了下面的代码 它旋转输入图像 用黑色边框填充它 给我 A 我想要的是 B 旋转图像内最大可能的区域裁剪窗口 我将其称为轴对齐的有界框 这本质上是相同的旋转