質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.11%

C++とC#での画像データのメモリ共有について

受付中

回答 2

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 2,145

soramama

score 4

 前提・実現したいこと

ここに質問の内容を詳しく書いてください。

C++でOpenCVを使い画像を共有メモリに出力し
C#のアプリケーションで共有メモリにアクセスしてPictureBoxに画像を
反映させたいのですがプログラム初心者でどのように処理すればいいのか
わからないので良ければアドバイスの方よろしくお願いいたします。

 発生している問題・エラーメッセージ

 該当のソースコード

コード
#include "stdafx.h"                    
#include <Windows.h>                
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>            
#include <opencv2/imgproc.hpp>        
#include <opencv2/highgui.hpp>        
#include <stdlib.h>                    
#include <iostream>                    
#include "BmpMem.h"                    



#define _CRTBG_MAP_ALLOC
#define BMP_SHARE_MEM       _T ("BMP_SHARED_MEMORY")
#define MEMORY_SIZE 3000 * 2000 * 4

using namespace std;
using namespace cv;


BmpMem::BmpMem()
{
}
BmpMem::~BmpMem()
{
}


int BmpMem::CreateMapObject(void) {
    try {
        // マッピングオブジェクト作成
        m_hMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, MEMORY_SIZE, BMP_SHARE_MEM);
        if (m_hMapping == NULL) {
            cout << "マップドファイルオブジェクトの作成に失敗しました" << endl;
            getchar();
            return -1;
        }

        // 共有メモリをマッピング
        m_lpBuff = (LPSTR)::MapViewOfFile(m_hMapping, FILE_MAP_WRITE, 0, 0, 0);
        if (m_lpBuff == NULL) {
            cout << "MapViewOfFileエラー" << endl;
            getchar();
            return -1;
        }

        ::ZeroMemory(m_lpBuff, MEMORY_SIZE);
    }
    catch (...) {
        cout << endl << "CreateMapObject Exception !" << endl;
        getchar();
    }

    return 0;
}


void BmpMem::OutMem(unsigned long* bmp_img) {
    try {
        memcpy(m_lpBuff, bmp_img, (16 + (m_ImageWidth * m_ImageHeight)) * 4);
        cout << "出力が完了しました" << endl;
    }
    catch (...) {
        cout << endl << "OutMem Exception !" << endl;
        getchar();
    }
}



void BmpMem::OutBmpData(cv::Mat* src) {

    m_ImageWidth = src->rows;
    m_ImageHeight = src->cols;
    IplImage IplImg;
    BITMAPINFO info;
    BITMAPFILEHEADER bmpHeader;

    try {
        IplImg = *src;

        HANDLE hHandle;
        unsigned long* bmpData;
        hHandle = GetProcessHeap();
        bmpData = (LPDWORD)HeapAlloc(hHandle, HEAP_ZERO_MEMORY, (16 + (m_ImageWidth * m_ImageHeight)) * 4);
        cout << "共有メモリに出力します" << endl;
        iplTobmp(&IplImg, info, bmpHeader, bmpData);

        OutMem(bmpData);

        HeapFree(hHandle, 0, bmpData);
        cout << "出力が完了しました" << endl;
    }
    catch (...) {
        cout << endl << "OutBmpData Exception !" << endl;
        getchar();
    }
}


