1

I have an numpy array of uint8, that I would like to pass as a void const pointer to a C++ library through SWIG.

I use this python code:

print(arr.ctypes.data_as(ctypes.c_void_p))
display.GetImageDisplay().SetImage(arr.ctypes.data_as(ctypes.c_void_p),arr.shape[0],arr.shape[1],1,1)
display.GetImageDisplay().Update()

To call the SetImage method of the ImageDisplay class, in the mvIMPACT aquire library:

void SetImage   (   const void *    pData,
        int     width,
        int     height,
        int     bitsPerPixel,
        int     pitch 
    )   

I get the following error:

c_void_p(193598576)
Traceback (most recent call last):
  File ".\mvIMPACT test.py", line 137, in <module>
    display.GetImageDisplay().SetImage(arr.ctypes.data_as(ctypes.c_void_p),arr.shape[0],arr.shape[1],1,1)
  File "C:\Program Files (x86)\Python37-32\lib\site-packages\mvIMPACT\acquire.py", line 7101, in SetImage
    def SetImage(self, *args): return lib_mvIMPACT_acquire.ImageDisplay_SetImage(self, *args)
TypeError: in method 'ImageDisplay_SetImage', argument 2 of type 'void const *'

I think the type of pData is wrong. I checked the SWIG wrapper, and I'm quite sure I'm calling the correct function, and the first type matches

SWIGINTERN PyObject *_wrap_ImageDisplay_SetImage__SWIG_0(PyObject *SWIGUNUSEDPARM(self), int nobjs, PyObject **swig_obj) {
  PyObject *resultobj = 0; mvIMPACT::acquire::display::ImageDisplay *arg1 = (mvIMPACT::acquire::display::ImageDisplay *) 0 ;
  void *arg2 = (void *) 0 ; int arg3 ; int arg4 ; int arg5 ; int arg6 ; void *argp1 = 0 ; int res1 = 0 ; int res2 ; int val3 ;
  int ecode3 = 0 ; int val4 ; int ecode4 = 0 ; int val5 ; int ecode5 = 0 ; int val6 ; int ecode6 = 0 ;
  if ((nobjs < 6) || (nobjs > 6)) SWIG_fail;
  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_mvIMPACT__acquire__display__ImageDisplay, 0 |  0 );
  if (!SWIG_IsOK(res1)) {
    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ImageDisplay_SetImage" "', argument " "1"" of type '" "mvIMPACT::acquire::display::ImageDisplay *""'");  }
   arg1 = reinterpret_cast< mvIMPACT::acquire::display::ImageDisplay * >(argp1);
  res2 = SWIG_ConvertPtr(swig_obj[1],SWIG_as_voidptrptr(&arg2), 0, 0); if (!SWIG_IsOK(res2)) {
    SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "ImageDisplay_SetImage" "', argument " "2"" of type '" "void const *""'");  }
   ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); if (!SWIG_IsOK(ecode3)) {
    SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "ImageDisplay_SetImage" "', argument " "3"" of type '" "int""'"); }
    arg3 = static_cast< int >(val3); ecode4 = SWIG_AsVal_int(swig_obj[3], &val4); if (!SWIG_IsOK(ecode4)) {
    SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "ImageDisplay_SetImage" "', argument " "4"" of type '" "int""'"); }
    arg4 = static_cast< int >(val4); ecode5 = SWIG_AsVal_int(swig_obj[4], &val5); if (!SWIG_IsOK(ecode5)) {
    SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "ImageDisplay_SetImage" "', argument " "5"" of type '" "int""'"); }
    arg5 = static_cast< int >(val5); ecode6 = SWIG_AsVal_int(swig_obj[5], &val6); if (!SWIG_IsOK(ecode6)) {
    SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "ImageDisplay_SetImage" "', argument " "6"" of type '" "int""'"); }
    arg6 = static_cast< int >(val6); { try { { SWIG_PYTHON_THREAD_BEGIN_ALLOW;
        (arg1)->SetImage((void const *)arg2,arg3,arg4,arg5,arg6); SWIG_PYTHON_THREAD_END_ALLOW; }  }     
    catch(const mvIMPACT::acquire::ImpactAcquireException &e) { handleImpactAcquireException(e); SWIG_fail; }  
    catch(const std::exception &e) { SWIG_SetErrorMsg(PyExc_RuntimeError, e.what()); SWIG_fail; }  catch(...) {
      SWIG_SetErrorMsg(PyExc_RuntimeError, "unknown C++ exception"); SWIG_fail; }  }  resultobj = SWIG_Py_Void();
  return resultobj; fail: return NULL; }
  1. Am I running into issues because my pointer is not constant?
  2. Can I make a constant pointer with ctypes?
  3. Do I need to make a copy of my numpy array with another library? I know that the C++ code is not using the array once I return form that call.
