Archiv verlassen und diese Seite im Standarddesign anzeigen : Suche zwei Funktionen
incredible
11. May 2006, 23:06
Ich brauche für das neue PARanoia Skripte für neue optionen.
Ich denke solche habe ich schon mal irgendwo geposted gesehen.
1.) Ein Detector welcher erkennt ob die Source interlaced ist oder nicht.
Ich wollte diese Funktion gerne via Selectrange every über die Source laufen lassen.
2.) Ein ..... Fieldorder Detector. Ja, ich kenne den von Shodan auf avisynth.org. Gibt es noch andere?
3.) Jetzt wirds unverschämt! ;) Aber viell. hat es sowas auch schon mal gegeben.
Ein Detector, der erkennt ob die Quelle aus richtigen individuellen einzelnen Halbbildern besteht, also echtes interlaced oder .... eben bestehend aus Blending murx. Erkennung obs bei NTSC pulldowned ist wäre zudem auch ganz nett ;)
Wenns a bisserl langsam ist - egal.
Ohrfeigen an meine priv. Email, den Rest bitte hier hin schreiben ;)
MOmonster
12. May 2006, 08:14
Also wenn schon solche Skripte, denn doch gleich alles in einem Durchgang. Da Punkt 1 und 2 sich nicht mit selectrange realisieren lassen, empfehle ich aber eine Funktion die einen bestimmten Bereich durcharbeitet (z.B. 1000 Frames) und anschließend ausgibt, was erkannt wurde.
Hab' übers Wochenende wieder kein Avisynth, aber vielleicht ja nächste Woche.;)
Ich philosophiere mal ... wenn man das Video in Fields trennt (einmal TFF, einmal BFF), und sich die Entwicklung der Differenzen zwischen ihnen (mit "geeigneten" Metriken) anschaut, dann würde man sicherlich recht typische Muster bemerken; über die automatische Auswertung dieser Werteentwicklungen mache ich mir da noch keinen Kopf, auch wenn ich mir schon ziemlich sicher bin, dass AviSynth alleine für die Auswertung nicht reichen dürfte. Es muss wohl ein Plugin in einer üblichen mächtigen Hochsprache werden. Neuronale Netze wären bestimmt nützlich.
MOmonster
12. May 2006, 08:48
über die automatische Auswertung dieser Werteentwicklungen mache ich mir da noch keinen Kopf, auch wenn ich mir schon ziemlich sicher bin, dass AviSynth alleine für die Auswertung nicht reichen dürfte. Es muss wohl ein Plugin in einer üblichen mächtigen Hochsprache werden. Neuronale Netze wären bestimmt nützlich.
Also ich halte Avisynth für diese einfache Aufgabe mehr als ausreichend, kompliziert wird es erst mit welchselnder Fieldorder, aber davon wollen wir hier mal nicht ausgehen. Zur Fieldordererkennung wäre es grundsätzlich noch nicht einmal notwendig Bff und Tff miteinander zu vergleichen, ein Clip würde schon ausreichen, aber der Vergleich wäre natürlich sicherer.
incredible
12. May 2006, 10:05
Bzgl. 1)
Das alte Fieldorder detection script von shodan sieht so aus (YV12 obligatorisch):
function CheckTopFirst(clip v1){
global top_hits=1
global bot_hits=1
global text = ""
global text2 = ""
v1 = assumeframebased(v1)
global tff=assumetff(v1).separatefields().bob()
global bff=assumebff(v1).separatefields().bob()
istff = tff.subtitle("Is frame TFF: TRUE").frameevaluate("top_hits=top_hits+1.0")
isnottff = tff.subtitle("Is frame TFF: FALSE").frameevaluate("bot_hits=bot_hits+1.0")
outclip = conditionalfilter(tff,istff, isnottff, "yDifferenceFromPrevious(tff)+ydifferenceToNext(tff)","<","yDifferenceFromPrevious(bff)+yDifferenceToNext(bff)",false)
outclip = frameevaluate(outclip,"text = "+chr(34)+"STATS: TFF = "+chr(34)+" + string(100.0*top_hits/(top_hits+bot_hits)) + " + chr(34) + "%"+chr(34))
outclip = frameevaluate(outclip,"text2 = "+chr(34)+"STATS: BFF = "+chr(34)+" + string(100.0*bot_hits/(top_hits+bot_hits)) + " + chr(34) + "%"+chr(34))
outclip = scriptclip(outclip, "Subtitle(text,y=50)")
outclip = scriptclip(outclip, "Subtitle(text2,y=70)")
return outclip
}
Ich dachte nur viell gäbe es etwas neues.
MOmonster
12. May 2006, 10:14
Na wenn es gut funktioniert. Aber etwas einfach gehalten ist es schon. Es werden lediglich die Summen der Lumadifferenzen miteinander verglichen und keine Randbedingungen berücksichtigt.
incredible
12. May 2006, 11:49
Die Absicht ist, die resultierenden Stats ( "STATS: TFF = ....") in Avisynth als via "Global" als environment Viariable zu sichern, die ich in Paranoia via GetEnvironmentVariable abrufen kann. Am Ende habe ich eben eine (in diesem Beispiel) Summe, die ich mit der Anzahl der verglichenen Frames gegenüberstellen kann. Somit in C
if (TFF_Stats > 80) // ---> 80%
Clip_is_TFF = True;
else
Clip_is_TFF = False;
Wäre somit ein Beispiel falls ich die oben gezeigte Shodan-Routine nutzen sollte.
MOmonster
16. May 2006, 10:26
So, ich hatte gestern etwas Zeit und hier ist das Resultat:
Funktion auf Seite 3
Die Funktion sollte eigentlich für die drei Aufgaben ausreichen. Sie bekommt drei Parameter, nämlich den zu untersuchenden Clip, "start" zur Festlegung des ersten zu untersuchenden Frames (Standard: 0) und "mthresh" um sicher zu gehen, dass die Erkennung nur in bewegenden Szenen erfolgt (Standard: 3.0).
Die Funktion legt sechs globale Variablen fest (ich hoffe du kannst sie nutzen, ansonsten wäre auch eine Logdatei möglich).
progressive - zählt progressive Frames
interlaced - zählt die interlaced Frames
telecined - alles was tfm sonst noch so matched
Die Summe aller drei Werte ergibt die Gesamtzahl der untersuchten Frames (Framedifferenzen kleiner mthresh werden nicht untersucht).
tffcount/bffcount - zählen alle Frames die als tff/bff erkannt werden
Dabei werden jedoch nur Frames bei denen es eindeutig scheint verwendet.
real_interlaced - ist eine Untermenge von "interlaced" und zählt nur die Bewegungen, die so flüssig sind, dass man von tatsächlich interlacten Material ausgehen kann
Zur Auswertung:
Normales Interlacing - wenn real_interlaced größer 70% von Summe aller untersuchten Frames
NTSC pulldown - wenn NTSC und interlaced kleiner 5% und telecined größer eindrittel und kleiner zweidrittel von progressive
Progressive - wenn interlaced kleiner 1% und progressive größer 95%
-> ansonsten irgendein anderer Murks
Gedacht ist es so, dass bei genügender Anzahl untersuchter Frames der Durchlauf abgebrochen wird und die Ergebnisse zur Erkennung genutzt werden können.
Aufgrund der Einfachheit der Funktion gibt es allerdings ein paar Beschränkungen.
1. Sie arbeitet nicht mit Graustufenbildern und nur in yuv
2. Bob ist schnell, aber andere Filter könnten genauere Ergebnisse liefern (ich halte es trotzdem für ausreichend)
3. Die Einstellungen von tfm sind für diesen Gebrauch sicher nicht ganz optimal (die richtige Auswertung der Werte sollte trotzdem ausreichen)
Na, denn hoffe ich mal, dass sie für dich nützlich ist.;)
scharfis_brain
16. May 2006, 12:42
ich habe mich schon (erfolglos) an einer zuverlässigen 24p, 30, 60i erkennungsroutine probiert. wenn das pattern stabil ist, geht das einigermaßen. Ausserdem muss man festlegen, wie statische szenen, oder solche mit wenig bewegeung erkannt werden: interlaced oder progressive?
ich bevorzuge interlaced, denn bei statischen szenen tut ein deinterlacer nicht weh, mouth-combing aber sehr wohl.
weiterhin habe ich es nicht in den griff bekommen, modiwechsel (zwischen 24p, 30p und 60i) sauber zu schalten, ohne dass es zum vor-rück springen an der schaltstelle kommt.
Das script ist jetzt schon abartige 500 Zeilen lang.
MOmonster
17. May 2006, 08:40
@scharfis_brain
Das kann ich mir gut vorstellen.:D Aber mit hybriden Quellen möchte ich mich gar nicht erst rumplagen, die werden zufrieden gelassen. Bei statischen Szenen bevorzuge ich progressive Behandlung. Mit yv12lutxy kann man auch sehr geringe Bewegungen wie mouthmoving noch von tatsächlich statischen Szenen unterscheiden.
@incredible
Hab die Funktion leicht verändert. Sie gibt jetzt eine Logdatei aus, deren Pfad mit dem Parameter "file" verändert werden kann. Start(0) und end(5000) stehen für die Range von Frames die untersucht werden soll und every(17) gibt einen Dezimierungsfaktor vor. Es werden also (end-start)/every Frames zur Erkennung herangezogen und davon wiederum nur die Framedifferenzen verwendet die über mthresh(3.0) liegen. Aufgrund üblicher Patternlängen sollte man es dringed vermeiden every als Teiler (Ausnahme 1) oder Vielfaches von fünf und sechs zu setzen, deshalb auch diese schöne Zahl 17.
Funktion auf Seite 3
incredible
17. May 2006, 12:23
Hi MOmonster!
Erstmal: GANZ VIELEN DANK! .. für diese tolle Initiative! Hätte erst vermutet, dass man mir hier lediglich Links gibt, aber damit hätte ich so schnell nicht gerechnet :)
Mein Windows Install ist nun endlich nach 2 Jahren abgepfiffen und ich muss mir heute Abend, wenn die Zeit es zulässt das Windows von einer Image Partition restoren und ... natürlich die ganzen Apps. etc. neu installieren. (Das nächste Image wird inkl. der Apps. werden ;) )
Demnach kann ich es erst morgen oder dieser Tage testen. Nicht dass du denkst ich lasse es im Winde verlaufen.
Nochmals danke.
incredible
19. May 2006, 22:30
Habe die Funktion ausprobiert ....
.. nur bei mir wird nix, aber auch nix ins log file geschrieben.
Habe die default Werte genommen und als import line lediglich ...
mpeg2source("X:/Progressives.d2v")
= keine Werte im log file.
mpeg2source("X:/Progressives.d2v")
Separatefields()
Trim(1,0)
Weave()
= auch hier keine Werte im log file.
Das log file hat immer 0 kb und es steht somi auch nichts drin.
Eine Ahnung woran es liegen kann?
MOmonster
22. May 2006, 08:55
Ja, ich weiß woran das liegt. Die writeif-bedingung wird meistens nicht erfüllt. Bei mir wurde die log trotzdem immer mit den Werten erstellt sobald der Durchlauf beendet war, deshalb habe ich es nicht geändert. Sonst wurde der Inhalt immer zweimal in die Datei geschrieben, keine Ahnung weshalb. deci = default(every,17)
...
c99=WriteFileIf(source, output, "current_frame==stop", ...->
global deci = default(every,17)
...
c99=WriteFileIf(source, output, "current_frame>=stop-deci", ...
Diese Änderung sollte eine erfüllbare Bedingung bringen. Ich kann es im Moment aber nicht testen. Ansonsten kann man zu Testzwecken ja eine immerwahre Bedingung wählen. Weshalb bei mir am Ende des Durchlauf aber die Log automatisch mit den Werten befüllt wird und bei dir nicht, da habe ich absolut keine Ahnung.
Edit: Ich habe writefile leider auch nicht dazu bewegen können die Logdatei immer mit den aktuellen Werten zu überschreiben. Egal welche Einstellungen, ohne writefileif wurde jede Veränderung in einer neuen Zeile festgehalten.
incredible
22. May 2006, 20:51
Ich habe das Script jetzt dazu gebracht, mir schon mal die Stats als Subtitles anzuzeigen:
Function Interlacing2Reader(clip clp, float "mthresh", int "end", int "start", int "every", string "file")
{
###### PREPARATION ######
begin = default(start,0)
global stop = default(end, 5000)
deci = default(every,17)
global mthresh = default(mthresh,3.0)
global source = clp.trim(begin,stop)
global cltff = source.AssumeTFF().bob()
global clbff = source.AssumeBFF().bob()
global matched = source.tfm(mode=0,pp=2, MI=74,clip2=source.greyscale())
output = default(file,"C:\interlacing.log")
###### VAR.. ######
global interlaced = 0
global progressive = 0
global fieldshifted = 0
global tffcount = 0
global bffcount = 0
global real_interlaced = 0
global currentframe_s = "CurrFrame :"
global interlaced_s = "interlaced : "
global progressive_s = "progressive : "
global fieldshifted_s = "fieldshifted: "
global real_interlaced_s = "real_interlaced: "
global bffcount_s = "bff :"
global tffcount_s = "tff :"
###### Conditional Function Chain, evaluated from bottom to top (!) ######
c99=scriptclip(source, "Subtitle(currentframe_s+string(current_frame),size=12)."+
\ "Subtitle(interlaced_s+string(interlaced),size=12,y=10)."+
\ "Subtitle(progressive_s+string(progressive),size=12,y=20)."+
\ "Subtitle(fieldshifted_s+string(fieldshifted),size=12,y=30)."+
\ "Subtitle(real_interlaced_s+string(real_interlaced),size=12,y=40)."+
\ "Subtitle(bffcount_s+string(bffcount),size=12,y=50)."+
\ "Subtitle(tffcount_s+string(tffcount),size=12,y=60)")
#c99=WriteFileIf(source, output, "current_frame==stop", """ "interlaced:" """, "interlaced", """ ", progressive:" """, "progressive", """ ", fieldshifted:" """,
# \ "fieldshifted", """ ", real_interlaced:" """ , "real_interlaced", """ ", BFF:" """, "bffcount",
# \ """ ", TFF:" """, "tffcount", append=false, flush=true)
c4=FrameEvaluate(c99, " global interlaced = count==2 || count==3 ? interlaced+1 : interlaced
global progressive = count==0 ? progressive+1 : progressive
global fieldshifted = count==1 ? fieldshifted+1 : fieldshifted
global tffcount = count!=20 && (tffc+tffb)*1.25<bffc+bffb ? tffcount+1 : tffcount
global bffcount = count!=20 && (bffc+bffb)*1.25<tffc+tffb ? bffcount+1 : bffcount
global real_interlaced = count==2 && tffc*0.7<tffb && tffc*1.33>tffb ||
\ count==3 && bffc*0.7<bffb && bffc*1.33>bffb ? real_interlaced+1 : real_interlaced")
c3=FrameEvaluate(c4, " global count = (tffc>tffb && tffc>bffc && tffc>bffb ? tffc : (tffb>bffc && tffb>bffb ? tffb :
\ (bffc>bffb ? bffc : bffb))) > mthresh ? (AverageChromaU(matched)==128 ? (tffc+tffb<bffc+bffb ? 2 : 3) :
\ (LumaDifference(source,matched)<0.0002 ? 0 : 1)): 20")
c2=FrameEvaluate(c3, " global tffc = LumaDifference(cltff.selecteven(), cltff.selectodd())
global tffb = LumaDifference(cltff.selectodd().duplicateframe(0), cltff.selecteven())
global bffc = LumaDifference(clbff.selecteven(), clbff.selectodd())
global bffb = LumaDifference(clbff.selectodd().duplicateframe(0), clbff.selecteven())")
return(c2.selectevery(deci,0))
} Mir reichts wenn die Globalen Variablen am Ende ihren Wert besitzen und ich diese Variable des aktuellen Scriptenvironments mit GetVar(...) via der Avisynth.dll API auslesen kann.
Mit den Graustufen ist das so ein Problem, was ist, wenn z.B. ein User einen Stream nutzt der partiell Graustufeninhalte hat, so z.B. wie in KillBill (nur hier als Beispiel erwähnt). Könnte man das ändern? Ansonsten ist die Funktion genial.
Wäre es möglich auch eine NTSC Pulldown-Detection einzubauen?
Ich habe mal im Avisynth wiki nachgelesen und da istw as erwähnt von wegen einem Einhalten eines 5-Frame-Intervalls wegen des angebl. Pulldownpatterns:
http://www.avisynth.org/mediawiki/wiki/Interlace_detection#Algorithm
MOmonster
24. May 2006, 10:55
@incredible
Bei mir hat jetzt auch wieder alles normal gearbeitet. Es wurde aufgrund der Bedingung nichts in die Log geschrieben. Die schon angesprochene Änderung behebt dieses Fehlverhalten. Ich habe die Funktion editiert.
Die Pulldownerkennung ist grundsätzlich möglich. Um eine sichere Erkennung zu gewährleisten müsste man allerdings jedes Frame bzw. mehrere patternlange (also 5 Frames) Abschnitte untersuchen. Wenn wir eine NTSC-Quelle haben, und interlaced unter 2% und der telecined und fieldshifted Anteil etwa 3:2 verteilt ist (2:2 und sogar 2:3 sind auch möglich, wenn auch unwahrscheinlich, da sich tfm bei 1 von 5 Frames für c- oder p-match entscheiden kann), denn ist die Wahrscheinlichkeit für NTSC-Pulldown wirklich sehr hoch.
Wenn du das allerdings für nicht ausreichend hälst, ist eine separate Pulldownerkennung natürlich machbar.;)
Edit:
Wegen dem Graustufenproblem, da werde ich mal testen, ob sich doch noch was anderes finden lässt. Tfm kommt mit vielen Funktionen nicht so gut klar. Das Problem tritt auch nur auf, wenn AverageChromaU tatsächlich 128,0 ist. Das hat sich bei Tests kurzer Graustufenbereiche bis jetzt jedoch nicht gezeigt. Aber bei tatsächlich graustufenencodierten Material (kann man ja zum Beispiel für Abspann usw. einstellen) kommt es zur Fehlerkennungen.
MOmonster
31. May 2006, 11:15
Funktion auf Seite 3Hier ist meine überarbeitete Funktion. Die Interlacingerkennung arbeitet jetzt auch mit Graustufenbildern. Zudem ist auch ein Parameter dazu gekommen. "INF" steht für die Informationsausgabe, bei Null wird nichts extern ausgegeben, bei 1 wird deine Subtitleversion verwendet (etwas langsamer/nicht getestet) und sonst (standard) die log ausgegeben.
So, ich hoffe das wars vorerst, ich plage mich jetzt mit vfw rum.;)
incredible
31. May 2006, 11:22
Momonster, ... im neuen PARanoia gibts ne "Credits" Sektion, wo ich mit Wonne deinen Namen in Zusammenhang mit dem Namen dieser Routine erwähne :)
Werde sie dieses WE testen, vielen Dank
PS: Wenn ich dir ansonsten mit VFW etc. helfen kann, einfach klingeln.
incredible
31. May 2006, 23:42
Ich habe noch die fehlenden String Globals zu der Subtitle-Ausgabe hinzugefügt, die hattest du noch vergessen ;)
Nochmals vielen Dank!
Ich habe festgestellt, dass SelectEvery(deci,0) doch mit selectrangeevery(deci,1) ausgetauscht werden könnte, habe es getestet, die Folge und die Framessind die gleichen, aber weitaus schneller.
Zudem wollte ich gerne, dass ebenso der gesamte Stream mit gescannt werden kann, ohne dass nur jeder 17te Frame analysiert wird.
Jetzt die Frage: Via einem Deci*x kann ich die Anzahl der gesamten zu analysierenden Frames doch kürzen und die Pattern-Logik "17" bleibt erhalten? Habe es getestet und klappt anscheinend gut.
Selbst mit einem optionalen "LowPrec" Argument, dass die Source in ihrer Breite vor der Analyse auf die Hälfte reduziert funktioniert die Analyse auch noch sehr gut, ist aber mit Sicherheit nicht so akkurat.
Ich habe mal aus Spaß eine progr. PAL Source genommen und ein Pulldown angewendet.
assumefps(23.976).assumeframebased()
SeparateFields()
SelectEvery(8, 0,1, 2,3,2, 5,4, 7,6,7)
Weave()
... und sodann die Funktion drüber laufen lassen.
Also entweder habe ich was verpast oder ****wow***.
Demnach scheint das every=17 genau für die erkennung des eigentlichen progressiven Inhalts verantwortlich zu sein? Ist es das, was es mit der krummen "17" auf sich hat? Denn .... nach anwenden der Funktion auf den pulldowned Stream wurde komplett progressive angezeigt. Was ja auch stimmt.
Nur die Frage: Wie siehts denn da mit dem Offset beim Pattern in einem Pulldowned Stream aus? Habe nach dem Pulldown mal via einem Trim(2,0) einen offset simuliert und zumindest wird dann "fieldshifted" ausgegeben - Klasse.
Function Interlacing2Reader(clip clp, float "mthresh", int "end", int "start", int "every", int "amount", int "inf", string "file", bool "lowprec", int "FontSize")
{
###### PREPARATION ######
begin = default(start, 0)
out = default(inf, 1)
amount = default(amount, 10)
output = default(file,"X:\YourPathToThe\interlacing.log")
lowprec = default(lowprec, false)
global stop = default(end, 0)
global deci = default(every, 17)
global mthresh = default(mthresh, 3.0)
global FontSize = default(FontSize, 16)
global multi = (begin==0 && stop==0) ? int(framecount(clp)/(amount*1000))+1 : 1
global source = (lowprec==false) ? clp.trim(begin,stop) : clp.trim(begin,stop).pointresize(width(clp)/2,height(clp))
global cltff = source.AssumeTFF().bob()
global clbff = source.AssumeBFF().bob()
global matched = source.tfm(mode=0,pp=2, MI=74,clip2=cltff.selecteven())
###### VAR.. ######
global interlaced = 0
global progressive = 0
global fieldshifted = 0
global tffcount = 0
global bffcount = 0
global real_interlaced = 0
global currentframe_s = "CurrFrame: "
global interlaced_s = "interlaced: "
global progressive_s = "progressive: "
global fieldshifted_s = "fieldshifted: "
global real_interlaced_s = "real_interlaced: "
global bffcount_s = "bff: "
global tffcount_s = "tff : "
###### Conditional Function Chain, evaluated from bottom to top (!) ######
c99= out==0 ? scriptclip(source, "source") : ( out==1 ?
\ scriptclip(source, "Subtitle(currentframe_s+string(current_frame),size= FontSize )."+
\ "Subtitle(interlaced_s+string(interlaced),size=FontSize,y= FontSize )."+
\ "Subtitle(progressive_s+string(progressive),size=FontSize,y= FontSize*2 )."+
\ "Subtitle(fieldshifted_s+string(fieldshifted),size=FontSize,y= FontSize*3 )."+
\ "Subtitle(real_interlaced_s+string(real_interlaced),size=FontSize,y= FontSize*4 )."+
\ "Subtitle(tffcount_s+string(tffcount),size=FontSize,y= FontSize*5 )."+
\ "Subtitle(bffcount_s+string(bffcount),size=FontSize,y= FontSize*6 )") :
\ WriteFileIf(source, output, "current_frame>stop-deci", """ "interlaced:" """, "interlaced", """ ", progressive:" """, "progressive",
\ """ ", fieldshifted:" """, "fieldshifted", """ ", real_interlaced:" """ , "real_interlaced", """ ", BFF:" """,
\ "bffcount", """ ", TFF:" """, "tffcount", append=false, flush=true) )
c4=FrameEvaluate(c99, " global interlaced = count==2 || count==3 ? interlaced+1 : interlaced
global progressive = count==0 ? progressive+1 : progressive
global fieldshifted = count==1 ? fieldshifted+1 : fieldshifted
global tffcount = count!=20 && (tffc+tffb)*1.25<bffc+bffb ? tffcount+1 : tffcount
global bffcount = count!=20 && (bffc+bffb)*1.25<tffc+tffb ? bffcount+1 : bffcount
global real_interlaced = count==2 && tffc*0.7<tffb && tffc*1.33>tffb ||
\ count==3 && bffc*0.7<bffb && bffc*1.33>bffb ? real_interlaced+1 : real_interlaced")
c3=FrameEvaluate(c4, " global count = (tffc>tffb && tffc>bffc && tffc>bffb ? tffc : (tffb>bffc && tffb>bffb ? tffb :
\ (bffc>bffb ? bffc : bffb))) > mthresh ? (abs(LumaDifference(matched,cltff.selectodd())-tffc)<0.0001 ?
\ (tffc+tffb<bffc+bffb ? 2 : 3) : (LumaDifference(source,matched)<0.0002 ? 0 : 1)): 20")
c2=FrameEvaluate(c3, " global tffc = LumaDifference(cltff.selecteven(), cltff.selectodd())
global tffb = LumaDifference(cltff.selectodd().duplicateframe(0), cltff.selecteven())
global bffc = LumaDifference(clbff.selecteven(), clbff.selectodd())
global bffb = LumaDifference(clbff.selectodd().duplicateframe(0), clbff.selecteven())")
#return(c2.selectevery(deci*multi,0))
return(c2.selectrangeevery(deci*multi,1))
}
MOmonster
1. June 2006, 08:43
Ich habe noch die fehlenden String Globals zu der Subtitle-Ausgabe hinzugefügt, die hattest du noch vergessen ;)
Nochmals vielen Dank!
Oops! Ja, ich habe einfach nur deine Subtitleversion reinkopiert und darauf irgendwie nicht geachtet.
Ich habe festgestellt, dass SelectEvery(deci,0) doch mit selectrangeevery(deci,1) ausgetauscht werden könnte, habe es getestet, die Folge und die Framessind die gleichen, aber weitaus schneller.
Ich wusste nicht, dass selectrangeevery deutlich schneller ist. Danke für die Info.
Zudem wollte ich gerne, dass ebenso der gesamte Stream mit gescannt werden kann, ohne dass nur jeder 17te Frame analysiert wird.
Jetzt die Frage: Via einem Deci*x kann ich die Anzahl der gesamten zu analysierenden Frames doch kürzen und die Pattern-Logik "17" bleibt erhalten? Habe es getestet und klappt anscheinend gut.
Selbst mit einem optionalen "LowPrec" Argument, dass die Source in ihrer Breite vor der Analyse auf die Hälfte reduziert funktioniert die Analyse auch noch sehr gut, ist aber mit Sicherheit nicht so akkurat.
Das stimmt leider nicht so ganz. Zum einen kannst du die every ja frei wählen und der Wert 17 ist absolut kein muss. Für deci*x besteht also keine Notwendigkeit. Der gewähle Dezimierungsfaktor darf einfach nicht die Teiler 2, 3 oder 5 haben. Sollte das nämlich der Fall sein, wird bei Pattern der Länge 5 (oder auch 5*5) oder 6 (->üblich Bsp: Pulldown, Normkonvertierung) nicht jeder Frame eines Patterns behandelt. Zwar werden bei der Zahl 17 nur wenige Frames genutzt, allerdings ist jedes neue Frame an einer anderen Position im jeweiligen Pattern. Also kann für every auch 7 oder 13 zum Beispiel gewählt werden (Primzahlen sind immer gut).
Ich habe mal aus Spaß eine progr. PAL Source genommen und ein Pulldown angewendet.
assumefps(23.976).assumeframebased()
SeparateFields()
SelectEvery(8, 0,1, 2,3,2, 5,4, 7,6,7)
Weave()
... und sodann die Funktion drüber laufen lassen.
Also entweder habe ich was verpast oder ****wow***.
Demnach scheint das every=17 genau für die erkennung des eigentlichen progressiven Inhalts verantwortlich zu sein? Ist es das, was es mit der krummen "17" auf sich hat? Denn .... nach anwenden der Funktion auf den pulldowned Stream wurde komplett progressive angezeigt. Was ja auch stimmt.
Nur die Frage: Wie siehts denn da mit dem Offset beim Pattern in einem Pulldowned Stream aus? Habe nach dem Pulldown mal via einem Trim(2,0) einen offset simuliert und zumindest wird dann "fieldshifted" ausgegeben - Klasse.
Das sollte eigentlich nicht der Fall sein. Ich habe es mit einer NTSC Hybrid Quelle getestet und die Erkennung lieferte das erwartete Ergebnis. Um sicher zu gehen, dass die Erkennung macht, was sie soll, kann man mit every=1 erstmal testen, ob alles funktioniert. Das Conditional Enviroment kann manchmal Probleme machen, wenn vorher ein selecteven(), selectevery() usw. angewendet wird. Also am besten Source vorher zwischenspeichern. Zur guten Erkennung müssen denn natürlich noch genug Frames bearbeitet werden und die Bewegungen dürfen nicht zu gering ausfallen. Mit trim(2,0) wird denn nur noch fieldshifted ausgegeben? Hast du bereits den Multiplikator verwendet? Einen "amount" Parameter, der zum Beispiel 1000 Frames gleichmäßig verteilt über die Source zur Erkennung heranzieht ohne in den Pattern zu fallen, kann ich gerne integrieren, every halte ich dann aber für unnötig.
PS: Wenn ich dir ansonsten mit VFW etc. helfen kann, einfach klingeln. Danke für das Angebot. Erstmal sehen wie weit ich es mit meinen Quellen schaffe. Gegebenfalls komme ich darauf zurück.;D
Edit:Hier (http://forum.doom9.org/showthread.php?t=111870) ist gerade ein neues Tool erschienen, welches bestimmt sehr brauchbar für dich sein könnte, habe es allerding noch nicht getestet.
incredible
1. June 2006, 11:29
... Zwar werden bei der Zahl 17 nur wenige Frames genutzt, allerdings ist jedes neue Frame an einer anderen Position im jeweiligen Pattern. .....
... Denn .... nach anwenden der Funktion auf den pulldowned Stream wurde komplett progressive angezeigt. ...
.... Das sollte eigentlich nicht der Fall sein. ...
Also wie gesagt, nehme ich eine Progressive Source und wende ein Pulldown drauf an wie oben beschrieben, wird der gesamte Film auf ca. 2% gestückelte Stream (via einem errechnetem deci*multi) als Progressive erkannt. Daher scheint jeder xte genommene Frame im jeweiligen Pattern-Abschnitt genau der zu sein, welcher im "kontinuierlichen" Pattern immer progressiv daherkommt. .... sollte nich genau das der 17ner deci verhindern? Und die Pulldown Routine oben habe ich von der Avisynth Homepage, ich weiss nicht, ob es da versch. Arten von Pulldownpattern gibt, respektive wo im Pattern nun progressive und wo doubled/fieldshifted angesiedelt ist.
Am besten ich würde hingehen und via meinem Programm eine Xpass Analyse vornehmen.
Pass 1. Finden eines Patterns mit Bewegung (oder mehrere motion-chunks des kompl. Streams wg. Hybrid).
Pass 2. Dieses Pattern mit deci=1 komplett analysieren, und dadurch rausfinden wie das Pattern ist. Demnach kann könnte man schnell sehen ob da lediglich was fieldshifted (SexInTheCity 2nd Season DVDs z.B.) oder Telecined ist. Es wäre somit egal, ob innerhalb des gesamten Filmes sich da die Patternoffsets verschieben (Schnitt auf bereits Telecined Material), es geht lediglich darum zu erkennen welche Folge das Pattern hat.
Bei Hybrid Streams kanns du das natürluch vergessen. Wäre aber auch möglich, wenn du im 1ten Pass quer über den Stream mehrere Patternfolgen analysierst. Sind diese untereinander unterschiedlich, also die eine Folge "real interlaced" und die andere "fieldshifted" dann ists Hybrid.
Pass 3. Die Funktion wie gewohnt mit deci=x über den gesamten (chunked) Stream drüber laufen lassen. Sodann ergebnisse auswerten.
Einen "amount" Parameter, der zum Beispiel 1000 Frames gleichmäßig verteilt über die Source zur Erkennung heranzieht ohne in den Pattern zu fallen, kann ich gerne integrieren, every halte ich dann aber für unnötig.
Ich denke beides macht sinn - auf jeden Fall. Daher habe ich auch mit eingefügt, dass der deci-multi nur funktioniert, wenn man die chunks nicht manuell via start/end definiert hat, denn dann wird deci*multi autom. zu deci*1.
Eine individuelle deci Einstellung macht so einen Pass2 wie o.g. überhaupt erst möglich ;)
Danke für das Angebot. Erstmal sehen wie weit ich es mit meinen Quellen schaffe. Gegebenfalls komme ich darauf zurück.;D
Edit:Hier (http://forum.doom9.org/showthread.php?t=111870) ist gerade ein neues Tool erschienen, welches bestimmt sehr brauchbar für dich sein könnte, habe es allerding noch nicht getestet. Gestern 23:42 incredible
Ja, ich kenne Berrinhams Analyse und auch die Beschr. auf Avisynth-Wiki, aber als ich seine vorgänger Version jenes Programms getestet hatte, hat dies mir bei einem simplen "dumb-phaseshift" Separatefileds().trim(1,0).Weave() testscript einen "Hybrid" Stream gemeldet, was mit deiner Routine nicht der Fall ist, da der Zähler hier bei dir NUR bei "fieldshifted steigt" und nicht zudem bei "interlaced" etc.
Was hast du mit vfw vor?
Ich hatte mal eine avisynthredirect.dll Änderung im doom9 veröffentlicht, welche auch Dizmon für seinen Wrapper genutzt hat. Vorteil: Du kannst die Frames direkt via Avisynth holen und zudem brauchst du kein temp skript auf Platte zu schreiben um das Video reinzuholen.
Der Clou: Du kannst via der o.g. Dll die aktuell definierten Global Variablen deines Scriptes direkt in deine Applikation einlesen. Bedeutet du könntest ebenso auch in den Environment Variablen Speicher der entsprechenden Global Variable neue Werte via deiner Applikation setzen.
Somit kannst du nach jedem geholten Frame eines Conditional scriptes ebenso deine Appl. den neuen Wert jener Global Variable ändern ;)
... das würde solche Dinge wie o.g. und auch eine richtige Avisynth GUI mit slider controls in Echtzeit bei filtern etc. möglich machen.
vielleicht auch interessant:
BAutoDeint: A small automatic deinterlacing program based on AviSynth
http://forum.doom9.org/showthread.php?t=111870
incredible
1. June 2006, 16:54
Hat Momonster zwei Postings weiter oben erwähnt ( http://forum.gleitz.info/showpost.php?p=271970&postcount=19 ) und ich habe darunter was dazu geschrieben was die vorherige Version betrifft. Diese jetzige Version habe ich noch nicht getestet. ;)
Was auch sehr interessant ist, sind dort die Links zur Programmiersprache "D". :)
http://www.digitalmars.com/d/
ich wollt mir demnächst erstmal Ruby noch antun ;)
MOmonster
2. June 2006, 08:14
Was auch sehr interessant ist, sind dort die Links zur Programmiersprache "D". :)
http://www.digitalmars.com/d/
Ja, das ist mir auch gleich ins Auge gefallen. Solange es keine Standardisierung gibt ist das aber wohl noch nicht so interessant.
Daher scheint jeder xte genommene Frame im jeweiligen Pattern-Abschnitt genau der zu sein, welcher im "kontinuierlichen" Pattern immer progressiv daherkommt. .... sollte nich genau das der 17ner deci verhindern?
Ja, das 17ner deci sollte das eigentlich verhindern, wenn die 17 jetzt aber mal ein Vielfaches von fünf genommen wird, hat man von der 17 nichts mehr.
Zur Funktion:
Aus amount und every wird emount. Wird emount unter 100 gesetzt arbeitet es wie every, ansonsten wird mit den Werten die Anzahl der zu untersuchenden Frames angegeben. Ob nun unter oder uber hundert, der angegebene bzw. resultierende Dezimierungsfaktor wird so angepasst, dass er nicht mehr durch 2, 3 oder 5 teilbar ist. Dadurch ist emount zwar immer nur eine grobe Annährung an die tatsächlichen Gegebenheiten, dafür treten die oben genannten Patternprobleme nicht mehr auf. Achja, emount (default:300) wird immer an die vorgegebene Länge (end) angepasst.
Function Interlacing2Reader(clip clp, float "mthresh", int "start", int "end", int "emount", int "inf", string "file", bool "lowprec", int "FontSize")
{
###### PREPARATION ######
begin = default(start, 0)
out = default(inf, 1)
output = default(file,"C:\interlacing.log")
lowprec = default(lowprec, false)
global stop = default(end, framecount(clp))
global mthresh = default(mthresh, 3.0)
global FontSize = default(FontSize, 16)
amount = default(emount,300)
dec = amount<100 ? amount : int(stop/amount)+1
mod3 = dec%3
mod5 = dec%5
global deci = dec%2==1 ? ( mod3!=0 && mod5!=0 ? dec : (mod3!=1 && mod5!=3 ? dec+2 : dec-2) ) :
\ ( mod3!=1 && mod5!=1 ? dec-1 : (mod3!=2 && mod5!=4 ? dec+1 : dec+3) )
global source = lowprec==false ? clp.trim(begin,stop) : clp.trim(begin,stop).pointresize(width(clp)/2,height(clp))
global cltff = source.AssumeTFF().bob()
global clbff = source.AssumeBFF().bob()
global matched = source.tfm(mode=0,pp=2, MI=74,clip2=cltff.selecteven())
###### VAR.. ######
global interlaced = 0
global progressive = 0
global fieldshifted = 0
global tffcount = 0
global bffcount = 0
global real_interlaced = 0
global currentframe_s = "CurrFrame: "
global interlaced_s = "interlaced: "
global progressive_s = "progressive: "
global fieldshifted_s = "fieldshifted: "
global real_s = " (real: "
global real_s2 = ")"
global bffcount_s = "bff: "
global tffcount_s = "tff : "
###### Conditional Function Chain, evaluated from bottom to top (!) ######
c99= out==0 ? scriptclip(source, "source") : ( out==1 ?
\ scriptclip(source, "Subtitle(currentframe_s+string(current_frame),size= FontSize )."+
\ "Subtitle(interlaced_s+string(interlaced)+real_s+string(real_interlaced)+real_s2,size=FontSize,y= FontSize )."+
\ "Subtitle(progressive_s+string(progressive),size=FontSize,y= FontSize*2 )."+
\ "Subtitle(fieldshifted_s+string(fieldshifted),size=FontSize,y= FontSize*3 )."+
\ "Subtitle(tffcount_s+string(tffcount),size=FontSize,y= FontSize*4 )."+
\ "Subtitle(bffcount_s+string(bffcount),size=FontSize,y= FontSize*5 )") :
\ WriteFileIf(source, output, "current_frame>stop-deci", """ "interlaced:" """, "interlaced", """ ", progressive:" """, "progressive",
\ """ ", fieldshifted:" """, "fieldshifted", """ ", real_interlaced:" """ , "real_interlaced", """ ", BFF:" """,
\ "bffcount", """ ", TFF:" """, "tffcount", append=false, flush=true) )
c4=FrameEvaluate(c99, " global interlaced = count==2 || count==3 ? interlaced+1 : interlaced
global progressive = count==0 ? progressive+1 : progressive
global fieldshifted = count==1 ? fieldshifted+1 : fieldshifted
global tffcount = count!=20 && (tffc+tffb)*1.25<bffc+bffb ? tffcount+1 : tffcount
global bffcount = count!=20 && (bffc+bffb)*1.25<tffc+tffb ? bffcount+1 : bffcount
global real_interlaced = count==2 && tffc*0.7<tffb && tffc*1.33>tffb ||
\ count==3 && bffc*0.7<bffb && bffc*1.33>bffb ? real_interlaced+1 : real_interlaced")
c3=FrameEvaluate(c4, " global count = (tffc>tffb && tffc>bffc && tffc>bffb ? tffc : (tffb>bffc && tffb>bffb ? tffb :
\ (bffc>bffb ? bffc : bffb))) > mthresh ? (abs(LumaDifference(matched,cltff.selectodd())-tffc)<0.0001 ?
\ (tffc+tffb<bffc+bffb ? 2 : 3) : (LumaDifference(source,matched)<0.0002 ? 0 : 1)): 20")
c2=FrameEvaluate(c3, " global tffc = LumaDifference(cltff.selecteven(), cltff.selectodd())
global tffb = LumaDifference(cltff.selectodd().duplicateframe(0), cltff.selecteven())
global bffc = LumaDifference(clbff.selecteven(), clbff.selectodd())
global bffb = LumaDifference(clbff.selectodd().duplicateframe(0), clbff.selecteven())")
return(c2.selectrangeevery(deci,1))
}
Und wegen vfw: Ich möchte einen Codec programmieren (zunächst lossless). Ich bin gerade dabei den Code erstmal mit selbserstellten log Files zu testen, aber habe jetzt auch genug Informationen um als nächstes den Code mit einer BMP zu testen. Am hilfreichsten waren bis jetzt Quellcodes von anderen C-Codecs. Aber es wird wohl noch etwas Zeit in Anspruch nehmen.
"Ich möchte einen Codec programmieren (zunächst lossless)."
Wenn man fragen darf: auf welcher Theoriebasis? (Wavelet, Mpeg4, fractal, was eigenes,..)
Cu Selur
MOmonster
2. June 2006, 09:46
OFFTOPIC: Weder Wavelet noch DCT. Es gibt genügend und zudem auch sehr gute Implementierungen davon. Ich glaube nicht, dass ich da irgendetwas besser machen würde. Fractal war noch eine Überlegung, aber vorerst viel zu komplex für mich als Anfänger. Die verlustbehaftete Version wird linearadaptiv arbeiten und basiert auf keiner mir bekannten Komprimierungsgrundlage, was eben genau der Anlass für mich ist, diesen Versuch zu starten. Zu gegebener Zeit mache ich einen Thread dazu auf, aber vorher muss sich der Code noch als brauchbar erweisen.
incredible
2. June 2006, 09:51
Hey super, dann ist heute Abend testen angesagt.
Nur .... wenn die 17 jetzt aber mal ein Vielfaches von fünf genommen wird, hat man von der 17 nichts mehr.
... aber bei deiner ersten "deci" version wäre sodann auch irgendwann mal ein 15ter Durchlauf eines 17ten Frames via SelectEvery(deci,0) stattgefunden.
Aber wie auch immer, dass Teil wird immer besser :) Danke
Bzgl. Codec: Interessant wäre auch ein Versuch den LZW Algorhythmus bei Videostreams anwenden zu können. Diesen nutze ich hier im Job immer in Photoshop. Weiss aber nicht ob z.B. Huffy da sogar besser abschneidet UND ob LZW mit YUV alignments klar kommt.
MOmonster
2. June 2006, 10:09
@incredible
Zur besseren Vorstellung nehme ich mal einfach immer den modulo5 Wert des jeweiligen Vielfachen von 17:
1. (17*1)%5=2
2. (17*2)%5=4
3. (17*3)%5=1
4. (17*4)%5=3
5. (17*5)%5=0
6. (17*6)%5=2
...
Wenn multi jetzt ein Vielfaches von 5 ist (Beispiel 10):
1. (17*10*1)%5=0
2. (17*10*2)%5=0
3. (17*10*3)%5=0
4. (17*10*4)%5=0
5. (17*10*5)%5=0
6. (17*10*6)%5=0
Daraus resultiert das vorher besprochene Patternproblem.
Nochmal zum Codec: Bei dem Codec geht es darum meine eigene Idee umzusetzen und diese hat glaub ich sehr wenig mit LZW gemeinsam und lässt auch nicht so gut damit kombinieren (weiß im Moment aber auch nicht viel darüber).
Eastermeyer
4. June 2006, 09:47
Moment , also nen LZW-Codec hab ich schon ziemlich häufig gesehen.
Zum Beispiel von Gabest:
http://www.codeclibrary.com/details.php?fourcc=GLZW
http://212.9.224.7/~video/CODECs/Gabest_GLZW_Video_Codec_v.1.0.exe
Was auch sehr interessant ist, sind dort die Links zur Programmiersprache "D". :)
http://www.digitalmars.com/d/
Auf B folgte C. Und nun kommt D. :ja: Und als Gegenpol zu C++ gibt es C--.
Naito
6. November 2006, 20:05
MOmonster, ich hab da ein kleines Problem.
___
Anmerkungen vorweg:
* Man sollte erwähnen, dass die TIVTC.dll (von tritical) mit geladen muss. :D
* Könntest du eine Überprüfung einbauen, damit das Startframe nicht kleiner 0 sein kann. (Ich weiß das es keine negativen Frames gibt. Als ich jedoch nach trim()-Notation, also mit Null beginnend, das Skript laufen ließ, "drehte es irgendwie durch". Schlecht zu beschreiben.)
___
Ich hab versucht dein Script als externes "Interlacing2Reader.avs" über die OVA 2 von Lupin the 3rd (http://forum.gleitz.info/showpost.php?p=306922&postcount=4) laufen zu lassen. Eigentlich über den gesamten Film (132.438 Frames). Nur hat er das irgendwie nicht gemocht. :redface: Also hab ich den Auschnitt auf 500 begrenzt (beginnend bei 1).
Das Skript hab ich mit dem MPC abgespielt, 60min lang. Auf dem genutzten CPU-Kern gab es eine Prozessorauslastung von ~90% und 500MB Speichernutzung.
Ergebnis:
CurrFrame: 164
interlaced: 6855 (real: 3077)
progressive: 4914
fieldshifted: 3245
ttf: 14750
bff: 0
Wie gesagt, er lief, und lief und lief ... 60 min lang, dann hab ich abgebrochen. (hat leider auch kein Logfile geschrieben). Frage: Hört der auch mal auf?
Ist es möglich eine "Restanzeige" (Lauf x von y) einzublenden?
Ich versuch mal das Ergebnis anhand dessen was du hier (http://forum.gleitz.info/showpost.php?p=269583&postcount=8) gesagt hast zu interpretieren:
6855 (i) + 4914 (p) + 3245 (fs) = 15014 (summe)
Interlaced?: 3077 (real_i) / 15014 (summe) = 0,205 < 0,70 (i) ==> nicht Interlaced
NTSC pulldown?: 3245 (fs) / 4914 (p) = 0,66 ==> NTSC pulldown (fieldshiefted liegt zw. 33% und 66% von progressiv)
Progressiv?: 4914 (p) / 15014 (summe) = 0,33 < 0,95 (p) ==> nicht progressiv
Wenn es stimmt was ich da gerechnet habe, dann ist die OVA 2 "NTSC Pulldown" (und ich hab schon wieder versucht TDeint auf sowas anzuwenden). :wall:
MOmonster
8. November 2006, 12:01
@Naito
Könntest du bitte mal das gesamte Skript posten. Currframe 164 aber bei der Erkennung sind viel mehr herangezogen worden. Da muss in der tat irgendetwas falsch gelaufen sein. Stecke gerade nicht in der Materie, weil ich an Mrestore und moderate_ee arbeite, aber wenn du das Skript postest werfe ich mal ein Blick drauf.
Die Werte sehen übrigens nach einer Normwandlung aus. Bei NTSC Pulldown werden nur fieldshifted und progressiv erkannt aber keine interlaced fields (bzw verschwindet wenig).
Naito
9. November 2006, 17:08
Mein Skript sieht so aus:
LoadPlugin("I:\Programme\DGMPGDec\DGDecode.dll")
LoadPlugin("I:\Programme\AviSynth\filter\TIVTC.dll")
Import("I:\Programme\AviSynth\filter\Interlacing2Reader.avs")
MPEG2Source("H:\Lupin\OVA 2\OVA 2.d2v", cpu=0)
Interlacing2Reader(start=1, end=500, file="H:\500.txt")
Im Anhang findest du nochmal die "Interlacing2Reader.avs", falls was beim Copy&Paste schief gegangen sein sollte. Geändert habe ich aber nur "output = default(file,"C:\interlacing.log")" zu "output = default(file,"H:\interlacing.log")".
Currframe 164 aber bei der Erkennung sind viel mehr herangezogen worden. Da muss in der tat irgendetwas falsch gelaufen sein.
Das ich "Currframe 164" geschrieben habe liegt daran, dass das Skript ja immer von von nach hinten durchläuft. Abgeschrieben habe ich die Werte, als er wieder ganz am Anfang war. Die höchste Framezahl die unter "Currframe" stand war (wenn ich mich recht erinnere) irgendwas bei 31xx.
Stecke gerade nicht in der Materie, weil ich an Mrestore und moderate_ee arbeite, aber wenn du das Skript postest werfe ich mal ein Blick drauf.
Weiß ich, ich will deswegen auch nicht drängeln. Aber ich wollt die Funktion unbedingt mal ausprobieren. (Ich hab leider immer noch nicht den Durchblick, bei den ganzen Wandlungsmethoden; geschweige denn, wie ich erkenne was es ist.)
Die Werte sehen übrigens nach einer Normwandlung aus. Bei NTSC Pulldown werden nur fieldshifted und progressiv erkannt aber keine interlaced fields (bzw verschwindet wenig).
Danke für den Tipp, werd schaun was ich tun kann.
MOmonster
21. December 2006, 08:55
So ich hab mich gestern mal an die Funktion gesetzt. Lief wirklich sehr instabil, was aber auch mit den letzten TIVTC Versionen zu tun hatte (rc7-rc9). Also unbedingt die neuste Version verwenden.
Ich habe den Code überarbeitet, ein paar Dinge verbessert und neu strukturiert. Sollte TIVTC nicht stabil laufen, kann man jetzt zum Beispiel auch auf Decomb zurueckgreifen.
Ich habe die letzte Version hier (http://forum.gleitz.info/showthread.php?t=31622) integriert.
vBulletin® v3.7.3, Copyright ©2000-2009, Jelsoft Enterprises Ltd.