Poor quality from "Save as JPEG"? (Linux, 2.6.4 x86_64)

Started by FeRD_NYC, January 27, 2014, 02:32:01 PM

Previous topic - Next topic

FeRD_NYC

All,

I originally reported this to the Fedora packagers @ rpmfusion, but I thought I'd bring this issue here and ask if anyone else has encountered similar issues? I'm posting this in the Unix-Like subsection of the forum as I suspect it's an issue that's specific to either the packages or the platform, though that's purely my assumpton.

Basically, see my rpmfusion bug report for full details and sample files. But I'll copy over the meat of what I reported there. (Yes, at the moment we're still running 2.6.4, though the packager is currently working on 2.6.7 builds. If that solves this issue, then great.)

Quote
I'm used to reaching for avidemux as a reliable tool for creating still frame images from video files, as it offers better frame-by-frame seeking than anything else out there and has generally produced excellent captures. However, attempting this today with a video file produced AWFUL results, with the resulting JPEG appearing blocky and distorted.

I was able to produce a screencap at the quality I needed, by using gnome-screenshot to literally capture an image of the frame right from the avidemux interface.

[...]

The issue does not appear dependent on whether the frame in question is a keyframe, the JPEG is poor-quality regardless.

I experimented with the "Save as BMP" feature, in the QT case the file was distorted beyond recognition, in the AVI case it was actually fairly close to the screengrab. So, that may be a workaround in same cases. Generally, though, BMP files are of little use to... well, anyone.

[...]

Especially since the JPEG file appears degraded just in comparison to what's visible in the avidemux interface for that given "frame". Even absent frame-accurate seeking, I would think that saved images should at least have equivalent quality to what's shown on the screen.

The problem I observed seemed more like it was writing the output file at the absolute lowest (most lossy) JPEG quality level. I attempted to examine the file to determine whether this was the case, but none of the tools I pointed at the image gave any indication of the JPEG quality setting it was saved under, which I found odd. I was also unable to find any way to specify JPEG output quality in the avidemux interface.

Still, the fact that the 1280x720 still from Big Buck Bunny (third attachment) is only 32.67KB in size, whereas the corresponding .PNG screengrab clocked in at over a megabyte, seems to indicate that the JPEG file is a victim of some very serious compression. Perhaps, absent any setting for JPEG quality, the output library is defaulting to the lowest possible level?

mean

I checked the code and did some test : Jpeg are saved using quantizer=2, should be high-ish quality
Tested on a sample, jpg was 200kBytes, which is not big, but no ridiculously small either

FeRD_NYC

mean,

Thanks for looking at this. Your reply inspired me to look at the code myself, and also do a little digging/testing re: encoding parameters.

I ginned up a test program based on the video_encode_example() function from ffmpeg's decoding_encoding.c sample file, but with the output setup logic from avidemux's ADM_imageSave.cpp. Then I varied some parameters.

What I found was, at least when compiling and linking against RPMFusion's ffmpeg-libs-1.2.5-1.fc19.x86_64 (libavcodec.so.54.92.100, libavutil.so.52.18.100), setting frame.quality had NO EFFECT on the size of the output file. Nor did setting the global_quality parameter of the context, which I'd seen in code I found on various mailing lists / forums.

It was only when I set the context's qmin and qmax parameters that the size of the output file varied, and when those parameters are set both global_quality and frame.quality seem to be ignored. (Well, they always seem to be ignored.)

With frame.quality and/or c->global_quality set to any value, my output file size for a 1280x720 dummy frame was consistently 58192 bytes (58k).
c->qmin = c->qmax = 2: output file size=93684 bytes (93k).
c->qmin = c->qmax = 1: output file size=143125 bytes (143k).
c->qmin = c->qmax = 10: output file size=56574 bytes (56k).

So, it appears that, by not setting qmin/qmax in the encoding context, recent ffmpegs are falling back to a quantizer not much better than 10.

