Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/298.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
检测python/cython类属性的部分更新_Python_Cython - Fatal编程技术网

检测python/cython类属性的部分更新

检测python/cython类属性的部分更新,python,cython,Python,Cython,以下是场景: 作为我的2D渲染cython库重构过程的一部分,我正在删除无关的“getter/setter”函数,以使其更加用户友好。我的渲染器的任务之一是在屏幕上显示图像。目前,我使用一些SDL2\u image函数加载图像并提取其像素数据。然后,每当通过“setter”函数修改某些图像像素数据时,我都会通知OpenGL更新后的数据需要从CPU复制到GPU。我的目标是通过将图像的像素作为可直接修改的属性公开,并使CPU GPU同步在幕后进行,从而使这种“簿记”对用户更加隐藏 当前代码: 下面是

以下是场景:

作为我的2D渲染cython库重构过程的一部分,我正在删除无关的“getter/setter”函数,以使其更加用户友好。我的渲染器的任务之一是在屏幕上显示图像。目前,我使用一些
SDL2\u image
函数加载图像并提取其像素数据。然后,每当通过“setter”函数修改某些图像像素数据时,我都会通知OpenGL更新后的数据需要从CPU复制到GPU。我的目标是通过将图像的像素作为可直接修改的属性公开,并使CPU GPU同步在幕后进行,从而使这种“簿记”对用户更加隐藏

当前代码:

下面是我的
Image
类的相关cython代码(包含implementation.pyx文件只是为了完整性,并且只与之相关):

image.pxd

from libs.sdl2 cimport *
from libc.stdint cimport uint8_t, uint32_t, int64_t
from libc.stdlib cimport malloc, free
from cpython cimport bool
from cython.view cimport array as cvarray

cdef class Image:
    cdef unsigned int[:, :, :] pixels
    cdef readonly int width
    cdef readonly int height
image.pyx

from libs.sdl2 cimport *
from libc.stdint cimport uint8_t, uint32_t, int64_t
from libc.stdlib cimport malloc, free
from cpython cimport bool
from cython.view cimport array as cvarray

cdef int rgba_size = 4

cdef class Image:

    def __cinit__(self, int width, int height, unsigned int[:, :, :] pixels=None):
        self.width = width
        self.height = height
        self.pixels = pixels

    def __dealloc__(Image self):
        pass

    @property
    def pixels(self):
        return self.pixels

    @pixels.setter
    def pixels(self, unsigned int[:, :, :] new_pixels):
        self.pixels = new_pixels
        print("updated")

    @staticmethod
    def from_url(str image_url):
        """
        1. Load in an image to an SDL_Surface using SDL_Image module
        2. Convert the image to the SDL_PIXELFORMAT_ABGR8888 format (right channels for OpenGL's RGBA)
        3. Flip the image as OpenGL spec states texture's BOTTOM-left = (0, 0)!
        """
        cdef Image out
        cdef char * err
        cdef bytes b_image_url = bytes(image_url, "utf-8")
        cdef SDL_Surface *surf = IMG_Load(b_image_url)
        if <int>surf == 0:
            err = IMG_GetError()
            raise ValueError(err)

        cdef SDL_Surface *rgba_surf = SDL_ConvertSurfaceFormat(surf, SDL_PIXELFORMAT_ABGR8888, 0)
        cdef int width = rgba_surf.w
        cdef int height = rgba_surf.h
        cdef uint32_t *pixel_data = <uint32_t *>rgba_surf.pixels

        cdef unsigned int[:, :, :] pixels
        pixels = cvarray(shape=(width, height, rgba_size), itemsize=sizeof(int), format="I")
        cdef int x, y, flipped = 0
        cdef uint32_t abgr = 0
        for y in xrange(height):
            for x in xrange(width):
                flipped = (height - 1 - y) * width + x
                abgr = pixel_data[flipped]
                pixels[x, y, 0] = abgr >> 0 & 0xFF
                pixels[x, y, 1] = abgr >> 8 & 0xFF
                pixels[x, y, 2] = abgr >> 16 & 0xFF
                pixels[x, y, 3] = abgr >> 24 & 0xFF

        SDL_FreeSurface(surf)
        SDL_FreeSurface(rgba_surf)
        out = Image(width, height, pixels=pixels)
        return out
