QuickImage – Rev 1

Subversion Repositories:
Rev:
using QuickImage.Database;
using Shipwreck.Phash;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace QuickImage.ImageListViewSorters
{
    public class PerceptionImageListViewItemSorter : IComparer, IComparer<ListViewItem>
    {
        private readonly Dictionary<string, double> _score;
        private readonly Dictionary<string, Digest> _digest;
        private readonly QuickImageDatabase _quickImageDatabase;
        private readonly CancellationToken _cancellationToken;
        private readonly IReadOnlyList<ListViewItem> _listViewItems;
        private readonly TaskCompletionSource<object> _initializedTaskCompletionSource;

        private PerceptionImageListViewItemSorter()
        {
            _score = new Dictionary<string, double>(StringComparer.Ordinal);
            _digest = new Dictionary<string, Digest>();
            _initializedTaskCompletionSource = new TaskCompletionSource<object>();
        }
        public PerceptionImageListViewItemSorter(IReadOnlyList<ListViewItem> items, QuickImageDatabase quickImageDatabase, CancellationToken cancellationToken) : this()
        {
            _listViewItems = items;
            _quickImageDatabase = quickImageDatabase;
            _cancellationToken = cancellationToken;

            Task.Run(RetrieveDigests)
                .ContinueWith(ComputeScores)
                .ContinueWith(task => _initializedTaskCompletionSource.TrySetResult(new { }));
        }

        private async void RetrieveDigests()
        {
            await foreach (var item in _quickImageDatabase.GetAll(_cancellationToken).WithCancellation(_cancellationToken))
            {
                _digest.Add(item.File, item.Hash);
            }
        }

        private void ComputeScores(Task _)
        {
            var collection = new List<(ListViewItem A, ListViewItem B, double Score)>();

            foreach (var a in _listViewItems)
            {
                foreach (var b in _listViewItems)
                {
                    if (a.Name == b.Name)
                    {
                        continue;
                    }

                    if (!_digest.TryGetValue(a.Name, out var p) || !(p is { }) ||
                        !_digest.TryGetValue(b.Name, out var q) || !(q is { }))
                    {
                        continue;
                    }

                    var score = ImagePhash.GetCrossCorrelation(p, q);

                    collection.Add((a, b, score));
                }
            }

            collection.Sort((a, b) => b.Score.CompareTo(a.Score));

            var visit = new HashSet<string>(collection.Count);
            foreach (var (a, b, score) in collection)
            {
                foreach (var imageListViewItem in new[] { a, b })
                {
                    if (visit.Contains(imageListViewItem.Name))
                    {
                        continue;
                    }

                    visit.Add(imageListViewItem.Name);

                    _score.Add(imageListViewItem.Name, score);
                }
            }
        }

        public int Compare(ListViewItem x, ListViewItem y)
        {
            _initializedTaskCompletionSource.Task.Wait(_cancellationToken);

            if (!_score.TryGetValue(x.Name, out var scoreA) || !_score.TryGetValue(y.Name, out var scoreB))
            {
                return 0;
            }

            return scoreB.CompareTo(scoreA);
        }

        public int Compare(object x, object y)
        {
            return Compare(x as ListViewItem, y as ListViewItem);
        }
    }
}