pserra
  • 206
  • 1
  • 11
  • 1
    I would recommend that you consider not using void pointers. SWIG is an interface generator. The developers of NumPy have made it quite easy to handle array inputs and outputs, see e.g. https://github.com/numpy/numpy/blob/master/tools/swig/numpy.i – Jens Munk May 22 '19 at 15:49
  • @JensMunk Interesting link, thanks. However, if I don't use void pointers, then do I need to modify the SDK code? The SWIG layer is also provided as part of the SDK. – pserra May 22 '19 at 16:12
  • I am not familiar with `mvIMPACT`, but my guess is that `SetImage` is already wrapped and exported in their SWIG layer. If the function is an ordinary public function, you could find the symbol and call it using ctypes. Any exported function can be called, see the many examples of how to use ctypes – Jens Munk May 22 '19 at 16:19
  • @Ok, my problem is that cannot find a way to get a const void * with ctypes. That, or another type miss-match issue. – pserra May 22 '19 at 17:44
  • That is not your issue. Even without const, it will not work. SWIG wraps it to a function taking void* not ctypes.c_voidp. Try to make a simple complete example – Jens Munk May 22 '19 at 18:00
  • I really don't want to change the SDK. I could make an example and write my own C code, but I'm not sure how that helps me with this issue. – pserra May 23 '19 at 00:44
  • 1
    It think this question belongs in an mvIMPACT forum. A small example will show that swig wraps the function such that you cannot use ctypes.c_voidp. If you instead use ctypes.cdll.LoadLibrary('xxx.so'), you can call the function like you do, but you need to know the mangled name of SetImage if C++ – Jens Munk May 23 '19 at 05:29

1 Answers1

1

I have made a small example demonstrating why you cannot use ctypes.c_void_p together with a function wrapped with SWIG and provided two alternative solutions.

The example is given for a C library - for C++ you need to find the mangled names for the first part.

test.h

#pragma once

void SetImage(const void* pData,
              int width,
              int height,
              int hitsPerPixel,
              int pitch);

test.c

#include "test.h"

#include <stdio.h>

void SetImage(const void* pData,
              int width,
              int height,
              int hitsPerPixel,
              int pitch) {
  double* _pData = (double*)pData;
  printf("%f\n",_pData[0]);
}

To compile

swig3.0 -python -includeall -Wall test.i
gcc -shared -o _example.so test.c test_wrap.c -Wall -Wextra -fPIC -I/usr/include/python2.7/ -DSWIGRUNTIME=extern

In Python, you can do the following

import ctypes
import example
import numpy as np

arr = np.ones(10,dtype=np.float64)

arg = arr.ctypes.data_as(ctypes.c_void_p)

# Using the C functions
libexample = ctypes.cdll.LoadLibrary("_example.so")

libexample.SetImage(arg, 0,0,0,0)

n = 20
a = example.doubleArray(n)
a[0] = 37.0

# Using the SWIG wrapping
example.SetImage(a,0,0,0,0)
Jens Munk
  • 4,627
  • 1
  • 25
  • 40