Error in using cvVideoWriter in opencv? - c++

This code snippet is supposed to save part of a video whose range is defined by start and end. There is an array of structures (data[i]) that holds the starting and end frame of a video shot in the original video. There are total of 8 shots.
for (int i = 0; i < finalCount-1; ++i) {
capture = cvCaptureFromAVI("Stats\\Shots\\Cricketc1.avi");
assert(capture);
int frame_number = 0;
int start = data[i].start_frame;
int end = data[i].end_frame;
char shotname[100];
strcpy_s(shotname, "shot_");
char shot_id[30];
_itoa_s(data[i].shot_no, shot_id, 10);
strcat_s(shotname, shot_id);
strcat_s(shotname, ".avi");
IplImage* image = NULL;
CvVideoWriter* writer = NULL;
writer = cvCreateVideoWriter (shotname, CV_FOURCC('i','Y','U','V'), fps, cvSize(width, height), 1);
assert(writer);
while (frame_number >= start && frame_number < end) {
image = cvQueryFrame(capture);
assert(image);
cvWriteFrame(writer, image);
}
cvReleaseImage(&image);
cvReleaseVideoWriter(&writer);
cvReleaseCapture(&capture);
cout << shotname << " saved ..." << endl;
}
After running the program 8 video files are created that have a size of 6kb and do not run. I have tried various codecs like divx, mjpg, mpg2, iyuv etc but all give the same result.

In your while loop, frame_number is never incremented. Since you say the program actually executes and creates the files this means nothing in your while loop ever runs... otherwise you'd get stuck in an infinite loop because frame_number will always be 0.
I would advise you initialize frame_number to start instead of 0 and there's no reason for it to exist outside of the scope of the loop so a for seems more appropriate:
int start = data[i].start_frame;
int end = data[i].end_frame;
...
for (int frame_number = start; frame_number < end; frame_number++) {
image = cvQueryFrame(capture);
assert(image);
cvWriteFrame(writer, image);
}

If Gunther Fox answer won't help try to use different codec - it's very strange, but in my situation iyuv is not working at all and some other codecs works ok, but i can't read them while debugging... For me - ms video and radius cinepak always works fine(writing and reading), iyuv is not working at all, other codes - writing and reading, but not while debugging.

Related

OpenCV vs byte array

I am working on a simple C++ image processing application and deciding whether to use OpenCV for loading the image and accessing individual pixels.
My current approach is to simply load the image using fopen, reading the 54 byte header and load the rest of the bytes in a char* array.
To access a specific pixel I use
long q = (long*)(bmpData + x*3 + (bmpSize.height - y - 1) * bmpSize.stride);
To perform a simple color check, for ex. "is blue?"
if (((long*)q | 0xFF000000) == 0xFFFF0000) //for some reason RGB is reversed to BGR
//do something here
Is OpenCV any faster considering all the function calls, parsing, etc.?
Bitmap file header is actually 54 bytes and you can't skip it. You have to read it to find the width, height, bitcount... calculate padding if necessary... and other information.
Depending on how the file is opened, OpenCV will read the header and reads the pixels directly in to a buffer. The only change is that the rows are flipped so the image is right side up.
cv::Mat mat = cv::imread("filename.bmp", CV_LOAD_IMAGE_COLOR);
uint8_t* data = (uint8_t*)mat.data;
The header checks and the small changes made by OpenCV will not significantly affect performance. The bottle neck is mainly in reading the file from the disk. The change in performance will be difficult to measure, unless you are doing a very specific task, for example you want only 3 bytes in a very large file, and you don't want to read the entire file.
OpenCV is overkill for this task, so you may choose other libraries for example CImg as suggested in comments. If you use smaller libraries they load faster, it might be noticeable when your program starts.
The following code is a test run on Windows.
For a large 16MB bitmap file, the result is almost identical for opencv versus plain c++.
For a small 200kb bitmap file, the result is 0.00013 seconds to read in plain C++, and 0.00040 seconds for opencv. Note the plain c++ is not doing much beside reading the bytes.
class stopwatch
{
std::chrono::time_point<std::chrono::system_clock> time_start, time_end;
public:
stopwatch() { reset();}
void reset(){ time_start = std::chrono::system_clock::now(); }
void print(const char* title)
{
time_end = std::chrono::system_clock::now();
std::chrono::duration<double> diff = time_end - time_start;
if(title) std::cout << title;
std::cout << diff.count() << "\n";
}
};
int main()
{
const char* filename = "filename.bmp";
//I use `fake` to prevent the compiler from over-optimization
//and skipping the whole loop. But it may not be necessary here
int fake = 0;
//open the file 100 times
int count = 100;
stopwatch sw;
for(int i = 0; i < count; i++)
{
//plain c++
std::ifstream fin(filename, std::ios::binary);
fin.seekg(0, std::ios::end);
int filesize = (int)fin.tellg();
fin.seekg(0, std::ios::beg);
std::vector<uint8_t> pixels(filesize - 54);
BITMAPFILEHEADER hd;
BITMAPINFOHEADER bi;
fin.read((char*)&hd, sizeof(hd));
fin.read((char*)&bi, sizeof(bi));
fin.read((char*)pixels.data(), pixels.size());
fake += pixels[i];
}
sw.print("time fstream: ");
sw.reset();
for(int i = 0; i < count; i++)
{
//opencv:
cv::Mat mat = cv::imread(filename, CV_LOAD_IMAGE_COLOR);
uint8_t* pixels = (uint8_t*)mat.data;
fake += pixels[i];
}
sw.print("time opencv: ");
printf("show some fake calculation: %d\n", fake);
return 0;
}

