Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fast array access using Racket FFI

Tags:

opencv

racket

ffi

I am trying to write OpenCV FFI in Racket and arrived at a point where arrays need to be manipulated efficiently. However, all my attempts to access arrays by using Racket FFI resulted in very inefficient code. Is there a way for fast access of C arrays using FFI?

In Racket, this type of manipulation is reasonably fast, i.e.:

(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

Now, in OpenCV, there is a struct called IplImage that looks like this:

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

The struct is defined in Racket as follows:

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

Now we load an image using cvLoadImage function as follows:

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

The pointer imageData can be accessed by: (define data (IplImage-imageData img)))

Now, we want to manipulate data, and the most efficient way I could come up with was by using pointers:

(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

This is very slow, compared to the speed of native Racket vectors. I also tried other ways, such as _array, _cvector that don't even come close to the speed of using pointers, except for writing a first-class function in C that gets a function for running over the whole array. This C function is compiled to a library and bound in Racket using FFI. Then, Racket procedures can be passed to it and applied to all elements of the array. The speed was the same as with pointers, but still not sufficient to continue porting OpenCV library to Racket.

Is there a better way to do this?

like image 907
Peter Samarin Avatar asked May 08 '12 00:05

Peter Samarin


2 Answers

I tried the approach suggested by Eli and it worked out! The idea is to use a bytestring. Since in this case the size of the array is known, (make-sized-byte-string cptr length) can be used:

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

This results in run times close to Racket's native vectors:

(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

Thank you, Eli.

like image 152
Peter Samarin Avatar answered Oct 14 '22 13:10

Peter Samarin


It would probably be better to set the whole thing using a bytestring (via _bytes), but that's a very rough guess. It would be much better to ask this question on the mailing list...

like image 38
Eli Barzilay Avatar answered Oct 14 '22 13:10

Eli Barzilay