본문 바로가기
opencv

C# 와 C++/CLI Dll 사이의 이미지 전달

by 꼰대코더 2025. 2. 10.

 

Windows 에서 GUI 개발은 MFC 보다 UI도 풍부하고 다루기 쉬운 C# 으로 개발하는게 편리하다.

이러한 .NET 환경에서 OpenCV(C++) 을 사용하기 위해서는 Wrapper 인 OpenCVSharp 을 이용하는 방법도 있으나 interface를 새로 알아야 한다는 단점이 있다.

 

하지만 직접 OpenCV를 C++ 그대로 사용하고자 할때는 C++/CLI(Managed Code와 Native C++ 혼용) 환경을 이용하자.

프로젝트 생성 방법

 

GUI(C#) 에서 읽어들인 BMP 메모리를 C++/CLI 에 전달하여 OpenCV로 처리한 후 결과 이미지를 다시 GUI에 전달하는 방법은 아래와 같이 .NET Managed memory 와 Native Heap memory에의 변환 처리가 필요하다.

 

1. Bitmap -> OpenCV Mat 변환

cv::Mat ImgPrc::ToMat(System::Drawing::Bitmap^ bitmap)
{
     // 24 bpp(BGR칼라) 또는 8 bpp(그레이) 만 가능
     if ((bitmap->PixelFormat != System::Drawing::Imaging::PixelFormat::Format24bppRgb) &&
            (bitmap->PixelFormat != System::Drawing::Imaging::PixelFormat::Format8bppIndexed))
      {
            throw gcnew NotSupportedException("Only images of type CV_8UC3 OR CV_8UC1 are supported for conversion to Bitmap");
      }

      // Bitmap 에 읽고 쓰기 Lock을 설정
      System::Drawing::Imaging::BitmapData^ bmpdata = bitmap->LockBits(
                  System::Drawing::Rectangle(0, 0, bitmap->Width, bitmap->Height),
                  System::Drawing::Imaging::ImageLockMode::ReadWrite,
                  bitmap->PixelFormat
      );

      // bpp 를 OpenCV에 맞게 설정
      int type = (bitmap->PixelFormat == System::Drawing::Imaging::PixelFormat::Format24bppRgb) ? CV_8UC3 : CV_8UC1;
      // 로우의 바이트 수
      int wb   = bmpdata->Stride;
      // 변환
      cv::Mat cv_img(cv::Size(bitmap->Width, bitmap->Height), type, bmpdata->Scan0.ToPointer(), wb);

      bitmap->UnlockBits(bmpdata);

      return cv_img;
}

 

2. OpenCV Mat -> Bitmap 변환

System::Drawing::Bitmap^ CMatch::ToBmp(cv::Mat img)
{
      System::Drawing::Imaging::PixelFormat fmt;
      // OpenCV 타입을 .NET 타입으로 설정
      switch (img.type()) {
            case CV_8UC3:
                  fmt = System::Drawing::Imaging::PixelFormat::Format24bppRgb;
                  break;
            case CV_8UC1:
                  fmt = System::Drawing::Imaging::PixelFormat::Format8bppIndexed;
                  break;
            default:
                  throw gcnew NotSupportedException("Only images of type 8, 24 bpp are supported for conversion to Bitmap");
      }

      // 이미지를 담을 메니지드 메모리 할당
      System::Drawing::Bitmap^ bmpimg = gcnew System::Drawing::Bitmap(img.cols, img.rows, fmt);
      // 할당된 메모리를 Lock
      System::Drawing::Imaging::BitmapData^ data = bmpimg->LockBits(
            System::Drawing::Rectangle(0, 0, img.cols, img.rows),
            System::Drawing::Imaging::ImageLockMode::WriteOnly,
            fmt);

      // Mat 의 데이터 메모리 포인터
      uchar* srcData = img.data;
      // Managed 의 Bitmap 데이터 메모리 포인터
      uchar* dstData = reinterpret_cast<uchar*>(data->Scan0.ToPointer());

      // 한 로우씩 카피
      for (int row = 0; row < data->Height; ++row)
      {
            memcpy(reinterpret_cast<void*>(&dstData[row * data->Stride]), reinterpret_cast<void*>(&srcData[row * img.step]), img.cols * img.channels());
      }

      bmpimg->UnlockBits(data);

      return bmpimg;
}