Log in

View Full Version : U-V-Swap issue


Frank62
11th September 2018, 13:32
Hi,

I got copies from older tape recordings (1'') of a series. About ten to 30 times in more than one episode a problem with the colours occurs: Obviously U and V are swapped.

I first thought I could build a detector for this, seemed not too hard because the right black margin expands a few pixels each time the swapping occurs. So far so good, BUT:

The channels are not swapped in the whole frame. Only rectangular parts of the picture, the rest keeps correct.

Has anyone an idea how to build a mask that is transparent only where UV swapping occurs and opaque on the rest of the picture?

Thanks for any idea.

StainlessS
11th September 2018, 13:51
I'm sure that somebody would like to take a gander at your peculiar clip [if only for the 'wierd clip collection'], maybe post a sample,
no idea how to do as you require, sorry.

Frank62
11th September 2018, 14:05
https://s33.postimg.cc/wb1cxys8v/Area_UVSwap.jpg

https://s33.postimg.cc/jjn6rgar3/Area_UVSwap2.jpg

As you can see the areas also change, that's the problem.

StainlessS
11th September 2018, 14:19
Is the block 'mirrored' on one of the preceding/postceding frames ? (are you getting chroma from the wrong frame, is it at scene change).

Post a bit of the clip if you can (not just an image).

EDIT: Also try another source filter, and try load into eg VDub2, same ?

Frank62
11th September 2018, 14:35
No scenechange. The chroma is really the swapped chroma from the exact frame. Funny is that the left border of the swapping is fluent, no exact border. If I only could detect the area, I had the rest of the solution in mind already.
This is already from the restored clip, will post a original short clip later.

Yes, I also thought of decoding problems, but from 26 episodes the most do not have this error. Anyway I tried to open the mxf (containing mpeg2) after lwlibavvideosource also by first demuxing with ffmpeg and then indexing with the most reliable DGIndex, as I did earlier, before LSMASH times. Same problem.

Frank62
11th September 2018, 16:10
A short sample:
https://we.tl/t-zBuudR6KqL

