c++ - How do I send an image from C# to a dll accepting OpenCV Mat properly? -
i have c++ class created c dll used in our c# solution.
need send images dll, don't know proper way of doing this! c++ function signature :
std::vector<prediction> classify(const cv::mat& img, int n = 2); and how went :
tried create wrapper method in dll :
#ifdef cdll2_exports #define cdll2_api __declspec(dllexport) #else #define cdll2_api __declspec(dllimport) #endif #include "../classification.h" extern "c" { cdll2_api void classify_image(unsigned char* img_pointer, unsigned int height, unsigned int width, char* out_result, int* length_of_out_result, int top_n_results = 2); //... } code in dll:
cdll2_api void classify_image(unsigned char* img_pointer, unsigned int height, unsigned int width, char* out_result, int* length_of_out_result, int top_n_results) { auto classifier = reinterpret_cast<classifier*>(gethandle()); cv::mat img = cv::mat(height, width, cv_32fc3, (void*)img_pointer); std::vector<prediction> result = classifier->classify(img, top_n_results); //misc code... *length_of_out_result = ss.str().length(); } and in c# code wrote :
[dllimport(@"cdll2.dll", callingconvention = callingconvention.cdecl, charset = charset.ansi)] static extern void classify_image(byte[] img, uint height, uint width, byte[] out_result, out int out_result_length, int top_n_results = 2); private string classify(bitmap img, int top_n_results) { byte[] result = new byte[200]; int len; var img_byte = (byte[])(new imageconverter()).convertto(img, typeof(byte[])); classify_image(img_byte, (uint)img.height, (uint)img.width,res, out len, top_n_results); return asciiencoding.ascii.getstring(result); } but whenever try run code, access violation error :
an unhandled exception of type 'system.accessviolationexception' occurred in classification using dotnet.exe
additional information: attempted read or write protected memory. indication other memory corrupt.
and exception error says :
{"attempted read or write protected memory. indication other memory corrupt."}
deeper investigation code made clear that, exception error in function :
void classifier::preprocess(const cv::mat& img, std::vector<cv::mat>* input_channels) { /* convert input image input image format of network. */ cv::mat sample; if (img.channels() == 3 && num_channels_ == 1) cv::cvtcolor(img, sample, cv::color_bgr2gray); else if (img.channels() == 4 && num_channels_ == 1) cv::cvtcolor(img, sample, cv::color_bgra2gray); else if (img.channels() == 4 && num_channels_ == 3) cv::cvtcolor(img, sample, cv::color_bgra2bgr); else if (img.channels() == 1 && num_channels_ == 3) cv::cvtcolor(img, sample, cv::color_gray2bgr); else sample = img; //resize image according input cv::mat sample_resized; if (sample.size() != input_geometry_) cv::resize(sample, sample_resized, input_geometry_); else sample_resized = sample; cv::mat sample_float; if (num_channels_ == 3) sample_resized.convertto(sample_float, cv_32fc3); else sample_resized.convertto(sample_float, cv_32fc1); cv::mat sample_normalized; cv::subtract(sample_float, mean_, sample_normalized); /* operation write separate bgr planes directly * input layer of network because wrapped cv::mat * objects in input_channels. */ cv::split(sample_normalized, *input_channels); check(reinterpret_cast<float*>(input_channels->at(0).data) == net_->input_blobs()[0]->cpu_data()) << "input channels not wrapping input layer of network."; } the access violation occurs when attempted resize image meaning running snippet:
//resize image according input cv::mat sample_resized; if (sample.size() != input_geometry_) cv::resize(sample, sample_resized, input_geometry_); further investigation , debugging (here) made culprit visible!
approach proved plain wrong, or @ least buggy. using code, image on c++ side seemed have been initialized properly, number of channels, height, , width seemed fine.
moment attempt use image, either resizing or showing using imshow(), crash application , giving access violation exception, same error happened when resizing , posted in question.
looking @ answer, changed c# code responsible handing image dll. new code follows :
//dll import [dllimport(@"cdll2.dll", callingconvention = callingconvention.cdecl, charset = charset.ansi)] static extern void classify_image(intptr img, uint height, uint width, byte[] out_result, out int out_result_length, int top_n_results = 2); //... //main code bitmap img = new bitmap(txtimagepath.text); bitmapdata bmpdata = img.lockbits(new rectangle(0, 0, img.width, img.height), imagelockmode.readwrite, pixelformat.format24bpprgb); result = classify_usingimage(bmpdata, 1); img.unlockbits(bmpdata); //remember unlock!!! and c++ code in dll :
cdll2_api void classify_image(unsigned char* img_pointer, unsigned int height, unsigned int width, char* out_result, int* length_of_out_result, int top_n_results) { auto classifier = reinterpret_cast<classifier*>(gethandle()); cv::mat img = cv::mat(height, width, cv_8uc3, (void*)img_pointer, mat::auto_step); std::vector<prediction> result = classifier->classify(img, top_n_results); //... *length_of_out_result = ss.str().length(); } and doing rectified access violations getting previously.
although can send images c# dll, have issue current implementation of mine.
dont know how send opencv type c# needed function, i'm using hardcoded image type can see, , begs question, should when input image grayscale or png 4 channels ?
grateful if assist me in rectifying these issues.
after trying many different approaches, guess beneficial other people seek the same thing, know this. cut long story short (see question), best way find this (as @edchum says it) :
i'd pass file memory opencv dll, should able call imdecode sniff file type, additionally can pass flag
and explained here send pointer dll , there use imdecode decode image. solved lot of issues other approaches introduced. , save alot of headaches.
here code of intrest:
how functions in dll , c# should have looked :
#ifdef cdll2_exports #define cdll2_api __declspec(dllexport) #else #define cdll2_api __declspec(dllimport) #endif #include "classification.h" extern "c" { cdll2_api void classify_image(unsigned char* img_pointer, long data_len, char* out_result, int* length_of_out_result, int top_n_results = 2); //... } the actual method :
cdll2_api void classify_image(unsigned char* img_pointer, long data_len, char* out_result, int* length_of_out_result, int top_n_results) { auto classifier = reinterpret_cast<classifier*>(gethandle()); vector<unsigned char> inputimagebytes(img_pointer, img_pointer + data_len); cv::mat img = imdecode(inputimagebytes, cv_load_image_color); cv::imshow("img recieved c#", img); std::vector<prediction> result = classifier->classify(img, top_n_results); //... *length_of_out_result = ss.str().length(); } here c# dll import:
[dllimport(@"cdll2.dll", callingconvention = callingconvention.cdecl, charset = charset.ansi)] static extern void classify_image(byte[] img, long data_len, byte[] out_result, out int out_result_length, int top_n_results = 2); and actual method sending image dll:
private string classify_usingimage(bitmap image, int top_n_results) { byte[] result = new byte[200]; int len; bitmap img; if (chkresizeimagecshap.checked) img = resizeimage(image, int.parse(txtwidth.text), (int.parse(txtheight.text))); else img = image; imageformat fmt = new imageformat(image.rawformat.guid); var imagecodecinfo = imagecodecinfo.getimageencoders().firstordefault(codec => codec.formatid == image.rawformat.guid); //this situations, image not read disk, , stored in memort(e.g. image comes camera or snapshot) if (imagecodecinfo == null) { fmt = imageformat.jpeg; } using (memorystream ms = new memorystream()) { img.save(ms,fmt); byte[] image_byte_array = ms.toarray(); classify_image(image_byte_array, ms.length, result, out len, top_n_results); } return asciiencoding.ascii.getstring(result); }
Comments
Post a Comment