当然,问题是,由于只修改了
像素的单个值,因此不会触发
@pixels.setter
装饰器。触发它的唯一方法似乎是替换整个memoryview


TL;DR:给定一个类的一个公开的、可公开修改的属性,该属性是具有多个子值的list/array/np.arrray/memoryview/where,有没有一种方法可以透明地检测所述属性是否在该类中进行了部分更新?

我认为通过属性和上下文管理器的组合(对于with语句),您可以获得相当有效的结果

首先,保持你的能手和能手与问题中的一样。修改getter,使其返回只读numpy数组:

@property
def pixels(self):
    r = np.asarray(self._pixels) # gets a view, not a copy
    r.flags['WRITEABLE'] = False
    return r
因此,不应该有任何“不受监控”的更改。setter保持不变,只允许完全替换像素

现在定义一个返回contex manager对象的函数:

def writeable_pixels(self):
    class WriteablePixelsCtx:
        def __enter__(self2):
            return self._pixels # get the underlying (writeable) memoryview
        def __exit__(self2,*args):
            # update changes 
            # handle any exceptions appropriately (see the main Python documentation on __exit__ for this)
   return WriteablePixelsCtx()
然后,您可以执行以下操作:

with im.writeable_pixels() as pixels:
    pixels[i,j] = something
在块的末尾,您的更改将通过退出一次有效地处理

我喜欢的是:

  • 可以在最后进行一次大的更新来进行一系列小的更改
  • 可以在Cython中高效地写入像素(这是一个memoryview,因此您只需
    cdef
    一个局部变量作为memoryview)
我不喜欢的是:

  • 两个不同的轴机构有点乱
  • 在Cython中无法快速进行读取访问-MemoryView不适用于只读数组
  • 您可以在with块之后设法写入保存的
    像素
    对象。您可以通过添加一个额外的间接层来避免这种情况,但在Cython中这将大大降低速度

相关(对于Python而不是Cython):。真的没有赛昂特有的把戏though@DavidW谢谢你的链接!我查看了这些选项,但它似乎仍然不是一个完整的解决方案。如果我使用
\uuuu getitem\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。这对于访问单个像素非常有效,但是我不能用
memoryview
对象一次性设置所有像素。这将迫使python for循环在像素上迭代(我们都知道这很慢,因此cython),用户无法利用numpy来生成像素数据。@DavidW如果我还是使用
获取像素()
获取像素()
设置像素()
,可能会更好,和
set_pixels()
set函数来完成这项工作。我在那页上看到的另一个选择是围绕pixels data属性创建一个包装器(像一个扩展cython数组的
ImageData
类),但这看起来既丑陋又笨拙。
\u getitem\uuuuuuuuuuuuuuuuuu
\uuuuuuuuuuuuuuuuu setitem\uuuuuuuuuuuuuuuuuu
也可以处理切片,这应该非常快。不幸的是,我不认为有一个“好”的解决办法,(否则我会回答的)@DavidW谢谢,没有意识到这是事实,我会尝试一下。感谢您一如既往地为这些问题提供帮助!我应该指出的是,我还没有测试过这个,但它相当直接,所以即使确切的代码不需要答案,这个想法也应该有效!带有
上下文的
确实很好地解决了这一问题,尽管正如您所说,使用单独的函数获得只读属性有点奇怪。为了缓解这种情况,我添加了一个setter,它只会引发一个错误,告诉用户使用上下文方法。也就是说,进行大规模更新更聪明。此外,contextlib现在在cython中对python 2和3都能很好地工作,因此不再需要
可写\u像素中的嵌入式类了!再想一想,setter永远不会被调用,因为用户得到了一个
ValueError:assignment destination是只读的
error,这使得它毫无意义,所以我放弃了它。谢谢你的帮助!我已经意识到它并不像我想象的那样安全——在with块之后,可写对象仍然是可写的——但我仍然认为这是一个相当好的方法(意图非常清楚),这正是我所想的。我会提出一个新问题(部分原因是我认为我不能回答这个问题……)。
with im.writeable_pixels() as pixels:
    pixels[i,j] = something