/// <summary> /// Reduces the height of the <see cref="Image"/> by the factor of 2. /// </summary> /// <returns>The scaled <see cref="Image"/>.</returns> public Image Reduce1x2() { if (this.BitsPerPixel != 1) { throw new NotSupportedException(Properties.Resources.E_UnsupportedDepth_1bpp); } Image dst = new Image( this.Width, (this.Height + 1) >> 1, this.BitsPerPixel, this.HorizontalResolution, this.VerticalResolution / 2); int stride = this.Stride; ulong[] bitssrc = this.Bits; ulong[] bitsdst = dst.Bits; int offsrc = 0; int offdst = 0; for (int i = 0, ii = this.Height >> 1; i < ii; i++, offsrc += 2 * stride, offdst += stride) { Vectors.Or(stride, bitssrc, offsrc, bitssrc, offsrc + stride, bitsdst, offdst); } if ((this.Height & 1) != 0) { Vectors.Copy(stride, bitssrc, offsrc, bitsdst, offdst); } dst.AppendTransform(new MatrixTransform(1.0, 0.5)); return(dst); }
public Image Inflate(int left, int top, int right, int bottom, BorderType borderType, uint borderValue) { // calculate and verify target area in source coordinates Rectangle bounds = Rectangle.FromLTRB( -left, -top, this.Width + right, this.Height + bottom); if (bounds.Width <= 0) { throw new ArgumentException("The new image width is invalid."); } if (bounds.Height <= 0) { throw new ArgumentException("The new image height is invalid."); } Image dst = new Image(bounds.Size, this); // calculate source area to copy from Rectangle srcarea = Rectangle.Intersect(bounds, this.Bounds); // calculate destination area to copy to Rectangle dstarea = Rectangle.Offset(srcarea, -bounds.X, -bounds.Y); Image.CopyArea(dst, dstarea.X, dstarea.Y, srcarea.Width, srcarea.Height, this, srcarea.X, srcarea.Y); // set border dst.SetBorder(dstarea, borderType, borderValue); dst.AppendTransform(new MatrixTransform(left, top)); return(dst); }
/// <summary> /// Crops the <see cref="Image"/> using rectangle specified by a pair of coordinates, a width, and a height. /// </summary> /// <param name="x">The x-coordinate of the upper-left corner of the area.</param> /// <param name="y">The y-coordinate of the upper-left corner of the area.</param> /// <param name="width">The width of the area.</param> /// <param name="height">The height of the area.</param> /// <returns> /// A new cropped <see cref="Image"/>. /// </returns> /// <exception cref="ArgumentOutOfRangeException"> /// <para>The rectangular area described by <paramref name="x"/>, <paramref name="y"/>, <paramref name="width"/> and <paramref name="height"/> is outside of this <see cref="Image"/> bounds.</para> /// </exception> public Image Crop(int x, int y, int width, int height) { this.ValidateArea(x, y, width, height); Image dst = new Image(width, height, this); Image.CopyArea(dst, 0, 0, width, height, this, x, y); dst.AppendTransform(new MatrixTransform(-x, -y)); return(dst); }
/// <summary> /// Reduces the height of the <see cref="Image"/> by the factor of 4. /// </summary> /// <returns>The scaled <see cref="Image"/>.</returns> public Image Reduce1x4() { if (this.BitsPerPixel != 1) { throw new NotSupportedException(Properties.Resources.E_UnsupportedDepth_1bpp); } Image dst = new Image( this.Width, (this.Height + 3) / 4, this.BitsPerPixel, this.HorizontalResolution, this.VerticalResolution / 4); int stride = this.Stride; ulong[] bitssrc = this.Bits; ulong[] bitsdst = dst.Bits; int offsrc = 0; int offdst = 0; for (int i = 0, ii = this.Height / 4; i < ii; i++, offsrc += 4 * stride, offdst += stride) { Vectors.Or(stride, bitssrc, offsrc, bitssrc, offsrc + stride, bitssrc, offsrc + (2 * stride), bitssrc, offsrc + (3 * stride), bitsdst, offdst); } switch (this.Height % 4) { case 1: Vectors.Copy(stride, bitssrc, offsrc, bitsdst, offdst); break; case 2: Vectors.Or(stride, bitssrc, offsrc, bitssrc, offsrc + stride, bitsdst, offdst); break; case 3: Vectors.Or(stride, bitssrc, offsrc, bitssrc, offsrc + stride, bitssrc, offsrc + (2 * stride), bitsdst, offdst); break; } dst.AppendTransform(new MatrixTransform(1.0, 0.25)); return(dst); }
/// <summary> /// Crops the <see cref="Image"/> using rectangle calculated by <see cref="Image.BlackArea"/> method. /// </summary> /// <param name="dx">The amount by which to expand or shrink the left and right sides of the image black area.</param> /// <param name="dy">The amount by which to expand or shrink the top and bottom sides of the image black area.</param> /// <returns> /// A new cropped <see cref="Image"/>. /// </returns> /// <exception cref="NotSupportedException"> /// <para> /// The <see cref="Image{T}.BitsPerPixel"/> is not 1. /// </para> /// </exception> public Image CropBlackArea(int dx, int dy) { // determine black area of the image Rectangle blackArea = this.BlackArea(); if (dx == 0 && dy == 0) { // no frame - simply crop the black area return(this.Crop(blackArea)); } // expand target area Rectangle bounds = Rectangle.Inflate(blackArea, dx, dy); Image dst = new Image(bounds.Size, this); if (!blackArea.IsEmpty) { Rectangle srcarea = Rectangle.Intersect(bounds, blackArea); Rectangle dstarea = Rectangle.Offset(srcarea, -bounds.X, -bounds.Y); Image.CopyArea(dst, dstarea.X, dstarea.Y, srcarea.Width, srcarea.Height, this, srcarea.X, srcarea.Y); if (this.BitsPerPixel > 1) { // set frame to white dst.SetWhiteBorder(dstarea); } } else { if (this.BitsPerPixel > 1) { // set all image to white Vectors.Set(dst.Bits.Length, ulong.MaxValue, dst.Bits, 0); } } dst.AppendTransform(new MatrixTransform(-bounds.X, -bounds.Y)); return(dst); }
public Image Affine(Image dst, System.Windows.Media.Matrix matrix, BorderType borderType, uint borderValue) { const float Eps = 1e-8f; if (matrix.IsIdentity) { return(this.Copy(dst, true)); } // IPP does not support 1bpp images - convert to 8bpp Image src; bool convert1bpp = false; if (this.BitsPerPixel == 1) { src = this.Convert1To8(null); borderValue = borderValue != 0 ? 0u : 255u; convert1bpp = true; } else { src = this; } // calculate new image size and position PointD tr = TransformPoint(src.Width, 0); PointD br = TransformPoint(src.Width, src.Height); PointD bl = TransformPoint(0, src.Height); double x1dst = Core.MinMax.Min(bl.X, tr.X, br.X, 0.0); double x2dst = Core.MinMax.Max(bl.X, tr.X, br.X, 0.0); double y1dst = Core.MinMax.Min(bl.Y, tr.Y, br.Y, 0.0); double y2dst = Core.MinMax.Max(bl.Y, tr.Y, br.Y, 0.0); // translate matrix so the transformed image fits into new frame matrix.OffsetX = -Core.MinMax.Min(x1dst, x2dst); matrix.OffsetY = -Core.MinMax.Min(y1dst, y2dst); // note: add epsilon to avoid rounding problems int widthdst = (int)Math.Floor(x2dst - x1dst + Eps); int heightdst = (int)Math.Floor(y2dst - y1dst + Eps); bool inplace = dst == this; dst = src.CreateTemplate(dst, widthdst, heightdst, src.BitsPerPixel); IPP.Execute(() => { return(NativeMethods.affine( src.BitsPerPixel, src.Width, src.Height, src.Stride, src.Bits, dst.Width, dst.Height, dst.Stride, dst.Bits, matrix.M11, matrix.M12, matrix.OffsetX, matrix.M21, matrix.M22, matrix.OffsetY, (int)borderType, borderValue)); }); dst.AppendTransform(new MatrixTransform(matrix)); // convert back to 1bpp if (convert1bpp) { dst.Convert8To1(dst, 1); /*using (Pix pixs = transformedImage.CreatePix()) * { * using (Pix pixd = pixs.pixOtsu(false)) * { * if (pixd != null) * { * return pixd.CreateImage(transformedImage.HorizontalResolution, transformedImage.VerticalResolution); * } * } * }*/ } if (inplace) { this.Attach(dst); return(this); } return(dst); PointD TransformPoint(int ptx, int pty) { return(new PointD( (matrix.M11 * ptx) + (matrix.M12 * pty) + matrix.OffsetX, (matrix.M21 * ptx) + (matrix.M22 * pty) + matrix.OffsetY)); } }
/// <summary> /// Reduces the size and resolution of this <see cref="Image"/> by a factor of 4 by downsampling. /// </summary> /// <param name="dst">The destination <see cref="Image"/>. Can be <b>null</b>.</param> /// <returns> /// The destination <see cref="Image"/>. /// </returns> /// <remarks> /// <para>If <paramref name="dst"/> is <b>null</b> the method creates new destination <see cref="Image"/> with dimensions of this <see cref="Image"/>.</para> /// <para>If <paramref name="dst"/> equals this <see cref="Image"/>, the operation is performed in-place.</para> /// <para>Conversely, the <paramref name="dst"/> is reallocated to the dimensions of this <see cref="Image"/>.</para> /// </remarks> public Image ScaleByDownsampling4(Image dst) { int width = this.Width; int height = this.Height; int bitsPerPixel = this.BitsPerPixel; int dstwidth = width / 4; int dstheight = height / 4; bool inplace = dst == this; dst = this.CreateTemplate(dst, dstwidth, dstheight, bitsPerPixel); switch (bitsPerPixel) { case 1: unsafe { fixed(ulong *bitssrc = this.Bits, bitsdst = dst.Bits) { ulong * ptrsrc = (ulong *)bitssrc; ushort *ptrdst = (ushort *)bitsdst; int stridesrc = this.Stride; int stride16dst = dst.Stride * 4; for (int ydst = 0; ydst < dstheight; ydst++, ptrsrc += 4 * stridesrc, ptrdst += stride16dst) { for (int x = 0, xdst = 0, xsrc = 0; x < dstwidth; x += 16, xdst++, xsrc++) { ulong bits = ptrsrc[xsrc]; ptrdst[xdst] = (ushort)( (bits & 0x0001) | ((bits >> 3) & 0x0002) | ((bits >> 6) & 0x0004) | ((bits >> 9) & 0x0008) | ((bits >> 12) & 0x0010) | ((bits >> 15) & 0x0020) | ((bits >> 18) & 0x0040) | ((bits >> 21) & 0x0080) | ((bits >> 24) & 0x0100) | ((bits >> 27) & 0x0200) | ((bits >> 30) & 0x0400) | ((bits >> 33) & 0x0800) | ((bits >> 36) & 0x1000) | ((bits >> 39) & 0x2000) | ((bits >> 42) & 0x4000) | ((bits >> 45) & 0x8000)); } } } } break; case 2: unsafe { fixed(ulong *bitssrc = this.Bits, bitsdst = dst.Bits) { ulong * ptrsrc = (ulong *)bitssrc; ushort *ptrdst = (ushort *)bitsdst; int stridesrc = this.Stride; int stride16dst = dst.Stride * 4; for (int ydst = 0; ydst < dstheight; ydst++, ptrsrc += 4 * stridesrc, ptrdst += stride16dst) { for (int x = 0, xdst = 0, xsrc = 0; x < dstwidth; x += 8, xdst++, xsrc++) { ulong bits = ptrsrc[xsrc]; ptrdst[xdst] = (ushort)( (bits & 0x0003) | ((bits >> 6) & 0x000c) | ((bits >> 12) & 0x0030) | ((bits >> 18) & 0x00c0) | ((bits >> 24) & 0x0300) | ((bits >> 30) & 0x0c00) | ((bits >> 36) & 0x3000) | ((bits >> 42) & 0xc000)); } } } } break; case 4: unsafe { fixed(ulong *bitssrc = this.Bits, bitsdst = dst.Bits) { ulong * ptrsrc = (ulong *)bitssrc; ushort *ptrdst = (ushort *)bitsdst; int stridesrc = this.Stride; int stride16dst = dst.Stride * 4; for (int ydst = 0; ydst < dstheight; ydst++, ptrsrc += 4 * stridesrc, ptrdst += stride16dst) { for (int x = 0, xdst = 0, xsrc = 0; x < dstwidth; x += 4, xdst++, xsrc++) { ulong bits = ptrsrc[xsrc]; ptrdst[xdst] = (ushort)( (bits & 0x000f) | ((bits >> 12) & 0x00f0) | ((bits >> 24) & 0x0f00) | ((bits >> 36) & 0xf000)); } } } } break; case 8: unsafe { fixed(ulong *bitssrc = this.Bits, bitsdst = dst.Bits) { byte *ptrsrc = (byte *)bitssrc; byte *ptrdst = (byte *)bitsdst; int stride8src = this.Stride8; int stride8dst = dst.Stride8; for (int ydst = 0; ydst < dstheight; ydst++, ptrsrc += 4 * stride8src, ptrdst += stride8dst) { for (int xdst = 0, xsrc = 0; xdst < dstwidth; xdst++, xsrc += 4) { ptrdst[xdst] = ptrsrc[xsrc]; } } } } break; case 16: unsafe { fixed(ulong *bitssrc = this.Bits, bitsdst = dst.Bits) { ushort *ptrsrc = (ushort *)bitssrc; ushort *ptrdst = (ushort *)bitsdst; int stride16src = this.Stride * 4; int stride16dst = dst.Stride * 4; for (int ydst = 0; ydst < dstheight; ydst++, ptrsrc += 4 * stride16src, ptrdst += stride16dst) { for (int xdst = 0, xsrc = 0; xdst < dstwidth; xdst++, xsrc += 4) { ptrdst[xdst] = ptrsrc[xsrc]; } } } } break; case 24: unsafe { fixed(ulong *bitssrc = this.Bits, bitsdst = dst.Bits) { byte *ptrsrc = (byte *)bitssrc; byte *ptrdst = (byte *)bitsdst; int stride8src = this.Stride8; int stride8dst = dst.Stride8; for (int ydst = 0; ydst < dstheight; ydst++, ptrsrc += 4 * stride8src, ptrdst += stride8dst) { for (int xdst = 0, xsrc = 0; xdst < 3 * dstwidth; xdst += 3, xsrc += 4 * 3) { ptrdst[xdst + 0] = ptrsrc[xsrc + 0]; ptrdst[xdst + 1] = ptrsrc[xsrc + 1]; ptrdst[xdst + 2] = ptrsrc[xsrc + 2]; } } } } break; case 32: unsafe { fixed(ulong *bitssrc = this.Bits, bitsdst = dst.Bits) { uint *ptrsrc = (uint *)bitssrc; uint *ptrdst = (uint *)bitsdst; int stride32src = this.Stride * 2; int stride32dst = dst.Stride * 2; for (int ydst = 0; ydst < dstheight; ydst++, ptrsrc += 4 * stride32src, ptrdst += stride32dst) { for (int xdst = 0, xsrc = 0; xdst < dstwidth; xdst++, xsrc += 4) { ptrdst[xdst] = ptrsrc[xsrc]; } } } } break; default: throw new NotSupportedException(string.Format( CultureInfo.InvariantCulture, Properties.Resources.E_UnsupportedDepth, bitsPerPixel)); } dst.SetResolution(this.HorizontalResolution / 4, this.VerticalResolution / 4); dst.AppendTransform(new MatrixTransform(0.25, 0.25)); if (inplace) { this.Attach(dst); return(this); } return(dst); }
/// <summary> /// Scales the <see cref="Image"/> vertically and horizontally without changing its resolution. /// </summary> /// <param name="dst">The destination <see cref="Image"/>. Can be <b>null</b>.</param> /// <param name="width">The desired width of the image, in pixels.</param> /// <param name="height">The desired height of the image, in pixels.</param> /// <param name="options">The scaling options.</param> /// <returns> /// The destination <see cref="Image"/>. /// </returns> public Image ScaleToSize(Image dst, int width, int height, ScalingOptions options) { if (width == this.Width && height == this.Height) { return(this.Copy(dst, true)); } if (width <= 0) { throw new ArgumentException(Properties.Resources.E_InvalidWidth, nameof(width)); } if (height <= 0) { throw new ArgumentException(Properties.Resources.E_InvalidHeight, nameof(height)); } System.Windows.Media.Matrix matrix = System.Windows.Media.Matrix.Identity; matrix.Scale((double)width / this.Width, (double)height / this.Height); #if false dst = this.Affine(dst, matrix, BorderType.BorderConst, this.WhiteColor); Debug.Assert(width == dst.Width && height == dst.Height, "Image dimensions are wrong."); return(dst); #else // IPP does not support 1bpp images - convert to 8bpp Image src; bool convert1bpp = false; if (this.BitsPerPixel == 1) { src = this.Convert1To8(null); convert1bpp = true; } else { src = this; } bool inplace = dst == this; dst = src.CreateTemplate(dst, width, height, src.BitsPerPixel); IPP.Execute(() => { return(NativeMethods.resize( src.BitsPerPixel, src.Width, src.Height, src.Bits, src.Stride8, dst.Width, dst.Height, dst.Bits, dst.Stride8, options.InterpolationType, options.Antialiasing, options.ValueB, options.ValueC, options.Lobes, BorderType.BorderConst, src.WhiteColor)); }); dst.AppendTransform(new MatrixTransform(matrix)); // convert back to 1bpp if (convert1bpp) { dst.Convert8To1(dst, 1); } if (inplace) { this.Attach(dst); return(this); } return(dst); #endif }