Log in

View Full Version : SSIM-SAD Optimization: How much should we trust our metrics?


Pages : [1] 2

Dark Shikari
25th July 2007, 15:37
There are really two types of video metrics out there, defined not by what they do but by how they are used. The first are those that are used to measure video quality after encoding, like SSIM and PSNR. The second are those that are used as part of the encoding process, usually for block-matching; in this case video quality becomes not just an interesting curiosity but a critical part of an encoder! Commonly used metrics used here include SAD, Hadamard, etc.

While hiking in a canyon near the Dead Sea on Monday, a thought struck me: should those two categories of metrics overlap? So the next day I went to try it out. For a laugh, I replaced SAD with SSIM in the code, and properly scaled SSIM so as to act similarly, using (16000*(1-SSIM)). It unfortunately dropped quality a bit, not at all to my surprise considering I expected it to fail. But then in a flash of insight I tried something else; I re-added SAD to the function and had the function average the SSIM and SAD values. The result was incredible; I got up to a 4% SSIM increase in some tests. Even the lowest increase of 0.8% (on a very noisy analog-capture source) was quite impressive considering it was just a single changed #define function.

Note: To count an "improvement" in SSIM I scaled all SSIM values by using Quality = 1/(1-SSIM), and compared quality values. This is equivalent (for you math folks), IIRC, to a three-point conformal mapping with 0 improvement mapped to 0%, half the variation (i.e. 0.99 SSIM instead of 0.98) mapped to 100% improvement, and 1.00 SSIM (perfect quality) mapped to an infinite amount of improvement. Since all it does is scale SSIM itself and ignores bitrate, it is likely a fair mapping and a fair method of comparing SSIM values. In simple terms it means that half the variation from the source = double the quality.

Ghost in the Shell: Standalone Complex Intro
3D CGI with complex karaoke subtitle effects, high motion)

1Mb/s (medium bitrate, 704x480, 2200 frames)
0.9689152
0.9684290
(1/(1-0.9689152))/(1/(1-0.9684290)) = 1.56% improvement

EVE Online FRAPS Footage
(Zero-noise, low-motion video)

5Mb/s (high bitrate, 1680x1050, 201 frames):
0.9966596
0.9966255
(1/(1-0.9966596))/(1/(1-0.9966255))= 1.02% improvement
2Mb/s (medium bitrate, 1680x1050, 201 frames):
0.9948298
0.9946849
(1/(1-0.9948298))/(1/(1-0.9946849)) = 2.8% improvement
1Mb/s (low bitrate, 1680x1050, 201 frames):
0.9917035
0.9913744
(1/(1-0.9917035))/(1/(1-0.9913744)) = 3.97% improvement
600kbps (very low bitrate, 1680x1050, 201 frames)
0.9785377
0.9781433
(1/(1-0.9785377))/(1/(1-0.9781433)) = 1.84% improvement

Rush Hour
(High-noise raw HD video footage)

2Mb/s (low bitrate, 1920x1080, 500 frames):
0.9457524
0.9451425
(1/(1-0.9457524))/(1/(1-0.9451425))= 1.12% improvement
5Mb/s (medium bitrate, 1920x1080, 500 frames):
0.9557523
0.9553930
(1/(1-0.9557523))/(1/(1-0.9553930))= 0.81% improvement

As you can see, at medium to low bitrates in the tests with less noise, the improvement was the best. The main curiosity of this was not at all a surprise, but still interesting nonetheless: PSNR dropped in the majority of the tests, usually about 0.02-0.04db. (though one test had it go up 0.08db) This means that SSIM was optimized at the expense of PSNR. This raises some questions:

1. Is it valid to use the same metric used for block-matching as a quality metric for the final encode?
1a. If not, what's another good metric that correlates well to human vision?

2. Does optimizing for a metric that correlates well to human vision improve subjective visual quality?

3. Why does averaging SSIM with SAD give the best results? Weighting one more than the other always decreased the resulting SSIM relative to the value achieved by the average.

4. I also tried using SSIM in place of or as part of the SATD function (Hadamard block-matching). This, no matter what, decreased overall SSIM, regardless of my tweaks to scaling factors and such. Did I make a mistake implementing SSIM into that part of the code, or is there a good reason for this?
4a. Is SSIM accurate at extremely high quality values, where SSIM >= 0.999? If it isn't, this would answer question 4, as the vast majority of SATD comparisons are done on blocks that are very similar to each other, at least from my tests.

5. Are there any good assembly implementations of SSIM out there? Using the non-assembly SAD function combined with a non-assembly SSIM doesn't exactly ensure the best speed. I was thinking that the best way to do a fast SSIM calculation would be to use a single 256x256 lookup table for the integer multiplications, passed as a pointer to the assembly function.

6. The following is my hacked-together SSIM-SAD function. I guessed on certain parts of it in terms of adapting the function for use as a block-matching metric; tell me if I made any mistakes.
#define PIXEL_SAD_C( name, lx, ly ) \
static int name( uint8_t *pix1, int i_stride_pix1, \
uint8_t *pix2, int i_stride_pix2 ){\
int x, y; \
int sad_sum=0; \
uint32_t s1=0, s2=0, ss=0, s12=0; \
for( y = 0; y < ly; y++ ) \
{ \
for( x = 0; x < lx; x++ ) \
{ \
sad_sum += abs(pix1[x] - pix2[x]); \
int a = pix1[x]; \
int b = pix2[x]; \
s1 += a; \
s2 += b; \
ss += a*a; \
ss += b*b; \
s12 += a*b; \
} \
pix1 += i_stride_pix1; \
pix2 += i_stride_pix2; \
} \
int ssim_c1 = (int)(.01*.01*255*255*lx*ly + .5); \
int ssim_c2 = (int)(.03*.03*255*255*lx*ly*(lx*(ly-1)) + .5); \
long vars = ss*ly*lx - s1*s1 - s2*s2; \
long covar = s12*ly*lx - s1*s2; \
float result = (float)(2*s1*s2 + ssim_c1) * (float)(2*covar + ssim_c2) \
/ ((float)(s1*s1 + s2*s2 + ssim_c1) * (float)(vars + ssim_c2)); \
return (sad_sum + 16000*(1-result))/2;} \

