/// <summary> /// Returns a Tensor constructed from the data in the Bitmap. The Tensor's dimensions are /// ordered CHW (channel x height x width). The color channel dimension is in the same order /// as the original Bitmap data. For 24bit bitmaps, this will be BGR. For 32bit bitmaps this /// will be BGRA. /// </summary> /// <param name="bitmap"></param> /// <param name="allocator"></param> /// <returns></returns> public static Tensor ToTensor(this Bitmap bitmap, IAllocator allocator) { Cpu.CpuAllocator cpuAllocator = new Cpu.CpuAllocator(); int bytesPerPixel = 0; if (bitmap.PixelFormat == PixelFormat.Format24bppRgb) { bytesPerPixel = 3; } else if (bitmap.PixelFormat == PixelFormat.Format32bppArgb || bitmap.PixelFormat == PixelFormat.Format32bppPArgb || bitmap.PixelFormat == PixelFormat.Format32bppRgb) { bytesPerPixel = 4; } else { throw new InvalidOperationException("Bitmap must be 24bit or 32bit"); } BitmapData lockData = bitmap.LockBits( new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat); try { long[] sizes = new long[] { bitmap.Height, bitmap.Width, bytesPerPixel }; long[] strides = new long[] { lockData.Stride, bytesPerPixel, 1 }; using (Tensor cpuByteTensor = new Tensor(cpuAllocator, DType.UInt8, sizes, strides)) { cpuByteTensor.Storage.CopyToStorage(cpuByteTensor.StorageOffset, lockData.Scan0, cpuByteTensor.Storage.ByteLength); using (Tensor permutedTensor = cpuByteTensor.Permute(2, 0, 1)) { using (Tensor cpuFloatTensor = new Tensor(cpuAllocator, DType.Float32, permutedTensor.Sizes)) { Ops.Copy(cpuFloatTensor, permutedTensor); // TODO this could be made more efficient by skipping a the following copy if allocator is a CpuAllocator, // but make sure that in that case the result tensor is not disposed before returning. Tensor result = new Tensor(allocator, DType.Float32, permutedTensor.Sizes); Ops.Copy(result, cpuFloatTensor); Ops.Div(result, result, 255); return(result); } } } } finally { bitmap.UnlockBits(lockData); } }
public static void FillOneHot(Tensor result, int labelCount, int[] labels) { if (result.Storage is Cpu.CpuStorage) { DoFillOneHot(result, labelCount, labels); } else { //If the result is not on the CPU, it is much faster to build the tensor on the CPU and then copy //An alternative to this would be building a specific GPU kernel for this operation var cpuAlloc = new Cpu.CpuAllocator(); using (var cpuResult = new Tensor(cpuAlloc, result.ElementType, result.Sizes)) { DoFillOneHot(cpuResult, labelCount, labels); Ops.Copy(result, cpuResult); } } }
/// <summary> /// Converts a Tensor to a Bitmap. Elements of the tensor are assumed to be normalized in the range [0, 1] /// The tensor must have one of the following structures: /// * 2D tensor - output is a 24bit BGR bitmap in greyscale /// * 3D tensor where first dimension has length 1 - output is 24bit BGR bitmap in greyscale /// * 3D tensor where first dimension has length 3 - output is 24bit BGR bitmap /// * 3D tensor where first dimension has length 4 - output is 32bit BGRA bitmap /// /// 2D tensors must be in HW (height x width) order; /// 3D tensors must be in CHW (channel x height x width) order. /// </summary> /// <param name="tensor"></param> /// <returns></returns> public static Bitmap ToBitmap(this Tensor tensor) { if (tensor.DimensionCount != 2 && tensor.DimensionCount != 3) { throw new InvalidOperationException("tensor must have 2 or 3 dimensions"); } if (tensor.DimensionCount == 3 && (tensor.Sizes[0] != 1 && tensor.Sizes[0] != 3 && tensor.Sizes[0] != 4)) { throw new InvalidOperationException("3D tensor's first dimension (color channels) must be of length 1, 3 or 4"); } Tensor src; if (tensor.DimensionCount == 2) { src = tensor.RepeatTensor(3, 1, 1); } else if (tensor.DimensionCount == 3 && tensor.Sizes[0] == 1) { src = tensor.RepeatTensor(3, 1, 1); } else { src = tensor.CopyRef(); } Cpu.CpuAllocator cpuAllocator = new Cpu.CpuAllocator(); long bytesPerPixel = src.Sizes[0]; try { using (Tensor cpuFloatTensor = new Tensor(cpuAllocator, DType.Float32, src.Sizes)) using (Tensor permutedFloatTensor = cpuFloatTensor.Permute(1, 2, 0)) { Ops.Copy(cpuFloatTensor, src); Ops.Mul(cpuFloatTensor, cpuFloatTensor, 255); PixelFormat resultFormat = bytesPerPixel == 3 ? PixelFormat.Format24bppRgb : PixelFormat.Format32bppArgb; Bitmap result = new Bitmap((int)src.Sizes[2], (int)src.Sizes[1], resultFormat); BitmapData lockData = result.LockBits( new Rectangle(0, 0, result.Width, result.Height), ImageLockMode.WriteOnly, result.PixelFormat); long[] sizes = new long[] { result.Height, result.Width, bytesPerPixel }; long[] strides = new long[] { lockData.Stride, bytesPerPixel, 1 }; Tensor resultTensor = new Tensor(cpuAllocator, DType.UInt8, sizes, strides); // Re-order tensor and convert to bytes Ops.Copy(resultTensor, permutedFloatTensor); int byteLength = lockData.Stride * lockData.Height; resultTensor.Storage.CopyFromStorage(lockData.Scan0, resultTensor.StorageOffset, byteLength); result.UnlockBits(lockData); return(result); } } finally { src.Dispose(); } }