#c_sharp #cpp #распознавание_образов
Пишу автокликер для одной мобильной игры (скрин ниже). Задача находить на карте объекты. Точнее, программа делает скриншот экрана игры запущенной в эмуляторе Bluestacks, получаю bitmap, и нужно искать объекты на карте по фрагментам на скриншоте. Подскажите алгоритм поиска фрагмента на изображении?
Ответы
Ответ 1
Использование OpenCV конечно должно быть более успешным. Я поэкспериментировал с AForge получилось тоже неплохо Вот класс работы с библиотекой, фактически само использование занимает 2 строки, каюсь, взял эту часть из интернета, остальное написал сам public class AforgeService { //найденные совпадения private TemplateMatch[] _matchings; ////// Количество найденных совпадений /// public int CountMatchings { get => _matchings != null ? _matchings.Length : 0; } //ctor public AforgeService() { } ////// Содержит ли исходное изображение представленый образец /// /// путь к файлу исходного изображения /// путь к файлу образца ///true если содержит public async TaskIsContains(string pathOriginalImage, string pathSampleImage) { if (String.IsNullOrEmpty(pathOriginalImage)) throw new ArgumentNullException(nameof(pathOriginalImage)); if (String.IsNullOrEmpty(pathSampleImage)) throw new ArgumentNullException(nameof(pathSampleImage)); var sample = new Bitmap(pathSampleImage); var orig = new Bitmap(pathOriginalImage); //пользуемся библиотекой ExhaustiveTemplateMatching tm = new ExhaustiveTemplateMatching(0.921f); _matchings = await Task.Run(() => tm.ProcessImage(orig, sample)); return _matchings.Any(); } /// /// Получение коллекции найденных мест где находится образец /// ///коллекция найденных мест public ListGetPlaces() { List result = new List (); if (CountMatchings == 0) return result; int id = 0; foreach (var match in _matchings) { FoundPlace place = new FoundPlace { Id = ++id, Similarity = match.Similarity, Top = match.Rectangle.Top, Left = match.Rectangle.Left, Height = match.Rectangle.Height, Width = match.Rectangle.Width }; result.Add(place); } return result; } } Этот класс для сохранения найденного места public class FoundPlace { public int Id { get; set; } public double Left { get; set; } public double Top { get; set; } public double Width { get; set; } public double Height { get; set; } public double Similarity { get; set; } } Это класс вьюмодели public class MainViewModel : INotifyPropertyChanged, IDisposable { public event PropertyChangedEventHandler PropertyChanged; private readonly IMainWindow _mainWindow; private string _pathOriginalImage; //ctor public MainViewModel(IMainWindow mainWindow) { _mainWindow = mainWindow; } /// /// Флаг запуска поиска, для выкл./вкл. кнопок /// private bool _IsSearching; public bool IsSearching { get => _IsSearching; set { _IsSearching = value; SelectSampleCommand.RaiseCanExecuteChanged(); SearchCommand.RaiseCanExecuteChanged(); } } ////// Исходное изображение (поле игры) /// public string OriginalImage { get => @"~\..\Assets\Original.jpg"; } ////// Образец для поиска /// private string _SampleImage; public string SampleImage { get => _SampleImage; set { _SampleImage = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SampleImage))); SearchCommand.RaiseCanExecuteChanged(); } } ////// Текстовое сообщение о процессе /// private string _Message; public string Message { get => _Message; set { _Message = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Message))); } } ////// Список найденных мест для ListBox /// private List_Places; public List Places { get { return _Places; } set { _Places = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Places))); } } private FoundPlace _SelectedPlace; public FoundPlace SelectedPlace { get => _SelectedPlace; set { _SelectedPlace = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedPlace))); _mainWindow.DrawPlace(_SelectedPlace); } } /// /// Кнопка Выбрать /// private RelayCommand _SelectSampleCommand; public RelayCommand SelectSampleCommand { get => _SelectSampleCommand = _SelectSampleCommand ?? new RelayCommand(OnSelectSample, CanSelectSample); } private bool CanSelectSample() { if (IsSearching) { return false; } return true; } private void OnSelectSample() { string file = _mainWindow.SelectSample(); if (String.IsNullOrEmpty(file)) return; SampleImage = file; } ////// Кнопка Искать /// private RelayCommand _SearchCommand; public RelayCommand SearchCommand { get => _SearchCommand = _SearchCommand ?? new RelayCommand(OnSearch, CanSearch); } private bool CanSearch() { if (String.IsNullOrEmpty(SampleImage) || IsSearching) { return false; } return true; } private async void OnSearch() { Message = "Ждите..."; IsSearching = true; AforgeService service = new AforgeService(); try { using (OverrideCursor cursor = OverrideCursor.GetWaitOverrideCursor()) { string pathOrigin = GetOriginalImage(); bool isContains = await service.IsContains(pathOrigin, SampleImage); if (isContains) { Places = service.GetPlaces(); } else { Places = new List(); } } } catch (Exception ex) { var message = $"Возникла ошибка: {ex.Message}"; _mainWindow.ShowMessage(message, "Ошибка"); } finally { Message = $"Найдено мест: {service.CountMatchings}"; IsSearching = false; } } /// /// Получение пути к оригинальному изображению (полю игры) /// ///private string GetOriginalImage() { if (!String.IsNullOrEmpty(_pathOriginalImage) && File.Exists(_pathOriginalImage)) { return _pathOriginalImage; } _pathOriginalImage = Path.Combine(Path.GetTempPath(), "Original.jpg"); Uri imgUri = new Uri("pack://application:,,,/Assets/Original.jpg"); StreamResourceInfo imgStream = Application.GetResourceStream(imgUri); using (Stream imgs = imgStream.Stream) using (FileStream fs = File.Create(_pathOriginalImage)) { byte[] ar = new byte[imgs.Length]; imgs.Read(ar, 0, ar.Length); fs.Write(ar, 0, ar.Length); } return _pathOriginalImage; } /// /// IDisposable /// public void Dispose() { if (!String.IsNullOrEmpty(_pathOriginalImage) && File.Exists(_pathOriginalImage)) { File.Delete(_pathOriginalImage); } } } Весь пример можно скачать здесьОтвет 2
Думаю, тебе хватит функционала реализованного в OpenCV OpenCV (англ. Open Source Computer Vision Library, библиотека компьютерного зрения с открытым исходным кодом) — библиотека алгоритмов компьютерного зрения, обработки изображений и численных алгоритмов общего назначения с открытым кодом. (с) Википедия. Вот неплохие уроки по ней, а вот то, что тебе в теории может пригодиться.Ответ 3
Есть большая, сложная и популярная область технологий, называемая распознанием образов. По большей части там используются глубокие нейронные сети, но не только. И вряд ли в Вашем случае нужна такая сложность (хотя и может вдохновить на правильный вектор дальнейших действий). Простой способ – разбить игровое поле на части (кажется, в данной игре это особенно актуально) и сверять каждую часть на сходство с известными предметами. Если же предметы могут находиться в случайных местах, а не только на заданной сетке, то нужен более продвинутый анализ: можно сначала сверять по сетке, затем по той же сетке со смещение на половину (или даже треть и две трети). Ещё можно масштабировать, брать некий хэш от части изображения... Кстати, если не удастся добиться 99% точности (что в принципе возможно, ведь все предметы будут статично, а не поворачиваться под разным освещением, как в реальном мире), то можно компенсировать точность продуманностью кликов: сохранять позиции, куда уже безуспешно кликах, чтобы в следующие разы не тупить, не тратить время. Тогда главное, чтобы алгоритм поиска выдавал как можно меньше ложно отрицательных срабатываний, а ложно положительные будут быстро отсекаться на практике. Полезные ссылки: Закрытый вопрос на ruSO, но всё же с некоторыми интересными идеями. Статья о распознании образов (Хабр) Метод Виолы-Джонса для поиска и распознания лиц (Хабр)Ответ 4
Если нужно искать на изображении фрагмент этого изображения, а не что-то отдаленно напоминающее фрагмент, нет ничего лучше, чем корреляционный метод. Быстро и надежно. И не нужно никакого распознавания.
Комментариев нет:
Отправить комментарий