/// <summary> /// Return a Bitmap created from the given CIELab color values /// </summary> /// <param name="source"></param> /// <returns></returns> public Bitmap GetBitmapFromCIELABImage( CIELab[] s, int width, int height ) { // create map Bitmap b = new Bitmap( width, height, PixelFormat.Format32bppPArgb ); // iterate pixels for( int y = 0 ; y < height ; y++ ) { for( int x = 0 ; x < width ; x++ ) { // get CIE XYZ mapping for pixel and then convert this to CIE Lab double CIE_X, CIE_Y, CIE_Z; CIELab cie = s[ y * width + x ]; RgbaHls.CIELAB_2_CIEXYZ( cie.L, cie.a, cie.b, out CIE_X, out CIE_Y, out CIE_Z ); // convert back to RGB Color c = RgbaHls.CIEXYZ_2_RGB( CIE_X, CIE_Y, CIE_Z ); // insert into bitmap b.SetPixel( x, y, c ); } } // return bitmap return b; }
/// <summary> /// Return a Bitmap created from the given CIELab color values /// </summary> /// <param name="source"></param> /// <returns></returns> public Bitmap GetBitmapFromCIELABImage(CIELab[] s, int width, int height) { // create map Bitmap b = new Bitmap(width, height, PixelFormat.Format32bppPArgb); // iterate pixels for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { // get CIE XYZ mapping for pixel and then convert this to CIE Lab double CIE_X, CIE_Y, CIE_Z; CIELab cie = s[y * width + x]; RgbaHls.CIELAB_2_CIEXYZ(cie.L, cie.a, cie.b, out CIE_X, out CIE_Y, out CIE_Z); // convert back to RGB Color c = RgbaHls.CIEXYZ_2_RGB(CIE_X, CIE_Y, CIE_Z); // insert into bitmap b.SetPixel(x, y, c); } } // return bitmap return(b); }
/// <summary> /// Return and a CIE Lab color mapping of the source image. Alpha is ignored. Also returns the /// min/max CIE L value found in the image. /// </summary> /// <param name="source"></param> /// <returns></returns> public CIELab[] GetCIELABImage(Bitmap source, out double minL, out double maxL) { // init L limits minL = double.MaxValue; maxL = double.MinValue; // create map CIELab[] map = new CIELab[source.Width * source.Height]; // iterate pixels for (int y = 0; y < source.Height; y++) { for (int x = 0; x < source.Width; x++) { // get CIE XYZ mapping for pixel and then convert this to CIE Lab double CIE_X, CIE_Y, CIE_Z; RgbaHls.RGB_2_CIEXYZ(source.GetPixel(x, y), out CIE_X, out CIE_Y, out CIE_Z); double CIE_L, CIE_a, CIE_b; RgbaHls.CIEXYZ_2_CIELAB(CIE_X, CIE_Y, CIE_Z, out CIE_L, out CIE_a, out CIE_b); // insert into map map[y * source.Width + x] = new CIELab(CIE_L, CIE_a, CIE_b); // update L limits minL = Math.Min(minL, CIE_L); maxL = Math.Max(maxL, CIE_L); } } // return mapping return(map); }
/// <summary> /// Create a new ( red eye removed ) image. /// </summary> /// <returns></returns> public Bitmap GetNewImage(CIELab[] cieMap, double minL, double maxL, Bitmap mask) { // create a set of target color which are the original CIE Lab colors set to gray scales by setting a* and b* to zero and stretching the L* to // the dynamic range of the image. This formula is used // // L = maxL // ---------------------------- * ( L - minL ) // maxL - minL // // a = 0 // b = 0 // CIELab[] t = new CIELab[cieMap.Length]; double r = maxL / (maxL - minL); for (int y = 0; y < mask.Height; y++) { for (int x = 0; x < mask.Width; x++) { t[y * mask.Width + x] = new CIELab(r * (cieMap[y * mask.Width + x].L - minL), 0, 0); } } // now calculate a new CIE Lab color for pixel using: // // pixel = t[i,j] * mask[i,j] + ( cieMap[i,j] * ( 1 - mask[i,j] ) ) // for (int j = 0; j < mask.Height; j++) { for (int i = 0; i < mask.Width; i++) { // get normalized mask value for this pixel ( any of the RGB components will do, they should all be the same ) double m = (double)(mask.GetPixel(i, j).R) / 255.0; // get original CIE Lab from ciemap CIELab original = cieMap[j * mask.Width + i]; CIELab target = t[j * mask.Width + i]; // calculate new L* a* b* values using formula above double L = target.L * m + original.L * (1 - m); double a = target.a * m + original.a * (1 - m); double b = target.b * m + original.b * (1 - m); // reuse target array to hold new value for pixel ( in CIE Lab space ) t[j * mask.Width + i] = new CIELab(L, a, b); } } // return new image return(GetBitmapFromCIELABImage(t, mask.Width, mask.Height)); }
/// <summary> /// Create a new ( red eye removed ) image. /// </summary> /// <returns></returns> public Bitmap GetNewImage( CIELab[] cieMap, double minL, double maxL, Bitmap mask ) { // create a set of target color which are the original CIE Lab colors set to gray scales by setting a* and b* to zero and stretching the L* to // the dynamic range of the image. This formula is used // // L = maxL // ---------------------------- * ( L - minL ) // maxL - minL // // a = 0 // b = 0 // CIELab[] t = new CIELab[ cieMap.Length ]; double r = maxL / ( maxL - minL ); for( int y = 0 ; y < mask.Height ; y++ ) { for( int x = 0 ; x < mask.Width ; x++ ) { t[ y * mask.Width + x ] = new CIELab( r * ( cieMap[ y * mask.Width + x ].L - minL ) , 0, 0 ); } } // now calculate a new CIE Lab color for pixel using: // // pixel = t[i,j] * mask[i,j] + ( cieMap[i,j] * ( 1 - mask[i,j] ) ) // for( int j = 0 ; j < mask.Height ; j++ ) { for( int i = 0 ; i < mask.Width ; i++ ) { // get normalized mask value for this pixel ( any of the RGB components will do, they should all be the same ) double m = (double)(mask.GetPixel( i, j ).R) / 255.0; // get original CIE Lab from ciemap CIELab original = cieMap[ j * mask.Width + i ]; CIELab target = t[ j * mask.Width + i ]; // calculate new L* a* b* values using formula above double L = target.L * m + original.L * ( 1 - m ); double a = target.a * m + original.a * ( 1 - m ); double b = target.b * m + original.b * ( 1 - m ); // reuse target array to hold new value for pixel ( in CIE Lab space ) t[ j * mask.Width + i ] = new CIELab( L, a, b ); } } // return new image return GetBitmapFromCIELABImage( t, mask.Width, mask.Height ); }
/// <summary> /// Returns a bitmap of gray levels where the 'red-ness' of the original pixel determines the /// black/white value. Black pixels are farthest from red and white pixels are closest. /// </summary> /// <param name="source"></param> /// <returns></returns> public Bitmap GetMaskImage( CIELab[] cieMap, int width, int height ) { // get chromaticty distance map and min/max chromaticty distances double minCD, maxCD; double[] cdMap = GetChromaticityDistanceMap( cieMap, width, height, out minCD, out maxCD ); // create a black image of the same size Bitmap mask = ImageUtility.CreateImage( width, height, PixelFormat.Format32bppPArgb, Color.FromArgb(255,0,0,0) ); // process each pixel for( int y = 0 ; y < height ; y++ ) { for( int x = 0 ; x < width ; x++ ) { // Mask value = // // maxCD - chromaticty distance [x,y] // Round( 255 * ------------------------------------- ) // maxCD - minCD // double temp = ( maxCD - cdMap[ y * width + x ] ) / ( maxCD - minCD ); int maskValue = (int)Math.Min( 255.0, Math.Max( 0.0, Math.Round( 255.0 * temp ) ) ); // set RGBA values mask.SetPixel( x, y, Color.FromArgb( 255, maskValue, maskValue, maskValue ) ); } } // return mask image return mask; }
/// <summary> /// Return and a CIE Lab color mapping of the source image. Alpha is ignored. Also returns the /// min/max CIE L value found in the image. /// </summary> /// <param name="source"></param> /// <returns></returns> public CIELab[] GetCIELABImage( Bitmap source, out double minL, out double maxL ) { // init L limits minL = double.MaxValue; maxL = double.MinValue; // create map CIELab[] map = new CIELab[ source.Width * source.Height ]; // iterate pixels for( int y = 0 ; y < source.Height ; y++ ) { for( int x = 0 ; x < source.Width ; x++ ) { // get CIE XYZ mapping for pixel and then convert this to CIE Lab double CIE_X, CIE_Y, CIE_Z; RgbaHls.RGB_2_CIEXYZ( source.GetPixel( x,y ), out CIE_X, out CIE_Y, out CIE_Z ); double CIE_L, CIE_a, CIE_b; RgbaHls.CIEXYZ_2_CIELAB( CIE_X, CIE_Y, CIE_Z, out CIE_L, out CIE_a, out CIE_b ); // insert into map map[ y * source.Width + x ] = new CIELab( CIE_L, CIE_a, CIE_b ); // update L limits minL = Math.Min( minL, CIE_L ); maxL = Math.Max( maxL, CIE_L ); } } // return mapping return map; }
/// <summary> /// Given a CIELab array ( obtained with GetCIELABImage ) this calculate the chromaticity distance of /// each pixel fro the 'typical' red eye CIELAB color. Also returns the Min/Max chromaticity distances found /// </summary> /// <returns></returns> public double[] GetChromaticityDistanceMap( CIELab[] map, int width, int height, out double minDistance, out double maxDistance ) { // initialize min/max minDistance = double.MaxValue; maxDistance = double.MinValue; // create return array double[] cdMap = new double[ width * height ]; // iterate pixels for( int y = 0 ; y < height ; y++ ) { for( int x = 0 ; x < width ; x++ ) { // get distance for this pixel double c = RgbaHls.ChromaticityDistance( map[ y * width + x ].a, kA_REDEYE, map[ y * width + x ].b, kB_REDEYE ); // assign to map cdMap[ y * width + x ] = c; // update min/max minDistance = Math.Min( minDistance, c ); maxDistance = Math.Max( maxDistance, c ); } } // return chromaticty map return cdMap; }