首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >跟踪眼动

跟踪眼动
EN

Code Review用户
提问于 2015-01-02 21:20:57
回答 1查看 684关注 0票数 6

下面的类(这是我在python中的第一个类)用于眼睛跟踪上下文。想象一下,在这样的背景下,你总是会有一个单一的注视点和一些封闭的轮廓。我怎样才能归还他们之间所有可能的关系?

例如,对于两个等高线,要点可以是:

  • 关系1:内轮廓A和外轮廓B;
  • 关系2:内轮廓B和外轮廓A;
  • 关系3:内部轮廓A和B;
  • 关系4:外部等高线A和B;

在我的例子中,我使用每个关系作为颜色的标记,在轮廓数目变化的上下文中。

您也可以想象这些轮廓集,但让我们避免高级集合论的复杂性。

这个问题给了我们2的幂c=等高线数。一个算盘给我们的列数乘以幂i=线数。因此,算盘似乎是一个很好的比喻,例如:

代码语言:javascript
运行
复制
contours = [c1, c2]
Abacus = ClassAbacus (len(contours))
color_tags = Abacus.Enumerate()

给我们一个列表,上面有所有可能的标签,其中有+在里面,-在外面,数字是等高线:

“+1+2”、+1-2“、”-1+2“、”-1-2“

所以,现在我们有了4种颜色的参考。我们可以给每一点取一个名字:

代码语言:javascript
运行
复制
import cv2

...

def PolygonTestEx(contours, pt, contours_counter = 0, counter_code = ''):
    for x in xrange(1, len(contours) + 1):
        Inside = cv2.pointPolygonTest(contours[x -1], pt, False)
        # Inside = contours[x]
        if Inside > -1:
            counter_code = counter_code + '+' + str(x + contours_counter)
        else:
            counter_code = counter_code + '-' + str(x + contours_counter)
    contours_counter = contours_counter + len(contours)
    return contours_counter, counter_code

在这方面,它应该给出四种可能的状态:

我能以更好的可读性写这门课吗?

拜托,可以建议一种更快的方法来做同样的事情,或者在OOP中错过一些常见的实践。

代码语言:javascript
运行
复制
CURRENT_LINE = 0
SIGNS = 1

class BaseError(Exception):
    def __init__(self, value):
        self.value = value

    def __str__(self):
        return repr(self.value)

class ClassAbacus(object):
    """
    Recombining tool.
    Imagine each char in char_set as digits, or beads, and count them accordingly to a base reference.
    Imagine the base as the abacus lines.
    'Counter' returns an integer equals to len(char_set) raised to the power x = base, giving the number
        of all possible bead states of the "abacus".
    'Enumarate' prints each or all states.

    """

    def __init__(   
                self,
                char_set = ['+', '-'],
                base = 2
                ):
        super(ClassAbacus, self).__init__()
        self.Chars = char_set
        self.Counter = 0
        self.States = []
        self.Abacus = []

        self.Base = base
        if (base + 1) > 2:
            self.__Base = base + 1
            self.Instantiate()
        else:
            try:
                raise BaseError(1)
            except BaseError as e:
                print 'Use argument "base" higher then one.', e.value

    def doSaveState(self):
        state = ''
        for column in xrange(0, len(self.Abacus)):
            # def addSignTo(state, column, item):
            state = state + self.Abacus[column][SIGNS][ self.Abacus[column][CURRENT_LINE] ]
        self.States.append(state)

    def doLineReset(self, from_column):
        to_column = len(self.Abacus)
        for column in xrange(from_column, to_column):
            self.Abacus[column][CURRENT_LINE] = 0

    def doLineIncrement(self, column):
        self.Counter += 1
        self.doSaveState()
        n = self.Abacus[column][CURRENT_LINE]
        self.Abacus[column][CURRENT_LINE] = n + 1

    def doCount(self, Column):
        KeepsCounting = True

        HighColumn = len(self.Abacus) -1
        LowColumn = 0

        if Column < LowColumn:
            self.Counter += 1
            self.doSaveState()
            KeepsCounting = False
        else:
            ColumnMaxLine = len(self.Abacus[Column][SIGNS]) -1
            ColumnCurLine = self.Abacus[Column][CURRENT_LINE]

            if ColumnCurLine == ColumnMaxLine:
                Column -= 1
                KeepsCounting = self.doCount(Column)
                return KeepsCounting

            if ( Column < HighColumn ) and ( ColumnCurLine < ColumnMaxLine ):
                self.doLineIncrement(Column)
                self.doLineReset( Column + 1 )
                self.doCount(HighColumn)
                return KeepsCounting

            if ( Column == HighColumn ) and ( ColumnCurLine < ColumnMaxLine ):
                self.doLineIncrement(Column)
                self.doCount(Column)
                return KeepsCounting

    def Instantiate(self):
        # set the grid of the abacus
        for x in xrange(1, self.__Base):
            container = []
            for char in self.Chars:
                # concatenate each char with its base reference
                container.append(char + str(x))
            self.Abacus.append([0, container])

        max_column = max(self.Abacus)
        max_index = self.Abacus.index(max_column)
        self.doCount(max_index)            

    def Enumerate(self, index = -1):
        container = []
        if (index < 0) or (index > self.Counter - 1):
            for state in self.States:
                container.append(state)
            return container
        else:
           return self.States[index]