Manipulating pixels of a cv::MAT just doesn't take effect

The following code is just supposed to load an image, fill it with a constant value and save it again.
Of course that doesn't have a purpose yet, but still it just doesn't work.
I can read the pixel values in the loop, but all changes are without effect and saves the file as it was loaded.
Think I followed the "efficient way" here accurately: http://docs.opencv.org/2.4/doc/tutorials/core/how_to_scan_images/how_to_scan_images.html
int main()
{
Mat im = imread("C:\\folder\\input.jpg");
int channels = im.channels();
int pixels = im.cols * channels;
if (!im.isContinuous())
{ return 0; } // Just to show that I've thought of that. It never exits here.
uchar* f = im.ptr<uchar>(0);
for (int i = 0; i < pixels; i++)
{
f[i] = (uchar)100;
}
imwrite("C:\\folder\\output.jpg", im);
return 0;
}
Normal cv functions like cvtColor() are taking effect as expected.
Are the changes through the array happening on a buffer somehow?
Huge thanks in advance!
The problem is that you are not looking at all pixels in the image. Your code only looks at im.cols*im.channels() which is a relatively small number as compared to the size of the image (im.cols*im.rows*im.channels()). When used in the for loop using the pointer, it only sets a value for couple of rows in an image ( if you look closely you will notice the saved image will have these set ).
Below is the corrected code:
int main()
{
Mat im = imread("C:\\folder\\input.jpg");
int channels = im.channels();
int pixels = im.cols * im.rows * channels;
if (!im.isContinuous())
{ return 0; } // Just to show that I've thought of that. It never exits here.
uchar* f = im.ptr<uchar>(0);
for (int i = 0; i < pixels; i++)
{
f[i] = (uchar)100;
}
imwrite("C:\\folder\\output.jpg", im);
return 0;
}

Array of Mats from video file - opencv