StainlessS
11th September 2018, 20:05
Strange.
As well as black rhs border swelling on problem, also significant reduction in chroma saturation for both U and V channels when problem occurs.
(Histogram(mode="levels")
When color comes back, seems to jump up from the bottom, (squares your mention), like I/Q (color burst or whatever it is in TV signal), aint quite in sync,
maybe some TV engineer could give better insight.

EDIT:

AVISource("D:\UVSwap.mp4.AVI")
ORG=LAst
A=Trim(0,20)
B=Trim(21,33)
C=Trim(34,0)
B=B.SwapUV.Subtitle("***",Align=5)
Fx=A++B++C
ORG=ORG.Histogram(Mode="Levels")
Fx=FX.Histogram(Mode="Levels")
StackVertical(ORG,Fx)


EDIT: What is approx max duration of the probs ? (in sample about 13 frames)

EDIT: Added separate histograms to each clip in code block.

EDIT: "Aint quite in sync",
I'm sure that you've seen tumbling picture on old analogue TV, disappears off top of screen only to reappear up from the bottom,
is kinda like that, TV / capture card could not lock to the signal.
[EDIT: Lost both horizontal and vertical sync, but just for chroma]

StainlessS
11th September 2018, 21:26
This does detect, to some extent.


AVISource("D:\UVSwap.mp4.AVI").ConvertToYV24
### Prep
Crop_L=10 CROP_T=36 CROP_R=-20 CROP_B=-4
x0=701 x1=702 x2=703 x3=704
MID = Crop(Crop_L,CROP_T,CROP_R,CROP_B) # Crop off all border crap
YC = MID.ConvertToY8 # available if needed
UC = MID.UToY8
VC = MID.VToY8
### Config ###
GAP=0
Y0_TH=24.0 # AveLuma of RHS vertical column of pixels @ x0, ie border. [Border creeps in on damage]
U_TH =17.0 # 17.0, Percent population in range 128-GAP -> 128+GAP [On damage, gets less saturated, more chroma at 128, less outside of 128]
V_TH =23.5 # 23.5, Percent population in range 128-GAP -> 128+GAP [Ditto]
ClipClopName= "ClipClopCmd.txt"
FrameSelName= "FrameSelFrames.txt"
RangeName = "Ranges.txt"
WRITE_ENDS=True # If RangeName != "" then Also write RangeName+"_PRE.TXT" and RangeName+"_POST.TXT" frame before 1st & frame after last BAD frames.
# Can use FrameSel to 'pull out' bad frame plus up to 5 frames either side of bad frame (total 11 frames incl bad frame).
SHOW = True # True Show Metrics
FLAGS= True # True Show Frame Number and Flags (Also Shown if SHOW=False)
###########################
PREV_BINGO=False
PREV_START=-1
PREV_FRAME=-1
ClipClopName=(ClipClopName!="") ? RT_GetFullPathName(ClipClopName): ""
FrameSelName=(FrameSelName!="") ? RT_GetFullPathName(FrameSelName): ""
RangeName= (RangeName!="") ? RT_GetFullPathName(RangeName) : ""
DORANGES=RangeName!=""
Pre_Name=(DORANGES)?RangeName+"_PRE.TXT":""
Post_Name=(DORANGES)?RangeName+"_POST.TXT":""
(ClipClopName!="") ? RT_FileDelete(ClipClopName) : NOP
(FrameSelName!="") ? RT_FileDelete(FrameSelName) : NOP
(RangeName!="") ? RT_FileDelete(RangeName) : NOP
(Pre_Name!="") ? RT_FileDelete(Pre_Name) : NOP
(Post_Name!="") ? RT_FileDelete(Post_Name) : NOP
HILITE=RT_Ord("!") # RT_Subtitle Hilite control code
NORM =RT_Ord("-") # RT_Subtitle Normal white text control code
LOLITE=RT_Ord("L") # RT_Subtitle LoLite/DIM control code

SSS="""
n=current_frame
y0=RT_AverageLuma(x=x0,y=CROP_T,h=CROP_B,w=1)
y1=RT_AverageLuma(x=x1,y=CROP_T,h=CROP_B,w=1)
y2=RT_AverageLuma(x=x2,y=CROP_T,h=CROP_B,w=1)
y3=RT_AverageLuma(x=x3,y=CROP_T,h=CROP_B,w=1)
#
Y=YC.RT_AverageLuma
U=UC.RT_YInRange(lo=128-GAP,hi=128+GAP) * 100.0 # %
V=VC.RT_YInRange(lo=128-GAP,hi=128+GAP) * 100.0 # %
T1 = (Y0<Y0_TH) T2 = (U>U_TH) T3 = (V>V_TH)
BINGO=(T1 && T2 && T3)
T1_C=(T1)?HILITE:NORM T2_C=(T2)?HILITE:NORM T3_C=(T3)?HILITE:NORM # Hilite control codes for Metrics
F1_C=(T1)?NORM:LOLITE F2_C=(T2)?NORM:LOLITE F3_C=(T3)?NORM:LOLITE F_BINGO=(BINGO)?HILITE:LOLITE # Ctrl codes for FLAGS
(FLAGS)?RT_Subtitle("%d] \a%cB\a%cU\a%cV \a%c*",n,F1_C,F2_C,F3_C,F_BINGO):NOP
(SHOW)?RT_Subtitle("Y at RHS: \a%cx0=%.2f\a- x1=%.2f x2=%.2f x3=%.2f",T1_C,y0,y1,y2,y3,align=5,Y=5*20):NOP
(SHOW)?RT_Subtitle("YAve=%.2f : \a%cU=%.2f%%\a- : \a%cV=%.2f%%\a-",Y,T2_C,U,T3_C,V,align=5,Y=8*20):NOP
(SHOW&&BINGO)?SUBTITLE("!!!BINGO!!!",Align=5,size=48):NOP
(BINGO&&ClipClopName!="") ? RT_WriteFile(ClipClopName,"1 %d",n,Append=True) : NOP # Use ClipClop to replace frame with same from clip Index 1.
(BINGO&&FrameSelName!="") ? RT_WriteFile(FrameSelName,"%d",n,Append=True) : NOP # Use FrameSel Frame number only file.
if(DORANGES) {
If(PREV_FRAME!=n-1) { DORANGES = False } # User jumped about so all bets are OFF
Else {
if(BINGO) {
if(!PREV_BINGO) { # New Start
PREV_START=n
}
} Else {
if(PREV_BINGO) { # New End
RT_WriteFile(RangeName,"%d,%d",PREV_START,n-1,Append=True) # Write Range
if(WRITE_ENDS) {
RT_WriteFile(Pre_Name,"%d",PREV_START-1,Append=True) # Write pre BAD frame
RT_WriteFile(Post_Name,"%d",n,Append=True) # Write post BAD frame
}
PREV_START=-1
}
}
PREV_BINGO=BINGO
}
PREV_FRAME=n
}
Return last
"""
Return Scriptclip(SSS)


The triple detector numbers shown in Orange in metrics, for x0, U, and V.
https://s20.postimg.cc/9ezh9o9t9/FIX_UVSwap.jpg (https://postimg.cc/image/ffx66qwfd/)

Frank62
12th September 2018, 11:28
Wow! That looks interesting! I also detected meanwhile beginning, ends and single frame swaps, but this seems a way to detect everything between also without need for programming some logic outside Avisynth.
Thanks a lot!
No time to test this the whole day but I will report how it works earliest tomorrow evening.

:thanks:

StainlessS
12th September 2018, 13:57
Have updated above script, writes Pre bad range and post bad range frames (ie frame before bad range and also frame after).

Can use FrameSel to pull out eg pre-bad + some extra frames, and simple fix might be to repair using frame before pre-bad,
same type thing for frame after bad range.

From FrameSel

FrameSel(Clip, int F1, ... , int Fn, string 'scmd',string 'cmd', bool 'show', bool 'ver',bool "reject",bool "ordered",
\ bool "debug", int "Extract"=1)



Extract Int, Default 1, MUST be ODD, 1 to 11. Error if not default 1 AND neither Ordered nor Reject == True.
Let us call the UNIQUE resultant frames after Ordered and/or Reject Processing 'Target' frames.
The number of frames pulled out on either side of Target frame is Extract / 2 (integer divide), so when Extract=3, it will
extract 1 frame before target, the Target frame, and 1 frame after target, for each and every Target frame.
This arg allows you to select eg 3 frames for each Target frame, the previous frame, Target frame and next frame.
May be of use to extract bad frames together with eg 1 frame either side for saving as bitmaps and editing
in some kind of RotoScope editor to repair bad frames using image from adjacent frames. The edited frames could
then be re-loaded, middle one selected via SelectEvery(3,1), and then put back into original source clip via FrameRep().
Another simple use could be when you have a clip where each frame before a scene change is bad, with Ordered=True
and Extract=3, you pull out 3 frames, a SelectEvery(3,0) would select the frames previous to the bad frames and
then use Framerep() to replace the bad frames with those previous to them. A SelectEvery(3,2) would select the frames
after Target frames.
The total number of frames pulled out of the source clip will be (Extract * number of Target frames).
Reject is less likely to be of use with Extract, unless frame select commands specify good frames rather than bad,
where Reject in FrameRep should also be true (same as in FrameSel, as always).
See FrameRep() for example uses of Extract.



C=Colorbars().Trim(0,-50).ShowFrameNumber() # 50 frames (0 -> 49)
BADFRAMES = "10;20;30;40" # 4 bad frames in source clip
EXTRACT=FrameSel(C,scmd=BADFRAMES,ordered=True,extract=3) # Extract Previous, Target and Next frames for each Target frame
REPAIR=EXTRACT.SelectEvery(3,0) # Select each frame previous to Target bad frames, 1st of each triplet
#REPAIR=REPAIR.Invert() # So we can easily see repaired frames
Return FrameRep(C,REPAIR,scmd=BADFRAMES) # Fix bad frames with REPAIR clip using exact same BADFRAMES spec.

StainlessS
12th September 2018, 15:51
Added Flags shown top LHS,
Here last bad frame of range, and the following half bad frame where only two of detector metrics satified.
(Border metric fails detect but chroma metric still detecting)
[In such case might just be able to use chroma from following frame].
Might be best to do a prescan, to establish scene cuts, and process each separately after establishing some average chroma metric
for the scene (how do we do that, considering some of scene may be bad ???).

Script updated.

A=trim(33,-1)
B=trim(34,-1)
StackVertical(A,B)
Spline36Resize(640,960)

https://s20.postimg.cc/iotodz2wd/FIX_UVSwap_2.jpg (https://postimg.cc/image/ajbmftend/)

Frame after half bad frame, ie 2 frames after last bad detect.

X=trim(35,-1)
return X.Spline36Resize(640,480)

https://s20.postimg.cc/snen069od/FIX_UVSwap_3.jpg (https://postimages.org/)

StainlessS
12th September 2018, 17:42
Modified to detect both bad range and last bad + 1 half bad frame, writing clipclop file to fix both
(assumes that detector works for complete clip, maybe does, maybe does not,
Who can tell the secret of the Black Magic Box.
Is the box of chocolates alive, or is the box of chocolates dead, you can only know when you open the box :) ).


