Как мне написать сопоставимый cffi:translate-in-иностранный defmethod для этого cffi:translate-from-foreign?
Хорошо, я попробовал этот метод translate-from-foreign, и он сработал. Я определил это в моем файле structs.lisp в моей библиотеке, который загружается первым, прежде чем все мои другие зависимости
(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))))
и мои оболочки opencv для CvGetSize и cvCreateImage, get-size и create-image определены следующим образом
;; 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)))
вот определение размера->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
но мне нравится идея иностранного переводчика
поэтому мне было интересно, если вы покажете мне, как сделать переведенную на иностранную версию приведенную ниже методику from, это действительно сделает мою библиотеку потрясающей
(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))))
Я собирался попробовать что-то добавить и добавить, но для структуры вывода get-size это не список, так что я не совсем уверен, что нужно поместить для
(let ((plist (call-next-method)))
часть, для
(make-size :width (getf plist 'width)
:height (getf plist 'height))))
Часть, я надеялся найти другой метод, кроме функции size->64, потому что это было сделано 2 года назад, когда впервые вышел cl-opencv https://github.com/ryepup/cl-opencv, и я хотел бы сделать даже лучше, чем это... Я уже взял cl-opencv, добавил 100 новых функций, 5000 строк примеров кода и документации, а также новый файл structs.lisp, так что я бы хотел, чтобы кто-нибудь помог мне со всеми последними инструментами cffi так что я мог бы сделать что-то еще, кроме int64... плюс, если у меня есть функция для переноса, где не работает int64, я буду готов
Еще раз спасибо всем ответчикам на SO, вы все очень помогли моей библиотеке.
редактировать
Хорошо, я думаю, что я определил все, что вы, мистер Мадейра, как показано ниже (я показываю сессию реплея)
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))
но я получаю ошибку
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]
потому что функция translate-from-foreign, которую я имею, преобразовывает выходные данные из cv-size в структуру
CL-OPENCV> img-size
#S(SIZE :WIDTH 640 :HEIGHT 480)
Я ценю функцию translate-in foreign, но со старой функцией translate-from-foreign она не работает из-за части make-size... не могли бы вы помочь мне выяснить, что cvCreateImage нужно для ее удовлетворения.... вот ссылка 4, которая:
http://docs.opencv.org/modules/core/doc/old_basic_structures.html?highlight=eimage
Я могу получить эту версию ниже, чтобы работать правильно (я показываю сеанс repl)
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
так что я получаю данные из слотов для ipl-изображения, но это не похоже на правильный путь, потому что id должен иметь возможность разыменовывать вывод указателя cv-size на get-size
here is documentation on cvGetSize the function im wrapping
http://docs.opencv.org/modules/core/doc/old_basic_structures.html?highlight=eimage
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)))
я получил
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]
и когда я делаю
(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)))
я получил
(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
а также
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 ответа
Это должно быть точное обратное определение к вашему translate-from-foreign
определение метода. Я не могу проверить это прямо сейчас, но вы можете попробовать, если это работает:
(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)))
Как правильно указывает другой ответ, вы обязательно должны изменить тип для size
в defcfun
от :int64
в (:struct cv-size)
иначе этот метод не будет вызван.
Обновление 2016-10-12
Вот демо намного лучше и действительно просто!
Вам просто нужно добавить :class xxx
в cffi:defcstruct
затем (cffi:defmethod translate-into-foreign-memory (object (type xxx) pointer) yyyy)
, он автоматически передаст структуру по значению сторонней функции!! Удивительно!!
А также (cffi:defmethod translate-from-foreign (pointer (type xxx)) zzzz)
преобразует возвращенные данные структуры в данные lisp.
ОК, вот код:
(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))
Тестовый код для %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)
Примечание: возвращаемое значение больше не является указателем или чем-то странным, оно возвращает список (вы можете изменить код в (cffi:defmethod translate-from-foreign () xxxx)
чтобы он преобразовал возвращаемое значение в другие типы.
Тестовый код для %cvCreateImage
:
(%cvCreateImage (list 480 640))
Примечание: да! Просто передайте список, и он будет автоматически преобразован в (:struct %CvSize)! Это здорово, не правда ли?!!! Не нужно использовать make-instance
или другой странный код больше ^_^
Примечание: конечно, вы должны cffi:define-foreign-libray
а также cffi:use-foreign-library
сначала вот так:
(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)
Старый ответ ниже (пожалуйста, игнорируйте это уродливое решение!)
Вот код работает отлично:
(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))
Замечания: (:actual-type :int64)
означает фактический тип cv-size
является :int64
(C int64).
Замечания: (:simple-parser %cv-size)
означает, что вы можете разместить %cv-size
вернуть тип или тип параметра, как :pointer
:int
делать в cffi:defcfun
, Пожалуйста, посмотрите на декларацию %cvGetSize
а также %cvCreateImage
,
Тестовый код для %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
Тестовый код для %cvCreateImage
:
(%cvCreateImage (make-instance 'cv-size
:width 480
:height 640))
Примечание: конечно, вы должны cffi:define-foreign-libray
а также cffi:use-foreign-library
сначала вот так:
(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)
Из того, что я вижу в коде CFFI, вы можете использовать следующую форму в качестве формы верхнего уровня вместо определения своей собственной translate-from-foreign
а также translate-into-foreign-memory
(или же translate-to-foreign
):
(cffi:translation-forms-for-class cv-size cv-size-type)
РЕДАКТИРОВАТЬ: Некоторые заметки о вашем defcfun
s.
defcfun
за cvGetSize
должен объявить аргумент как (:pointer cv-arr)
, Я думаю. Я не знаю как cv-arr
объявлен
defcfun
за cvCreateImage
должен пройти (:struct cv-size)
вместо :int64
, Первый должен быть корректным на всех платформах, а второй может не работать там, где int
не является 32-битным, где выравнивание полей делает структуру не компактной, а общий размер структуры может отличаться от sizeof(int) + sizeof(int)
,
Все еще на cvCreateImage
, он должен вернуть (:pointer ipl-image)
, хотя я не уверен, обрабатываются ли структуры по значению или по указателю, когда тип не является прямым (:struct <name>)
, Лучше проверить это в списке рассылки CFFI или в чате на #lisp @ irc.freenode.net.