Welcome to Doom9's Forum, THE in-place to be for everyone interested in DVD conversion. Before you start posting please read the forum rules. By posting to this forum you agree to abide by the rules. |
8th June 2009, 22:08 | #1 | Link |
Avisynth language lover
Join Date: Dec 2007
Location: Spain
Posts: 3,431
|
Chroma shift in interlaced YV12 to YUY2 conversion
The wiki page on Sampling describes Avisynth's method of converting interlaced YV12 to YUY2, and states that it differs from the MPEG2 specs. It also speculates that this may be for performance reasons - can anyone confirm this?
Whatever the reason for its adoption, the method used has the undesirable effect of introducing a vertical chroma shift. Moreover, the shift is in different directions for top and bottom fields, making the effect more visible. Top fields have chroma shifted down 1/2 pixel and bottom fields up 1/2 pixel. I couldn't find any mention of this problem in the archives (though I did find a reference to a similar problem where conversion of RGB to YUY2 introduces a horizontal chroma shift). The shift can be demonstrated in this test script, which progressively repeats conversion of YV12 to YUY2 and back again. Even after only a single conversion, the effect is clearly visible. Code:
function Convert(clip c, int times) { times == 0 ? c : c.ConvertToYUY2(interlaced=true).ConvertToYV12(interlaced=true).Convert(times-1) } ColorBars(pixel_type="YV12").TurnRight() Animate(0, 10, "Convert", 0, 10) ConvertToYUY2(interlaced=true).ConvertToYV12(interlaced=true) to be almost a no-op, just a slight chroma blur. Using the MPEG2 interpolation coefficients, it would have a kernel of [3 26 3]/32, which fits the bill. Interestingly, IanB states here that the Avisynth implementation also gives this kernel, but by my calculations it actually has a kernel of [3 12 1]/16 for top fields and [1 12 3]/16 for bottom fields. Both kernels are asymmetric, resulting in shifts (each in a different direction), which is exactly what we see. |
9th June 2009, 15:02 | #2 | Link | |
Registered User
Join Date: Jan 2002
Location: France
Posts: 2,856
|
Quote:
__________________
|
|
9th June 2009, 17:07 | #3 | Link | |
Avisynth language lover
Join Date: Dec 2007
Location: Spain
Posts: 3,431
|
Quote:
The case I am talking about is YV12 to YUY2, so it is your yv12toyuy2.h that is relevant here. It says the kernels for YV12 interlaced are (7, 1) and (5, 3) for top fields and (5, 3) and (7, 1) for bottom fields. This agrees with the MPEG2 specs, whereas Avisynth uses (3, 1) and (1, 3), which produces a shift. |
|
9th June 2009, 18:20 | #4 | Link |
Registered User
Join Date: Dec 2003
Location: MO, US
Posts: 999
|
You are right that Avisynth's alternating 0.25/0.75, 0.75/0.25 approximation, which I think is used because it can be done quickly with pavgb, instead of 1/8,7/8 3/8,5/8 5/8,3/8 7/8,1/8 in the interlaced conversion will shift chroma after chaining yv12<->yuy2 interlaced conversions together. The fact that it is off has been mentioned before. I think nobody ever mentioned the shifting problem because chaining interlaced conversions together doesn't happen very often. It would definitely be nice if the interlaced=true/false conversions agreed on positioning. It would also be nice if the rgb<->yuy2 conversions agreed. With Avisynth being open source it just comes down to if someone wants to put in the work to change it in the code, and whether the speed hit is worth it.
To illustrate the point, here is your colorbars example after 20 conversions using avisynth's conversions on the left, and correct placement on the right. This is after a utoy() call at the end. (I added the original in the middle) Last edited by tritical; 9th June 2009 at 18:29. |
9th June 2009, 18:49 | #5 | Link | |
Registered User
Join Date: Jan 2002
Location: France
Posts: 2,856
|
Quote:
__________________
|
|
9th June 2009, 23:49 | #6 | Link |
Avisynth language lover
Join Date: Dec 2007
Location: Spain
Posts: 3,431
|
Manao,
This is distinct from the original topic, but it's interesting that your code for YUY2 to YV12 conversion uses a four-pixel kernel, ie [1 3 3 1] for progressive and [3 7 5 1] or [1 5 7 3] for interlaced, while Avisynth uses only two pixels, [1 1] for progressive and [3 1] or [1 3] for interlaced. Your version, though slower, is theoretically better for a 2x downsampling without aliasing, but in practice is there a visible difference in quality? |
12th June 2009, 09:42 | #7 | Link | |
Registered User
Join Date: Jan 2002
Location: France
Posts: 2,856
|
Quote:
__________________
|
|
12th June 2010, 16:34 | #8 | Link |
Registered User
Join Date: Feb 2004
Posts: 74
|
Reading the above, it seems that Manao's attached files offer a more accurate level of conversion for interlaced YV12 to YUY2 than the built in avisynth conversion.
Forgive my ignorance, but can someone explain to me how I get my avisynth install to use the files? |
12th January 2011, 22:10 | #10 | Link |
Registered User
Join Date: Jan 2011
Posts: 121
|
I'm sorry to raise this thread from the dead for a second time, but if I started a new thread I'd just be copy/pasting from here. I came looking for this thread because I noticed that Avisynth isn't converting my own interlaced YUY2 sources to YV12 correctly...I think.*
I'm a nobody here, but IMO this is an obstacle for people with interlaced YUY2 sources, and that includes myself. Even done as well as it can be, interlaced YV12 is a pretty screwy format. However, most Avisynth scripts only work in YV12...including the best deinterlacer (TGMC/QTGMC), ironically enough. People with interlaced YUY2 sources can't deinterlace before YV12 conversion without sub-optimal chroma deinterlacing (like using Yadif for chroma), and we can't convert to YV12 before deinterlacing (or running any other filter that affects chroma) without sub-optimal colorspace conversion. On top of that, anyone with an interlaced YUY2 avi file probably has it in a large and/or lossless format (e.g. Huffyuv) that needs to be trimmed down...but high-quality encoders like x264 only take YV12 and won't encode 4:2:2 material. People with interlaced YUY2 files cannot deinterlace optimally, cannot convert to interlaced YV12 optimally, and cannot directly compress their YUY2 source and wait out a solution to the other problems. It's currently a lose/lose/lose situation that I'd like to call "YV12 hell." (A pox on the house of whoever invented interlaced YV12! I used to blame the inventor of interlacing, but the invention of TGMC now makes me reconsider interlaced content as the larval stage of super-duper-awesome double framerate content. ) WorBry asked in June if this is being addressed in Avisynth 2.6. Does anyone know the answer? In any case, Avisynth 2.6 has been in the works for a long time, and alpha 2 has been out for over a year, so I'm not sure when it will actually come to fruition. In the meantime, does anyone know of a workaround? Do Manao's routines mentioned above fix it? Without confirmation I'm assuming they don't, since they were written before Gavino and tritical brought this particular issue to Manao's attention, but it's worth asking. If not, is there any software or existing filter/plugin which can do a proper conversion to YV12? If there's no existing solution, what would it take for someone with no experience to create one? The .h file Manao mentioned includes a description of several interlaced YV12 chroma placements, but there's no real mention of which is optimal for display and for further manipulation by other filters. This seems pretty hairy, because a lot of filters - especially deinterlacing - are probably greatly affected by their assumptions about chroma placement, so the "best" kernel to use may be a tossup. Regardless, the one being used now seems pretty broken. *EDIT: Actually, I'm not really sure about what artifacts I'm getting under which conditions. There are four ways to convert to YV12, given the four combinations of interlaced/progressive conversion with woven/separated fields. I can then compare them in YV12, or I can convert back to YUY2 - again, four different ways - and compare them against each other and the original, both with separated fields and woven frames. On top of that, it seems the chroma placement varies a bit with the arrangement of the clip in Avisynth stacking (which I found out on accident by subtracting the original clip from the same conversion twice and stacking the same subtraction on top of itself). Anyway, that's a lot of possibilities to test. I had been using really screwed up frames with tons of combing from the beginning of a VHS capture to better compare the artifacts too, but that may not have been the best idea. Last edited by Mini-Me; 13th January 2011 at 06:59. |
13th January 2011, 12:28 | #11 | Link | |||
Avisynth language lover
Join Date: Dec 2007
Location: Spain
Posts: 3,431
|
Quote:
Quote:
Quote:
Last edited by Gavino; 13th January 2011 at 14:59. Reason: chromaresample="bilinear", not "BilinearResize" |
|||
13th January 2011, 21:14 | #12 | Link | |||
Registered User
Join Date: Jan 2011
Posts: 121
|
Quote:
Quote:
If you want to see what I'm talking about, here's an interesting comparison: Quote:
The image with brokenyv12 is broken, as expected. It seems that alternating lines from the same field end up getting different color depending on combing characteristics. However, interestingyuy2 is...interesting. Find a heavily combed clip where the progressive conversion causes noticeable blending of colors. In scenes where the progressive conversion shows blending, the "interesting" version looks far closer to the original (almost indistinguishable) than any other YUY2->YV12->YUY2 conversion I've tried...and I think I tried all 16. It retains all of the color of the original fields in certain heavily combed areas, and it has no especially obvious artifacts. There are two downsides to the "interesting" version though: First, it's totally pointless and academic, since the goal is to convert to YV12, not to convert to YV12 and back to YUY2...and the YV12 clip looks terrible and broken before converting back to YUY2 in the exact way given. Second, while it's usually imperceptible to the naked eye with a single conversion, this method does seem to smear the color within each field (and it aliases somewhat on your test pattern). If you subtract the conversions from the original, the interesting version will show much larger differences in most areas, though the difference image will not be combed. Anyway, I'll either try the 2.6 alpha or try to make a plugin out of Manao's routines. Hopefully the fixed interlaced conversion will be the best of both worlds. Last edited by Mini-Me; 13th January 2011 at 22:07. |
|||
13th January 2011, 21:44 | #13 | Link |
Avisynth language lover
Join Date: Dec 2007
Location: Spain
Posts: 3,431
|
I've just tried my test program (from the first post) with Avisynth 2.60 Alpha 2. The results appear identical to 2.58 (as I expected).
I then tried again setting chromaresample="bilinear" in both Convert calls. The result was arguably worse - the shift is greater, although the effect is different, as both fields are now shifted in the same direction (upwards). Also, using chromaresample="bilinear" even with interlaced=false also produces a shift, this time about half as big as with interlaced=true. IanB, any idea what is going on here? |
15th January 2011, 05:09 | #14 | Link |
Avisynth language lover
Join Date: Dec 2007
Location: Spain
Posts: 3,431
|
ConvertToPlanarGeneric broken?
I think the 2.60 code in class ConvertToPlanarGeneric is setting the wrong offsets for chroma resampling, when converting in either direction between YV16 and YV12 (which affects YUY2<->YV12 when chromaresample is set).
The code does not seem to take into account that: (1) the resizers maintain the image centre, so an offset of zero should be used in non-interlaced YV12<->YUY2 conversions; (2) different offsets are needed for top and bottom fields when interlaced=true. I believe the required offsets and those actually produced by the code are: Code:
From YV12 Progressive Top fields Bottom fields Required 0 0.125 -0.125 Actual 0 0 0 To YV12 Required 0 -0.25 0.25 Actual 0.5 0.5 0.5 Whaddaya think, Ian? |
15th January 2011, 21:52 | #15 | Link |
Avisynth Developer
Join Date: Jan 2003
Location: Melbourne, Australia
Posts: 3,167
|
Yes that ConvertToPlanarGeneric alignment code is a bit (lot) of a mess. It's all related to problems with ConvertTo...(chromaplacement=""...") which is currently just plain wrong. It's "defined" in multiple place and sometimes confusingly gets overridden later.
|
15th January 2011, 22:39 | #16 | Link | |
Avisynth language lover
Join Date: Dec 2007
Location: Spain
Posts: 3,431
|
Quote:
As well as the points I already made, - the 3rd and 4th offsets are wrongly set as they should always be just width and height respectively. - need to distinguish when resizer is "Point", since there is no centre correction in this case. Here's a workaround for the original 2.58 problems. It should be functionally equivalent to Manao's code, I think, eliminating the chroma shift on interlaced YV12->YUY2 and the chroma aliasing on YUY2->YV12. Code:
# Replacement code for YUY2<->YV12 conversions in v2.58 function YV12ToYUY2(clip c, bool "interlaced") { interlaced = Default(interlaced, false) interlaced ? c.YV12ToYUY2i() : c.YV12ToYUY2p() } function YUY2ToYV12(clip c, bool "interlaced") { interlaced = Default(interlaced, false) interlaced ? c.YUY2ToYV12i() : c.YUY2ToYV12p() } # ------- Support functions -------- function YV12ToYUY2p(clip c) { U = c.UToY().BilinearResize(c.width/2, c.height).ConvertToYUY2() V = c.VToY().BilinearResize(c.width/2, c.height).ConvertToYUY2() YToUV(U, V, c.ConvertToYUY2()) #crashes on v2.60 } function YV12ToYUY2i(clip c) { f = c.AssumeTFF().SeparateFields() top = f.SelectEven() bot = f.SelectOdd() topU = top.UToY().BilinearResize(c.width/2, c.height/2, 0, 0.125) botU = bot.UToY().BilinearResize(c.width/2, c.height/2, 0, -0.125) U = Interleave(topU, botU).ConvertToYUY2() topV = top.VToY().BilinearResize(c.width/2, c.height/2, 0, 0.125) botV = bot.VToY().BilinearResize(c.width/2, c.height/2, 0, -0.125) V = Interleave(topV, botV).ConvertToYUY2() YToUV(U, V, f.ConvertToYUY2()).Weave() #crashes on v2.60 } function YUY2ToYV12p(clip c) { U = c.UToY().BilinearResize(c.width/2, c.height/2).ConvertToYV12() V = c.VToY().BilinearResize(c.width/2, c.height/2).ConvertToYV12() YToUV(U, V, c.ConvertToYV12()) } function YUY2ToYV12i(clip c) { f = c.AssumeTFF().SeparateFields() top = f.SelectEven() bot = f.SelectOdd() topU = top.UToY().BilinearResize(c.width/2, c.height/4, 0, -0.25) botU = bot.UToY().BilinearResize(c.width/2, c.height/4, 0, 0.25) U = Interleave(topU, botU).ConvertToYV12() topV = top.VToY().BilinearResize(c.width/2, c.height/4, 0, -0.25) botV = bot.VToY().BilinearResize(c.width/2, c.height/4, 0, 0.25) V = Interleave(topV, botV).ConvertToYV12() YToUV(U, V, f.ConvertToYV12()).Weave() } In any case, the new formats available in 2.6 make this more efficient version possible: Code:
# Replacement code for YUY2<->YV12 conversions in v2.60 function YV12ToYUY2_26(clip c, bool "interlaced") { interlaced = Default(interlaced, false) interlaced ? c.YV12ToYUY2i_26() : c.YV12ToYUY2p_26() } function YUY2ToYV12_26(clip c, bool "interlaced") { interlaced = Default(interlaced, false) interlaced ? c.YUY2ToYV12i_26() : c.YUY2ToYV12p_26() } # ------- Support functions -------- function YV12ToYUY2p_26(clip c) { U = c.UToY8().BilinearResize(c.width/2, c.height).ConvertToYV16() V = c.VToY8().BilinearResize(c.width/2, c.height).ConvertToYV16() YToUV(U, V, c.ConvertToYV16()).ConvertToYUY2() } function YV12ToYUY2i_26(clip c) { f = c.AssumeTFF().SeparateFields() top = f.SelectEven() bot = f.SelectOdd() topU = top.UToY8().BilinearResize(c.width/2, c.height/2, 0, 0.125) botU = bot.UToY8().BilinearResize(c.width/2, c.height/2, 0, -0.125) U = Interleave(topU, botU).ConvertToYV16() topV = top.VToY8().BilinearResize(c.width/2, c.height/2, 0, 0.125) botV = bot.VToY8().BilinearResize(c.width/2, c.height/2, 0, -0.125) V = Interleave(topV, botV).ConvertToYV16() YToUV(U, V, f.ConvertToYV16()).ConvertToYUY2().Weave() } function YUY2ToYV12p_26(clip c) { U = c.UToY8().BilinearResize(c.width/2, c.height/2) V = c.VToY8().BilinearResize(c.width/2, c.height/2) YToUV(U, V, c.ConvertToY8()) } function YUY2ToYV12i_26(clip c) { f = c.AssumeTFF().SeparateFields() top = f.SelectEven() bot = f.SelectOdd() topU = top.UToY8().BilinearResize(c.width/2, c.height/4, 0, -0.25) botU = bot.UToY8().BilinearResize(c.width/2, c.height/4, 0, 0.25) U = Interleave(topU, botU) topV = top.VToY8().BilinearResize(c.width/2, c.height/4, 0, -0.25) botV = bot.VToY8().BilinearResize(c.width/2, c.height/4, 0, 0.25) V = Interleave(topV, botV) YToUV(U, V, f.ConvertToY8()).Weave() } |
|
16th January 2011, 08:01 | #18 | Link |
Registered User
Join Date: Feb 2003
Location: Russia, Moscow
Posts: 854
|
Gavino!
It is very interesting where we can find problem. I am like Mini-Me use ConvertToYV12() before calling QTGMC deinterlacer and using Set 2.6 build. Which script I could use (for 2.5.8 or 2.6)? yup. |
17th January 2011, 05:04 | #20 | Link |
Avisynth Developer
Join Date: Jan 2003
Location: Melbourne, Australia
Posts: 3,167
|
A theoretical improvement for a correctly working 2.6 (yes there are still more bugs in the 'fixed' YtoUV code )
Code:
# ------- Support functions -------- function YV12ToYUY2p_26(clip c) { UV = Interleave(c.UToY8(), c.VToY8()).BilinearResize(c.width/2, c.height) YToUV(UV.SelectEven(), UV.SelectOdd(), c).ConvertToYUY2() } function YV12ToYUY2i_26(clip c) { f = c.AssumeTFF().SeparateFields() top = f.SelectEven() bot = f.SelectOdd() topUV = Interleave(top.UToY8(), top.VToY8()).BilinearResize(c.width/2, c.height/2, 0, 0.125) botUV = Interleave(bot.UToY8(), bot.VToY8()).BilinearResize(c.width/2, c.height/2, 0, -0.125) U = Interleave(topUV.SelectEven(), botUV.SelectEven()) V = Interleave(topUV.SelectOdd(), botUV.SelectOdd()) YToUV(U, V, f).ConvertToYUY2().Weave() } Note the above script blows up on both the released 2.6 and the current CVS code. I'll commit the latest fixes in the next few days. |
Thread Tools | Search this Thread |
Display Modes | |
|
|