I used long for the vars/covars because I wasn't entirely sure that the int wouldn't overflow in all cases. It probably won't but I can fix that later.

What are your thoughts on this?

akupenguin
25th July 2007, 16:12
1. Is it valid to use the same metric used for block-matching as a quality metric for the final encode?
If and only if the answer to (2) is yes. Block matching is no less valid than any other place in the codec to optimize for any given metric. That doesn't necessarily mean it's a good idea, see (3).

2. Does optimizing for a metric that correlates well to human vision improve subjective visual quality?
Probably. It will certainly improve subjective quality more than optimizing for a metric that doesn't correlate well with human vision. Sure you can over-fit to your model, but what's the alternative? Any search algorithm can be rephrased as optimizing for some criterion, and a criterion that was designed as a HVS metric is probably better than a criterion that wasn't.

3. Why does averaging SSIM with SAD give the best results? Weighting one more than the other always decreased the resulting SSIM relative to the value achieved by the average.
Possibly because there are two components that the block matrching metric tries to combine: bit cost of the DCT coefficients (approximated with DCT or SATD or SAD), and distortion remaining after quantization (approximated with SSIM or SSD).

4. I also tried using SSIM in place of or as part of the SATD function (Hadamard block-matching). This, no matter what, decreased overall SSIM, regardless of my tweaks to scaling factors and such. Did I make a mistake implementing SSIM into that part of the code, or is there a good reason for this?
Did you try SATD for the fullpel part? Maybe it's just better.

4a. Is SSIM accurate at extremely high quality values, where SSIM >= 0.999? If it isn't, this would answer question 4, as the vast majority of SATD comparisons are done on blocks that are very similar to each other, at least from my tests.
SSIM should be more accurate than SAD or SATD when the blocks in question are sufficiently similar that no DCT coefficients will be coded (then any difference is pure distortion). It's the blocks with somewhat larger differences that are in question.

5. Are there any good assembly implementations of SSIM out there?
You mean besides the one in x264? ;)

I used long for the vars/covars because I wasn't entirely sure that the int wouldn't overflow in all cases. It probably won't but I can fix that later.
long is int on 32bit systems, so if it overflows you havn't helped much. Also arithmetic on ints gets truncated to int even if the result is stored in a larger type; you have to upcast before multiplying if you want to keep bits that would overflow.

Dark Shikari
25th July 2007, 16:16
You mean besides the one in x264? ;)
I must have missed it, I just looked for the C code and ignored the assembly :p

I'll try implementing SATD in place of SAD and seeing if there's a similar improvement.

What's a good secondary metric, similar to SSIM in that it correlates well with human vision, that could be used to see whether the quality is actually increasing or whether the optimization is merely tweaking the encode in a way that increases SSIM without increasing any other metric of quality?

akupenguin
25th July 2007, 17:10
VQM? DVQ? All I remember is that none of them is very much better than PSNR.

ToS_Maverick
25th July 2007, 21:06
thx Dark Shikari for the really interesting topics!

a question for you guys: would it be possible to add a "constant PSNR or SSIM" mode like CRF?
for further tweaks i think it would be nice to have switches like min max quant => min max PSNR/SSIM.

foxyshadis
26th July 2007, 02:38
CWSSIM and WCWSSIM correlate somewhat better with subjective impressions, but as far as I know there is no reference code, only formulae, and as both use wavelets they're more full-frame tests than block tests.

Dark Shikari
26th July 2007, 05:47
thx Dark Shikari for the really interesting topics!

a question for you guys: would it be possible to add a "constant PSNR or SSIM" mode like CRF?
for further tweaks i think it would be nice to have switches like min max quant => min max PSNR/SSIM.
That would be difficult I would think.

Anyways, I've found two cases in which my change fails catastrophically :p

1. For some reason, anime does not benefit from the SSIM-SAD optimization at all. PSNR/SSIM hardly change at all, so its not worth it in this case.

2. If a really really low CRF/QP is used (especially lossless -qp 0), x264 fails miserably; it uses loads of I-frames and the bitrate rises 3-4x.

akupenguin
26th July 2007, 06:00
At -qp 0, there are only two sane metrics: SAD and RD. Lossless mode has no distortion, so SSIM is useless. And it has no DCT, so approximations of a DCT are useless.

Dark Shikari
26th July 2007, 06:19
At -qp 0, there are only two sane metrics: SAD and RD. Lossless mode has no distortion, so SSIM is useless. And it has no DCT, so approximations of a DCT are useless.Yes, that's exactly what I figured, as visual quality doesn't matter at --qp 0, only absolute mathematical difference.

DeathTheSheep
26th July 2007, 15:54
I'll try implementing SATD in place of SAD and seeing if there's a similar improvement.

That would be quite interesting indeed. :) Keep us posted with your results; it's always nice to have more people tweaking the quality settings experimentally.

Dark Shikari
26th July 2007, 22:03
That would be quite interesting indeed. :) Keep us posted with your results; it's always nice to have more people tweaking the quality settings experimentally.

Here's an interesting update for you!

I've been futzing with SSD (sum of squared differences) by trying something similar to what I did with SAD.

So far I haven't done much, but my change currently has lowered PSNR by 2db (!) while basically keeping SSIM constant (?!?!?!!). :p (And no, I made sure that the PSNR metric calculation wasn't changed by my SSD changes)

