public static void Test()
        {
            IdentificationSet <int> set = new IdentificationSet <int>();

            // 1 = 4 = 6 = 7 = 8
            // 2 = 5 = 9 = 10 = 11
            // 3
            set.MakeEquivalent(1, 4);
            set.MakeEquivalent(5, 2);
            set.MakeEquivalent(3, 3);
            set.MakeEquivalent(5, 10);
            set.MakeEquivalent(11, 5);
            set.MakeEquivalent(5, 9);
            set.MakeEquivalent(6, 8);
            set.MakeEquivalent(7, 1);
            set.MakeEquivalent(7, 4);
            set.MakeEquivalent(7, 6);
            set.CompactIdentificationNumber();
            StringBuilder ret = new StringBuilder();

            for (int i = 1; i <= 11; i++)
            {
                ret.AppendLine(string.Format("{0} => {1}", i, set.ConvertToIdentificationNumber(i)));
            }
            System.Windows.MessageBox.Show(ret.ToString());
        }
        public static UserSegmentation[] Identification(FrameSequence frameseq, double maxDistance)
        {
            if (frameseq.Segmentations.Any(seg => seg == null))
            {
                throw new InvalidOperationException("ユーザトラッキングデータがセグメンテーションされていません");
            }

            HashSet <Tuple <RecordAndUser, RecordAndUser> > contemporaryList = new HashSet <Tuple <RecordAndUser, RecordAndUser> >();


            for (int recordNo = 0; recordNo < frameseq.recordNum; recordNo++)
            {
                IEnumerable <MotionData> record = frameseq.GetMotionDataSequence(recordNo);
                int frameIndex = 0;
                foreach (MotionData motionData in record)
                {
                    IList <ulong> users = motionData.bodies.ToList().Select(b => b.TrackingId).ToList();
                    foreach (var tuple in users.SelectMany(u => users.Select(v => new Tuple <RecordAndUser, RecordAndUser>(new RecordAndUser(recordNo, u), new RecordAndUser(recordNo, v)))))
                    {
                        contemporaryList.Add(tuple);
                    }
                    frameIndex++;
                }
            }
            DateTime beginTime  = frameseq.startTime;
            DateTime endTime    = frameseq.endTime;
            double   frequency  = frameseq.frameRate;
            TimeSpan increment  = new TimeSpan((long)(10000000 / frequency));
            long     totalCount = (endTime.Ticks - beginTime.Ticks) / increment.Ticks;
            long     totalIndex = 0;
            Dictionary <Tuple <RecordAndUser, RecordAndUser>, List <double> > distanceListMatrix = new Dictionary <Tuple <RecordAndUser, RecordAndUser>, List <double> >();

            foreach (Frame frame in frameseq.Frames)
            {
                // 現在の時刻での各レコードの各ユーザの各骨格の絶対座標を求める
                Dictionary <ulong, Dictionary <JointType, CvPoint3D64f> >[] absPositions = new Dictionary <ulong, Dictionary <JointType, CvPoint3D64f> > [frameseq.recordNum];
                for (int recordNo = 0; recordNo < frameseq.recordNum; recordNo++)
                {
                    Dictionary <ulong, Dictionary <JointType, CvPoint3D64f> > recordUserPositions = new Dictionary <ulong, Dictionary <JointType, CvPoint3D64f> >();
                    foreach (SerializableBody body in frame.GetBodyList(recordNo))
                    {
                        Dictionary <JointType, CvPoint3D64f> userPositions = new Dictionary <JointType, CvPoint3D64f>();
                        if (body.Joints == null)
                        {
                            continue;
                        }
                        foreach (var jointPair in body.Joints)
                        {
                            CvPoint3D64f posInCamera = jointPair.Value.Position.ToCvPoint3D();
                            CvPoint3D64f posInWorld  = CvEx.ConvertPoint3D(posInCamera, frameseq.ToWorldConversions[recordNo]);
                            userPositions[jointPair.Key] = posInWorld;
                        }
                        recordUserPositions[body.TrackingId] = userPositions;
                    }
                    absPositions[recordNo] = recordUserPositions;
                }
                // 現在の時刻で各レコード間のユーザ間の距離を求める
                for (int i = 0; i < frameseq.recordNum; i++)
                {
                    if (absPositions[i] == null)
                    {
                        continue;
                    }
                    for (int j = i + 1; j < frameseq.recordNum; j++)
                    {
                        if (absPositions[j] == null)
                        {
                            continue;
                        }
                        foreach (var userJoint1 in absPositions[i])
                        {
                            RecordAndUser recordUser1 = new RecordAndUser(i, userJoint1.Key);
                            foreach (var userJoint2 in absPositions[j])
                            {
                                RecordAndUser recordUser2                = new RecordAndUser(j, userJoint2.Key);
                                double        distanceNormal             = getDistance(userJoint1.Value, userJoint2.Value, false);
                                double        distanceMirrored           = getDistance(userJoint1.Value, userJoint2.Value, true);
                                double        distance                   = Math.Min(distanceNormal, distanceMirrored);
                                Tuple <RecordAndUser, RecordAndUser> key = new Tuple <RecordAndUser, RecordAndUser>(recordUser1, recordUser2);
                                List <double> distanceList;
                                if (!distanceListMatrix.TryGetValue(key, out distanceList))
                                {
                                    distanceListMatrix[key] = distanceList = new List <double>();
                                }
                                distanceList.Add(distance);
                            }
                        }
                    }
                }
                totalIndex++;
            }
            // 中央値で集計して小さい順に並べる
            Dictionary <Tuple <RecordAndUser, RecordAndUser>, double> distanceMatrix = distanceListMatrix.ToDictionary(p => p.Key, p => CalcEx.GetMedian(p.Value));
            List <Tuple <RecordAndUser, RecordAndUser, double> >      neighborList   = (
                from x in distanceMatrix
                orderby x.Value
                select new Tuple <RecordAndUser, RecordAndUser, double>(x.Key.Item1, x.Key.Item2, x.Value)
                ).ToList();
            IdentificationSet <RecordAndUser> identificationSet = new IdentificationSet <RecordAndUser>();

            // 同一判定をする
            foreach (var neighbor in neighborList)
            {
                if (neighbor.Item3 > maxDistance)
                {
                    identificationSet.Add(neighbor.Item1);
                    identificationSet.Add(neighbor.Item2);
                    continue;
                }
                IList <RecordAndUser> recordUsers1 = identificationSet.GetEquivalentElements(neighbor.Item1);
                IList <RecordAndUser> recordUsers2 = identificationSet.GetEquivalentElements(neighbor.Item2);
                // 同フレーム内にいるか判定
                bool contemporary = (
                    from ru1 in recordUsers1
                    from ru2 in recordUsers2
                    select new Tuple <RecordAndUser, RecordAndUser>(ru1, ru2)).Any(pair => contemporaryList.Contains(pair));
                if (!contemporary)
                {
                    // 同フレーム内にいなければ同一視
                    identificationSet.MakeEquivalent(neighbor.Item1, neighbor.Item2);
                }
            }
            // 番号を圧縮
            identificationSet.CompactIdentificationNumber();
            // 新しいセグメンテーション番号を与える
            UserSegmentation[] ret = Enumerable.Range(0, frameseq.recordNum).Select(i => new UserSegmentation()).ToArray();
            for (int recordNo = 0; recordNo < frameseq.recordNum; recordNo++)
            {
                foreach (var pair in frameseq.Segmentations[recordNo].Conversions)
                {
                    int frameIndex = pair.Key;
                    Dictionary <ulong, int> newConversions = new Dictionary <ulong, int>();
                    foreach (var conv in pair.Value)
                    {
                        int ident = identificationSet.ConvertToIdentificationNumber(new RecordAndUser(recordNo, conv.Key));
                        newConversions[conv.Key] = ident;
                    }
                    ret[recordNo].Conversions[frameIndex] = newConversions;
                }
                ret[recordNo].fixNumUsers();
            }
            return(ret);
        }