#_FIX_UVSWAP_MOD.avs
AVISource("D:\UVSwap.mp4.AVI").ConvertToYV24
### Prep
Crop_L=10 CROP_T=36 CROP_R=-20 CROP_B=-4
x0=701 x1=702 x2=703 x3=704
MID = Crop(Crop_L,CROP_T,CROP_R,CROP_B) # Crop off all border crap
YC = MID.ConvertToY8 # available if needed
UC = MID.UToY8
VC = MID.VToY8
### Config ###
GAP=0
Y0_TH=24.0 # AveLuma of RHS vertical column of pixels @ x0, ie border. [Border creeps in on damage]
U_TH =17.0 # 17.0, Percent population in range 128-GAP -> 128+GAP [On damage, gets less saturated, more chroma at 128, less outside of 128]
V_TH =23.5 # 23.5, Percent population in range 128-GAP -> 128+GAP [Ditto]
ClipClopName= "ClipClopCmd.txt"
SHOW = True # True Show Metrics
FLAGS= True # True Show Frame Number and Flags (Also Shown if SHOW=False)
###########################
PREV_BINGO=False
PREV_START=-1
PREV_FRAME=-1
ClipClopName=(ClipClopName!="") ? RT_GetFullPathName(ClipClopName): ""
DORANGES=ClipClopName!=""
(ClipClopName!="") ? RT_FileDelete(ClipClopName) : NOP
HILITE=RT_Ord("!") # RT_Subtitle Hilite control code
NORM =RT_Ord("-") # RT_Subtitle Normal white text control code
LOLITE=RT_Ord("L") # RT_Subtitle LoLite/DIM control code

SSS="""
n=current_frame
y0=RT_AverageLuma(x=x0,y=CROP_T,h=CROP_B,w=1)
y1=RT_AverageLuma(x=x1,y=CROP_T,h=CROP_B,w=1)
y2=RT_AverageLuma(x=x2,y=CROP_T,h=CROP_B,w=1)
y3=RT_AverageLuma(x=x3,y=CROP_T,h=CROP_B,w=1)
#
Y=YC.RT_AverageLuma
U=UC.RT_YInRange(lo=128-GAP,hi=128+GAP) * 100.0 # %
V=VC.RT_YInRange(lo=128-GAP,hi=128+GAP) * 100.0 # %
T1 = (Y0<Y0_TH) T2 = (U>U_TH) T3 = (V>V_TH)
BINGO=(T1 && T2 && T3)
T1_C=(T1)?HILITE:NORM T2_C=(T2)?HILITE:NORM T3_C=(T3)?HILITE:NORM # Hilite control codes for Metrics
F1_C=(T1)?NORM:LOLITE F2_C=(T2)?NORM:LOLITE F3_C=(T3)?NORM:LOLITE F_BINGO=(BINGO)?HILITE:LOLITE # Ctrl codes for FLAGS
(FLAGS)?RT_Subtitle("%d] \a%cB\a%cU\a%cV \a%c*",n,F1_C,F2_C,F3_C,F_BINGO):NOP
(SHOW)?RT_Subtitle("Y at RHS: \a%cx0=%.2f\a- x1=%.2f x2=%.2f x3=%.2f",T1_C,y0,y1,y2,y3,align=5,Y=5*20):NOP
(SHOW)?RT_Subtitle("YAve=%.2f : \a%cU=%.2f%%\a- : \a%cV=%.2f%%\a-",Y,T2_C,U,T3_C,V,align=5,Y=8*20):NOP
(SHOW&&BINGO)?SUBTITLE("!!!BINGO!!!",Align=5,size=48):NOP
if(DORANGES) {
If(PREV_FRAME!=n-1) { DORANGES = False } # User jumped about so all bets are OFF
Else {
if(BINGO) {
if(!PREV_BINGO) { # New Start
PREV_START=n
}
} Else {
if(PREV_BINGO) { # New End
RT_WriteFile(ClipClopName,"1 %d,%d",PREV_START,n-1,Append=True) # Write ClipClop Range to replace with clip index 1
if(T2 && T3) { # Border Detect Fail
RT_WriteFile(ClipClopName,"2 %d",n,Append=True) # replace End Bad + 1 frame with clip index 2 frame
}
PREV_START=-1
}
}
PREV_BINGO=BINGO
}
PREV_FRAME=n
}
Return last
"""
Scriptclip(SSS)


ClipClop repair script

# _FIX_UVSwap_MOD_ClipClop.avs
AVISource("D:\UVSwap.mp4.AVI")
ClipClopName= "D:\ClipClopCmd.txt"
ORG=Last

CONT_U = 0 # ColorYUV U Sat adjust for bad range (0=no adjust)
CONT_V = 0 # ColorYUV V Sat adjust for bad range (0=no adjust)

Nxt=SelectEvery(1,1) # Shift next frame to current
CN_Chroma = ORG.MergeChroma(Nxt) # Copy Next Chroma into current, Fix End Bad + 1 half bad frame


V1 = SwapUV().ColorYUV(cont_u=CONT_U,cont_v=CONT_V)
V2 = CN_Chroma

NickNames =""" # Psuedonyms for clips (clip index number)
SwapUV = 1 # Fixed Bad range clip (SwapUV with optional Chroma Sat adjustment)
CN_Chroma = 2 # Fixed half bad end bad + 1 frame (Copy Chroma from next)
"""

SHOW=True

ClipClop(ORG,V1,V2,Cmd=ClipClopName,nickname=NickNames,show=SHOW)
StackVertical(Last,ORG)


Last bad range frame fixed by ClipClop, (Src on bottom)
https://s20.postimg.cc/gs1jn7m7h/FIX_UVSwap_MOD_Clip_Clop_1.jpg (https://postimg.cc/image/tw73zwe95/)

Last bad range + 1 frame (half bad frame, copy chroma from next frame) fixed (src on bottom)

https://s20.postimg.cc/uyhaihf31/FIX_UVSwap_MOD_Clip_Clop_2.jpg (https://postimg.cc/image/qcl6a4tjt/)

EDIT:
ClipClopCmd.txt to fix range Plus End Bad + 1 (Half bad) frame

1 21,33
2 34

StainlessS
12th September 2018, 21:23
I might not be able to do anything else before about Saturday, here a tool to perhaps enable you to continue with detector,
eg detect scenes, get some kind of average metrics for Chroma, and mod chroma detect thresholds for current scene.

Just wrote the DBase_DetectScenes thing for this pupose.

EDIT: Below DB_FindTrim() mentions clips, rather than scenes, function was written for different purpose.

Library funcs

# ############
# DB_FindTrim.avs [Replaced with RT_DBaseFindSeq() plugin function in RT_Stats v2.0]
# DB_FindTrim Returns clip (or record number) containing movie frame Frame.
# Search DBase for the record (clip) that contains the arg Frame, using Binary Search.
# S_Field is the DB field that contains the first frame of the record clip.
# E_Field is the DB field that contains the Last frame of the record clip.
# Return of -1 = NOT FOUND.
# ############

