I am writing a autoclicker for one mobile game (screenshot below). The task is to find objects on the map. More precisely, the program makes a screenshot of the game running in the Bluestacks emulator, I get a bitmap , and I need to search for objects on the map by fragments in the screenshot. Tell me the algorithm for finding a fragment in the image?
4 answers
Using OpenCV should of course be more successful. I experimented with AForge
it turned out well too
Here is the class of work with the library, in fact the use itself takes 2 lines, I confess, I took this part from the Internet, the rest was written by myself
public class AforgeService { //Π½Π°ΠΉΠ΄Π΅Π½Π½ΡΠ΅ ΡΠΎΠ²ΠΏΠ°Π΄Π΅Π½ΠΈΡ private TemplateMatch[] _matchings; /// <summary> /// ΠΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ Π½Π°ΠΉΠ΄Π΅Π½Π½ΡΡ
ΡΠΎΠ²ΠΏΠ°Π΄Π΅Π½ΠΈΠΉ /// </summary> public int CountMatchings { get => _matchings != null ? _matchings.Length : 0; } //ctor public AforgeService() { } /// <summary> /// Π‘ΠΎΠ΄Π΅ΡΠΆΠΈΡ Π»ΠΈ ΠΈΡΡ
ΠΎΠ΄Π½ΠΎΠ΅ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½ΡΠΉ ΠΎΠ±ΡΠ°Π·Π΅Ρ /// </summary> /// <param name="pathOriginalImage">ΠΏΡΡΡ ΠΊ ΡΠ°ΠΉΠ»Ρ ΠΈΡΡ
ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ</param> /// <param name="pathSampleImage">ΠΏΡΡΡ ΠΊ ΡΠ°ΠΉΠ»Ρ ΠΎΠ±ΡΠ°Π·ΡΠ°</param> /// <returns>true Π΅ΡΠ»ΠΈ ΡΠΎΠ΄Π΅ΡΠΆΠΈΡ</returns> public async Task<bool> IsContains(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(); } /// <summary> /// ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΠΊΠΎΠ»Π»Π΅ΠΊΡΠΈΠΈ Π½Π°ΠΉΠ΄Π΅Π½Π½ΡΡ
ΠΌΠ΅ΡΡ Π³Π΄Π΅ Π½Π°Ρ
ΠΎΠ΄ΠΈΡΡΡ ΠΎΠ±ΡΠ°Π·Π΅Ρ /// </summary> /// <returns>ΠΊΠΎΠ»Π»Π΅ΠΊΡΠΈΡ Π½Π°ΠΉΠ΄Π΅Π½Π½ΡΡ
ΠΌΠ΅ΡΡ</returns> public List<FoundPlace> GetPlaces() { List<FoundPlace> result = new List<FoundPlace>(); 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; } } This class is for saving the found place.
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; } } This is a view model class.
public class MainViewModel : INotifyPropertyChanged, IDisposable { public event PropertyChangedEventHandler PropertyChanged; private readonly IMainWindow _mainWindow; private string _pathOriginalImage; //ctor public MainViewModel(IMainWindow mainWindow) { _mainWindow = mainWindow; } /// <summary> /// Π€Π»Π°Π³ Π·Π°ΠΏΡΡΠΊΠ° ΠΏΠΎΠΈΡΠΊΠ°, Π΄Π»Ρ Π²ΡΠΊΠ»./Π²ΠΊΠ». ΠΊΠ½ΠΎΠΏΠΎΠΊ /// </summary> private bool _IsSearching; public bool IsSearching { get => _IsSearching; set { _IsSearching = value; SelectSampleCommand.RaiseCanExecuteChanged(); SearchCommand.RaiseCanExecuteChanged(); } } /// <summary> /// ΠΡΡ
ΠΎΠ΄Π½ΠΎΠ΅ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ (ΠΏΠΎΠ»Π΅ ΠΈΠ³ΡΡ) /// </summary> public string OriginalImage { get => @"~\..\Assets\Original.jpg"; } /// <summary> /// ΠΠ±ΡΠ°Π·Π΅Ρ Π΄Π»Ρ ΠΏΠΎΠΈΡΠΊΠ° /// </summary> private string _SampleImage; public string SampleImage { get => _SampleImage; set { _SampleImage = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SampleImage))); SearchCommand.RaiseCanExecuteChanged(); } } /// <summary> /// Π’Π΅ΠΊΡΡΠΎΠ²ΠΎΠ΅ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ ΠΎ ΠΏΡΠΎΡΠ΅ΡΡΠ΅ /// </summary> private string _Message; public string Message { get => _Message; set { _Message = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Message))); } } /// <summary> /// Π‘ΠΏΠΈΡΠΎΠΊ Π½Π°ΠΉΠ΄Π΅Π½Π½ΡΡ
ΠΌΠ΅ΡΡ Π΄Π»Ρ ListBox /// </summary> private List<FoundPlace> _Places; public List<FoundPlace> 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); } } /// <summary> /// ΠΠ½ΠΎΠΏΠΊΠ° ΠΡΠ±ΡΠ°ΡΡ /// </summary> 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; } /// <summary> /// ΠΠ½ΠΎΠΏΠΊΠ° ΠΡΠΊΠ°ΡΡ /// </summary> 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<FoundPlace>(); } } } catch (Exception ex) { var message = $"ΠΠΎΠ·Π½ΠΈΠΊΠ»Π° ΠΎΡΠΈΠ±ΠΊΠ°: {ex.Message}"; _mainWindow.ShowMessage(message, "ΠΡΠΈΠ±ΠΊΠ°"); } finally { Message = $"ΠΠ°ΠΉΠ΄Π΅Π½ΠΎ ΠΌΠ΅ΡΡ: {service.CountMatchings}"; IsSearching = false; } } /// <summary> /// ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΠΏΡΡΠΈ ΠΊ ΠΎΡΠΈΠ³ΠΈΠ½Π°Π»ΡΠ½ΠΎΠΌΡ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ (ΠΏΠΎΠ»Ρ ΠΈΠ³ΡΡ) /// </summary> /// <returns></returns> 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; } /// <summary> /// IDisposable /// </summary> public void Dispose() { if (!String.IsNullOrEmpty(_pathOriginalImage) && File.Exists(_pathOriginalImage)) { File.Delete(_pathOriginalImage); } } } The whole example can be downloaded here.
- OpenCV is learning as far as I know? Is it possible to train him so that he determines whether the buildings are in the inside of the fence or outside? - VikiMayson pm
- @VikiMayson I have not worked with
OpenCV, because, excuse me, I can not say anything intelligible. I just quickly sketched an example, because I was interested in your question. - Bulson - aforge see, too, has machine learning and neurons. On it there is nothing in my task do not tell? There are just moments when some objects overlap with others, and I definitely want to realize that it determines where objects are located, inside or outside the fence. Therefore, I want to teach. - VikiMayson
- 2@VikiMayson, again I'm sorry, but Iβm saying that in a hurry ... I myself hope to find the time and read the docks, experiment :) - Bulson
- oneAt the end of my answer there is a link to an example that can be downloaded. In the project folder you will find the
binfolder in it is theDebugfolder. Copy it with all the files and run theexefile, it should work (it is important that all files are in the same folder). Only this is an example, not a complete program. It is not possible to replace the image by the cat. You need to search. - Bulson pm
I think you have enough functionality implemented in OpenCV
OpenCV (English Open Source Computer Vision Library, open source computer vision library) is a library of computer vision, image processing and general-purpose numerical algorithms with open source.
(c) Wikipedia.
Here are some good lessons about it, but what you can come in handy in theory.
There is a large, complex and popular area of ββtechnology called image recognition. For the most part deep neural networks are used there, but not only. And it is unlikely that such complexity is necessary in your case (although it can inspire the correct vector of further actions).
A simple way is to break the playing field into parts (it seems that this game is especially important in this game) and check each part for similarities with known objects. If items can be in random places, and not just on a given grid, then a more advanced analysis is needed: you can first check on the grid, then on the same grid with a half shift (or even a third and two thirds). You can also scale it, take a certain hash from a part of the image ...
By the way, if you canβt achieve 99% accuracy (which is possible in principle, because all objects will be static and not rotated under different lighting, as in the real world), then you can compensate for the accuracy by the reasonableness of clicks: to save positions that have been unsuccessfully clicked in the next times do not blunt, do not waste time Then the main thing is that the search algorithm should produce as few false positives as possible, and false positives will be quickly cut off in practice.
Useful links:
Closed question on ruSO , but still with some interesting ideas.
Viola-Jones method for searching and recognizing faces (Habr)
If you need to search for an image of a fragment of this image, and not something remotely resembling a fragment, there is nothing better than the correlation method. Fast and reliable. And no recognition is needed.



Mod CoC:) there is on Google Play, there are third-party development of course. On the emulator it is possible, but these are terrible brakes, better device is usb + adb. - NewView pm