Python转Cython编译错误:assert len(cubemap.shape)==3报错
问题
将Python代码转为Cython以提升性能时,编译阶段在assert len(cubemap.shape) == 3语句处报错,错误提示为Cannot convert 'npy_intp *' to Python object。
完整Cython代码
import numpy from . import utils cimport numpy cimport cython cdef bytes mode_bilinear = b'bilinear' cdef bytes mode_nearest = b'nearest' cdef bytes cube_format_horizon = b'horizon' cdef bytes cube_format_list = b'list' cdef bytes cube_format_dict = b'dict' cdef bytes cube_format_dice = b'dice' @cython.boundscheck(False) @cython.wraparound(False) def c2e(numpy.ndarray[numpy.float64_t, ndim=3] cubemap, int h, int w, bytes mode=mode_bilinear, bytes cube_format=cube_format_dice): cdef int order if mode == mode_bilinear: order = 1 elif mode == mode_nearest: order = 0 else: raise NotImplementedError('unknown mode') if cube_format == cube_format_horizon: pass elif cube_format == cube_format_list: cubemap = utils.cube_list2h(cubemap) elif cube_format == cube_format_dict: cubemap = utils.cube_dict2h(cubemap) elif cube_format == cube_format_dice: cubemap = utils.cube_dice2h(cubemap) else: raise NotImplementedError('unknown cube_format') assert len(cubemap.shape) == 3 assert cubemap.shape[0] * 6 == cubemap.shape[1] assert w % 8 == 0 cdef int face_w = cubemap.shape[0] cdef numpy.ndarray[numpy.float64_t, ndim=2] uv = utils.equirect_uvgrid(h, w) cdef numpy.ndarray[numpy.float64_t, ndim=2] u = uv[:, 0] cdef numpy.ndarray[numpy.float64_t, ndim=2] v = uv[:, 1] cdef numpy.ndarray[numpy.float64_t, ndim=3] cube_faces = numpy.stack(numpy.split(cubemap, 6, 1), 0) # Get face id to each pixel: 0F 1R 2B 3L 4U 5D cdef numpy.ndarray[numpy.int32_t, ndim=2] tp = utils.equirect_facetype(h, w) cdef numpy.ndarray[numpy.float64_t, ndim=2] coor_x = numpy.zeros((h, w)) cdef numpy.ndarray[numpy.float64_t, ndim=2] coor_y = numpy.zeros((h, w)) for i in range(4): mask = (tp == i) coor_x[mask] = 0.5 * numpy.tan(u[mask] - numpy.pi * i / 2) coor_y[mask] = -0.5 * numpy.tan(v[mask]) / numpy.cos(u[mask] - numpy.pi * i / 2) mask = (tp == 4) cdef numpy.ndarray[numpy.float64_t, ndim=2] c = 0.5 * numpy.tan(numpy.pi / 2 - v[mask]) coor_x[mask] = c * numpy.sin(u[mask]) coor_y[mask] = c * numpy.cos(u[mask]) mask = (tp == 5) c = 0.5 * numpy.tan(numpy.pi / 2 - numpy.abs(v[mask])) coor_x[mask] = c * numpy.sin(u[mask]) coor_y[mask] = -c * numpy.cos(u[mask]) # Final renormalize coor_x = (numpy.clip(coor_x, -0.5, 0.5) + 0.5) * face_w coor_y = (numpy.clip(coor_y, -0.5, 0.5) + 0.5) * face_w cdef numpy.ndarray[numpy.float64_t, ndim=3] equirec = numpy.stack([ utils.sample_cubefaces(cube_faces[..., i], tp, coor_y, coor_x, order=order) for i in range(cube_faces.shape[2]) ], axis=-1) return equirec
编译错误信息
Compiling utils.pyx because it changed. Compiling c2e.pyx because it changed. [1/2] Cythonizing c2e.pyx C:\Users\Ju-Bei\AppData\Local\Programs\Python\Python311\Lib\site-packages\Cython\Compiler\Main.py:384: FutureWarning: Cython directive 'language_level' not set, using '3str' for now (Py3). This has changed from earlier releases! File: C:\Users\Ju-Bei\Documents\Hyper Render 360\Hyper 360 Convert\fullcython\c2e.pyx tree = Parsing.p_module(s, pxd, full_module_name) Error compiling Cython file: ------------------------------------------------------------ ... elif cube_format == cube_format_dice: cubemap = utils.cube_dice2h(cubemap) else: raise NotImplementedError('unknown cube_format') assert len(cubemap.shape) == 3 ^ ------------------------------------------------------------ c2e.pyx:37:22: Cannot convert 'npy_intp *' to Python object Traceback (most recent call last): File "C:\Users\Ju-Bei\Documents\Hyper Render 360\Hyper 360 Convert\fullcython\setup.py", line 5, in <module> ext_modules = cythonize([ ^^^^^^^^^^^ File "C:\Users\Ju-Bei\AppData\Local\Programs\Python\Python311\Lib\site-packages\Cython\Build\Dependencies.py", line 1134, in cythonize cythonize_one(*args) File "C:\Users\Ju-Bei\AppData\Local\Programs\Python\Python311\Lib\site-packages\Cython\Build\Dependencies.py", line 1301, in cythonize_one raise CompileError(None, pyx_file) Cython.Compiler.Errors.CompileError: c2e.pyx
解决方案
错误原因
函数参数cubemap最初被声明为静态类型的numpy.ndarray[numpy.float64_t, ndim=3],但当通过utils.cube_list2h等函数重新赋值后,Cython会丢失其静态类型信息,将它视为普通Python对象。此时访问cubemap.shape返回的是C语言层面的npy_intp*指针,而非Python的tuple对象,因此无法直接用len()函数处理。
修改方法
有两种简洁的修复方式:
方式一:使用ndim属性替代len(cubemap.shape)
ndim是numpy数组的Python属性,Cython能正确识别并处理,直接用它检查维度数量:
# 替换原assert语句 assert cubemap.ndim == 3 assert cubemap.shape[0] * 6 == cubemap.shape[1]
方式二:将shape转换为Python tuple
通过numpy.shape()函数将数组的shape转为Python tuple,再用len()检查:
# 替换原assert语句 assert len(numpy.shape(cubemap)) == 3 assert cubemap.shape[0] * 6 == cubemap.shape[1]
修改后的关键代码片段
if cube_format == cube_format_horizon: pass elif cube_format == cube_format_list: cubemap = utils.cube_list2h(cubemap) elif cube_format == cube_format_dict: cubemap = utils.cube_dict2h(cubemap) elif cube_format == cube_format_dice: cubemap = utils.cube_dice2h(cubemap) else: raise NotImplementedError('unknown cube_format') # 修改后的assert语句 assert cubemap.ndim == 3 assert cubemap.shape[0] * 6 == cubemap.shape[1] assert w % 8 == 0 cdef int face_w = cubemap.shape[0]
内容的提问来源于stack exchange,提问作者Gabriel Garcia
相关产品推荐
相关产品推荐