Function DB_FindTrim(String DB,Int S_Field,Int E_Field,Int Frame) {
IsAvsPlus=(FindStr(UCase(versionString),"AVISYNTH+")!=0) HasGScript=RT_FunctionExist("GScript")
Assert(IsAvsPlus || HasGScript,RT_String("DB_FindTrim: Need either GScript or AVS+"))
result = -1 # Init NOT FOUND
low = 0
high = RT_DBaseRecords(DB) - 1
GS="""
while(low <= high) {
mid = (low + high) / 2
if(RT_DBaseGetField(DB,mid,E_Field) < Frame) {
low = mid + 1
} Else If (RT_DBaseGetField(DB,mid,S_Field) > Frame) {
high = mid - 1
} Else {
low = high + 1 # Force exit
Result = mid
}
}
"""
HasGScript ? GScript(GS) : Eval(GS) # Use GSCript if installed (loaded plugs override builtin)
return result
}


Function DBase_DetectScenes(clip c,String DB,Int "SOS_Field",Int "EOS_Field",Int "OffSet",Int "thSCD1",Int "thSCD2") {
/*
Requires RT_Stats, MVTools2, MaskTools2. (and either Avs+ or GSCript)
Given a pre-allocated DBase (can have 0 records), with at least two int fields for SOS and EOS (Start and End Of Scene),
will APPEND records with SOS and EOS frame numbers inserted into SOS_Field and EOS_Field fields, remaining fields of record
will be initialized to nul, ie int=0, Float=0.0, Bool=False, Bin=0,String="".
Will Scan clip using MvTools2 MSCDetection, and when established SOS and EOS, will write new record
filling in SOS_Field field with (StartOfSceneFrame + Offset) and EOS_Field field with (EndOfSceneFrame + Offset).
SOS_Field, Default 0.
EOS_Field, Default SOS_Field+1.
Offset, Default 0.
thSCD1, Default 400, See MvTools
thSCD2, Default 130, See MvTools
Returns Number of Records (ie Scenes found in clip) added to DBase.
*/
c myName="DBase_DetectScenes: "
IsAvsPlus=(FindStr(UCase(versionString),"AVISYNTH+")!=0) HasGScript=RT_FunctionExist("GScript")
HasMvTools=RT_FunctionExist("MSuper") HasMaskTools=RT_FunctionExist("MT_Lutxy")
Assert(IsAvsPlus || HasGScript,RT_String("DB_FindTrim: Need either GScript or AVS+"))
Assert(HasMVTools,RT_String("%sNeed MvTools2:-http://forum.doom9.org/showthread.php?t=131033",myName))
Assert(HasMaskTools,RT_String("%sNeed MaskTools",myName))
SOS_Field=Default(SOS_Field,0)
EOS_Field=Default(EOS_Field,SOS_Field+1)
OffSet=Default(OffSet,0)
thSCD1=Default(thSCD1,400) thSCD2=Default(thSCD2,130)
FC=FrameCount
Records=RT_DBaseRecords(DB) Fields=RT_DBaseFields(DB) Rec=Records
Assert(SOS_Field>=0 && SOS_Field<fields,RT_String("%sSOS_Field(%d) Does Not exist in DBase(0->%d)",myName,SOS_Field,Fields-1))
Assert(EOS_Field>=0 && EOS_Field<fields,RT_String("%sEOS_Field(%d) Does Not exist in DBase(0->%d)",myName,EOS_Field,Fields-1))
Assert(EOS_Field != SOS_Field,RT_String("%sEOS_Field(%d) Cannot be same as SOS_Field",myName,EOS_Field))
Sup = c.MSuper(pel=1,sharp=0,rfilter=2,hpad=16, vpad=16)
BvEos= Sup.MAnalyse(isb=True, delta=1,blksize=16)
FvSos= Sup.MAnalyse(isb=False, delta=1,blksize=16)
Eos = c.MSCDetection(BvEos,thSCD1=thSCD1,thSCD2=thSCD2)
Sos = c.MSCDetection(FvSos,thSCD1=thSCD1,thSCD2=thSCD2)
SC_Clip = MT_Lutxy(Eos,Sos,yexpr="y 0 == x 0 == 0 1 ? x 0 == 2 3 ? ?",u=-128,v=-128)
LastWrE=-1
GS="""
For(n=0,FC-1) {
eosos = SC_Clip.RT_AverageLuma(n,w=1,h=1).Int # All Luma pixel = 0 =Norm, 1=EOS, 2=SOS, 3=EOS & SOS
eosos = (n==FC-1) ? RT_BitSet(eosos,0) : eosos # Force EOS at Last frame, fix some bugged
if(eosos>=2 && LastWrE<n-1 && n!=0) { # Is flagged SOS, so force any earlier non written range
Rec=RT_DBaseExtend(DB,1) # Add 1 Record, nullified fields, Rec = current number of records
RT_DBaseSetField(DB,Rec-1,SOS_Field,LastWrE+1+OFfset)
LastWrE=n-1
RT_DBaseSetField(DB,Rec-1,EOS_Field,LastWrE+OFfset)
}
if(eosos==1 || eosos==3) { # Flagged EOS, Write to current
Rec=RT_DBaseExtend(DB,1) # Add 1 Record, nullified fields, Rec = current number of records
RT_DBaseSetField(DB,Rec-1,SOS_Field,LastWrE+1+OFfset)
LastWrE=n
RT_DBaseSetField(DB,Rec-1,EOS_Field,LastWrE+OFfset)
}
}
"""
HasGScript ? GScript(GS) : Eval(GS) # Use GSCript if installed (loaded plugs override builtin)
Return Rec-Records
}


DB_FindTrim.avs [Replaced with RT_DBaseFindSeq() plugin function in RT_Stats v2.0], so can use RT_DBaseFindSeq if you use RT_Stats v2.0, Beta.