This means one of two things:

1. SSD is affecting the encoder in two places (analyzing and RDO). In one of these, my change increases SSIM a lot and keeps PSNR roughly the same, maybe decreasing it a bit. In the other, the change drops SSIM and drops PSNR because the code just isn't meant to work that way.

2. The change just sucks. :rolleyes:

I hope its 1, but I have a gut feeling its 2. :p

DeathTheSheep
27th July 2007, 02:14
And that is interesting indeed!

It almost makes you wonder whether SSD would yield results much different than SSE, Sum of Squared Error...

Anyway, how goes the SATD implementation? :)

akupenguin
27th July 2007, 02:24
SSD is SSE is MSE

DeathTheSheep
27th July 2007, 02:30
Now that's a relief; its just a classic case of "too many terms chasing too few algorithms."

:p

Dark Shikari
27th July 2007, 19:25
Ugh... I'm having a very ugly problem.

Originally I just used --no-asm to force the program to use my C files instead of the ASM code, but I figured it would be smarter to simply add my own new functions while keeping the current ASM, so I don't have to disable all the ASM to make a small change.

It worked great... until...

(line 2356 of analyze.c)

else if( analysis.b_mbrd )
{
i_bskip_cost = ssd_mb( h );

/* 6 = minimum cavlc cost of a non-skipped MB */
if( i_bskip_cost <= 6 * analysis.i_lambda2 )
{
h->mb.i_type = B_SKIP;
x264_analyse_update_cache( h, &analysis );
return;
}
}

When I replace ssd_mb(h) with ssd_ssim(mb(h)), my modified function (which works fine anywhere else), I get a whole slew of infinities!

But wait! Where are they coming from? Guess where..

An integer value, like (2*s1*s2 + ssim_c1), is cast as float... the integer value is perfectly normal... but the float ends up as an infinity, just from a simple cast!

And guess what... turning on --no-asm fixes it.

I'm guessing one of the assembly functions is doing something ugly with the MMX/float registers :rolleyes: any ideas?

Manao
27th July 2007, 19:43
You need to use the mmx instruction "emms" when switching from simd to float computaions.

Basically, in your ssd_ssim function, call x264_emms() before doing any float compuation.

Dark Shikari
27th July 2007, 19:45
You need to use the mmx instruction "emms" when switching from simd to float computaions.

Basically, in your ssd_ssim function, call x264_emms() before doing any float compuation.
Ah, that makes sense, thanks.

Dark Shikari
27th July 2007, 22:35
OK, testing the SSIM-SSD optimizations is done. The results are quite curious.

In my main test video, the zero-noise FRAPS footage that gained most from the SSIM-SAD optimization, the final version of the SSIM-SSD optimization gave a very minimal boost.

Now, I then tried the same with my DVD source, a 1001-frame clip from Ocean's Eleven, encoded at 1 mbps., a relatively high bitrate considering the heavy denoising I did when originally backing up the source.

SSIM-SAD Optimization Only:
(1/(1-0.9937967))/(1/(1-0.9937528))= +0.71% SSIM
PSNR change: +0.022db

SSIM-SAD + SSIM-SSD Optimization:
(1/(1-0.9938638))/(1/(1-0.9937528))= +1.8% SSIM
PSNR change: -0.060db

SSIM-SSD Optimization Only:
(1/(1-0.9938192))/(1/(1-0.9937528))= +1.07% SSIM
PSNR change: -0.081db

I'll do it on a better source next.

DeathTheSheep
27th July 2007, 22:41
Hey, would you mind uploading a copy of the binaries for us to test, too? (at least for those of us with compiler troubles)

I think your work might actually be benefited by more input. :)

Dark Shikari
27th July 2007, 22:52
Hey, would you mind uploading a copy of the binaries for us to test, too? (at least for those of us with compiler troubles)

I think your work might actually be benefited by more input. :)
I agree, it would probably help a lot to have your input.

