/// <summary> /// Constructor which takes .NET array /// dtype and shape is determined from array /// </summary> /// <param name="values"></param> /// <param name="shape"></param> /// <param name="order"></param> /// <returns>Array with values</returns> /// <remarks>This constructor calls <see cref="IStorage.Allocate(NumSharp.Shape,System.Type)"/></remarks> public NDArray(IArraySlice values, Shape shape = default, char order = 'C') : this(values.TypeCode) { if (order != 'C') shape.ChangeTensorLayout(order); if (shape.IsEmpty) shape = Shape.Vector((int) values.Count); //TODO! when long index, remove cast int Storage.Allocate(values, shape); }
/// <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> /// Constructor which takes .NET array /// dtype and shape is determined from array /// </summary> /// <param name="values"></param> /// <param name="shape"></param> /// <param name="order"></param> /// <returns>Array with values</returns> /// <remarks>This constructor calls <see cref="IStorage.Allocate(NumSharp.Shape,System.Type)"/></remarks> public NDArray(IArraySlice values, Shape shape = default, char order = 'C') : this(values.TypeCode) { if (order != 'C') shape.ChangeTensorLayout(order); if (shape.IsEmpty) shape = Shape.Vector(values.Count); Storage.Allocate(values, shape); }
/// <summary> /// Return evenly spaced values within a given interval. /// /// Values are generated within the half-open interval [start, stop) /// (in other words, the interval including start but excluding stop). /// For integer arguments the function is equivalent to the Python built-in /// range function, but returns an ndarray rather than a list. /// /// When using a non-integer step, such as 0.1, the results will often not /// be consistent. It is better to use numpy.linspace for these cases. /// </summary> /// <param name="start"> /// Start of interval. The interval includes this value. The default /// start value is 0. /// </param> /// <param name="stop"> /// End of interval. The interval does not include this value, except /// in some cases where step is not an integer and floating point /// round-off affects the length of out. /// </param> /// <param name="step"> /// Spacing between values. For any output out, this is the distance /// between two adjacent values, out[i+1] - out[i]. The default /// step size is 1. If step is specified as a position argument, /// start must also be given. /// </param> /// <returns> /// Array of evenly spaced values. /// /// For floating point arguments, the length of the result is /// ceil((stop - start)/step). Because of floating point overflow, /// this rule may result in the last element of out being greater /// than stop. /// </returns> public static NDArray arange(float start, float stop, float step = 1) { if (Math.Abs(step) < 0.000001) { throw new ArgumentException("step can't be 0", nameof(step)); } bool negativeStep = false; if (step < 0) { negativeStep = true; step = Math.Abs(step); //swap var tmp = start; start = stop; stop = tmp; } if (start > stop) { throw new Exception("parameters invalid, start is greater than stop."); } int length = (int)Math.Ceiling((stop - start + 0.0d) / step); var nd = new NDArray(typeof(float), Shape.Vector(length), false); //do not fill, we are about to if (negativeStep) { step = Math.Abs(step); unsafe { var addr = (float *)nd.Array.Address; for (int add = length - 1, i = 0; add >= 0; add--, i++) { addr[i] = 1 + start + add * step; } } } else { unsafe { var addr = (float *)nd.Array.Address; for (int i = 0; i < length; i++) { addr[i] = start + i * step; } } } 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 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))); }
/// <summary> /// Creates a Vector <see cref="NDArray"/> from given <paramref name="data"/>. /// </summary> /// <typeparam name="T">The type of given array, must be compliant to numpy's supported dtypes.</typeparam> /// <param name="data">The enumeration of data to create <see cref="NDArray"/> from.</param> /// <returns>An <see cref="NDArray"/> with the data and shape of the given array.</returns> /// <remarks> /// https://docs.scipy.org/doc/numpy/reference/generated/numpy.array.html <br></br> /// Always performs a copy.<br></br> /// <paramref name="size"/> can be used to limit the amount of items to read form <paramref name="data"/>. Reading stops on either size or <paramref name="data"/> ends. /// </remarks> public static NDArray array <T>(IEnumerable <T> data, int size) where T : unmanaged { var slice = new ArraySlice <T>(new UnmanagedMemoryBlock <T>(size)); unsafe { using (var enumerator = data.GetEnumerator()) { Func <bool> next = enumerator.MoveNext; var addr = slice.Address; for (int i = 0; i < size && next(); i++) { addr[i] = enumerator.Current; } } } return(new NDArray(slice, Shape.Vector(slice.Count))); }
/// <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)); } }
/// <summary> /// Constructor which initialize elements with length of <paramref name="size"/> /// </summary> /// <param name="dtype">Internal data type</param> /// <param name="size">The size as a single dimension shape</param> /// <param name="fillZeros">Should set the values of the new allocation to default(dtype)? otherwise - old memory noise</param> /// <remarks>This constructor calls <see cref="IStorage.Allocate(NumSharp.Shape,System.Type)"/></remarks> public NDArray(NPTypeCode dtype, int size, bool fillZeros) : this(dtype, Shape.Vector(size), true) { }
/// <summary> /// Constructor which initialize elements with length of <paramref name="size"/> /// </summary> /// <param name="dtype">Internal data type</param> /// <param name="size">The size as a single dimension shape</param> /// <remarks>This constructor calls <see cref="IStorage.Allocate(NumSharp.Shape,System.Type)"/></remarks> public NDArray(NPTypeCode dtype, int size) : this(dtype, Shape.Vector(size), true) { }
/// <summary> /// Constructor which initialize elements with length of <paramref name="size"/> /// </summary> /// <param name="dtype">Internal data type</param> /// <param name="size">The size as a single dimension shape</param> /// <param name="fillZeros">Should set the values of the new allocation to default(dtype)? otherwise - old memory noise</param> /// <remarks>This constructor calls <see cref="IStorage.Allocate(NumSharp.Shape,System.Type)"/></remarks> public NDArray(Type dtype, int size, bool fillZeros) : this(dtype, Shape.Vector(size), fillZeros) { }
public static NDArray array <T>(params T[] data) where T : unmanaged => new NDArray(ArraySlice.FromArray(data), Shape.Vector(data.Length));
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> /// 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> /// Creates a Vector <see cref="NDArray"/> from given <paramref name="data"/>. /// </summary> /// <typeparam name="T">The type of given array, must be compliant to numpy's supported dtypes.</typeparam> /// <param name="data">The enumeration of data to create <see cref="NDArray"/> from.</param> /// <returns>An <see cref="NDArray"/> with the data and shape of the given array.</returns> /// <remarks>https://docs.scipy.org/doc/numpy/reference/generated/numpy.array.html <br></br>Always performs a copy.</remarks> public static NDArray array <T>(IEnumerable <T> data) where T : unmanaged { var slice = ArraySlice.FromArray(data.ToArray(), false); return(new NDArray(slice, Shape.Vector(slice.Count))); }