OpenCV Mat object - get data length - c++

In OpenCV, I'm able to capture frames using VideoCapture in C++, however, when I try to get the data from a frame and calculate length, it just returns me 0.
Below is my sample code:
VideoCapture cap(0);
for(;;) {
Mat frame;
cap >> frame;
int length = strlen((char*) frame.data); // returns 0
}
As I mentioned above that if I save the frame in a PNG file, I can actually see the image so I'm not able to understand why the data length is coming out to be zero.
Any clue?

You can also do:
Mat mat;
int len = mat.total() * mat.elemSize(); // or mat.elemSize1()

The strlen method only works on strings, which are arrays of chars terminated by a special character:
http://www.cplusplus.com/reference/cstring/strlen/
You have cast a Mat type as a char*, so it is not a string.
Building on the solution here, try:
Mat mat;
int rows = mat.rows;
int cols = mat.cols;
int num_el = rows*cols;
int len = num_el*mat.elemSize1();
to get the size of one channel in bytes. Also, use elemSize() if you want all the channels (i.e. you'll get 3 times the value of elemSize1() if the Mat is a 3 channel image).
Take a look here for discussion of the various types Mat can contain:
http://docs.opencv.org/modules/core/doc/basic_structures.html#mat-type

Related

Creating OpenCV Mat from user data results in image with circular shifted columns

I have an image that I load from a file. Is it a .png. I convert this to a 1D array for use in a function via a pointer to the array. When I create a Mat from the 1D pointer, the resulting image looks like it takes the right-most dozen or so columns, and puts them on the left side of the image, almost like a circular shift of columns.
// SAMPLE CODE
Mat img = imread(argv[1], CV_LOAD_IMAGE_ANYDEPTH); // 16U1 png
int ncols = img.cols;
int nrows = img.rows;
//--Create input array and pointer--
uint16_t rawImage[nrows*ncols];
uint16_t *rawImage_ptr = rawImage;
//Assign value to array
for (int i=0;i<(ncols*nrows);i++){
*(rawImage_ptr+i) = img.at<uint16_t>(i);
}
// Create Mat from pointer
Mat image(nrows, ncols, CV_16UC1, &rawImage_ptr);
The result 'image' has some of the right columns wrapped around to the left. Any idea what is going on here?
Images are stored in opencv with each new row starting at a 32bit boundary.
If the number of cols * pixel size isn't a multiple of 4 then each row if the image will be padded.
You should use cv::mat ptr(row) to get a pointer to the start of each row and then loop along a row.

how t get the get the frame information using opencv and c++

is there any function in opencv which can be used to get the last frame in a frame sequence?
I tried to use this
dst = cvCreateImage( cvGetSize(src), IPL_DEPTH_8U, 1 );
but seems just work for IplImage format. I am working with the Mat and the dst should be float pointer.
Depending on how your frames are stored, you might want something like this:
float* frames; // pointer to array of floats containing N frames;
float* last_frame = frames + (N - 1) * rows * cols;
cv:Mat dst(rows, cols, CV_32FC1, last_frame);

Getting the difference between two frames in opencv

I'm trying to get the the difference between two cv::Mat frames in OpenCv. So here is what I tried,
#include<opencv2\opencv.hpp>
#include<opencv2\calib3d\calib3d.hpp>
#include<opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
int main ()
{
cv::VideoCapture cap(0);
cv::Mat frame, frame1,frame2;
int key=0;
while(key!=27){
cap >> frame;
if(key=='c'){
frame1 = frame;
key = 0;
}
if(key =='x'){
cv::absdiff(frame, frame1, frame2); // I also tried frame2= (frame -frame1)*255;
cv::imshow("difference ",frame2);
key =0;
}
cv::imshow("stream",frame);
key = cv::waitKey(10);
}
}
the result is always the same a 0 Matrix, any idea what I'm doing wrong here?
thanks in advance for your help.
Mat objects are pointer typed. After setting frame1 to frame directly using frame1 = frame, both matrices show the same point and same frame also. You have to copy frame value using "copyTo" method of Mat.
OpenCV Matrixes use pointers internally
The documentation of the Mat type states:
Mat is basically a class with two data parts: the matrix header and a pointer to the matrix containing the pixel values.
[...]
Whenever somebody copies a header of a Mat object, a counter is increased for the matrix. Whenever a header is cleaned this counter is decreased. When the counter reaches zero the matrix too is freed. Sometimes you will want to copy the matrix itself too, so OpenCV provides the clone() and copyTo() functions.
cv::Mat F = A.clone();
cv::Mat G;
A.copyTo(G);
OpenCV overloads the affectation operator on cv::Mat objects so that the line mat1 = mat2 only affects the pointer to the data in mat1 (that points to the same data as mat2). This avoids time consuming copies of all the image data.
If you want to save the data of a matrix, you have to write mat1 = mat2.clone() or mat2.copyTo(mat1).
I was looking for a similar program and I came across your post, here is a sample I have written for frameDifferencing, hope this helps, the below function will give you the difference between two frames
/** #function differenceFrame */
Mat differenceFrame( Mat prev_frame, Mat curr_frame )
{
Mat image = prev_frame.clone();
printf("frame rows %d Cols %d\n" , image.rows, image.cols);
for (int rows = 0; rows < image.rows; rows++)
{
for (int cols = 0; cols < image.cols; cols++)
{
/* printf("BGR value %lf %lf %lf\n" , abs(prev_frame.at<cv::Vec3b>(rows,cols)[0] -
curr_frame.at<cv::Vec3b>(rows,cols)[0]),
abs(prev_frame.at<cv::Vec3b>(rows,cols)[1] -
curr_frame.at<cv::Vec3b>(rows,cols)[0]),
abs(prev_frame.at<cv::Vec3b>(rows,cols)[2] -
curr_frame.at<cv::Vec3b>(rows,cols)[0]));
*/
image.at<cv::Vec3b>(rows,cols)[0] = abs(prev_frame.at<cv::Vec3b>(rows,cols)[0] -
curr_frame.at<cv::Vec3b>(rows,cols)[0]);
image.at<cv::Vec3b>(rows,cols)[1] = abs(prev_frame.at<cv::Vec3b>(rows,cols)[1] -
curr_frame.at<cv::Vec3b>(rows,cols)[1]);
image.at<cv::Vec3b>(rows,cols)[2] = abs(prev_frame.at<cv::Vec3b>(rows,cols)[2] -
curr_frame.at<cv::Vec3b>(rows,cols)[2]);
}
}
return image;
}

Array to OpenCV matrix

I have a array double dc[][] and want to convert this as to a IplImage* image and further to a video frame.
What I had to do was I was given a video and I extracted out some features and then make a new video of the extracted features.
My approach was I divided the video into frames extracted the features from each frame then did the updation like this and in each iteration of frame I get a new dc
double dc[48][44];
for(int i=0;i<48;i++)
{
for(int j=0;j<44;j++)
{
dc[i][j]=max1[i][j]/(1+max2[i][j]);
}
}
Now I need to save this dc in such a way that I can reconstruct the video.Anybody help me with this.
Thanks in advance
If you're okay with using Mat, then you can make a Mat for existing user-allocated memory. One of the Mat constructors has the signature:
Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)
where the parameters are:
rows: the memory height,
cols: the width,
type: one of the OpenCV data types (e.g. CV_8UC3),
data: pointer to your data,
step: (optional) stride of your data
I'd encourage you to take a look at the documentation for Mat here
EDIT: Just to make things more concrete, here's an example of making a Mat from some user-allocated data
int main()
{
//allocate and initialize your user-allocated memory
const int nrows = 10;
const int ncols = 10;
double data[nrows][ncols];
int vals = 0;
for (int i = 0; i < nrows; i++)
{
for (int j = 0; j < ncols; j++)
{
data[i][j] = vals++;
}
}
//make the Mat from the data (with default stride)
cv::Mat cv_data(nrows, ncols, CV_64FC1, data);
//print the Mat to see for yourself
std::cout << cv_data << std::endl;
}
You can save a Mat to a video file via the OpenCV VideoWriter class. You just need to create a VideoWriter, open a video file, and write your frames (as Mat). You can see an example of using VideoWriter here
Here's a short example of using the VideoWriter class:
//fill-in a name for your video
const std::string filename = "...";
const double FPS = 30;
VideoWriter outputVideo;
//opens the output video file using an MPEG-1 codec, 30 frames per second, of size height x width and in color
outputVideo.open(filename, CV_FOURCC('P','I','M,'1'), FPS, Size(height, width));
Mat frame;
//do things with the frame
// ...
//writes the frame out to the video file
outputVideo.write(frame);
The tricky part of the VideoWriter is the opening of the file, as you have a lot of options. You can see the names for different codecs here

Using Mat::at(i,j) in opencv for a 2-D Mat object

I am using Ubuntu 12.04 and OpenCV 2
I have written the following code :
IplImage* img =0;
img = cvLoadImage("nature.jpg");
if(img != 0)
{
Mat Img_mat(img);
std::vector<Mat> RGB;
split(Img_mat, RGB);
int data = (RGB[0]).at<int>(i,j)); /*Where i, j are inside the bounds of the matrix size .. i have checked this*/
}
The problem is I am getting negative values and very large values in the data variable. I think I have made some mistake somewhere. Can you please point it out.
I have been reading the documentation (I have not finished it fully.. it is quite large. ) But from what I have read, this should work. But it isnt. What is going wrong here?
Img_mat is a 3 channeled image. Each channel consists of pixel values uchar in data type.
So with split(Img_mat, BGR) the Img_mat is split into 3 planes of blue, green and red which are collectively stored in a vector BGR. So BGR[0] is the first (blue) plane with uchar data type pixels...hence it will be
int dataB = (int)BGR[0].at<uchar>(i,j);
int dataG = (int)BGR[1].at<uchar>(i,j);
so on...
You have to specify the correct type for cv::Mat::at(i,j). You are accessing the pixel as int, while it should be a vector of uchar. Your code should look something like this:
IplImage* img = 0;
img = cvLoadImage("nature.jpg");
if(img != 0)
{
Mat Img_mat(img);
std::vector<Mat> BGR;
split(Img_mat, BGR);
Vec3b data = BGR[0].at<Vec3b>(i,j);
// data[0] -> blue
// data[1] -> green
// data[2] -> red
}
Why are you loading an IplImage first? You are mixing the C and C++ interfaces.
Loading a cv::Mat with imread directly would be more straight-forward.
This way you can also specify the type and use the according type in your at call.