private PixImage GetSubImage(int x, int y, int width, int height) { PixImage newImage = new PixImage(width, height); for (int i = x; i < x + width; i++) { for (int j = y; j < y + height; j++) { newImage.setPixel(i - x, j - y, this.getRed(i, j), this.getGreen(i, j), this.getBlue(i, j)); } } return(newImage); }
/// <summary> /// array2PixImage() converts a 2D array of grayscale intensities to /// a grayscale PixImage. /// </summary> /// <param name="pixels"> a 2D array of grayscale intensities in the range 0...255. </param> /// <returns> a new PixImage whose red, green, and blue values are equal to /// the input grayscale intensities. </returns> private static PixImage array2PixImage(int[][] pixels) { int width = pixels.Length; int height = pixels[0].Length; PixImage image = new PixImage(width, height); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { image.setPixel(x, y, (short)pixels[x][y], (short)pixels[x][y], (short)pixels[x][y]); } } return(image); }
//this helper function returns a new PixImage with pixels on the edges reflected across the axis //so that each pixel in the original image has 9 neighbors (including itself) private PixImage GetReflection() { var newMatrix = new PixImage(Width + 2, Height + 2); for (int x = 1; x < newMatrix.Width - 1; x++) { for (int y = 1; y < newMatrix.Height - 1; y++) { newMatrix.setPixel(x, y, this.getRed(x - 1, y - 1), this.getGreen(x - 1, y - 1), this.getBlue(x - 1, y - 1)); } } for (int x = 0; x < newMatrix.Width; x++) { for (int y = 0; y < newMatrix.Height; y++) { int a = x; int b = y; short red = 0; if (x == 0) { a = 1; } if (y == 0) { b = 1; } if (x == newMatrix.Width - 1) { a = newMatrix.Width - 2; } if (y == newMatrix.Height - 1) { b = newMatrix.Height - 2; } red = (short)newMatrix.getRed(a, b); //Console.WriteLine("getting a: {0}, b: {1}, for x: {2}, y: {3} which is: {4}", a, b, x, y, red); newMatrix.setPixel(x, y, red, red, red); } } //Console.WriteLine(newMatrix); return(newMatrix); }
/// <summary> /// sobelEdges() applies the Sobel operator, identifying edges in "this" /// image. The Sobel operator computes a magnitude that represents how /// strong the edge is. We compute separate gradients for the red, blue, and /// green components at each pixel, then sum the squares of the three /// gradients at each pixel. We convert the squared magnitude at each pixel /// into a grayscale pixel intensity in the range 0...255 with the logarithmic /// mapping encoded in mag2gray(). The output is a grayscale PixImage whose /// pixel intensities reflect the strength of the edges. /// /// See http://en.wikipedia.org/wiki/Sobel_operator#Formulation for details. /// </summary> /// <returns> a grayscale PixImage representing the edges of the input image. /// Whiter pixels represent stronger edges. </returns> public virtual PixImage SobelEdges() { var newImage = new PixImage(this.Width, this.Height); //constant, Sobel operator int[,] xSobelOperator = { { 1, 0, -1 }, { 2, 0, -2 }, { 1, 0, -1 } }; //constant, Sobel operator int[,] ySobelOperator = { { 1, 2, 1 }, { 0, 0, 0 }, { -1, -2, -1 } }; int[] gx, gy; long[,] energy = new long[Width, Height]; //get reflection so every pixel within the original image has 9 neighbors (incl itself) var newImageWithReflection = this.GetReflection(); //traverse each pixel within original image and calculate dot product for (int i = 0; i < Width; i++) { for (int j = 0; j < Height; j++) { //get subimage for each pixel (from newImageWithReflection) var image = newImageWithReflection.GetSubImage(i, j, 3, 3); //calculate dot product with x and y sobel operators gx = image.DotProduct(xSobelOperator); gy = image.DotProduct(ySobelOperator); //calculate energy values energy[i, j] = (gx[0] * gx[0]) + (gy[0] * gy[0]) + (gx[1] * gx[1]) + (gy[1] * gy[1]) + (gx[2] * gx[2]) + (gy[2] * gy[2]); newImage.setPixel(i, j, mag2gray(energy[i, j]), mag2gray(energy[i, j]), mag2gray(energy[i, j])); } } return(newImage); }
/// <summary> /// equals() checks whether two images are the same, i.e. have the same /// dimensions and pixels. /// </summary> /// <param name="image"> a PixImage to compare with "this" PixImage. </param> /// <returns> true if the specified PixImage is identical to "this" PixImage. </returns> public virtual bool Equals(PixImage image) { int width = Width; int height = Height; if (image == null || width != image.Width || height != image.Height) { return(false); } for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { if (!(getRed(x, y) == image.getRed(x, y) && getGreen(x, y) == image.getGreen(x, y) && getBlue(x, y) == image.getBlue(x, y))) { return(false); } } } return(true); }
/// <summary> /// boxBlur() returns a blurred version of "this" PixImage. /// /// If numIterations == 1, each pixel in the output PixImage is assigned /// a value equal to the average of its neighboring pixels in "this" PixImage, /// INCLUDING the pixel itself. /// /// A pixel not on the image boundary has nine neighbors--the pixel itself and /// the eight pixels surrounding it. A pixel on the boundary has six /// neighbors if it is not a corner pixel; only four neighbors if it is /// a corner pixel. The average of the neighbors is the sum of all the /// neighbor pixel values (including the pixel itself) divided by the number /// of neighbors, with non-integer quotients rounded toward zero (as C# does /// naturally when you divide two integers). /// /// Each color (red, green, blue) is blurred separately. The red input should /// have NO effect on the green or blue outputs, etc. /// /// The parameter numIterations specifies a number of repeated iterations of /// box blurring to perform. If numIterations is zero or negative, "this" /// PixImage is returned (not a copy). If numIterations is positive, the /// return value is a newly constructed PixImage. /// /// IMPORTANT: DO NOT CHANGE "this" PixImage!!! All blurring/changes should /// appear in the new, output PixImage only. /// </summary> /// <param name="numIterations"> the number of iterations of box blurring. </param> /// <returns> a blurred version of "this" PixImage. </returns> public virtual PixImage BoxBlur(int numIterations) { if (numIterations > 0) { //make a new image var newImage = new PixImage(this.Width, this.Height); for (int i = 0; i < this.Width; i++) { for (int j = 0; j < this.Height; j++) { Pixel newPixel = this.GetBlurredPixel(i, j); newImage.setPixel(i, j, newPixel.Red, newPixel.Green, newPixel.Blue); // Console.WriteLine("At pixel {0},{1}: {2}, {3}, {4}. Counter: {5}", i, j, red, green, blue, counter); } } for (int n = 0; n < numIterations; n++) { newImage = newImage.BoxBlur(n); } return(newImage); } return(this); }
/// <summary> /// main() runs a series of tests to ensure that the convolutions (box blur /// and Sobel) are correct. /// </summary> public static void Main(string[] args) { // Be forwarned that when you write arrays directly in C# as below, // each "row" of text is a column of your image--the numbers get // transposed. PixImage image1 = array2PixImage(new int[][] { new int[] { 0, 10, 240 }, new int[] { 30, 120, 250 }, new int[] { 80, 250, 255 } }); Console.WriteLine("Testing getWidth/getHeight on a 3x3 image. " + "Input image:"); Console.Write(image1); doTest(image1.Width == 3 && image1.Height == 3, "Incorrect image width and height."); Console.WriteLine("Testing blurring on a 3x3 image."); doTest(image1.BoxBlur(1).Equals(array2PixImage(new int[][] { new int[] { 40, 108, 155 }, new int[] { 81, 137, 187 }, new int[] { 120, 164, 218 } })), "Incorrect box blur (1 rep):\n" + image1.BoxBlur(1)); doTest(image1.BoxBlur(2).Equals(array2PixImage(new int[][] { new int[] { 91, 118, 146 }, new int[] { 108, 134, 161 }, new int[] { 125, 151, 176 } })), "Incorrect box blur (2 rep):\n" + image1.BoxBlur(2)); doTest(image1.BoxBlur(2).Equals(image1.BoxBlur(1).BoxBlur(1)), "Incorrect box blur (1 rep + 1 rep):\n" + image1.BoxBlur(2) + image1.BoxBlur(1).BoxBlur(1)); Console.WriteLine("Testing edge detection on a 3x3 image."); doTest(image1.SobelEdges().Equals(array2PixImage(new int[][] { new int[] { 104, 189, 180 }, new int[] { 160, 193, 157 }, new int[] { 166, 178, 96 } })), "Incorrect Sobel:\n" + image1.SobelEdges()); PixImage image2 = array2PixImage(new int[][] { new int[] { 0, 100, 100 }, new int[] { 0, 0, 100 } }); Console.WriteLine("Testing getWidth/getHeight on a 2x3 image. " + "Input image:"); Console.Write(image2); doTest(image2.Width == 2 && image2.Height == 3, "Incorrect image width and height."); Console.WriteLine("Testing blurring on a 2x3 image."); doTest(image2.BoxBlur(1).Equals(array2PixImage(new int[][] { new int[] { 25, 50, 75 }, new int[] { 25, 50, 75 } })), "Incorrect box blur (1 rep):\n" + image2.BoxBlur(1)); Console.WriteLine("Testing edge detection on a 2x3 image."); doTest(image2.SobelEdges().Equals(array2PixImage(new int[][] { new int[] { 122, 143, 74 }, new int[] { 74, 143, 122 } })), "Incorrect Sobel:\n" + image2.SobelEdges()); Console.ReadLine(); }