Mat BmpMem::InBmpData() {
    HANDLE mh_lpBuff = (LPSTR)::OpenFileMapping(FILE_MAP_ALL_ACCESS, false, BMP_SHARE_MEM);
    // 共有メモリをマッピング
    LPSTR mp_lpBuff = (LPSTR)::MapViewOfFile(mh_lpBuff, FILE_MAP_WRITE, 0, 0, 0);
    if (mp_lpBuff == NULL) {
        cout << "MapViewOfFileエラー" << endl;
        getchar();
    }
    unsigned long bmpInfo[16] = {};
    memcpy(bmpInfo, mp_lpBuff, 16 * 4);
    cout << "インフォ部を読み込みました" << endl;
    int width = bmpInfo[6];
    int height = bmpInfo[7];
    cv::Mat image;
    cv::Mat bmpImage(cvSize(bmpInfo[6], bmpInfo[7]), CV_8UC3, cv::Scalar(0));
    cout << "Mat宣言完了" << endl;
    try {
        unsigned char b, g, r;
        unsigned long pixel;
        cout << "変数宣言完了しました" << endl;

        for (int countHeight = 0; countHeight < bmpInfo[7]; countHeight++) {
            //cout << countHeight << "行目を読み込みます" << endl;
            for (int countWidth = 0; countWidth < bmpInfo[6]; countWidth++) {
                CopyMemory(&pixel, (mp_lpBuff + ((16 + countWidth + ((bmpInfo[7] - countHeight - 1) * bmpInfo[6])) * 4)), sizeof(unsigned long));
                b = pixel;
                g = pixel >> 8;
                r = pixel >> 16;
                bmpImage.at<cv::Vec3b>(countHeight, countWidth)[0] = b;
                bmpImage.at<cv::Vec3b>(countHeight, countWidth)[1] = g;
                bmpImage.at<cv::Vec3b>(countHeight, countWidth)[2] = r;
            }
            //cout << "読み込み完了しました" << endl;
        }
        cout << "読み込み完了しました" << endl;
    }
    catch (...) {
        cout << endl << "CloseMapObject Exception !" << endl;
        getchar();
    }
    return bmpImage;
}






void BmpMem::CloseMapObject(void)
{
    try {
        UnmapViewOfFile(m_lpBuff);
        CloseHandle(m_hMapping);
    }
    catch (...) {
        cout << endl << "OutBmpData Exception !" << endl;
        getchar();
    }
}

void BmpMem::iplTobmp(const IplImage* srcIplImage, BITMAPINFO& bmpInfo, BITMAPFILEHEADER& bmpHeader, unsigned long* bmpData) {

    int width = srcIplImage->width;
    int height = srcIplImage->height;

    // ビットマップのヘッダーを0で初期化
    ZeroMemory(&bmpHeader, sizeof(bmpHeader));
    bmpHeader.bfType = 'BM';                                // ファイルタイプ
    bmpHeader.bfSize = (16 + width * height) * 4;            // ファイル全体のサイズ
    bmpHeader.bfReserved1 = 0;                                // 予約領域    常に0
    bmpHeader.bfReserved2 = 0;                                // 予約領域 常に0
    bmpHeader.bfOffBits = 16;                                // ファイル先頭から画像データまでのオフセット値
    // bmpData[0-4]にヘッダ情報を格納
    bmpData[0] = bmpHeader.bfType;
    bmpData[1] = bmpHeader.bfSize;
    bmpData[2] = bmpHeader.bfReserved1;
    bmpData[3] = bmpHeader.bfReserved2;
    bmpData[4] = bmpHeader.bfOffBits;

    // ビットマップのインフォを0で初期化
    ZeroMemory(&bmpInfo, sizeof(bmpInfo));
    bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);    // Info構造体のサイズ 40
    bmpInfo.bmiHeader.biWidth = width;                        // 画像の幅[pix]
    bmpInfo.bmiHeader.biHeight = height;                    // 画像の高さ[pix]
    bmpInfo.bmiHeader.biPlanes = 1;                            // プレーン数 常に1
    bmpInfo.bmiHeader.biBitCount = 32;                        // 1画素あたりのビット数
    bmpInfo.bmiHeader.biCompression = BI_RGB;                // 圧縮形式
    bmpInfo.bmiHeader.biSizeImage = 0;                        // 画像データのサイズ(byte) BI_RGBは0でも可
    bmpInfo.bmiHeader.biXPelsPerMeter = 0;                    // 水平方向1[m]あたりの画素数 0でも可
    bmpInfo.bmiHeader.biYPelsPerMeter = 0;                    // 垂直方向1[m]あたりの画素数 0でも可
    bmpInfo.bmiHeader.biClrUsed = 0;                        // カラーテーブルの色数 0でも可
    bmpInfo.bmiHeader.biClrImportant = 0;                    // 表示に必要なカラーテーブルの色数 0でも可
    // bmpData[5-15]にインフォ情報を格納
    bmpData[5] = bmpInfo.bmiHeader.biSize;
    bmpData[6] = bmpInfo.bmiHeader.biWidth;
    bmpData[7] = bmpInfo.bmiHeader.biHeight;
    bmpData[8] = bmpInfo.bmiHeader.biPlanes;
    bmpData[9] = bmpInfo.bmiHeader.biBitCount;
    bmpData[10] = bmpInfo.bmiHeader.biCompression;
    bmpData[11] = bmpInfo.bmiHeader.biSizeImage;
    bmpData[12] = bmpInfo.bmiHeader.biXPelsPerMeter;
    bmpData[13] = bmpInfo.bmiHeader.biYPelsPerMeter;
    bmpData[14] = bmpInfo.bmiHeader.biClrUsed;
    bmpData[15] = bmpInfo.bmiHeader.biClrImportant;
    cout << "情報を出力しました" << endl;

    for (int count = 0; count < 16; count++) {
        cout << count << " : " << bmpData[count] << endl;
    }
    unsigned long pixel;
    unsigned char r, g, b;
    for (int countHeight = 0; countHeight < height; countHeight++) {
        for (int countWidth = 0; countWidth < width; countWidth++) {
            // Brue,Green,Redの画素値を取得する
            b = srcIplImage->imageData[srcIplImage->widthStep * countHeight + countWidth * 3];
            g = srcIplImage->imageData[srcIplImage->widthStep * countHeight + countWidth * 3 + 1];
            r = srcIplImage->imageData[srcIplImage->widthStep * countHeight + countWidth * 3 + 2];
            //imageBuffer->push_back(b);
            //imageBuffer->push_back(g);
            //imageBuffer->push_back(r);
            // pixelを初期化する
            pixel = 0x00000000;
            // rは16bitシフト,gは8bitシフト,bはそのままでpixelに格納
            pixel = (r << 16) + (g << 8) + b;
            // pixelをbmpDataに格納
            bmpData[16 + countWidth + (height - countHeight - 1) * width] = pixel;
        }
        //cout << countHeight << "行目の画素データを格納します" << endl;
    }
    cout << "画像を出力しました" << endl;
}