I'm coding using C++ and opencv on linux. I've found this similar question; although, I can't quite get it to work.
What I want to do is read in a video file and store a certain number of frames in an array. Over that number, I want to delete the first frame and add the most recent frame to the end of the array.
Here's my code so far.
VideoCapture cap("Video.mp4");
int width = 2;
int height = 2;
Rect roi = Rect(100, 100, width, height);
vector<Mat> matArray;
int numberFrames = 6;
int currentFrameNumber = 0;
for (;;){
cap >> cameraInput;
cameraInput(roi).copyTo(finalOutputImage);
if(currentFrameNumber < numberFrames){
matArray.push_back(finalOutputImage);
}else if(currentFrameNumber <= numberFrames){
for(int i=0;i<matArray.size()-1; i++){
swap(matArray[i], matArray[i+1]);
}
matArray.pop_back();
matArray.push_back(finalOutputImage);
}
currentFrameNumber++;
}
My understanding of mats says this is probably a problem with pointers; I'm just not sure how to fix it. When I look at the array of mats, every element is the same frame. Thank you.
There's no need for all this complication if you were to make use of C++'s highly useful STL.
if( currentFrameNumber >= numberFrames )
matArray.remove( matArray.begin() );
matArray.push_back( finalOutputImage.clone() ); //check out #berak's comment
should do it.

getting the mean value of image parts

I'm trying to subdivide a gray frame in multiple small squares and than caluculate for each one of them the mean color value of each one so I can build a result frame that display those values, here 's what I' done :
int main (){
cv::Mat frame= cv::imread("test2.jpg",0), result, myROI;
int key = 0;
int roiSize =10;
cv::Scalar mean(0);
cv::Mat meanS;
meanS = cv::Mat::zeros (frame.rows/roiSize,frame.cols/roiSize,CV_32FC1) ;
cv::Rect roi;
if(frame.channels()!=1)
cv::cvtColor(frame,frame,CV_BGR2GRAY);
for ( int i=0 ; i< frame.cols /roiSize; i++){
for (int j = 0 ; j < frame.rows/roiSize; j++){
roi.x= i*roiSize;
roi.y= j*roiSize;
roi.height=roiSize;
roi.width= roiSize;
myROI = frame(roi);
cv::imshow("myRoi",myROI);
mean = cv::mean(myROI);
std::cout << mean[0] << std::endl;
meanS.at<float>(j,i) = mean[0];
}
}
//meanS *=1/255; // I've tried this one also, it didn't help !
cv::imshow("the reuslt ",meanS);
cv::waitKey(0);
return 0;
}
in the console the values are correct but when I display the result with imshow I get only a white frame ! !!
any Idea how can I solve this ?
thanks in advance !
your comment line is actually correct but it's doing integer division and thus multiplying by zero. just add a dot at the end like meanS *=1/255.; // I've tried this one also, it didn't help !

How to fix the insufficient memory error (openCV)

Please help how to handle this problem:
OpenCV Error: Insufficient memory (Failed to allocate 921604 bytes) in
unknown function, file
........\ocv\opencv\modules\core\src\alloc.cpp, line 52
One of my method using cv::clone and pointer
The code is:
There is a timer every 100ms;
In the timer event, I call this method:
void DialogApplication::filterhijau(const Mat &image, Mat &result) {
cv::Mat resultfilter = image.clone();
int nlhijau = image.rows;
int nchijau = image.cols*image.channels();;
for(int j=0; j<nlhijau; j++) {
uchar *data2=resultfilter.ptr<uchar> (j); //alamat setiap line pada result
for(int i=0; i<nchijau; i++) {
*data2++ = 0; //element B
*data2++ = 255; //element G
*data2++ = 0; //element R
}
// free(data2); //I add this line but the program hung up
}
cv::addWeighted(resultfilter,0.3,image,0.5,0,resultfilter);
result=resultfilter;
}
The clone() method of a cv::Mat performs a hard copy of the data. So the problem is that for each filterhijau() a new image is allocated, and after hundreds of calls to this method your application will have occupied hundreds of MBs (if not GBs), thus throwing the Insufficient Memory error.
It seems like you need to redesign your current approach so it occupies less RAM memory.
I faced this error before, I solved it by reducing the size of the image while reading them and sacrificed some resolution.
It was something like this in Python:
# Open the Video
cap = cv2.VideoCapture(videoName + '.mp4')
i = 0
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
frame = cv2.resize(frame, (900, 900))
# append the frames to the list
images.append(frame)
i += 1
cap.release()
N.B. I know it's not the most optimum solution for the problem but, it was enough for me.