/// <summary> /// Superimpose a monochrome image, and return the resulting image. /// </summary> /// <param name="img">A monochrome image.</param> /// <param name="reference">A reference to img1 for downsampling and rotation.</param> /// <returns></returns> public static Image SaveSuperimposedImage(Image img, Image reference) { //If image isn't monochrome format, throw exception if (img.PixelFormat != PixelFormat.Format1bppIndexed) { throw new Exception(imageFormatNotMonochrome); } //Construct two ImageAsPointCollection instances. ImageAsPointCollection pointCollection1 = new ImageAsPointCollection(img); ImageAsPointCollection pointCollection2 = new ImageAsPointCollection(reference); //Downsample pointCollection1 = DownSampling(pointCollection1, Math.Min(pointCollection1.listOfPoints.Count, pointCollection2.listOfPoints.Count)); pointCollection2 = DownSampling(pointCollection2, Math.Min(pointCollection1.listOfPoints.Count, pointCollection2.listOfPoints.Count)); //Translate pointCollection1 = Translate(pointCollection1); pointCollection2 = Translate(pointCollection2); //Uniform scale pointCollection1 = UniformScale(pointCollection1); pointCollection2 = UniformScale(pointCollection2); //Rotate pointCollection1 = RotateToReference(pointCollection2, pointCollection1); Image superimposedImage1 = pointCollection1.ToMonoImage(); return superimposedImage1; }
private static string imageNotSameDimension = "Images have different sizes; computation aborted."; //Exception message. #endregion Fields #region Methods /// <summary> /// Given two images, compute their square differences. /// No superimposition function is applied in this step. /// To compare superimposed images, they must be superimposed before input into this function. /// </summary> /// <param name="img1">A monochrome image. Must have same dimensions with img2.</param> /// <param name="img2">A monochrome image. Must have same dimensions with img1.</param> /// <returns></returns> public static double ComputeHausdroffDistance(Image img1, Image img2) { //If any image isn't monochrome format, throw exception if (img1.PixelFormat != PixelFormat.Format1bppIndexed || img2.PixelFormat != PixelFormat.Format1bppIndexed) { throw new Exception(imageFormatNotMonochrome); } //If images aren't of same dimension, throw exception if (img1.Width != img2.Width || img1.Height != img2.Height) { throw new Exception(imageNotSameDimension); } //Construct two ImageAsPointCollection instances. ImageAsPointCollection pointCollection1 = new ImageAsPointCollection(img1); ImageAsPointCollection pointCollection2 = new ImageAsPointCollection(img2); //Calculate difference of two superimposed images. double difference = 0; for(int i = 0; i < pointCollection1.listOfPoints.Count; i++) { double min_difference = double.MaxValue; for(int j = 0; j < pointCollection2.listOfPoints.Count; j++) { double temp_difference = Math.Pow((pointCollection1.listOfPoints[i].x - pointCollection2.listOfPoints[j].x), 2) + Math.Pow((pointCollection1.listOfPoints[i].y - pointCollection2.listOfPoints[j].y), 2); if (temp_difference < min_difference) min_difference = temp_difference; } difference += min_difference; } difference = Math.Sqrt(difference/(pointCollection1.listOfPoints.Count * pointCollection2.listOfPoints.Count)); return difference; }
/// <summary> /// Calculates the root mean square distance (RMSD) of a set of points, /// then scale them. /// </summary> /// <param name="original"></param> /// <returns></returns> private static ImageAsPointCollection UniformScale(ImageAsPointCollection original) { ImageAsPointCollection result = original; //Get list's length int k = original.listOfPoints.Count; //Calculate sum of x's and y's double x_sum = 0; double y_sum = 0; for (int i = 0; i < k; i++) { x_sum += original.listOfPoints[i].x; y_sum += original.listOfPoints[i].y; } //Calculate averages of x's and y's double x_bar = x_sum / k; double y_bar = y_sum / k; //Calculate RMSD double rmsd = 0; for (int i = 0; i < k; i++) { rmsd += (original.listOfPoints[i].x - x_bar) * (original.listOfPoints[i].x - x_bar); rmsd += (original.listOfPoints[i].y - y_bar) * (original.listOfPoints[i].y - y_bar); } rmsd = Math.Sqrt(rmsd / k); //Apply scaling for (int i = 0; i < k; i++) { result.listOfPoints[i].x = ((original.listOfPoints[i].x - x_bar)/rmsd); result.listOfPoints[i].y = ((original.listOfPoints[i].y - y_bar)/rmsd); } return result; }
/// <summary> /// Takes an ImageAsPointCollection instance, return its translated version. /// </summary> /// <param name="original"></param> /// <returns></returns> private static ImageAsPointCollection Translate(ImageAsPointCollection original) { ImageAsPointCollection result = original; //Get list's length int k = original.listOfPoints.Count; double x_sum = 0; double y_sum = 0; for (int i = 0; i < k; i++) { x_sum += original.listOfPoints[i].x; y_sum += original.listOfPoints[i].y; } double x_bar = x_sum / k; double y_bar = y_sum / k; for(int i = 0; i < k; i++) { result.listOfPoints[i].x = (original.listOfPoints[i].x - x_bar); result.listOfPoints[i].y = (original.listOfPoints[i].y - y_bar); } return result; }
/// <summary> /// Rotates original image relative to reference image. /// </summary> /// <param name="original"></param> /// <param name="reference"></param> /// <returns></returns> private static ImageAsPointCollection RotateToReference(ImageAsPointCollection reference, ImageAsPointCollection original) { ImageAsPointCollection result = original; //Get list's length int k = Math.Min(reference.listOfPoints.Count, original.listOfPoints.Count); //Calculate theta double numerator = 0; double denominator = 0; for(int i = 0; i < k; i++) { numerator += original.listOfPoints[i].x * reference.listOfPoints[i].y - original.listOfPoints[i].y * reference.listOfPoints[i].x; denominator += original.listOfPoints[i].x * reference.listOfPoints[i].x + original.listOfPoints[i].y * reference.listOfPoints[i].y; } double theta = Math.Atan(numerator/denominator); //Do rotation for(int i = 0; i < result.listOfPoints.Count; i++) { result.listOfPoints[i].x = (Math.Cos(theta) * original.listOfPoints[i].x - Math.Sin(theta) * original.listOfPoints[i].y); result.listOfPoints[i].y = (Math.Sin(theta) * original.listOfPoints[i].x + Math.Cos(theta) * original.listOfPoints[i].y); } return result; }
//--------------------------------------------Helper methods-------------------------------------------- /// <summary> /// Downsample a set of data to a desired size. /// </summary> /// <param name="original"></param> /// <param name="desiredNumberOfPoints"></param> /// <returns></returns> private static ImageAsPointCollection DownSampling(ImageAsPointCollection original, int desiredNumberOfPoints) { ImageAsPointCollection result = original; //Get list's length int k = original.listOfPoints.Count; if(k <= desiredNumberOfPoints) { return original; } while(result.listOfPoints.Count > desiredNumberOfPoints) { for(int i = 0; i < result.listOfPoints.Count; i += (int)((double)result.listOfPoints.Count/desiredNumberOfPoints)) { result.listOfPoints.RemoveAt(i); if(result.listOfPoints.Count <= desiredNumberOfPoints) { return result; } } } return result; }
/// <summary> /// Returns the translated version of a given image. /// </summary> /// <param name="img">A monochrome image.</param> /// <returns></returns> public static Image SaveTranslatedImage(Image img) { //If image isn't monochrome format, throw exception if (img.PixelFormat != PixelFormat.Format1bppIndexed) { throw new Exception(imageFormatNotMonochrome); } //Construct ImageAsPointCollection instance. ImageAsPointCollection pointCollection1 = new ImageAsPointCollection(img); //Translate pointCollection1 = Translate(pointCollection1); Image superimposedImage1 = pointCollection1.ToMonoImage(); return superimposedImage1; }