BmpMem.h
#pragma once
#include <Windows.h>
#include <vector>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>

class BmpMem
{
public:
    BmpMem();
    ~BmpMem();
private:
    HANDLE m_hMapping;
    LPSTR m_lpBuff;
    int m_memSize;
    int m_ImageWidth;
    int m_ImageHeight;


    // 共有メモリにデータ書き込み
    void OutMem(unsigned long*);
    // IplImageからBMP(DIB)へ変換する
    void iplTobmp(const IplImage* srcIplImage, BITMAPINFO& bmpInfo, BITMAPFILEHEADER& bmpHeader, unsigned long* bmpData);

public:
    // 共有メモリの作成
    int CreateMapObject(void);
    // ビットマップデータの書き込み
    void OutBmpData(cv::Mat *src);
    // ビットマップデータの読み込み
    //void InBmpData();
    cv::Mat InBmpData();
    // 共有メモリオブジェクトの破棄
    void CloseMapObject(void);



};


読み込み部分
#include "stdafx.h"
#include <conio.h>
#include <opencv2/core.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
#include "BmpMem.h"

using namespace std;
using namespace cv;


int _tmain(int argc, _TCHAR* argv[])
{
    int key;
    Mat img;
    Mat returnImage;


    cout << "共有メモリのオープンに成功しました" << endl;
    img = imread("D:\\デスクトップ\\Project1\\Project1\\954704.png",1);

    if (img.empty()) {
        cout << "画像は読み込まれませんでした\nプログラムを終了します" << endl;
        return -1;
    }
    cout << "画像のオープンに成功しました" << endl;
    BmpMem *pBmp = new BmpMem;
    if (pBmp->CreateMapObject() == -1) {
        cout << "共有メモリ(画像)のオープンに失敗しました。" << endl;
        getchar();
        return -1;
    }


        pBmp->OutBmpData(&img);
        cout << "読み込みを開始します" << endl;

        returnImage = pBmp->InBmpData();
        cout << "読み込みが完了しました" << endl;

        while (true) {
            cv::imshow("書き込み元の画像", img);
            cv::imshow("共有メモリ内の画像", returnImage);
            waitKey();

            Sleep(100);

            if (_kbhit() == TRUE) {
                break;
            }
        }

        pBmp->CloseMapObject();
        delete pBmp;

        _CrtDumpMemoryLeaks();

        return 0;
}
C++ C#

 試したこと

C++ 共有メモリを作り,画像の出力はできました

 補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。
VisualStudio2017
OpenCV 3.4.1

