OpenCV (C++): write produces big file compared to input - c++

I am trying to read a video file using OpenCV (C++), apply a filter to each frame and write a new modified frame into an output file.
The crucial parts of the code are these:
int out_format = CV_FOURCC('M','P','4','2'); // can be another one
double fps = media.get(CV_CAP_PROP_FPS),
width = media.get(CV_CAP_PROP_FRAME_WIDTH),
height = media.get(CV_CAP_PROP_FRAME_HEIGHT);
// On Linux FFMPEG is used to write videos
VideoWriter writer("./" + outputname + ".mkv", out_format, fps, Size(width, height) );
.......
.......
writer.write(newFrame); // Mat newFrame
The fact is, I am not proficient with codecs and video output formats, thus I would like to know why a file which is around 280 MB produces an output of 2 GB.
Is that a codec problem? I have tried DIVX, MPEG and others. Moreover, some output formats must go together with specific codecs.

Try using the 'M','P', '4', 'V' codec with the .mp4 container, that is, set the extension to your filename as something.mp4
For the mp4 container, you could also try, MPEG, MJPG, FMP4

Related

How to read alpha channel from .webm video using ffmpeg in c++

Background
I have a .webm file (pix_fmt: yuva420p) converted from .mov video file in order to reduce file size and I would like to read the video data using c++, so I followed using this repo as a reference.
This works perfectly on .mov video.
Problem
By using same repo, however, there is no alpha channel data (pure zeros on that channel) for .webm video but I can get the alpha data from .mov video.
Apparently many people already noticed that after the video conversion, ffmpeg somehow detect video as yuv420p + alpha_mode : 1 and thus alpha channel is not used but there is no one discuss about workaround of this.
I tried forcing pixel format during this part to use yuva420p but that just broke the whole program.
// Set up sws scaler
if (!sws_scaler_ctx) {
auto source_pix_fmt = correct_for_deprecated_pixel_format(av_codec_ctx->pix_fmt);
sws_scaler_ctx = sws_getContext(width, height, source_pix_fmt,
width, height, AV_PIX_FMT_RGB0,
SWS_BILINEAR, NULL, NULL, NULL);
}
I also verified my video that it contains alpha channel using other source so I am sure there is alpha channel in my webm video but I cannot fetch the data using ffmpeg.
Is there a way to fetch the alpha data? Other video format or using other libraries work as well as long as it does have some file compression but I need to access the data in c++.
Note: This is the code I used for converting video to webm
ffmpeg -i input.mov -c:v libvpx-vp9 -pix_fmt yuva420p output.webm
You have to force the decoder.
Set the following before avformat_open_input()
AVCodec *vcodec;
vcodec = avcodec_find_decoder_by_name("libvpx-vp9");
av_fmt_ctx->video_codec = vcodec;
av_fmt_ctx->video_codec_id = vcodec->id;
You don't need to set pixel format or any scaler args.
This assumes that your libavcodec is linked with libvpx.

How to store images in c++ with Ximea

