/// <summary> /// Use Vscan to verify the current visuals (e.g. screenshot) against the given model. /// </summary> /// <param name="elementToCapture">UIElement to take a screen shot of.</param> /// <param name="modelFilename">Full path to model file to compare current visuals against.</param> /// <exception cref="ArgumentException">If modelFilename is not a valid path.</exception> public void CompareVisualsToModel(UIElement elementToCapture, object modelFilename) { string modelName = modelFilename as string; Console.WriteLine("[VisualScan]: Master is '" + modelFilename + "'."); EnsureRenderComplete(); // Create vscan object. VScan scan = new VScan(CreateBitmap(elementToCapture)); // Analyze Data. Console.WriteLine("[VisualScan]: analyzing screen capture."); scan.OriginalData.Analyze(); // Compare model with image. Console.WriteLine("[VisualScan]: comparing against master."); bool result = scan.OriginalData.CompareModels(modelName); Console.WriteLine("[VisualScan]: " + ((result) ? "SUCCEEDED" : "FAILED")); if (!result) { // Dump a report of the differences. // XmlNode xmlDiff = scan.OriginalData.ModelDifferences; XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(xmlDiff.OuterXml); string errorFilename = "vscan_error_" + DateTime.Now.Ticks + ".xml"; xmlDoc.Save(errorFilename); throw new Exception("Visuals differ from expected, error log generated '" + errorFilename + "'."); } }
/// <summary> /// The scanner attemps to locate content assuming that: /// contentOff is the imagaeadapter of the scene before content is shown /// contentOn is the imagaeadapter of the scene with the content shown /// Note: content may have a variable alpha map /// </summary> public Result Process(IImageAdapter content, IImageAdapter contentOff, IImageAdapter contentOn, double[] transformMatrix) { string paramEx = string.Empty; if (content == null) { paramEx = "<IImageAdapter content> "; } if (contentOff == null) { paramEx += "<IImageAdapter contentOff>"; } if (contentOn == null) { paramEx += "<IImageAdapter contentOn>"; } if (content == null) { paramEx += "<IImageAdapter content>"; } if (transformMatrix == null) { paramEx += "<double []transformMatrix>"; } /* * if (transformMatrix.Length != Interpolator.TransformLength) * { * throw new Exception("Transform array length != " + Interpolator.TransformLength); * } */ //sentinel bool validResult = true; string log = string.Empty; ImageAdapter res = GetTransformedContent(content, transformMatrix, contentOff.Width, contentOff.Height); // locate rec // 1) compute the diff image // 2) synthetise the transformed content // 3) compare with the sourceOn ImageComparator imageComparator = new ImageComparator(); imageComparator.Compare(contentOn, contentOff); IImageAdapter imdiff = imageComparator.GetErrorDifference(ErrorDifferenceType.FilterEdge); // make sure there is only one descriptor and the background. //convert the diff image to a binary image (black-bg and white-fg) ImageAdapter imbinmap = new ImageAdapter(imdiff.Width, imdiff.Height); IColor black = new ColorByte(Color.Black); IColor white = new ColorByte(Color.White); for (int j = 0; j < imbinmap.Width; j++) { for (int i = 0; i < imbinmap.Height; i++) { if (imdiff[j, i].Red + imdiff[j, i].Green + imdiff[j, i].Blue > 1e-6) { imdiff[j, i] = white; } else { imdiff[j, i] = black; } } } #if DEBUG ImageUtility.ToImageFile(imdiff, "bwmap.png", ImageFormat.Png); #endif //Analyze the bin-diff-image VScan lvsc = new VScan(imdiff); lvsc.OriginalData.Analyze(); //topological check //root nodes: either a pair of white and black descriptor //or a tree. // all further descendant must be children of the white cell /* * int[] descriptorCounter = new int[2]; * * //loop on the descriptors * foreach (IDescriptor desc in lvsc.OriginalData.Descriptors.ActiveDescriptors) * { * if (desc.Depth <= 1) * { * descriptorCounter[desc.Depth]++; * } * * Console.WriteLine("Descr " + desc.BoundingBox + " " + desc.Depth); * } * * //check * int summ = descriptorCounter[0] + descriptorCounter[1]; * if (summ != 2) * { * validResult = false; * if (summ == 0) * { * log = "<Fail> No top level descriptors found"; * } * else * { * log = "<Fail> Too many top level descriptors found (should be two) :" + summ; * } * } */ // topology is good to go, time to find the bounding box of the dynamic content int minx = int.MaxValue; int miny = int.MaxValue; int maxx = int.MinValue; int maxy = int.MinValue; if (validResult == true) { for (int j = 0; j < imdiff.Width; j++) { for (int i = 0; i < imdiff.Height; i++) { double sum = imdiff[j, i].Blue + imdiff[j, i].Red + imdiff[j, i].Green; if (sum > 1e-6) { if (j < minx) { minx = j; } if (i < miny) { miny = i; } if (j > maxx) { maxx = j; } if (i > maxy) { maxy = i; } } } } // bounding box maxx -= minx; maxy -= miny; Console.WriteLine("<Target> found at " + minx + " " + miny + " " + maxx + " " + maxy); ImageUtility.ToImageFile(imdiff, "Recpos.png"); // synthetize content into contentOff IImageAdapter iafcomp = new ImageAdapter(contentOff.Width, contentOff.Height); double dx = minx - _imageAdapterXmin; double dy = miny - _imageAdapterYmin; // translate results for (int j = 0; j < 4; j++) { _contentLocation[j, 0] += dx; _contentLocation[j, 1] += dy; } // copy the background iafcomp = (IImageAdapter)contentOff.Clone(); // add the transformed content for (int j = 0; j < res.Width; j++) { for (int i = 0; i < res.Height; i++) { if (j + minx < iafcomp.Width && i + miny < iafcomp.Height) { if (res[j, i].Alpha > 0) { double lalp = res[j, i].Alpha; int jid = j + minx; int iid = i + miny; IColor lvc = iafcomp[jid, iid]; lvc.Red = lalp * res[j, i].Red + (1 - lalp) * iafcomp[jid, iid].Red; lvc.Green = lalp * res[j, i].Green + (1 - lalp) * iafcomp[jid, iid].Green; lvc.Blue = lalp * res[j, i].Blue + (1 - lalp) * iafcomp[jid, iid].Blue; iafcomp[jid, iid] = lvc; } } } } #if DEBUG ImageUtility.ToImageFile(iafcomp, "SynthGlyph.png", ImageFormat.Png); #endif /* * if (Tolerance != null) * { * imageComparator.Tolerance.Clear(); * double x = double.NaN; * double y = double.NaN; * for(int t=0;t<Tolerance.Count;t++) * { * x = (double)Tolerance.GetKey(t); * y = (double)Tolerance[x]; * imageComparator.Tolerance.Add(x,y); * } * } */ validResult = imageComparator.Compare(iafcomp, contentOn); string toluse = "No Tolerance used - strict comparison"; if (_tolerance != null) { toluse = "with the given Tolerance"; } if (validResult == false) { log = "<Fail> Computed Content does not match actual content -- " + toluse; } else { log = "<Pass>"; } Console.WriteLine("Final comp pass " + validResult); ImageUtility.ToImageFile(imageComparator.GetErrorDifference(ErrorDifferenceType.FilterEdge), "SynthError.png"); #if DEBUG using (Bitmap fbmp = ImageUtility.ToBitmap(contentOn)) { using (Graphics graphics = Graphics.FromImage(fbmp)) { using (Brush brush = new SolidBrush(Color.FromArgb(40, 255, 0, 0))) { graphics.FillRectangle(brush, minx, miny, maxx, maxy); } using (Pen pen = new Pen(Color.Red, 2)) { graphics.DrawRectangle(pen, minx, miny, maxx, maxy); } using (Font fnt = new Font("Arial", 10)) { SizeF sz = graphics.MeasureString("TL", fnt); graphics.FillRectangle(Brushes.Yellow, (float)_contentLocation[0, 0], (float)_contentLocation[0, 1], sz.Width, sz.Height); graphics.FillRectangle(Brushes.Yellow, (float)_contentLocation[1, 0], (float)_contentLocation[1, 1], sz.Width, sz.Height); graphics.FillRectangle(Brushes.Yellow, (float)_contentLocation[2, 0], (float)_contentLocation[2, 1], sz.Width, sz.Height); graphics.FillRectangle(Brushes.Yellow, (float)_contentLocation[3, 0], (float)_contentLocation[3, 1], sz.Width, sz.Height); graphics.DrawString("TL", fnt, Brushes.Red, (float)_contentLocation[0, 0], (float)_contentLocation[0, 1]); graphics.DrawString("TR", fnt, Brushes.Red, (float)_contentLocation[1, 0], (float)_contentLocation[1, 1]); graphics.DrawString("BL", fnt, Brushes.Red, (float)_contentLocation[2, 0], (float)_contentLocation[2, 1]); graphics.DrawString("BR", fnt, Brushes.Red, (float)_contentLocation[3, 0], (float)_contentLocation[3, 1]); } fbmp.Save("TrackMatch.png"); } } #endif } Result dcres = new Result(validResult, log, _contentLocation); return(dcres); }