View Full Version : Chroma shift in interlaced YV12 to YUY2 conversion
Gavino
8th June 2009, 22:08
The wiki page on Sampling (http://avisynth.org/mediawiki/Sampling#YV12_interlaced_conversion_-.3E_YUY2) 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.
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)
Another angle on the problem is that one would expect
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.
Manao
9th June 2009, 15:02
Top fields have chroma shifted down 1/2 pixel and bottom fields up 1/2 pixel.Which is normal for interlaced content. I advise you to check yuy2toyv12.h in http://manao4.free.fr/conversion-v1.0.zip for an explanation
Gavino
9th June 2009, 17:07
Which is normal for interlaced content. I advise you to check yuy2toyv12.h in http://manao4.free.fr/conversion-v1.0.zip for an explanation
Yes, I understand the horrible details of chroma placement in the different formats. When I talk about chroma shift, I mean that the code does not produce the required output chroma placement and resamples to the wrong position. (In that sense, a shift cannot be 'normal', by definition.)
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.
tritical
9th June 2009, 18:20
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)
http://bengal.missouri.edu/~kes25c/chroma.png
Manao
9th June 2009, 18:49
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.Well, the link i gave point toward the yuy2 <-> yv12 conversions I had written for avs3. It comes with isse asm, and can fairly easily be upgraded to sse2 / ssse3 if need be.
Gavino
9th June 2009, 23:49
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?
Manao
12th June 2009, 09:42
but in practice is there a visible difference in quality? I hate aliasing, so I didn't even consider comparing [1 1] with [1 3 3 1]. You can easily run a comparison using avisynth : bilinearesize(clip, clip.width, clip.height / 2) uses [1 3 3 1], while mt_average(clip, clip.pointresize(clip.width, clip.height, 0, 0, 1, 0)).pointresize(clip.width, clip.height/2) uses [1 1]. The second one is far sharper, because it aliases as hell (though bilinearresize aliases too, but to a lesser extent)
Incast
12th June 2010, 16:34
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?
WorBry
13th June 2010, 05:32
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
I think this is (being) addressed in AVISynth v2.6...is it not ?
Mini-Me
12th January 2011, 22:10
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. ;)
Gavino
13th January 2011, 12:28
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?
I don't think the default behaviour of the converters has changed in Avisynth 2.6 (perhaps IanB can confirm?). However, 2.6 has added parameters chromaplacement and chromaresample to the Convert (http://avisynth.org/mediawiki/Convert) filters. I haven't tried it yet, but in principle I believe using chromaresample="bilinear" should give the correct results.
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.
Manao's routines are correct (and were written long before this thread existed), but they are not part of Avisynth.
There are four ways to convert to YV12, given the four combinations of interlaced/progressive conversion with woven/separated fields.
Converting separated interlaced fields will always give the wrong result, as the converters are not set up to handle this.
Mini-Me
13th January 2011, 21:14
I don't think the default behaviour of the converters has changed in Avisynth 2.6 (perhaps IanB can confirm?). However, 2.6 has added parameters chromaplacement and chromaresample to the Convert (http://avisynth.org/mediawiki/Convert) filters. I haven't tried it yet, but in principle I believe using chromaresample="bilinear" should give the correct results.
Manao's routines are correct (and were written long before this thread existed), but they are not part of Avisynth.
That's helpful to know. I may have to try the 2.6 alpha then, unless I can shoehorn Manao's routines into a plugin.
Converting separated interlaced fields will always give the wrong result, as the converters are not set up to handle this.
That's what I figured, but I had to try it anyway out of desperation and for completeness. ;) As expected, the actual YV12 video is screwed up when you convert with separated fields...but strangely enough, weaving again and upsampling back to YUY2 in interlaced mode actually resembles the original more closely under some circumstances than any other combination I've tried.
If you want to see what I'm talking about, here's an interesting comparison:
original = Avisource("yuy2video.avi", pixel_type = "YUY2")
progyv12 = original.AssumeTFF.ConvertToYV12()
progyv12backtoyuy2 = progyv12.ConvertToYUY2()
brokenyv12 = original.AssumeTFF.SeparateFields.ConvertToYV12.Weave()
interestingyuy2 = brokenyv12.ConvertToYUY2(interlaced = True)
return Interleave(original, interestingyuy2, progyv12backtoyuy2)
In this case, the progressively converted video has pretty much the same characteristics in YV12 and YUY2 after converting back. It looks fine for the most part, but the progressive conversion blends chroma between combed fields, which gives incorrect (blended) color in combed areas. It's better than the artifacts from built-in interlaced conversion though and seems to give the best YV12 image overall (before trying the corrected conversions), which is why I included it in the comparison instead of the interlaced conversion. Deinterlacing probably makes the blended color far more obvious though, which I haven't checked yet.
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. :)
Gavino
13th January 2011, 21:44
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?
Gavino
15th January 2011, 05:09
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:
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
I think this explains the results I reported in the previous post.
Whaddaya think, Ian?
IanB
15th January 2011, 21:52
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. :confused:
Gavino
15th January 2011, 22:39
Yes that ConvertToPlanarGeneric alignment code is a bit (lot) of a mess.
That's the conclusion I came to after studying it more closely today. :(
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.
In the meantime, does anyone know of a workaround?
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.
# 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()
}
Don't use the above code on the current 2.60 as it will crash due to a bug in YToUV (fixed but not yet released).
In any case, the new formats available in 2.6 make this more efficient version possible:
# 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()
}
Mini-Me
16th January 2011, 03:14
Wow, thanks Gavino! I'll test those routines out in just a bit. Assuming they're correct - which I'm sure they are - that eliminates the need for me to figure out how to turn Manao's source into a plugin. :D
yup
16th January 2011, 08:01
Gavino!:thanks:
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.
Gavino
16th January 2011, 10:33
... using Set 2.6 build. Which script I could use (for 2.5.8 or 2.6)?
I don't have that build installed, so I can't test it.
But I think the 2.6 version of my script should work.
IanB
17th January 2011, 05:04
A theoretical improvement for a correctly working 2.6 (yes there are still more bugs in the 'fixed' YtoUV code :o)# ------- 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()
}
In planar mode the intended operation of YtoUV is to auto-determine the output pixel format from the relative dimensions of the input clips, so you can use Y8 everywhere for the intermediate clips. Also a cunning trick to share the resizer between U and V paths.
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.
Mounir
17th January 2011, 17:37
I find the Gavino tweak interesting but sadly i have no idea how to use this workaround in the v2.6. Should i save it as .avsi and import it, or what ? You tell me
Gavino
17th January 2011, 18:13
... to use this workaround in the v2.6. Should i save it as .avsi and import it, or what ?
You can either
- just copy it into every script that needs it, or
- save it as either .avs or .avsi, and then Import() it, or
- save it as .avsi to Avisynths plugin folder, then it will be imported automatically.
Don't forget to include IanB's performance improvements (but only once the 2.60 UVToY bug is fixed - see his post #20).
The functions you actually want to call in your script are YV12ToYUY2_26 (which replaces ConvertToYUY2 for YV12 inputs) and YUY2ToYV12_26 (which replaces ConvertToYV12 for YUY2 inputs).
Mounir
17th January 2011, 19:11
Thanks for the reply, that's helping.
So i must completely replace the Gavino's coding part for "Support functions " YV12ToYUY2p_26 and YV12ToYUY2i_26 (progressive & interlaced respectively) with IanB tweaks if i understand it right.
Edit:
Well i tried importing and using YUY2ToYV12_26 in my script i get an error in avsp
script error: end of file reached without matching }
Gavino
17th January 2011, 19:31
You probably missed a bit when copying and pasting.
Note that some of the code as shown on the forum page is scrolled off the bottom of the displayed 'code' box. Be sure you scroll to the end to get it all.
Mounir
17th January 2011, 19:51
It seems to be working now ,i forgot a } in the script.
I have a tie-breaker: Is it normal that there is no matrix parameter ?
Edit: nevermind the question it's YUV, no matrix involved
jmac698
17th January 2011, 20:58
Updated http://avisynth.org/mediawiki/Known_Issues
thanks.
Gavino
17th January 2011, 23:17
Is it normal that there is no matrix parameter ?
Edit: nevermind the question it's YUV, no matrix involved
That's right, but it raises a related point.
RGB conversions to and from YV12 go via YUY2 internally in Avisynth. So, for example, an interlaced YV12 to RGB conversion will also suffer the chroma shift problem. If that's a concern, you need to convert to YUY2 using the workaround code, then do the final conversion to RGB yourself.
Another thing: I forgot that IanB's changes won't work until there is an updated 2.60 (see his post#20), so don't include them just yet. I've edited my earlier post to reflect that.
Mounir
18th January 2011, 00:07
I see that's interesting.One can now see the benefit of capturing videos in YUY2 directly.
poisondeathray
18th January 2011, 01:48
The thread title refers to interlaced conversions, but Gavino can you confirm if this code affect progressive conversions? for example RGB<=>YV12 . The "support functions" seem to have a "p" version - I'm assuming for progressive ?
It seems to be more accurate in my tests, less blurring/shifting after consecutive conversions
#start with progressive RGB image
ConverttoYUY2()
YUY2ToYV12_26()
ConvertToRGB()
vs.
#progressive RGB image
ConvertToYV12()
ConvertToRGB()
After several rounds of conversions, the old method is noticably worse
Is Manao's "four pixel kernal method" a similar method that NLE's use? Because several rounds of RGB<=>YV12 conversions there do not incur the same losses as the old avisynth method either
Speed was mentioned as slower ; is this negligible or is there a big difference?
Gavino
18th January 2011, 14:26
The "support functions" seem to have a "p" version - I'm assuming for progressive ?
Yes, the "p" versions are for the progressive case.
YUY2ToYV12 avoids potential aliasing in chroma subsampling (in both interlaced and progressive);
YV12ToYUY2(interlaced=false) is not strictly needed, since both 2.58 and 2.60 handle this case correctly, but is included for consistency and completeness.
for example RGB<=>YV12
Once you bring RGB into the frame, you open a different (but related) can of worms in the current implementation.
The RGB->YUY2 code (also used for RGB->YV12 on 2.58) adds a horizontal shift (see this thread, also referenced in the first post here).
On 2.60, RGB<->YV12 goes via YV24 (not YUY2), but this path currently also has bugs - YV24->YV12 adds both a horizontal and a vertical shift, while YV12->YV24 has an exactly opposite horizontal shift (which cancels out in YV24->YV12->YV24).
To summarise, for progressive RGB->YV12->RGB, you get the following chroma shifts (Horizontal and/or Vertical):
2.58 2.60
RGB->YV12: H H+V
YV12->RGB: OK -H
RGB->YV12->RGB: H V
For interlaced, it is:
2.58 2.60
RGB->YV12: H H+V
YV12->RGB: V -H
RGB->YV12->RGB: H+V V
Is Manao's "four pixel kernal method" a similar method that NLE's use? Because several rounds of RGB<=>YV12 conversions there do not incur the same losses as the old avisynth method either
I don't know what NLEs do, but once 2.60 is working properly, the RGB<->YV12 conversions will be about as good as you can get, with bicubic chroma interpolation and anti-aliased downsampling.
Speed was mentioned as slower ; is this negligible or is there a big difference?
It will be slower, but I don't know how noticeable - perhaps IanB can quantify?
poisondeathray
18th January 2011, 16:07
Thanks for clarifying ,Gavino
once 2.60 is working properly, the RGB<->YV12 conversions will be about as good as you can get, with bicubic chroma interpolation and anti-aliased downsampling.
Sounds wonderful. Any idea of a timeframe ?
IanB
18th January 2011, 22:58
The resizer core is very fast for what it does, but if one has an explicit job to do then maybe one could write even faster code. It comes down to the law of diminishing returns. If something takes 1% of the total time making it 10 times faster only changes the total by 0.9%. The currently released versions still needlessly blit the chroma planes before resizing them, the CVS code no longer does this, that saves about ~12%.
The bugfix list has become pretty meaty, perhaps I should devote some cycles to give people a release. I'll try to put some effort into fixing the offset errors in the planar conversions and then roll out another alpha release soon.
Nevilne
18th January 2011, 23:36
Is progressive YV12 presented as RGB affected as well?
Basically, ConvertToRGB32(chromaresample="point") from YV12 source (this is not done by avisynth but rather similar by NLE), and then ConvertToYV12(chromaresample="point")?
Gavino
19th January 2011, 01:08
If the conversion is done entirely independently of Avisynth, it obviously depends on the algorithm used. If the algorithm is the equivalent of ConvertToRGB32(chromaresample="point"), there will be a vertical chroma shift up 1/2 pixel. However, if then converted back by the equivalent of ConvertToYV12(chromaresample="point"), there would be a corresponding shift down again, so the two would cancel out.
On the other hand, tools designed to work directly with Avisynth (such as AvsP) will use Avisynth's own ConvertToRGB32(), so will inherit any problems therein.
yup
10th February 2011, 10:40
Hi all!
I have collection cleaned VHS rip. I use tsp Avisynth MT 0.7 build. My video is interlaced. Can I repairing, remove shift introduced by conversion YUY2->YV12?
yup.
Gavino
10th February 2011, 19:17
I use tsp Avisynth MT 0.7 build. My video is interlaced. Can I repairing, remove shift introduced by conversion YUY2->YV12?
There is no shift if going in the direction YUY2->YV12, but there is a potential for aliasing if you have high frequency chroma content.
My 2.58 fix for YUY2ToYV12 (post #16 above) should work for tsp's Avisynth MT 0.7 build too (and for vanilla 2.57).
yup
11th February 2011, 07:43
Gavino! :thanks:
Now I have only filtered YV12 AVI files, source YUY2 erased. If i right understand in my files problem aliasing at U and V plane and I need use AA filters for color planes?
yup.
Gavino
11th February 2011, 10:58
That's right - my 'fix' is a replacement for ConvertToYV12, and doesn't help if you've already done the conversion and no longer have the original source.
For that, as you say, you would need to use AA on the chroma planes. However, the extent of the problem depends on the source and there may no visible effects. It's more likely to occur on material with sharp color edges, such as anime or CGI graphics.
yup
11th February 2011, 11:52
Gavino!
My source is VHS rip for lectures, not very sharp. May I can do not worry? And do nothing.
yup.
Rumbah
12th February 2011, 02:47
A little bit off topic but is it possible to do a chroma resampling YV12 -> YV12?
I have a mjpeg source with mpeg1/jpeg chroma sampling and want to convert it to h264. As every h264 decoder I know upsamples assuming the mpeg2/4 chroma location it would be good to resample the chroma from mpeg1 location to mpeg2 location. Is this possible with avisynth?
Gavino
12th February 2011, 10:58
... to resample the chroma from mpeg1 location to mpeg2 location. Is this possible with avisynth?
Yes, you can use the extra parameters of the resizers to do a sub-pixel shift, and use MergeChroma to combine the original luma with the shifted chroma.
# mpeg1 to mpeg2:
MergeChroma(BilinearResize(width(), height(), src_left=-0.5)) # or any other resizer
Rumbah
12th February 2011, 13:37
Ah, thank you, I thought that parameter was only for cropping. Who would have thought that it would be that easy :D
Does it make any sense to use a better resize filter for this case?
Gavino
12th February 2011, 18:03
Does it make any sense to use a better resize filter for this case?
Probably not, but you might want to use a sharper resizer if you have strong chroma edges.
Rumbah
12th February 2011, 22:27
Ok, thank you for your help.
I think I'll use Spline36resize then as it is followed by needi and MCTemporalDenoise so the chroma shift computing time will be small in comparison.
Mini-Me
2nd March 2011, 12:25
I finally got around to playing with the new routines, and I found something interesting. I may be wrong - this may be the intended behavior, and it may be necessary to prevent other issues - but I think the shifts in the resize are actually incorrect. EDIT: Using Gavino's original offsets does in fact "prevent other issues." They result in gradual smearing, but they are in fact correct, and doubling them results in chroma bobbing from field to field, relative to luma. I suppose the smearing simply comes from Avisynth's resizer core not playing nicely with the smaller offsets. View post 51 further down the page for more details. The "ConvertAlmostEquivalent" methods in the post below avoid both smearing and significant chroma bobbing though.
The new routines don't have the awful combed chroma shifts that the old routines had, but they still smear color more and more with every iteration. You can verify this by testing them with [a modified version of] Gavino's color bar test script (at least in 2.6). Eliminating or halving the offsets/shifts in the resize calls will make this worse, but if you double the offsets/shifts in the resize calls (0.125 -> 0.25, and 0.25 -> 0.5), the smearing stops after just a few iterations. Even though this is an unrealistic number of conversions, converting back and forth between YUY2 and YV12 a few hundred times shows an enormous difference.
Unless the original offsets were just wrong, maybe the difference is that Resize bases the offsets on the clip's current size before resizing, instead of its size after resizing? That's just a guess though, and some of you guys might know for a fact I'm wrong. :p
Anyway, it seems the support functions should be modified as follows:
# ------- Support functions --------
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.25)
botU = bot.UToY8().BilinearResize(c.width/2, c.height/2, 0, -0.25)
U = Interleave(topU, botU).ConvertToYV16()
topV = top.VToY8().BilinearResize(c.width/2, c.height/2, 0, 0.25)
botV = bot.VToY8().BilinearResize(c.width/2, c.height/2, 0, -0.25)
V = Interleave(topV, botV).ConvertToYV16()
YToUV(U, V, f.ConvertToYV16()).ConvertToYUY2().Weave()
}
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.5)
botU = bot.UToY8().BilinearResize(c.width/2, c.height/4, 0, 0.5)
U = Interleave(topU, botU)
topV = top.VToY8().BilinearResize(c.width/2, c.height/4, 0, -0.5)
botV = bot.VToY8().BilinearResize(c.width/2, c.height/4, 0, 0.5)
V = Interleave(topV, botV)
YToUV(U, V, f.ConvertToY8()).Weave()
}
I tested the two versions by renaming all six functions associated with my modified version (I created copies of the functions, replacing all instances of "_26" with "_26DoubleShifts"). I used the following script (actually transcribed from another computer, so there may be typos or something). (Also, note that there are two other much simpler if "unapproved" formulations that give similar results.)
function ConvertBad(clip c, int times) {
times == 0 ? c : c.ConvertToYUY2(interlaced=true).ConvertToYV12(interlaced=true).ConvertBad(times-1)
}
function ConvertSmallShifts(clip c, int times) {
times == 0 ? c : c.YV12ToYUY2_26(interlaced=true).YUY2ToYV12_26(interlaced=true).ConvertSmallShifts(times-1)
}
function ConvertDoubleShifts(clip c, int times) {
times == 0 ? c : c.YV12ToYUY2_26DoubleShifts(interlaced=true).YUY2ToYV12_26DoubleShifts(interlaced=true).ConvertDoubleShifts(times-1)
}
# Someone mentioned earlier that separating fields and converting doesn't work,
# because it wasn't intended that way. Nevertheless, the below methods seem to
# work very well for regular sources, and they're also well-behaved in this script
# (they stop accumulating error after a while). In both YV12 and YUY2, they look
# better than "double shifts" in some areas of the image and worse in others
# (e.g. in YUY2, these look better at the red/blue transition and worse at others).
# I'm not sure which behavior is preferable or why, and they're only subtly
# different, both in this comparison and in real sources. Either way, they are far
# better than any methods I tried (sane or insane ;)) until Gavino posted the
# updated routines, and they still seem to do better than anything except for
# maybe the "double shifts" version, so I decided it couldn't hurt posting about
# them. ;)
function ConvertAlmostEquivalent(clip c, int times) {
times == 0 ? c : c.SeparateFields.ConvertToYUY2.ConvertToYV12.Weave.ConvertAlmostEquivalent(times-1)
# Another version exactly identical to this one, at least in Avisynth 2.6
# times == 0 ? c : c.ConvertToYUY2(interlaced=true).SeparateFields.ConvertToYV12.Weave.ConvertAlmostEquivalent(times-1)
}
bars = ColorBars(pixel_type="YV12").TurnRight()
bad = bars.Animate(0, 200, "ConvertBad", 0, 200)
smallshifts = bars.Animate(0, 200, "ConvertSmallShifts", 0, 200)
doubleshifts = bars.Animate(0, 200, "ConvertDoubleShifts", 0, 200)
similar = bars.Animate(0, 200, "ConvertAlmostEquivalent", 0, 200)
return StackHorizontal(bad, smallshifts, doubleshifts, similar)
STC-Fan
2nd March 2011, 20:23
Just thought I'd post the colour bar test from AviSynth 2.5.8, using 10 iterations, based on what Mini-Me posted above - the bars from left to right are in the order described in the above script (ConvertBad, ConvertSmallShifts, ConvertDoubleShifts and ConvertAlmostEquivalent):
http://stc.hacking-cult.org/images/colourconversiontest_avisynth2.58.png
From my observations it would seem that DoubleShifts and AlmostEquivalent are superior to the standard script Gavino wrote. Admittedly the differences are a lot smaller than comparisons between Gavino's conversion scripts and the old ConvertToYV12/YUY2 functions built into AviSynth, but it is an improvement nonetheless.
Mini-Me
2nd March 2011, 20:39
Just thought I'd post a colour bar test from AviSynth 2.5.8 based on what Mini-Me posted above - the bars from left to right are in the order described in the above script (ConvertBad, ConvertSmallShifts, ConvertDoubleShifts and ConvertAlmostEquivalent):
http://stc.hacking-cult.org/images/colourconversiontest_avisynth2.58.png
From my observations it would seem that DoubleShifts and AlmostEquivalent are superior to the standard script Gavino wrote. Admittedly the differences are a lot smaller than comparisons between Gavino's conversion scripts and the old ConvertToYV12/YUY2 functions built into AviSynth, but it is an improvement nonetheless.
How many iterations did you use? That's about what I get if I use 10 iterations in 2.6, but if I use 200 it looks more like this, where the differences become much more apparent:
http://i368.photobucket.com/albums/oo130/UnrealMiniMe/BadSmallShiftsDoubleShiftsSimilar.png
(From left to right: Bad (Avisynth 2.6's built-in conversions), SmallShifts (Gavino's updated conversions), DoubleShifts(Gavino's updated conversions with double resize offsets), and AlmostEquivalent (from the above script).
STC-Fan
2nd March 2011, 21:02
How many iterations did you use? That's about what I get if I use 10 iterations in 2.6
It was 10, yes - although it said 500 in the original script in your post, but that caused MeGUI to crash. However, I tried 200 iterations in 2.5.8 just now, and I got virtually the same results as you posted there - now I see what you meant!
Mini-Me
2nd March 2011, 21:08
It was 10, yes - although it said 500 in the original script in your post, but that caused MeGUI to crash. However, I tried 200 iterations in 2.5.8 just now, and I got virtually the same results as you posted there - now I see what you meant!
Come to think of it, I never got it up to a full 500 without crashing either, actually. :p It's because Animate has to evaluate every previous frame in order to render the current frame. I assume frame/result caching helps at low frame numbers, but once you get high enough, I think Avisynth has to calculate each frame starting from frame 0...which somehow makes it choke. Either that or it tries to cache all the results and ends up exceeding memory, but it runs so slowly that I don't know if that makes sense. (Someone more experienced will know for a fact, of course.) Either way, I never should have written 500 iterations into the above script, so I'll change that to 200 now.
Didée
2nd March 2011, 23:08
BTW, speaking of ye good old ConvertBackToYUY2. What was its reason for existance? :p
Mini-Me
2nd March 2011, 23:19
BTW, speaking of ye good old ConvertBackToYUY2. What was its reason for existance? :p
To allow people to losslessly convert back and forth between YUY2 and YV12 200 times as part of a perfectly ordinary and logical workflow? ;) Point taken of course, but the real reason for repeated conversions in this thread is just to make flaws in the conversion routines more apparent.
I just realized something though: Although my "corrected" offsets smear chroma less (apparent with 200 applications ;)), it turns out that they are not "corrected" at all but wrong. They have another problem: If you perform a one-way conversion from YUY2 to YV12 or vice-versa without converting back, the chroma will bob from field to field, relative to the luma. (Converting back cancels out the bobbing and masks the problem, which is why I didn't recognize it earlier.) In contrast, Gavino's original offsets keep chroma perfectly aligned with luma, at least in the sources I tested. Since his offsets seem perfectly correct, my best guess is that the smearing comes from Avisynth's resizer core not playing as well with the smaller offsets...or something to that effect.
In other words, my doubled offsets are wrong, even though they don't cause smearing. I noticed the chroma bobbing in some of my own captures when I viewed them with separated fields, where bright reds especially would appear to slightly "swim" around the details of the image as they bobbed. I'm not sure whether deinterlacing with Q/TGMC would exacerbate or alleviate the chroma bobbing, but either way, it's not great news.
So, using Gavino's offsets results in gradual chroma smearing, and using my doubled offsets results in significant chroma bobbing from field-to-field. Instead, you can use the methods I included in my "ConvertAlmostEquivalent" function further up the page. They don't have perpetual chroma smearing, and they don't displace chroma relative to luma either.* If you want to do it this way, convert to YV12 by:
yuy2clip.SeparateFields.ConvertToYV12.Weave()
and you have to convert from YV12 to YUY2 with either:
yv12clip.ConvertToYUY2(interlaced=True) #, or
yv12clip.SeparateFields.ConvertToYUY2.Weave()
This is assuming the current ConvertToYV12 and ConvertToYUY2 implementations in Avisynth 2.6. I've edited this post and changed my mind about a half-dozen times, but now that I've sorted things out a bit better, I think the above methods are probably the most accurate, both for one-way and round-trip conversion. At the very least, I cannot find any issues with them.
Before editing this post again, I previously wrote that the "separated fields" methods bob chroma by a very subtle amount. Upon further testing, it turns out this is untrue, at least if you're using Virtualdub to view your test videos like I'm doing. The slight chroma bobbing I saw with these methods was already present in my YUY2 source, and it was not introduced by the "separated fields" YV12 conversion. (It's a long story, but my VHS captures are a bit of a pain sometimes. ;)) The bottom line is, the above methods will not perpetually smear, and they will also introduce no chroma bobbing (from what I can tell). Using Virtualdub as a player, the chroma is in the same place for YUY2 and YV12. In contrast, I just realized that Gavino's method actually does very slightly displace the chroma relative to the luma if you do a one-way conversion, but in my case it was actually fixing the error present in my source, at least for a one-way conversion. :o Using resizer offsets of 0.0 will eliminate the bobbing entirely, but 0.0 offsets greatly exacerbate the smearing, so Gavino's offsets appear to be a better compromise.
I still don't have a whole lot of certainty about all this though, because it ultimately depends on the assumptions your player makes about YV12 chroma placement. I mean, I'm using Virtualdub as my player, so if it upscales YV12 incorrectly, then I just came to the wrong conclusion about which conversion method bobs chroma and which does not. ;)
Gavino
3rd March 2011, 02:47
Hi guys, I have been occupied elsewhere today so I've missed the latest round of postings from the last 24 hours.
I was pretty sure the offsets I used (worked out from first principles rather than by experiment) were the right ones to get the chroma placement correct, and I'm relieved to see that in the end Mini-Me agrees with that.
The various test results are interesting, and I will look into them in more detail tomorrow in an attempt to understand what lies behind them. What I can say is that the reason the 'double shifts' (though wrong) do not show any chroma shift on repeated conversion of YV12 to YUY2 and back is because the error in one side of the conversion is exactly reversed on the other, so there is no error on the 'round trip'.
BTW ConvertBackToYUY2 is only for converting from RGB, and cannot be used for YV12->YUY2.
IanB
3rd March 2011, 11:05
Strip away all the gory details and you are left with :-BilinearResize(width, height/2, 0, -0.5).BilinearResize(width, height, 0, 0.25)Of course this is going to smear after lots of iteration. You might as well have strung 200 iterations of Blur() together. Doh!
Mini-Me
3rd March 2011, 12:06
Strip away all the gory details and you are left with :-BilinearResize(width, height/2, 0, -0.5).BilinearResize(width, height, 0, 0.25)Of course this is going to smear after lots of iteration. You might as well have strung 200 iterations of Blur() together. Doh!
I guess it's really not surprising that it blurs with 0.125 and 0.25 offsets. What surprises me is that using 0.0 offsets makes it blur faster (rather than less or not at all), and using 0.25 and 0.5 offsets doesn't blur (although it bobs chroma terribly). Gavino mentioned the doubled offsets don't blur because the errors cancel each other out, but I haven't yet worked out how/why they cancel out when his own offsets and especially 0.0 offsets don't. (A lot of it is because I'm a noob; I'm still not sure whether the resizer offsets are given in terms of input or output resolution. :o)
Anyway, the more surprising thing is that clip.SeparateFields.ConvertToYV12.Weave() and clip.SeparateFields.ConvertToYUY2.Weave() work so well, because the progressive conversion used internally isn't even meant to be used on individual half-height fields (and Gavino said as much on page 1). I should be jumping for joy that they work without any apparent issues, but I keep suspecting there's some hidden snag waiting to bite me. It seems too good to be true, since it's giving me no perpetual blurring and no chroma bobbing. I feel like something has to be wrong with it, but it keeps giving me perfect results.
Technically speaking, Gavino's 0.125 and 0.25 offsets are supposed to be correct (and are definitely the best compromise for the functions on page 1), but I noticed that a one-way conversion actually introduces a very slight chroma bob from field to field if I compare the image between YUY2 and YV12 in Virtualdub (using two windows side-by-side and flipping between fields, since you can't actually stack or interleave different formats). Of course, the offsets shift the chroma on purpose to accomodate the chroma placement of the new format, but I assume they're not supposed to introduce a perceptible chroma bob from field to field, relative to the luma. The picture is supposed to look the same in both formats (or as close as possible, given half the vertical chroma resolution in YV12 and hairy interaction with interlacing). In contrast, simply separating fields and converting results in identical chroma placement in YUY2 and YV12, when I compare using Virtualdub.
So I guess my question is: What's going on? Is Virtualdub making unusual assumptions about chroma placement and upsampling from YV12 incorrectly for display (relative to MPEG standards and the behavior we expect from players)? If so, I can see why that would erroneously introduce chroma bob to Gavino's correct version and inadvertantly fix the hacky separated fields version. Or is Virtualdub upsampling from YV12 correctly, meaning the best results actually come from separating fields and converting the colorspace as progressive?
Gavino
6th March 2011, 01:45
Having now found time to look at your points in detail, I think the trees are preventing you from seeing the forest (so to speak) and you're looking at the wrong thing. Let's take a step back and revisit the problem we're trying to solve.
The original problem
At the start of this thread, I pointed out that Avisynth's method of converting interlaced YV12 to YUY2 introduced a chroma shift (which was in a different direction for each field). To make the effect more visible, I demonstrated the problem with a script that repeatedly converts interlaced YV12 to YUY2 and back, showing that a two-way (combed) chroma shift accumulates with each round of conversions.
As I also stated in that very first post, the expected result of a round trip YV12->YUY2->YV12 conversion is a slight blurring (aka smearing) of chroma (because of upscaling followed by downscaling). The error demonstrated by the original test script is the shift which occurs in addition to the (expected) blurring.
The workaround
Later (over 18 months later), when it became clear that the problem still existed in v2.60, I posted a workaround consisting of a set of script functions which could be used in place of Avisynth's built-in YV12<->YUY2 conversions.
[The original problem was just about interlaced YV12->YUY2, but the replacement functions also include YUY2->YV12 (both progressive and interlaced) with chroma anti-aliasing (emulating the 'manao functions'), as well as a progressive YV12->YUY2 for completeness.]
The 'offsets'
The functions use BilinearResize to do the chroma resampling, with appropriate sub-pixel offsets where necessary to get the correct chroma placement.
Note that a correct set of offsets must necessarily 'cancel out' on a round trip YV12->YUY2->YV12, as this has to return chroma to its starting placement. So if some offset y is used for upsizing (YV12->YUY2), it must be matched by an offset of -2*y for downsizing (YUY2->YV12).
(The factor of two is because the offset is expressed in terms of source pixels, and the distance between the upscaled pixels is half that between the downscaled ones.)
Conversely however, you cannot confirm the validity of the individual offsets by using them in pairs on a round trip - any pair of offsets y and -2*y will combine on a round trip without a net shift, even if these offsets actually give an incorrect chroma placement for the individual ('one-way') conversions. All you will see from this test is the expected chroma blurring (whose strength varies with the offsets - see later).
To check the individual offsets, you must modify the round trip test to combine my interlaced YV12->YUY2 function with the built-in ConvertToYV12 (which is known to give the correct chroma placement).
If you do that, you will find that my offsets work, and no shift is seen - with any other offsets, the combed shift is still present to some degree.
You can do the same test using your SeparateFields.ConvertToYUY2.Weave() for the YV12->YUY2 part and see that it also creates a combed shift. This is because it treats top and bottom fields the same way, which is wrong as they have different chroma alignment in YV12.
The error (here with chroma) is similar in nature to the one you get (with both luma and chroma) resizing an interlaced clip with SeparateFields.xxxResize.Weave (which produces an incorrect field alignment).
Other points
We are left to explain why some offsets blur more than others on the round trip.
This is simply a side-effect of where the resampling positions fall. I believe the reason an upscaling offset of (+ or -) 0.25 gives the smallest blur is that this preserves the original pixels when upscaling and so each pixel retains a higher proportion of its original value after downscaling again.
Since the Avisynth resizers maintain the image centre position, an offset of zero does not preserve the original pixels, so blurs more on the round trip. (Note that we don't want to preserve the original pixels when converting because YV12 and YUY2 have different chroma sampling points.)
Regarding VDub, I believe it always treats YV12 as progressive when converting to RGB (eg to apply certain filters or even simply for display), so interlaced YV12 is displayed incorrectly. See this post from IanB:
Over in VirtualDub land Avery Lee rigidly follows the fourCC definition for YV12 to the letter, ie. Mpeg2 chroma sitting, Black=16, White=235, Progressive picture structure, Rec601 colorimetry, etc ... It saves him a whole lot of grief from his users but it limits the available functionality. Here in Avisynth land we are a bit more laid back, we provide an internal format misnamed YV12. It provides 8 bit data planes with chroma information subsampled 50% both horizontally and vertically. That's it, everything else is freely a user choice. Various filters assume certain attributes about the data and it is the script authors responsibility to use or abuse those assumptions.
Also see here:
http://forum.videohelp.com/threads/309449-Adobe-Premiere-MainConcept-s-H-264-vs-x264-open-source-encoder?p=1952599#post1952599
http://forum.videohelp.com/threads/316415-Check-Over-My-Options-From-DVD-to-XviD-Conversion-Thread?p=1958565#post1958565
Mini-Me
7th March 2011, 09:14
Regarding VDub, I believe it always treats YV12 as progressive when converting to RGB (eg to apply certain filters or even simply for display), so interlaced YV12 is displayed incorrectly. See this post from IanB:
Also see here:
http://forum.videohelp.com/threads/309449-Adobe-Premiere-MainConcept-s-H-264-vs-x264-open-source-encoder?p=1952599#post1952599
http://forum.videohelp.com/threads/316415-Check-Over-My-Options-From-DVD-to-XviD-Conversion-Thread?p=1958565#post1958565
Aha! Thank you for your detailed summary, Gavino, and thank you especially for looking into the Virtualdub issue. Now that I know that Virtualdub's resampling is incorrect, everything makes way more sense.
I never fed Virtualdub actual interlaced YV12, so I never saw its chroma upsampling bug. Instead, I tested YV12 by separating fields before viewing in Virtualdub, so I could view each field clearly and individually and see how much the chroma would bob from field to field. Since Virtualdub always treats YV12 as progressive, it just so happened to precisely reverse my SeparateFields.ConvertToYV12 conversion...but that same process failed to correctly upsample your own conversion.
I know I've been a pain, but I appreciate your patience guiding me through this. I'll make sure to use your conversion routines from here on out! :)
Wilbert
7th March 2011, 17:45
Aha! Thank you for your detailed summary, Gavino, and thank you especially for looking into the Virtualdub issue. Now that I know that Virtualdub's resampling is incorrect, everything makes way more sense.
I never fed Virtualdub actual interlaced YV12, so I never saw its chroma upsampling bug.
To confuse things a bit more :) Avery Lee will tell you that Virtualdub is not incorrect, since YV12 is progressive by definition. What we call interlaced YV12 is actually interlaced 4:2:0. Not that the problem doesn't go away though :)
edit: typo
Mini-Me
7th March 2011, 21:41
To confuse things a bit more :) Avery Lee will tell you that Virtualdub is not incorrect, since YV12 is progress by definition. What we call interlaced YV12 is actually interlaced 4:2:0. Not that the problem doesn't go away though :)
Considering one of the most common video standards in the world (DVD) calls for interlaced 4:2:0 for interlaced sources, how did anyone think it was a good idea to omit interlacing from the YV12 spec in the first place? Granted, interlaced 4:2:0 is a mess, but the "official" YV12 spec is pretty incomplete if it doesn't allow straight encoding to common end-user formats (without another colorspace conversion). All standard definition TV content is interlaced, 1080i content is interlaced, and for a while hard-telecining was popular for DVDs. (DVD players and conforming MPEG2/MPEG4-ASP/MPEG4-AVC software and hardware players do all use the same chroma placement for interlaced 4:2:0, right?)
IanB
7th March 2011, 23:07
@Mini-Me,
Avery is 100% correct in his interpretation of YV12. The "official" fourcc YV12 spec is a complete definition of the data it represents.
The definition used in Avisynth is somewhat of a misnomer, in hind site we probably should have called the initial planar format just "4:2:0" as that is the only attribute that is guaranteed. Everything else is the responsibility of the script author.
None of the interlaced example you quote are YV12. Avisynth lets you import any 4:2:0 format as what we call YV12 and do useful things with that data.
Mini-Me
7th March 2011, 23:44
@Mini-Me,
Avery is 100% correct in his interpretation of YV12. The "official" fourcc YV12 spec is a complete definition of the data it represents.
The definition used in Avisynth is somewhat of a misnomer, in hind site we probably should have called the initial planar format just "4:2:0" as that is the only attribute that is guaranteed. Everything else is the responsibility of the script author.
None of the interlaced example you quote are YV12. Avisynth lets you import any 4:2:0 format as what we call YV12 and do useful things with that data.
I understand that Avery is technically correct, because fourCC YV12 is always progressive, and industry 4:2:0 formats are not actually called YV12. It just seems shortsighted for people to have defined fourCC YV12 without a specified interlaced mode in the first place. AFAIK YV12 is meant to match up with progressive 4:2:0 end formats, but interlaced 4:2:0 is also so common that the computer world needed a standards-compliant interlaced 4:2:0 format just as badly as a progressive one. (Obviously I'm not alone in thinking this, since you guys created an ad hoc/de facto extension to YV12 to address it. ;))
I also understand that if Avisynth is fed 4:2:0 input, there's no way of telling the chroma placement, and it just has to accept the chroma planes it's given. What ultimately matters to me is knowing whether Avisynth's conversion from 4:2:2 to 4:2:0 uses the same chroma sampling as my end formats'. Confusion aside, I'm not really concerned with what Avisynth's (or MPEG-2's, or MPEG-4 AVC's) 4:2:0 formats are called; they could be identified by YV12 or HACK or KRZY, but the important thing is that Avisynth's 4:2:2->4:2:0 conversion places chroma samples at the same locations that MPEG formats and software/hardware players are expecting. It's probably only a small minority of Avisynth scripts that are actually affected by chroma placement, but the final display is always affected by the manner of upsampling, which makes it a larger issue. They do match, correct? (I'm asking for confirmation, but it seems logical that Avisynth would have defined its 4:2:0 placement - upon conversion - to match up with the placement defined by common end formats.)
Gavino
8th March 2011, 11:32
the important thing is that Avisynth's 4:2:2->4:2:0 conversion places chroma samples at the same locations that MPEG formats and software/hardware players are expecting. It's probably only a small minority of Avisynth scripts that are actually affected by chroma placement, but the final display is always affected by the manner of upsampling, which makes it a larger issue. They do match, correct? (I'm asking for confirmation, but it seems logical that Avisynth would have defined its 4:2:0 placement - upon conversion - to match up with the placement defined by common end formats.)
The default(*) conversions are defined to use MPEG-2 chroma placement for 4:2:0.
The implementation matches the definition, except for interlaced 4:2:0 to 4:2:2 (the problem identified by this thread).
(*)In 2.60, you can specify the chroma placement explicitly (but the implementation is wrong in the current release).
Mini-Me
8th March 2011, 12:37
The default(*) conversions are defined to use MPEG-2 chroma placement for 4:2:0.
The implementation matches the definition, except for interlaced 4:2:0 to 4:2:2 (the problem identified by this thread).
(*)In 2.60, you can specify the chroma placement explicitly (but the implementation is wrong in the current release).
That's what I expected, so thank you for confirming. :)
Mounir
25th March 2011, 18:16
After reading all these posts i can certify that i'm totally lost.
My goal is to convert yuy2 to yv12
Should i use YUY2ToYV12_26(interlaced=true) (IanB script)
... or a simple ConverttoYV12(interlaced=true)
Gavino
25th March 2011, 20:09
After reading all these posts i can certify that i'm totally lost.
My goal is to convert yuy2 to yv12
Should i use YUY2ToYV12_26(interlaced=true) (IanB script)
... or a simple ConverttoYV12(interlaced=true)
The chroma shift problem only occurs going the other way (interlaced YV12->YUY2).
For YUY2->YV12, there is just a potential problem of chroma aliasing, but for many (perhaps most?) sources a simple ConverttoYV12(interlaced=true) will produce acceptable results. If not, then use my function YUY2ToYV12_26(interlaced=true). (post #16 (http://forum.doom9.org/showthread.php?p=1471623#post1471623))
(IanB's changes in post #20 were only for YV12->YUY2 and in any case require a fixed 2.6 release.)
I'm assuming from your question that you are using v2.6 - otherwise you would remove "_26", ie use function YUY2ToYV12.
BTW I'm not surprised you're confused. Every time this problem comes up, I find I've forgotten the details and it takes me a while to get my head around it again. :(
Maybe a bit off-topic,
Will AutoYUY2() be a suitable replacement to YV12ToYUY2() ?
It is off topic.
AutoYUY2() would be an excellent replacement for YV12ToYUY2(interlaced=True) but not for straight YV12ToYUY2() [progressive mode].
It is an advanced filter that tries to determine "static" versus "in motion" areas of a frame. For static areas it uses "progressive style" chroma interpolation which has double the vertical resolution i.e. YV12ToYUY2(interlaced=False). For motion areas it uses "interlaced style" chroma interpolation which has the normal half vertical resolution i.e YV12ToYUY2(interlaced=True) but with no bug. So for interlaced conversion it is probably a win on 2 fronts, 1. no bug and 2. advanced chroma interpolation for static areas.
Mounir
3rd July 2012, 04:23
Today i have spotted a problem of chroma, please note i am using the v2.6.0.2 i'd like to know if this has been fixed or not in the latest avisynth release:
sample (yuy2, tff, interlaced, vhs):
http://www.mediafire.com/?716kzh9lacez3e5
I have spotted it because i did a ivtc (which i rarely do), there is chroma misplacement, it is ahead of 1 frame when i do the ivtc
the solution i use to fix that (note that i do yuy2>yv12>rgb32>yv12 conversions in most of my scripts):
avisource("...")
assumetff()
separatefields()
pointresize(last.width, last.height*2).converttoyv12(matrix="Rec601",interlaced=false).pointresize(last.width, last.height)
weave()
separatefields()
pointresize(last.width, last.height*2).ConverttoRGB32(matrix="rec601",interlaced=false).pointresize(last.width, last.height)
weave()
separatefields()
pointresize(last.width, last.height*2).converttoyv12(matrix="Rec601",interlaced=false).pointresize(last.width, last.height)
weave()
Note the placement of the separatefields() , must happen before the pointresize or it doesn't work otherwise(chroma still is ahead of 1 frame)
This way the result is lossless
Edit:
It seems i was wrong in a way, my capture has a strange fps (29.968 ) i don't know how that happened (!), hence the problem during the ivtc , the pointresizing don't hurt though
Any idea how i can have a 29.970 without re capturing, changefps do more harm than good it appears.
After the ivtc i end up with 23.974 fps, answer me in private 'cause it's off topic
zambelli
2nd December 2012, 20:26
I nominate this thread to be a sticky. :) Thanks for the education, guys.
real.finder
16th May 2015, 20:58
now in avs 2.6 ConvertToYV16(interlaced=true) is ok as I see, but ConvertToYV12(interlaced=true) with yuy2 not yv16 not fixed
Wilbert
16th May 2015, 21:21
now in avs 2.6 ConvertToYV16(interlaced=true) is ok as I see, but ConvertToYV12(interlaced=true) with yuy2 not yv16 not fixed
I have no idea what you mean here. Please elaborate. What is your source and what goes wrong?
Also if your source is anything else then YV12, the interlaced option won't do anything in ConvertToYV16(interlaced=true).
real.finder
16th May 2015, 21:42
I have no idea what you mean here. Please elaborate. What is your source and what goes wrong?
Also if your source is anything else then YV12, the interlaced option won't do anything in ConvertToYV16(interlaced=true).
I just do a test with Gavino script in 1st post
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)
and edit it like this
function Convert(clip c, int times) {
times == 0 ? c : c.ConvertToYV16(interlaced=true).ConvertToYV12(interlaced=true).Convert(times-1)
}
ColorBars(pixel_type="YV12").TurnRight()
Animate(0, 10, "Convert", 0, 10)
and of course I also do an other edits as an experiment
and then compared the result with his YV12ToYUY2i and YUY2ToYV12i functions
and since the conversion between yuy2 and yv16 is lossless, we can do ConvertToYV16 then ConvertToYV12(interlaced=true) if we have an interlaced yuy2 source and we want converted to yv12
and in opposite case ConvertToYV16(interlaced=true) then ConvertToYUY2
Wilbert
16th May 2015, 22:30
I see. It's a bit late to fix this now for 2.60 ;( The reason that yuy2 and yv16 produce difference results is that they take different code paths. I wonder what kernel is used by YV16<->YV12. Must be tUsubSampling[4] and bUsubSampling[4] in ConvertToPlanarGeneric. Anyone?
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.