Create DBase with scene change range data (can add some fields for other stuff, eg stored chroma thresh to use in each detector stage
(generated by some kind of chroma average script).

DBase Generator (after generated, can fill in your additional empty fields via some eg chroma thresh based on average)

# Create DBase
AVISource("D:\Parade.AVI")
DB="D:\Parade.DB"
RT_DBaseAlloc(DB,0,"ii") # Alloc minimum of two int fields, 0=SOS, 1=EOS. (add others if required)
Records = DBase_DetectScenes(DB) # Use Defaults for fields, Offset, and thSCD1 and thSCD2
Return MessageClip("DBase_DetectScenes: Records="+String(records))


Demo showing scenes only

# Show Scenes
AVISource("D:\Parade.AVI")
DB="D:\Parade.DB"
SSS="""
n=current_frame
Record=DB_FindTrim(DB,0,1,n) # Record or scene number (relative 0) # -1 NOT FOUND
SceneStart = (Record>=0) ? RT_DBaseGetField(DB,Record,0) : -1 # Get scene start from field 0 (SOS)
SceneEnd = (Record>=0) ? RT_DBaseGetField(DB,Record,1) : -1 # Get scene end from field 1 (EOS)
RT_Subtitle("%d] #%d Start=%d End=%d",n,Record,SceneStart,SceneEnd)

"""
Scriptclip(SSS)
return Last


EDIT: Scene detectors dont seem to always generate a Start Of Scene following an End Of Scene, or an End Of Scene before
a Start Of Scene, above detector should be devoid of such behaviour.
NOTE, Some scenes may be single frame, especially at blended transition. [ie both SOS and EOS]

EDIT: You can use Offset, to concatenate multiple clip scene change data into a single DBase for spliced final clip.

StainlessS
13th September 2018, 01:11
Additional Demo.

All done in one script, init DBase with scene change data, scan each scene getting min and max aveluma of scene, update DBase with min/max stuff
then play clip showing result scene numbers, and scene min/max aveluma for each frame in scene.


######
# Demo, Add something to DBase for each scene/record, in this case just min and max average luma as 2 Floats @ Field 2=min, 3=max
######
AVISource("D:\Parade.AVI")
DB="D:\Parade.DB"
######
# 1st Create DBase with 2 additional fields
######
RT_DBaseAlloc(DB,0,"iiff") # Alloc two int fields for SOS & EOS, also add Field 2 Float for MinAveLuma, and Field 3 for MaxAveLuma
Records = DBase_DetectScenes(DB) # Use Defaults for fields, Offset, and thSCD1 and thSCD2
RT_DebugF("DEMO: Detected %d Scenes, added to DBase",Records) # See output in DebugView (Google)
#return MessageClip("DONE")

######
### 2nd, Scan scenes in clip, establish Min/max luma for each scene, and update DBase
### Using AVS+ here, mod if using GSCript
######
Records=RT_DBaseRecords(DB)
For(scene=0,Records-1) {
SFrm = RT_DBaseGetField(DB,scene,0) # Get scene start from field 0 (SOS)
EFrm = RT_DBaseGetField(DB,scene,1) # Get scene end from field 1 (EOS)
MinLum=256.0 MaxLum=-1.0 # Prep for scene
for(frm=SFrm,EFrm) { # Scan all Frames in Scene
AveLum=RT_AverageLuma(n=frm) # could specify eg area to measure eg x=32,y=32,w=-32,h=-32 # as for crop(32,32,-32,-32)
MinLum=Min(MinLum,AveLum)
MaxLum=Max(MaxLum,AveLum)
}
RT_DbaseSetField(DB,scene,2,MinLum) # Set Minimum encountered luma in this scene to 3rd field, field 2
RT_DbaseSetField(DB,scene,3,MaxLum) # Set Maximum encountered luma in this scene to 4th field, field 3
RT_DebugF("DEMO: Scene#%d[%d,%d] MinLuma=%.3f MaxLuma=%.3f",scene,SFrm,EFrm,MinLum,MaxLum) # See output in DebugView (Google)
}
#return MessageClip("DONE")

######
# 3rd, NOW PLAY Clip
######

SSS="""
n=current_frame
Record=DB_FindTrim(DB,0,1,n) # Record or scene number (relative 0) # -1 NOT FOUND
if(Record>=0) {
Records = RT_DBaseREcords(DB) # Show number of records ie scenes
SceneStart = RT_DBaseGetField(DB,Record,0) # Get scene start from field 0 (SOS)
SceneEnd = RT_DBaseGetField(DB,Record,1) # Get scene end from field 1 (EOS)
MinLum = RT_DBaseGetField(DB,Record,2) # Get MinLuma for entire scene (same for each frame in scene)
MaxLum = RT_DBaseGetField(DB,Record,3) # Get MaxLuma for entire scene (same for each frame in scene)
SceneFMax = SceneEnd-SceneStart # For show scene relative frame number n/nmax, 0 rel
RT_Subtitle("%d/%d] #%d/%d:%d/%d[%d,%d,Len=%d]\nMinLuma=%.3f\nMaxLuma=%.3f",
\ n,FrameCount-1,Record,Records-1,n-scenestart,SceneFMax,SceneStart,SceneEnd,SceneFMax+1,MinLum,MaxLum)
} Else { Subtitle("ERROR: RECORD NOT FOUND !",align=5,size=48)}
"""
Scriptclip(SSS)


It will take some time before playing with long clip (scan for scene change/ scan for minmax avluma, then play).

Can view some stuff via DebugView while Scanning (Google)
EDIT: You also need prev posted library funcs.

EDIT: In above ScriptClip Play section, could if required only update scene constant stuff from DBase, when a new scene
is encountered, eg MinLuma is set for entire scene, and would only need be gotten once per scene, but
is no real problem to get it every time, when it would be a good idea, would be when getting some string
from DBase, where strings take up quite a bit of memory as new string required for every iteration, (waste of mem).
However, if updating only on new record/scene, then would require a Global variable to remember the previous record/scene,
and also the previous string.

Frank62
13th September 2018, 18:39
You're great, a big thank you again!
I tried this with the whole episode: Unfortunately:

-At some swapped ranges U and V population/saturation get a lot higher compared to the frames before and after, on other ranges it is as you assumed, they get smaller.
At those ranges I observed, the differences between pop before/after and pop inside are quite significant - but this gives me again only the direct possibility to detect the beginnings and ends of the ranges - if you don't have another idea for this. And it's not safe, because with some ranges significantly higher, some ranges significantly lower, I bet that there are some where there will be no difference...

-The other method with testing the right margin works most times well, but fails if the near content is also very dark - and I already found some scenes where exactly this happens...

-My first method with this:


v=avisource("26.avi")
vminus1=trim(v,1,0)
global sep=";"
x=WriteFileIf(v,"F:\Scripts\26UV.log","(UDifferenceFromPrevious(v)-UDifferenceFromPrevious(vminus1)>1.7) && (YDifferenceFromPrevious(v)-YDifferenceFromPrevious(vminus1)<10)","current_frame-1")

works quite well for beginning and ends of the ranges, and also detects the one-frame-swaps.

So I fear I will save the beginnings and ends with my/your mixed detector (works for this purpose quite at 100%) and write something meta that will output a final repair-script.

To work myself into all that great stuff with database and all I haven't got the time, which I regret - all episodes have to be done next week.

Again :thanks: for your big efforts! Great people in this forum!

StainlessS
14th September 2018, 09:48
works quite well for beginning and ends of the ranges, and also detects the one-frame-swaps.

So I fear I will save the beginnings and ends with my/your mixed detector (works for this purpose quite at 100%) and write something meta that will output a final repair-script.


Yeh but how do you tell if beginning, end, or single frame, what magic do you have for that ?

If you can up a longer (~500MB max, no audio), I'll try D/L today.

I was thinking of compare with next frame, and also compare with eg SwapUV for detector.
[I'm also still sticking with DBase, too much potential for error crossing scene boundaries].

EDIT:

[CODE]
v=avisource("26.avi")
vminus1=trim(v,1,0)
global sep=";"
x=WriteFileIf(v,"F:\Scripts\26UV.log","(UDifferenceFromPrevious(v)-UDifferenceFromPrevious(vminus1)>1.7) &&" +
\ "(YDifferenceFromPrevious(v)-YDifferenceFromPrevious(vminus1)<10)","current_frame-1")
Also note that UDifferenceFromPrevious(vminus1) same as UDifferenceToNext(v), no need for additional next vminus clip.

Frank62
14th September 2018, 11:46
Yeh but how do you tell if beginning, end, or single frame, what magic do you have for that ?

Walking through from the beginning.
First hit is either single or beginning.
If between this and next hit more than let's say 30 frames (maximum length I saw was 20 frames) then it was a single swap, if not, then next one is end.
a. s. o.
Only if two single swaps are nearer than 30 frames I will become problems. But can fix this by hand, maybe once an episode, if it ever happens.

I can't do this in avisynth, but extern no problem, I am doing similar things all the time, so one or two hours of work should do.

I was thinking of compare with next frame, and also compare with eg SwapUV for detector.
Big thanks again, but it's no more necessary, because the detector works 99% - only some work to do.

Also note that UDifferenceFromPrevious(vminus1) same as UDifferenceToNext(v), no need for additional next vminus clip.
Due to my inclination to maximum simplicity, readability, through-thinkability I wrote so. But sometimes very simple is more complicated, so I overlooked this. ;)

StainlessS
14th September 2018, 17:13
When I get home, I shall try complete my version detector as in your 2nd last quote, seemed to be working before I had to leave.
Maybe yours works fine, but gonna try my altrnative, just the same.

be good.

Frank62
14th September 2018, 19:12
Sorry, I didn't want to thwart. Just avoid work for you.

StainlessS
14th September 2018, 19:58
Sorry, I didn't want to thwart. Just avoid work for you.

Yeh, I know, just found it an interesting prob.
In the words of some UK TV program (that I cant quite remember the name of, EDIT: MasterMind with Magnus Magnusson), 'I've started, so I'll finish', if your detector works, then great, if not then there might be a viable alternative, I'll post mine anyways, whether you successfully complete or not.

But not right now, think I shall succumb to this booze induced coma that I'm about to fall into :).

StainlessS
15th September 2018, 16:50
Franc62,
This should work perfectly for entire clip (mehopes).
Use original ClipClop script for final render.
I've used correlation, rather than frame diff.
Need prev posted lib funcs.



### CONFIG ###
AVISource("D:\UVSwap.mp4.AVI")
ClipClopName= "D:\ClipClopCmd.txt"
DB="D:\UVSwap.DB"
DEBUG = False # False only write Detections, not metrics
CLIPCLOP_SHOW = True
STACK = True
CONT_U = 0 # ColorYUV U Sat adjust for bad range (0=no adjust)
CONT_V = 0 # ColorYUV V Sat adjust for bad range (0=no adjust)
###### END CONFIG ###
ORG=Last
Crop_L=10 CROP_T=36 CROP_R=-20 CROP_B=-4
MID = Crop(Crop_L,CROP_T,CROP_R,CROP_B) # Crop off all border crap
UC = MID.UToY8
VC = MID.VToY8
SWAPUV_C = MID.SwapUV()
SWAPUV_UC = SWAPUV_C.UToY8
SWAPUV_VC = SWAPUV_C.VToY8
CN_Chroma = MID.MergeChroma(MID.SelectEvery(1,1)) # Copy Next Chroma into current, Fix End Bad + 1 half bad frame
CN_Chroma_UC = CN_Chroma.UToY8
CN_Chroma_VC = CN_Chroma.VToY8
OUT_V1 = (CONT_U!=0 || CONT_V!=0)?ORG.SwapUV.ColorYUV(cont_u=CONT_U,cont_v=CONT_V):ORG.SwapUV
OUT_V2 = ORG.MergeChroma(ORG.SelectEvery(1,1))
###
ClipClopName = (ClipClopName!="") ? RT_GetFullPathName(ClipClopName) : ""
(ClipClopName!="") ? RT_Filedelete(ClipClopName) : NOP

RT_DBaseAlloc(DB,0,"ii") # Alloc two int fields for SOS & EOS
Records = DBase_DetectScenes(MID,DB) # Use Defaults for fields, Offset, and thSCD1 and thSCD2
RT_DebugF("DEMO: Detected %d Scenes, added to DBase",Records) # See output in DebugView (Google)

BadSeqs=0
HalfBad=0
Records=RT_DBaseRecords(DB)
For(scene=0,Records-1) {
SFrm = RT_DBaseGetField(DB,scene,0) # Get scene start from field 0 (SOS)
EFrm = RT_DBaseGetField(DB,scene,1) # Get scene end from field 1 (EOS)
Prev_Start=-1 IN=False Prev_In=False
for(n=SFrm+1,EFrm) { # Scan Frames in Scene, (start at 1st frame +1)
U = RT_LumaCorrelation(UC,UC,n=n-1,n2=n) # U prev to current [correlation, higher is better match, -ve is invert)
V = RT_LumaCorrelation(VC,VC,n=n-1,n2=n) # V prev to current
USW = RT_LumaCorrelation(UC,SWAPUV_UC,n=n-1,n2=n) # U prev to current U SWAP
VSW = RT_LumaCorrelation(VC,SWAPUV_VC,n=n-1,n2=n) # V prev to current V SWAP
(DEBUG) ? RT_DebugF("%d] U=%f V=%f USW=%f VSW=%f",n,U,V,USW,VSW) : NOP
T1 = (USW > U) T2 = (VSW > V)
Bingo = T1 && T2
In = (BINGO) ? !In : IN
if(In) { # Start
if(!Prev_In) {
Prev_start=n
(DEBUG) ? RT_DebugF("%d] START: = %d",n,Prev_Start) : NOP
}
} Else {
if(Prev_In) { # Stop
BadSeqs = BadSeqs + 1
RT_DebugF("%d] STOP: Writing ClipClop replacment with Index 1 clip, range %d,%d",n,Prev_Start,n-1)
RT_WriteFile(ClipClopName,"1 %d,%d",Prev_Start,n-1,Append=True) # Write ClipClop Range to replace with clip index 1
SWP_CN_U = RT_LumaCorrelation(SWAPUV_UC, CN_Chroma_UC, n=n-1,n2=n) # U SWAP prev to current CN_Chroma_U
SWP_CN_V = RT_LumaCorrelation(SWAPUV_VC, CN_Chroma_VC, n=n-1,n2=n) # V SWAP prev to current CN_Chroma_V
SWP_U = RT_LumaCorrelation(SWAPUV_UC, UC, n=n-1,n2=n) # U SWAP prev to current U
SWP_V = RT_LumaCorrelation(SWAPUV_VC, VC, n=n-1,n2=n) # V SWAP prev to current V
(DEBUG) ? RT_DebugF("%d] SWP_CN_U=%f SWP_CN_V=%f SWP_U=%f SWP_V=%f",n,SWP_CN_U,SWP_CN_V,SWP_U,SWP_V) : NOP
T1 = (SWP_CN_U > SWP_U) T2 = (SWP_CN_V > SWP_V) # Correlation to Next chroma better ?
Bingo = T1 && T2
if(Bingo) { # Half Bad Frame, next Chroma Better match
HalfBad = HalfBad + 1
RT_DebugF("%d] HALFBAD: Writing ClipClop replacment with Index 2 clip, Frame %d",n,n)
RT_WriteFile(ClipClopName,"2 %d",n,Append=True) # replace End Bad + 1 frame with clip index 2 frame
}
Prev_Start=-1
}
}
Prev_In=In
}
}

(DEBUG) ? RT_DebugF("BadSeqs=%d HalfBad=%d",BadSeqs,HalfBad) : NOP

NickNames =""" # Psuedonyms for clips (clip index number)
SwapUV = 1 # Fixed Bad range clip (SwapUV with optional Chroma Sat adjustment)
CN_Chroma = 2 # Fixed half bad end bad + 1 frame (Copy Chroma from next)
"""

ClipClop(ORG,OUT_V1,OUT_V2,Cmd=ClipClopName,nickname=NickNames,show=CLIPCLOP_SHOW)
(STACK)?StackVertical(Last,ORG):Last

EDIT: DEBUG, mod to only write detections, when False.

For any mishaps (dont expect any), then can edit ClipClopCmd.txt file.
EDIT: There are no thresholds, either better match or not, where in initial comparison, 1 pair is expected to be -ve, and other +ve.
EDIT: If 1st frame of scene is swapped, then aint no way we can detect, with likely error, not likely to be able to detect using any
other method either.

EDIT:
1 pair is expected to be -ve, and other +ve
What was I thinking, is swapped chroma, not inverted ???
but should still work OK.

Frank62
15th September 2018, 18:42
Thanks, @StainlessS!
Will I need any drivers or other database files to run this, or is the whole database functionality integrated in your RT library, so that the code generates the DB-file and simply works?

I have no time at all to test and try out at the moment, that's a real pity. But I will, by the beginning of next week, that's for sure.

StainlessS
15th September 2018, 20:21
Just need RT_Stats, old v1.43 should be OK.
+ Library func DBase_DetectScenes() previousy posted in post #13 [DB_FindTrim() not necessary].
+ MvTools2, Masktools2, Avs+.
Nothing else I think, should 'simply work'.

EDIT:
1 pair is expected to be -ve, and other +ve
What was I thinking, is swapped chroma, not inverted ???
but should still work OK.

Frank62
20th September 2018, 11:46
Hi @StainlessS
series done. There were a lot of other issues, and also a small error: The scene we took for testing could be fixed ACCIDENTALLY by swapping U and V. There were a lot of different bad colours, all blue, all green, interlaced mixed, etc. So the only chance was to fix everything by hand, taking colour from nearest correct frames, motion compensated as good as possible. Looks quite ok now, though not perfect.

:thanks: again for help. As soon as I will find the time I will try all this - will be useful in the future, I am sure.

StainlessS
20th September 2018, 11:54
Ah, sorry to hear that, Having now completed the odyssey, I'm sure that you feel quite relieved.

EDIT: I guess that the DBase scene scanning stuff should be re-usable for something,
was an interesting excercise anyway.

Frank62
20th September 2018, 12:54
Having now completed the odyssey, I'm sure that you feel quite relieved.

I have to do things like that almost every month... - but feel somehow relieved, yes. A bit. ;)

