问题
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.
回答1:
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.
回答2:
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)
回答3:
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 defcfun
s.
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.
来源:https://stackoverflow.com/questions/19133763/how-do-i-write-a-comparable-cffitranslate-into-foreign-defmethod-for-this-cffi