I tried the approach suggested by Eli on Stackoverflow, 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.
On May 8, 2012, at 3:34 PM, Petr Samarin wrote:
> Hello everyone,
>
> 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?
>
> Regards,
> Petr
____________________
Racket Users list:
http://lists.racket-lang.org/users