[racket] Fast array access using Racket FFI
The thing that I couldn't write properly in that format is that I
don't remember whether there was some way to get a Racket bytestring
object that wraps a block of external memory, which means that you can
use Racket functions to manipulate it. Seems that you found it now...
A few minutes ago, Petr Samarin wrote:
> 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
--
((lambda (x) (x x)) (lambda (x) (x x))) Eli Barzilay:
http://barzilay.org/ Maze is Life!