I have a superSpeed usb 3.0 Ximea camera and I'm trying to code an application with a Ximea camera that consists on computer vision and machine learning.
I've been able to alocate the frames captured by the camera in it's buffer but I can find the way to save those images or frames as an JPEG or BMP file. I don't know if it's just a command line in my script or I need some kind of libraries to do it.
The images are aquired using these commands:
#define EXPECTED_IMAGES 10
for (int images=0;images < EXPECTED_IMAGES;images++)
{
// getting image from camera
stat = xiGetImage(xiH, 5000, &image);
HandleResult(stat,"xiGetImage");
printf("Image %d (%dx%d) received from camera\n", images, (int)image.width, (int)image.height);
}
As I can extract the data from the images, I suppose that the frame is still in the buffer, but I can't figure out the way to save it as a JPEG or BMP file in the computer.
I would appreciate any help with the code.
Thank you!
Aha, saving the image. I think you might have gotten the answer by now.
But here is mine, and I hope this will be useful for anyone working with machine vision cameras.
I have been working with XIMEA for quite a while now. XIMEA API does not include any functions to save images from the buffer to hard drive. So, you need to write your own function or use some library to save out images. And I think, essentially it all comes down to whether it's RAW or compressed image and what kind of image format you want to save out. ie. BMP, JPEG, PNG, PGM, EXR ......
Let's make couple assumptions first.
Here I assume you want to save out 8bit per pixel RAW image having a resolution of 1024*1024. The size of the image will be 8bit * 1024 * 1024 = 8388608bit = 1048576btye ~= 1MB
By looking at your code, you are using XIMEA API in C++.
Okay...... Here are two ways I used most often to save out images from XIMEA.
Writing all the image pixels to a binary file with a proper header according to the format you want to save out. Here is an example saving a data to a PGM format image.
FILE *file;
char fileName = "example.pgm";
char *image;
int width = 1024;
int height = 1024;
int byte_per_pixel = 1;
int max_pixel_value = 255;
file = fopen (fileName , "w+bx");
if(file == NULL){
fprintf(stderr, "Cannot open %s\n", fileName);
return -1;
}
size_t n = 0;
n += fprintf(file, "P5\n# Comment goes here!\n%d %d\n%d\n", width, height, max_pixel_value);
n += fwrite(image, 1, width * height * byte_per_pixel, file);
fclose (fileToSave);
Saving image to PGM may seem easy but when you need to save an image having pixel depth higher than 8bit, you need to deal with endianness issue, since PGM big-endian format. Here is a link to Netpbm formats if you want to read more about it. https://en.wikipedia.org/wiki/Netpbm_format
And also, other formats may have way more complicated data structure then you cannot just simply put down a header. So, using an image library or OpenCV will be a lot less cumbersome.
The handy OpenCV imwrite. Since you are gonna deal with pixels, OpenCV is a good library to have. OpenCV is a powerful library helps you with manipulating matrixes easier than ever. And it comes with a lot of useful stuff like GPU accelerated OpenCV functions. Back to the topic, imwrite can save images to many formats. Here is an example I wrote to save RAW data to PNG format.
string fileName = "example.png";
char *image;
int width = 1024;
int height = 1024;
int byte_per_pixel = 1;
int max_pixel_value = 255;
cv::Mat img_raw = cv::Mat(height, width, CV_8UC1, image);
vector compression_params;
compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);
compression_params.push_back(0);
cv::imwrite(PNGFileName, img_raw, compression_params);
imwirte will determine what kind of format you want to save out based on the filename extension. And just a couple lines of code. OpenCV saves out the image for you effortlessly. Here is a link to OpenCV documentation of imwirte, http://docs.opencv.org/2.4/modules/highgui/doc/reading_and_writing_images_and_video.html?highlight=imwrite
I hope my answer can help you and others are wondering how to save out images.
to store images from XIMEA cameras I would recommend to use the OpenCV library
as it provides tools for storing both JPEG and BMP image formats. Please download
a short example that demonstrates the storing of several different data formats
from the camera to JPEG and BMP images. Download the archive with MSVC 2013 project and OpenCV3.0 binaries from here and use password SHWJGRAIHFLG for
extraction.
If you should have any other questions concerning XIMEA products, please visit
and register directly on the XIMEA customer support. Thank you.
Best regards,
XIMEA team

Writing a video file using H.264 compression in OpenCV

How do I write a video using H.264 compression with the VideoWriter class in OpenCV? I basically want to get a video from the webcam and save it after a character is pressed. The ouput video file is huge when using MPEG4 Part 2 compression.
You can certainly use the VideoWriter class, but you need to use the correct FourCC code that represents the the H264 standard. FourCC stands for Four Character Code, which is an identifier for a video codec, compression format, colour or pixel format used in media files.
Specifically, when you create a VideoWriter object, you specify the FourCC code when constructing it. Consult the OpenCV docs for more details: http://docs.opencv.org/trunk/modules/highgui/doc/reading_and_writing_images_and_video.html#videowriter-videowriter
I'm assuming you're using C++, and so the definition of the VideoWriter constructor is:
VideoWriter::VideoWriter(const String& filename, int fourcc,
double fps, Size frameSize, bool isColor=true)
filename is the output of the video file, fourcc is the FourCC code for the code you wish to use, fps is the desired frame rate, frameSize is the desired dimensions of the video, and isColor specifies whether or not you want the video to be in colour. Even though FourCC uses four characters, OpenCV has a utility that parses FourCC and outputs a single integer ID which is used as a lookup to be able to write the correct video format to file. You use the CV_FOURCC function, and specify four single characters - each corresponding to a single character in the FourCC code of the codec you want. Note that CV_FOURCC is for OpenCV 2.x. It is recommended you use cv::Videowriter::fourcc for OpenCV 3.x and beyond.
Specifically, you would call it like this:
int fourcc = CV_FOURCC('X', 'X', 'X', 'X');
int fourcc = VideoWriter::fourcc('X', 'X', 'X', 'X');
Replace X with each character that belongs to the FourCC (in order). Because you want the H264 standard, you would create a VideoWriter object like so:
#include <iostream> // for standard I/O
#include <string> // for strings
#include <opencv2/core/core.hpp> // Basic OpenCV structures (cv::Mat)
#include <opencv2/highgui/highgui.hpp> // Video write
using namespace std;
using namespace cv;
int main()
{
VideoWriter outputVideo; // For writing the video
int width = ...; // Declare width here
int height = ...; // Declare height here
Size S = Size(width, height); // Declare Size structure
// Open up the video for writing
const string filename = ...; // Declare name of file here
// Declare FourCC code - OpenCV 2.x
// int fourcc = CV_FOURCC('H','2','6','4');
// Declare FourCC code - OpenCV 3.x and beyond
int fourcc = VideoWriter::fourcc('H','2','6','4');
// Declare FPS here
double fps = ...;
outputVideo.open(filename, fourcc, fps, S);
// Put your processing code here
// ...
// Logic to write frames here... see below for more details
// ...
return 0;
}
Alternatively, you could simply do this when declaring your VideoWriter object:
VideoWriter outputVideo(filename, fourcc, fps, S);
If you use the above, it's not required that you call open as this will automatically open up the writer for writing frames to file.
If you're not sure if H.264 is supported on your computer, specify -1 as the FourCC code, and a window should pop up when you run the code that displays all of the available video codecs that are on your computer. I'd like to mention that this only works for Windows. Linux or Mac OS doesn't have this window popping out when you specify -1. In other words:
VideoWriter outputVideo(filename, -1, fps, S);
You can choose which one is most suitable should H.264 not exist on your computer. Once that is done, OpenCV will create the right FourCC code to be input into the VideoWriter constructor so that you will get a VideoWriter instance that represents a VideoWriter that will write that type of video to file.
Once you have a frame ready, stored in frm for writing to the file, you can do either:
outputVideo << frm;
OR
outputVideo.write(frm);
As a bonus, here's a tutorial on how to read/write videos in OpenCV: http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_gui/py_video_display/py_video_display.html - However, it's written for Python, but what is good to know is near the bottom of the link, there is a list of FourCC codes that are known to work for each operating system. BTW, the FourCC code they specify for the H264 standard is actually 'X','2','6','4', so if 'H','2','6','4' doesn't work, replace H with X.
Another small note. If you are using Mac OS, then what you need to use is 'A','V','C','1' or 'M','P','4','V'. From experience, 'H','2','6','4'or 'X','2','6','4'when trying to specify the FourCC code doesn't seem to work.
As rayryeng states, you need to pass the correct FourCC code to VideoWriter.
For H.264 most people use AVC, which would look like this:
cv2.VideoWriter_fourcc('a','v','c','1')
mp4v also seems to work for many. If they both don't, check your list of available codecs.

