Python 如何使此图像处理功能更快?已经试过Cython了

Python 如何使此图像处理功能更快?已经试过Cython了,python,image,image-processing,cython,Python,Image,Image Processing,Cython,我目前正试图在Python中实现一个函数,该函数应该查找图像中某个颜色值的出现情况,以便确定颜色区域的边界框。这似乎有效,尽管速度非常慢。在单个1920x1080图像上迭代大约需要30秒。我尝试将其转换为Cython代码,这只会使每个图像的性能提高约2秒。这仍然是我在寻找的害羞。因为我是Cython的新手,我希望你能给我一些改进的建议。你可以在下面看到我的代码,非常感谢 cimport cython import numpy as np cimport numpy as np @cython

我目前正试图在Python中实现一个函数,该函数应该查找图像中某个颜色值的出现情况,以便确定颜色区域的边界框。这似乎有效,尽管速度非常慢。在单个1920x1080图像上迭代大约需要30秒。我尝试将其转换为Cython代码,这只会使每个图像的性能提高约2秒。这仍然是我在寻找的害羞。因为我是Cython的新手,我希望你能给我一些改进的建议。你可以在下面看到我的代码,非常感谢

cimport cython

import numpy as np
cimport numpy as np

@cython.wraparound(False)
@cython.boundscheck(False)
cdef _cget_bboxes_(img):

    cdef int y_lim = img.shape[0]
    cdef int x_lim = img.shape[1]

    cdef np.ndarray img_array = img

    color_dict = {}


    cdef int y, x

    for y in range(y_lim):
        for x in range(x_lim):

            pix = img_array[y][x]
            pix = tuple(pix)

            if np.any(pix >= (10, 10, 10)):
                if pix not in color_dict:

                    color_dict[pix] = {"min_x": x, "max_x": x, "min_y": y, "max_y": y, "count": 1}

                else:

                    if color_dict[pix]["min_x"] >= x:
                        color_dict[pix]["min_x"] = x

                    if color_dict[pix]["max_x"] <= x:
                        color_dict[pix]["max_x"] = x

                    if color_dict[pix]["min_y"] >= y:
                        color_dict[pix]["min_y"] = y

                    if color_dict[pix]["max_y"] <= y:
                        color_dict[pix]["max_y"] = y

                color_dict[pix]["count"] += 1

    return color_dict
cimport cython
将numpy作为np导入
cimport numpy作为np
@cython.wrapparound(假)
@cython.boundscheck(错误)
cdef、cget、bboxes(img):
cdef int y_lim=img.shape[0]
cdef int x_lim=图像形状[1]
cdef np.ndarray img_数组=img
颜色_dict={}
cdef int y,x
对于范围内的y(y_lim):
对于范围内的x(x_lim):
pix=img_数组[y][x]
pix=元组(pix)
如果np.any(pix>=(10,10,10)):
如果pix不是彩色的:
颜色dict[pix]={“min_x”:x,“max_x”:x,“min_y”:y,“max_y”:y,“count”:1}
其他:
如果颜色dict[pix][“min\u x”]>=x:
颜色dict[pix][“min_x”]=x
如果颜色dict[pix][“max_x”]=y:
颜色记录[pix][“最小y”]=y

如果color_dict[pix][“max_y”]运行cython和
--annotate
突出显示与python交互严重的部分,这将为您提供更改内容的良好方向。有几件事马上就跳出来了:

1) 只需清理,但应直接在函数sig中键入
img
,无需分配给
img\u数组

2)
np.ndarray
不是一个足够具体的类型,您还需要底层的数据类型。我喜欢memoryview语法,因此函数sig可以

def _cget_boxes(np.uint8_t[:, :, :] img)
3) 任何可以打字的东西都应该


4) 元组和dict比数组和c类型标量慢。尝试将
color\u dict
重构为一组数组可能(也可能不是!)更好。

使用字典查找颜色三元组是一个非常糟糕的主意。三元组值有一个固定的范围(我假设为0..255)。用大小为256x256x256的3D数组替换字典将极大地提高代码的速度(查找将是微不足道的)

请注意,您所做的是计算颜色直方图。如果这在Python中还不存在,我会感到惊讶


此外,颜色直方图通常根据更粗略量化的颜色值进行计算,例如,在每个维度中使用64个存储单元。这将减少内存使用并提高速度,在大多数应用程序中不太重要。

我看到您能够在大约1秒的时间内运行代码,并且对性能感到满意。但是,您可以使用的强大功能使您的代码更快

按照@chrisb和@CrisLuengo的建议,您不仅需要向变量添加类型信息,还需要选择适当的数据结构。我建议您看一看,但简而言之,像
dict
这样的Python容器不在内存中连续存储数据,而是在访问特定元素时需要指向Python对象的“非装箱”指针。这很慢,会影响CPU缓存性能