MemoryMappedFileをどのようにして扱えばいいのか
で手が止まってます。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • y_waiwai

    2018/10/16 18:38

    ソースコードを提示してください。ソースは、<code>ボタンで、’’’の枠の中に貼り付けてください

    キャンセル

  • soramama

    2018/10/16 23:21

    ご返事ありがとうございます ソースコードの方掲示しましたのでよろしくお願いします。

    キャンセル

回答 2

+1

こんにちは。

共有メモリ上のデータ形式が分かりませんが、C#を使う場合は Byte[]が多いと思います。
Byte[]からBitmapへ変換するには、ImageConverterクラスのByteArrayToImageメソッドを使えば良いようです。

しかし、プログラミング初心者にはそこそこ荷が思いと思いますし、手間がそこそこかかります。C++側でbmpファイルへ保存してC#で読み込むと簡単ですよ。
C++で画像を加工し続けるものを高速(もしくは大量)に表示したいというのでなければ性能的にも許容範囲に収まる可能性はあると思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/10/16 20:28

    多分、C#では直接、OpenCVを使えないので、C++ から、OpenCVを使ってメインの処理をを C# で行うのでは?
    確か、Memoryをロックし、共有メモリでアクセスすれば、良いのですが、具体的な方法が思い出せません。(人の作ったのをそのまま、使っているので)

    キャンセル

  • 2018/10/16 22:38

    多分 Windows なので、C++ 側で DLL を作って API を実装し、C# から P/Invoke を使ってそれを呼び出してイメージハンドルを返すのが一番楽じゃないかなと思いました。
    https://docs.microsoft.com/ja-jp/dotnet/api/system.drawing.image.fromhbitmap?view=netframework-4.7.2#System_Drawing_Image_FromHbitmap_System_IntPtr_

    キャンセル

  • 2018/10/16 22:45 編集

    C++側で、.NETから、OpenCVで扱える形式(アンマネージ)に変換するだけです。メモリ管理が違うので、ちょっと注意が必要です。ただ、知っている人なら、簡単な筈なので、今から、調べるより、知っている人の回答が無いかと。
    あ、使った方法は、Bitmap形式。C++で、OpenCV の Mat ⇔ Bitmap変換をする。

    キャンセル

  • 2018/10/17 01:07

    過去に対応した事案を記載しておきます:
    1. C++上でCvMatからビットマップサイズ(メモリをC#から確保する量)を取得するAPIを公開
    2. C#側でbyte配列を指定されたサイズ分を確保
    3. C++側に2のメモリを渡してCvMatから画像データをコピー
    4. `Bitmap(Int32, Int32, Int32, PixelFormat, IntPtr)` でビットマップ化した記憶があります。(補足: Bitmapインスタンスを使い終わるまでIntPtrは開放してはいけないのがつらかった....)
    ※残念ながらソースがないので、正式回答は出来かねます
    ※C++の再配布パッケージをインストールさせたくないために上記手順を行っていました(が、OpenCVが再配布パッケージが必須であることを知って絶望した記憶があります)

    キャンセル

0

共有メモリは使って無く、C++ もC++/CLI ですが、以下のようなコードで Bitmapにしてます。

cv::Mat MatData;        // Matデータ

Bitmap ^MatToBmp()
{
    Bitmap ^bmp = gcnew Bitmap(MatData.cols, MatData.rows, Imaging::PixelFormat::Format24bppRgb);
    Imaging::BitmapData^ bmpData = bmp->LockBits(Rectangle(0, 0, bmp->Width, bmp->Height),
        Imaging::ImageLockMode::WriteOnly, Imaging::PixelFormat::Format24bppRgb
    );

    int lineSize = bmpData->Stride;
    uint8_t *destData = (uint8_t *)bmpData->Scan0.ToPointer();

    for (int y = 0; y < MatData.rows; y++) {
        for (int x = 0; x < MatData.cols; x++) {
            destData[y * lineSize + x * 3 + 0] = MatData.at<cv::Vec3b>(y, x)[0];    // B
            destData[y * lineSize + x * 3 + 1] = MatData.at<cv::Vec3b>(y, x)[1];    // G
            destData[y * lineSize + x * 3 + 2] = MatData.at<cv::Vec3b>(y, x)[2];    // R
        }
    }
    bmp->UnlockBits(bmpData);
    return bmp;
}

こんな感じで、Bitmapに変換し、
C#は単に呼び出すだけ。

  Bitmap bmp = MatToBmp();

実際には、エラー処理とか、色々とありますが、、。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.11%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る