GOCW的重點和難點就在於Csharp調用OpenCV,其中的橋梁就是CLR,當然我們也有其他方法,但是CLR是一個比較新的、比較可靠的、關鍵是能用的橋梁。這里關於CLR的基本原理知識、如何用於GOCW項目的相關內容加以整理思考,以圖深入:
1、什么是CLR
CLR(Common Language Runtime)是“公共語言運行時”的縮寫,簡單來說它是和Java虛擬機一樣的一個運行時環境。它負責資源管理(內存分配和垃圾收集),並保證應用和底層操作系統之間必要的分離。
通用語言運行時是.NET 框架應用程序的執行引摯。它提供了許多服務,其中包括:代碼管理(裝入和執行)、類型安全性驗證、元數據(高級類型信息)訪問、為管理對象管理內存、管理代碼,COM對象和預生成的DLLs(非管理代碼和數據)的交互操作性、對開發人員服務的支持等等。
我們GOCW項目中為了能夠使用Csharp調用OpenCV,采用了托管C++;
2、什么是托管C++?
托管是.NET的一個專門概念,它是融於通用語言運行時(CLR)中的一種新的編程理念,使用托管C++意味着,我們的代碼可以被CLR所管理,並能開發出具有最新特性如垃圾自動收集、程序間相互訪問等的.NET框架應用程序。
由托管概念所引發的C++應用程序包括托管代碼、托管數據和托管類三個組成部分。
(1) 托管代碼:. Net環境提供了許多核心的運行(RUNTIME)服務,比如異常處理和安全策略。為了能使用這些服務,必須要給運行環境提供一些信息代碼(元數據),這種代碼就是托管代碼。所有的C#、VB.NET、JScript.NET默認時都是托管的,但Visual C++默認時不是托管的,必須在編譯器中使用命令行選項(/CLR)才能產生托管代碼。
(2) 托管數據:與托管代碼密切相關的是托管數據。托管數據是由公共語言運行的垃圾回收器進行分配和釋放的數據。默認情況下,C#、Visual Basic 和 JScript.NET 數據是托管數據。不過,通過使用特殊的關鍵字,C# 數據可以被標記為非托管數據。Visual C++數據在默認情況下是非托管數據,即使在使用 /CLR 開關時也不是托管的。
(3) 托管類: 盡管Visual C++數據在默認情況下是非托管數據,但是在使用C++的托管擴展時,可以使用"__gc"關鍵字將類標記為托管類。就像該名稱所顯示的那樣,它表示類實例的內存由垃圾回收器管理。另外,一個托管類也完全可以成為 .NET 框架的成員,由此可以帶來的好處是,它可以與其他語言編寫的類正確地進行相互操作,如托管的C++類可以從Visual Basic類繼承等。但同時也有一些限制,如托管類只能從一個基類繼承等。需要說明的是,在托管C++應用程序中既可使用托管類也可以使用非托管類。這里的非托管類不是指標准C++類,而是使用托管C++語言中的__nogc關鍵字的類。
3、托管C++與標准C++的主要區別
盡管托管C++是從標准C++建立而來的,但它與標准C++有着本質上的區別,這主要體現在以下幾個方面:
(1) 廣泛采用"名稱空間"(namespace)
名稱空間是類型的一種邏輯命名方案,.NET使用該命名方案用於將類型按相關功能的邏輯類別進行分組,利用名稱空間可以使開發人員更容易在代碼中瀏覽和引用類型。當然,我們也可將名稱空間理解成是一個"類庫名"。
(2) 基本數據類型的變化
我們知道,標准C++語言的數據類型是非常豐富的。而托管C++的數據類型更加豐富,不僅包含了標准C++中的數據類型,而且新增了__int64 (64位整型)、Decimal(96位十進制數)、String*(字符串類型)和Object*(對象類型)等類型,表1-1列出它們各自數據類型。
(3) 新增三個托管C++類型:__gc class、__value class和__gc interface
一個__gc類或結構意味着該類或結構的生命周期是由.NET開發平台自動管理及垃圾自動收集,用戶不必自已去調用delete來刪除。定義一個__gc類或結構和標准C++基本相似,所不同的是在class或struct前加上__gc。
////////將輸入cli::array<unsigned char>轉換為cv::Mat//////////////////
pin_ptr <System : :Byte > p1 = &pCBuf1[ 0];
unsigned char * pby1 = p1;
cv : :Mat img_data1(pCBuf1 - >Length, 1,CV_8U,pby1);
cv : :Mat img_object = cv : :imdecode(img_data1,IMREAD_UNCHANGED);
if ( !img_object.data)
return nullptr;
System : :Drawing : :Bitmap ^ MatToBitmap( const cv : :Mat & img)
{
if (img.type() != CV_8UC3)
{
throw gcnew NotSupportedException( "Only images of type CV_8UC3 are supported for conversion to Bitmap");
}
//create the bitmap and get the pointer to the data
PixelFormat fmt(PixelFormat : :Format24bppRgb);
Bitmap ^bmpimg = gcnew Bitmap(img.cols, img.rows, fmt);
BitmapData ^data = bmpimg - >LockBits(System : :Drawing : :Rectangle( 0, 0, img.cols, img.rows), ImageLockMode : :WriteOnly, fmt);
Byte *dstData = reinterpret_cast <Byte * >(data - >Scan0.ToPointer());
unsigned char *srcData = img.data;
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;
}
-
Scan0:數據矩陣在內存中的地址。
-
Stride:數據矩陣中的行寬,以byte為單位。可能會擴展幾個Byte,后面會介紹。
-
PixelFormat:像素格式,這對矩陣中字節的定位很重要。
-
Width:位圖的寬度。
-
Height:位圖的高度。
具體關系見下圖:
-
32位RGB:假設X、Y為位圖中像素的坐標,則其在內存中的地址為scan0+Y*stride+X*4。此時指針指向藍色,其后分別是綠色、紅色,alpha分量。
-
24位RGB:scan0+Y*stride+X*3。此時指針指向藍色,其后分別是綠色和紅色。
-
8位索引:scan0+Y*stride+X。當前指針指向圖像的調色盤。
-
4位索引:scan0+Y*stride+(X/2)。當前指針所指的字節包括兩個像素,通過高位和低位索引16色調色盤,其中高位表示左邊的像素,低位表示右邊的像素。
-
1位索引:scan0+Y*stride+X/8。當前指針所指的字節中的每一位都表示一個像素的索引顏色,調色盤為兩色,最左邊的像素為8,最右邊的像素為0 。(TODO EMGUCV ISSUE)
int GOClrClass : :allTest( int a, int b, int * c);
{
*c =a +b;
return *c;
}
{
int * value = stackalloc int[ 1];
value[ 0] = 0;
int iret = client.allTest( 2, 3, value);
}
{
System : :String ^ retstr = "fsdfsdf";
return retstr;
}