View Full Version : Bad 1950s Kinescope - Hopeless?
johnmeyer
6th April 2013, 08:05
This is one of the few Kinescopes of an actual American NFL football TV broadcast from the 1950s.
1955 NFL TV Kinescope (http://dl.dropbox.com/u/1561578/Bad%20Kinescope.mpg)
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:
https://i.imgur.com/IGeWK0t.jpg
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 (http://forum.doom9.org/showthread.php?p=1625400#post1625400)
creaothceann
7th April 2013, 00:27
The picture is interlaced, so assuming the source was progressive means that it can be deinterlaced with AnimeIVTC. Then... try adding a temporal denoiser?
johnmeyer
7th April 2013, 01:17
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.
IanB
7th April 2013, 09:36
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.Mpeg2Source("Bad Kinescope.d2v")
Bob()Recognised as 3:2 telecine material.
Analyse for correct phase.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)Recognise 1,4 and 2,4 as the two correct phase results.
Check for soft or hard telecine.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)Yes the 2 correct phase results are slightly different, indicating hard telecine (okay DGIndex already told me this).
Thus we can get back the original 24fps progressive film frames :-Mpeg2Source("Bad Kinescope.d2v")
DoubleWeave()
c = Pulldown(2,4)
e = Pulldown(1,4)
Merge(e, c)
Here we can see from the bright bars cycling on an approximate 5 frame pattern that this was a movie camera stuck in front of a TV. Also the motion non-fluidity is typical of 60i sampled at 24p.
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.
IanB
7th April 2013, 12:49
Well the bright pattern appears to be actually a 7 frame cycle :-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)For each seventh frame we can see the bright region is in roughly the same place. Well it actually creeps upwards slightly each cycle, but that is to be expected because the original camera was not synced to the original TV.
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.
johnmeyer
8th April 2013, 16:50
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.
johnmeyer
8th April 2013, 18:17
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:
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)
3. Create a mask using some blurred difference between the actual current frame and the estimated ("inter") frame. Do this difference in such a way that only the luma differences are used, and such a way that the average across the entire horizontal raster line is used.
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.
IanB
9th April 2013, 05:52
@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.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)
The bright bar region is at least 15 units brighter than in the adjacent frames, but the positional brightness variation is least 20 units.
johnmeyer
9th April 2013, 06:06
It's late at night here, and I'll be gone all day tomorrow, but on Wednesday I'll try your script and see where that takes me. Thanks!!
IanB
9th April 2013, 07:01
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. :-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)This really needs a plugin to do the SeparateRows/ScriptClip/WeaveRows max luma per line work.
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.
StainlessS
10th April 2013, 10:32
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
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
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()
and description
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.
LINK DELETED
EDIT:- AvsMeter shows about 9 FPS for 1st script, and 63 FPS for 2nd script.
StainlessS
11th April 2013, 02:38
New Version Zebra v1.01, Previous Post updated.
johnmeyer
11th April 2013, 03:31
Stainless & IanB,
Wow, I can't wait to try this out tomorrow. Sorry for the delay in responding, but I was out for a day and then spent all day today catching up. Your code looks very cool.
John
StainlessS
11th April 2013, 04:54
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.
IanB
11th April 2013, 07:18
Once the bright bar was located, I was sort of thinking aligning and applying a hand draw mask to the area in question, assuming the bright bar is a fixed geometry.
StainlessS
11th April 2013, 18:42
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.
StainlessS
11th April 2013, 19:42
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.
johnmeyer
11th April 2013, 22:30
It looks to me as if the bands are slanted from top left to bottom right.I can fix that outside of AVISynth. My editing program (Vegas Pro) can easily rotate the clip, and I can interactively use the display in that program to ensure that the picture (and noise bars) are exactly horizontal.
No need to spend time on this.
I easily got the Zebra function to work:
https://i.imgur.com/vRTYQD0.jpg
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.
johnmeyer
12th April 2013, 00:51
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:
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 then get this:
https://i.imgur.com/RkjjcAx.jpg
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.
IanB
12th April 2013, 03:55
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. ....
johnmeyer
12th April 2013, 04:58
I'll post a link tomorrow (it's late here) to a properly rotated image, with the noise bars perfectly horizontal.
A portion of the later part of the film has more uniform exposure, measured from left to right. I'll see if I can find a punt or kickoff where the camera pans towards the sky.
StainlessS
12th April 2013, 13:01
This kind of works, have not tweaked it, just first guess.
AVISource("D:\AVS\BadKinescope.AVI")
#Mpeg2Source("Bad Kinescope.d2v")
#ConvertToY8()
ConvertToYV24()
#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)
ScriptClip("""
Bingo = RT_YInRangeLocate(x=width/2,w=1,lo=128,hi=255,baffle=24)
(Bingo) ? RT_Debug(String(Current_frame)+"]",RT_StrPad("Y="+String(YIRL_Y),8),RT_StrPad("H="+String(YIRL_H),8)) : \
RT_Debug(String(Current_frame)+"] NOT FOUND")
(Bingo) ? Overlay(Last.BlankClip(Width=4,height=YIRL_H,color=$FF0000),x=48/2-2,y=YIRL_Y) : NOP
Return Last
""")
StackHorizontal(Orig, Last)
ConvertToRGB32()
EDIT:
now working on understanding the RT_Stats DLL. I need to go look at the original AVS scripts on which the DLL is based.
I developed the YInRangeLocate algorithm specifically for PlanetCrop, it had been using a naive
method to locate planet, scanning sequential lines.
New algo, based on fact that if a minimum of scanlines that have to pass (Baffle) is eg 8, and you
examine 7 scanlines and it fails on the 8th, then you wasted your time examining the first 7.
It jumps through image, Baffle scanlines at a time until it find a scanline that fulfills
requirements and then scans backwards. If all 8 scan then done, but if not, then moves End Cursor
Baffle-1 lines higher than lowest scanline that past OK, and starts scanning downwards from there.
I'm guessing that it would take more than one reading to understand what it does, it took some time
to get correct and I think may be quite optimal, is many times faster than original naive method.
I'm guessing that a similar algo was already in existence, perhaps in string pattern matching where
pattern string is composed of all same character.
johnmeyer
12th April 2013, 16:44
Here's a frame that is more uniform in exposure from the left to right edge than any frame in the entire 30 minute film/video:
https://i.imgur.com/Mz5jM6Y.jpg
This has been rotated (1.7 degrees) so that both ends of the noise bar line up horizontally. I also applied a non-uniform gamma correction (using "Color Curves" in Vegas) so the gamma is closer to normal. I did not correct for PAR, so the 720x480 image is stretched horizontally. Feel free to re-size to 656x480 if this is a problem.
And, here is a link to a 9 MB 23.976 AVI clip (DV format) of a few frames from this same section:
Football Noise Bars #2 (https://dl.dropboxusercontent.com/u/1561578/1955%20Football%2002.avi)
This has been IVTC'd using TFM/TDecimate, so no need to perform that step on this clip.
Hopefully I can figure out the last step (creating and applying the exposure mask), although for some reason I'm being really dense on this project. Brain cells start to rot when you get north of sixty ...
johnmeyer
12th April 2013, 20:59
Well, I've made some progress, but it's not good enough.
I couldn't figure out how to do the mask inside of AVISynth, so I used the output of the Zebra/Subtract code I posted before, resized back up to 720x480, and created a video, the same length as the original video, consisting of mask bars. I put this in Vegas and used the mask functions there to create a mask from the horizontal stripes that result from Stainless' detection code. I couldn't find any blur function in AVISynth that would properly blur in the vertical direction (I tried quite a few, and blur(0,1), even used multiple times, doesn't cut it), but Vegas' Median filter works great. Here is one frame from the mask video, after it has been blurred and manipulated in Vegas:
https://i.imgur.com/szJopiS.jpg
The good news is that the mask tracks the noise bars quite well and is roughly the correct height. The bad news is that I cannot figure out how to get the intensity of the mask to track the intensity of the noise bar. The problem is that some noise bars are brighter than others, and the result of the masking operation needs to be more powerful for those brighter bars.
I suppose I could try to do the masking in multiple steps.
I'm also having a little trouble getting edges of the mask to feather properly, but that is something I have to solve in Vegas, assuming no one has a good idea of how to do all this within AVISynth.
Here's a frame from the original video:
https://i.imgur.com/4cdIXMz.jpg
and here's the same frame after the automatic masking:
https://i.imgur.com/nFfbaVX.jpg
This obviously looks better, but since the intensity still varies quite a bit, and because the feathering at the edge of the mask is still not optimal, the resulting video still flickers a lot.
Good progress, but more to do ...
StainlessS
12th April 2013, 22:37
Added two more bands to Zebra, Here:-
http://forum.doom9.org/showthread.php?t=167663
Looks like your getting closer, nice.
johnmeyer
12th April 2013, 22:53
Added two more bands to Zebra, Here:-
http://forum.doom9.org/showthread.php?t=167663
Looks like your getting closer, nice.Thanks for all the help. Yes, I'm getting close, and I may be able to "sooth" the residual flicker using MDegrain with DCT<>0.
My front-load washing machine is broken and the bearings just arrived, so I have a "honey-do" that is going to take a few hours and that must be done now. I'll get back to this as soon as I'm finished.
StainlessS
12th April 2013, 23:19
Perhaps chop out the area of a located band, and generate row=false horizontal bars to adjust your mask, maybe.
EDIT: Maybe using Average plugin on horizontal bands.
EDIT: Above probably rubbish.
StainlessS
14th April 2013, 16:28
The bad news is that I cannot figure out how to get the intensity of the mask to track the intensity of the noise bar.
You do know that you can sample the located noise bar in any of the zebra bars using RT_Stats Y type funcs (used in the zebra bars eg RT_AverageLuma).
Something like:-
Adjust=ZebraRows.RT_AverageLuma(x=2*48 + 48/2,w=1,y=YIRL_Y,h=YIRL_H)
to select the middle of the YPlaneMinMaxDifference band, the height of the
noise bar located by RT_YInRangeLocate().
Same for other Zebra bands if more appropriate.
Or you could also use any of the other RT funcs with same coords.
and apply Adjust somehow to your mask.
EDIT: The RT funcs can be used to single pixel resolution, so you could even scan in a loop from top
of located noise bar to bottom of located noise bars to have adjustments for every raster line,
although it would not matter much which func you chose, ie min and max would be same for single pixel,
but RT_AverageLuma would be at least twice as fast (probably more) as other RT funcs as it is handled as
a special case and does not use a count array (In the common RT_YStats func, used for low level Y funcs).
If you wanted to sample a single located noise bar for multiple Y funcs simultaneously then look at RT_YStats.
johnmeyer
16th April 2013, 01:39
Stainless,
I haven't forgotten about you or this project. I keep getting interrupted by other projects, and now I am leaving town for three days. I know you spent a lot of time on this, and I am going to use all of what you've done, but life is intruding ...
StainlessS
16th April 2013, 04:43
C'est la vie
johnmeyer
23rd April 2013, 17:04
The following is a reply to this thread:
Zebra v1.03, Test clip generator (http://forum.doom9.org/showthread.php?p=1623921#post1623921)
but it is more proper to post here, since I am talking about solving the problem that is the original subject of this thread.
John, changed my mind about doing an additional CMAD func in zebra, would be just as good
to just crop out the required Zebra band and resize to full frame, results should be just the same,
even pointresize should make no difference.When I got back to this yesterday, I re-read all your posts and realized you'd added the cmad function in your latest build of Zebra. I was using an earlier version, so I hadn't been using that. CMAD does exactly what I need, and I'm getting much better results using a fairly high cmad value (25) and lots of blur. It helps feather the mask and this will make the exposure correction less likely to exhibit transition lines between the uncorrected rows and the exposure-compensated rows.
I have added code to motion compensate the previous frame prior to creating the difference frame ("subtract"). This obviously improves the results somewhat.
My only remaining refinement is to figure out if I can somehow "modulate" the mask so that it is less strong on the right side than the left side. To do that, I need to create a clip by cropping the difference clip to include only the rows where the average difference, after all the filtering, etc., is greater than 128, and then produce a true mask that is shaded, left to right, so that it changes the exposure more strongly on the right side than on the left. I don't know if I can do that or not, but am re-reading Stainless' April 14 post above to see if I can adapt his suggestions.
Here's a pic showing the difference clip on the left, and all six columns of the Zebra function on the right. As you can see, the YPlainMedian result from the difference clip produces an excellent, feathered mask result. I can simply re-size that to the width of the original, and use that to modulate the exposure, but as the arrow on the left side of the picture below shows, it would be better if I could make that mask less strong on the left side of this particular frame.
https://i.imgur.com/AnCyqMQ.jpg
I'll put in a few more hours on this today, as time permits, and the post my results.
johnmeyer
23rd April 2013, 21:59
OK, I've got it all working. I can post an actual video clip of the results, if anyone wants, but in the meantime, here is the simple code that creates the mask (it is simple because Stainless did all the work):
# Create mask that matches white noise bar
# ZebraTest.avs, by StainlessS
Orig = AVISource("e:\fs.avi").ConvertToYV12()
Cropped = Orig.crop(156,0,-48,0) #Get rid of left/right portion of TV screen bezel, especially the left side, where it is dark
# Create estimated clip (temporarily triples number of frames)
mdata = DePanEstimate(Cropped)
Estimated = DePanInterleave(Cropped, data=mdata)
Prev = selectevery(Estimated,1,-1)
# Create difference frame (will be pure grey, if frames are identical)
Subtract(Estimated, Prev)
# Eliminate both "before" and "after" estimated frames, and apply lots of vertical blur
Noise_Mask=SelectEvery(3, 1).Blur(1,0).Blur(1,0).Blur(1,0).Blur(1,0).Blur(1,0)
# Create mask by using the YInRange plane of the Zebra function
# Only use pixels that are brighter than gray (i.e., where there is a white noise bar in this frame, but not in previous frame)
# The "lo=128" will ignore black bars created when there is a noise bar in the previous frame, but not in current frame
# Scale everything so result is black except where there is a noise bar. Crop to keep just the YInRange band
# Mask height is VERY sensitive to "lo" parameter. 128 creates large mask, whereas 133 creates a very small mask.
# First value in Levels also affects mask. Larger values makes mask smaller and "tighter."
# CMAD parameter changes both mask size, and smoothing (feathering).
Zebra(Noise_Mask,threshold=0.0,matrix=1,lo=128,hi=255,cmad=23).Crop(240,0,0,0) # Crop Selects 2nd band, YPlaneMax (Bands are 48 pixels wide)
Levels(143, 1, 255, 15, 235, False)
# Use following line to view mask next to difference frame
#stackhorizontal(Noise_Mask,last)
# Use following line to view mask next to original, cropped frame
#stackhorizontal(Cropped,last)
# Create mask
LanczosResize(720,480)
Here is a typical mask frame, created with the above code:
https://i.imgur.com/jjr2uQR.jpg
Here is the original frame of film from which that mask was created. Note the horizontal white noise bar just below the center of the screen:
https://i.imgur.com/WwTLLgV.jpg
And here is the same frame after using the mask to apply gamma correction just to the noise bar, using Vegas to do the masking and correction (within Vegas, I can use a mask to apply any transform or effect, and can change the power of the effect, and feather the effect, until I get just what I want):
https://i.imgur.com/hxR5Nyt.jpg
johnmeyer
24th April 2013, 00:54
Here's a link to show everyone what the video looked like before, and what it looks like after the restoration.
http://www.youtube.com/watch?v=qx26T6WOZ_4
Many thanks, Stainless!!
If someone were paying me for this, I think I could remove all traces of the original flicker by modifying the mask, making it different for each scene. This is straightforward to do in Vegas. This would take less than half an hour, but I didn't want to spend any more time. I could also have removed the film dirt, but since the film is such low quality to begin with, I didn't think it worthwhile to take that extra step. There is also a dirt spot that shows up in most frames that I could remove using Delogo.
poisondeathray
24th April 2013, 01:13
Incredible work guys!
johnmeyer
24th April 2013, 20:47
I mentioned that I used Sony Vegas to apply the mask created by Stainless' Zebra function. I thought it might be useful to provide a link to the thread in the Sony forum where I describe how that works:
Point Camera at TV; Get Flicker; How I Eliminated (http://www.sonycreativesoftware.com/forums/ShowMessage.asp?MessageID=857495)
If you are not a member of the Sony forum, the embedded images in the post won't show up. Here is the screenshot of the Vegas timeline from that post:
https://i.imgur.com/LhYhOOH.jpg
vBulletin® v3.8.11, Copyright ©2000-2025, vBulletin Solutions Inc.