/// <summary> /// Rotates an image /// </summary> /// <param name="image">Image to rotate</param> /// <param name="angleInDegrees">Number of degrees to rotate in counter clockwise direction</param> /// <param name="mode">Pixel resampling method</param> /// <returns>Rotated image</returns> public static Shared <Image> Rotate(this Image image, float angleInDegrees, SamplingMode mode) { float ca = (float)System.Math.Cos(angleInDegrees * System.Math.PI / 180.0f); float sa = (float)System.Math.Sin(angleInDegrees * System.Math.PI / 180.0f); float minx = 0.0f; float miny = 0.0f; float maxx = 0.0f; float maxy = 0.0f; float x = image.Width - 1; float y = 0.0f; float nx = (x * ca) - (y * sa); float ny = (x * sa) + (y * ca); if (nx < minx) { minx = nx; } if (nx > maxx) { maxx = nx; } if (ny < miny) { miny = ny; } if (ny > maxy) { maxy = ny; } x = image.Width - 1; y = image.Height - 1; nx = (x * ca) - (y * sa); ny = (x * sa) + (y * ca); if (nx < minx) { minx = nx; } if (nx > maxx) { maxx = nx; } if (ny < miny) { miny = ny; } if (ny > maxy) { maxy = ny; } x = 0.0f; y = image.Height - 1; nx = (x * ca) - (y * sa); ny = (x * sa) + (y * ca); if (nx < minx) { minx = nx; } if (nx > maxx) { maxx = nx; } if (ny < miny) { miny = ny; } if (ny > maxy) { maxy = ny; } int dstWidth = (int)(maxx - minx + 1); int dstHeight = (int)(maxy - miny + 1); var bitmap = new Bitmap(dstWidth, dstHeight); var graphics = Graphics.FromImage(bitmap); graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy; switch (mode) { case SamplingMode.Point: graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed; graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed; graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed; break; case SamplingMode.Bilinear: graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bilinear; graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; break; case SamplingMode.Bicubic: graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; break; } graphics.TranslateTransform(-minx, -miny); graphics.RotateTransform(angleInDegrees); graphics.DrawImage(image.ToManagedImage(), new Point(0, 0)); return(ImagePool.GetOrCreate(bitmap)); }
/// <summary> /// Flips an image along a specified axis /// </summary> /// <param name="image">Image to flip</param> /// <param name="mode">Axis along which to flip</param> /// <returns>A new flipped image</returns> public static Shared <Image> Flip(this Image image, FlipMode mode) { if (image.PixelFormat == PixelFormat.Gray_16bpp) { // We can't handle this through GDI. Shared <Image> dstImage = ImagePool.GetOrCreate(image.Width, image.Height, image.PixelFormat); unsafe { int srcBytesPerPixel = PixelFormatHelper.GetBytesPerPixel(image.PixelFormat); int dstBytesPerPixel = PixelFormatHelper.GetBytesPerPixel(dstImage.Resource.PixelFormat); byte *srcRow = (byte *)image.ImageData.ToPointer(); byte *dstRow = (byte *)dstImage.Resource.ImageData.ToPointer(); int ystep = dstImage.Resource.Stride; if (mode == FlipMode.AlongHorizontalAxis) { dstRow += dstImage.Resource.Stride * (image.Height - 1); ystep = -dstImage.Resource.Stride; } int xstep = dstBytesPerPixel; int xoffset = 0; if (mode == FlipMode.AlongVerticalAxis) { xoffset = dstBytesPerPixel * (dstImage.Resource.Width - 1); xstep = -dstBytesPerPixel; } for (int i = 0; i < image.Height; i++) { byte *srcCol = srcRow; byte *dstCol = dstRow + xoffset; for (int j = 0; j < image.Width; j++) { ((ushort *)dstCol)[0] = ((ushort *)srcCol)[0]; srcCol += srcBytesPerPixel; dstCol += xstep; } srcRow += image.Stride; dstRow += ystep; } } return(dstImage); } else { var bitmap = new Bitmap(image.Width, image.Height); var graphics = Graphics.FromImage(bitmap); switch (mode) { case FlipMode.AlongHorizontalAxis: graphics.TranslateTransform(0.0f, image.Height - 1); graphics.ScaleTransform(1.0f, -1.0f); break; case FlipMode.AlongVerticalAxis: graphics.TranslateTransform(image.Width - 1, 0.0f); graphics.ScaleTransform(-1.0f, 1.0f); break; } graphics.DrawImage(image.ToManagedImage(), new Point(0, 0)); return(ImagePool.GetOrCreate(bitmap)); } }
/// <summary> /// Flips an image along a specified axis /// </summary> /// <param name="imageA">First image</param> /// <param name="imageB">Second image</param> /// <returns>Difference image</returns> public static Shared <Image> AbsDiff(this Image imageA, Image imageB) { if (imageA.Width != imageB.Width || imageA.Height != imageB.Height || imageA.PixelFormat != imageB.PixelFormat) { throw new System.Exception("Images sizes/types don't match"); } Shared <Image> dstImage = ImagePool.GetOrCreate(imageA.Width, imageA.Height, imageA.PixelFormat); unsafe { int bytesPerPixel = PixelFormatHelper.GetBytesPerPixel(imageA.PixelFormat); byte *srcRowA = (byte *)imageA.ImageData.ToPointer(); byte *srcRowB = (byte *)imageB.ImageData.ToPointer(); byte *dstRow = (byte *)dstImage.Resource.ImageData.ToPointer(); for (int i = 0; i < imageA.Height; i++) { byte *srcColA = srcRowA; byte *srcColB = srcRowB; byte *dstCol = dstRow; int delta0, delta1, delta2, delta3; for (int j = 0; j < imageA.Width; j++) { switch (imageA.PixelFormat) { case PixelFormat.BGRA_32bpp: delta0 = srcColA[0] - srcColB[0]; delta1 = srcColA[1] - srcColB[1]; delta2 = srcColA[2] - srcColB[2]; delta3 = srcColA[3] - srcColB[3]; dstCol[0] = (byte)((delta0 < 0) ? -delta0 : delta0); dstCol[1] = (byte)((delta1 < 0) ? -delta1 : delta1); dstCol[2] = (byte)((delta2 < 0) ? -delta2 : delta2); dstCol[3] = (byte)((delta3 < 0) ? -delta3 : delta3); break; case PixelFormat.BGRX_32bpp: delta0 = srcColA[0] - srcColB[0]; delta1 = srcColA[1] - srcColB[1]; delta2 = srcColA[2] - srcColB[2]; dstCol[0] = (byte)((delta0 < 0) ? -delta0 : delta0); dstCol[1] = (byte)((delta1 < 0) ? -delta1 : delta1); dstCol[2] = (byte)((delta2 < 0) ? -delta2 : delta2); break; case PixelFormat.BGR_24bpp: delta0 = srcColA[0] - srcColB[0]; delta1 = srcColA[1] - srcColB[1]; delta2 = srcColA[2] - srcColB[2]; dstCol[0] = (byte)((delta0 < 0) ? -delta0 : delta0); dstCol[1] = (byte)((delta1 < 0) ? -delta1 : delta1); dstCol[2] = (byte)((delta2 < 0) ? -delta2 : delta2); break; case PixelFormat.Gray_16bpp: delta0 = ((ushort *)srcColA)[0] - ((ushort *)srcColB)[0]; ((ushort *)dstCol)[0] = (ushort)((delta0 < 0) ? -delta0 : delta0); break; case PixelFormat.Gray_8bpp: delta0 = srcColA[0] - srcColB[0]; dstCol[0] = (byte)((delta0 < 0) ? -delta0 : delta0); break; case PixelFormat.RGBA_64bpp: delta0 = (ushort)(((ushort *)srcColA)[0] - ((ushort *)srcColB)[0]); delta1 = (ushort)(((ushort *)srcColA)[1] - ((ushort *)srcColB)[1]); delta2 = (ushort)(((ushort *)srcColA)[2] - ((ushort *)srcColB)[2]); delta3 = (ushort)(((ushort *)srcColA)[3] - ((ushort *)srcColB)[3]); ((ushort *)dstCol)[0] = (ushort)((delta0 < 0) ? -delta0 : delta0); ((ushort *)dstCol)[1] = (ushort)((delta1 < 0) ? -delta1 : delta1); ((ushort *)dstCol)[2] = (ushort)((delta2 < 0) ? -delta2 : delta2); ((ushort *)dstCol)[3] = (ushort)((delta3 < 0) ? -delta3 : delta3); break; default: throw new System.Exception("Unexpected image format"); } srcColA += bytesPerPixel; srcColB += bytesPerPixel; dstCol += bytesPerPixel; } srcRowA += imageA.Stride; srcRowB += imageB.Stride; dstRow += dstImage.Resource.Stride; } } return(dstImage); }
/// <summary> /// Initializes a new instance of the <see cref="ImageTransformer"/> class. /// </summary> /// <param name="pipeline">The pipeline to add the component to.</param> /// <param name="transformer">Function for transforming the source image.</param> /// <param name="pixelFormat">Pixel format for destination image.</param> /// <param name="sharedImageAllocator ">Optional image allocator for creating new shared image.</param> /// <param name="name">An optional name for the component.</param> public ImageTransformer( Pipeline pipeline, TransformDelegate transformer, PixelFormat pixelFormat, Func <int, int, PixelFormat, Shared <Image> > sharedImageAllocator = null, string name = nameof(ImageTransformer)) : base(pipeline, name) { this.transformer = transformer; this.pixelFormat = pixelFormat; this.sharedImageAllocator = sharedImageAllocator ?? ((width, height, pixelFormat) => ImagePool.GetOrCreate(width, height, pixelFormat)); }
/// <summary> /// Performs per channel thresholding on the image /// </summary> /// <param name="image">Image to be thresholded</param> /// <param name="threshold">Threshold value</param> /// <param name="maxvalue">Maximum value</param> /// <param name="type">Type of thresholding to perform</param> /// <returns>The thresholded image</returns> public static Shared <Image> Threshold(this Image image, int threshold, int maxvalue, Threshold type) { Shared <Image> dstImage = ImagePool.GetOrCreate(image.Width, image.Height, image.PixelFormat); unsafe { int bytesPerPixel = PixelFormatHelper.GetBytesPerPixel(image.PixelFormat); byte *srcRow = (byte *)image.ImageData.ToPointer(); byte *dstRow = (byte *)dstImage.Resource.ImageData.ToPointer(); for (int i = 0; i < image.Height; i++) { byte *srcCol = srcRow; byte *dstCol = dstRow; for (int j = 0; j < image.Width; j++) { int r = 0, g = 0, b = 0, a = 0; switch (image.PixelFormat) { case PixelFormat.BGRA_32bpp: b = srcCol[0]; g = srcCol[1]; r = srcCol[2]; a = srcCol[3]; break; case PixelFormat.BGRX_32bpp: b = srcCol[0]; g = srcCol[1]; r = srcCol[2]; break; case PixelFormat.BGR_24bpp: b = srcCol[0]; g = srcCol[1]; r = srcCol[2]; break; case PixelFormat.Gray_16bpp: r = g = b = a = ((ushort *)srcCol)[0]; break; case PixelFormat.Gray_8bpp: r = g = b = a = srcCol[0]; break; case PixelFormat.RGBA_64bpp: r = ((ushort *)srcCol)[0]; g = ((ushort *)srcCol)[1]; b = ((ushort *)srcCol)[2]; a = ((ushort *)srcCol)[3]; break; default: break; } switch (type) { case Imaging.Threshold.Binary: r = (r > threshold) ? maxvalue : 0; g = (g > threshold) ? maxvalue : 0; b = (b > threshold) ? maxvalue : 0; a = (a > threshold) ? maxvalue : 0; break; case Imaging.Threshold.BinaryInv: r = (r > threshold) ? 0 : maxvalue; g = (g > threshold) ? 0 : maxvalue; b = (b > threshold) ? 0 : maxvalue; a = (a > threshold) ? 0 : maxvalue; break; case Imaging.Threshold.Truncate: r = (r > threshold) ? threshold : r; g = (g > threshold) ? threshold : g; b = (b > threshold) ? threshold : b; a = (a > threshold) ? threshold : a; break; case Imaging.Threshold.ToZero: r = (r > threshold) ? r : 0; g = (g > threshold) ? g : 0; b = (b > threshold) ? b : 0; a = (a > threshold) ? a : 0; break; case Imaging.Threshold.ToZeroInv: r = (r > threshold) ? 0 : r; g = (g > threshold) ? 0 : g; b = (b > threshold) ? 0 : b; a = (a > threshold) ? 0 : a; break; } switch (image.PixelFormat) { case PixelFormat.BGRA_32bpp: dstCol[0] = (byte)b; dstCol[1] = (byte)g; dstCol[2] = (byte)r; dstCol[3] = (byte)a; break; case PixelFormat.BGRX_32bpp: dstCol[0] = (byte)b; dstCol[1] = (byte)g; dstCol[2] = (byte)r; break; case PixelFormat.BGR_24bpp: dstCol[0] = (byte)b; dstCol[1] = (byte)g; dstCol[2] = (byte)r; break; case PixelFormat.Gray_16bpp: ((ushort *)srcCol)[0] = (ushort)r; break; case PixelFormat.Gray_8bpp: srcCol[0] = (byte)r; break; case PixelFormat.RGBA_64bpp: ((ushort *)srcCol)[0] = (ushort)r; ((ushort *)srcCol)[1] = (ushort)g; ((ushort *)srcCol)[2] = (ushort)b; ((ushort *)srcCol)[3] = (ushort)a; break; default: break; } srcCol += bytesPerPixel; dstCol += bytesPerPixel; } srcRow += image.Stride; dstRow += dstImage.Resource.Stride; } } return(dstImage); }