プログラム初心者です.

以下のようにVC++でSharedMemoryを使ってクラスを複数プロセスで共有したいと思っていますが
cv::Mat* mat;のようなポインタを含むクラスの共有が上手くいかないで困っております.
なにか上手い方法をご存知の方がいましたらご指導いただけないでしょうか?
# Sender側のプログラムで読み込んだ画像(Test.jpg)をメモリ経由でReciever側のプログラムから読みたいと考えています


# 共通
class ShareData
{
public:
int hoge;
float pt[10];
cv::Mat* mat;
};
HANDLE SMhandle;
ShareData *data;

void init(){
SMhandle = CreateFileMapping((HANDLE)0xFFFFFFFF, 0,PAGE_READWRITE,0,sizeof(ShareData),"SMtest");
data = (ShareData*)MapViewOfFile(SMhandle, FILE_MAP_WRITE,0,0,sizeof(ShareData));
}
void close(){
UnmapViewOfFile(SMhandle);
CloseHandle(SMhandle);
}

# Sender側======================
void main(){
init();
cv::Mat img;
std::string filename("C:\\Test.jpg");
img = cv::imread(filename);

while(1){
data->hoge = 1;
data->mat = &img;
printf("address:%p\n",&img);
}
close();
}


# Reciever側======================
void main(){
init();
while(1){
printf("hoge=%d\n",data->hoge);
printf("mat=[%d,%d]\n",data->mat->cols,data->mat->rows);
printf("address:%p\n",data->mat);
}
close();
}

回答の条件
  • 1人5回まで
  • 登録:
  • 終了:2011/04/29 17:45:03
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

回答2件)

id:tdoi No.1

回答回数174ベストアンサー獲得回数75

ポイント35pt

SharedMemoryでポインタを共有することはできません。

それは、プロセスAにとっての100番地と、プロセスBにとっての100番地が異なるためです。

上記のような構造体をSharedMemroy内に格納すると、matのデータとしてcv::Matオブジェクトへのポインタそのもの(メモリアドレス)が共有されることになります。しかし、メモリ自体を共有していないので、同じアドレスのデータはプロセスごとにことなり期待した通りの動作はしません。

一番簡単な方法は、SharedDataに実体をもたせてしまうことです。

つまり、

class ShareData
{
public:
  int hoge;
  float pt[10];
  cv::Mat mat;
};

こうしてしまうことです。

もし、cv::Matに対して何か抽象化しておく必要があるのであれば、それを考慮してシリアライズ・デシリアライズを行う必要があるでしょう。

画像など可変でもっと大きなデータについてはこういう風にはいかないと思うので、画像自体を別なSharedMemoryを作成して格納しておき、それの参照情報をSharedData構造体に格納しておけばよいかと思います。


つまり、送る側はこんな感じです。


class ShareData
{
public:
  int hoge;
  float pt[10];
  std::string objectName;
};

std::string objectName = 何か識別子;
HANDLE hData = CreateFileMapping((HANDLE)0xFFFFFFFF, 0,PAGE_READWRITE,0, 必要なサイズ, objectName);
void* buf = MapViewOfFile(hData, FILE_MAP_WRITE,0,0, 必要なサイズ);
// bufにデータを流し込む

SharedData data;
// dataに情報を設定
data.objectName = objectName;
SMhandle = CreateFileMapping((HANDLE)0xFFFFFFFF, 0,PAGE_READWRITE,0,sizeof(ShareData),"SMtest");
data = (ShareData*)MapViewOfFile(SMhandle, FILE_MAP_WRITE,0,0,sizeof(ShareData));

受取側はこの逆でいいでしょう。


何かの参考になれば。

id:rockafeller

早速のアドバイスありがとうございます!

↓ような感じで画像を共有しようと思いましたが,受け取り側は

このような感じで取り出せるのでしょうか?

見当違いでしたらご指摘いただけたら幸いです.

よろしくお願いいたします.


//-----共通

std::string objectName = "IMG_DATA";

class ShareData

{

public:

 int size;

std::string objectName;

};

//-----送信側

cv::Mat img;

std::string filename("C:\\tmp\\Sunset.jpg");

img = cv::imread(filename);


HANDLE hData = CreateFileMapping((HANDLE)0xFFFFFFFF, 0,PAGE_READWRITE,0, sizeof(img), objectName);

void* buf = MapViewOfFile(hData, FILE_MAP_WRITE,0,0,sizeof(img));

buf = &img;

SMhandle = CreateFileMapping((HANDLE)0xFFFFFFFF, 0,PAGE_READWRITE,0,sizeof(ShareData),"SMtest");

SharedData* data = (ShareData*)MapViewOfFile(SMhandle, FILE_MAP_WRITE,0,0,sizeof(ShareData));

data->objectName = objectName;

data->size = sizeof(img);


//-----受信側

SMhandle = CreateFileMapping((HANDLE)0xFFFFFFFF, 0,PAGE_READWRITE,0,sizeof(ShareData),"SMtest");

SharedData* data = (ShareData*)MapViewOfFile(SMhandle, FILE_MAP_WRITE,0,0,sizeof(ShareData));

HANDLE hData = CreateFileMapping((HANDLE)0xFFFFFFFF, 0,PAGE_READWRITE,0, data->size, objectName);

void* buf = MapViewOfFile(hData, FILE_MAP_WRITE,0,0, data->size);

cv::Mat img = *(cv::Mat*)buf;

2011/04/25 15:27:42
id:tdoi No.2

回答回数174ベストアンサー獲得回数75

ポイント35pt

実際には確認していませんが、何か所がよくないところがあります。

一つは送信側のcv::Matオブジェクトの格納の仕方です。

buf = &img;

これでは、bufのポインタが指すアドレスが、一時的に作成したimgオブジェクト変わるだけで、作成した共有メモリ上にimgオブジェクトを格納していることにはなりません。

ここは、

memcpy(buf, &img, sizeof(img));

のように作成した共有メモリ内にimgオブジェクトデータを格納する必要があります。

もう1つは、cv::Matオブジェクトの部分です。

恐らく、このクラスは、以下のものと思います。

http://opencv.jp/opencv-2.1/cpp/basic_structures.html#mat

前回の回答時は調べていなかったのですが、このクラスの定義を見るだけでも、

uchar* data;
int* refcount;
uchar* datastart;
uchar* dataend;

といったポインタのメンバが存在しています。

これらが、前回の回答と同じ理由でうまく受け渡せません。

この辺りをもう少し詳しく知りたければ、ディープコピーとシャローコピーについて調べてみてください。



さて、前者の問題は直してもらえればよいのですが、後者の方はちょっと工夫しなければなりません。

ぱっと思いつく選択肢としては、

1.画像ファイルデータそのものを共有メモリに書き出して、cv::Matオブジェクトは受信側で生成する。

2.boost::serializeのような仕組みを使って、cv::Matオブジェクトをシリアライズする。

辺りでしょうか。

この辺りは、送信側でも同じcv::Matオブジェクトを利用したいのかなど状況にもよるので、どうすべきとは言えませんが。


何かの参考になれば。

id:rockafeller

早速のご回答ありがとうございました!

serializeしたオブジェクトを共有メモリに載せる方法の方が楽そうですね.

もう少しこちらで検討してみたいと思います..

親切な解説感謝します,ありがとうございました!!

2011/04/25 17:58:55

コメントはまだありません

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません