How to estimate size of a Jpeg file before saving it with a certain quality factor? - jpeg

I'v got a bitmap 24bits, I am writing application in c++, MFC,
I am using libjpeg for encoding the bitmap into jpeg file 24bits.
When this bitmap's width is M, and height is N.
How to estimate jpeg file size before saving it with certain quality factor N (0-100).
Is it possible to do this?
For example.
I want to implement a slide bar, which represent save a current bitmap with certain quality factor N.
A label is beside it. shows the approximate file size when decode the bitmap with this quality factor.
When user move the slide bar. He can have a approximate preview of the filesize of the tobe saved jpeg file.

In libjpeg, you can write a custom destination manager that doesn't actually call fwrite, but just counts the number of bytes written.
Start with the stdio destination manager in jdatadst.c, and have a look at the documentation in libjpeg.doc.
Your init_destination and term_destination methods will be very minimal (just alloc/dealloc), and your empty_output_buffer method will do the actual counting. Once you have completed the JPEG writing, you'll have to read the count value out of your custom structure. Make sure you do this before term_destination is called.

It also depends on the compression you are using and to be more specific how many bits per color pixel are you using.
The quality factor wont help you here as a quality factor of 100 can range (in most cases) from 6 bits per color pixel to ~10 bits per color pixel, maybe even more (Not sure).
so once you know that its really straight forward from there..

If you know the Sub Sampling Factor this can be estimated. That information comes from the start of frame marker.
In the same marker right before the width and height so is the bit depth.
If you let
int subSampleFactorH = 2, subSampleFactorV = 1;
Then
int totalImageBytes = (Image.Width / subSampleFactorH) * (Image.Height / subSampleFactorV);
Then you can also optionally add more bytes to account for container data also.
int totalBytes = totalImageBytes + someConstantOverhead;

Related

AVAssetWriter getting raw bytes makes corrupt videos on device (works on sim)

So my goal is to add CVPixelBuffers into my AVAssetWriter / AVAssetWriterInputPixelBufferAdaptor with super high speed. My previous solution used CGContextDrawImage but it is very slow (0.1s) to draw. The reason seems to be with color matching and converting, but that's another question I think.
My current solution is trying to read the bytes of the image directly to skip the draw call. I do this:
CGImageRef cgImageRef = [image CGImage];
CGImageRetain(cgImageRef);
CVPixelBufferRef pixelBuffer = NULL;
CGDataProviderRef dataProvider = CGImageGetDataProvider(cgImageRef);
CGDataProviderRetain(dataProvider);
CFDataRef da = CGDataProviderCopyData(dataProvider);
CVPixelBufferCreateWithBytes(NULL,
CGImageGetWidth(cgImageRef),
CGImageGetHeight(cgImageRef),
kCVPixelFormatType_32BGRA,
(void*)CFDataGetBytePtr(da),
CGImageGetBytesPerRow(cgImageRef),
NULL,
0,
NULL,
&pixelBuffer);
[writerAdaptor appendPixelBuffer:pixelBuffer withPresentationTime:presentTime];
-- releases here --
This works fine on my simulator and inside an app. But when I run the code inside the SpringBoard process, it comes out as the images below. Running it outside the sandbox is a requirement, it is meant for jailbroken devices.
I have tried to play around with e.g. pixel format styles but it mostly comes out with differently corrupted images.
The proper image/video file looks fine:
But this is what I get in the broken state:
Answering my own question as I think I got the answer(s). The resolution difference was a simple code error, not using the device bounds on the latter ones.
The color issues. In short, the CGImages I got when running outside of the sandbox was using more bytes per pixel, 8 bytes. The images I get when running inside the sandbox was 4 bytes. So basically I was simply writing the wrong data into the buffer.
So, instead of simply slapping all of the bytes from the larger image into the smaller buffer. I loop through the pixel buffer row-by-row, byte-by-byte and I pick the RGBA values for each pixel. I essentially had to skip every other byte from the source image to get the right data into the right place within the buffer.

How do I cap / clip audio volume via code?

I am dealing with an audio file that shows sudden spikes:
When I try to normalize the audio file, the audio program sees this spike, notices that it is at 0 dB and won't normalize the audio any more.
To solve this issue, I have applied a Limiter that limits to -3 dB. I have used both Steinberg Wavelab and Audacity Hard Limiter. Instead, they both diminish the entire audio volume.
Both do not eliminate this spike.
Edit: I have found out that "Hard Clip" in Audacity does what I need, but now I still want to finish my own approach.
So I was thinking that they perhaps do not work correctly.
Then I tried to write my own limiter in VB6 in order to have full control over what's happening.
To do that, I'm loading the audio data of a wav file like this:
(I have stripped the process down very much)
Dim nSamples() As Integer
ReDim nSamples(0 To (lCountSamples - 1))
Get #iFile, , nSamples
I have the audio data in "nSamples" now.
Dim i&
For i = 1 To UBound(nSamples)
Dim dblAmplitude As Double
dblAmplitude = nSamples(i) / 32767
Dim db As Double
db = 20 * Log10(dblAmplitude)
If db > -0.3 Then
nSamples(i)=0 'how would I lower the volume here instead of just setting it to 0???
End If
Next
I'm not sure how I could calculate the dB from the sample data in order to clip it and how to clip it to -3dB.
I have now simply tried it with settings the clipping value to "0".
However, something strange happens here: The lower part of the audio is gone:
I expected that setting a value to 0 would mute the audio, but in my case, the lower wav form is gone.
What is wrong about my approach?
Thank you!
Calculating dB from audio samples isn't that simple.
It sounds like what you want to do is find an appropriate threshold and then clip the audio with something like:
If Abs(nSamples(i)) > threshold Then
nSamples(i) = threshold * Sgn(nSamples(i))
You could set the threshold to some fixed value if it's the same for all of your audio clips. Otherwise, it might be more accurate to sort the samples and search for a large gap between points near the top and bottom to find your threshold.

