I'm trying read a .bmp grayscale image from a file with a given width and height, convert it to std::vector<uint8_t>, run some sort of filter function on that vector, and then create a new image from that std::vector. I'm stuck in the last part.
How do I create a .bmp file from a given std::vector<uint8_t>, height and width?
P.S. I'm trying to do this without using external libraries.
This is the code I have thus far:
class Image {
int weight;
int width;
std::vector<uint8_t> image;
Image(int weight,int width) : weight(weight),width(width);
void read_image(char* pic);
void save_image(const std::vector<uint8_t>& new_image);
std::vector<uint8_t> filter_image(int ww,int wh,double filter);
};
void Image::read_image(char *pic) {
std::ifstream file(pic,std::ios_base::in | std::ios_base::binary);
if(!file.is_open()) return;
while(file.peek()!=EOF){
this->image.push_back(static_cast<uint8_t>(file.get()));
}
}
void Image::save_image(const std::vector<uint8_t> &new_image) {
//what to do here?
}
A .bmp file does not only store raw pixel data. It begins with a header describing the image stored inside the file: width, height, pixel size, color type, etc... The read_image() function you wrote reads the whole file, including the header, and running any image processing algorithm on your vector will ruin your data and produce garbage.
If you are learning image processing, it would be far easier to use raw image files. A raw image file contains only pixel data, without any metadata. When working with a raw image file, it is your responsibility to know the width and height of the image, as well as the pixel encoding.
Converting an image file to a raw image file, and vice versa, involves the use of an external tool. ffmpeg is such a tool. ffmpeg is a linux tool, but it should be easy to find ffmpeg packaged for any operating system.
For converting from a file in almost any format to a raw image file (ffmpeg deduces the size of the image from the input file). The order of each parameter is important:
ffmpeg -i your_file.jpeg -f rawvideo -c rawvideo -pix_fmt gray output.raw
When converting back to your input format, you have to explicitly tell ffmpeg the size of your picture. Again the order of each parameter is important:
ffmpeg -f rawvideo -c rawvideo -pix_fmt gray -s 1280x720 -i input.raw your_processed_file.jpeg
Adapt the width and height to the real size of your image, or ffmpeg will resize the image. you can also play with the pixel type: gray specifies an 8 bits per pixel grayscale format, but you can use rgb24 to keep color information (use ffmpeg -pix_fmts to see a list of all available formats).
If you are lucky enough to have ffplay availabel in your ffmpeg package, you can view the raw file directly on screen:
ffplay -f rawvideo -c rawvideo -pix_fmt gray -s 1280x720 input.raw
Additionally some image processing software are able to open a raw image file: gimp, photoshop, ...
Related
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.
Context
I used a C++ program to write raw bytes to a file (image.raw) in RGB32 format:
R G B A R G B A R G B A ...
and I want to be able to view it in some way. I have the dimensions of the image.
My tools are limited to command line commands (e.g. ffmpeg). I have visited the ffmpeg website for instructions, but it deals more with converting videos to images.
Questions
Is it possible to turn this file into a viewable file type (e.g. .jpeg, .png) using ffmpeg. If so, how would I do it?
If it's not possible, is there a way I can use another command?
It that's still not viable, is there any way I can manipulate the RGB32 bytes inside a C++ program to make it more suitable without the use of external libraries? I also don't want to encode .jpeg myself like this.
Use the rawvideo demuxer:
ffmpeg -f rawvideo -pixel_format rgba -video_size 320x240 -i input.raw output.png
Since there is no header specifying the assumed video parameters you must specify them, as shown above, in order to be able to decode the data correctly.
See ffmpeg -pix_fmts for a list of supported input pixel formats which may help you choose the appropriate -pixel_format.
get a single frame from raw RGBA data
ffmpeg -y -f rawvideo -pix_fmt rgba -ss 00:01 -r 1 -s 320x240 -i input.raw -frames:v 1 output.png
-y overwrite output
-r input framerate (placed before -i)
-ss skip to this time
-frames:v number of frames ot output
I want to read and show a video using opencv. I've recorded with Direct-show, the Video has UYVY (4:2:2) codec, since opencv can't read that format, I want to convert the codec to an RGB color model, I readed about ffmpeg and I want to know if it's possible to get this done with it ? if not if you a suggestion I'll be thankful.
As I explained to you before, OpenCV can read some formats of YUV, including UYVY (thanks to FFmpeg/GStreamer). So I believe the cv::Mat you get from the camera is already converted to the BGR color space which is what OpenCV uses by default.
I modified my previous program to store the first frame of the video as PNG:
cv::Mat frame;
if (!cap.read(frame))
{
return -1;
}
cv::imwrite("mat.png", frame);
for(;;)
{
// ...
And the image is perfect. Executing the command file on mat.png reveals:
mat.png: PNG image data, 1920 x 1080, 8-bit/color RGB, non-interlaced
A more accurate test would be to dump the entire frame.data() to the disk and open it with an image editor. If you do that keep in mind that the R and B channels will be switched.
I'm trying to get a YUV420 palanar or semiplanar (NV12) image out of jpeg using libjpeg.
I see that there is a option to specify output format to JCS_YCbCr which would generally be a YUV format, but as far as i understand it would give me the data as arrays of 3 elements { Y, U, V }. So to get the image to the right format i would have to rearange and subsample the pixels myself and i want to avoid that for performance reasons.
So I was wondering is there a way to configure libjpeg to output a YUV420p / NV12 buffer directly.
Just take a look at gst_jpeg_decode() in gstreamer source tree. This function along with gst_jpeg_decode_direct() function does exactly what you want to do.
Note that it gives YUV420 planar output, bypassing all color conversion done by libjpeg. (Note: this assumes that the input JPEG is encoded in YUV420 color space (aka I420), which is true for almost all JPEGs out there.
I have an array of rgb data generated from glReadPixels().
Note that RGB data is pixel packed (r1,g1,b1,r2,g2,b2,...).
How can I quickly write a YUV video frame using OpenCV or another C++ library, so that I can stream them to FFMPEG? Converting RGB pixel to YUV pixel is not a problem, as there are many conversion formula available online. However writing the YUV frame is the main problem for me. I have been trying to write the YUV video frame since the last few days and were not successful in doing that.
This is one of my other question about writing YUV frame and the issues that I encountered: Issue with writing YUV image frame in C/C++
I don't know what is wrong with my current approach in writing the YUV frame to a file.
So right now I may want to use existing library (if any), that accepts an RGB data, and convert them to YUV and write the YUV frame directly to a file or to a pipe. Of course it would be much better if I can fix my existing program to write the YUV frame, but you know, there is also a deadline in every software development project, so time is also a priority for me and my project team members.
FFmpeg will happily receive RGB data in. You can see what pixel formats FFmpeg supports by running:
ffmpeg -pix_fmts
Any entry with an I in the first column can be used as an input.
Since you haven't specified the pixel bit depth, I am going to assume it's 8-bit and use the rgb8 pixel format. So to get FFmpeg to read rgb8 data from stdin you would use the following command (I am cating data in but you would be supplying via your pipe):
cat data.rgb | ffmpeg -f rawvideo -pix_fmt rgb8 -s WIDTHxHEIGHT -i pipe:0 output.mov
Since it is a raw pixel format with no framing, you need to subsitite WIDTH and HEIGHT for the appropriate values of your image dimensions so that FFmpeg knows how to frame the data.
I have specifed the output as a MOV file but you would need to configure your FFmpeg/Red5 output accordingly.
OpenCV does not support the YUV format directly, as you know, so it's really up to you to find a way to do RGB <-> YUV conversions.
This is a very interesting post as it shows how to load and create YUV frames on the disk, while storing the data as IplImage.
ffmpeg will write an AVI file with YUV but as karl says there isn't direct support for it in openCV.
Alternatively (and possibly simpler) you can just write the raw UYVY values to a file and then use ffmpeg to convert it to an AVI/MP4 in any format you want. It's also possible to write directly to a pipe and call ffmpeg directly from your app avoiding the temporary yuv file
eg. to convert an HD yuv422 stream to a h264 MP4 file at 30fps
ffmpeg -pix_fmt yuyv422 -s 1920x1080 -i input.yuv -vcodec libx264 -x264opts -r 30 output.mp4