为了完整起见,这个类将被称为这里

EN

回答 1

Code Review用户

回答已采纳

发布于 2015-01-03 08:44:08

我还没有安装cv2,所以我在这里失明了,但我有一些评论。首先,您所描述的问题可以很容易地用itertools.product解决:

代码语言:javascript
运行
复制
import itertools

def get_set_intersections(chars="+-", base=2):
    numbers = range(1, base+1)

    for signs in itertools.product(chars, repeat=base):
        yield "".join("{}{}".format(sign, n) for sign, n in zip(signs, numbers))

list(get_set_intersections())
#>>> ['+1+2', '+1-2', '-1+2', '-1-2']

第二,对代码的一些评论。

在长度为2的列表中使用CURRENT_LINESIGNS作为常量索引,而应该使用类。这里有一个:

代码语言:javascript
运行
复制
class AbacusColumn(object):
    def __init__(self, current_line, signs):
        self.current_line = current_line
        self.signs = signs

你上了一门新课,BaseError。但是,您不应该在旧错误已经足够的地方创建新错误;只需使用ValueError即可。即使您确实想要一个新类,它也应该是ValueError的一个子类。

唯一一次使用BaseError时:

代码语言:javascript
运行
复制
try:
    raise BaseError(1)
except BaseError as e:
    print 'Use argument "base" higher then one.', e.value

...which等价于

代码语言:javascript
运行
复制
print 'Use argument "base" higher then one. 1'

相反,做

代码语言:javascript
运行
复制
raise ValueError('Use argument "base" higher then one.')

另外,切换条件,这将变成:

代码语言:javascript
运行
复制
if (base + 1) <= 2

呃,你是说

代码语言:javascript
运行
复制
if base <= 1

不要使用两个双下划线(self.__Base)的属性。使用单个下划线。两个给你起了一个你不想要的名字。

函数定义行应格式化为

代码语言:javascript
运行
复制
def __init__(self, char_set=['+', '-'], base=2):

因为它太短,不需要线包装。

属性应该在snake_case中。

你从不使用self.Base。把它移开。实际上,移除_Base,因为您只需在使用点添加一个。

您不需要调用super;您的超类是object

(在转到AbacusColumn类之后)

代码语言:javascript
运行
复制
state = ''
for column in xrange(0, len(self.abacus)):
    state = state + self.abacus[column].signs[self.abacus[column].current_line]