StainlessS
20th September 2018, 20:00
Propose something like below to avoid massive string memory allocation where function may be called hundreds of thousands of times in a
single script (where script can be a lot bigger than below one).


# ############
# DB_FindTrim.avs [Replaced with RT_DBaseFindSeq() plugin function in RT_Stats v2.0]
# DB_FindTrim Returns clip (or record number) containing movie frame Frame.
# Search DBase for the record (clip) that contains the arg Frame, using Binary Search.
# S_Field is the DB field that contains the first frame of the record clip.
# E_Field is the DB field that contains the Last frame of the record clip.
# Return of -1 = NOT FOUND.
# ############

Function DB_FindTrim(String DB,Int S_Field,Int E_Field,Int Frame) {
IsAvsPlus=(FindStr(UCase(versionString),"AVISYNTH+")!=0) HasGScript=RT_FunctionExist("GScript")
Assert(IsAvsPlus || HasGScript,RT_String("DB_FindTrim: Need either GScript or AVS+"))
result = -1 # Init NOT FOUND
low = 0
high = RT_DBaseRecords(DB) - 1
Global DB_FindTrim_GLOBAL_STRING=RT_VarExist("DB_FindTrim_GLOBAL_STRING")?DB_FindTrim_GLOBAL_STRING:"""
while(low <= high) {
mid = (low + high) / 2
if(RT_DBaseGetField(DB,mid,E_Field) < Frame) {
low = mid + 1
} Else If (RT_DBaseGetField(DB,mid,S_Field) > Frame) {
high = mid - 1
} Else {
low = high + 1 # Force exit
Result = mid
}
}
"""
HasGScript ? GScript(DB_FindTrim_GLOBAL_STRING) : Eval(DB_FindTrim_GLOBAL_STRING) # Use GSCript if installed (loaded plugs override builtin)
return result
}


