使用Racket FFI进行快速阵列访问

时间:2012-05-08 00:43:30

标签: opencv racket ffi

我正在尝试在Racket中编写OpenCV FFI,并且到达了需要高效操作数组的点。但是,我使用Racket FFI访问数组的所有尝试都导致代码效率非常低。有没有办法使用FFI快速访问C数组?

在Racket中,这种类型的操作速度相当快,即:

(define a-vector (make-vector (* 640 480 3)))
(time (let loop ([i (- (* 640 480 3) 1)])
    (when (>= i 0)
      ;; invert each pixel channel-wise
      (vector-set! a-vector i (- 255 (vector-ref a-vector i)))
      (loop (- i 1)))))
->  cpu time: 14 real time: 14 gc time: 0

现在,在OpenCV中,有一个名为IplImage的结构,如下所示:

typedef struct _IplImage
{
    int  imageSize;             /* sizeof(IplImage) */
    ...
    char *imageData;        /* Pointer to aligned image data.*/
}IplImage;

结构在Racket中定义如下:

(define-cstruct _IplImage
    ([imageSize _int]
     ...
     [imageData _pointer]))

现在我们使用cvLoadImage函数加载图像,如下所示:

(define img
  (ptr-ref
   (cvLoadImage "images/test-image.png" CV_LOAD_IMAGE_COLOR)
   _IplImage))

指针imageData可以通过以下方式访问:(define data (IplImage-imageData img)))

现在,我们想要操纵data,我能想到的最有效的方法是使用指针:

(time (let loop ([i (- (* width height channels) 1)]) ;; same 640 480 3
    (when (>= i 0)
      ;; invert each pixel channel-wise
      (ptr-set! data _ubyte i (- 255 (ptr-ref data _ubyte i)))
      (loop (- i 1)))))
-> cpu time: 114 real time: 113 gc time: 0

与本机Racket向量的速度相比,这非常慢。 我还尝试了其他方法,例如_array_cvector甚至没有接近使用指针的速度,除了在C中编写一个获得运行函数的第一类函数整个阵列。此C函数被编译为库并使用FFI绑定在Racket中。然后,可以将Racket过程传递给它并应用于数组的所有元素。速度与指针相同,但仍然不足以继续将OpenCV库移植到Racket。

有更好的方法吗?

2 个答案:

答案 0 :(得分:4)

我尝试了Eli建议的方法并且它成功了!想法是使用bytestring。由于在这种情况下,数组的大小是已知的,因此可以使用(make-sized-byte-string cptr length)

(define data (make-sized-byte-string (IplImage-imageData img)
                                     (* width height channels)))

这导致运行时间接近Racket的原生载体:

(time (let loop ([i (- (* 640 480 3) 1)])
    (when (>= i 0)
      ;; invert each pixel channel-wise
      (bytes-set! data i (- 255 (bytes-ref data i)))
      (loop (- i 1)))))
-> cpu time: 18 real time: 18 gc time: 0

谢谢你,Eli。

答案 1 :(得分:2)

使用bytestring(通过_bytes)设置整个事情可能会更好,但这是一个非常粗略的猜测。在邮件列表上提出这个问题要好得多......