Size in kbytes of AVI of video in C++/OpenCV

Is there a way to know the size of an avi video in OpenCV? There are some videos that are blank and instead of processing them I want to get rid of them by comparing their size to a threshold. For example, if the size of the video is 200 kB or less, then I skip processing that video.
Update:
CvCapture *capture = cvCreateFileCapture(path);
IplImage *img = cvQueryFrame( capture );
All videos have the same number of frames but some videos have all blank frames.
You can get number of frames by getting CV_CAP_PROP_FRAME_COUNT video property using GetCaptureProperty:
CV_CAP_PROP_FRAME_COUNT Number of frames in the video file.
To get size of file (video) there is another API - depends on your system (WinAPI, POSIX etc).
For example on Windows it's GetFileSizeEx() function.
Also look at this SO discussion.
If you want to get the actual file size, you will have to use fopen() or stat as OpenCV doesn't store this information in a cvCapture object. However, you can detect blank videos like so:
CvCapture *capture = cvCaptureFromAVI( argv[1] );
int numFrames = (int) cvGetCaptureProperty(&capture, CV_CAP_PROP_FRAME_COUNT);
Then just decide if it's blank based on the number of frames.
Relevant Documentation:
http://opencv.willowgarage.com/documentation/reading_and_writing_images_and_video.html?highlight=cvcapture#getcaptureproperty
If the file size is different for the blank videos, but the files have the same number of frames, it's due to video compression via keyframing. That means the best way to detect blank frames is via getting the file size, which is outside of the scope of OpenCV and can easily be accomplished using plain C/C++:
#include <sys/stat.h>
...
struct stat st;
stat(avi_filename, &st);
size = st.st_size;

OpenCV - VideoWriter produces a video with a "repeated" image

I'm trying to process each frame in a pair of video files in OpenCV and then write the resulting frames to an output avi file. Everything works, except that the output video file looks strange: instead of one solid image, the image is repeated three times and horizontally compressed so all three copies fit into the window. I suspect there is something going wrong with the number of channels the writer is expecting, but I'm giving it 8-bit single channel images to write. Below are the setting with which I'm initializing my videowriter:
//Initialize the video writer
CvVideoWriter *writer = cvCreateVideoWriter("out.avi",CV_FOURCC('D','I','V','X'), 30, frame_sizeL, 0);
Has anyone encountered this strange output from the openCV videowriter before? I've been checking the resulting frames with cvSaveImage just to see if somehow my processing step is creating the "tripled" image, but it's not. It's only when I write to the output avi with cvWriteFrame that the image gets "tripled" and compressed.
Edit: So I've discovered that this only happens when I attempt to write single channel images using write frame. If I write 3 channel 8-bit RGB images, the output video turns out fine. Why is it doing this? I am correctly passing "0" for the color argument when initializing the CvVideoWriter, so it should be expecting single channel images.
In the C++ version you have to tell cv::VideoWriter that you are sending a single channel image by setting the last paramter "false", are you sure you are doing this?
edit: alternatively you can convert a greyscale image to color using cvtColor() and CV_GRAY2RGB