以下是我版本的
\u cget\u bboxes\u
函数的外观:

cimport cython
from libc.stdint cimport uint8_t
import numpy as np
cimport numpy as np

cdef packed struct ColorData:
    np.uint16_t min_x, max_x, min_y, max_y
    np.uint32_t count

@cython.wraparound(False)
@cython.boundscheck(False)
cpdef get_histogram(np.uint8_t[:, :, :] img):
    cdef int y_lim = img.shape[0]
    cdef int x_lim = img.shape[1]
    cdef int y, x
    cdef uint8_t r, g, b

    """
    #You can define a numpy structured array dtype by hand using tuples...
    cdef np.dtype color_dtype = np.dtype([
        ("min_x", np.uint16),
        ("max_x", np.uint16),
        ("min_y", np.uint16),
        ("max_y", np.uint16),
        ("count", np.uint32)])
    """

    """
    Or, instead of rewriting the struct's definition as a numpy dtype, you can use this generic approach:
    1- making a temp object
    2- getting its pointer
    3- converting to memoryview
    4- converting to numpy array
    5- then getting that numpy array's dtype
    """
    cdef ColorData _color
    cdef np.dtype color_dtype = np.asarray(<ColorData[:1]>(&_color)).dtype


    #cdef ColorData[:, :, :] out#this alternatively works
    cdef np.ndarray[ColorData, ndim=3] out
    out = np.zeros(shape=(256, 256, 256), dtype=color_dtype)

    for y in range(y_lim):
        for x in range(x_lim):
            r = img[y, x, 0]
            g = img[y, x, 1]
            b = img[y, x, 2]
            if r >= 10 or g >= 10 or b >= 10:
                if out[r, g, b].count == 0:
                    out[r, g, b] = [x, x, y, y, 1]
                    """
                    out[r, g, b].min_x = x
                    out[r, g, b].max_x = x
                    out[r, g, b].min_y = y
                    out[r, g, b].max_y = y
                    out[r, g, b].count = 1
                    """
                else:
                    if out[r, g, b].min_x >= x:
                        out[r, g, b].min_x = x
                    if out[r, g, b].max_x <= x:
                        out[r, g, b].max_x = x
                    if out[r, g, b].min_y >= y:
                        out[r, g, b].min_y = y
                    if out[r, g, b].max_y <= y:
                        out[r, g, b].max_y = y
                    out[r, g, b].count += 1
    return out
cimport cython
从libc.stdint cimport uint8\t
将numpy作为np导入
cimport numpy作为np
cdef压缩结构ColorData:
np.uint16最小值,最大值,最小值,最大值
np.uint32\u t计数
@cython.wrapparound(假)
@cython.boundscheck(错误)
cpdef get_直方图(np.uint8_t[:,:,:]img):
cdef int y_lim=img.shape[0]
cdef int x_lim=图像形状[1]
cdef int y,x
cdef uint8\u t r,g,b
"""
#您可以使用元组手动定义numpy结构化数组数据类型。。。
cdef np.dtype color\u dtype=np.dtype([
(“min_x”,np.uint16),
(“max_x”,np.uint16),
(“min_y”,np.uint16),
(“最大值”,np.uint16),
(“计数”,np.uint32)])
"""
"""
或者,您可以使用以下通用方法,而不是将结构的定义重写为numpy数据类型:
1-制作临时对象
2-获取其指针
3-转换为memoryview
4-转换为numpy阵列
5-然后获取numpy数组的数据类型
"""
cdef ColorData\U color
cdef np.dtype color\u dtype=np.asarray((&\u color)).dtype
#cdef ColorData[:,:,:]out#这也适用
cdef np.ndarray[颜色数据,ndim=3]输出
out=np.zeros(shape=(256,256,256),dtype=color\u dtype)
对于范围内的y(y_lim):
对于范围内的x(x_lim):
r=img[y,x,0]
g=img[y,x,1]
b=img[y,x,2]
如果r>=10或g>=10或b>=10:
如果输出[r,g,b]。计数==0:
out[r,g,b]=[x,x,y,y,1]
"""
out[r,g,b].min_x=x
out[r,g,b].max_x=x
out[r,g,b].min_y=y
out[r,g,b].max_y=y
out[r,g,b]。计数=1
"""
其他:
如果输出[r,g,b]。最小值x>=x:
out[r,g,b].min_x=x
如果输出[r,g,b]。最大值x=y:
out[r,g,b].min_y=y

如果是[r,g,b].max_y是的,我怀疑字典是造成这种情况的原因,但它们太方便了,以至于我几乎不忍心与它们分开。无论如何,多亏了你的提示,我才在1,3秒内成功地运行了这个方法。非常成功!非常感谢。1.3秒在Cython???它似乎没有做很多编译。