【问题标题】:Implement a script in a function. Some suggestions?在函数中实现脚本。一些建议?
【发布时间】:2012-11-25 18:10:36
【问题描述】:

首先,我对 Python(编程领域)还很陌生,但我想学习和转换 jwpat7 开发的函数。给定一组从凸包导出的点

hull= [(560023.44957588764,6362057.3904932579), 
      (560023.44957588764,6362060.3904932579), 
      (560024.44957588764,6362063.3904932579), 
      (560026.94957588764,6362068.3904932579), 
      (560028.44957588764,6362069.8904932579), 
      (560034.94957588764,6362071.8904932579), 
      (560036.44957588764,6362071.8904932579), 
      (560037.44957588764,6362070.3904932579), 
      (560037.44957588764,6362064.8904932579), 
      (560036.44957588764,6362063.3904932579), 
      (560034.94957588764,6362061.3904932579), 
      (560026.94957588764,6362057.8904932579), 
      (560025.44957588764,6362057.3904932579), 
      (560023.44957588764,6362057.3904932579)]

此脚本返回此post problem 之后所有可能区域的打印。代码由 jwpat7 是:

import math

def mostfar(j, n, s, c, mx, my): # advance j to extreme point
    xn, yn = hull[j][0], hull[j][1]
    rx, ry = xn*c - yn*s, xn*s + yn*c
    best = mx*rx + my*ry
    while True:
        x, y = rx, ry
        xn, yn = hull[(j+1)%n][0], hull[(j+1)%n][1]
        rx, ry = xn*c - yn*s, xn*s + yn*c
        if mx*rx + my*ry >= best:
            j = (j+1)%n
            best = mx*rx + my*ry
        else:
            return (x, y, j)

n = len(hull)
iL = iR = iP = 1                # indexes left, right, opposite
pi = 4*math.atan(1)
for i in range(n-1):
    dx = hull[i+1][0] - hull[i][0]
    dy = hull[i+1][1] - hull[i][1]
    theta = pi-math.atan2(dy, dx)
    s, c = math.sin(theta), math.cos(theta)
    yC = hull[i][0]*s + hull[i][1]*c    
    xP, yP, iP = mostfar(iP, n, s, c, 0, 1)
    if i==0: iR = iP
    xR, yR, iR = mostfar(iR, n, s, c,  1, 0)
    xL, yL, iL = mostfar(iL, n, s, c, -1, 0)
    area = (yP-yC)*(xR-xL) 
    print '    {:2d} {:2d} {:2d} {:2d} {:9.3f}'.format(i, iL, iP, iR, area)

结果是:

i iL iP iR    Area
 0  6  8  0   203.000
 1  6  8  0   211.875
 2  6  8  0   205.800
 3  6 10  0   206.250
 4  7 12  0   190.362
 5  8  0  1   203.000
 6 10  0  4   201.385
 7  0  1  6   203.000
 8  0  3  6   205.827
 9  0  3  6   205.640
10  0  4  7   187.451
11  0  4  7   189.750
12  1  6  8   203.000

我希望创建一个函数,返回最小矩形的长度、宽度和面积。例如:

Length, Width, Area = get_minimum_area_rectangle(hull)
print Length, Width, Area
18.036, 10.392, 187.451

我的问题是:

  1. 我需要创建一个函数还是两个函数。例如:定义 mostfar 和 get_minimum_area_rectangle
  2. hull 是一个值列表。它是最好的格式吗?
    1. 遵循单一功能的方法,我在最远的内部集成时遇到问题

提前致谢

1) 解决方案:一个函数 按照 Scott Hunter 建议的第一个解决方案,我在将 mostfar() 集成到 get_minimum_area_rectangle() 中时遇到了问题。任何建议或帮助都非常感谢,因为我可以学习。

#!/usr/bin/python
import math

