Возврат указателя изображения в Erlang

Я пытаюсь использовать openCV с Erlang NIF. Итак, я хочу сделать основную вещь, и это просто прочитать изображение и отправить указатель обратно в erlang. и иметь возможность снова отправить обратно полученный указатель на C и просто показать картинку

поэтому niftest.cpp выглядит так:

/* niftest.cpp */

#include "erl_nif.h"
#include <opencv/highgui.h>
#include <opencv/cv.h>
using namespace cv;
using namespace std;


static ErlNifResourceType* frame_res = NULL;


typedef struct _frame_t {
IplImage* _frame;
} frame_t;

//------------------------------------------------------------------------------
// NIF callbacks
//------------------------------------------------------------------------------

static void frame_cleanup(ErlNifEnv* env, void* arg) {
enif_free(arg);
}

static int load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info)
{

ErlNifResourceFlags flags = (ErlNifResourceFlags) (ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER);
frame_res = enif_open_resource_type(env, "niftest", "ocv_frame",
                      &frame_cleanup,
                      flags, 0);
return 0;
}


static ERL_NIF_TERM get_pic(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{

IplImage* src = cvLoadImage("/home/khashayar/Downloads/pic.png");

cout << src->width << endl;

IplImage* gray = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
cvCvtColor(src, gray, CV_RGB2GRAY);

frame_t* frame = (frame_t*)enif_alloc_resource(frame_res, sizeof(frame_t));
frame->_frame = gray ;

ERL_NIF_TERM term = enif_make_resource(env, frame);
enif_release_resource(frame);
return enif_make_tuple2(env, enif_make_atom(env, "ok"), term); 

}


static ERL_NIF_TERM show_pic(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]){

frame_t* frame;
 if (!enif_get_resource(env, argv[0], frame_res, (void**) &frame)) {
   return enif_make_badarg(env);
 }

 cvShowImage("YOOHOO", frame->_frame);

 cvWaitKey(30);

 return enif_make_atom(env, "ok");
}

static ErlNifFunc nif_funcs[] =
  {
    {"show_pic", 1, show_pic},
    {"get_pic", 0, get_pic}
  };

ERL_NIF_INIT(niftest,nif_funcs,load,NULL,NULL,NULL)

и мой niftest.erl выглядит так:

-module(niftest).

-compile(export_all).

init() ->
      erlang:load_nif("./niftest", 0).

get_pic() ->
      "NIF library not loaded".

show_pic(F) ->
      "NIF library not loaded".

Итак, теперь проблема в том, что когда я вызываю get_pic, я получаю в ответ {ok, <<>>}, а указатель вообще недействителен.

когда я cout фрейм перед созданием enif_make_resource имеет значение, и я его вижу, но он возвращается мне пустым!

Что я делаю неправильно? Я прочитал всю документацию, и я действительно не могу понять это.

ПРИМЕЧАНИЕ: вы можете скомпилировать код с помощью этой команды:

g++ -fPIC -shared -o niftest.so niftest.cpp -lopencv_core -lopencv_imgproc -lopencv_highgui -I /usr/lib64/erlang/usr/include/

а затем запустите оболочку erlang и вызовите функцию init и get_pic


person Khashayar    schedule 24.09.2013    source источник


Ответы (1)


NIF — это неправильное решение для портирования высокоуровневого графического интерфейса OpenCV.

Тем не менее, чтобы ответить на ваш вопрос: явно пустой двоичный файл в кортеже {ok, <<>>}, который вы получаете, непрозрачен для Erlang. Это объект ресурса, как описано в erl_nif страница руководства.

Ресурсные объекты дружелюбны к сборщикам мусора. Если ни один процесс не ссылается на данный ресурс, будет вызвана функция очистки. Как правило, они являются подходящей структурой для встраивания указателей C или C++ в ваш NIF.

Указатель IplImage* — идеальный кандидат на роль объекта ресурса. Вероятно, вам не нужен тип frame_t, так как вы можете просто привести указатель к IplImage**. Функция очистки должна освободить память и, в вашем примере, вызвать cvReleaseImage.

Поскольку указатель непрозрачен, вам необходимо портировать функции доступа для предоставления данных в Erlang. Это действительно зависит от типа данных, которые вы хотите извлечь из изображения. Например, вы можете перенести функцию cvEncodeImage и преобразовать данные из CvMat* в двоичный файл erlang, используя enif_make_binary.

Кроме того, в качестве примечания, вместо возврата списка "NIF library not loaded" вы должны вызвать erlang:nif_error/1,2 в функциях-заглушках.

Правильный подход к переносу API, такого как High GUI OpenCV, — это внешний драйвер (или C-узел).

Причин несколько, в том числе:

  • Вызовы NIF должны возвращаться быстро (вызов cvWaitKey — плохой кандидат для NIF, а вычисления занимают слишком много времени), так как в противном случае они могут запутать планировщик;
  • с NIF или подключенным драйвером управление памятью напрямую влияет на виртуальную машину Erlang, и любой сбой приведет к отключению всего узла Erlang.

Внешний драйвер – это процесс, который получает данные от stdin (обычно) и отвечает на stdout. Это очень просто спроектировать на C или C++. Вы можете либо портировать API OpenCV, либо более сложные функции в зависимости от ваших потребностей. В этом случае указатели, такие как IplImage*, могут быть переданы как непрозрачная последовательность из 4 или 8 байтов или как ссылочный номер, если вы поддерживаете список всех указателей IplImage*, выделенных Erlang. Однако, в отличие от NIF, здесь нет объекта ресурсов, и вам придется разрабатывать свой код на стороне Erlang, чтобы обеспечить правильное освобождение памяти.

Дополнительную информацию и примеры кода можно найти в Руководстве пользователя учебного пособия по взаимодействию.

См. также этот вопрос: OpenCV на Erlang

person Paul Guyot    schedule 25.09.2013
comment
Я думаю, что вы уделили много внимания деталям в моем коде, здесь он упрощен, чтобы его было легче читать. Я, конечно, прочитал документы, которые вы предложили. поверьте мне, я работаю над этим какое-то время, а не просто публикую что-то здесь. Спасибо за ваш ответ - person Khashayar; 25.09.2013