Adding two Mat into a window in OpenCV - c++

According to the link of Operations on Arrays of OCV, I was not finding the way to have two different Mat and putting them into an only window which displays both of the images.
PS: It's not about merging images into a single one.
Any ideas?

Use Qt and this function:
how to convert an opencv cv::Mat to qimage
Highgui does not support several matrices per window yet.

I don't think it's possible to do it in pure opencv, as opencv is image processing library and support bare minimum of user interface, with a few functionality.
You can create a bigger Mat which contains your original two images. In order to be able to distinguish images from each other you can create a black line boundry, e.g:
// the 20 there is an example for border between images
Mat display = Mat::zeros ( MAX ( image1.rows, image2.rows ), image1.cols + 20 + image2.cols, image1.type() );
image1.copyTo ( Mat ( display, Rect ( 0, 0, image1.cols, image1.rows ) ) );
image2.copyTo ( Mat ( display, Rect ( image1.cols + 20, 0, image2.cols, image2.rows ) ) );

I was thinking about this exact same thing and came across your question. Given the way Mat works, it is really just a smart pointer, i.e. has header information that defines how the data it points to is organized.
This means that you can't use the mat container, and everything display related right now only uses one mat object at a time. So a nice easy valid way is not easily achievable.
Warning, you now are responsible for handling the memory yourself
If you are extremely determined to do it (i know that feeling, and i did it this way since i couldn't find a better option). You could allocate a contiguous space in memory for your data buffer.
Now, you can partition that memory as you see fit for your multiple images. i.e. using pointer arithmetic. You need to make sure the images you want to display are next to each other.
At this point, you can create a Mat object that covers the whole memory space of your 2 objects using Mat(cv::Size size, int type, void *buffer).
Of course, using this method, they are permanently stuck together in a certain formation.
Obviously, this can also be done the other way around, i.e. create the mat object and let it allocate the space, and then take the data pointer from the Mat object using the uchar *data member (remember to cast to your preferred type for your pointer arithmetic). And you can create 2 new Mat objects using that buffer.
Sadly, this method doesn't allow you do horizontal concatenation because of the way Mat objects are represented in memory.

Related

Why does any OpenCv (C++) substract operation result in a full black image?

I want to start with that I am certainly no beginner using OpenCV. But I have been mostly using it in C#, and now I am completely lost on a simple issue on the C++ variant that I cannot seem to solve...
Issue: Whenever I use any operation that will subtract one image from another, the output image is 100% black. With 1 in 50 images showing partly the expected result, but also partly being full black (I will link example images in the end). To clarify, with full black I mean every pixel value 0. I am expecting quite some visable noise after this operation, but none is present.
Here is my simple piece of debugging code:
Mat moveImage, stillImage, subst;
while(capture) {
moveImage = CameraClass::cameraVector[0]->imageList.front().clone();
stillImage = CameraClass::cameraVector[0]->imageList.back().clone();
cv::resize(moveImage, moveImage, Size(moveImage.cols * 0.5, moveImage.rows * 0.5), INTER_LINEAR);
cv::resize(stillImage, stillImage, Size(stillImage.cols * 0.5, stillImage.rows * 0.5), INTER_LINEAR);
cv::addWeighted(moveImage, 1, stillImage, -1, 0, subst, CV_8UC1);
stringstream ss, ss2, ss3;
ss << path + "/camtest/image" + to_string(ui32FrameCount) + ".png";
ss2 << path + "/camtest2/image" + to_string(ui32FrameCount) + ".png";
ss3 << path + "/camtest3/image" + to_string(ui32FrameCount) + ".png";
string filepath1 = ss.str();
string filepath2 = ss2.str();
string filepath3 = ss3.str();
cv::imwrite(filepath1, moveImage);
cv::imwrite(filepath2, stillImage);
cv::imwrite(filepath3, subst);
}
My results here are 2 perfectly nice images being saved from moveImage and stillImage, while subst is just completely black.
The addWeighted option is already an attempt to solve it from my side. I have also tried:
cv::subtract(moveImage, stillImage, subst);
And also the C++ operation:
subst = moveImage - stillImage;
All resulting in the same result, black images. I tried adding the images in all these different ways, and the output result is completely fine. So it must be something with the subtract operation and perhaps values dropping below 0? But a CV_8UC1 MatType should truncate below 0 values automatically right?
Here are some example images and results:
Partly black, partly noise (Had to amplify the noise or else it got lost in upload compression)
100% black image result
moveImage frame 1
stillImage frame 1
Additional information that could be important:
My code is Threaded/Asynchronous. This exmaple piece of code is in one thread and isolated though. Since moveImage and stillImage are fine I do not see why other threads or code could badly influence my subtract operation. Subst only exists in this piece of code and is not used or accessable anywhere else.
I am not releasing my Mat resources at the moment. I have tried doing that in the debugging process but it made no difference in the results.
Right now all 3 Mat variables are declared once before the loop of this code and then used over and over. I don't know if that could be an issue, but I also already tried declaring them again in each loop.
Every loop of this code stillImage and moveImage are updated with a new frame from a camera. Maybe the subtract operation somehow keeps a reference somewhere instead of a copy?
The size of moveImage and stillImage is exactly the same and does not change throughout the runtime.
It can easily happen that cv::Mat share their data memory within lists or arrays so that if on of the images is updated, all the images still point to the same memory. For example if capturing updates the same cv::Mat element in every iteration and this cv::Mat is pushed back to a list or vector without deep-copying the pixels (with .clone() or .copyTo() ).
std::vector<cv::Mat> buffer;
cv::Mat img;
while(true)
{
bool success = capture.read(img);
//buffer.push_back(img); // in this case Mat objects will share their memory!
buffer.push_back(img.clone()); // this one is ok
}
Another, less common case is to initialize a cv::Mat with known image size and to create a container with a specific size and a default-object. In that case, all the objects will share their data memory.
cv::Mat img = cv::Mat::zeros(cv::Size(knownWidth, knownHeight), CV_8UC3);
// WARNING: this initialized element creation will lead to shared memory between different Mat objects in the buffer!
std::vector<cv::Mat> buffer(BUFFER_SIZE, img);
The reason in both cases is that cv::Mat is basically some reference counting smart header which can be copied without the need to copy underlying pixel memory space, so an explicit deep-copying of data is necessary if memory shall not be shared.
For debugging of such cases:
Make sure that within your function/loop, both images aren't identical. You can do that with cv::absdiff and thresholding the result by > 0. If there are any white pixels, the image content isnt identical.

Change columns order in opencv mat without copying data

I have an opencv mat which has M rows and 3 columns, is there a way to reorder the mat such that the first and last (i.e., third) columns are switched while the middle column kept in place without copying the data?
OpenCV data is an array of pixels. Sometimes you can get a column or a rectangle view of an image (like with col() ). In which the data is not continuous, and it is calculated, as far as I know, with a step between rows. However the data is shared and is still an array.
Then the question becomes: can I swap two portions of an array without copying the data? Not as far as I know.
You can use optimized functions of OpenCV to swap them, but the data will be copied.
Also, non continuous data is way slower than continuous data in OpenCV functions. More can be read here.
You can use the OpenCV function flip for that. As an example the following code flips an image about the mid column.
int main ()
{
Mat img, flipped; //your mat
img =imread("lena.jpg");
flip(img,flipped ,1); // flipped is the output
imshow("img",flipped);
waitKey(0);
}

How to write opencv cv::Mat image directly in boost shared memory

I have two processes who want to share cv::Mat image information, and I want to use the boost managed_shared_memory to realize it. Since copying an image is really time consuming, so I am trying to find a way to write the image directly to the shared memory when it first appears.
However, since cv::Mat is only a header who has the a pointer to the image data, and the data locates somewhere else, I couldn't realize my idea. I have some test code but they are very chaos and can not work, so I think I am in the totally wrong direction. Anyone has experience about this? thank you!
The cv::Mat.ptr() function gives you the first pointer of an OpenCV image.
The size of the data buffer is equal to Channels * Height * Width * elmsize, so you can just use
memcpy(dest, image.ptr(), Channels * Height * Width) if the elements are 1 byte each (based on CvType).
Caveats:
- The image must be continuous. Use isContinuous() to check. If it fails, clone() the image to get a continuous copy.
- To retrieve the image from Shared memory, you will have to construct a new cv:Mat with the same height, width, channels, CvType and step. Then use memcpy.
See Shared Memory Example for a minimal working example.

Taking a screenshot of a particular area

Looking for a way for taking a screenshot of a particular area on the screen in C++. (So not the whole screen) Then it should save it as .png .jpg whatever to use it with another function afterwards.
Also, I am going to use it, somehow, with openCV. Thought i'd mention that, maybe it's a helpful detail.
OpenCV cannot take screenshots from your computer directly. You will need a different framework/method to do this. #Ben is correct, this link would be worth investigating.
Once you have read this image in, you will need to store it into a cv:Mat so that you are able to perform OpenCV operations on it.
In order to crop an image in OpenCV the following code snippet would help.
CVMat * imagesource;
// Transform it into the C++ cv::Mat format
cv::Mat image(imagesource);
// Setup a rectangle to define your region of interest
cv::Rect myROI(10, 10, 100, 100);
// Crop the full image to that image contained by the rectangle myROI
// Note that this doesn't copy the data
cv::Mat croppedImage = image(myROI);

How do I crop an open CV matrix with an rectangle that overlaps the boundary of the source image

There is an assert in the implementation of cropping a matrix that prevents the cropRect from overlapping the edges of the source image.
// Asserts that cropRect fits inside the image's bounds.
cv::Mat croppedImage = image(cropRect);
I want to lift this restriction and be able to do this using black pixels that lie outside the image. Is this possible?
The answer is: technically it is possible but you really really don't want to do it. There no "black pixels" that lie around your image. Your 'image' allocated just enough memory for himself, and that's it. So if you try to access pixels outside of allocated memory you will get runtime error. If you want to have some black pixels you will have to do that yourself in the way that #ffriend described. image(cropRect) is not allocating anything, it just creating new pointer to memory that already exist.
In case you are still curious about how this crop can be done, OpenCV is doing the following:
// check that cropRect is inside the image
if ((cropRect & Rect(0,0,image.cols,image.rows)) != cropRect)
return -1; // some kind of error notification
// use one of Mat constructors (x, y, width and height are taken from cropRect)
Mat croppedImage(Size(width,height), image.type(), image.ptr(y)+x, image.step);
You can skip the test and go to initialization, but as I said this is a good recipe for disaster.