I'll upload the binaries, but note that I'm using Cygwin, not MinGW (just because its what I'm used to), so it'll require the Cygwin DLLs and such to run.

Also note I'm not using the latest x264 code or any patches; the code I have is from a few SVN revisions ago but I doubt that matters for any of these changes. The only really significant change recently was to the AQ code, I think.

Link to the 4 different EXEs I'm currently using is http://www.mediafire.com/?a3oydml9dvc.

Which ones they are should be obvious by the names. Included is of course an "original" EXE with no modifications, for comparison.

Note currently you MUST use --no-asm on the last three for them to work entirely correctly. I think the SSD-OPT one might work without it, but the SAD-OPT hasn't been fixed to work without --no-asm yet.

The EXEs also have no pthread or mp4 support.

I'm currently running 5 tests (1 first pass, and 4 2nd passes, one for each EXE) on a very very high-action scene (90 seconds) from Appleseed, a CGI film that should hopefully stretch x264 to its limits here.

IgorC
28th July 2007, 00:41
Dark Shikari. What version of SSIM did you implement?
The last one was 0.24a with a lumimasking=true fix.

Dark Shikari
28th July 2007, 02:01
Appleseed: 2159 frames, high-motion scene, 1:31:00 to 1:32:30 (approx).

Results (1 megabit/s):

(1/(1-0.9798201))/(1/(1-0.9795658))=

Original
SSIM: 0.9795658
Average PSNR: 42.725

SAD-Opt
SSIM: 0.9798201 (+1.26%)
Average PSNR: 42.738 (+0.13db)

SAD+SSD-Opt
SSIM: 0.9796064
Average PSNR: 42.608

SSD-Opt
SSIM: 09793269
Average PSNR: 42.593

So in this case SSD-Opt seems to be a complete failure.

Dark Shikari
28th July 2007, 02:02
Dark Shikari. What version of SSIM did you implement?
The last one was 0.24a with a lumimasking=true fix.
I just copied the SSIM function used within x264. Not sure if its the latest one, or how good it is; I inferred some things, for example: I figured 64 was actually lx*ly, for example, which might not be true. See my code in the first post.

DeathTheSheep
28th July 2007, 02:19
First thing I noticed is how slow x264 is without assembly optimizations--almost 2.5 times slower. :p

Clip: Bleach (anime) episode 45 clip (1626 frames).
Features: Low-motion, low-resolution, dark (low lighting and contrast).

Codec settings: --me esa --no-asm --qp 30 --filter 1,0 --no-fast-pskip --no-cabac
--subme 7 --analyse p8x8,b8x8,i4x4,p4x4 --thread-input --progress --keyint 1500

ORIGINAL:

SSIM: 0.9649598
PSNR: 38.570

SAD-Opt:
SSIM: 0.9646586
PSNR: 38.480

SSD-Opt:
SSIM: 0.9668956
PSNR: 38.693

SAD+SSD-Opt:
SSIM: 0.9668645
PSNR: 38.608

Exactly the opposite results. Here, SAD-Opt is the complete failure, getting both PSNR and SSIM well below the original, while SSD-opt got the highest PSNR and SSIM of all (just slightly above SSD). I used a completely different (opposite) source than you did, and perhaps vastly different codec settings, but it led to opposite results.

The common theme in all of this is that SAD&SSD optimized encoder gets the higher average result in the long run and in most situations. But this, too, will need some more testing.

Hope this helped somewhat!

PS: I hope you can get the asm working soon because it's painful to encode so slowly. ;)

[edit]Woah, I didn't notice how the filesizes differed so drastically (679KB original, 686KB SAD, 737KB SSD, 757 SSD+SAD). I'll do some 3-pass now and try to hit at an identical filesize...

Dark Shikari
28th July 2007, 02:28
First thing I noticed is how slow x264 is without assembly optimizations--almost 2.5 times slower. :p

Clip: Bleach (anime) episode 45 clip (1626 frames).
Features: Low-motion, low-resolution, dark (low lighting and contrast).

Codec settings: --me esa --no-asm --qp 30 --filter 1,0 --no-fast-pskip --no-cabac
--subme 7 --analyse p8x8,b8x8,i4x4,p4x4 --thread-input --progress --keyint 1500

ORIGINAL:

SSIM: 0.9649598
PSNR: 38.570

SAD-Opt:
SSIM: 0.9646586
PSNR: 38.480

SSD-Opt:
SSIM: 0.9668956
PSNR: 38.693

SAD+SSD-Opt:
SSIM: 0.9668645
PSNR: 38.608

Exactly the opposite results. Here, SAD-Opt is the complete failure, getting both PSNR and SSIM well below the original, while SSD-opt got the highest PSNR and SSIM of all (just slightly above SSD). I used a completely different (opposite) source than you did, and perhaps vastly different codec settings, but it led to opposite results.

The common theme in all of this is that SAD&SSD optimized encoder gets the higher average result in the long run and in most situations. But this, too, will need some more testing.

Hope this helped somewhat!

PS: I hope you can get the asm working soon because it's painful to encode so slowly. ;)

[edit]Woah, I didn't notice how the filesizes differed so drastically (679KB original, 686KB SAD, 737KB SSD, 757 SSD+SAD). I'll do some 3-pass now and try to hit at an identical filesize...
Don't use --qp to test, use 2-pass bitrate! Using --qp is a very bad idea because its hard to compare videos at different bitrates due to the nonlinearity of the rate/distortion curve.

I've also found that this optimization seems to have very minimal effects on anime sources, for some reason.

Manao
28th July 2007, 07:23
On the contrary, use --qp, but do several qps ( for example, 29, 30 & 31 ). You are testing the coding efficiency of the codec, not the rate control robustness toward your metric's changes.

Dark Shikari
28th July 2007, 09:41
On the contrary, use --qp, but do several qps ( for example, 29, 30 & 31 ). You are testing the coding efficiency of the codec, not the rate control robustness toward your metric's changes.
What do you mean? I can very very easily get exactly-correct rates on the clips I test regardless of my changes (at least on longer clips). On shorter clips there are often slight bitrate differences but they're usually very very small in my experience.

Or do you mean that the ratecontrol algorithm, in an attempt to keep the bitrate accurate, might hurt the quality? In that case couldn't that be fixed by running an extra first-pass for each of the modified EXEs?

Dark Shikari
28th July 2007, 10:06
Another set of results, this time with Rush Hour (one of the raw YUV files available on that site) denoised with fft3dGPU(sigma=3.5,beta=1,bt=3,precision=2,sharpen=0.8) to handle the heavy analog noise.

Results (5 megabit, 1920x1080):

(1/(1-0.9918567))/(1/(1-0.9917711))=

Original
SSIM: 0.9917711
PSNR: 47.109

SAD-Opt
SSIM: 0.9918567
PSNR: 47.119

SAD-SSD-Opt
SSIM: 0.9918456
PSNR: 47.011

SSD-Opt
SSIM: 0.9917702
PSNR: 47.015

Seems SAD-Opt is the only particularly successful version, with a roughly 1.05% SSIM boost.

Manao
28th July 2007, 10:19
I mean that rate control has a great influence on SSIM/PSNR, and that disabling it by using --qp makes the PSNR/SSIM changes solely the result of your changes on the macroblock decision.

With a two passes encoding, you mesure both your changes and their side effects on the rate control.

Have a look at : http://akuvian.org/src/x264/ratecontrol.txt

You'll see that a one pass ABR encoding uses the result of a ME made on the frame in order to take the quantizer decision on that frame. The ME depends on the function h->pixf.mbcmp which you have modified, if I'm not mistaken. And the rate control uses the best cost scores found to take the quantizer decision.

The same happens in the frame type decision algorithm.

So, the best imho is to force the number of bframes, to use --qp, and to plot several (metric,bitrate) couple in order to compare two different distorsion measures.

akupenguin
28th July 2007, 10:28
Your patch adjusts the bitrate-per-qp. Ratecontrol adjusts qp to compensate. But there's no guarantee that either of those effects is uniform across all frames. If your patch causes ratecontrol to move bits from one frame to another, then that could cause a change in quality.
You might ask: most people use ratecontrol, so isn't that the real-world application? No, because if tweaking ratecontrol improved quality then we could get the same boost by tweaking ratecontrol code rather than your patch; and if tweaking ratecontrol reduced quality then we should still determine whether your patch improves raw compression rate and then tweak ratecontrol to compensate if necessary.
And that's even aside from the specific relationship between ME and RC that Manao discussed.

Dark Shikari
28th July 2007, 11:02
You might ask: most people use ratecontrol, so isn't that the real-world application? No, because if tweaking ratecontrol improved quality then we could get the same boost by tweaking ratecontrol code rather than your patch; and if tweaking ratecontrol reduced quality then we should still determine whether your patch improves raw compression rate and then tweak ratecontrol to compensate if necessary.
That does make sense; if ratecontrol affects quality in a negative way in this case, we'd want to improve it, and if it improves quality, we'd want to know why anyways.

I'll run some tests later on constant QP with one b-frame. What other options should I remove to ensure that we're focusing only on MB prediction and avoiding different frametypes and so forth?

./x264_original.exe --merange 16 --trellis 2 --subme 6 --bframes 4 --ref 1 --b-pyramid --partitions all --8x8dct --me umh --bime --b-rdo --mixed-refs --direct auto --weightb --progress --pass 2 --bitrate 5000 --deblock 0:0 -o test.mkv input.avs

foxyshadis
28th July 2007, 22:07
Do you have current patches available?

Dark Shikari
28th July 2007, 22:12
Do you have current patches available?
I'll post some patches in a bit. I haven't made patches in a while--what tool is usually used to make patches... diff?

I'm currently running the --qp test using --trellis 2 --subme 6 --bframes 1 --partitions all --8x8dct --me hex --bime --b-rdo --direct auto --progress --no-asm --deblock 0:0, using QPs 24, 25, and 26.

Dark Shikari
28th July 2007, 23:18
OK, results so far.

Obviously the bitrate isn't constant between the various modifications of x264, though its close. Here's my method for "normalizing" the SSIM for different bitrates.

1. Convert all SSIMs to more linear quality values (1/(1-SSIM)).
2. Find the numerical derivative of Quality/Bitrate at QP=25 using the derivative between 24 and 25, and 25 and 26, and then averaging those.
3. Use this derivative, multiplied by the small difference in bitrate from the original, to normalize the quality value.
4. Convert quality back to SSIM.

It should be pretty accurate; I used the same method for PSNR (but without the quality transform, as PSNR doesn't have the same inverse property). It seems SAD modification actually lowers bitrate a tiny bit, but SSD raises it enough to make it no longer worthwhile. SAD-Opt gives a 1.509% SSIM increase with a minimal PSNR loss (-0.002db). SSD-Opt gives a 0.28% SSIM loss and a 0.1db PSNR loss. SAD-SSD-Opt is still testing.

foxyshadis
28th July 2007, 23:39
I'll post some patches in a bit. I haven't made patches in a while--what tool is usually used to make patches... diff?

svn diff is probably the simplest, if you're working off svn. Otherwise any ol' diff tool works, you just need a backup of your starting code. Kdiff is nice.

I figure since I'm testing out a couple recent patches from the mailing list, I should check on this too. =p

Dark Shikari
28th July 2007, 23:42
svn diff is probably the simplest, if you're working off svn. Otherwise any ol' diff tool works, you just need a backup of your starting code. Kdiff is nice.

I figure since I'm testing out a couple recent patches from the mailing list, I should check on this too. =p
There's a mailing list? Link, I need to join in on this :cool:

I'm not using SVN... just notepad, an "original" set of code, and a "modified" set of code with comments, combined with my memory :p

Dark Shikari
28th July 2007, 23:49
Diffs:

(Note: pixel.c diff is needed for both SAD-OPT and SSD-OPT. Currently, it has SAD-OPT and SSD-OPT enabled, but SSD-OPT is enabled separately in the other files, so it is not enabled unless you also patch rdo.c and analyse.c)

pixel.c:
28,29d27
< #include "stdio.h"
< #include "math.h"
45,49c43
<
< //This is a rewritten "SAD" function that actually averages a SAD value and an SSIM value.
< // Much slower but gives about a 1-1.5% SSIM boost!
<
< #define PIXEL_SAD_C( name, lx, ly ) \
---
> #define PIXEL_SAD_C( name, lx, ly ) \
52a47
> int i_sum = 0; \
54,55d48
< int sad_sum=0; \
< uint32_t s1=0, s2=0, ss=0, s12=0; \
60,67c53
< sad_sum += abs(pix1[x] - pix2[x]); \
< int a = pix1[x]; \
< int b = pix2[x]; \
< s1 += a; \
< s2 += b; \
< ss += a*a; \
< ss += b*b; \
< s12 += a*b; \
---
> i_sum += abs( pix1[x] - pix2[x] ); \
72,87c58,59
< x264_emms(); \
< int ssim_c1 = (int)(.01*.01*255*255*lx*ly + .5); \
< int ssim_c2 = (int)(.03*.03*255*255*lx*ly*(lx*(ly-1)) + .5); \
< int vars = ss*ly*lx - s1*s1 - s2*s2; \
< int covar = s12*ly*lx - s1*s2; \
< float result = (float)(2*s1*s2 + ssim_c1) * (float)(2*covar + ssim_c2) \
< / ((float)(s1*s1 + s2*s2 + ssim_c1) * (float)(vars + ssim_c2)); \
< if(0){printf("s1: %d s2: %d ss: %d s12: %d\n",s1,s2,ss,s12); \
< printf("SSIM: %f, ",result); \
< printf("SAD: %d\n",sad_sum); } \
< return (sad_sum + 16000*(1-result))/2; \
< }
<
< //This is a regular SAD function
< /*
< #undef PIXEL_SAD_C
---
> return i_sum; \
> }
89,105d60
< #define PIXEL_SAD_C( name, lx, ly ) \
< static int name( uint8_t *pix1, int i_stride_pix1, \
< uint8_t *pix2, int i_stride_pix2 ) \
< { \
< int x, y; \
< int sad_sum=0; \
< for( y = 0; y < ly; y++ ) \
< { \
< for( x = 0; x < lx; x++ ) \
< { \
< sad_sum += abs(pix1[x] - pix2[x]); \
< } \
< pix1 += i_stride_pix1; \
< pix2 += i_stride_pix2; \
< } \
< return sad_sum; \
< }*/
114a70
>
118,120d73
<
< //This is a regular SSD function
<
140,186d92
< //This is an SSIM-SSD combination like above.
<
< #define PIXEL_SSD_SSIM_C( name, lx, ly ) \
< static int name( uint8_t *pix1, int i_stride_pix1, \
< uint8_t *pix2, int i_stride_pix2 ) \
< { \
< int x, y; \
< int i_sum = 0; \
< uint32_t s1=0, s2=0, ss=0, s12=0; \
< for( y = 0; y < ly; y++ ) \
< { \
< for( x = 0; x < lx; x++ ) \
< { \
< int d = abs(pix1[x] - pix2[x]); \
< i_sum += d*d; \
< int a = pix1[x]; \
< int b = pix2[x]; \
< s1 += a; \
< s2 += b; \
< ss += a*a; \
< ss += b*b; \
< s12 += a*b; \
< } \
< pix1 += i_stride_pix1; \
< pix2 += i_stride_pix2; \
< } \
< x264_emms(); \
< int ssim_c1 = (int)(.01*.01*255*255*lx*ly + .5); \
< int ssim_c2 = (int)(.03*.03*255*255*lx*ly*(lx*(ly-1)) + .5); \
< int vars = ss*ly*lx - s1*s1 - s2*s2; \
< int covar = s12*ly*lx - s1*s2; \
< float result = (float)(2*s1*s2 + ssim_c1) * (float)(2*covar + ssim_c2) \
< / ((float)(s1*s1 + s2*s2 + ssim_c1) * (float)(vars + ssim_c2)); \
< if(0){printf("SSIM: %f, ",result); \
< printf("SSD: %d",i_sum); \
< printf(" Modified SSIM: %f\n",(50000*(1-result)));} \
< return (i_sum + 50000*(1-result))/2; \
< }
<
< PIXEL_SSD_SSIM_C( x264_pixel_ssd_ssim_16x16, 16, 16 )
< PIXEL_SSD_SSIM_C( x264_pixel_ssd_ssim_16x8, 16, 8 )
< PIXEL_SSD_SSIM_C( x264_pixel_ssd_ssim_8x16, 8, 16 )
< PIXEL_SSD_SSIM_C( x264_pixel_ssd_ssim_8x8, 8, 8 )
< PIXEL_SSD_SSIM_C( x264_pixel_ssd_ssim_8x4, 8, 4 )
< PIXEL_SSD_SSIM_C( x264_pixel_ssd_ssim_4x8, 4, 8 )
< PIXEL_SSD_SSIM_C( x264_pixel_ssd_ssim_4x4, 4, 4 )
<
556d461
< INIT( ssd_ssim, );
622c527
< //these are faster on both Intel and AMD
---
> // these are faster on both Intel and AMD

rdo.c:
63,72d62
< static int ssd_ssim_mb( x264_t *h )
< {
< return h->pixf.ssd_ssim[PIXEL_16x16]( h->mb.pic.p_fenc[0], FENC_STRIDE,
< h->mb.pic.p_fdec[0], FDEC_STRIDE )
< + h->pixf.ssd_ssim[PIXEL_8x8]( h->mb.pic.p_fenc[1], FENC_STRIDE,
< h->mb.pic.p_fdec[1], FDEC_STRIDE )
< + h->pixf.ssd_ssim[PIXEL_8x8]( h->mb.pic.p_fenc[2], FENC_STRIDE,
< h->mb.pic.p_fdec[2], FDEC_STRIDE );
< }
<
75c65
< return h->pixf.ssd_ssim[size]( h->mb.pic.p_fenc[p] + x+y*FENC_STRIDE, FENC_STRIDE,
---
> return h->pixf.ssd[size]( h->mb.pic.p_fenc[p] + x+y*FENC_STRIDE, FENC_STRIDE,
87c77
< i_ssd = ssd_ssim_mb( h );
---
> i_ssd = ssd_mb( h );
analyse.c:
2358c2358
< i_bskip_cost = ssd_ssim_mb( h );
---
> i_bskip_cost = ssd_mb( h );

Dark Shikari
29th July 2007, 00:03
OK, final results from the QP test on Rushour.yuv are in.


SAD-Opt:
+1.509% SSIM
-0.002db PSNR

SSD-Opt:
-0.28% SSIM
-0.1db PSNR

SAD-SSD-Opt:
-0.24% SSIM
-0.119db PSNR

It appears the main problem is that SSD-Opt, while it increases quality slightly, screws up RDO, resulting in a large bitrate increase (which kills the relative quality per bitrate).

On the other hand, SAD-Opt gives (quite literally) a completely free SSIM boost along with a tiny bitrate reduction. I should probably compare the results with a SATD-based metric (and perhaps a combination of SAD, SATD, and SSIM!) before finalizing this, but this could make a neat little patch, perhaps a useful extra command-line option (--optimize-ssim).

foxyshadis
29th July 2007, 09:48
Thanks for the patch, here's the list: http://www.videolan.org/developers/lists.html (and archive (http://www.via.ecp.fr/via/ml/x264-devel/))

You weren't kidding about older revision, it had to be around rev 650. It normally wouldn't matter with a unified diff, but this older one doesn't apply cleanly without reverting - though it's not really that complex to do it by hand. diff -u is the standard for that reason (it can also hold all file diffs in a single patch file).

DeathTheSheep
29th July 2007, 21:27
SAD-Opt gives (quite literally) a completely free SSIM boost along with a tiny bitrate reduction.
Not necessarily.

I wouldn't call it a "completely free" SSIM boost at all, since its effects on certain sources at uniform QP (my anime test above, for instance) proved that it both increased filesize by +1.03% and decreased SSIM and PSNR by -0.86% and -0.09dB, respectively. (Remember not to rely on multipass mode for quality comparisons when underlying bit distribution algorithms have been altered.)

But maybe this case is isolated to a few situations; that's what I hope to find out.

Dark Shikari
29th July 2007, 21:28
I wouldn't call it a "completely free" SSIM boost at all, since its effects on certain sources (my anime test above, for instance) proved that it both increased filesize by +1.03% and decreased SSIM and PSNR by -0.86% and -0.09dB, respectively.

But maybe this case is isolated to a few situations; that's what I hope to find out.
I'm not sure whether it depends on the source or the settings.

Have you tried using the exact same settings I used and seeing if the results change? It could be that this option only becomes useful with a certain command-line option or combination thereof.

I would not be surprised if it doesn't work well with anime though, as there were hints to that in earlier tests.

DeathTheSheep
29th July 2007, 22:15
With your commandline:
--trellis 2 --subme 6 --bframes 1 --partitions all --8x8dct --bime --b-rdo --direct auto --progress --no-asmat QP 25, the results were significantly different than what I attained using mine at QP 30. Differences in results included:
- Different filesize distributions (yours produces all filesizes between 1020 and 1029KB, whereas mine were erratic as stated on previous page).
- SSIM quality was improved with your commandline (although PSNR improved with SAD-opt only), but worsened with mine.

Here are the results:
ORIGINAL:
1019KB
SSIM Mean Y:0.9809464
PSNR Mean Y:42.226

SSD:
1026KB
SSIM Mean Y:0.9810021
PSNR Mean Y:42.155

SAD:
1022KB
SSIM Mean Y:0.9812006
PSNR Mean Y:42.242

SAD-SSD:
1029KB
SSIM Mean Y:0.9812578
PSNR Mean Y:42.171

The configuration settings, above all, seem to account for such huge differences in results that I was experiencing. However, the commanline I used previously was truly how I encoded my files, and produced the best metric of all option configurations on non-patched builds. The patch builds lowered the quality. I encoded to baseline profile (no b-frames, trellis, cabac, etc), used 0:1 deblocking for anime, used umh or esa ME, --subme 7, and only analyzed certain baseline partitions, not "all." Needless to say, I was using mobile settings (compatible with iPod, PSP, PocketPC, all Quicktime, etc).

I'm going to check now to see what options in particular harm the quality of the patched builds.

Dark Shikari
29th July 2007, 23:10
With your commandline:
--trellis 2 --subme 6 --bframes 1 --partitions all --8x8dct --bime --b-rdo --direct auto --progress --no-asmat QP 25, the results were significantly different than what I attained using mine at QP 30. Differences in results included:
- Different filesize distributions (yours produces all filesizes between 1020 and 1029KB, whereas mine were erratic as stated on previous page).
- SSIM quality was improved with your commandline (although PSNR improved with SAD-opt only), but worsened with mine.

Here are the results:
ORIGINAL:
1019KB
SSIM Mean Y:0.9809464
PSNR Mean Y:42.226

SSD:
1026KB
SSIM Mean Y:0.9810021
PSNR Mean Y:42.155

SAD:
1022KB
SSIM Mean Y:0.9812006
PSNR Mean Y:42.242

SAD-SSD:
1029KB
SSIM Mean Y:0.9812578
PSNR Mean Y:42.171

The configuration settings, above all, seem to account for such huge differences in results that I was experiencing. However, the commanline I used previously was truly how I encoded my files, and produced the best metric of all option configurations on non-patched builds. The patch builds lowered the quality. I encoded to baseline profile (no b-frames, trellis, cabac, etc), used 0:1 deblocking for anime, used umh or esa ME, --subme 7, and only analyzed certain baseline partitions, not "all." Needless to say, I was using mobile settings (compatible with iPod, PSP, PocketPC, all Quicktime, etc).

I'm going to check now to see what options in particular harm the quality of the patched builds.
The reason the quality (SSIM-wise) is lower with my settings is adaptive quantization, which lowers SSIM but avoids blocking in flat areas.

Remove that for much better (SSIM-wise) results.

What would be interesting would be to find which of the options in my encode that differs from yours is the one that causes the SAD-OPT patch to be more effective.

DeathTheSheep
29th July 2007, 23:12
The reason the quality (SSIM-wise) is lower with my settings is adaptive quantization.
Adaptive quantization? It's a good thing I never used it in the first place. ;)

I used the process of elimination on the sad-opt build until I found the culprit causing the previous ridiculously bad results.
B-frame settings, CABAC, pskip, partitions, subme, 8x8 high profile, etc, nothing was out of the ordinary, all the mod builds had significantly better SSIM and a similar filesize (just a hair bigger or smaller than the original). Until I got to the motion search settings. Yes, the culprit could be here, I thought...

And I've found one: --me esa.

The x264-original filesize with --me esa is the smallest, and its SSIM is highest of all x264-original encodes. SAD-mod produces the biggest filesize of all sad-mod encodes.

This is very odd behavior. Very strange indeed, since x264 usually produces the absolute highest SSIM on these files, especially when combined with high quality partition decision, pskip settings, etc. But this is very strangely not the case with sad-mod.

Very, very strange, especially since I use ESA heavily now that it has been so heavily optimized and gave me the best results on all mobile, low-res encodes.

It's interesting (and pleasing) to note, however, that x264-original's esa encode was slightly worse (SSIM) than x264-sad-mod's umh encode. :D

Dark Shikari
29th July 2007, 23:14
I used the process of elimination on the sad-opt build until I found the culprit causing the previous ridiculously bad results.
B-frame settings, CABAC, pskip, partitions, subme, 8x8 high profile, etc, nothing was out of the ordinary, all the mod builds had significantly better SSIM and a similar filesize (just a hair bigger or smaller than the original). Until I got to the motion search settings. Yes, the culprit could be here, I thought...

And I've found one: --me esa.

The x264-original filesize with --me esa is the smallest, and its SSIM is highest of all x264-original encodes. SAD-mod produces the biggest filesize of all sad-mod encodes.

This is very odd behavior. Very strange indeed, since x264 usually produces the absolute highest SSIM on these files, especially when combined with high quality partition decision, pskip settings, etc. But this is very strangely not the case with sad-mod.

Very, very strange, especially since I use ESA heavily now that it has been so heavily optimized and gave me the best results on all mobile, low-res encodes.

It's interesting (and pleasing) to note, however, that x264-original's esa encode was slightly worse (SSIM) than x264-sad-mod's umh encode. :D
I'll venture to guess the reason: SAD-Opt is not always the best way of picking what block to center a short-range, high-precision search around. I'm guessing that an exhaustive search has a tendency to find blocks that otherwise wouldn't be looked at but just happen to have a decent SSIM, resulting in a bad high-precision search.

Oh the irony! :cool:

To check my theory, try comparing a SAD-Opt ESA and SAD-Opt UMH encode... see which is better! If the UMH is actually better, that would be quite interesting.

DeathTheSheep
29th July 2007, 23:15
It is, much much so. In SSIM and filesize!

It's interesting (and pleasing) to note, however, that x264-original's esa encode was slightly worse (SSIM) than x264-sad-mod's umh encode.

;)


Now I get to tweak --merange settings!!

PS: Could you perchance possibly manage to compile an asm-enabled SAD build? The SSD one works perfectly with asm, producing bit-identical files. :)

Dark Shikari
29th July 2007, 23:21
It is, much much so. In SSIM and filesize!



;)


