最近需要开发一些C#的项目,但是却需要使用C++编写的动态库进行图像处理(核心算法使用C++ opencv,防止反编译),其中涉及到图像cv::Mat 与C#的 Bitmap之间的相互转换。经查阅资料,最终通过C++/CLR实现。
//--------------Functions.h--------------------
#include<opencv.hpp>
#include <string>
cv::Mat BitmapToMat(System::Drawing::Bitmap^ bitmap);
System::Drawing::Bitmap^ MatToBitmap(cv::Mat srcImg);
//-----------------Functions.cpp-------------------
#include "Stdafx.h"
#include "Functions.h"
cv::Mat BitmapToMat(System::Drawing::Bitmap^ bitmap)
{
IplImage* tmp;
System::Drawing::Imaging::BitmapData^ bmData = bitmap->LockBits(System::Drawing::Rectangle(0, 0, bitmap->Width, bitmap->Height), System::Drawing::Imaging::ImageLockMode::ReadOnly, bitmap->PixelFormat);
if (bitmap->PixelFormat == System::Drawing::Imaging::PixelFormat::Format8bppIndexed)
{
tmp = cvCreateImage(cvSize(bitmap->Width, bitmap->Height), IPL_DEPTH_8U, 1);
tmp->imageData = (char*)bmData->Scan0.ToPointer();
}
else if (bitmap->PixelFormat == System::Drawing::Imaging::PixelFormat::Format24bppRgb)
{
tmp = cvCreateImage(cvSize(bitmap->Width, bitmap->Height), IPL_DEPTH_8U, 3);
tmp->imageData = (char*)bmData->Scan0.ToPointer();
}
else
{
tmp = cvCreateImage(cvSize(bitmap->Width, bitmap->Height), IPL_DEPTH_8U, 4);
tmp->imageData = (char*)bmData->Scan0.ToPointer();
}
bitmap->UnlockBits(bmData);
cv::Mat cvImage = cv::cvarrToMat(tmp);
cvReleaseImage(&tmp);
return cvImage;
}
System::Drawing::Bitmap^ MatToBitmap(cv::Mat srcImg)
{
int stride = srcImg.size().width * srcImg.channels();//calc the srtide
int hDataCount = srcImg.size().height;
System::Drawing::Bitmap^ retImg;
System::IntPtr ptr(srcImg.data);
//create a pointer with Stride
if (stride % 4 != 0) {//is not stride a multiple of 4?
//make it a multiple of 4 by fiiling an offset to the end of each row
uchar *dataPro = new uchar[((srcImg.size().width * srcImg.channels() + 3) & -4) * hDataCount];//to hold processed data
uchar *data = srcImg.ptr();
//current position on the data array
int curPosition = 0;
//current offset
int curOffset = 0;
int offsetCounter = 0;
//itterate through all the bytes on the structure
for (int r = 0; r < hDataCount; r++) {
//fill the data
for (int c = 0; c < stride; c++) {
curPosition = (r * stride) + c;
dataPro[curPosition + curOffset] = data[curPosition];
}
//reset offset counter
offsetCounter = stride;
//fill the offset
do {
curOffset += 1;
dataPro[curPosition + curOffset] = 0;
offsetCounter += 1;
} while (offsetCounter % 4 != 0);
}
ptr = (System::IntPtr)dataPro;//set the data pointer to new/modified data array
//calc the stride to nearest number which is a multiply of 4
stride = (srcImg.size().width * srcImg.channels() + 3) & -4;
//retImg = gcnew System::Drawing::Bitmap(srcImg.size().width, srcImg.size().height,
// stride,
// System::Drawing::Imaging::PixelFormat::Format24bppRgb,
// ptr);
}
if (srcImg.channels() == 4)
{
retImg = gcnew System::Drawing::Bitmap(srcImg.size().width, srcImg.size().height,
stride, System::Drawing::Imaging::PixelFormat::Format32bppArgb, ptr);
}
else if (srcImg.channels() == 3)
{
retImg = gcnew System::Drawing::Bitmap(srcImg.size().width, srcImg.size().height,
stride, System::Drawing::Imaging::PixelFormat::Format24bppRgb, ptr);
}
else
{
retImg = gcnew System::Drawing::Bitmap(srcImg.size().width, srcImg.size().height,
stride, System::Drawing::Imaging::PixelFormat::Format8bppIndexed, ptr);
}
array<System::Byte>^ imageData;
System::Drawing::Bitmap^ output;
// Create the byte array.
{
System::IO::MemoryStream^ ms = gcnew System::IO::MemoryStream();
// System::Drawing::Imaging::ImageFormat^ png = System::Drawing::Imaging::ImageFormat::Png;
retImg->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp);
imageData = ms->ToArray();
delete ms;
}
// Convert back to bitmap
{
System::IO::MemoryStream^ ms = gcnew System::IO::MemoryStream(imageData);
output = (System::Drawing::Bitmap^)System::Drawing::Bitmap::FromStream(ms);
delete ms;
}
return output;
}
// OpenCvDotNet.h
#pragma once
#include <opencv\cv.h>
#include <opencv\highgui.h>
using namespace System;
namespace OpenCvDotNet {
public ref class MyOpenCvWrapper
{
public:
System::Drawing::Bitmap^ Threshold(System::Drawing::Bitmap^ srcImage, int minGray, int maxGray);
};
}
// OpenCvDotNet.cpp
// This is the main DLL file.
#include "stdafx.h"
#include "OpenCvDotNet.h"
#include "Functions.h"
using namespace OpenCvDotNet;
System::Drawing::Bitmap^ MyOpenCvWrapper::Threshold(System::Drawing::Bitmap^ srcImage, int minGray, int maxGray){
System::Drawing::Bitmap^ output;
cv::Mat cvsrcImage = BitmapToMat(srcImage);//convert Bitmap to Mat
if (!cvsrcImage.data) {
return nullptr;
}
cv::Mat cvdstImage;//destination image
cv::Mat dstImage1, dstImage2;
cv::threshold(cvsrcImage, dstImage1, minGray, 255, cv::THRESH_BINARY);
cv::threshold(cvsrcImage, dstImage2, maxGray, 255, cv::THRESH_BINARY_INV);
cvdstImage = dstImage1 & dstImage2;
output = MatToBitmap(cvdstImage);
return output;
}