MatchSubtitles() публичный статический Метод

Matches the two subtitles. The problem is that no two subtiles have the same timestamps or the same number of lines (they can be split, omitted, ...). This algotithm tries to find matching lines nontheless. The general algorithm works as following: Compare every "line1" in list1 with every "line2" in list2. Generate a score (between 0 and 1) how good they overlap. If the score is above a certain threshold, there will be an edge (as in graph theory) beween line1 and line2. There result is a bipartite graph that has many connected compontens. Every connected component is then a mached line. There is another step that removes matchings where two lines in same list overlap. See documentation of "RemoveOverlappings()" for more information.
public static MatchSubtitles ( SubtitleMatcherCache cache ) : LinkedList
cache SubtitleMatcherCache
Результат LinkedList
        /// <summary>
        /// Shift all lines in "list to change" by "offset", create a matching, rate the matching and shift lines back.
        /// </summary>
        public double GetRatingOfOffset(double offset, SubtitleMatcher.SubtitleMatcherCache subtitleMatcherParams)
        {
            /*
             * Other ideas for rating:
             *      bonus value for censecutive good bi-match ratings
             */

            // move timings of every line
            subtitleMatcherParams.ShiftTime(offset, false, true);

            // match lines
            var    biMatchedLinesList = SubtitleMatcher.MatchSubtitles(subtitleMatcherParams);
            double finalRating        = 0;

            foreach (var biMatchedLines in biMatchedLinesList)
            {
                double rating = RateBiMatchedLines(biMatchedLines);

                // use rating^3 because that way small values will become smaller
                finalRating += rating * rating * rating;
            }

            // shift timings back
            subtitleMatcherParams.ShiftTime(-offset, false, true);

            return(finalRating);
        }
        /// <summary>
        /// This function will...
        /// a) move all lines to change in slice by offset
        /// b) match the lines in "listToChange" and "referenceList"
        /// c) find a threshold value for matchings
        /// d) find the longest row of consecutive matchings that are above the threshold
        /// e) create slices for lines before and lines after this "good row"
        /// f) reset offset of these remaining lines and put slice into queue
        /// </summary>
        private void ApplyOffset(RemainingSlice slice, double offset, Queue <RemainingSlice> queue)
        {
            UtilsSubtitle.ShiftByTime(slice.listToChangeLines, offset);

            // match lines
            var subtitleMatcherParameters = SubtitleMatcher.GetParameterCache(slice.referenceListLines, slice.listToChangeLines);
            var biMatchedLinesLinkedList  = SubtitleMatcher.MatchSubtitles(subtitleMatcherParameters);
            var biMatchedLinesList        = new List <SubtitleMatcher.BiMatchedLines>(biMatchedLinesLinkedList);

            biMatchedLinesList.Sort(delegate(SubtitleMatcher.BiMatchedLines x, SubtitleMatcher.BiMatchedLines y) {
                return(GetStartTime(x) < GetStartTime(y) ? -1 : 1);
            });


            // --------------------------------------------------
            // find threshold rating
            double averageRating = 0;
            int    numRatings    = 0;

            foreach (var biMatchedLines in biMatchedLinesList)
            {
                double rating = RateBiMatchedLines(biMatchedLines);
                if (rating > 0.0001)                  // ignore "zero" ratings
                {
                    averageRating += rating;
                    numRatings++;
                }
            }
            averageRating /= numRatings;
            double thresholdValue = averageRating * 0.8;

            // --------------------------------------------------
            // Find longest row over threshold rating.
            //
            // Zero ratings may be inbetween good ratings when some
            // lines couldn't get matched (for example street-sign
            // translations that aren't in subtitle file in native language).
            // These are stepped over: There can be a limited number of zero ratings
            // ratings in the row except at the beginning and the end (these will
            // get matched at a different time if possible).
            int numGoodMatched     = 0;
            int currentRowStart    = 0;
            int allowedZeroRatings = 0;


            int maxNumGoodMatched = -1;
            int bestRowStart      = 0;

            for (int index = 0; index < biMatchedLinesList.Count; index++)
            {
                var    biMatchedLines = biMatchedLinesList[index];
                double rating         = RateBiMatchedLines(biMatchedLines);

                if (rating < thresholdValue)
                {
                    // step over zero ratings
                    if (rating > 0.000001 || allowedZeroRatings-- < 0)
                    {
                        numGoodMatched = 0;                         // not a zero rating
                    }
                }
                else
                {
                    // update row start/end
                    if (numGoodMatched == 0)
                    {
                        currentRowStart = index;
                    }
                    numGoodMatched = index - currentRowStart + 1;

                    // save best value
                    if (numGoodMatched > maxNumGoodMatched)
                    {
                        maxNumGoodMatched = numGoodMatched;
                        bestRowStart      = currentRowStart;
                    }

                    allowedZeroRatings = 4;
                }
            }

            // could good row be found?
            if (maxNumGoodMatched == -1)
            {
                return;
            }

            // create new slices left and right
            RemainingSlice newSlice;

            // left slice
            newSlice = GetSubSlice(biMatchedLinesList, 0, bestRowStart);
            //UtilsSubtitle.ShiftByTime(newSlice.listToChangeLines, -offset); // by disabling this code, remaining slices are now embedded in greater slices
            queue.Enqueue(newSlice);

            // right slice
            newSlice = GetSubSlice(biMatchedLinesList, bestRowStart + maxNumGoodMatched + 1, biMatchedLinesList.Count);
            //UtilsSubtitle.ShiftByTime(newSlice.listToChangeLines, -offset);
            queue.Enqueue(newSlice);
        }