I'm attaching the test code I used, please excuse the VERY quick-and-dirty nature. All of my testing was done by editing the parameters found at the two XXX comments and recompiling, via:
cc `pkg-config --cflags libavcodec libavutil` `pkg-config --libs libavcodec libavutil` testframe.c -o testframe

mean

I dont know how it is packaged, but normally avidemux is using its own internal version off libav* (libADM5av..)

FeRD_NYC

I had near-identical results (give or take a few bytes) linking against the libADM6avcodec.so.54 and libADM6avutil.so.52 from avidemux-libs-2.6.4-5.fc19.x86_64, which (near as I can tell from the build logs) looks like it's ffmpeg-1.1.2 built in the avidemux approved manner.


[ferd]% ls -l libA*
lrwxrwxrwx. 1 ferd ferd 31 Feb  3 04:05 libADM6avcodec.so -> /usr/lib64/libADM6avcodec.so.54
lrwxrwxrwx. 1 ferd ferd 30 Feb  3 04:06 libADM6avutil.so -> /usr/lib64/libADM6avutil.so.52

### XXX: c->qmin = c->qmax = 1

[ferd]% make testframe
cc -I/usr/include/avidemux/2.6/ -L. -lADM6avcodec -lADM6avutil testframe.c -Wno-deprecated-declarations -o testframe
[ferd]% ./testframe
Encode video file testframe.jpg
Write frame (size=142650)

### XXX: frame->quality = (int) floor (FF_QP2LAMBDA * 2+ 0.5);

[ferd]% make testframe
cc -I/usr/include/avidemux/2.6/ -L. -lADM6avcodec -lADM6avutil testframe.c -Wno-deprecated-declarations -o testframe
[ferd]% ./testframe
Encode video file testframe.jpg
Write frame (size=57764)

### XXX: frame->quality = (int) floor (FF_QP2LAMBDA * 10+ 0.5);

[ferd]% make testframe
cc -I/usr/include/avidemux/2.6/ -L. -lADM6avcodec -lADM6avutil testframe.c -Wno-deprecated-declarations -o testframe
[ferd]% ./testframe
Encode video file testframe.jpg
Write frame (size=57764)

mean

Ok, thanks for checking
I might do the long overdue update to ffmpeg 2.X first

FeRD_NYC

This one-line patch (also attached)...


diff --git a/avidemux_core/ADM_coreImage/src/ADM_imageSave.cpp b/avidemux_core/ADM_coreImage/src/ADM_imageSave.cpp
index dbea3a0..797cad0 100644
--- a/avidemux_core/ADM_coreImage/src/ADM_imageSave.cpp
+++ b/avidemux_core/ADM_coreImage/src/ADM_imageSave.cpp
@@ -194,6 +194,7 @@ ADM_byteBuffer   byteBuffer;
     
     // Encode!
       context->flags |= CODEC_FLAG_QSCALE;
+      context->qmin = context->qmax = 2;
       frame.quality = (int) floor (FF_QP2LAMBDA * 2+ 0.5);

       byteBuffer.setSize(_width*_height*4);


When applied to avidemux-2.6.4, has results that speak for themselves. The same video frame,

1. Saved from rpmfusion's avidemux-2.6.4-5.fc19.x86_64
https://dl.dropboxusercontent.com/u/1031979/bugs/avidemux-testframe-bad.jpg

2. Saved from my patched avidemux-2.6.4-6.fc19.x86_64
https://dl.dropboxusercontent.com/u/1031979/bugs/avidemux-testframe-fix.jpg

mean

The thing is that i tried with both ubuntu and win32, and they both work fine
I'd like to understand why you have a different behaviour

FeRD_NYC

Quote from: mean on February 04, 2014, 06:47:45 AM
The thing is that i tried with both ubuntu and win32, and they both work fine
I'd like to understand why you have a different behaviour