Anybody any better suggestion ?

EDIT: There is no string memory garbage collection in avsynth, strings are allocated/reallocated and never released until avs closedown.
EDIT: And should above work OK (or perhaps even makes things worse, maybe I have to check).

EDIT: Nah, dont seem to make a damn bit of difference (except being slower).

# DB_FindTrim (original)
AVSMeter 2.8.1 (x86) - Copyright (c) 2012-2018, Groucho2004
AviSynth+ 0.1 (r2728, MT, i386) (0.1.0.0)

Number of frames: 73730
Length (hh:mm:ss.ms): 00:41:00.124
Frame width: 490
Frame height: 360
Framerate: 29.970 (30000/1001)
Colorspace: YV12
Audio channels: 2
Audio bits/sample: 16
Audio sample rate: 44100
Audio samples: 108491612

Frames processed: 73730 (0 - 73729)
FPS (min | max | average): 211.1 | 420.5 | 290.6
Memory usage (phys | virt): 122 | 148 MiB
Thread count: 15
CPU usage (average): 24%

Time (elapsed): 00:04:13.727
##########################################
# DB_FindTrim_MOD1

D:\>avsmeter _fix.avs

AVSMeter 2.8.1 (x86) - Copyright (c) 2012-2018, Groucho2004
AviSynth+ 0.1 (r2728, MT, i386) (0.1.0.0)

