opencvsharp
opencvsharp copied to clipboard
[Question] How to use InputOutputArray as InputArray?
Summary of your issue
I am trying to define a function which accepts an InputOutputArray
to perform various tasks with Cv2 method calls. When I try to pass this into the parameter for InputArray
I am getting a compiler error that it cannot convert from InputOutputArray
to InputArray
.
From my research this is the intended use for this class in C++ (here is one source discussing this usage) and the OpenCV documentation shows the inheritance being InputArray -> OutputArray -> InputOutputArray
. Looking at the source code in this library it looks like OutputArray
does not inherit from InputArray
and this might be the cause of the issue? If so, how can I work around this?
Example code:
This is a simplified example of how I want to use this parameter:
public static void DoStuff(InputOutputArray image)
{
Cv2.Resize(image, image, new Size(500, 500));
Cv2.CvtColor(image, image, ColorConversionCodes.BGR2GRAY);
}
Alternatively, if this is not currently possible, I would like to do something like this instead.
public static void DoStuff(InputArray src, OutputArray dst)
{
Cv2.Resize(src, dst, new Size(500, 500));
Cv2.CvtColor(dst, dst, ColorConversionCodes.BGR2GRAY);
}
The issue I run into here is the CvtColor
call now has a compiler error since dst
is an OutputArray
and cannot be passed as an InputArray
. Is there accomplish this without allocating memory to a new Mat
?
I do not see how CvtColor in your example can run in place. Input is 24bpp, output is 8bpp. Same with Resize. You will need to allocate data array for pixels twice in DoStuff. Even if you somehow trick OpenCV to re-use Mat, it will not save you much, because pixel data array will need to be re-allocated anyways.
I would write it like:
public static Mat DoStuff(Mat src) { Mat resized = new Mat() Cv2.Resize(src, resized, new Size(500, 500)); Mat gray = new Mat(); Cv2.CvtColor(resized , gray , ColorConversionCodes.BGR2GRAY); resized.Release(); return gray; }
or do using (Mat resized = new Mat()) { ... }
@BalashovK Thank you for the response. I am still learning OpenCV and maybe miss some nuances in it's usage, so my exact example might not be the best. The original code I am working with (written in python) is passing the same Mat
instance into both the InputArray
and OutputArray
parameters which is what I am trying to understand.
I see in articles like this one with C++ examples like the below snippet which also recommend this approach to reduce allocating memory for a copy of the original image
// Revision 3
void preprocess(InputArray input, OutputArray output) {
Mat gray;
cvtColor(input, gray, COLOR_BGR2GRAY);
blur(gray, output, Size(3, 3));
}
And they give the following explanation as follows:
Now instead of returning a new Mat object, whatever we pass into output will be written to. Why would we want to do this? Well, when you’re writing computer vision code, you need to be careful about how your image data is being passed around. After all, the images you’re passing around are uncompressed; a 4000x4000 image in BGR888 format would take around 48 megabytes of space in RAM. That’s not a lot, but it adds up fast, particularly if you’re parallelizing or running on a mobile platform. If we gave that image to Revision 2 of the preprocess() function, we would be allocating two copies of the image; one temporary (gray) and one that we keep and return ( output). With Revision 3, we’ve moved the decision to allocate the output from inside of the function to wherever it gets called. If you want a copy, call preprocess(someMat, someOtherMat). If you want to overwrite the original Mat, just call preprocess(someMat, someMat), using the same object for the input and the output. If you’ve wondered why so many of OpenCV’s functions write outputs to an OutputArray parameter instead of returning a Mat, this is why!
(Revision 2 in the article is taking a similar approach to your recommendation, allocating a new Mat
object.
And then finally, they go on to recommend using InputOutputArray
when you know you always want to overwrite the existing Mat
with the final result
Now let’s say we always want preprocess() to operate in-place. We can make use of OpenCV’s InputOutputArray to completely avoid allocating new images in preprocess() and make the function a little easier to read
// Revision 4
void preprocess(InputOutputArray input) {
cvtColor(input, input, COLOR_BGR2GRAY);
blur(input, input, Size(3, 3));
}
So this is the heart of the original question: is it possible to create a function with an InputOutputArray
which can be passed as an InputArray
like is possible in C++? And to your point, is the example form the article a valid one, or is there only specific scenarios where this would make sense?