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
Related
I am having an issue when I want to combine two computer graphic libraries, namely OpenCV and Vigra. I want to use OpenCVs k-means clustering algorithm for grayscale image binarization. The framework of my image processing was built earlier and depends strongly on Vigra, that's why I have to combine both libraries.
So basically, I am loading the image employing Vigra functionality, than convert the Vigra object to an OpenCV matrix, run the k-means clustering, re-convert the matrix object to a vigra object and finally save the image again by employing Vigra functionality. Here is a code example:
#include <vigra/impex.hxx>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
int main()
{
std::string InputFilePath = "path/to/image/image_name.tif";
// Vigra functionality to load an image from path
vigra::FImage InputImg;
const char* cFile = InputFilePath.c_str();
vigra::ImageImportInfo info(cFile);
int b = info.width();
int h = info.height();
InputImg.resize(b,h);
vigra::importImage(info, destImage(InputImg));
vigra::FImage OutputImg(InputImg.width(), InputImg.height());
// Setting up OCV Matrix as an one channel, 32bit float grayscale image
cv::Mat InputMat(InputImg.width(), InputImg.height(), CV_32FC1);
// my workaround to convert vigra::FImage to cv::Mat
for(unsigned int i=0; i<InputImg.width(); i++){
for(unsigned int j=0; j<InputImg.height(); j++){
InputMat.at<float>(j,i) = InputImg(i,j);
}
}
// OCVs k-means clustering
const unsigned int singleLineSize = InputMat.rows*InputMat.cols;
const unsigned int k=2;
cv::Mat data = InputMat.reshape(1, singleLineSize);
std::vector<int> labels;
data.convertTo(data, CV_32FC1);
cv::Mat1f centers;
cv::kmeans(data, k, labels, cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 10, 1.0), 2, cv::KMEANS_RANDOM_CENTERS, centers);
for (unsigned int i = 0; i < singleLineSize; i++) {
data.at<float>(i) = centers(labels[i]);
}
cv::Mat OutputMat = data.reshape(1, InputMat.rows);
OutputMat.convertTo(OutputMat, CV_8UC1);
// re-convert cv::Mat to vigra::FImage
for(unsigned int i=0; i<InputImg.width(); i++){
for(unsigned int j=0; j<InputImg.height(); j++){
OutputImg(i,j) = OutputMat.at<float>(j,i);
}
}
std::string SaveFileName = "path/to/save_location/save_img_name.tif";
// vigra functionality to save the image
const char* cFile = SaveFileName.c_str();
vigra::ImageExportInfo exinfo(cFile);
vigra::exportImage(srcImageRange(OutputImg), exinfo.setPixelType("FLOAT")); // pixel type could also be "UINT8"
// for the sake of comparability
std::string SaveFileNameOCV = "path/to/save_location/save_mat_name.tif";
cv::imwrite(SaveFileNameOCV, OutputMat);
return 0;
}
k-means clustering works fine, and when I save the cv::Mat directly with
cv::imwrite()
everything is good. But when I re-convert the cv::Mat to a vigra::FImage object and save it, the image is corrupted. It looks as if the object (in the image) is mirrored or duplicated four times, allthough image width and heigth stay the same. I attached the images (InputImg, OutputImg and OutputMat).
Moreover, if I re-convert InputMat to OutputImg (after the k-means), and save this image, everything is fine (this image is also attached).
And finally, I do not understand why I have to switch the indices when converting from vigra::FImage to cv::Mat and vice versa:
InputMat.at<float>(j,i) = InputImg(i,j);
But if I don't, the resulting image is rotated.
Ok, so I am not quite sure if anybody uses Vigra AND OpenCV, I guess OpenCV is definitly more common than Vigra. But anyway, if anybody could help, this would be great.
BTW: I am running everything in Code::Blocks on OpenSuSE 15.1. Any library was installed via the official OpenSuSE repositories.
OK, first of all, I did not found out, why this happens. But what I now know, is that, if I am using the type-defined version Mat_ (e.g. Mat1f...) I can handle everything propperly and the saved results are as expected.
For conversion I wrote 2 methods:
cv::Mat1f convertImg2Mat(vigra::FImage &img){
int b = img.width();
int h = img.height();
cv::Mat1f mat(h, b);
for(unsigned int j=0; j<h; j++){
for(unsigned int i=0; i<b; i++){
mat(j,i) = img(i,j);
}
}
return mat;
}
and
vigra::FImage convertMat2Img(cv::Mat mat){
int b = mat.rows;
int h = mat.cols;
cv::Mat1f tmp = mat.clone();
vigra::FImage img(h, b);
for(unsigned int j=0; j<h; j++){
for(unsigned int i=0; i<b; i++){
img(i,j) = tmp(j,i);
}
}
return img;
}
which both work fine.
A stupid beginners mistake was the indexing, because vigra follows fortran order which is
img(cols, rows)
and OpenCV uses another convention which is
mat(rows, cols).
So from my side this question is not yet propperly answered, but I found a working solution anyways.
I have a dicom 3D image which is [512,512,5] (rows, cols, slices). I want to read it with DCMTK toolkit and convert it to a OpenCV Mat object. The image is 16 bits unsigned int.
My question is: Does anyone know the correct way to convert this dicom image into a Mat object? How to properly read all the slices with the method getOutputData?
Based on the comments of #Alan Birtles, there is the possibility to specify the frame you want to read on the getOutputData method. After reading each frame, you simply merge the Mat objects into a single Mat.
I wrote this code to get the whole volume:
DicomImage *image = new DicomImage(file);
// Get the information
unsigned int nRows = image->getHeight();
unsigned int nCols = image->getWidth();
unsigned int nImgs = image->getFrameCount();
vector <Mat> slices(nImgs);
// Loop for each slice
for(int k = 0; k<nImgs; k++){
(Uint16 *) pixelData = (Uint16 *)(image->getOutputData(16 /* bits */,k /* slice */));
slices[k] = Mat(nRows, nCols, CV_16U, pixelData).clone();
}
Mat img;
// Merge the slices in a single img
merge(slices,img);
cout << img.size() << endl;
cout << img.channels() << endl;
// Output:
// [512 x 512]
// 5
I am currently programming with a PixeLINK USB3 machine vision camera along with OpenCV in C. I have had some success passing camera images in Mat format with the following code:
PXL_RETURN_CODE rc = PxLInitialize(0, &hCamera);
if (!API_SUCCESS(rc))
{
printf("Error: Unable to initialize a camera. \n");
return EXIT_FAILURE;
}
vector<U8> frameBuffer(3000 * 3000 * 2);
FRAME_DESC frameDesc;
if (API_SUCCESS(PxLSetStreamState(hCamera, START_STREAM)))
{
while (true)
{
frameDesc.uSize = sizeof(frameDesc);
rc = GetNextFrame(hCamera, (U32)frameBuffer.size(), &frameBuffer[0],
&frameDesc, 5);
Mat image(2592, 2048, CV_8UC1);
Mat imageCopy;
// Where passing of image data occurs
int k = 0;
for (int row = 0; row < 2048; row++)
{
for (int col = 0; col < 2592; col++)
{
image.at<uchar>(row, col) = frameBuffer[k];
k++;
}
}...
As I mentioned this works, but it seems very sloppy. I have looked online but haven't found too much detail.
I have tried:
Mat image(2592, 2048, CV_8UC1, &frameBuffer, size_t step=AUTO_STEP);
as well as,
Mat image(2592, 2048, CV_8UC1, frameBuffer, size_t step=AUTO_STEP).
The former is the only one that compile successfully, and displays gibberish - I mean, it doesn't form an image.
Have you tried switching the row and col of your Mat?
You initialized your Mat with row = 2592, col = 2048,
but you're using switched row and col in your for() loop.
I think this code should work properly:
Mat image(2048, 2592, CV_8UC1, &frameBuffer[0]);
Or, if you're using C++11,
Mat image(2048, 2592, CV_8UC1, frameBuffer.data());
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
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;
}