pthom / cvnp

cvnp: pybind11 casts between numpy and OpenCV, with shared memory

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Undefined symbol: _ZTIN2cv12MatAllocatorE

justincdavis opened this issue · comments

I am attempting to use cvnp to handle the type casting for numpy arrays and cv::Mat for a project. I have the following directory structure:

  • project_dir
    • cpp (This handles all my source code and CMakeLists.txt for installing as a library)
    • python (This handles creating bindings for the cpp folder)
    • extern
      • cvnp
      • pybind11
    • CMakeLists.txt

When I build my project everything succeeds with the following warning:

/home/jcdavis/pteranodon/extern/pybind11/include/pybind11/cast.h:38:7: note: type ‘struct type_caster’ itself violates the C++ One Definition Rule
38 | class type_caster : public type_caster_base {};
| ^
/home/jcdavis/pteranodon/extern/cvnp/cvnp/cvnp.h:137:16: note: the incompatible type is defined here
137 | struct type_castercv::Mat
| ^
make[3]: Leaving directory '/home/jcdavis/pteranodon/build_ext'

Once I install my project and attempt to import in python I get the following error:

import pteranodon_ext
Traceback (most recent call last):
File "", line 1, in
ImportError: /home/jcdavis/.local/lib/python3.8/site-packages/pteranodon_ext.cpython-38-x86_64-linux-gnu.so: undefined symbol: _ZTIN2cv12MatAllocatorE

Here is my CMakeLists.txt for reference:

cmake_minimum_required(VERSION 3.4)
project(pteranodon_ext VERSION 0.1.0)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

if(NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE Release)
endif()

set(CMAKE_CXX_FLAGS "-O3")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/cpp/include/headers)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/python)

add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/extern/pybind11)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/extern/cvnp)

find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})