首先,我们应该把这件事讨论一下。

代码语言:javascript
运行
复制
state = ''
for column in self.abacus:
    state += column.signs[column.current_line]

其次,在循环中添加不可变的容器是不好的。使用str.join代替:

代码语言:javascript
运行
复制
state = ''.join(column.signs[column.current_line] for column in self.abacus)

do_line_reset中,您应该做类似的事情

代码语言:javascript
运行
复制
for column in self.abacus[from_column:]:
    column.current_line = 0

do_line_increment应该将+=用于

代码语言:javascript
运行
复制
self.abacus[column].current_line += 1

我有一种感觉,keeps_countingdo_count坏了。逻辑是:

代码语言:javascript
运行
复制
keeps_counting = True

if ...:
    keeps_counting = False
else:
    if ...:
        keeps_counting = self.do_count(column)
        return keeps_counting

    if ...:
        return keeps_counting

    if ...:
        return keeps_counting

[function ends]

这可以简化为:

代码语言:javascript
运行
复制
if ...:
    ...
else:
    if ...:
        return self.do_count(column)
    if ...:
        return True
    if ...:
        return True

这意味着在第一个分支中返回的是None,而不是False,或者如果三个ifs都失败了。但是,这是可以的,因为你从来不使用结果。把它移开。

instantiate中,可以使用列表理解简化生成self.abacus

代码语言:javascript
运行
复制
self.abacus = [
    AbacusColumn(0, [char + str(x+1) for char in self.chars])
    for x in range(self.base)
]

您的max(self.abacus)将不得不更改,因为AbacusColumn是不可比拟的,但是它还是有缺陷的,因为它们都被初始化为[0, list of strings]。由于字符串都是相同的表单,所以实际上只是找到了最大的索引(尽管因为要进行字符串比较,它的基数大于9)。就这么做

代码语言:javascript
运行
复制
self.do_count(self.base - 1)

enumerate中,您需要:

代码语言:javascript
运行
复制
for state in self.states:
    container.append(state)
return container

这只是

代码语言:javascript
运行
复制
return list(self.states)

你的比较最好写成

代码语言:javascript
运行
复制
if not (0 < index < self.counter):

虽然我建议你把这个改成

代码语言:javascript
运行
复制
if not (0 <= index < self.counter):

事实上,根据index是否超出界限返回不同类型是愚蠢的,您应该一直返回list(self.states)。如果您希望第二种方法只访问一种方法,请创建一个新方法。

你的名字ClassAbacus很奇怪:当然Abacus更好。

您只需从instantiate__init__调用,所以我建议将它移到那里。如果不在函数中,则在函数下面。这样做允许您删除self.baseself.chars

__init__中的错误检查移到顶部。

不要给self.abacus默认设置,它只隐藏bug。

大多数方法都是从do_开始的。就我个人而言,我会把它移除。把人物花在更好的名字上(如。count是做什么的?)

目前,这意味着:

代码语言:javascript
运行
复制
class AbacusColumn(object):
    def __init__(self, current_line, signs):
        self.current_line = current_line
        self.signs = signs

