在C#中调整图像大小并将其发送到OpenCV会导致图像失真

这是与此相关的后续问题。 基本上,我有一个使用OpenCV进行图像处理的DLL。 有两种方法,一种接受image-Path ,另一种接受cv::Mat 。 使用image-path工作正常。 接受image那个是有问题的。

这是接受文件名(DLL)的方法:

 CDLL2_API void Classify(const char * img_path, char* out_result, int* length_of_out_result, int N) { auto classifier = reinterpret_cast(GetHandle()); cv::Mat img = cv::imread(img_path); cv::imshow("img recieved from c#", img); std::vector result = classifier->Classify(std::string(img_path), N); std::string str_info = ""; //... *length_of_out_result = ss.str().length(); } 

这是接受图像(DLL)的方法:

 CDLL2_API void Classify_Image(unsigned char* img_pointer, unsigned int height, unsigned int width, int step, char* out_result, int* length_of_out_result, int top_n_results) { auto classifier = reinterpret_cast(GetHandle()); cv::Mat img = cv::Mat(height, width, CV_8UC3, (void*)img_pointer, step); std::vector result = classifier->Classify(img, top_n_results); //... *length_of_out_result = ss.str().length(); } 

这是C#应用程序中的代码:DllImport:

 [DllImport(@"CDll2.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] static extern void Classify_Image(IntPtr img, uint height, uint width, int step, byte[] out_result, out int out_result_length, int top_n_results = 2); 

将图像发送到DLL的方法:

 private string Classify_UsingImage(Bitmap img, int top_n_results) { byte[] res = new byte[200]; int len; BitmapData bmpData; bmpData = img.LockBits(new Rectangle(0, 0, img.Width, img.Height), ImageLockMode.ReadOnly, img.PixelFormat); Classify_Image(bmpData.Scan0, (uint)bmpData.Height, (uint)bmpData.Width, bmpData.Stride, res, out len, top_n_results); //Remember to unlock!!! img.UnlockBits(bmpData); string s = ASCIIEncoding.ASCII.GetString(res); return s; } 

现在,当我将图像发送到DLL时,这很有效。 如果我使用imshow()来显示收到的图像,图像就会显示得很好。

实际问题:

但是 ,当我调整相同的图像并使用上面相同的方法发送它时,图像会失真。

我需要补充一点,如果我使用下面给定的C#方法调整图像大小,然后保存它,然后使用Classify(std::string(img_path), N);将文件名传递给DLL打开Classify(std::string(img_path), N); 它完美地运作。

这是截图,显示了这种情况的一个例子:
从C`发送的图像没有resize:

首次调整相同的图像大小,然后发送到DLL:

这里首先调整图像大小(在C#中),保存到磁盘然后将其文件filepath发送到DLL:

这是负责resize的片段(C#):

 ///  /// Resize the image to the specified width and height. ///  /// The image to resize. /// The width to resize to. /// The height to resize to. /// The resized image. public static Bitmap ResizeImage(Image image, int width, int height) { var destRect = new Rectangle(0, 0, width, height); var destImage = new Bitmap(width, height); destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution); using (var graphics = Graphics.FromImage(destImage)) { graphics.CompositingMode = CompositingMode.SourceCopy; graphics.CompositingQuality = CompositingQuality.HighQuality; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; using (var wrapMode = new ImageAttributes()) { wrapMode.SetWrapMode(WrapMode.TileFlipXY); graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode); } } return destImage; } 

这是原始图像

这是使用文件路径读取图像的Classify方法:

 std::vector Classifier::Classify(const std::string & img_path, int N) { cv::Mat img = cv::imread(img_path); cv::Mat resizedImage; std::vector results; std::vector output; // cv::imshow((std::string("img classify by path") + type2str(img.type())), img); if (IsResizedEnabled()) { ResizeImage(img, resizedImage); output = Predict(resizedImage); } else { output = Predict(img); img.release(); } N = std::min(labels_.size(), N); std::vector maxN = Argmax(output, N); for (int i = 0; i < N; ++i) { int idx = maxN[i]; PredictionResults r; r.label = labels_[idx]; r.accuracy = output[idx]; results.push_back(r); } return results; } 

这是上面方法中使用的ResizeImage

 void Classifier::ResizeImage(const cv::Mat & source_image, cv::Mat& resizedImage) { Size size(GetResizeHeight(), GetResizeHeight()); cv::resize(source_image, resizedImage, size);//resize image CHECK(!resizedImage.empty()) << "Unable to decode image "; } 

问题2:
resize后的失真,我面临着在C#中resize和使用OpenCV本身resize之间的差异。
我已经创建了另一种使用EmguCV的方法(也在下面给出)并传递了所需的信息,并且没有遇到任何这样的扭曲,当我们在C#中调整图像并将其发送到DLL时会发生这种扭曲。
但是 ,这种差异使我想要了解导致这些问题的原因。
这是使用EmguCV.Mat的方法,无论resize,它都可以运行:

 private string Classify_UsingMat(string imgpath, int top_n_results) { byte[] res = new byte[200]; int len; Emgu.CV.Mat img = new Emgu.CV.Mat(imgpath, ImreadModes.Color); if (chkResizeImageCShap.Checked) { CvInvoke.Resize(img, img, new Size(256, 256)); } Classify_Image(img.DataPointer, (uint)img.Height, (uint)img.Width, img.Step, res, out len, top_n_results); string s = ASCIIEncoding.ASCII.GetString(res); return s; } 

我为什么在意?
因为,当我使用OpenCVresize(当我使用EMguCV的CvInvoke.resize()cv::resize() )时,我获得了不同的准确度,而不是我在C#中调整图像大小,将其保存到磁盘并发送图像openCV的路径。
因此,当我在C#中处理图像时,我需要修复发生的失真,或者我需要理解为什么OpenCV中的大小调整与C#resize的结果不同。

总结到目前为止所提出的问题和要点:

  1. 所有情况都完好无损,如果我们在C#应用程序中调整图像大小,并像以前一样正常传递信息,图像将会失真(例如上面给出的例子)
  2. 如果我们调整图像大小,将其保存到磁盘,并将其文件名提供给OpenCV以创建新的cv::Mat ,它可以完美地工作而不会出现任何问题。
  3. 如果我使用EmugCV而不是使用Bitmap ,请使用Emug.CV.Mat并使用来自C#的mat对象发送所需的参数,不会发生失真。

    但是 ,从C#resize的图像(参见#2)获得的精度与使用OpenCV从resize的图像获得的精度不同。 如果我使用C#中的CvInvoke.Resize()手动调整图像大小并将结果图像发送到DLL,或者发送原始图像(使用EmguCV)并使用cv::resize()在C ++代码中resize,这没有任何区别cv::resize() 。 这是阻止我使用EmguCV或传递图像原始图像并使用OpenCV在DLL内resize的原因。

以下是具有不同结果的图像,显​​示了以下问题:

 --------------------No Resizing------------------------------ 1.Using Bitmap-No Resize, =>safe, acc=580295 2.Using Emgu.Mat-No Resize =>safe, acc=0.580262 3.Using FilePath-No Resize, =>safe, acc=0.580262 --------------------Resize in C#------------------------------ 4.Using Bitmap-CSharp Resize, =>unsafe, acc=0.770425 5.Using Emgu.Mat-EmguResize, =>unsafe, acc=758335 6.**Using FilePath-CSharp Resize, =>unsafe, acc=0.977649** --------------------Resize in DLL------------------------------ 7.Using Bitmap-DLL Resize, =>unsafe, acc=0.757484 8.Using Emgu.DLL Resize, =>unsafe, acc=0.758335 9.Using FilePath-DLL Resize, =>unsafe, acc=0.758335 

我需要获得我在#6获得的准确度。 正如你可以看到EmguCVresize以及DLL中使用的OpenCVresize函数,行为相似并且不能按预期工作(例如#2)!,应用于图像的C#resize方法是有问题的,而如果它是resize,保存并传递文件名,结果会很好。

您可以在此处看到描述不同场景的屏幕截图: http : //imgur.com/a/xbgIQ

我做的是使用imdecode 作为EdChum建议 。 这就是DLL和C#中的函数现在的样子:

 #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); //... } 

实际方法:

 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(GetHandle()); vector inputImageBytes(img_pointer, img_pointer + data_len); cv::Mat img = imdecode(inputImageBytes, CV_LOAD_IMAGE_COLOR); cv::imshow("img just recieved from c#", img); std::vector result = classifier->Classify(img, top_n_results); //... *length_of_out_result = ss.str().length(); } 

这是C#DllImport:

 [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); 

这是将图像发送回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; //this is for situations, where the image is not read from disk, and is stored in the memort(eg image comes from a camera or snapshot) ImageFormat fmt = new ImageFormat(image.RawFormat.Guid); var imageCodecInfo = ImageCodecInfo.GetImageEncoders().FirstOrDefault(codec => codec.FormatID == image.RawFormat.Guid); 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); } 

通过在从C#调整图像大小后执行此操作,我们根本不会遇到任何扭曲。

但是,我无法弄清楚为什么OpenCV部分的resize不能按预期工作!