How to reduce image data size without changing its resolution? - c++

I have tried couple of ways to reduce image size, but all failed. All similar to this:
//Mat frame...
vector<uchar> buff;
vector<int> params;
params.push_back(cv::IMWRITE_JPEG_QUALITY);
params.push_back(50); //Tried any value here, didn't change much
cv::imencode(".jpg", frame, buff, params);
Mat a = cv::imdecode(buff,1);
// a size is bigger than frame's
How can one reduce an image (Mat object) size without changing its resolution?
Edit:
I'm taking frames from camera and making video of them. Saving the frames as is makes a 1 minute video to weight about 100mb, which is totally unacceptable. How can I reduce the size (number of bytes) of each frame?

You need video compression like mpeg instead of image compression.
Another way might be to first blur the image and them encode to jpeg. Blurring will reduce entropy and so the image might compress better. Considering that you are not satisfied with the JPEG compression, to reduce image size I guess you can't escape reducing quality, blurring might not be the worst solution, it depends on your problem.

Related

Change data of OpenCV matrix from pointer

I am trying to capture images from several cameras using the cameras driver,OpenCV and C++. My goal is to get as many FPS as possible, and to this end I have found saving the images in the hard drive to be the slowest operation. In order to speed up the process, I am doing each saving in separate threads. Problem is, I still have to wait for the saving to be complete to avoid the captured image being overwritten. Doing this provides good results, but for unknown reasons every 30-40 frames the speed is 10x higher.
I am addressing this by creating a ring buffer where I store the images, as these sudden drops in write speed are very short. I have obtained very good results using this approach, but unfortunately for more than 3 cameras the camera driver can't handle the stress and my program halts, waiting for the first image of the 4th camera to be saved. I checked and it's not the CPU, as 3 cameras + a thread writing random data in the disk works fine.
Now, seeing how using opencv reduced the stress on the camera driver, I would like to create a OpenCV mat buffer to hold the images while they are saved without my camera overwritting them (well, not until the buffer has done a whole lap, which I will make sure won't happen).
I know I can do
cv::Mat colorFrame(cv::Size(width, height),CV_8UC3,pointerToMemoryOfCamera);
to initialize a frame from the memory written by the camera. This does not solve my problem, as it will only point to the data, and the moment the camera overwrites it, it will corrupt the image saved.
How do I create a matrix with a given size and type, and then copy the contents of the memory to this matrix?
You need to create a deep copy. You can use clone:
cv::Mat colorFrame = cv::Mat(height, width, CV_8UC3, pointerToMemoryOfCamera).clone();
You can also speed up the process of saving the images using matwrite and matread functions.

Encode image in JPG with OpenCV avoiding the ghost effect

I have an application (openCV - C++) that grab an image from webcam, encode it in JPG and trasmitt it from a Server to Client. Thwebcam is stereo so actually I have two image, LEFT and RIGHT. In the client, when I recieve the image I decode it and I generate an Anaglyph 3D Effect.
For do this I use the OpenCV...
Well I encode the image in this way:
params.push_back(CV_IMWRITE_JPEG_QUALITY);
params.push_back(60); //image quality
imshow(image); // here the anagliphic image is good!
cv::imencode(".jpg", image, buffer, params);
and decode in this way:
cv::Mat imageRecieved = cv::imdecode( cv::Mat(v), CV_LOAD_IMAGE_COLOR );
What I see is that this kind of encode generate in the Anaglyph image a "ghost effect" (artifact?) so there is a bad effect with the edges of the object. If look a door for example there a ghost effect with the edge of the door. I'm sure that this depends of the encode because if I show the Anaglyph image before encode instruction this work well. I cannot use the PNG because it generate to large image and this is a problem for the connection between the Server and the Client.
I look for the GIF but, if I understood good, is nt supported by the cv::encode function.
So there is another way to encode a cv:Mat obj in JPG withou this bad effect and without increase to much the size of the image?
If your server is only used as an image storage, you can send to the server the 2 original stereo images (compressed) and just generate the Anaglyph when you need it. I figure that if you fetch the image pair (JPEG) from the server and then generate the Anaglyph (client-side), it will have no ghosting. It might be that the compressed pair of images combined is smaller than the Anaglyph .png.
I assume the anaglyph encoding is using line interlacing to combine both sides into one image.
You are using JPEG to compress the image.
This algorithm optimized to compress "photo-like" real world images from cameras, and works very well on these.
The difference of "photo-like" and other images, regarding image compression, is about the frequencies occurring in the image.
Roughly speaking, in "photo-like" images, the high frequency part is relatively small, and mostly not important for the image content.
So the high frequencies can be safely compressed.
If two frames are interlaced line by line, this creates an image with very strong high frequency part.
The JPEG algorithm discards much of that information as unimportant, but because it is actually important, that causes relatively strong artefacts.
JPEG basically just "does not work" on this kind of images.
If you can change the encoding of the anaqlyph images to side by side, or alternating full images from left and right, JPEG compression should just work fine.
Is this an option for you?
If not, it will get much more complicated. One problem - if you need good compression - is that the algorithms that are great for compressing images with very high frequencies are really bad at compressing "photo-like" data, which is still the larger part of your image.
Therefore, please try really hard to change the encoding to be not line-interlacing, that should be about an order of magnitude easier than other options.

Detecting a black/blank frame in video using OpenCV

I'm using OpenCV 2.4.2 VideoCapture class to grab frames from multiple videos and my aim is to compare the frames between videos to retrieve similar videos (visually similar).
I'm facing two issues.
The videos contain blank/black frames.
I can loop over each individual frame (while capturing the video) and check the pixels etc. to detect these frames. Is there a faster and more efficient way to somehow do this? I have more than 1k videos and each video has around 5k-20k frames [I'm capturing 1 frame per second]. I'm coding in C++.
Comparing two huge matrices to check how "similar" they are.
I eventually compute a huge matrix for each video where the rows correspond to the number of the frames, and the cols correspond to the dimensionality of the descriptor being computed on each frame. If I need to compare two videos for similarity, the simplest thing I found was to compute Euclidean matrix. But again, horribly inefficient if I scale up to 1000s of videos.
Any advice and suggestion will be appreciated.
Thanks,
Concerning the first problem, I think cv::countNonZero is the most suitable method, it works very fast as well. cv::countNonZero returns the number of non-zero elements in input single-channel array.

How can I load 8 bit bmp with OpenGL?

Here is my situation: I need to preload 2000 images and display them in sequence to be an animation in 60 fps. Currently, I am using OpenGL to load bmp files, but due to memory limit, I can only preload up to 500+ images. How can I solve this problem? I can so far come up with two directions of solutions: First, maybe I can load 8 bit bmp images to save memory. But I have difficulty in using glDrawPixels. Secondly, if possible can I load jpeg directly? Thanks for any advice!
The reason for not using video is that I need to change to animation speed by skipping one or more images as you can see in the code (imgCount+=stp; // stp means how many images to escape. it can make video faster). And in my animation, frame rate is important, FPS lower than 50 shows flickering.
Here is the code:
void Frame::LoadBMP(void){
FILE *in;
in=fopen(file,"rb");//open file
if(in==NULL){
exit(0);
}
fread(&(this->bmfh),sizeof(BITMAPFILEHEADER),1,in);//read bmp file header
fread(&(this->bmih),sizeof(BITMAPINFOHEADER),1,in);//read bmp infomation header
colours=new RGBQUAD[bmih.biBitCount];
fread(colours,sizeof(RGBQUAD),bmih.biBitCount,in);//read bmp colour table
size=bmfh.bfSize-bmfh.bfOffBits;
tempPixelData=new GLubyte[size];
if(tempPixelData==NULL) {
fclose(in);
}
fread(tempPixelData,sizeof(GLubyte),size,in);//read bmp image data
fclose(in);
}
and I will display the sequence of images, the display code:
void display(void){
static clock_t start=clock();
static clock_t end=clock();
CurrtempPixelData=msFrame[offset]->tempPixelData;
glEnable(GL_ALPHA_TEST);
glEnable(GL_BLEND);
glDrawPixels(frWidth, frHeight, GL_RGBA, GL_UNSIGNED_BYTE, msFrame[offset]->tempPixelData);
for(int i=0;i<m;i++){
clock_t c=clock();
}
glutSwapBuffers();
imgCount+=stp; // stp means how many images to escape. it can make video faster.
offset=imgCount%numFrame;
glutPostRedisplay();
}
You should not use glDrawPixels, it is deprecated functionality. The best way to do it would probably be drawing a screen-sized quad (-1,-1 => 1,1 without any matrix transform) that you texture with these images.
For the textures you can specify several internal formats in glTexImage2D and similar functions. For example, you could use the GL_R3_G3_B2​ format to get your 8-bit size, but could as well use the compressed formats like S3TC. You could for example pass COMPRESSED_SRGB_S3TC_DXT1_EXT, which should reduce your image size to 4 bits per pixel, likely at better quality than the 8 bit format. You cannot use JPEG as a compression format in OpenGL (it's too complex).
Finally, why do you want to do this through OpenGL? blitting an image to a regular window will likely give you well enough performance. Then you could even store your image sequence as video and just blit the decoded frames. It's very unlikely you will ever get memory problems in this case.
Maybe you don't need to have 2000 images and display them at 60fps at all? Stable 25fps is just enough for any movie.
I encourage you to rethink your original problem and come up with a better suited solution (video, animation, vectors, maybe something else)
As for original question:
If you need images only once - put them to memory when you need them and discard them right after displaying.
Use DXT packed images. With a slight degrade in quality you get a constant x4/x8 compression ratio.
OpenGL is not very good at working with paletted textures these days (many vendors have poor implementations). But you can implement that with shaders.

How can I compress jpeg image with compression rate 4 bpp or less?

I am trying to compress my .jpeg image in Photoshop.
WHat is the best way to do this?
I am now calculating the bpp taking the image size in kb, calculating how many bits that is. Then I take the image size in pixel*pixel to get the amount of pixels in the image. After that I divide bits/pixels, to find how many bits per pixel the image has.
But How can I change this number? My guess is to change how many kb the image is, but how do i do this?
Thanks for any help!!
Yes, you can achieve higher compression ratio than 4 bits per pixel. Images with solid color can have rate as low as 0.13bpp.
In fact 4bpp is quite poor compression — it's same as uncompressed 16-color image or half of 256-color image, which even GIF can manage. JPEG can look decent at 1-2bpp.
in general, you cannot "compress" a jpeg image. all you can do is to reduce the image quality further in order to achieve a lower bpp value. jpeg streams are always compressed and they use a lossy compression method. it means that the original image will never ever be reconstructed from a jpeg image. the smaller the file the more information you have lost.
a specific "bpp value" is not, and should never be your target. especially with lossy compression. you should always look at your current image and decide whether it is still good enough or not.
if you still have the original image, try a lossless compression format, like zip-compressed or lzw-compressed tiff or compressed png. i'm sure PhotoShop can handle these formats as well. another softwares like IrfanView (https://www.irfanview.com/) or XnView MP (https://www.xnview.com/en/xnviewmp/) will convert your images too.
if you want manual (eg. full) control over your images, you should use command line utilities, like ImageMagick (https://imagemagick.org/) or NConvert (please find the XnView MP link above)
if you have only the jpeg images do not touch (edit & save) them. with every single save operation you lose another bunch of information. you should always work on file copies.
you should always keep your master image (the very picture you took with your phone or your camera).
of course, these rules of thumb will not answer your original question.