/// <summary> /// Wraps given <see cref="BitmapData"/> as n <see cref="NDArray"/> without performing copy. /// </summary> /// <param name="bmpData">Targetted bitmap data with reading capabilities.</param> /// <param name="flat"> /// If true, returns NDArray be 1-d of pixels: R1G1B1R2G2B2 ... RnGnBn where n is the amount of pixels in the image.<br></br> /// If false, returns a 4-d NDArray shaped: (1, bmpData.Height, bmpData.Width, bbp) /// </param> /// <param name="discardAlpha">If the given <see cref="Bitmap"/> has an alpha pixel (transparency pixel), return a slice without the alpha.</param> /// <returns>An NDArray that wraps the given bitmap, doesn't copy</returns> /// <remarks>If the BitmapData is unlocked via <see cref="Bitmap.UnlockBits"/> - the NDArray will point to an invalid address which will cause heap corruption. Use with caution!</remarks> public static unsafe NDArray AsNDArray(this BitmapData bmpData, bool flat = false, bool discardAlpha = false) { if (bmpData == null) { throw new ArgumentNullException(nameof(bmpData)); } var ret = new NDArray(new ArraySlice <byte>(new UnmanagedMemoryBlock <byte>((byte *)bmpData.Scan0, bmpData.Stride * bmpData.Height))); if (flat) { if (ret.shape[3] == 4 && discardAlpha) { return(ret.reshape(1, bmpData.Height, bmpData.Width, bmpData.Stride / bmpData.Width) //reshape [Slice.All, Slice.All, Slice.All, new Slice(stop: 3)] //slice .flat); //flatten } return(ret); } else { ret = ret.reshape(1, bmpData.Height, bmpData.Width, bmpData.Stride / bmpData.Width); //reshape if (ret.shape[3] == 4 && discardAlpha) { ret = ret[Slice.All, Slice.All, Slice.All, new Slice(stop: 3)]; //slice } return(ret); } }
/// <summary> /// Return coordinate matrices from coordinate vectors. /// Make N-D coordinate arrays for vectorized evaluations of /// N-D scalar/vector fields over N-D grids, given /// one-dimensional coordinate arrays x1, x2,..., xn. /// .. versionchanged:: 1.9 /// 1-D and 0-D cases are allowed. /// </summary> /// <param name="x1"> 1-D arrays representing the coordinates of a grid</param> /// /// <param name="x2"> 1-D arrays representing the coordinates of a grid</param> /// <returns></returns> public static (NDArray, NDArray) meshgrid(NDArray x1, NDArray x2, Kwargs kwargs = null) { if (kwargs == null) { kwargs = new Kwargs(); } int ndim = 2; var s0 = (1, 1); var output = new NDArray[] { x1.reshape(x1.size, 1), x2.reshape(1, x2.size) }; if (kwargs.indexing == "xy" && ndim > 1) { // Switch first and second axis output = new NDArray[] { x1.reshape(1, x1.size), x2.reshape(x2.size, 1) }; } if (!kwargs.sparse) { // Return the full N-D matrix(not only the 1 - D vector) output = np.broadcast_arrays(output[0], output[1], true); } if (kwargs.copy) { } return(output[0], output[1]); }
public static NDArray zeros <T>(params int[] shape) { var nd = new NDArray(typeof(T)); nd.reshape(shape); return(nd); }
/// <summary> /// Return a contiguous flattened array. A 1-D array, containing the elements of the input, is returned /// </summary> /// <remarks>https://docs.scipy.org/doc/numpy/reference/generated/numpy.ravel.html</remarks> /// <param name="a">Input array. The elements in a are read in the order specified by order, and packed as a 1-D array.</param> /// <remarks><br></br>If this array's <see cref="Shape"/> is a sliced or broadcasted, the a copy will be made.</remarks> public static NDArray ravel(NDArray a) { // ReSharper disable once ConvertIfStatementToReturnStatement if (!a.Shape.IsContiguous) { return(new NDArray(new UnmanagedStorage(a.Storage.CloneData(), Shape.Vector(a.size)))); } return(a.reshape(Shape.Vector(a.size))); }
/// <summary> /// Wraps given <see cref="BitmapData"/> as n <see cref="NDArray"/> without performing copy. /// </summary> /// <param name="bmpData">Targetted bitmap data with reading capabilities.</param> /// <param name="flat"> /// If true, returns NDArray be 1-d of pixels: R1G1B1R2G2B2 ... RnGnBn where n is the amount of pixels in the image.<br></br> /// If false, returns a 4-d NDArray shaped: (1, bmpData.Height, bmpData.Width, 3) /// </param> /// <returns>An NDArray that wraps the given bitmap, doesn't copy</returns> /// <remarks>If the BitmapData is unlocked via <see cref="Bitmap.UnlockBits"/> - the NDArray will point to an invalid address which will cause heap corruption. Use with caution!</remarks> public static unsafe NDArray AsNDArray(this BitmapData bmpData, bool flat = false) { if (bmpData == null) { throw new ArgumentNullException(nameof(bmpData)); } var nd = new NDArray(new ArraySlice <byte>(new UnmanagedMemoryBlock <byte>((byte *)bmpData.Scan0, bmpData.Stride * bmpData.Height))); return(flat ? nd : nd.reshape(1, bmpData.Height, bmpData.Width, 3)); }
/// <summary> /// Return a contiguous flattened array. A 1-D array, containing the elements of the input, is returned /// </summary> /// <remarks>https://docs.scipy.org/doc/numpy/reference/generated/numpy.ravel.html</remarks> /// <param name="a">Input array. The elements in a are read in the order specified by order, and packed as a 1-D array.</param> /// <remarks><br></br>If this array's <see cref="Shape"/> is a slice, the a copy will be made.</remarks> public static NDArray ravel(NDArray a) { //TODO! when slice reshaping is done, prevent this cloning. // ReSharper disable once ConvertIfStatementToReturnStatement if (a.Shape.IsSliced) { return(new NDArray(new UnmanagedStorage(a.Storage.CloneData(), Shape.Vector(a.size)))); } return(a.reshape(Shape.Vector(a.size))); }
public static NDArray expand_dims(NDArray a, int axis) { //test if the ndarray is empty. if (a.size == 0) { return(a); } var shape = np.array(a.shape); var left = shape[$":{axis}"].Array.Cast <int>(); var right = shape[$"{axis}:"].Array.Cast <int>(); return(a.reshape(_combineEnumerables(left, __one, right).ToArray())); }
/// <summary> /// Convert inputs to arrays with at least one dimension. /// Scalar inputs are converted to 1-dimensional arrays, whilst higher-dimensional inputs are preserved. /// </summary> /// <returns>An array, or list of arrays, each with a.ndim >= 1. Copies are made only if necessary.</returns> /// <remarks>https://docs.scipy.org/doc/numpy/reference/generated/numpy.atleast_1d.html</remarks> public static NDArray atleast_1d(NDArray arr) { if (arr == null) { throw new ArgumentNullException(nameof(arr)); } switch (arr.ndim) { case 0: return(arr.reshape(1)); default: return(arr); } }
/// <summary> /// Remove single-dimensional entries from the shape of an array. /// </summary> /// <param name="a">Input data.</param> /// <param name="axis">Selects a subset of the single-dimensional entries in the shape. If an axis is selected with shape entry greater than one, an error is raised.</param> /// <returns>The input array, but with all or a subset of the dimensions of length 1 removed. This is always a itself or a view into a.</returns> /// <remarks>https://docs.scipy.org/doc/numpy/reference/generated/numpy.squeeze.html</remarks> /// <exception cref="IncorrectShapeException">If axis is not None, and an axis being squeezed is not of length 1</exception> public static NDArray squeeze(NDArray a, int axis) { while (axis < 0) { axis = a.ndim + axis; //handle negative axis } if (axis >= a.ndim) { throw new ArgumentOutOfRangeException(nameof(axis)); } if (a.shape[axis] != 1) { throw new IncorrectShapeException($"Unable to squeeze axis {axis} because it is of length {a.shape[axis]} and not 1."); } return(a.reshape(squeeze_fast(a.Shape, axis))); }
public static NDArray load(Stream stream) { using (var reader = new BinaryReader(stream, System.Text.Encoding.ASCII #if !NET35 && !NET40 , leaveOpen: true #endif )) { int bytes; Type type; int[] shape; if (!parseReader(reader, out bytes, out type, out shape)) { throw new FormatException(); } Array array = Arrays.Create(type, shape.Aggregate((dims, dim) => dims * dim)); var result = new NDArray(readValueMatrix(reader, array, bytes, type, shape)); return(result.reshape(shape)); } }
/// <summary> /// View inputs as arrays with at least three dimensions. /// </summary> /// <returns>An array, or list of arrays, each with a.ndim >= 3. Copies are avoided where possible, and views with three or more dimensions are returned. For example, a 1-D array of shape (N,) becomes a view of shape (1, N, 1), and a 2-D array of shape (M, N) becomes a view of shape (M, N, 1).</returns> /// <remarks>https://docs.scipy.org/doc/numpy/reference/generated/numpy.atleast_3d.html</remarks> public static NDArray atleast_3d(NDArray arr) { if (arr == null) { throw new ArgumentNullException(nameof(arr)); } switch (arr.ndim) { case 0: return(arr.reshape(1, 1, 1)); case 1: return(np.expand_dims(np.expand_dims(arr, 1), 0)); case 2: return(np.expand_dims(arr, 2)); default: return(arr); } }
/// <summary> /// Creates <see cref="NDArray"/> from given <see cref="Bitmap"/>. /// </summary> /// <param name="image">The image to load data from.</param> /// <param name="flat"> /// If true, returns NDArray be 1-d of pixels: `R1G1B1R2G2B2 ... RnGnBn` where n is the amount of pixels in the image.<br></br> /// If false, returns a 4-d NDArray shaped: (1, bmpData.Height, bmpData.Width, 3) /// </param> /// <param name="copy"> /// If true, performs <see cref="Bitmap.LockBits(System.Drawing.Rectangle,System.Drawing.Imaging.ImageLockMode,System.Drawing.Imaging.PixelFormat)"/> /// and then copies the data to a new <see cref="NDArray"/> then finally releasing the locked bits.<br></br> /// If false, It'll call <see cref="Bitmap.LockBits(System.Drawing.Rectangle,System.Drawing.Imaging.ImageLockMode,System.Drawing.Imaging.PixelFormat)"/> /// , wraps the <see cref="BitmapData.Scan0"/> with an NDArray and call <see cref="Bitmap.UnlockBits"/> only when the NDArray will be collected by the <see cref="GC"/>. /// </param> /// <returns>An NDArray that holds the pixel data of the given bitmap</returns> public static unsafe NDArray ToNDArray(this System.Drawing.Bitmap image, bool flat = false, bool copy = true) { if (image == null) { throw new ArgumentNullException(nameof(image)); } BitmapData bmpData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, image.PixelFormat); if (copy) { try { unsafe { //Create a 1d vector without filling it's values to zero (similar to np.empty) var nd = new NDArray(NPTypeCode.Byte, Shape.Vector(bmpData.Stride * image.Height), fillZeros: false); // Get the respective addresses byte *src = (byte *)bmpData.Scan0; byte *dst = (byte *)nd.Unsafe.Address; //we can use unsafe because we just allocated that array and we know for sure it is contagious. // Copy the RGB values into the array. Buffer.MemoryCopy(src, dst, nd.size, nd.size); //faster than Marshal.Copy return(nd.reshape(1, image.Height, image.Width, 3)); } } finally { image.UnlockBits(bmpData); } } else { var nd = new NDArray(new ArraySlice <byte>(new UnmanagedMemoryBlock <byte>((byte *)bmpData.Scan0, bmpData.Stride * bmpData.Height, () => image.UnlockBits(bmpData)))); return(flat ? nd : nd.reshape(1, bmpData.Height, bmpData.Width, 3)); } }
public static unsafe NDArray ToNDArray(this System.Drawing.Bitmap image, bool flat = false, bool copy = true, bool discardAlpha = false) { if (image == null) { throw new ArgumentNullException(nameof(image)); } BitmapData bmpData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, image.PixelFormat); if (copy) { try { unsafe { //Create a 1d vector without filling it's values to zero (similar to np.empty) var nd = new NDArray(NPTypeCode.Byte, Shape.Vector(bmpData.Stride * image.Height), fillZeros: false); // Get the respective addresses byte *src = (byte *)bmpData.Scan0; byte *dst = (byte *)nd.Unsafe.Address; //we can use unsafe because we just allocated that array and we know for sure it is contagious. // Copy the RGB values into the array. Buffer.MemoryCopy(src, dst, bmpData.Stride * image.Height, nd.size); //faster than Marshal.Copy var ret = nd.reshape(1, image.Height, image.Width, bmpData.Stride / bmpData.Width); if (discardAlpha && ret.shape[3] == 4) { ret = ret[Slice.All, Slice.All, Slice.All, new Slice(stop: 3)]; } return(flat && ret.ndim != 1 ? ret.flat : ret); } } finally { image.UnlockBits(bmpData); } } else { var ret = new NDArray(new ArraySlice <byte>(new UnmanagedMemoryBlock <byte>((byte *)bmpData.Scan0, bmpData.Stride * bmpData.Height, () => image.UnlockBits(bmpData)))); if (flat) { if (discardAlpha) { if (bmpData.Stride / bmpData.Width == 4) //1byte-per-color { return(ret.reshape(1, bmpData.Height, bmpData.Width, bmpData.Stride / bmpData.Width) //reshape [Slice.All, Slice.All, Slice.All, new Slice(stop: 3)] //slice .flat); //flatten } if (bmpData.Stride / bmpData.Width == 8) //2bytes-per-color { return(ret.reshape(1, bmpData.Height, bmpData.Width, bmpData.Stride / bmpData.Width) //reshape [Slice.All, Slice.All, Slice.All, new Slice(stop: 6)] //slice .flat); //flatten } throw new NotSupportedException($"Given bbp ({bmpData.Stride / bmpData.Width}) is not supported."); } return(ret.flat); } else { ret = ret.reshape(1, bmpData.Height, bmpData.Width, bmpData.Stride / bmpData.Width); //reshape if (discardAlpha) { if (ret.shape[3] == 4) //1byte-per-color { ret = ret[Slice.All, Slice.All, Slice.All, new Slice(stop: 3)]; //slice } else if (ret.shape[3] == 8) //2bytes-per-color { ret = ret[Slice.All, Slice.All, Slice.All, new Slice(stop: 6)]; //slice } else { throw new NotSupportedException($"Given bbp ({bmpData.Stride / bmpData.Width}) is not supported."); } } return(ret); } } }
/// <summary> /// Remove single-dimensional entries from the shape of an array. /// </summary> /// <param name="nd"></param> /// <param name="axis"></param> /// <returns></returns> public static NDArray squeeze(NDArray nd, int axis = -1) { return(nd.reshape(nd.shape.Where(x => x > 1).ToArray())); }
/// <summary> /// Gives a new shape to an array without changing its data. /// </summary> /// <param name="nd">Array to be reshaped.</param> /// <param name="shape">The new shape should be compatible with the original shape. </param> /// <returns>original <paramref name="nd"/> reshaped without copying.</returns> /// <remarks>https://docs.scipy.org/doc/numpy-1.16.0/reference/generated/numpy.reshape.html</remarks> public static NDArray reshape(NDArray nd, params int[] shape) { return(nd.reshape(shape)); }
internal static NDArray squeeze_fast(NDArray a, int axis) { return(a.reshape(squeeze_fast(a.Shape, axis))); }
/// <summary> /// Converts <see cref="NDArray"/> to a <see cref="Bitmap"/>. /// </summary> /// <param name="nd">The <see cref="NDArray"/> to copy pixels from, <see cref="Shape"/> is ignored completely. If nd.Unsafe.Shape.IsContiguous == false then a copy is made.</param> /// <param name="width">The height of the <see cref="Bitmap"/></param> /// <param name="height">The width of the <see cref="Bitmap"/></param> /// <param name="format">The format of the expected bitmap, Must be matching to given NDArray otherwise unexpected results might occur.</param> /// <returns>A <see cref="Bitmap"/></returns> /// <exception cref="ArgumentException">When nd.size != width*height, which means the ndarray be turned into the given bitmap size.</exception> public static unsafe Bitmap ToBitmap(this NDArray nd, int width, int height, PixelFormat format = PixelFormat.DontCare) { if (nd == null) { throw new ArgumentNullException(nameof(nd)); } //if flat then initialize based on given format if (nd.ndim == 1 && format != PixelFormat.DontCare) { nd = nd.reshape(1, height, width, format.ToBytesPerPixel()); //theres a check internally for size mismatch. } if (nd.ndim != 4) { throw new ArgumentException("ndarray was expected to be of 4-dimensions, (1, bmpData.Height, bmpData.Width, bytesPerPixel)"); } if (nd.shape[0] != 1) { throw new ArgumentException($"ndarray has more than one picture in it ({nd.shape[0]}) based on the first dimension."); } var bbp = nd.shape[3]; //bytes per pixel. if (bbp != extractFormatNumber()) { throw new ArgumentException($"Given PixelFormat: {format} does not match the number of bytes per pixel in the 4th dimension of given ndarray."); } if (bbp * width * height != nd.size) { throw new ArgumentException($"The expected size does not match the size of given ndarray. (expected: {bbp * width * height}, actual: {nd.size})"); } var ret = new Bitmap(width, height, format); var bitdata = ret.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, format); try { var dst = new ArraySlice <byte>(new UnmanagedMemoryBlock <byte>((byte *)bitdata.Scan0, bitdata.Stride * bitdata.Height)); if (nd.Shape.IsContiguous) { nd.CopyTo(dst); } else { MultiIterator.Assign(new UnmanagedStorage(dst, Shape.Vector(bitdata.Stride * bitdata.Height)), nd.Unsafe.Storage); } } finally { ret.UnlockBits(bitdata); } return(ret); int extractFormatNumber() { if (format == PixelFormat.DontCare) { switch (bbp) { case 3: format = PixelFormat.Format24bppRgb; break; case 4: format = PixelFormat.Format32bppArgb; break; case 6: format = PixelFormat.Format48bppRgb; break; case 8: format = PixelFormat.Format64bppArgb; break; } return(bbp); } return(format.ToBytesPerPixel()); } }
/// <summary> /// Remove single-dimensional entries from the shape of an array. /// </summary> /// <param name="a">Input data.</param> /// <returns>The input array, but with all or a subset of the dimensions of length 1 removed. This is always a itself or a view into a.</returns> /// <remarks>https://docs.scipy.org/doc/numpy/reference/generated/numpy.squeeze.html</remarks> public static NDArray squeeze(NDArray a) { return(a.reshape(a.shape.Where(x => x != 1).ToArray())); }