def get_minimum_area_rectangle(hull):
    # get pi greek
    pi = 4*math.atan(1)
    # number of points
    n = len(hull)
     # indexes left, right, opposite
    iL = iR = iP = 1
    # work clockwise direction
    for i in range(n-1):
        # distance on x axis
        dx = hull[i+1][0] - hull[i][0]
        # distance on y axis
        dy = hull[i+1][1] - hull[i][1]
        # get orientation angle of the edge
        theta = pi-math.atan2(dy, dx)
        s, c = math.sin(theta), math.cos(theta)
        yC = hull[i][0]*s + hull[i][1]*c

在上面的 jwpat7 示例之后,我需要使用 mostfar()。在这一点上,我很难理解如何整合(对不起,这个术语不正确)

【问题讨论】:

  • 回答关于您选择的解决方案的问题:只需缩进 def mostfar:... 代码并将其放在开头的 def get_minimum_area_rectangle: 内。
  • @martineau,谢谢。但是你认为把 def mostfar 放在 def get_minimum_area_rectangle 里面是优雅的吗?你知道一些优雅的方法来解决这个问题吗?再次感谢
  • 它只是有点不优雅,特别是因为它确实是一个本地函数。在每次调用外部函数时都会执行函数定义的意义上,这有点低效。这样做可以避免这种情况,如果您在其名称中添加下划线前缀,它将被视为私有模块函数。实现此目的的另一种方法是将其作为_mostfar() 方法放入一个类中。然后get_minimum_area_rectangle 可以是一个单独的方法来调用它或重命名类的__call__() 方法(使其成为“函子”或函数对象类)。
  • @martineau。如果你有时间(我不希望滥用你的时间)。为了学习和提高,我可以问你一个写的例子吗?提前致谢

标签: python performance algorithm coding-style styles


【解决方案1】:
  1. 您可以使用一个函数或两个函数,但使用两个函数可能更简洁、更容易。您可以将mostfar 函数保持原样。然后,只需添加函数定义行,将代码的后半部分转换为函数即可:

    def get_minimum_area_rectangle(hull):
    

    ...然后缩进其余代码(以n = len(hull) 开头)以形成函数的主体。您还需要更改函数以返回您想要获取的值(长度、宽度和面积)。这将使您的代码保持模块化和干净,并且只需要很少的更改。

  2. 为此目的使用hull 的值列表似乎很好。另一种方法是使用数组(如 NumPy 数组),但在这种情况下,您将迭代地遍历数据,一次一个项目,而不是同时对多个数据点进行任何计算。所以列表应该没问题。访问列表中的项目很快,与您必须做的数学运算相比,它不应该成为瓶颈。

