This is a follow-up question related to this one. Basically, I have a DLL which uses OpenCV to do image manipulation. There are two methods, one accepting an image-Path
, and the other one accepting a cv::Mat
. The one working with image-path
works fine. The one that accepts an image
is problematic.
Here is the method which accepts the filename (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();
}
Here is the method which accepts the image (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();
}
Here is the code in C# application: 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);
The method which sends the image to the 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;
}
Now, this works well when I send an image to the DLL. if I use imshow()
to show the received image, the image is shown just fine.
The Actual Problem:
However, when I resize the very same image and send it using the very same method above, the image is distorted.
I need to add that, If I resize an image using the given C# method below, then save it, and then pass the filename to the DLL
to be opened using Classify(std::string(img_path), N);
it works perfectly.
Here is the screenshot showing an example of this happening:
Image sent from C` without being resized:
When The same image is first resized and then sent to the DLL:
Here the image is first resized (in C#), saved to the disk and then its filepath
sent to the DLL:
This is the snippet responsible for resizing (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;
}
and this is the original image
This is the Classify
method which uses the filepath to read the images:
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;
}
And this is the ResizeImage
used in the method above :
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 ";
}
Problem 2:
Distortion after resizing aside, I am facing a discrepancy between resizing in C# and resizing using OpenCV itself.
I have created another method using EmguCV (also given below) and passed the needed information and did not face any such distortions which happen when we resize the image in C# and send it to the DLL.
However, this discrepancy made me want to understand what is causing these issues.
Here is the method which uses EmguCV.Mat
is the code that works irrespective of resizing:
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;
}
Why do I care?
Because, I get a different accuracy when I use OpenCV resize (both when I use EMguCV's CvInvoke.resize()
and cv::resize()
) than what I get from resizing the image in C#, saving it to disk and send the image path to the openCV.
So I either need to fix the distortion happening when I deal with images in C#, or I need to understand why the resizing in OpenCV has different results than the C# resizing.
So to summarize issues and points made so far:
- All situations intact, If we resize the image inside C# application, and pass the info normally as we did before, the image will be distorted (example given above)
- If we resize the image, save it to the disk, and give its filename
to the OpenCV to create a new
cv::Mat
, it works perfectly without any issues. If I use EmugCV and instead of working with
Bitmap
, useEmug.CV.Mat
and send the needed parameters using mat object from C#, no distortion happens.However, the accuracy I get from a resized image from C# (see #2), is different than the one I get from the resized image using OpenCV. This doesn't make any difference if I resize the image before hand using
CvInvoke.Resize()
from C#, and send the resulting image to the DLL, or send the original image (using EmguCV) and resizing it in the C++ code usingcv::resize()
. This is what prevents me from using the EmguCV or passing the image original image and resizing it inside the DLL using OpenCV.
Here are the images with different results, showing the issues:
--------------------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
I need to get the accuracy which I get at #6. as you can see the EmguCV resize and also the OpenCV resize function used in the DLL, act similar and don't work as expected (i.e. like #2)!, The C# resize method applied on the image is problematic, while if it is resized, saved and the filename passed, the result will be fine.
You can see the screen shots depicting different scenarios here: https://i.stack.imgur.com/LnMMu.jpg