/// <summary> /// Creates an HSV representation from an RGB image using the method describe here http://en.wikipedia.org/wiki/HSL_and_HSV#Formal_derivation /// </summary> /// <param name="input">An image in the RGB colour space</param> public HsvImage(RgbImage input) { //Pull dimensions from the input Height = input.Height; Width = input.Width; Image = new int[Width, Height][]; //Cycle through every pixel for (int x = 0; x < Width; x++) { for (int y = 0; y < Height; y++) { //This just follows the maths in the wiki page listed above. //Again, it's fairly self-explanitory int[] cell = input.Image[x, y]; int R = cell[0]; int G = cell[1]; int B = cell[2]; int M = Math.Max(Math.Max(R, G), B); int m = Math.Min(Math.Min(R, G), B); int C = M - m; float hp; if (C == 0) hp = 0; //Technically undefined, but as it's a grey we can assign any value to it else if (M == R) hp = ((((float)G - (float)B) / (float)C) % 6 + 6) % 6; else if (M == G) hp = (((float)B - (float)R) / (float)C) + 2; else hp = (((float)R - (float)G) / (float)C) + 4; int H = (int)Math.Round(60 * hp); int V = M; int S; if (C == 0) S = 0; else S = (255 * C) / V; //Load the data in to the array Image[x, y] = new int[3]; Image[x, y][0] = H; Image[x, y][1] = S; Image[x, y][2] = V; } } }
/// <summary> /// Calculates the Root Mean Squared deviation between the RGB values of two images /// </summary> /// <param name="comparison">The image to compare to</param> /// <returns>The RMSD between the RGB values of the inputs</returns> public int compareBitmaps(RgbImage comparison) { return (new RgbImage(this)).compareBitmaps(comparison); }
/// <summary> /// Perform the enhancements on the waterfall image in RGB space /// </summary> static void rgbEnhance() { //Load the image RgbImage distorted = new RgbImage(Resources.waterfall); //Perform the 3 enhancements RgbImage brightened = distorted.brighten(1.5f); RgbImage logBrightened = distorted.logBrighten(); RgbImage darkLogBrightened = logBrightened.brighten(0.5f); //Save out the 3 enhancements brightened.toBitmap().Save(@"c:\output\brightened.bmp"); logBrightened.toBitmap().Save(@"c:\output\logBrightened.bmp"); darkLogBrightened.toBitmap().Save(@"c:\output\darkLogBrightened.bmp"); }
/// <summary> /// Brighten the image by a log scale /// </summary> /// <returns>The brightened image</returns> public RgbImage logBrighten() { //create a new image to output RgbImage output = new RgbImage(Height, Width); int properties = Image[0, 0].Length; //Create arrays to store the largest and smallest value for each channel double[] Max = new double[properties]; double[] Min = new double[properties]; for(int i = 0; i < properties; i++){ Max[i] = 0; Min[i] = 255; } //Create an array of floats to store intermediete values double[,][] interim = new double[Width, Height][]; //Cycle through every pixel for (int x = 0; x < Width; x++) { for (int y = 0; y < Height; y++) { //Cycle through every channel interim[x, y] = new double[properties]; for (int i = 0; i < properties; i++) { //Take the log of the value, making sure it's above 0 interim[x, y][i] = Math.Max(0,Math.Log(Image[x, y][i])); //Update the min/max if required if (interim[x, y][i] > Max[i]) Max[i] = interim[x, y][i]; if (interim[x, y][i] < Min[i]) Min[i] = interim[x, y][i]; } } } //Loop through every pixel (again) for (int x = 0; x < Width; x++) { for (int y = 0; y < Height; y++) { //Loop through every chanel for (int i = 0; i < properties; i++) { //Normalise the value, and put it in the output image output.Image[x, y][i] = (int)(255 * (Min[i] + interim[x, y][i]) / Max[i]); } } } return output; }
/// <summary> /// Calculates the Root Mean Squared deviation between the RGB values of two images /// </summary> /// <param name="comparison">The image to compare to</param> /// <returns>The RMSD between the RGB values of the inputs</returns> public int compareBitmaps(RgbImage comparison) { //If the images are the same, by definition their difference is 0 if (this == comparison) return 0; //If the widths or heights are different, no meaningful comparison can be made, therefore return the largest possible value. if (!(Height == comparison.Height && Width == comparison.Width)) return (int)Int32.MaxValue; //Itterate over every pixel in the primary image int errSum = 0; for (int x = 0; x < Width; x++) { for (int y = 0; y < Height; y++) { //Calculate the difference between each of the pixel values for this location int errR = Image[x, y][0] - comparison.Image[x, y][0]; int errG = Image[x, y][1] - comparison.Image[x, y][1]; int errB = Image[x, y][2] - comparison.Image[x, y][2]; //Find the sum of the squares of all of the errors errSum += (errR * errR) + (errB * errB) + (errG * errG); } } //Return the square root of the sum of squares of the errors (RMSD) return (int)Math.Round(Math.Sqrt(errSum)); }
/// <summary> /// Brightens the image by a linear factor /// </summary> /// <param name="factor">The factor to brighten by</param> /// <returns>The brightened image</returns> public RgbImage brighten(float factor) { //Create an empty image to output to RgbImage output = new RgbImage(Height, Width); int properties = Image[0,0].Length; //Cycle through all the pixels for (int x = 0; x < Width; x++) { for (int y = 0; y < Height; y++) { //Cycle through all the channels of the pixel for (int i = 0; i < properties; i++) { //Scale the value, keeping it within 0-255 output.Image[x, y][i] = Math.Max(0,Math.Min(255, (int)(Image[x, y][i] * factor))); } } } return output; }