Now I get to tweak --merange settings!!

PS: Could you perchance possibly manage to compile an asm-enabled SAD build? The SSD one works perfectly with asm, producing bit-identical files. :)
An ASM-enabled SAD-opt build? Well, ASM would be enabled for everything except that SAD function, just as in the SSD one, everything is enabled except for SSD.

Of course since a lot of the ASM is in SAD itself, this won't work that well, but it'll still be better than nothing. Let me try.

Dark Shikari
29th July 2007, 23:37
ASM-enabled SAD-opt build! (http://www.mediafire.com/?4e1dvk3qvg0) :cool:

Tested, output is identical. Note of course that the SAD function has no ASM in this, resulting in considerably lower speed even with ASM enabled.

DeathTheSheep
29th July 2007, 23:38
Another funny thing I found:
When --merange is increased, x264 usually decreases the output filesize slightly, leaving SSIM generally intact.

But SAD-opt actually increases the filesize and the SSIM (presumably the two are correlated, of course). This is due perhaps to the fact that the SAD function finds more data to stick into the motion vector...?

Right now, I'm doing some ESA merange 32 tests to see if sad-opt follows your hypothesis; that is, the filesize should go up further while not significantly benefiting SSIM.

...yay for 2 fps :)

[edit] Aww, I should have waited one more minute and I would've seen your new build... :(
[edit2] Hypothesis was correct: increasing merange with original build decreased filesize, left SSIM pretty much alone; whereas doing the same with SAD-opt increased filesize and left SSIM pretty much alone. ESA is clearly worse now, even in a 4x area!

[edit3]Okay, so I'm back to my original commandline from a day or two ago and want to draw a few conclusions about x264-SAD-opt UMH vs. x264-orig ESA at qp 30.
"C:\b\x264_sad_opt.exe" --qp 30 --no-fast-pskip --no-cabac --subme 7 --analyse p8x8,b8x8,i4x4,p4x4 --me umh --output SAD_umh.264 test.avs --progress(replace the exe name and "--me esa" for other test)

SAD-Opt UMH:
Size: 688KB
SSIM: 0.9662624
PSNR: 38.686
Speed: 15.37 fps

Orig. ESA:
Size: 683KB
SSIM: 0.9658875
PSNR: 38.698
Speed: 37.53 fps

For the mathematically minded, feel free to draw any conclusion you will from this data. I highlighted in green strengths (and in red, weaknesses) of each encode's results.

Of course, the biggest question is, which has the higher quality?! The two encoded files are right here (http://gabe.ej.am/SAD-test.zip). Enjoy.

jigole
1st November 2007, 22:13
The files are not available anymore