Python script skyrockets size of pagefile.sys

I wrote a Python script that tends to crash sometimes with a Memory Allocation Error. I noticed that the pagefile.sys of my Win10 64 system skyrockets in this script and exceeds the free memory.
My current solution is to run the script in steps, so that every time the script runs through, the pagefile empties.
I would like the script to run through all at once, though.
Moving the pagefile to another drive is not an option, unfortunately, because I only have this one drive and moving the pagefile to an external drive does not seem to work.
During my research, I found out about the module gc but that is not working:
import gc
and after every iteration I use
gc.collect()
Am I using it wrong or is there another (python-based!) option?
[Edit:]
The script is very basic and only iterates over image files (using Pillow). The script only checks for width, height and resolution of the image, calculates the dimensions in cm.
If height > width, the image is rotated 90° counterclockwise.
The images are meant to be enlarged or shrunk to A3 size (42 x 29.7cm), so I use the width/height ratio to calculate whether I can enlarge the width to 42cm and the height remains < 29.7cm and in case the height is > 29.7cm, I enlarge the height to 29.7 cm.
For the moment, I do the actual enlargement/shrinking still in Photoshop. Based on whether it is a width/height enlargement, the file is moved to a certain folder that contains either one of those file types.
Anyways, the memory explosion happens in the iteration that only reads the file dimensions.
For that I use
with Image.open(imgOri) as pic:
widthPX = pic.size[0]
heightPX = pic.size[1]
resolution = pic.info["dpi"][0]
widthCM = float(widthPX) / resolution * 2.54
heightCM = float(heightPX) / resolution * 2.54
I also calculate whether the shrinking would be too strong, the image gets divided in half and re-evaluated.
Even though it is unnecessary, I still added pic.close
to the with open()statement, because I thought Python may be keeping the image files open, but that didn't help.
Once the iteration finished, the pagefile.sys goes back to its original size, so in case that error occurs, I take some files out and do them gradually.

FFMPEG Understanding AVFrame::linesize (Audio)

As per the doucmentation of AVFrame, for audio, lineSize is size in bytes of each plane and only linesize[0] may be set. But however, am unsure whether lineszie[0] is holding per plane buffer size or is it the complete buffer size and we have to divide it by no of channels to get per plane buffer size.
For Example, when I call
int data_size = av_samples_get_buffer_size(NULL, iDesiredNoOfChannels, iAudioSamples, (AVSampleFormat)iDesiredFormat, 0) ; For iDesiredNoOfChannels = 2, iAudioSamples = 1024 & iDesiredFormat = AV_SAMPLE_FMT_FLTP data_size=8192. Pretty straightforward, as each sample is 4 bytes and since there are 2 channels total memory will be (1024 * 4 * 2) bytes. As such lineSize[0] should be 4096 for planar audio. data[0] & data[1] should be each of size 4096. However, pFrame->lineSize[0] is giving 8192. So to get the size per plane, I have to do pFrame->lineSize[0] / pFrame->channels. Isn't this behaviour different from what the documentation suggests or is my understanding of the documentaion wrong.
Old question but thought I'd answer it anyway for people who may be wondering the same thing.
In all audio AVFrames, only linesize[0] may be set and they are all required to be the same size. You should not be using linesize[1], etc. I don't know why they chose to do things this way because it's not consistent with video frames, but whatever. Just remember whether interleaved or planar only linesize[0] matters so you have to divide by channel count for planar.

Using mogrify to decrease image size

I'm trying to use mogrify to decrease the quality of the image to ultimately decrease the image size but rather than decreasing it, the image size is increasing. I'm using the following command:
mogrify -quality 20% 1.png
The image size is going from 2.5 mb to 4 mb, any idea?
PNG is a lossless format, so changing "quality" settings should do nothing at all with respect to the "image".
The mogrify documentation confirms this - "quality", when applied to a PNG, indicates which row filters to apply: a value ranging from 0 to 6.
Since the input 20 is invalid for a PNG file, it must have been silently replaced with a default value; presumably 0, which indicates no row filtering at all. (If you really want to know if this is the case, you could use a tool such as pngcheck on your before and after images.)
As to your target: it is unclear whether you want to decrease the physical image size in pixels, or the file size on disk, or (possibly) both. For the first, you can use -resize. For the second, try a PNG-recompressing tool such as pngcrush. For both, use the first method and then the second.
Another option may be to lower the number of color components, for example, from 24-bit RGB to indexed color. Finally, you can always convert the image type from PNG to JPEG, after which you can experiment with the "quality" parameter.

Resources