I'm certainly curious as well, but... at the same time, I guess I do and don't care why the behavior's different. I'm interested in investigating it further, though unfortunately without access to either of those OSes I'm unable to comparison-test myself, I'm stuck with what I can discover using the Fedora boxes at my disposal. I'd be happy to run further tests that might be informative, though right now I can't think of any. I previously pointed to the build logs for the avidemux version in question, also of interest may be the patches and .spec file that build was made from. But I doubt there's anything there that's of much use, honestly.

I could work up an all-in-one test program that generates a series of output files in a single run, without having to edit its code... compiling and running that linked against the libADM6 libraries on win32 or ubuntu would probably be informative. What I'd personally be most interested to see is whether setting filter.quality really controls the output quality/filesize on those other systems, because I saw no sign that it had ANY effect whatsoever in my tests.

And that's the thing, ultimately: From having surveyed other examples of how this sort of thing is usually coded, it feels "more right" to define output quality by setting the encoding context parameters (qmin/qmax, I suppose, unless there's a better choice). So, a change such as that patch doesn't seem "wrong" even if it doesn't appear to be necessary on some systems. More to the point, there shouldn't be any negative effects to setting qmin/qmax; at worst it may be unnecessary, but if it more "properly" sets up the encoding context then that may be reason enough to do it anyway. (So, that's the "I care but don't" part of it.)

FeRD_NYC

In fact, here's an updated testframe.c that creates a series of six test frame images, making successive calls to the test function while varying the way qmin/qmax and filter.quality are used. It just has to be compiled and linked against an appropriate libavcodec and libavutil, then run with no arguments.

I tried it with a just-built ffmpeg-2.1.3, like so:

%cc -I/home/ferd/include/ffmpeg/ -L/home/ferd/lib -lavcodec -lavutil -Wl,-rpath /home/ferd/lib testframe.c -o testframe
% ./testframe
Writing testframe-noq+f10.jpg (size=49131)
Writing testframe-noq+f2.jpg (size=49131)
Writing testframe-q2+f10.jpg (size=72867)
Writing testframe-q1+f10.jpg (size=116723)
Writing testframe-q2+f2.jpg (size=72867)
Writing testframe-q10+f2.jpg (size=47712)


Whereas, against avidemux-2.6.4 ffmpeg libs:

%cc -I/usr/include/avidemux/2.6/ -lADM6avcodec -lADM6avutil testframe.c -o testframe
% ./testframe
Writing testframe-noq+f10.jpg (size=58266)
Writing testframe-noq+f2.jpg (size=58266)
Writing testframe-q2+f10.jpg (size=93766)
Writing testframe-q1+f10.jpg (size=143207)
Writing testframe-q2+f2.jpg (size=93766)
Writing testframe-q10+f2.jpg (size=56653)


In every scenario I've tried it with, the filesizes have varied slightly but the results have been consistent in showing one thing: filter.quality has no effect on the output filesize/quality. I'd be extremely curious what the results would be on different/non-problematic systems.

mean

It seems the key is to set c->flags | = QSCALE **BEFORE* calling avcodec_open2

FeRD_NYC

Ah, yes! I see that now. Very interesting.

For the record, a run of my second testframe iteration, with that one change, against avidemux-2.6.4 libs:

% ./testframe
Writing testframe-noq+f10.jpg (size=56653)
Writing testframe-noq+f2.jpg (size=93766)
Writing testframe-q2+f10.jpg (size=93766)
Writing testframe-q1+f10.jpg (size=143207)
Writing testframe-q2+f2.jpg (size=93766)
Writing testframe-q10+f2.jpg (size=56653)


Seems like that flag causes ffmpeg to set up the context "hooked in" to frame.quality, perhaps?

It's also interesting to note that qmin/qmax still always override frame.quality, when set. (Though if they were used to define a range, as intended, into which frame.quality fell, I imagine the output quality of each frame would be set accordingly.)