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. |
6th April 2013, 08:05 | #1 | Link |
Registered User
Join Date: Feb 2002
Location: California
Posts: 2,691
|
Bad 1950s Kinescope - Hopeless?
This is one of the few Kinescopes of an actual American NFL football TV broadcast from the 1950s.
1955 NFL TV Kinescope The clip posted here is a simple MPEG-2 cut from the DVD I received from the owner. Unfortunately, this particular video to film transfer was not done with a true Kinescope setup: it was probably done by someone with a lot of money (film was expensive!!) simply filming their TV set. As a result, the shutter on the film camera was not synced to the TV screen, and the result is noise bars. These happen when the shutter on the film camera is closed during the period when the film is advanced. This is a typical frame: If you download the short (6MB) video clip, you'll see that it looks much worse than the still frame. I don't think this can be fixed, because nothing repeats exactly. However, because of the unique and valuable nature of this film, I thought I'd ask the experts here if they can suggest any approach that might reduce the noise bars. Thanks! ====================== [edit]Here is a link to the post, later in the thread, where I show an approach worked, and did solve the problem: Mask Code For Correcting Noise Bars Last edited by johnmeyer; 30th October 2019 at 23:59. Reason: Provide link to post that shows my "solution"; later, replace Photobucket image |
7th April 2013, 01:17 | #3 | Link |
Registered User
Join Date: Feb 2002
Location: California
Posts: 2,691
|
Deinterlacing Kinescopes can be a little tricky, but I have done that successfully. I think I can do that here as well. However, I don't know whether those wide horizontal noise bars will be diminished much by a temporal denoiser, but I guess I should try, and then see what happens.
|
7th April 2013, 09:36 | #4 | Link |
Avisynth Developer
Join Date: Jan 2003
Location: Melbourne, Australia
Posts: 3,167
|
Okay, we have old true 60i filmed at 24fps unsynced, then presented here as hard 3:2 telecine into 29.97i
First analyse the stream. Code:
Mpeg2Source("Bad Kinescope.d2v") Bob() Analyse for correct phase. Code:
Mpeg2Source("Bad Kinescope.d2v") BicubicResize(640, 480) # Fit on 1920 display DoubleWeave() a = Pulldown(0,2).Subtitle("0,2") b = Pulldown(1,3).Subtitle("1,3") c = Pulldown(2,4).Subtitle("2,4") d = Pulldown(0,3).Subtitle("0,3") e = Pulldown(1,4).Subtitle("1,4") ShowFiveVersions(a,b,c,d,e) Check for soft or hard telecine. Code:
Mpeg2Source("Bad Kinescope.d2v") BicubicResize(640, 480) ShowFrameNumber(True) DoubleWeave() c = Pulldown(2,4).Subtitle("2,4") e = Pulldown(1,4).Subtitle("1,4") StackHorizontal(e, Subtract(e, c).Levels(120, 1, 136, 0, 255), c) Thus we can get back the original 24fps progressive film frames :- Code:
Mpeg2Source("Bad Kinescope.d2v") DoubleWeave() c = Pulldown(2,4) e = Pulldown(1,4) Merge(e, c) The bright lines are where the TV raster was actually scanning when the shutter was open, the rest of the less bright image is the CRT persistence. With enough dedication this can be repaired. |
7th April 2013, 12:49 | #5 | Link |
Avisynth Developer
Join Date: Jan 2003
Location: Melbourne, Australia
Posts: 3,167
|
Well the bright pattern appears to be actually a 7 frame cycle :-
Code:
Mpeg2Source("Bad Kinescope.d2v") DoubleWeave() c = Pulldown(2,4) e = Pulldown(1,4) Merge(e, c) BicubicResize(480, 480) A=SelectEvery(7, 0) B=SelectEvery(7, 1) C=SelectEvery(7, 2) D=SelectEvery(7, 3) E=SelectEvery(7, 4) F=SelectEvery(7, 5) G=SelectEvery(7, 6) H=BlankClip(G) X=StackHorizontal(A,B,C,D) Y=StackHorizontal(E,F,G,H) StackVertical(X,Y) With this knowledge you can build a suitable mask clip with the same rolling pattern and correct the bright strips. From the sample there appears to be some jitter in the bright strip position so the mask clip may need some manual tweaking to keep it in sync with the film. Other may have some ideas how to better analyse the bright strip pattern timing. Points to remember are that original TV display would have been 60 interlaced fields of 243 picture lines with 19.5 blanking lines each second, or 15750 lines per second. A 24 fps frame period would be 656.25 line periods. |
8th April 2013, 16:50 | #6 | Link |
Registered User
Join Date: Feb 2002
Location: California
Posts: 2,691
|
Interesting examples in how to deconstruct the video back to 24fps. Quite different than how I was doing it, and very instructive.
How to make a moving mask that will follow the rising noise bar in the recovered film frames is still the challenge that stumps me. I'll work on this later today and see if I can come up with some detection ideas. |
8th April 2013, 18:17 | #7 | Link |
Registered User
Join Date: Feb 2002
Location: California
Posts: 2,691
|
Here is my current thinking:
1. Do IVTC. TFM/Tdecimate() seems to work fine. 2. Using the resulting 24 fps frames, create an estimated frame using the previous and next frame. Here's code for that: Code:
super = MSuper(source) backward_vectors = MAnalyse(super, isb = true, delta=2) forward_vectors = MAnalyse(super, isb = false, delta=2) inter = MFlowInter(source,super, backward_vectors, forward_vectors, time=50, ml=70) It is step #3 that has me completely stumped. I think the general idea is correct (although I need to come up with better code for the estimation in #2 above). The general idea is that if the current frame contains the bright horizontal bar, then in almost all cases the immediately preceding frame does not have that bar, nor does the next frame. Thus, if I estimate the current frame using these two adjacent frames, there should be no bright horizontal bar in the estimated frame. Therefore if I can somehow use some sort of blurred difference between the actual frame and this estimated frame, and do this in such a way as to ignore all the high frequency small details, and only keep the difference across the horizontal bar, then I can use that mask to decrease the gamma for that horizontal stripe. Last edited by johnmeyer; 8th April 2013 at 18:57. Reason: Changed code ten minutes after original post |
9th April 2013, 05:52 | #8 | Link |
Avisynth Developer
Join Date: Jan 2003
Location: Melbourne, Australia
Posts: 3,167
|
@johnmeyer,
Nice idea but the bright patches screw the frame motion interpolation. Of the 3 frame involved 1, 2 or 3 always have a bright patch. Great if the ones before and after a bright bar free, but that seems to only occur once every 7 frames. If you imagine a canvas 525 lines high, 480 lines of picture plus 45 extra lines of fly-back blanking then somewhere during the canvas scan time will be the camera shutter open time, which corresponds to the bright bar. Whenever the shutter is open during the fly-back blanking then you see no bright bar. I have been analysing your clip line by line looking for the brightest pixel on each line and there is moderate to good correlation to the bright bars, but the problem I have is that the overall image brightness varies with screen location. The left side has a dark region, the top is darker than the middle and the bottom is slightly darker than the middle and brighter than the top. This script adds a 48 pixel stripe to the end of each line corresponding to the brightest pixel in that line. The number written at the top of the strip is the maximum of all the brightest. The script is god awfully slow. Code:
Mpeg2Source("Bad Kinescope.d2v") ConvertToY8() DoubleWeave() c = Pulldown(2,4) e = Pulldown(1,4) Merge(e, c) Orig=Spline16Resize(428, 480) SeparateRows(480) ScriptClip("BlankClip(Last, Color_YUV=$8080+$10000*YplaneMax())") Crop(0, 0, 48, 0) WeaveRows(480) ScriptClip("SubTitle(String(YplaneMax()))") StackHorizontal(Orig, Last) A=SelectEvery(7, 0) B=SelectEvery(7, 1) C=SelectEvery(7, 2) D=SelectEvery(7, 3) E=SelectEvery(7, 4) F=SelectEvery(7, 5) G=SelectEvery(7, 6) H=BlankClip(G) X=StackHorizontal(A,B,C,D) Y=StackHorizontal(E,F,G,H) StackVertical(X,Y) |
9th April 2013, 07:01 | #10 | Link |
Avisynth Developer
Join Date: Jan 2003
Location: Melbourne, Australia
Posts: 3,167
|
Okay using the average of the peak brightness's from the next and previous frames as a base brightness seems to work very well, I suspect using the min of the 2 might work even better, but we don't have a tool to hand. :-
Code:
Mpeg2Source("Bad Kinescope.d2v") ConvertToY8() DoubleWeave() c = Pulldown(2,4) e = Pulldown(1,4) Merge(e, c) Orig=Last SeparateRows(480) ScriptClip("BlankClip(Last, Color_YUV=$8080+$10000*YplaneMax())") Crop(0, 0, 48, 0) WeaveRows(480) Blur(0, 1) Next=DeleteFrame(0) Prev=DuplicateFrame(0).FreezeFrame(0,0,3) Subtract(Merge(Prev, Next)) # Min(Prev, Next) might work better if we had such... ScriptClip("SubTitle(String(YplaneMax()))") Levels(137, 2, 150, 32, 235, False) StackHorizontal(Orig, Last) Next trick is to turn the bright line mask into a noise resistant bright bar correction. Rules need to be something like, when we have a 35 to 45 line mask area correct the matching lines centred on the mask. |
10th April 2013, 10:32 | #11 | Link |
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
|
I've knocked up a plugin to create the YPlaneMax band:- (filters for both Avisynth v2.58 & v2.6)
Link Removed, See this thread: http://forum.doom9.org/showthread.php?t=167663 script mod Code:
MPEG2Source("D:\AVS\Bad Kinescope.d2v") ConvertToY8() DoubleWeave() c = Pulldown(2,4) e = Pulldown(1,4) Merge(e, c) Orig=Spline16Resize(428, 480) #SeparateRows(480) #ScriptClip("BlankClip(Last, Color_YUV=$8080+$10000*YplaneMax())") #Crop(0, 0, 48, 0) #WeaveRows(480) # threshold as for YPlanemin/max/minmaxdiff, Matrix=PC601: Zebra creates 4 bands, Left=YPlaneMin,YPlaneMax,YPlaneMinMaxDifference,YPlaneMedian Zebra(threshold=0.0,matrix=2).Crop(48,0,48,0) # Crop Selects 2nd band, YPlaneMax (Bands are 48 pixels wide) ScriptClip("SubTitle(String(YplaneMax()))") StackHorizontal(Orig, Last) A=SelectEvery(7, 0) B=SelectEvery(7, 1) C=SelectEvery(7, 2) D=SelectEvery(7, 3) E=SelectEvery(7, 4) F=SelectEvery(7, 5) G=SelectEvery(7, 6) H=BlankClip(G) X=StackHorizontal(A,B,C,D) Y=StackHorizontal(E,F,G,H) StackVertical(X,Y) convertToRGB32() and Code:
Mpeg2Source("Bad Kinescope.d2v") ConvertToY8() DoubleWeave() c = Pulldown(2,4) e = Pulldown(1,4) Merge(e, c) Orig=Last #SeparateRows(480) #ScriptClip("BlankClip(Last, Color_YUV=$8080+$10000*YplaneMax())") #Crop(0, 0, 48, 0) #WeaveRows(480) # threshold as for YPlanemin/max/minmaxdiff, Matrix=PC601: Zebra creates 4 bands, Left=YPlaneMin,YPlaneMax,YPlaneMinMaxDifference,YPlaneMedian Zebra(threshold=0.0,matrix=2).Crop(48,0,48,0) # Crop Selects 2nd band, YPlaneMax (Bands are 48 pixels wide) Blur(0, 1) Next=DeleteFrame(0) Prev=DuplicateFrame(0).FreezeFrame(0,0,3) Subtract(Merge(Prev, Next)) # Min(Prev, Next) might work better if we had such... ScriptClip("SubTitle(String(YplaneMax()))") Levels(137, 2, 150, 32, 235, False) StackHorizontal(Orig, Last) ConvertToRGB32() Code:
Zebra(clip c, Float "threshold"=0.0,int "matrix"=2,bool "Row"=true) # Avisynth v2.5/v2.6 Plugin by StainlessS Creates a 4 bar clip in two modes, Row=True, creates a set of 4 bars 48 pixels wide per bar and same height as source clip. Each horizontal pixel row in the bars represents the contents of the horizontal rows of the source clip. Row=False, creates a set of 4 bars 48 pixels tall per bar and same width as source clip. Each vertical pixel column in the bars represents the contents of the vertical columns of the source clip. The 4 bands it creates are (from left to right if Row==true and top to bottom if Row==false) 1 ) YPlanemin 2 ) YPlanemax 3 ) YPlaneminmaxDifference 4 ) YPlaneMedian Threshold used for bands 1, 2, and 3 as for eg YPlaneMin (to avoid extreme pixels, ie noise). Matrix,(For RGB -> Luma-Y conversion, not used in YUV modes) 0) = rec601 1) = rec709 2) = PC601 3) = PC709 Zebra.dll is for Avisynth v2.58 and Zebra26.dll is for Avisynth v2.6. The v2.58 version supports Y8 in avisynth v2.6. Running version v2.6 on Avisynth v2.58, will not find a plugin named Zebra. EDIT:- AvsMeter shows about 9 FPS for 1st script, and 63 FPS for 2nd script.
__________________
I sometimes post sober. StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace "Some infinities are bigger than other infinities", but how many of them are infinitely bigger ??? Last edited by StainlessS; 12th April 2013 at 22:50. |
11th April 2013, 02:38 | #12 | Link |
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
|
New Version Zebra v1.01, Previous Post updated.
__________________
I sometimes post sober. StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace "Some infinities are bigger than other infinities", but how many of them are infinitely bigger ??? |
11th April 2013, 04:54 | #14 | Link |
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
|
It looks to me as if the bands are slanted from top left to bottom right.
Might like to chop clip in half and have zebra bars showing on both left and right to gauge the angle. Perhaps David Horman's plugs will come in handy.
__________________
I sometimes post sober. StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace "Some infinities are bigger than other infinities", but how many of them are infinitely bigger ??? |
11th April 2013, 18:42 | #16 | Link |
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
|
I was thinking about modifying the RT_YInRangeLocate func for this, if baffle (number of scanlines that must break thresh)
exceeds the number of scanlines (width or height of area to examine) then the Baffle for that axis is limited to the number of scanlines on that axis, could do this without changing args. Should allow use here to find the bands. Think I'll do that presently.
__________________
I sometimes post sober. StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace "Some infinities are bigger than other infinities", but how many of them are infinitely bigger ??? |
11th April 2013, 19:42 | #17 | Link |
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
|
Here RT_Stats with RT_YInRangeLocate() mod, not tested at all, but trivial mod. YInRangeLocate.avs
also modded. Have to go out now, thought you may like to test out. LINK REMOVED See sig for download. You could set eg width to examine to 1 pixel wide, and height to full height and baffle to eg minimum acceptable band height. think it should work.
__________________
I sometimes post sober. StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace "Some infinities are bigger than other infinities", but how many of them are infinitely bigger ??? Last edited by StainlessS; 12th April 2013 at 18:40. |
11th April 2013, 22:30 | #18 | Link | |
Registered User
Join Date: Feb 2002
Location: California
Posts: 2,691
|
Quote:
No need to spend time on this. I easily got the Zebra function to work: and I'm now working on understanding the RT_Stats DLL. I need to go look at the original AVS scripts on which the DLL is based. This is definitely beginning to look possible. Thanks so much for spending the time on this. Last edited by johnmeyer; 31st October 2019 at 00:01. Reason: removed comment about mask in Vegas; later, replace Photobucket image |
|
12th April 2013, 00:51 | #19 | Link |
Registered User
Join Date: Feb 2002
Location: California
Posts: 2,691
|
Getting close, but may need a little more help.
I apply this test script fragment to the IVTC'd 23.97 version of the video: Code:
source=AVISource("e:\fs.avi") p=selectevery(source,1, -1) #Previous frame Z1=Zebra(source,threshold=100,row=true) Z2=Zebra(p,threshold=100,row=true) output=Subtract(Z1,Z2).crop(144,0,0,0) stackhorizontal(source,output) I think I am two steps away from making this work. Here's what I think I need to do: 1. When the luma values in the comparison column are >128 for more than eight consecutive rows, construct a mask, the width of the original frame, and the height of that block. The beginning of the block [row(n)] will be the first row where the following averageluma condition is met: row(n) >128 row(n+1) >128 ... row(n+7) >128 The last row is determined in similar fashion. 2. Apply this mask with a gain factor derived from the averageluma of the bright block in the comparison column divided by the averageluma of the eight rows (or whatever number of rows seems to work) immediately above or below (depending on whether the bright block is in the upper or lower half of the screen). I'm pretty sure I could eventually figure out both of these tasks, but I would appreciate a few hints. This is especially needed for #2 because I think the mask needs to be "feathered" on the top and bottom edges. An alternative to doing everything in AVISynth would be to simply get the Y coordinates for the top and bottom of the block in #1, and export to a text file those two coordinates (assuming one horizontal band per frame), along with the current frame number. I can then create a series of PNG masks which I can import to Vegas and do the masking there. It would be a lot easier to do it all in AVISynth, however, if I can figure out the solution to at least these two issues. [edit]I'll also have to add some noise averaging code so that flicker that is not related to the noise bar doesn't harm the process. Last edited by johnmeyer; 31st October 2019 at 00:02. Reason: Removed next frame from code because it wasn't being used; added comment about noise averaging; later, replace Photobucket im |
12th April 2013, 03:55 | #20 | Link |
Avisynth Developer
Join Date: Jan 2003
Location: Melbourne, Australia
Posts: 3,167
|
We have a prototype for bright bar detection, that is surprisingly resilient. On the short sample I have it only has slight trouble with the few frames that are free of a bright bar (false positive). Analysing the occurrence pattern and using it for guidance would eliminate this, i.e. from the cycle and jitter we expect the next bright bar to be between lines 200 and 260, if we get a 225 it's okay, if we get a 142 its a false hit or a break in the cycle or a scene/reel change. Whatever it is it needs to be analysed. Also remember the cycle space is 525 lines not 480 because of the vertical blanking period. Dumping the positions to a file and loading it into Excel could be very illuminating.
Correcting the brightness variation versus screen position will probably help the detection algorithm a great deal. You probably want to do this as part of your restoration effort anyway. Do you have any scenes that are uniformly highly bright eg ball in the sky, or an advert graphic. We just need 1 frame were the content is an expected uniform brightness, you can hand paint out the ball or any small splotches. Having the screen brightness unified should also make the white bar correction much easier. Assuming such the mask becomes position independent and we just position it between -45 and +525. As we are at present we effectively need 570 different correction masks. Unfortunately because the image is slightly rotated counter clockwise, applying simple line brightness correction based on the detection values is not really possible. That said I will investigate rotating the sample to see what can be achieved. .... |
Thread Tools | Search this Thread |
Display Modes | |
|
|