/// <summary> /// Places one image on top of the other. The top image should fit within the bottom /// image. The top image completely cover the bottom image. /// </summary> /// <param name="bmpTop"></param> /// <param name="xstart">The x value of the point in the bottom image at which the left /// hand top corner of the top image is placed</param> /// <param name="ystart">The y value of the point in the bottom image at which the left /// hand top corner of the top image is placed</param> /// <returns>Returns an interface to the image</returns> IImageAdapterUnmanaged IImageAdapterUnmanaged.Overlay( IImageAdapterUnmanaged bmpTop, int xstart, int ystart) { System.Diagnostics.Debug.WriteLine("Overlay() started"); Rectangle rBg = new Rectangle(0, 0, this.Width, this.Height); Rectangle rTop = new Rectangle(xstart, ystart, bmpTop.Width, bmpTop.Height); System.Diagnostics.Debug.WriteLine("Overlay() \n\nrBg " + rBg.ToString() + "\nrTop " + rTop.ToString()); if (Rectangle.Intersect(rBg, rTop) != rTop) { throw new ArgumentOutOfRangeException("Top BMP must be inside Bg one"); } // Copy Color from background image into this (same as cloning bg image into 'this') ImageAdapter temp = new ImageAdapter((IImageAdapter)this); System.Diagnostics.Debug.WriteLine("Overlay() : Before the loop"); for (int x = rBg.Left; x < rBg.Right; x++) { for (int y = rBg.Top; y < rBg.Bottom; y++) { if (rTop.Contains(x, y)) { IColor colorTop = bmpTop[x - rTop.Left, y - rTop.Top]; temp[x, y] = colorTop; } } } return((IImageAdapterUnmanaged)temp); }
/// <summary> /// Merges two images taking into consideration their alpha values. /// </summary> /// <param name="bmpBg">The background Image</param> /// <param name="bmpTop">The image that goes on top of the background</param> /// <param name="xBg">Left corner position of background</param> /// <param name="yBg">Top corner position of the background</param> /// <param name="xTop">Left corner position of the top image</param> /// <param name="yTop">Top corner position of the top image</param> /// <returns>Returns an interface to the image</returns> IImageAdapterUnmanaged IImageAdapterUnmanaged.Merge(IImageAdapterUnmanaged bmpBg, IImageAdapterUnmanaged bmpTop, int xBg, int yBg, int xTop, int yTop) { System.Diagnostics.Debug.WriteLine("Merge() started"); #if DEBUG ImageUtility.ToImageFile(bmpBg, System.IO.Path.Combine(System.IO.Path.GetTempPath(), "Before_Merge_Bg.png")); ImageUtility.ToImageFile(bmpTop, System.IO.Path.Combine(System.IO.Path.GetTempPath(), "Before_Merge_Top.png")); #endif // debug #if DEBUG ImageUtility.ToImageFile((IImageAdapter)bmpBg, System.IO.Path.Combine(System.IO.Path.GetTempPath(), "Merge_BgImage.bmp")); ImageUtility.ToImageFile((IImageAdapter)bmpTop, System.IO.Path.Combine(System.IO.Path.GetTempPath(), "Merge_TopImage.bmp")); #endif // DEBUG Rectangle rBg = new Rectangle(xBg, yBg, bmpBg.Width, bmpBg.Height); Rectangle rTop = new Rectangle(xTop, yTop, bmpTop.Width, bmpTop.Height); System.Diagnostics.Debug.WriteLine("Merge() \n\nrBg " + rBg.ToString() + "\nrTop " + rTop.ToString()); if (Rectangle.Intersect(rBg, rTop) != rTop) { throw new ArgumentOutOfRangeException("Top BMP must be inside Bg one"); } int maxAlpha = 0; float bgAlpha = 0; float topAlpha = 0; // Copy Color from background image into this (same as cloning bg image into 'this') ImageAdapter temp = new ImageAdapter((IImageAdapter)bmpBg); System.Diagnostics.Debug.WriteLine("Merge1() : Before the loop"); for (int x = rBg.Left; x < rBg.Right; x++) { for (int y = rBg.Top; y < rBg.Bottom; y++) { if (rTop.Contains(x, y)) { IColor colorBg = bmpBg[x - rBg.Left, y - rBg.Top]; IColor colorTop = bmpTop[x - rTop.Left, y - rTop.Top]; maxAlpha = Math.Max(colorBg.A, colorTop.A); topAlpha = (float)colorTop.Alpha; bgAlpha = (float)((1f - colorTop.Alpha) * colorBg.Alpha); IColor color = new ColorByte((byte)maxAlpha, (byte)(bgAlpha * colorBg.R + topAlpha * colorTop.R), (byte)(bgAlpha * colorBg.G + topAlpha * colorTop.G), (byte)(bgAlpha * colorBg.B + topAlpha * colorTop.B)); temp[x - rBg.Left, y - rBg.Top] = color; } } } #if DEBUG ImageUtility.ToImageFile(temp, System.IO.Path.Combine(System.IO.Path.GetTempPath(), "After_Merge_This.png")); #endif return((IImageAdapterUnmanaged)temp); }
/// <summary> /// Get the machineInfo associated with this imageAdapter /// </summary> /// <param name="imageAdapter"></param> /// <returns></returns> static public string GetMachineInfo(ImageAdapter imageAdapter) { if (imageAdapter == null) { throw new ArgumentNullException("imageAdapter", "Cannot pass a null argument"); } if (imageAdapter.Metadata == null) { return(string.Empty); } return(GetMachineInfo(imageAdapter.Metadata)); }
/// <summary> /// Clips the image at the RECT specified /// </summary> /// <param name="left">The left side of the RECT</param> /// <param name="top">The top side of the RECT</param> /// <param name="right">The right side of the RECT</param> /// <param name="bottom">The bottom side of the RECT</param> /// <returns>Returns an interface to the image</returns> IImageAdapterUnmanaged IImageAdapterUnmanaged.Clip( int left, int top, int right, int bottom) { System.Diagnostics.Debug.WriteLine("Clip() started"); Rectangle rBg = new Rectangle(0, 0, this.Width, this.Height); Rectangle rTop = new Rectangle(left, top, right - left, bottom - top); System.Diagnostics.Debug.WriteLine("Clip() rBg " + rBg.ToString() + "\nrTop " + rTop.ToString()); Rectangle intersection = Rectangle.Intersect(rBg, rTop); System.Diagnostics.Debug.WriteLine("\n\rClip() Intersection " + intersection.ToString()); if (intersection != rTop) { System.Diagnostics.Debug.WriteLine("Clip area is not perfectly within the image\n"); } if (intersection.Width > 0 && intersection.Height > 0) { ImageAdapter temp = new ImageAdapter(intersection.Width, intersection.Height, (ColorByte)Color.Black); System.Diagnostics.Debug.WriteLine("Clip() : Before the loop"); for (int x = intersection.Left; x < intersection.Right; x++) { for (int y = intersection.Top; y < intersection.Bottom; y++) { IColor colorTop = this[x, y]; temp[x - rTop.Left, y - rTop.Top] = colorTop; } } return((IImageAdapterUnmanaged)temp); } else { throw new ArgumentOutOfRangeException("Width or Height of the Image are negetive\n\r"); } }
/// <summary> /// Equalize the image on all channels /// </summary> /// <param name="imageAdapter">The image to equalize</param> /// <returns>The Equalized image</returns> internal static IImageAdapter EqualizeImage(IImageAdapter imageAdapter) { // Check params if (imageAdapter == null) { throw new ArgumentNullException("imageAdapter", "Argument cannot be null"); } IImageAdapter retVal = new ImageAdapter(imageAdapter.Width, imageAdapter.Height); int[] sum = new int[] { 0, 0, 0, 0 }; Hashtable dico = BuildHistogram(imageAdapter); Hashtable equalized = new Hashtable(); for (int channel = 1; channel < intColor.Length; channel++) // Ignore Alpha Channel { ArrayList[] list = new ArrayList[256]; for (int index = 0; index < 256; index++) { list[index] = new ArrayList(); } equalized.Add(intColor[channel], list); } equalized[ALPHA] = ((ArrayList[])dico[ALPHA]).Clone(); // Clone Alpha channel from original Histogram // compute the sum per channel for (int channel = 0; channel < intColor.Length; channel++) { ArrayList[] array = (ArrayList[])dico[intColor[channel]]; for (int index = 0; index < 256; index++) { sum[channel] += array[index].Count; } } // Stretch and Normalize the histogram // Transformation used : // (min <= i <= max) // Normalize : 0.5 + ( Sum(0,i-1) + ( Sum(i-1,i) / 2 ) ) * 255 / Sum(0,255) for (int channel = 1; channel < intColor.Length; channel++) // Ignore Alpha channel { ArrayList[] channelOriginal = (ArrayList[])dico[intColor[channel]]; ArrayList[] channelEqualized = (ArrayList[])equalized[intColor[channel]]; float equalizeConstant = 255.0f / ((sum[channel] != 0) ? sum[channel] : 1); int currentSum = 0; float equalize = 0f; for (int index = 0; index < 256; index++) { equalize = 0.5f + (currentSum + channelOriginal[index].Count / 2) * equalizeConstant; currentSum += channelOriginal[index].Count; channelEqualized[(int)equalize].AddRange(channelOriginal[index]); } } retVal = HistogramToIImageAdapter(equalized, imageAdapter); equalized.Clear(); dico.Clear(); return(retVal); }
/// <summary> /// Contrast an image by Streching the Histogram associated with this image /// </summary> /// <param name="imageAdapter">the image to contrast</param> /// <returns>The contrasted image</returns> internal static IImageAdapter ContrastStretch(IImageAdapter imageAdapter) { // Check Params if (imageAdapter == null) { throw new ArgumentNullException("imageAdapter", "Argument cannot be null"); } IImageAdapter retVal = new ImageAdapter(imageAdapter.Width, imageAdapter.Height); IColor color = imageAdapter[0, 0]; int[] min = new int[] { (int)color.MinChannelValue, (int)color.MinChannelValue, (int)color.MinChannelValue, (int)color.MinChannelValue }; int[] max = new int[] { (int)color.MaxChannelValue, (int)color.MaxChannelValue, (int)color.MaxChannelValue, (int)color.MaxChannelValue }; Hashtable dico = BuildHistogram(imageAdapter); Hashtable stretched = new Hashtable(); for (int channel = 1; channel < intColor.Length; channel++) // channel = 1 to Ignore Alpha Channel { ArrayList[] list = new ArrayList[256]; for (int index = 0; index < 256; index++) { list[index] = new ArrayList(); } stretched.Add(intColor[channel], list); } stretched[ALPHA] = ((ArrayList[])dico[ALPHA]).Clone(); // Clone Alpha channel from original Histogram // Find the min and max value of the histogram for (int channel = 1; channel < intColor.Length; channel++) // ignore the alpha channel { bool minFound = false; ArrayList[] array = (ArrayList[])dico[intColor[channel]]; for (int index = 0; index < 256; index++) { if (array[index].Count != 0) { // sum[channel] += array[index].Count; max[channel] = index; minFound = true; } else { if (!minFound) { min[channel] = index; } } } } // Stretch the histogram // Transformation used : // (min <= i <= max) // Stretch : (i - min) * 255 / ( max - min) + 0.5 for (int channel = 1; channel < intColor.Length; channel++) // Ignore the Alpha Channel { ArrayList[] channelOriginal = (ArrayList[])dico[intColor[channel]]; ArrayList[] channelStretched = (ArrayList[])stretched[intColor[channel]]; int minChannel = min[channel]; int maxChannel = max[channel]; float stretchConstant = 255.0f / (float)(maxChannel - minChannel); float stretch = 0f; for (int index = minChannel; index <= maxChannel; index++) { stretch = (index - minChannel) * stretchConstant + 0.5f; channelStretched[(int)stretch].AddRange(channelOriginal[index]); } } retVal = HistogramToIImageAdapter(stretched, imageAdapter); dico.Clear(); stretched.Clear(); return(retVal); }
private void DoVscanCompare(object asyncData) { AsyncData data = asyncData as AsyncData; if (data == null) { throw new ArgumentException("Parameter passed in to the Method not of type AsyncData (or null)", "asyncData"); } ImageComparator ic = new ImageComparator(); ic.Curve.CurveTolerance.LoadTolerance(data.ToleranceSettings.XmlNodeTolerance); IImageAdapter masterAdapter = new ImageAdapter(data.MasterImage); IImageAdapter capturedAdapter = new ImageAdapter(data.CapturedImage); // compare Master to the Capture image using the Compare overload that will scale the images size accounting for the DPI data.Result.Succeeded = ic.Compare(masterAdapter, MetadataInfoHelper.GetDpi(masterAdapter), capturedAdapter, MetadataInfoHelper.GetDpi(capturedAdapter), false); if (data.Result.Succeeded == false) { Microsoft.Test.Logging.GlobalLog.LogStatus("Regular comparison failed"); } // On filaure, check if user whats to filter the image ( IgnoreAntiAliasing will do ) IImageAdapter masterFiltered = null; IImageAdapter captureFiltered = null; if (data.Result.Succeeded == false && data.ToleranceSettings.Filter != null) { // first save error diff image string errorDiffName = ".\\ErrorDiff_" + data.Index + IMAGE_EXTENSION; ImageUtility.ToImageFile(ic.GetErrorDifference(ErrorDifferenceType.IgnoreAlpha), errorDiffName); Microsoft.Test.Logging.GlobalLog.LogFile(errorDiffName); // Compare failed, filter the images and retry Microsoft.Test.Logging.GlobalLog.LogStatus("Filtering and recompare"); masterFiltered = data.ToleranceSettings.Filter.Process(masterAdapter); captureFiltered = data.ToleranceSettings.Filter.Process(capturedAdapter); data.Result.Succeeded = ic.Compare(masterFiltered, captureFiltered, false); if (data.Result.Succeeded == false) { Microsoft.Test.Logging.GlobalLog.LogStatus("==> Filtered comparison failed as well"); } } if (data.Result.Succeeded) { Microsoft.Test.Logging.GlobalLog.LogStatus("Comparison SUCCEEDED."); } else { // Save Masters * filtered master for easy analysis string masterName = ".\\Master_" + data.Index + IMAGE_EXTENSION; data.MasterImage.Save(masterName, System.Drawing.Imaging.ImageFormat.Tiff); Microsoft.Test.Logging.GlobalLog.LogFile(masterName); if (masterFiltered != null) { string filteredMasterName = ".\\MasterFiltered_" + data.Index + IMAGE_EXTENSION; using (Bitmap filteredMaster = ImageUtility.ToBitmap(masterFiltered)) { SetMetadataToImage(filteredMaster); filteredMaster.Save(filteredMasterName, System.Drawing.Imaging.ImageFormat.Tiff); } Microsoft.Test.Logging.GlobalLog.LogFile(filteredMasterName); } // Save rendered image (as "Actual_n") for easy analysis string capturedName = ".\\Actual_" + data.Index + IMAGE_EXTENSION; data.CapturedImage.Save(capturedName, System.Drawing.Imaging.ImageFormat.Tiff); Microsoft.Test.Logging.GlobalLog.LogFile(capturedName); // Save actual filtered for easy analysis if (captureFiltered != null) { string filteredRenderedName = ".\\ActualFiltered_" + data.Index + IMAGE_EXTENSION; using (Bitmap filteredRendered = ImageUtility.ToBitmap(captureFiltered)) { SetMetadataToImage(filteredRendered); filteredRendered.Save(filteredRenderedName, System.Drawing.Imaging.ImageFormat.Tiff); } Microsoft.Test.Logging.GlobalLog.LogFile(filteredRenderedName); } // Master might need to be updated, save with correct name and metadata // // In this image, encode full criteria string name = System.IO.Path.GetFileName(data.MasterName); string originalName = name.Replace(IMAGE_EXTENSION, "_FullCtriteria" + IMAGE_EXTENSION); Microsoft.Test.Logging.GlobalLog.LogStatus("Saving master with all criteria (new master) as '" + originalName + "'"); SetMetadataToImage(data.CapturedImage); data.CapturedImage.Save(originalName, System.Drawing.Imaging.ImageFormat.Tiff); Microsoft.Test.Logging.GlobalLog.LogFile(originalName); // // In this image, encode only criteria that match the master string originalNameFull = name.Replace(IMAGE_EXTENSION, "_MatchingCriteria" + IMAGE_EXTENSION); Microsoft.Test.Logging.GlobalLog.LogStatus("Saving master with matching criteria encoded (to replace previous master) as '" + originalNameFull + "'"); MasterMetadata metadata = ImageMetadata.MetadataFromImage(data.MasterImage); // Keep master Criteria but update its Description. IMasterDimension[] keys = new IMasterDimension[metadata.Description.Count]; metadata.Description.Keys.CopyTo(keys, 0); for (int t = 0; t < keys.Length; t++) { metadata.Description[keys[t]] = keys[t].GetCurrentValue(); } ImageMetadata.SetMetadataToImage(metadata, data.CapturedImage); data.CapturedImage.Save(originalNameFull, System.Drawing.Imaging.ImageFormat.Tiff); Microsoft.Test.Logging.GlobalLog.LogFile(originalNameFull); // first save error diff image string errorDiffFilterName = ".\\ErrorDiffFiltered_" + data.Index + IMAGE_EXTENSION; if (data.ToleranceSettings.Filter == null) { // Not filter were applied, change name (so it's not confusing) errorDiffFilterName = ".\\ErrorDiff_" + data.Index + IMAGE_EXTENSION; } ImageUtility.ToImageFile(ic.GetErrorDifference(ErrorDifferenceType.IgnoreAlpha), errorDiffFilterName); Microsoft.Test.Logging.GlobalLog.LogFile(errorDiffFilterName); } data.Result.IsCompleted = true; if (data.SynchronizationObject != null) { data.SynchronizationObject.Set(); } }