gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-28 05:01:48 27 4
gpt4 key购买 nike

这是与this one有关的后续问题。基本上,我有一个使用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<Classifier*>(GetHandle());

cv::Mat img = cv::imread(img_path);
cv::imshow("img recieved from c#", img);
std::vector<PredictionResults> 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<Classifier*>(GetHandle());
cv::Mat img = cv::Mat(height, width, CV_8UC3, (void*)img_pointer, step);
std::vector<Prediction> 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#方法调整图像大小,然后保存它,然后将文件名传递给 DLL,然后使用 Classify(std::string(img_path), N);打开它,则效果很好。

这是显示此情况示例的屏幕截图:
从C`发送而未调整大小的图像:



第一次调整同一图像的大小,然后将其发送到DLL时:



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



这是负责调整大小(C#)的代码段:
/// <summary>
/// Resize the image to the specified width and height.
/// </summary>
/// <param name="image">The image to resize.</param>
/// <param name="width">The width to resize to.</param>
/// <param name="height">The height to resize to.</param>
/// <returns>The resized image.</returns>
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;
}

这是 original image

这是 Classify方法,它使用文件路径读取图像:
std::vector<PredictionResults> Classifier::Classify(const std::string & img_path, int N)
{
cv::Mat img = cv::imread(img_path);
cv::Mat resizedImage;
std::vector<PredictionResults> results;
std::vector<float> 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<int>(labels_.size(), N);
std::vector<int> 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:
除了调整大小后出现失真之外,我面临着在C#中调整大小与使用OpenCV本身进行调整之间的差异。
我创建了使用EmguCV的另一种方法(也在下面给出),并传递了所需的信息,并且在我们使用C#调整图像大小并将其发送到DLL时,没有遇到这种失真。
但是,这种差异使我想了解是什么导致了这些问题。
这是使用 EmguCV.Mat的方法,该代码不管大小如何都可以工作:
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;
}

我为什么在乎?
因为,当我使用OpenCV调整大小时(使用EMguCV的 CvInvoke.resize()cv::resize()时),获得的精度与在C#中调整图像大小,将其保存到磁盘并将图像路径发送到openCV时获得的精度不同。
因此,我要么需要解决在C#中处理图像时发生的变形,要么我需要理解为什么OpenCV中的调整大小与C#调整具有不同的结果。

因此,总结到目前为止的问题和要点:
  • 在所有情况下,如果我们在C#应用程序中调整图像的大小,
    并像以前一样正常传递信息,图像将是
    扭曲(上面给出的示例)
  • 如果我们调整图像的大小,将其保存到磁盘上并提供其文件名
    到OpenCV来创建一个新的cv::Mat,它可以完美运行而没有任何问题。
  • 如果我使用EmugCV而不是使用Bitmap,请使用Emug.CV.Mat并使用mat对象发送所需的参数
    C#,不会发生扭曲。

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

  • 以下是具有不同结果的图像,显​​示了这些问题:
    --------------------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的准确性。如您所见,EmguCV调整大小以及DLL中使用的OpenCV调整大小功能,其行为类似且无法按预期工作(即像#2)!,应用于图像的C#调整大小方法存在问题,而如果调整大小,保存并传递文件名,结果会很好。

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

    最佳答案

    我所做的是使用imdecode as EdChum suggested
    这就是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<Classifier*>(GetHandle());
    vector<unsigned char> 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<Prediction> 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(e.g. 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部分上调整大小无法正常工作!

    关于c# - 在C#中调整图像大小并将其发送到OpenCV会导致图像失真,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45753429/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com