【讨论】:

    【解决方案2】:

    这是一个示例,说明如何从您的代码中使其成为 functor 对象并使用它——以及对我认为值得的其他一些事情进行的一些更改。函子是充当函数角色但可以像对象一样操作的实体。

    在 Python 中,两者之间的区别不大,因为函数已经是单例对象,但有时为一个对象创建一个专门的类很有用。在这种情况下,它允许将辅助函数设置为私有类方法,而不是您似乎反对这样做的全局或嵌套方法。

    from math import atan2, cos, pi, sin
    
    class GetMinimumAreaRectangle(object):
        """ functor to find length, width, and area of the smallest rectangular
            area of the given convex hull """
        def __call__(self, hull):
            self.hull = hull
            mostfar = self._mostfar  # local reference
            n = len(hull)
            min_area = 10**100  # huge value
            iL = iR = iP = 1  # indexes left, right, opposite
    #        print '    {:>2s} {:>2s} {:>2s} {:>2s} {:>9s}'.format(
    #                   'i', 'iL', 'iP', 'iR', 'area')
            for i in xrange(n-1):
                dx = hull[i+1][0] - hull[i][0]  # distance on x axis
                dy = hull[i+1][1] - hull[i][1]  # distance on y axis
                theta = pi-atan2(dy, dx)   # get orientation angle of the edge
                s, c = sin(theta), cos(theta)
                yC = hull[i][0]*s + hull[i][1]*c
                xP, yP, iP = mostfar(iP, n, s, c, 0, 1)
                if i==0: iR = iP
                xR, yR, iR = mostfar(iR, n, s, c,  1, 0)
                xL, yL, iL = mostfar(iL, n, s, c, -1, 0)
                l, w = (yP-yC), (xR-xL)
                area = l*w
    #            print '    {:2d} {:2d} {:2d} {:2d} {:9.3f}'.format(i, iL, iP, iR, area)
                if area < min_area:
                    min_area, min_length, min_width = area, l, w
            return (min_length, min_width, min_area)
    
        def _mostfar(self, j, n, s, c, mx, my):
            """ advance j to extreme point """
            hull = self.hull  # local reference
            xn, yn = hull[j][0], hull[j][1]
            rx, ry = xn*c - yn*s, xn*s + yn*c
            best = mx*rx + my*ry
            while True:
                x, y = rx, ry
                xn, yn = hull[(j+1)%n][0], hull[(j+1)%n][1]
                rx, ry = xn*c - yn*s, xn*s + yn*c
                if mx*rx + my*ry >= best:
                    j = (j+1)%n
                    best = mx*rx + my*ry
                else:
                    return (x, y, j)
    
    if __name__ == '__main__':
    
        hull= [(560023.44957588764, 6362057.3904932579),
               (560023.44957588764, 6362060.3904932579),
               (560024.44957588764, 6362063.3904932579),
               (560026.94957588764, 6362068.3904932579),
               (560028.44957588764, 6362069.8904932579),
               (560034.94957588764, 6362071.8904932579),
               (560036.44957588764, 6362071.8904932579),
               (560037.44957588764, 6362070.3904932579),
               (560037.44957588764, 6362064.8904932579),
               (560036.44957588764, 6362063.3904932579),
               (560034.94957588764, 6362061.3904932579),
               (560026.94957588764, 6362057.8904932579),
               (560025.44957588764, 6362057.3904932579),
               (560023.44957588764, 6362057.3904932579)]
    
        gmar = GetMinimumAreaRectangle()  # create functor object
        print "dimensions and area of smallest enclosing rectangular area:"
        print "  {:.3f}(L) x {:.3f}(W) = {:.3f} area".format(*gmar(hull))  # use it
    

    输出:

    dimensions and area of smallest enclosing rectangular area:
      10.393(L) x 18.037(W) = 187.451 area
    

    【讨论】:

      【解决方案3】:
      1. 您当然可以将其作为一个单独的功能来完成:稍微修改 mostfar,而不是打印找到的区域,而是跟踪最小的区域和随之而来的信息。或者你可以让它收集它正在打印的值到一个 lst,它是 G.E.A.R.然后可以用来找到最小值。

      编辑:(我错过了一些代码在 mostfar 之外)我会将“脚本”部分(mostfar 之后的代码)包装到一个函数中,并如上所述修改它。然后,您的“脚本”将调用该函数,或者,如果使用第二个修改,则从返回的列表中找到最小值。

      1. 我认为您对船体的表示没有任何问题。

      【讨论】:

      • 亲爱的@Scott Hunter。感谢您的重播。我需要 open mostfar (对不起,我真的是初学者)还是只在 get_minimum_area_rectangle 中复制 def mostfar ?
      • 将 mostfar() 移入 get_minimum_area_rectangle()。它将需要另一个或两个级别的缩进。使用 get_minimum_area_rectangle() 中的 mostfar(),mostfar() 可以正确访问参数hull,在早期代码中将其视为全局变量。
      【解决方案4】:

      我正在发布另一个答案,显示如何按照我(和其他人)的建议进行操作,这只是将辅助函数 mostfar() 嵌套在被调用的主要函数中。这在 Python 中很容易做到,因为嵌套函数可以访问其封闭范围的局部变量(例如 hull 在这种情况下)。我还按照约定重命名了函数_mostfar(),以表明某些东西是私有的,但这并不是绝对必要的(永远,而且绝对不是这里)。

      如您所见,尽管我确实简化了一些与嵌套函数无关的事情(因此它们可能会集成到您选择的任何答案中),但大部分代码与我的其他答案中的代码非常相似。

      from math import atan2, cos, pi, sin
      
      def get_minimum_area_rectangle(hull):
          """ find length, width, and area of the smallest rectangular
              area of the given convex hull """
      
          def _mostfar(j, n, s, c, mx, my):
              """ advance j to extreme point """
              xn, yn = hull[j]
              rx, ry = xn*c - yn*s, xn*s + yn*c
              best = mx*rx + my*ry
              k = j + 1
              while True:
                  x, y = rx, ry
                  xn, yn = hull[k % n]
                  rx, ry = xn*c - yn*s, xn*s + yn*c
                  if mx*rx + my*ry < best:
                      return (x, y, j)
                  else:
                      j, k = k % n, j + 1
                      best = mx*rx + my*ry
      
          n = len(hull)
          min_area = 10**100
          iL = iR = iP = 1  # indexes left, right, opposite
      #   print '    {:>2s} {:>2s} {:>2s} {:>2s} {:>9s}'.format(
      #              'i', 'iL', 'iP', 'iR', 'area')
          for i in xrange(n-1):
              dx = hull[i+1][0] - hull[i][0]  # distance on x axis
              dy = hull[i+1][1] - hull[i][1]  # distance on y axis
              theta = pi-atan2(dy, dx)   # get orientation angle of the edge
              s, c = sin(theta), cos(theta)
              yC = hull[i][0]*s + hull[i][1]*c
              xP, yP, iP = _mostfar(iP, n, s, c, 0, 1)
              if i==0: iR = iP
              xR, yR, iR = _mostfar(iR, n, s, c,  1, 0)
              xL, yL, iL = _mostfar(iL, n, s, c, -1, 0)
              l, w = (yP-yC), (xR-xL)
              area = l*w
      #       print '    {:2d} {:2d} {:2d} {:2d} {:9.3f}'.format(i, iL, iP, iR, area)
              if area < min_area:
                  min_area, min_length, min_width = area, l, w
          return (min_length, min_width, min_area)
      
      if __name__ == '__main__':
      
          hull= [(560023.44957588764, 6362057.3904932579),
                 (560023.44957588764, 6362060.3904932579),
                 (560024.44957588764, 6362063.3904932579),
                 (560026.94957588764, 6362068.3904932579),
                 (560028.44957588764, 6362069.8904932579),
                 (560034.94957588764, 6362071.8904932579),
                 (560036.44957588764, 6362071.8904932579),
                 (560037.44957588764, 6362070.3904932579),
                 (560037.44957588764, 6362064.8904932579),
                 (560036.44957588764, 6362063.3904932579),
                 (560034.94957588764, 6362061.3904932579),
                 (560026.94957588764, 6362057.8904932579),
                 (560025.44957588764, 6362057.3904932579),
                 (560023.44957588764, 6362057.3904932579)]
      
          print "dimensions and area of smallest enclosing rectangular area:"
          print "  {:.3f}(L) x {:.3f}(W) = {:.3f} area".format(
                   *get_minimum_area_rectangle(hull))
      

      【讨论】:

      • 谢谢马蒂诺。我们也在测试 OpenCV,如果您想分享您的知识,所有社区都很高兴stackoverflow.com/questions/13565237/… 这种方法与 OpenCV 中的方法存在一些差异
      • @Gianni:我去看看。谢谢。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-10-30
      • 1970-01-01
      • 2011-12-14
      • 1970-01-01
      • 1970-01-01
      • 2013-01-24
      • 1970-01-01
      相关资源
      最近更新 更多