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

Reply via email to