/// <summary> /// Find all instances of a subimage in this image. /// </summary> /// <param name="subimage">The subimage to find in the larger image. Must be smaller than the image in both dimensions.</param> /// <param name="verifier">Custom SnapshotVerifier, used to compare the subimages.</param> /// <returns>A Collection of rectangles indicating the matching locations. /// If there is no match, the Collection returned will be empty.</returns> public Collection <Rectangle> Find(Snapshot subimage, SnapshotVerifier verifier) { if (Width < subimage.Width || Height < subimage.Height) { throw new InvalidOperationException("Subimage snapshot must fit into larger snapshot to be found."); } Collection <Rectangle> resultList = new Collection <Rectangle>(); // This is a binary 2D convolve with early exit on any imperfect match. // Future optimisations will test the middle pixel first, then a diagonal through the subarea, then the full image. // This will make pathological cases much faster. for (int row = 0; row < Height - subimage.Height; row++) { for (int column = 0; column < Width - subimage.Width; column++) { // This is our success condition, add this location to the Collection. if (CompareSubImage(subimage, row, column, verifier)) { resultList.Add((new Rectangle(column, row, subimage.Width, subimage.Height))); } } } return(resultList); }
/// <summary> /// Private subimage comparer to help FindSubImage. This performs the actual work of comparing pixels. /// </summary> /// <param name="subimage">The subimage to find.</param> /// <param name="startingRow">The row offset into this snapshot to look for the subimage.</param> /// <param name="startingColumn">The column offset into this snapshot to look for the subimage.</param> /// <param name="verifier">Custom subimage comparison verifier, if specified.</param> /// <returns></returns> private bool CompareSubImage( Snapshot subimage, int startingRow, int startingColumn, SnapshotVerifier verifier) { //compare pixel-by-pixel for (int row = 0; row < subimage.Height; row++) { for (int column = 0; column < subimage.Width; column++) { // Use the custom verifier if the user has specified one if (verifier != null) { //verify this pixel Snapshot thisPixelImage = this.Crop(new Rectangle(startingColumn + column, startingRow + row, 1, 1)); Snapshot thatPixelImage = subimage.Crop(new Rectangle(column, row, 1, 1)); VerificationResult subCompareResult = verifier.Verify(thisPixelImage.CompareTo(thatPixelImage, false)); if (subCompareResult == VerificationResult.Fail) { //mismatch return(false); } } else { Color thisPixel = this[startingRow + row, startingColumn + column]; Color thatPixel = subimage[row, column]; int R = thisPixel.R - thatPixel.R; int G = thisPixel.G - thatPixel.G; int B = thisPixel.B - thatPixel.B; int A = thisPixel.A - thatPixel.A; if (R != 0x00 || G != 0x00 || B != 0x00) { //mismatch. return(false); } } } } return(true); }