file (GLOB_RECURSE SOURCE_FILES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/cpp/src/*.cpp)
file (GLOB_RECURSE HEADER_FILES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/cpp/include/headers/*.hpp)
file (GLOB_RECURSE PYTHON_FILES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/python/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/python/*.hpp)

pybind11_add_module(pteranodon_ext 
	${SOURCE_FILES}
	${HEADER_FILES}
	${PYTHON_FILES}
)

target_link_libraries(pteranodon_ext PUBLIC ${OPENCV_LIBS} PRIVATE cvnp pybind11::pybind11)

find_package(PythonInterp 3 REQUIRED)
find_package(PythonLibs 3 REQUIRED)

exec_program(${PYTHON_EXECUTABLE}
             ARGS "-c \"import sysconfig; print(sysconfig.get_paths()['purelib'])\""
             OUTPUT_VARIABLE PYTHON_LIBRARY_DIR
             RETURN_VALUE PYTHON_LIBRARY_DIR_NOT_FOUND
            )
if(PYTHON_LIBRARY_DIR_NOT_FOUND)
    message(FATAL_ERROR "Python library directory not found")
endif()

install(TARGETS pteranodon_ext
  COMPONENT python
  LIBRARY DESTINATION "${PYTHON_LIBRARY_DIR}"
)

My typical build process is as follows:

mkdir -p build && cd build && cmake -S ../ -B ./ && make
cd build && make install

I am wondering if there are any insights on this issues, thank you!

Hi,

When I build my project everything succeeds with the following warning:
/home/jcdavis/pteranodon/extern/pybind11/include/pybind11/cast.h:38:7: note: type ‘struct type_caster’ itself violates the C++ One Definition Rule
38 | class type_caster : public type_caster_base {};

May be look at pybind/pybind11#1055 this seems to have helped several people.

import pteranodon_ext
Traceback (most recent call last):
File "", line 1, in
ImportError: /home/jcdavis/.local/lib/python3.8/site-packages/pteranodon_ext.cpython-38-x86_64-linux-gnu.so: > undefined symbol: _ZTIN2cv12MatAllocatorE

_ZTIN2cv12MatAllocatorE when demangled (for example with demangler.com) is equivalent to typeinfo for cv::MatAllocator.

This SO asnwer provides some possible reasons:

  • virtual functions are missing bodies
  • attempting to use typeid on an object of a class that has no virtual functions: make the destructor virtual.
  • happen when you mix -fno-rtti and -frtti code
  • Linking order matters. If you list the libraries you want to link in an order which is incorrect you can get the typeinfo error. on some platforms you have to list libraries twice

Thank you for your response!

I resolved the ODR issue fairly quickly with the issues you linked, just had to include the cvnp header wherever I include pybind.

As for the typeinfo for cv::MatAllocator, I have not been able to resolve the issue there. All of my code has no virtual functions/methods. I made the destructor for the only class I use virtual and it also did not affect anything.

I believe the flags are also not an issue.

That leaves the linking order of the librarys. I have tried multiple permutations, but none have worked. Would you be able to provide any insight on the linking order when using cvnp? To me it seems most logical to link opencv, pybind11, cvnp but that ordering does not work.

Maybe you should try to link opencv twice : once at the start and once at the end

I ended up solving the issue and it appears it was not a "target_link_libraries" issue, but most likely from an error in the linking of directories or not including the numpy directories. Here is the final CMakeLists.txt I have:

cmake_minimum_required(VERSION 3.4)
project(pteranodon_ext VERSION 0.1.0)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

include(CTest)
enable_testing()

if(NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE Release)
endif()

set(CMAKE_CXX_FLAGS "-O3 -Wall")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/cpp/include/headers)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/python)

add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/extern/pybind11)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/extern/cvnp)

set(NUMPY_INCLUDE_DIR "" CACHE FILEPATH "Path to numpy header if cmake can't find them.")
if (NOT ${NUMPY_INCLUDE_DIR} STREQUAL "")
  message( " *** NUMPY_INCLUDE_DIR : ${NUMPY_INCLUDE_DIR}" )
  if(NOT EXISTS ${NUMPY_INCLUDE_DIR}/numpy/ndarrayobject.h)
    message(SEND_ERROR "Can't find numpy/ndarrayobject.h in ${NUMPY_INCLUDE_DIR}")
    endif()
  include_directories(${NUMPY_INCLUDE_DIR})
endif()

find_package(PythonInterp 3 REQUIRED)
find_package(PythonLibs 3 REQUIRED)
find_package(OpenCV REQUIRED)

include_directories(${pybind11_INCLUDE_DIR})
include_directories(${PYTHON_INCLUDE_DIRS})
include_directories(${OpenCV_INCLUDE_DIRS})
include_directories(${OpenCV_INCLUDE_DIRS}/opencv4)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})

file (GLOB_RECURSE SOURCE_FILES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/cpp/src/*.cpp)
file (GLOB_RECURSE HEADER_FILES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/cpp/include/headers/*.hpp)
file (GLOB_RECURSE PYTHON_FILES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/python/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/python/*.hpp)

LINK_DIRECTORIES(
  ${OpenCV_LIB_DIR}
)

pybind11_add_module(pteranodon_ext MODULE
	${SOURCE_FILES}
	${HEADER_FILES}
	${PYTHON_FILES}
)

target_link_libraries(pteranodon_ext PRIVATE cvnp pybind11::pybind11 ${OpenCV_LIBS})

exec_program(${PYTHON_EXECUTABLE}
             ARGS "-c \"import sysconfig; print(sysconfig.get_paths()['purelib'])\""
             OUTPUT_VARIABLE PYTHON_LIBRARY_DIR
             RETURN_VALUE PYTHON_LIBRARY_DIR_NOT_FOUND
            )
if(PYTHON_LIBRARY_DIR_NOT_FOUND)
    message(FATAL_ERROR "Python library directory not found")
endif()

install(TARGETS pteranodon_ext
  COMPONENT python
  RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
  LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
  ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}")

add_test(NAME pteranodon_ext_test
  COMMAND ${PYTHON_EXECUTABLE} -c "import pteranodon_ext"
)