Number of frames: 73730
Length (hh:mm:ss.ms): 00:41:00.124
Frame width: 490
Frame height: 360
Framerate: 29.970 (30000/1001)
Colorspace: YV12
Audio channels: 2
Audio bits/sample: 16
Audio sample rate: 44100
Audio samples: 108491612

Frames processed: 73730 (0 - 73729)
FPS (min | max | average): 36.98 | 397.1 | 277.6
Memory usage (phys | virt): 122 | 148 MiB
Thread count: 15
CPU usage (average): 24%

Time (elapsed): 00:04:25.597
##########################################


EDIT: Seems even assigning a global string var to itself still re-allocates more memory.

Frank62
20th September 2018, 23:26
Can you explain this a little bit?
-What do you search? A frame of a clip inside dbase? How? Do you have blobs or binary fields for each frame? Do I get this right?
-And why then do you perform a binary search by hand? Why not use database logic (which works binary searching) and fire some SQL-command?
I admit, I don't get the point at all.

StainlessS
21st September 2018, 03:52
The DataBase is specific to RT_Stats plugin and it dont understand SQL (tis a little basic).
However
DB_FindTrim.avs [Replaced with RT_DBaseFindSeq() plugin function in RT_Stats v2.0]
So it dont matter for this particular function as is already implemented as plugin, but the problem remains
how to make a script function that is likely called at every frame, to be compatible with both GScript and AVS+ version of GScript extensions
(and without using massive amounts of string memory, DB_FindTrim uses only a small script string but some funcs could be quite big).
RT_Stats DBase can hold up to 1024 fields, Bool, Int, Float, String, and BIN(8 bit unsigned int), in v2.0 Beta, up to just below 1TB in size.

In the DBase_DetectScenes() demo thing, all we did was detect scene changes using MVTools2, and create a record for each scene, with
start and end frame of each scene. The DB_FindTrim() thing, at each frame searches the DBase using binary search to determine which
record holds the current frame, and then client can just retrieve start and end frame of each scene from the DBase record.

EDIT:

RT_Stats DBASE functions allow fast access to a file based DataBase where each record can have up to 1024 fields of variable type,
Bool, Int, Float, String, or BIN. BIN is a BYTE (8 bit) sized Int where only lowest 8 bits of Int are stored in the DBase field
as an unsigned 8 bit Int, upper 24 bits ignored. there is also a type double used in DBase, this is only of use to internal RT_Stats
functions, setting or getting type double takes or returns type Float (as Avisynth cannot handle double precision float).
The maximum possible DBase file size is about 1TB, and the number of records is restricted to $7FFFFFFE, about 2 Billion.
Field types Bool and BIN require 1 byte, Int and Float 4 bytes, and String is of fixed maximum length (user chosen) up to
256KB ((256 * 1024), no nul terminating character is stored in a DBase String). The private type Double requires 8 bytes.
A DBase file allows up to 1024 Attributes of types Int or Float, which can be used for whatever a user desires, these
attributes are stored in the DBase file header which is 32768 bytes, so the actual data starts at $8000 hex.
In addition to user attributes, a DBase has currently 10 user String Attributes that can be be used for whatever you want, the
string attributes are currently a maximum of 1024 characters in length.
In addition to user attributes and user string attributes, there are currently 128 ID's (Int or Float) that are intended to be used
by either RT_Stats itself, or script developers, intended function of ID's are to in some way eg link a DBase to a particular
clip, by storing maybe Width, Height, Framecount and perhaps other values, the stored ID's might be used to Validate a DBase
against a clip.
You could use the DBase functions to return multiple results from a script function to the caller via a caller supplied DBase
filename. The DBase functions also make it more likely that script functions could be written to eg find where edits between
two clips have been made, or previously impossible/implausible tasks performed.


EDIT:

RT_Stats ARRAY functions allow fast access to a file based Array of up to 3 dimensions of fixed element type,
Bool, Int, Float, String, or BIN. BIN is a BYTE (8 bit) sized Int where only lowest 8 bits of Int are stored in the Array
as an unsigned 8 bit Int, upper 24 bits ignored.
The maximum possible file size is about 1TB, and depends on number and size of dimensions and element type and size, also
depends upon avilable disk space.
Element types Bool and BIN require 1 byte, Int and Float 4 bytes, and String is of fixed maximum length (user chosen) up to
256KB (256 * 1024, no nul terminating character is stored in an Array String).
Maximum possible size of dimension 1 is $7FFFFFFE but depends upon available disk space and also limited to max 1TB.
RT_ArrayAlloc, allows you to create an array file with a fixed number of dimensions and where the 2nd and/or 3rd dimensions
of a multi-dimensional array are fixed in size but the size of the first dimension can 'grow', single dimension arrays can
'grow' too. You can create an array where the first dimension is 0 (any other dimensions fixed), ie no elements pre-allocated
in array file, in this case you can use RT_ArrayExtend to 'grow' the first dimension of the array so that it can be used. In addition,
single dimension arrays can also use RT_ArrayAppend to add elements to the end of the array.
An Array file allows up to 1024 Attributes of types Int or Float, which can be used for whatever a user desires, these
attributes are stored in the Array file header which is 32768 bytes, so the actual data starts at $8000 hex.
In addition to user attributes, an Array has currently 10 user String Attributes that can be be used for whatever you want, the
string attributes are currently a maximum of 1024 characters in length.
In addition to user attributes and user string attributes, there are currently 128 ID's (Int of Float) that are intended to be used
by either RT_Stats itself, or script developers, intended function of ID's are to in some way eg link an array to a particular
clip, by storing maybe Width, Height, Framecount and perhaps other values, the stored ID's might be used to Validate an array
against a clip.
You could use the Array functions to return multiple results from a script function to the caller via a caller supplied Array
filename, perhaps with some kind of status variable returned directly.