class Abacus(object):
    """
    Recombining tool.
    Imagine each char in chars as digits, or beads, and count them accordingly to a base reference.
    Imagine the base as the abacus lines.
    'Counter' returns an integer equals to len(chars) raised to the power x = base, giving the number
        of all possible bead states of the "abacus".
    'Enumarate' prints each or all states.

    """

    def __init__(self, chars=['+', '-'], base=2):
        if base <= 1:
            raise ValueError('Use argument "base" higher then one.')

        self.counter = 0
        self.states = []

        self.abacus = [
            AbacusColumn(0, [char + str(x+1) for char in chars])
            for x in range(base)
        ]

        self.count(base - 1)

    def save_state(self):
        state = ''.join(column.signs[column.current_line] for column in self.abacus)
        self.states.append(state)

    def line_reset(self, from_column):
        for column in self.abacus[from_column:]:
            column.current_line = 0

    def line_increment(self, column):
        self.counter += 1
        self.save_state()
        self.abacus[column].current_line += 1

    def count(self, column):
        high_column = len(self.abacus) -1
        low_column = 0

        if column < low_column:
            self.counter += 1
            self.save_state()

        else:
            column_max_line = len(self.abacus[column].signs) -1
            column_cur_line = self.abacus[column].current_line

            if column_cur_line == column_max_line:
                column -= 1
                self.count(column)

            if ( column < high_column ) and ( column_cur_line < column_max_line ):
                self.line_increment(column)
                self.line_reset( column + 1 )
                self.count(high_column)

            if ( column == high_column ) and ( column_cur_line < column_max_line ):
                self.line_increment(column)
                self.count(column)

    def enumerate(self):
        return list(self.states)

counter只跟踪len(self.states)。把它移开。

count是递归的,但不需要递归。尝试将其设置为while,并使其成为yield结果,而不是保存它们:

代码语言:javascript
运行
复制
class Abacus(object):
    def __init__(self, chars=['+', '-'], base=2):
        if base <= 1:
            raise ValueError('Use argument "base" higher then one.')

        self.base = base
        self.abacus = [
            AbacusColumn(0, [char + str(x+1) for char in chars])
            for x in range(base)
        ]

    def gen_state(self):
        return ''.join(column.signs[column.current_line] for column in self.abacus)

    def line_reset(self, from_column):
        for column in self.abacus[from_column:]:
            column.current_line = 0

    def count(self, column):
        high_column = len(self.abacus) -1

        while column >= 0:
            column_max_line = len(self.abacus[column].signs) - 1
            column_cur_line = self.abacus[column].current_line

            if column_cur_line == column_max_line:
                column -= 1

            else:
                yield self.gen_state()
                self.abacus[column].current_line += 1

                if column < high_column:
                    self.line_reset( column + 1 )
                    column = high_column

        yield self.gen_state()

    def enumerate(self):
        return list(self.count(self.base - 1))

这突出说明类没有做任何有用的事情;将其分解为方法。

代码语言:javascript
运行
复制
class AbacusColumn(object):
    def __init__(self, current_line, signs):
        self.current_line = current_line
        self.signs = signs

def gen_state(abacus):
    return ''.join(column.signs[column.current_line] for column in abacus)

def line_reset(abacus, from_column):
    for column in abacus[from_column:]:
        column.current_line = 0

def abacus(chars=['+', '-'], base=2):
    if base <= 1:
        raise ValueError('Use argument "base" higher then one.')

    column = high_column = base - 1

    abacus = [
        AbacusColumn(0, [char + str(x+1) for char in chars])
        for x in range(base)
    ]

    while column >= 0:
        column_max_line = len(abacus[column].signs) - 1
        column_cur_line = abacus[column].current_line

        if column_cur_line == column_max_line:
            column -= 1

        else:
            yield gen_state(abacus)
            abacus[column].current_line += 1

            if column < high_column:
                line_reset(abacus, column + 1)
                column = high_column

    yield gen_state(abacus)

print(list(abacus(base=5)))

这比原来的要简单得多。我很想删除AbacusColumn,将abacus拆分成两个并行列表,并翻转搜索它们的顺序,从而允许进一步简化:

代码语言:javascript
运行
复制
def abacus(chars=['+', '-'], base=2):
    if base <= 1:
        raise ValueError('Use argument "base" higher then one.')

    digit = [0] * base

    while True:
        yield ''.join(chars[current_line] + str(i) for i, current_line in enumerate(digit, 1))

        for column in range(base):
            if digit[column] == len(chars) - 1:
                digit[column] = 0

            else:
                digit[column] += 1
                break

        else:
           return

print(list(abacus(base=5)))
票数 7
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/75524

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档