我有一個 C DLL,它的函式輸入是cv::Mat. 當我嘗試使用從 opencvsharp 接收到的幀輸入呼叫此函式時,它會出現錯誤,就像Mat在 C# 中一樣。我該如何解決這個問題?
如何Mat在 C 和MatC# 中匹配以防止錯誤?
我需要更改 C 函式還是需要在 C# 中執行其他操作才能訪問Mat作為 C 函式輸入的資料?
C 函式:
extern "C" __declspec(dllexport) vector<std::string> __cdecl ProcessFrame(cv::Mat image);
vector<std::string> ProcessFrame(cv::Mat image)
{
int k = 0;
cv::Mat croppedimage;
cv::Mat finalcropped;
string filename;
Mat result_image;
vector<string> listName;
Module module = torch::jit::load("D:/Project/libfacedetection/example/converted.pt");
int* pResults = NULL;
unsigned char* pBuffer = (unsigned char*)malloc(DETECT_BUFFER_SIZE);
if (!pBuffer)
{
fprintf(stderr, "Can not alloc buffer.\n");
return listName;
}
TickMeter cvtm;
cvtm.start();
pResults = facedetect_cnn(pBuffer, (unsigned char*)(image.ptr(0)), image.cols, image.rows, (int)image.step);
int face_num = (pResults ? *pResults : 0);
if (*pResults != 0)
{
result_image = image.clone();
for (int i = 0; i < face_num; i )
{
try
{
short* p = ((short*)(pResults 1)) 142 * i;
int confidence = p[0];
int x = p[1];
int y = p[2];
int w = p[3];
int h = p[4];
char sScore[256];
if (confidence >= 95)
{
//////////////////////////////////////////////////////////////////////////////
////////////// Rotate and Crop
//////////////////////////////////////////////////////////////////////////////
short angle = Face_rotate(p);
cv::Rect rc = AlignCordinates(x, y, w, h, result_image.cols, result_image.rows);
cv::Rect myroi(x, y, w, h);
cv::Rect newroi((x - rc.x) / 2, (y - rc.y) / 2, w, h);
croppedimage = result_image(rc);
//imshow("1", croppedimage);
croppedimage = croppedimage.clone();
croppedimage = rotate(croppedimage, (angle));
//imshow("Rotate", croppedimage);
croppedimage = croppedimage(newroi).clone();
finalcropped = Mat(112, 112, croppedimage.type());
//imshow("dst", croppedimage);
cv::resize(croppedimage, finalcropped, finalcropped.size());
//imshow("resize", finalcropped);
Mat flipimage;
flip(finalcropped, flipimage, 1);
torch::Tensor img_tensor = torch::from_blob(finalcropped.data, { finalcropped.rows,finalcropped.cols ,3 }, torch::kByte);
torch::Tensor img_tensor_flip = torch::from_blob(flipimage.data, { flipimage.rows, flipimage.cols, 3 }, torch::kByte);
//torch::Tensor img_tensor_final = img_tensor img_tensor_flip;
img_tensor = img_tensor.to(at::kFloat).div(255).unsqueeze(0);
img_tensor = img_tensor.sub_(0.5);
img_tensor = img_tensor.permute({ 0,3,1,2 });
img_tensor_flip = img_tensor_flip.to(at::kFloat).div(255).unsqueeze(0);
img_tensor_flip = img_tensor_flip.sub_(0.5);
img_tensor_flip = img_tensor_flip.permute({ 0,3,1,2 });
at::Tensor output_org = module.forward({ img_tensor }).toTensor();
at::Tensor output_flip = module.forward({ img_tensor_flip }).toTensor();
std::vector<double> out;
for (int i = 0; i < 512; i )
{
out.push_back(output_org[0][i].item().to<double>() output_flip[0][i].item().to<double>());
}
out = l2_norm(out);
std::ifstream file("D:/Project/libfacedetection/example/facebank.json");
json object = json::parse(file);
double min_dis = 1000;
std::string min_name;
for (auto& x : object.items()) {
auto dataSize = std::size(x.value());
std::vector<double> vec1 = x.value();
double res = cosine_similarity_vectors(vec1, out);
res = (res * -1) 1;
//double res = distance(vec1, out);
if (res <= min_dis) {
min_dis = res;
min_name = x.key();
}
}
std::cout << "One Frame " << min_name << " " << min_dis << std::endl;
if (min_dis < 0.8) {
listName.push_back(min_name);
}
else
{
listName.push_back("Unknown");
}
}
else
{
listName.push_back("conf_low");
}
}
catch (const std::exception& ex)
{
cout << "NASHOD" << endl;
//std::cout << ex.what();
}
}
}
else
{
listName.push_back("No_Body");
}
cvtm.stop();
//printf("time = %gms\n", cvtm.getTimeMilli());
//printf("%d faces detected.\n", (pResults ? *pResults : 0));
free(pBuffer);
return listName;
}
C#:
[DllImport("detect-camera.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern List<string> ProcessFrame(Mat image);
private void button1_Click(object sender, EventArgs e)
{
Mat image = Cv2.ImRead("D:/c /ImportCallFunction/ImportCallFunction/123.jpg");
List<string> facelist = ProcessFrame(image);
foreach (var item in facelist)
{
listBox1.Items.Add(item);
}
錯誤:
System.Runtime.InteropServices.MarshalDirectiveException: 'Cannot marshal 'return value': Generic types cannot be marshaled.'

uj5u.com熱心網友回復:
您遇到的錯誤與引數型別無關,cv::Mat而是與宣告為 的函式的回傳型別有關vector<std::string>。
首先,關于引數型別的說明:您可能希望const cv::Mat&避免將整個矩陣復制到每一幀的函式中。所以它會像:
std::vector<std::string> ProcessFrame(const cv::Mat& image)
您還需要一個用 C /CLI 撰寫的包裝函式,用作 C# 代碼和 C 代碼之間的介面。它為輸入引數和回傳值執行函式所需的自定義編組。請注意,您應該將包裝函式放置在編譯的編譯單元中/clr(以啟用 C /CLI)。您的原始(本機)函式不需要使用該/clr選項進行編譯。包裝函式宣告可能如下所示:
System::Collections::Generic<System::String>^ ProcessFrameWrapper(
OpenCvSharp::Mat^ mat);
在 C# 代碼中,您現在將呼叫包裝函式:
[DllImport("detect-camera.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern List<string> ProcessFrameWrapper(Mat image);
// ...
List<string> facelist = ProcessFrameWrapper(image);
總而言之,您將需要以下檔案:
DetectCamera.h :
// ...
std::vector<std::string> ProcessFrame(const cv::Mat& image);
// ... other native declarations
DetectCamera.cpp :
// ...
std::vector<std::string> ProcessFrame(const cv::Mat& image)
{
// actual function implementation
}
// ... other function implementations
DetectCameraWrapper.h :
// ...
System::String^ ProcessFrameWrapper(OpenCvSharp::Mat^ mat);
// ... other wrapper functions ...
DetectCameraWrapper.cpp :
// ...
System::Collections::Generic<System::String>^ ProcessFrameWrapper(
OpenCvSharp::Mat^ mat)
{
var names = gcnew System::Collections::Generic<System::String>();
auto matNativePtr =
reinterpret_cast<cv::Mat*>(marshal_as<void*>(mat->CvPtr));
auto namesNative = ProcessFrame(*matNativePtr);
for (const auto& nameNative : namesNative)
{
names->Add(marshal_as<System::String^>(nameNative));
}
return names;
}
// ... other wrapper function implementations
DetectCamera.cs :
// ...
[DllImport("detect-camera.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern List<string> ProcessFrameWrapper(Mat image);
private void button1_Click(object sender, EventArgs e)
{
Mat image = Cv2.ImRead("D:/c /ImportCallFunction/ImportCallFunction/123.jpg");
List<string> facelist = ProcessFrameWrapper(image);
foreach (var item in facelist)
{
listBox1.Items.Add(item);
}
// ...
These files can be organized in two or three separate projects:
- DetectCamera.cs is placed in a C# project - call it ProjCSharp.
- DetectCameraWrapper.cpp is placed in a C /CLI DLL project (with
/clr) - call it ProjWrapper. - DetectCamera.cpp could be placed either within the same project ProjWrapper, or in a separate native library project (.lib) - call it ProjNative. I recommend the latter. If it is placed in a separate library (ProjNative), the DLL project ProjWrapper must be linked to the library ProjNative.
我建議將本機代碼放在單獨的庫中的原因是模塊化和代碼可重用性。
uj5u.com熱心網友回復:
我不太記得Interop并且從來不是專家,盡管我曾經做過非常高級的事情。
@misoboute 的建議是使用我從未使用過的CLI,所以我不知道。這樣做肯定是可能的,正如他所解釋的那樣,這樣的 C 將理解通用 C# 串列并能夠成功編組。
但在我看來,這不是你的問題。您只想回傳一堆字串。它確實不必定義為List<string>. 它可以定義為string[]一個字串陣列。
所以我會嘗試
[DllImport("detect-camera.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern string[] ProcessFrame(Mat image);
然后,在我的 C 代碼中,通過將字串向量表示為陣列來回傳字串陣列:
如何將向量轉換為陣列
對不起,這就是我對你的全部。在此之后,您可能會遇到其他問題。
uj5u.com熱心網友回復:
從 C 到 C#,您有兩種“標準”的方式。
第一個是 C /CLI。在這種情況下,您將構建一個 C /CLI 庫,該庫采用 std::vectorstd::string 并將其轉換為 System::vectorSystem::string。然后你可以在 C# 中自由地將它用作 System.String[]。
另一個是COM。在那里您創建了一個 COM 介面,該介面回傳一個包含 BSTR 字串的 SAFEARRAY。然后通過 C# 中的 System.Runtime.InteropServices 實體化此 COM 介面。SAFEARRAY 是一個 Object[],它可以被區分為單個字串物件。
將 C 介面加載到 C# 的工具基本上僅限于 C。任何 C 都會失敗,Pete 提供了“非標準”方法。(它作業得很好,只是不是 MS 希望你做的。)
代碼:
extern "C" __declspec(dllexport) LPSAFEARRAY ListDevices();
LPSAFEARRAY ProcessFrame(int width, int height, unsigned char* data)
{
CComSafeArray<BSTR> a(listName.size()); // cool ATL helper that requires atlsafe.h
std::vector<std::string>::const_iterator it;
int i = 0;
for (it = listName.begin(); it != listName.end(); it, i)
{
// note: you could also use std::wstring instead and avoid A2W conversion
a.SetAt(i, A2BSTR_EX((*it).c_str()), FALSE);
}
return a.Detach();
}
C#:
[DllImport("detectcameratest.dll")]
[return: MarshalAs(UnmanagedType.SafeArray)]
private extern static string[] ProcessFrame2(int width, int height, IntPtr data);
private void button2_Click(object sender, EventArgs e)
{
Mat img = Cv2.ImRead(@"C:\Users\Subtek\source\repos\WindowsFormsApp17\WindowsFormsApp17\bin\x64\Debug\1.jpg");
var facelist = ProcessFrame2(img.Width, img.Height, img.Data);
foreach (var s in facelist)
{
listBox1.Items.Add(s);
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/372313.html
