How do i write a comparable cffi:translate-into fo

2019-08-15 00:35发布

ok I tried this translate-from-foreign method and it did work I have these defined in my structs.lisp file in my library which loads first before all my other dependencies

(cffi:defcstruct (cv-size :class cv-size-type)
  (width :int)
  (height :int))

(defmethod cffi:translate-from-foreign (p (type cv-size-type))
  (let ((plist (call-next-method)))
    (make-size :width (getf plist 'width)
               :height (getf plist 'height))))

and my opencv wrappers for CvGetSize and cvCreateImage, get-size and create-image, are defined like this

;; CvSize cvGetSize(const CvArr* arr)
 (cffi:defcfun ("cvGetSize" get-size) (:struct cv-size)
   (arr cv-arr))

 ;; IplImage* cvCreateImage(CvSize size, int depth, int channels)
 (cffi:defcfun ("cvCreateImage" %create-image) ipl-image
   (size :int64)
   (depth :int)
   (channels :int))

 (defun create-image (size depth channels)
   "Create an image with dimensions given by SIZE, DEPTH bits per
 channel, and CHANNELS number of channels."
   (let ((nsize (size->int64 size)))
     (%create-image nsize depth channels)))

here is the definition of size->int64

(DEFUN SIZE->INT64 (S) (+ (SIZE-WIDTH S) (ASH (SIZE-HEIGHT S) 32)))

 it converts get-size output which is a structure here:

#S(SIZE :WIDTH 640 :HEIGHT 480)

into 64-bit integer, which CFFI can handle 

but I love the idea of the translate-foreign defmethod's

so I was wondering if you can show my how to make the translate-into-foreign version of the below from method this would really make my library awesome

(defmethod cffi:translate-from-foreign (p (type cv-size-type))
  (let ((plist (call-next-method)))
    (make-size :width (getf plist 'width)
               :height (getf plist 'height))))

I was going to try stuff and add it but for the get-size output structure, it isn't a plist so not really sure what to put there for the

(let ((plist (call-next-method)))

part, for the

  (make-size :width (getf plist 'width)
               :height (getf plist 'height))))

part, I was hoping to find another method other than the size->64 function because that was made 2 years ago when cl-opencv https://github.com/ryepup/cl-opencv first came out and I would like to make an even better wrapper than that... I've already taken cl-opencv added 100 new function 5000 lines of code samples and documentation and a new structs.lisp file so I would love if someone could help me with all the latest cffi tools so I could do something else than int64...plus the if I have a function to wrap where the int64 thing wouldn't work ill be ready

Thanks again to all the answerers on S.O. you all really have helped my library great.

Edit

Ok I think I defined everthing as you Mr Madeira as below (i show the repl session)

 CL-OPENCV> 
 ;; (cffi:foreign-type-size '(:struct cv-size)) = 8
 (cffi:defcstruct (cv-size :class cv-size-type)
   (width :int)
   (height :int))



 (defmethod cffi:translate-from-foreign (p (type cv-size-type))
   (let ((plist (call-next-method)))
     (make-size :width (getf plist 'width)
                :height (getf plist 'height))))

 (defmethod cffi:translate-to-foreign (value (type cv-size-type))
   (let ((plist ()))
     (setf (getf plist 'width) (size-width value)
           (getf plist 'height) (size-height value))
     (call-next-method plist type)))



 ;; CvSize cvGetSize(const CvArr* arr)
 (cffi:defcfun ("cvGetSize" get-size) (:struct cv-size)
   (arr (:pointer cv-arr)))


 ;; IplImage* cvCreateImage(CvSize size, int depth, int channels)
 (cffi:defcfun ("cvCreateImage" create-image) (:pointer (:struct  ipl-image))
   (size (:struct cv-size))
   (depth :int)
   (channels :int))


 STYLE-WARNING: redefining CL-OPENCV:GET-SIZE in DEFUN
 STYLE-WARNING: redefining CL-OPENCV:CREATE-IMAGE in DEFUN
 CREATE-IMAGE
 CL-OPENCV> (defparameter capture (create-camera-capture 0))
 (defparameter frame (query-frame capture))
 (defparameter img-size (get-size frame))
 (defparameter img (create-image img-size +ipl-depth-8u+ 3))

but I get error

There is no applicable method for the generic function
  #<STANDARD-GENERIC-FUNCTION
    CFFI:TRANSLATE-INTO-FOREIGN-MEMORY (5)>
when called with arguments
  (#S(SIZE :WIDTH 640 :HEIGHT 480) #<CV-SIZE-TYPE CV-SIZE>
   #.(SB-SYS:INT-SAP #X7FFFE5427FF0)).
   [Condition of type SIMPLE-ERROR]

because the translate-from-foreign function I have is converting the output from cv-size into a structure

CL-OPENCV> img-size
#S(SIZE :WIDTH 640 :HEIGHT 480)

I appreciate the translate-into foreign function but with the old translate-from-foreign function it isn't woking, because of the make-size part...would you help me figure out what cvCreateImage needs to satisfy it ....here is the link 4 that:

http://docs.opencv.org/modules/core/doc/old_basic_structures.html?highlight=eimage#createimage

I can get this version below to run right(i show the repl session)

 5
 CL-OPENCV> ; TODO SIZE-WIDTH AND HEIGHT
 ;; CvSize cvGetSize(const CvArr* arr)
 (cffi:defcfun ("cvGetSize" get-size) (:pointer (:struct cv-size))
   (arr cv-arr))


 ;; IplImage* cvCreateImage(CvSize size, int depth, int channels)
 (cffi:defcfun ("cvCreateImage" create-image) (:pointer (:struct ipl-image))
   (size (:pointer (:struct cv-size)))
   (depth :int)
   (channels :int))

 STYLE-WARNING: redefining CL-OPENCV:GET-SIZE in DEFUN
 STYLE-WARNING: redefining CL-OPENCV:CREATE-IMAGE in DEFUN
 CREATE-IMAGE
 CL-OPENCV> (defparameter capture (create-camera-capture 0))
 (defparameter frame (query-frame capture))
 (defparameter img-size (get-size frame))
 (defparameter img (create-image img-size +ipl-depth-8u+ 3))
 IMG
 CL-OPENCV> (cffi:with-foreign-slots ((n-size id n-channels 
                           alpha-channel depth color-model 
                           channel-seq data-order origin  
                           align width height roi 
                           mask-roi image-id tile-info 
                           image-size image-data width-step 
                           border-mode border-const image-data-origin) 

                           img(:struct ipl-image))
                           (format t "n-size = ~a~%id = ~a~%n-channels = ~a~%alpha-channel = ~a~%depth = ~a~%color-model = ~a~%channel-seq = ~a~%data-order = ~a~%origin = ~a~%align = ~a~%width = ~a~%height = ~a~%roi = ~a~%mask-roi = ~a~%image-id = ~a~%tile-info = ~a~%image-size = ~a~%image-data = ~a~%width-step = ~a~%border-mode = ~a~%border-const = ~a~%image-data-origin = ~a~%" 
                           n-size id n-channels 
                           alpha-channel depth color-model 
                           channel-seq data-order origin  
                           align width height roi 
                           mask-rOI image-id tile-info 
                           image-size image-data width-step 
                           border-mode border-const image-data-origin))
 n-size = 144
 id = 0
 n-channels = 3
 alpha-channel = 0
 depth = 8
 color-model = 4343634
 channel-seq = 5392194
 data-order = 0
 origin = 0
 align = 4
 width = 640
 height = 480
 roi = #.(SB-SYS:INT-SAP #X00000000)
 mask-roi = #.(SB-SYS:INT-SAP #X00000000)
 image-id = #.(SB-SYS:INT-SAP #X00000000)
 tile-info = #.(SB-SYS:INT-SAP #X00000000)
 image-size = 921600
 image-data = 
 width-step = 1920
 border-mode = #.(SB-SYS:INT-SAP #X00000000)
 border-const = #.(SB-SYS:INT-SAP #X00000000)
 image-data-origin = NIL
 NIL

so I get data from the slots for ipl-image but this does'nt seem like correct way because id have to be able to derefrence the cv-size poiner output by get-size

here is documentation on cvGetSize the function im wrapping

http://docs.opencv.org/modules/core/doc/old_basic_structures.html?highlight=eimage#getsize

as u can see it is a pointer

CL-OPENCV> img-size
#.(SB-SYS:INT-SAP #X1E000000280)

so when I do :

  (cffi:with-foreign-object (img-size '(:pointer (:struct cv-size)))
            ;; Initialize the slots

            ;; Return a list with the coordinates
            (cffi:with-foreign-slots ((width height) img-size 

              (list width height)))

I get

 There is no applicable method for the generic function
   #<STANDARD-GENERIC-FUNCTION CFFI::SLOTS (1)>
 when called with arguments
   (#<CFFI::FOREIGN-POINTER-TYPE (:POINTER (:STRUCT CV-SIZE))>).
    [Condition of type SIMPLE-ERROR]     

and when I do

 (cffi:with-foreign-object (img-size '(:struct cv-size))
      ;; Initialize the slots

      ;; Return a list with the coordinates
      (cffi:with-foreign-slots ((width height) img-size (:struct cv-size))
        (list width height)))

I get

(346539 0)

just nonsensical output

I try mem-refing and mem-arefing the pointer and get unhandled memory fault errors

if you can help me figure out how to write compatible

translate-from-foreign

and

translate-into-foreign functions I would be very grateful =).

but if I use make-size or size-width,height anywhere in them the create-image would have to have the size->int64 in it because they work only because that function.

3条回答
劫难
2楼-- · 2019-08-15 01:08

This should be the exact reverse definition to your translate-from-foreign method definition. I can't test it right now, but you might want to try if it works:

(defmethod cffi:translate-to-foreign (value (type cv-size-type))
  (let ((plist ()))
    (setf (getf plist 'width) (size-width value)
          (getf plist 'height) (size-height value))
    (call-next-method plist type)))

As the other answer correctly points out, you definitely must change the type for size in the defcfun from :int64 to (:struct cv-size), otherwise this method won't be called.

查看更多
Ridiculous、
3楼-- · 2019-08-15 01:19

Update 2016-10-12

Here is a demo much better and really simple!

You just have to add :class xxx to cffi:defcstruct then (cffi:defmethod translate-into-foreign-memory (object (type xxx) pointer) yyyy), it will pass structure by value to a foreign function automatically!! Amazing!!

And (cffi:defmethod translate-from-foreign (pointer (type xxx)) zzzz) will convert the returned structure data into lisp data.

OK, here is the code:

(defcstruct (%CvSize :class cv-size)
  (width :int)
  (height :int))
(defmethod translate-into-foreign-memory (object (type cv-size) pointer)
  (with-foreign-slots ((width height) pointer (:struct %CvSize))
    ;; After this declare this method, you could just pass a two member
    ;;   list as a (:struct %CvSize)
    (setf width (nth 0 object))
    (setf height (nth 1 object))))
(defmethod translate-from-foreign (pointer (type cv-size))
  (with-foreign-slots ((width height) pointer (:struct %CvSize))
    ;; You can change this and get return value in other format
    ;; for example: (values width height)
    (list width height)))
(defcfun ("cvGetSize" %cvGetSize)
    (:struct %CvSize) ;; Here must use (:struct xxx)
  "C: CvSize cvGetSize(const CvArr* arr)"
  (arr :pointer))
(defcfun ("cvCreateImage" %cvCreateImage)
    %IplImage
  "C: IplImage* cvCreateImage(CvSize size, int depth, int channels)"
  (size (:struct %CvSize)) ;; Here must use (:struct xxx)
  (depth %IPL_DEPTH)
  (channels :int))

Test code for %cvGetSize:

(defmacro with-pointer-to-pointer ((var pointer) &body body)
  `(with-foreign-object (,var :pointer)
     (setf (mem-ref ,var :pointer)
           ,pointer)
     (progn ,@body)))
(defun release-image (image)
  (with-pointer-to-pointer (pointer image)
    (%cvReleaseImage pointer)))
(defmacro with-load-image ((var filename &optional (iscolor :%CV_LOAD_IMAGE_COLOR)) &body body)
  "Wrap %cvLoadImage and make sure %cvReleaseImage."
  (let ((result (gensym)))
    `(let ((,var (%cvLoadImage ,filename ,iscolor))
           ,result)
       (unwind-protect
            (setf ,result
                  (multiple-value-list (progn ,@body)))
         (release-image ,var))
       (values-list ,result))))
(defun image-width-height (filename)
  (with-load-image (image filename)
    (%cvGetSize image)))
(image-width-height "/path/to/image.jpg")
;;=>
;; (962 601)

Note: return value is no longer a pointer or something weird, it return a list(you can change the code in (cffi:defmethod translate-from-foreign () xxxx) to make it convert the returned value to other types.

Test code for %cvCreateImage:

(%cvCreateImage (list 480 640)) 

Note: Yeah! Just pass a list and will be automatically convert to (:struct %CvSize) ! That is great, isn't it?!!! No need to use make-instance or other weird code anymore ^_^

Note: of cause you have to cffi:define-foreign-libray and cffi:use-foreign-library first like this:

(cffi:define-foreign-library opencv-highgui
  (:darwin (:or "libopencv_highgui.dylib"))
  (:linux (:or "libhighgui.so"
               "libopencv_highgui.so"))
  (t (:default "libhighgui")))

(cffi:use-foreign-library opencv-highgui)



Old answer below(please ignore this ugly solution!)

Here is the code works fine:

(cffi:define-foreign-type cv-size ()
  ((width :reader width :initarg :width)
   (height :reader height :initarg :height))
  (:actual-type :int64)
  (:simple-parser %cv-size))
(defmethod translate-to-foreign (value (type cv-size))
  (+ (width value)
     (ash (height value) 32)))
(defmethod translate-from-foreign (value (type cv-size))
  (values (- value
             (ash (ash value -32) 32))
          (ash value -32)))
(cffi:defcfun ("cvGetSize" %cvGetSize)
    %cv-size
  "C: CvSize cvGetSize(const CvArr* arr)"
  (arr :pointer))
(cffi:defcfun ("cvCreateImage" %cvCreateImage)
    %IplImage
  "C: IplImage* cvCreateImage(CvSize size, int depth, int channels)"
  (size %cv-size)
  (depth %IPL_DEPTH)
  (channels :int))

Note: (:actual-type :int64) means the actual type of cv-size is :int64(C int64).

Note: (:simple-parser %cv-size) means you can place %cv-size to return-type or parameter-type like :pointer :int do in cffi:defcfun. Please take a look at the declaration %cvGetSize and %cvCreateImage.

Test code for %cvGetSize:

(defmacro with-pointer-to-pointer ((var pointer) &body body)
  `(with-foreign-object (,var :pointer)
     (setf (mem-ref ,var :pointer)
           ,pointer)
     (progn ,@body)))
(defun release-image (image)
  (with-pointer-to-pointer (pointer image)
    (%cvReleaseImage pointer)))
(defmacro with-load-image ((var filename &optional (iscolor :%CV_LOAD_IMAGE_COLOR)) &body body)
  "Wrap %cvLoadImage and make sure %cvReleaseImage."
  (let ((result (gensym)))
    `(let ((,var (%cvLoadImage ,filename ,iscolor))
           ,result)
       (unwind-protect
            (setf ,result
                  (multiple-value-list (progn ,@body)))
         (release-image ,var))
       (values-list ,result))))
(defun image-width-height (filename)
  (with-load-image (image filename)
    (%cvGetSize image)))
(image-width-height "/path/to/image.jpg")
;;=>
;; 962
;; 601

Test code for %cvCreateImage:

(%cvCreateImage (make-instance 'cv-size
                               :width 480
                               :height 640))

Note: of cause you have to cffi:define-foreign-libray and cffi:use-foreign-library first like this:

(cffi:define-foreign-library opencv-highgui
  (:darwin (:or "libopencv_highgui.dylib"))
  (:linux (:or "libhighgui.so"
               "libopencv_highgui.so"))
  (t (:default "libhighgui")))

(cffi:use-foreign-library opencv-highgui)
查看更多
虎瘦雄心在
4楼-- · 2019-08-15 01:19

From what I can see in CFFI's code, you may use the following as a top-level form instead of defining your own translate-from-foreign and translate-into-foreign-memory (or translate-to-foreign):

(cffi:translation-forms-for-class cv-size cv-size-type)

EDIT: Some notes about your defcfuns.

The defcfun for cvGetSize should declare the argument as (:pointer cv-arr), I think. I don't know how cv-arr is declared.

The defcfun for cvCreateImage should pass a (:struct cv-size) instead of :int64. The former is supposed to be correct on all platforms, while the latter may not work where int is not 32-bit, where field alignment makes the struct not be compact and where the total size of the struct may be different from sizeof(int) + sizeof(int).

Still on cvCreateImage, it should return a (:pointer ipl-image), although I'm not sure if structs are handled by value or by pointer when the type is not directly (:struct <name>). Better check this out at the CFFI mailing list, or chatting in #lisp @ irc.freenode.net.

查看更多
登录 后发表回答