View Full Version : Adaptive chroma upsampling
Seed
10th August 2005, 05:10
I acquired some new source footages (only in yuy2) for testing in past few days. Compared to previous footages, these ones are sharper/more detailed, and allow me to see subtle changes clearer.
(Note: by chroma blurring I mean some subtle changes in colour; by chroma smearing/bleeding I mean some obvious artefact where a blotch of colour bleeds/smears to its neighbourhood in high motion parts.)
The source: PAL interlaced top field first, yuy2
assumetff()
converttoyv12(interlaced=true)
converttoyuy2(interlaced=true)
# repeat to and fro yuy2 <--> yv12 conversion 5x
separatefields() # for a better view of chroma smearing on Vdub
RESULT: no significant loss of details, no noticable chroma blurring/smearing on first generation to and fro conversion, but the colour looks noticably "less solid" after 3-4 generations (? chroma blurring)
assumetff()
converttoyv12(interlaced=false)
converttoyuy2(interlaced=false)
# repeat to and fro yuy2 <--> yv12 conversion 5x
separatefields() # for a better view of chroma smearing on Vdub
RESULT: A lot of chroma smearing/bleeding in high motion scenes/parts even on first generation conversion! But the colour are overall "solid" (? no chroma blurring)
And the interesting new finding is
With an intention for exploration, I tried separatefields() first before colour space conversion:
assumetff()
separatefields()
converttoyv12(interlaced=false)
converttoyuy2(interlaced=false)
# repeat to and fro yuy2 <--> yv12 conversion 5x
RESULT: Rock solid colour even after 5 generation conversion - NO chroma smearing/bleeding in high motion scenes/parts, NO noticable luma/chroma changes, NO noticable details loss.
CONCLUSION: Until neuron2 or others sort out what is wrong in the algorithm of converttoyuy2/yv12(), I would use separatefields() first before colour space conversion, then weave() it back.
Guest
10th August 2005, 05:19
The use of vague and nonstandard terms like "less solid" and "rock solid" coupled with no screenshots makes it very difficult to digest your test results.
Wilbert
10th August 2005, 09:45
@neuron2,
I will look at your post later. I just was wondering which avs version you use. Somewhere in the avs v2.56 line the weights in 'YV12 (interlaced) -> YUY2' are corrected (3/4 and 1/4 were switched). Strange ... I can't find it in the cvs, perhaps it was already corrected in v2.55 :confused:
IanB
10th August 2005, 10:34
[ edit ]@Wilbert,
Revision 1.2 - Mon Jul 5 12:35:06 2004 UTC (13 months ago) by sh0dan
@All, [ /edit ]
The interlaced algorithm used is not absolutely correct. It uses alternating 1/3 - 3/1 weighting instead of the the more complicated 1/7 - 3/5 weighting strict mathematics would dictate. Sh0dan has previously discussed this claiming the error is to slight to notice. Also the top and bottom 4 lines are simply copied without interpolation.
@Neuron2,
While you are at it, it might be instructive to do a test using the resizer and UtoY()...
V=VtoY()
U=UtoY()
U=U.???Resize(Width(U), Height(U)/2)
U=U.???Resize(Width(U), Height(U)*2)
YtoUV(U, V, Last)
...And also with the appropriate SeparateFields() and Weave() inserted for simulating interlaced. Probably Bilinear and Lanczos4 would be good to try.
IanB
Guest
10th August 2005, 13:14
3/4 and 1/4 were switched Yes, that's the conclusion I had reached when I said above that if the way documented at www.avisynth.org is the way it is done, it's no surprise that we have problems. I use Avisynth 2.5.5.
@IanB
So is that revision in 2.5.5?
@Wilbert
Can we update www.avisynth.org with the correct information for 2.5.5 as soon as possible?
Wilbert
10th August 2005, 13:23
Do you mean this page?
http://www.avisynth.org/Sampling
Which conversion on that page are you referring to?
@IanB,
[ edit ]@Wilbert,
Revision 1.2 - Mon Jul 5 12:35:06 2004 UTC (13 months ago) by sh0dan
I noticed that one. That's fixed in avs v2.55 stable (the version neuron2 uses). But i guess that's the one i was referring to.
the the more complicated 1/7 - 3/5 weighting strict mathematics would dictate.
You mean 1/8 - 5/8 right?
Guest
10th August 2005, 13:27
Do you mean this page?
http://www.avisynth.org/Sampling
Which conversion on that page are you referring to? Yes, that page. I'm referring to all of them. They should accurately portray what is really done in the code. Is it your position that they are all accurate right now for 2.5.5? E.g., is the sh0dan swap fix included?
IanB
10th August 2005, 13:27
@Neuron2,
Yes the fix seems to have made it into 2.5.5 by the skin of it's teeth.Revision 1.2 - Mon Jul 5 12:35:06 2004 UTC (13 months ago) by sh0dan
AviSynth 2.5.5 2004-08-31 15:00
@Wilbert,
Yes I mean 1/8 to 7/8 and 3/8 to 5/8 (also 1/4 to 3/4)
IanB
Wilbert
10th August 2005, 13:35
Yes, that page. I'm referring to all of them. They should accurately portray what is really done in the code. Is it your position that they are all accurate right now for 2.5.5? E.g., is the sh0dan swap fix included?
IanB glanced over that page before it was ready. He said it is accurate (ie that's how it's done in the code). If not, i'm sure he will tell it in this thread :)
IanB
10th August 2005, 14:37
It's pretty much as I remember the code (man uncommented code sucks) except for the boundary conditions. Interlaced bash copies 4 lines top and bottom, progressive 2 lines. (which I think is a bug or at the least unpleasant)
scharfis_brain
10th August 2005, 17:02
@all: never do an interlaced YV12-related chroma conversion with separated fields!
this happens, if one does a separatefields().converttoyv12().weave()
http://home.arcor.de/scharfis_brain/samples/interlacedYV12.gif
an this, if one does a simple converttoyv12(interlaced=true)
Even Field....................................Odd Field..................................Weaved
http://www.mir.com/DMG/css-420-mpeg2-top.pnghttp://www.mir.com/DMG/css-420-mpeg2-bot.pnghttp://www.mir.com/DMG/css-420-mpeg2-prog.png
Non-handdrawn Images taken from http://www.mir.com/DMG/chroma.html
LigH
10th August 2005, 21:53
I hope I understood scharfis_brain's drawing well; after a short ICQ session, I'd like to present my versions (based on a few pictures I made for an - unfortunately abandoned - video guide):
Direct:
Even http://www.ligh.de/pics/YV12Even.png - Odd http://www.ligh.de/pics/YV12Odd.png - Weaved http://www.ligh.de/pics/YV12Weaved.png
Separated:
Even http://www.ligh.de/pics/YV12Even_S.png - Odd http://www.ligh.de/pics/YV12Odd_S.png - Weaved http://www.ligh.de/pics/YV12Weaved_S.png
Wilbert
10th August 2005, 22:31
Yup, you understand it very well.
If you prefer math:
YUY2 frame:
Y1t ...
C1t ...
Y2b ...
C2b ...
Y3t ...
C3t ...
Y4b ...
C4b ...
ConvertToYV12(interlaced=true):
Y1t ...
3/4*C1t+1/4*C3t ...
Y2b ...
x
Y3t ...
1/4*C2b+3/4*C4b ...
Y4b ...
x
SeparateFields.ConvertToYV12.Weave:
Y1t ...
1/2*C1t+1/2*C3t ...
Y2b ...
x
Y3t ...
1/2*C2b+1/2*C4b ...
Y4b ...
x
Guest
10th August 2005, 22:56
I prefer math. :)
Guest
10th August 2005, 22:58
"pretty much" That's not quite the level of rigor I had in mind. But never mind, I'll prove it one way or the other.
Wilbert
10th August 2005, 23:36
Hmm strange. If i put my mathmode on i get the following:
YUY2 frame:
Y1t ...
C1t ...
Y2b ...
C2b ...
Y3t ...
C3t ...
Y4b ...
C4b ...
Y5t ...
C5t ...
Y6b ...
C6b ...
Y7t ...
C7t ...
Y8b ...
C8b ...
ConvertToYV12(interlaced=true):
Y1t ...
3/4*C1t+1/4*C3t ...
Y2b ...
x
Y3t ...
1/4*C2b+3/4*C4b ...
Y4b ...
x
Y5t ...
3/4*C5t+1/4*C7t ...
Y6b ...
x
Y7t ...
1/4*C6b+3/4*C8b ...
Y8b ...
x
ConvertToYV12(interlaced=true).ConvertToYUY2():
Y1t ...
3/4*(3/4*C1t+1/4*C3t) + 1/4*(1/4*C2b+3/4*C4b) ...
Y2b ...
1/4*(3/4*C1t+1/4*C3t) + 3/4*(1/4*C2b+3/4*C4b) ...
Y3t ...
3/4*(1/4*C2b+3/4*C4b) + 1/4*(3/4*C5t+1/4*C7t) ...
Y4b ...
1/4*(1/4*C2b+3/4*C4b) + 3/4*(3/4*C5t+1/4*C7t) ...
Y5t ...
3/4*(3/4*C5t+1/4*C7t) + 1/4*(1/4*C6b+3/4*C8b) ...
Y6b ...
1/4*(3/4*C5t+1/4*C7t) + 3/4*(1/4*C6b+3/4*C8b) ...
Y7t ...
...
Y8b ...
...
which equals
Y1t ...
9/16*C1t + 1/16*C2b + 3/16*C3t + 3/16*C4b ...
Y2b ...
3/16*C1t + 3/16*C2b + 1/16*C3t + 9/16*C4b ...
Y3t ...
3/16*C2b + 9/16*C4b + 3/16*C5t + 1/16*C7t ...
Y4b ...
1/16*C2b + 3/16*C4b + 9/16*C5t + 3/16*C7t ...
Y5t ...
9/16*C5t + 1/16*C6b + 3/16*C7t + 3/16*C8b ...
Y6b ...
3/16*C5t + 3/16*C6b + 1/16*C7t + 9/16*C8b ...
Y7t ...
...
Y8b ...
...
So, indeed the chroma shifts down. If you start with
U1 = 40
U2 = 41
U3 = 42
U4 = 43
U5 = 44
U6 = 45
and run it through the conversion you will get
U1 = 41
U2 = 42
U3 = 43
U4 = 44
U5 = 45
U6 = 46
Although it's also shifted downwards, it's not exactly the same as what you get :confused:
edit: corrected values
edit2: the chroma in ConvertToYV12(interlaced=true).ConvertToYUY2() should be shifted down one line! So the conversion won't change the U values: http://forum.doom9.org/showthread.php?p=697622#post697622
Guest
11th August 2005, 04:25
Although it's also shifted downwards, it's not exactly the same as what you get :confused: That's my point. Something doesn't add up. If you like I can make available my test materials.
mg262
11th August 2005, 05:24
@Wilbert
For safety's sake rather than writing
3/4*(3/4*C1t+1/4*C3t) + 1/4*(1/4*C2b+3/4*C4b) ...
=9/16*C1t + 1/16*C2b + 3/16*C3t + 3/16*C4b ...
perhaps take 3/4*C1t+1/4*C3t and 1/4*C2b+3/4*C4b, round them, then plug in to the expression? Haven't been following this discussion properly so not sure if this matters... will check when i wake up 'for real' in a few hours :)
IanB
11th August 2005, 08:30
"pretty much"That's not quite the level of rigor I had in mind. But never mind, I'll prove it one way or the other.Okay, I suspect the code is not exactly as the megre comments indicate. Hence the reticence. When I have time I'll try to fully understand it. :mad:
Also note the confession about using 1/4::3/4 instead of 1/8::7/8 alternating with 3/8::5/8. At first glance I would just expect effectively a 1/8th pixel displacement error, but looking at Wilbert maths I think the displacement error is more. :(
Anyhow I think scharfis_brain's point is well proven, field level motion detect/comp for upscaling chroma is advantageous. :D
Lotsa serious thinking going on here :sly:
IanB
tritical
11th August 2005, 09:09
Hm, this: sample.zip (http://bengal.missouri.edu/~kes25c/sample.zip) might be useful for testing/comparisons. I made it back when the following thread: http://forum.doom9.org/showthread.php?t=79028 was taking place. It does yuy<->yv12 conversions in the exact way mentioned on the page at avisynth.org (using plain c code). syntax for the functions:
convToYUY2(bool interlaced, bool exact)
convToYV12(bool interlaced)
exact is only used when interlaced=true, exact=true means 1/8,7/8 3/8,5/8 averaging is used versus exact=false uses 1/4,3/4. The yuy2->yv12 conversions from avisynth's internal routines match the above conversions except for rounding in the interlaced routine, but the yv12->yuy2 routines have differences that are bigger than rounding errors. I was just testing against the conversions in dgdecode and found some bugs in dgdecode's 420->422 routines, but after correcting those dgdecode's 420->422 routines exactly match the output from sample.dll. The differences in avisynth's conversions definitely need investigating.
Wilbert
11th August 2005, 09:23
perhaps take 3/4*C1t+1/4*C3t and 1/4*C2b+3/4*C4b, round them, then plug in to the expression? Haven't been following this discussion properly so not sure if this matters...
Yeah, i forgot about that. It was a bit late last night :)
@neuron2, could you redo those checks (the logs you posted) using the conversions of tritical above (with exact=true)? (I will also calculate them, but that will have to wait till tonight.
Guest
11th August 2005, 14:39
@neuron2, could you redo those checks (the logs you posted) using the conversions of tritical above (with exact=true)? (I will also calculate them, but that will have to wait till tonight. Sure.
I agree that scharfis_brain's main point is proven *for the existing core conversions*. Whether that will hold up after the interlaced upsampling anomaly is corrected is still an open question, at least in my mind.
Guest
11th August 2005, 14:43
I made it back when the following thread: http://forum.doom9.org/showthread.php?t=79028 was taking place. Ooh, I missed that thread (on one of my sabbatticals). Thanks for linking it!
scharfis_brain
11th August 2005, 14:57
Please note, that I was totally wrong with my first points in this thread!
MfA took his time to convince me about the real thing.
But this thread discovered the interlaced=true-bug...
because I was relying on AVS's sampling...
Wilbert
11th August 2005, 18:19
Also note the confession about using 1/4::3/4 instead of 1/8::7/8 alternating with 3/8::5/8. At first glance I would just expect effectively a 1/8th pixel displacement error, but looking at Wilbert maths I think the displacement error is more.
I was thinking .... Those */8 weights shouldn't be used in ConvertToYV12(interlaced=true).ConvertToYUY2() right? I think they only used in ConvertToYUY2(interlaced=true). So that means that using the */4 weigts are not the cause of the chroma being shifted down one line.
mg262 corrections (proper rounding between the two filters):
ConvertToYV12(interlaced=true).ConvertToYUY2():
Y1t ...
3/4*round(3/4*C1t+1/4*C3t) + 1/4*round(1/4*C2b+3/4*C4b) ...
Y2b ...
1/4*round(3/4*C1t+1/4*C3t) + 3/4*round(1/4*C2b+3/4*C4b) ...
Y3t ...
3/4*round(1/4*C2b+3/4*C4b) + 1/4*round(3/4*C5t+1/4*C7t) ...
Y4b ...
1/4*round(1/4*C2b+3/4*C4b) + 3/4*round(3/4*C5t+1/4*C7t) ...
Y5t ...
3/4*round(3/4*C5t+1/4*C7t) + 1/4*round(1/4*C6b+3/4*C8b) ...
Y6b ...
1/4*round(3/4*C5t+1/4*C7t) + 3/4*round(1/4*C6b+3/4*C8b) ...
Y7t ...
...
Y8b ...
...
results in:
If you start with
U1 = 40
U2 = 41
U3 = 42
U4 = 43
U5 = 44
U6 = 45
and run it through the conversion you will get
U1 = 41.5 = 42
U2 = 42.5 = 43
U3 = 43.5 = 44
U4 = 44.5 = 45
U5 = 45.5 = 46
U6 = 46.5 = 47
Hmm, even a bigger shift, and not the same as neuron2 results. Am i doing the calculations wrong?
@neuron2,
That's my point. Something doesn't add up. If you like I can make available my test materials.
Yes that would be nice :)
tritical
11th August 2005, 18:28
I did some more testing and discovered the following. After adjusting the conversions for rounding (avisynth's round .5 down in the x/4 cases)...
converttoyv12(interlaced=true/false) both match exactly.
converttoyuy2(interlaced=true/false) (1/4,3/4 sampling for interlaced=true) match exactly except for the few lines at the top/bottom borders as IanB pointed out before.
The x/4 sampling instead of the x/8 sampling for converttoyuy2(interlaced=true) is what causes the 50% loss in resolution on the ramp for the pii/ppi cases. The one line shift upwards or downwards on the ramp is a result of the combined rounding for the two separate conversions... if both round upwards at .5 or both round downwards at .5 you get the shift, if one rounds up and one rounds down it doesn't happen.
currently with avisynth's isse routines:
converttoyuy2(interlaced=false) rounds down
converttoyuy2(interalced=true) rounds down
converttoyv12(interlaced=false) rounds up
converttoyv12(interlaced=true) rounds down
So ppp wont have a one line shift, while pip would shift one line down as demonstrated by neuron2's results.
Wilbert
11th August 2005, 19:04
The one line shift upwards or downwards on the ramp is a result of the combined rounding for the two separate conversions...
Sorry, i don't believe this. If you look at my corrected post on page 3, you will see that the chroma is shifted down one line (i didn't do any rounding). Of course my calculations can be wrong :)
tritical
11th August 2005, 19:37
Wilbert, your calculations are shifted themselves which is why you get shifted output. It should be:
ConvertToYV12(interlaced=true):
Y1t ...
3/4*C1t+1/4*C3t ...
Y2b ...
x
Y3t ...
1/4*C2b+3/4*C4b ...
Y4b ...
x
Y5t ...
3/4*C5t+1/4*C7t ...
Y6b ...
x
Y7t ...
1/4*C6b+3/4*C8b ...
Y8b ...
x
ConvertToYV12(interlaced=true).ConvertToYUY2():
Y1t ...
3/4*C1t+1/4*C3t ...
Y2b ...
3/4*(3/4*C1t+1/4*C3t) + 1/4*(1/4*C2b+3/4*C4b) ...
Y3t ...
3/4*(1/4*C2b+3/4*C4b) + 1/4*(3/4*C1t+1/4*C3t) ...
Y4b ...
3/4*(1/4*C2b+3/4*C4b) + 1/4*(3/4*C5t+1/4*C7t) ...
Y5t ...
3/4*(3/4*C5t+1/4*C7t) + 1/4*(1/4*C2b+3/4*C4b) ...
Y6b ...
3/4*(3/4*C5t+1/4*C7t) + 1/4*(1/4*C6b+3/4*C8b) ...
Y7t ...
3/4*(1/4*C6b+3/4*C8b) + 1/4*(3/4*C5t+1/4*C7t) ...
Y8b ...
1/4*C6b+3/4*C8b
which equals
Y1t ...
12/16*C1t+ 4/16*C3t ...
Y2b ...
9/16*C1t + 1/16*C2b + 3/16*C3t + 3/16*C4b ...
Y3t ...
3/16*C1t + 3/16*C2b + 1/16*C3t + 9/16*C4b ...
Y4b ...
3/16*C2b + 9/16*C4b + 3/16*C5t + 1/16*C7t ...
Y5t ...
1/16*C2b + 3/16*C4b + 9/16*C5t + 3/16*C7t ...
Y6b ...
9/16*C5t + 1/16*C6b + 3/16*C7t + 3/16*C8b ...
Y7t ...
3/16*C5t + 3/16*C6b + 1/16*C7t + 9/16*C8b ...
Y8b ...
4/16*C6b + 12/16*C8b
If you start with
U1 = 40
U2 = 41
U3 = 42
U4 = 43
U5 = 44
U6 = 45
U7 = 46
U8 = 47
and run it through the conversion you will get
U1 = 40.5
U2 = 41
U3 = 42
U4 = 43
U5 = 44
U6 = 45
U7 = 46
U8 = 46.5
if you do upwards rounding on both intermediate steps you get:
U1 = 41
U2 = 42
U3 = 43
U4 = 44
U5 = 45
U6 = 46
U7 = 47
U8 = 47
if you do downwards rounding on both intermediate steps you get:
U1 = 40
U2 = 40
U3 = 41
U4 = 42
U5 = 43
U6 = 44
U7 = 45
U8 = 46
if you do upwards and then downwards you get:
U1 = 41
U2 = 41
U3 = 42
U4 = 43
U5 = 44
U6 = 45
U7 = 46
U8 = 47
if you do downwards and then upwards you get:
U1 = 40
U2 = 41
U3 = 42
U4 = 43
U5 = 44
U6 = 45
U7 = 46
U8 = 46
Wilbert
11th August 2005, 20:17
What stupid of me! Thanks for the correction. So if IanB corrects the rounding everything should be ok.
tritical
11th August 2005, 23:25
I guess the next question would be what is "correct" for rounding. If the goal is to preserve a ramp across multiple converttoyv12() converttoyuy2() pairings then both yv12 conversions need to round .5 in one direction and both yuy2 conversions need to round .5 in the opposite direction. However, if you are only doing one conversion at a time then you might as well round them all the same way.
The other issues are the top/bottom border handling in converttoyuy2(interlaced=true/false) and the x/4 vs x/8 sampling in converttoyuy2(interlaced=true). Neither of those issues is probably gonna be noticeable to a viewer, but the testing with the ramp values does show how it effects things... especially the x/4 vs x/8 sampling.
mg262
12th August 2005, 11:13
If you were to want all the rounding to be consistent (i.e. go in the same direction), then there's always the possibility of more ConvertBackToxxxx functions. (Although personally I think this is unnecessarily complicated for this case.)
As a separate point, I think it may be worth looking at where/why yuy2 is used at all (except for input from capture devices, which only necessitates one conversion); in particular, where the colour space conversion to yuy2 is only used to run temporal filters, then using point sampling instead of the built-in functions may be more appropriate... I have no idea whether this happens often, but then the only yuy2 filters I use regularly are the dust ones.
IanB
15th August 2005, 08:43
Dear all,
Rounding int(f+0.5) or (I + (Q>>1)) / Q
Flooring int(f) or I / Q
Ceiling int(f+1.0-epsilon) or (I + (Q-1)) / QNow please choose and use the correct term.
@mg262,
Somebody else recently suggested a point resampling mode, thanks for the reminder.
IanB
tritical
15th August 2005, 09:33
Here is a crash course in tritical's highly-incorrect and unscientific terminology to help aid the translation:
Round .5 up (round up) = int(f+0.5) // conventional rounding - introduces upward bias
Round .5 down (round down) = int (f+0.5-epsilon) // introduces downward bias
Floor = int(f)
Ceiling = int(f+1.0-epsilon)
Wilbert
15th August 2005, 10:11
Round .5 up (round up) = int(f+0.5)
I vote for this one.
LigH
15th August 2005, 12:00
I slightly remember the days when coders used to set the 8087 control word to change the behaviour of higher level language's "Round()" functions... :D
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.