/// <summary> /// 卷积,外层是乘积法则,内层是卷积 /// </summary> /// <param name="core">卷积核平面</param> /// <param name="mode">卷积模式</param> /// <returns></returns> public Tensor Convolve(Tensor core, ConvolutionMode mode) { if (this.DimensionY != core.DimensionX) { throw new Exception("进行卷积的两个向量外层维度必须符合矩阵乘法规律"); } Tensor newTensor = TensorBuilder.Empty(this.DimensionX, core.DimensionY); //创建一个与后续卷积结果大小相等的0矩阵 Matrix <double> temp = this[0, 0].Convolve(core[0, 0], mode).Map(r => 0.0); for (int i = 0; i <= newTensor.DimensionX - 1; i++) { for (int j = 0; j <= newTensor.DimensionY - 1; j++) { for (int k = 0; k <= this.DimensionY - 1; k++) { temp = temp + this[i, k].Convolve(core[k, j], mode); } newTensor[i, j] = temp; temp = temp.Map(r => 0.0); } } newTensor.OuterAct(r => r.CoerceZero(1e-15)); return(newTensor); }
public static Tensor InvokeConvolutionKernel( ConvolutionMode mode, string name, Tensor input, Tensor filter, Tensor bias ) { var compute = Pix2PixResources.Compute; var kernel = compute.FindKernel(name); uint tgn_x, tgn_y, tgn_z; compute.GetKernelThreadGroupSizes(kernel, out tgn_x, out tgn_y, out tgn_z); var deconv = (mode == ConvolutionMode.Backward); var outHeight = deconv ? input.Shape[0] * 2 : input.Shape[0] / 2; var outWidth = deconv ? input.Shape[1] * 2 : input.Shape[1] / 2; var outChannels = filter.Shape[deconv ? 2 : 3]; Debug.Assert(outHeight % tgn_z == 0); Debug.Assert(outWidth % tgn_y == 0); Debug.Assert(outChannels % tgn_x == 0); var output = new Tensor(new [] { outHeight, outWidth, outChannels }); var buffer_input = new UnityEngine.ComputeBuffer(input.Data.Length, sizeof(float)); var buffer_filter = new UnityEngine.ComputeBuffer(filter.Data.Length, sizeof(float)); var buffer_bias = new UnityEngine.ComputeBuffer(bias.Data.Length, sizeof(float)); var buffer_output = new UnityEngine.ComputeBuffer(output.Data.Length, sizeof(float)); buffer_input.SetData(input.Data); buffer_filter.SetData(filter.Data); buffer_bias.SetData(bias.Data); compute.SetInts("InputShape", input.Shape); compute.SetInts("FilterShape", filter.Shape); compute.SetInts("OutputShape", output.Shape); compute.SetBuffer(kernel, "Input", buffer_input); compute.SetBuffer(kernel, "Filter", buffer_filter); compute.SetBuffer(kernel, "Bias", buffer_bias); compute.SetBuffer(kernel, "Output", buffer_output); compute.Dispatch(kernel, outChannels / (int)tgn_x, outWidth / (int)tgn_y, outHeight / (int)tgn_z ); buffer_output.GetData(output.Data); buffer_input.Dispose(); buffer_filter.Dispose(); buffer_bias.Dispose(); buffer_output.Dispose(); return(output); }
// Internal constructor private ConvolutionInfo( ConvolutionMode mode, int verticalPadding, int horizontalPadding, int verticalStride, int horizontalStride) { VerticalPadding = verticalPadding >= 0 ? verticalPadding : throw new ArgumentOutOfRangeException(nameof(verticalPadding), "The vertical padding must be greater than or equal to 0"); HorizontalPadding = horizontalPadding >= 0 ? horizontalPadding : throw new ArgumentOutOfRangeException(nameof(horizontalPadding), "The horizontal padding must be greater than or equal to 0"); VerticalStride = verticalStride >= 1 ? verticalStride : throw new ArgumentOutOfRangeException(nameof(verticalStride), "The vertical stride must be at least equal to 1"); HorizontalStride = horizontalStride >= 1 ? horizontalStride : throw new ArgumentOutOfRangeException(nameof(horizontalStride), "The horizontal stride must be at least equal to 1"); Mode = mode; }
public static ConvolutionInfoFactory Same( ConvolutionMode mode = ConvolutionMode.Convolution, int verticalStride = 1, int horizontalStride = 1) { return((input, kernels) => { int verticalPadding = (input.Height * verticalStride - input.Height + kernels.X - verticalStride - 1) / 2 + 1, horizontalPadding = (input.Width * horizontalStride - input.Width + kernels.Y - horizontalStride - 1) / 2 + 1; return new ConvolutionInfo(mode, verticalPadding, horizontalPadding, verticalStride, horizontalStride); }); }
public static Tensor InvokeConvolutionKernel( ConvolutionMode mode, string name, Tensor input, Tensor filter, Tensor bias ) { var compute = ComputeAssets.Convolution; var kernel = compute.FindKernel(name); uint tgn_x, tgn_y, tgn_z; compute.GetKernelThreadGroupSizes(kernel, out tgn_x, out tgn_y, out tgn_z); var trans = (mode == ConvolutionMode.Up); var outHeight = trans ? input.Shape[0] * 2 : input.Shape[0] / 2; var outWidth = trans ? input.Shape[1] * 2 : input.Shape[1] / 2; //var outChannels = filter.Shape[trans ? 2 : 3]; var outChannels = filter.Shape[3]; Debug.Assert(filter.Shape[0] == 4); Debug.Assert(filter.Shape[1] == 4); Debug.Assert(outHeight % tgn_z == 0); Debug.Assert(outWidth % tgn_y == 0); //Debug.Assert(outChannels % tgn_x == 0); var output = new Tensor(new [] { outHeight, outWidth, outChannels }); compute.SetInts("InputShape", input.Shape); compute.SetInts("FilterShape", filter.Shape); compute.SetInts("OutputShape", output.Shape); compute.SetInts("InputIndexer", CalculateIndexVector(input.Shape)); compute.SetInts("FilterIndexer", CalculateIndexVector(filter.Shape)); compute.SetInts("OutputIndexer", CalculateIndexVector(output.Shape)); compute.SetBuffer(kernel, "Input", input.Buffer); compute.SetBuffer(kernel, "Filter", filter.Buffer); compute.SetBuffer(kernel, "Bias", bias.Buffer); compute.SetBuffer(kernel, "Output", output.Buffer); if (outChannels == 3) { compute.Dispatch(kernel, 1, outWidth, outHeight); // final convolution } else { compute.Dispatch(kernel, outChannels / (int)tgn_x, outWidth, outHeight); } return(output); }
public static Matrix <double> Convolve(this Matrix <double> a, Matrix <double> b, ConvolutionMode mode) { switch (mode) { case ConvolutionMode.Narrow: { return(a.Convolve(b, 1)); } case ConvolutionMode.Wide: { Matrix <double> result = Matrix <double> .Build.Dense(a.RowCount + b.RowCount - 1, a.ColumnCount + b.ColumnCount - 1); var aCopy = a.Clone(); aCopy = aCopy.Pad(b.RowCount - 1, b.RowCount - 1, b.ColumnCount - 1, b.ColumnCount - 1); for (int i = 0; i <= aCopy.RowCount - b.RowCount; i++) { for (int j = 0; j <= aCopy.ColumnCount - b.ColumnCount; j++) { //先求hadamard积,然后求和 result[i, j] = aCopy.SubMatrix(i, b.RowCount, j, b.ColumnCount).PointwiseMultiply(b).RowSums().Sum(); } } return(result); } case ConvolutionMode.Same: { if (b.RowCount % 2 == 0 || b.ColumnCount % 2 == 0) { throw new Exception("等宽卷积的卷积核大小必须为奇数"); } Matrix <double> result = Matrix <double> .Build.Dense(a.RowCount, a.ColumnCount); var aCopy = a.Clone(); aCopy = aCopy.Pad((b.RowCount - 1) / 2, (b.RowCount - 1) / 2, (b.ColumnCount - 1) / 2, (b.ColumnCount - 1) / 2); for (int i = 0; i <= aCopy.RowCount - b.RowCount; i++) { for (int j = 0; j <= aCopy.ColumnCount - b.ColumnCount; j++) { //先求hadamard积,然后求和 result[i, j] = aCopy.SubMatrix(i, b.RowCount, j, b.ColumnCount).PointwiseMultiply(b).RowSums().Sum(); } } return(result); } default: { throw new Exception("需要从给定类型中选择"); } } }
internal static Bitmap Convolute(this Bitmap bmp, double[,] hkernel, double[,] vkernel, ConvolutionMode mode = ConvolutionMode.RepeatEdges) { int ksize = hkernel.GetLength(0); int h = bmp.Height; int w = bmp.Width; if ((ksize * ksize != hkernel.Length) || (ksize < 3) || (ksize % 2 == 0) || (ksize * ksize != vkernel.Length) || (vkernel.GetLength(0) != ksize)) { throw new ArgumentException("The convolution kernels must be squared with an odd side length and must have an equal size."); } Bitmap dst = new Bitmap(w, h); bmp.Lock(psrc => dst.Lock(pdst => { RGB getsrc(int x, int y) { if ((x >= 0) && (y >= 0) && (x < w) && (y < h)) { return(psrc[y * w + x]); } else if (mode == ConvolutionMode.Zero) { return(new RGB()); } else if (mode == ConvolutionMode.RepeatEdges) { return(getsrc(x < 0 ? 0 : x >= w ? w - 1 : x, y < 0 ? 0 : y >= h ? h - 1 : y)); } else { return(getsrc(x % w, y % h)); } } Parallel.For(0, w, x => { for (int y = 0; y < h; ++y) { double rv = 0, gv = 0, bv = 0; double rh = 0, gh = 0, bh = 0; for (int i = 0; i < ksize; ++i) { for (int j = 0; j < ksize; ++j) { RGB color = getsrc(x + i - ksize / 2, y + j - ksize / 2); rv += vkernel[i, j] * color.Rf; gv += vkernel[i, j] * color.Gf; bv += vkernel[i, j] * color.Bf; rh += hkernel[i, j] * color.Rf; gh += hkernel[i, j] * color.Gf; bh += hkernel[i, j] * color.Bf; } } pdst[y * w + x] = new RGB ( Sqrt(rv * rv + rh * rh).Constrain(), Sqrt(gv * gv + gh * gh).Constrain(), Sqrt(bv * bv + bh * bh).Constrain() ); } }); })); return(dst); }
public static ConvolutionInfo New( ConvolutionMode mode = ConvolutionMode.Convolution, int verticalPadding = 0, int horizontalPadding = 0, int verticalStride = 1, int horizontalStride = 1) => new ConvolutionInfo(mode, verticalPadding, horizontalPadding, verticalStride, horizontalStride);
public Tensor Convolve(Tensor core, ConvolutionMode mode) { return(null); }
/// <summary> /// Функция свёртки /// </summary> /// <param name="sourceImage">Исходное изображение</param> /// <param name="mode">Режим свёртки</param> /// <param name="channels">Каналы, над которыми проводится операция свёртки</param> /// <returns> /// Результирующее изображение /// </returns> public Image Convolution(Image sourceImage, ConvolutionMode mode = ConvolutionMode.collapse, int channels = (Channel.BLUE | Channel.RED | Channel.GREEN)) { Bitmap source = new Bitmap(sourceImage); //размеры фильтра int filterWidth = filterMatrix.GetLength(1); int filterHeight = filterMatrix.GetLength(0); int filterOffset = (filterWidth - 1) / 2; //смещение от центра if (mode == ConvolutionMode.expand) { source = (Bitmap)source.Expand(filterOffset); } //Для того, чтобы получить доступ к основные ARGB значения из Bitmap объекта мы сначала должны заблокировать Bitmap в памяти //Блокировка Bitmap в памяти предотвращает Garbage Collector от перемещения Bitmap объект на новое место в памяти. //При вызове Bitmap.LockBits метод исходный код создает экземпляр BitmapData объект из возвращаемого значения. BitmapData sourceData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); //BitmapData.Stride свойство представляет ряд байтов в одном растрового подряд пикселей. //В этом сценарии BitmapData.Stride должна быть равна ширине Bitmap в пикселях, умноженных на четыре, //так как каждый пиксель состоит из четырех байтов: Альфа, Красный, Зеленый и Синий. byte[] pixelBuffer = new byte[sourceData.Width * 4 * sourceData.Height]; int resWidth = source.Width - filterOffset * 2; int resHeight = source.Height - filterOffset * 2; byte[] resultBuffer = new byte[resWidth * 4 * resHeight]; //свойствл BitmapData.Scan0 BitmapData типа IntPtr представляет собой адрес памяти первого байта Bitmap //Использовуя Marshal.Copy метод, мы укажем адрес памяти отправной точки, //откуда начнём копирование байт Bitmap. Marshal.Copy(sourceData.Scan0, pixelBuffer, 0, pixelBuffer.Length); //Если растр был заблокирован в памяти обеспечим снятие блокировки с помощью вызова метода Bitmap.UnlockBits. source.UnlockBits(sourceData); double blue = 0.0; double green = 0.0; double red = 0.0; int calcOffset = 0; int byteOffset = 0; int byteOffset1 = 0; int width = source.Width; int height = source.Height; //проходим по массиву так, чтобы маска вписалась внутрь for (int offsetY = filterOffset; offsetY < height - filterOffset; offsetY++) { for (int offsetX = filterOffset; offsetX < width - filterOffset; offsetX++) { blue = 0; green = 0; red = 0; byteOffset = offsetY * width * 4 + offsetX * 4; byteOffset1 = (offsetY - filterOffset) * (width - 2 * filterOffset) * 4 + (offsetX - filterOffset) * 4; //Инициирование цикла с отрицательными значениями упрощает реализацию концепции соседних пикселей. for (int filterY = -filterOffset; filterY <= filterOffset; filterY++) { for (int filterX = -filterOffset; filterX <= filterOffset; filterX++) { //вычисляет индекс соседнего пиксела в отношении текущего пикселя. calcOffset = byteOffset + (filterX * 4) + (filterY * width * 4); //матрица значение применяется в качестве фактора индивидуальных цветовых компонентов соответствующих соседним пикселям. //Результаты будут добавлены в итоговые переменные голубых, зеленых и красных каналов. blue += (double)(pixelBuffer[calcOffset]) * filterMatrix[filterY + filterOffset, filterX + filterOffset]; green += (double)(pixelBuffer[calcOffset + 1]) * filterMatrix[filterY + filterOffset, filterX + filterOffset]; red += (double)(pixelBuffer[calcOffset + 2]) * filterMatrix[filterY + filterOffset, filterX + filterOffset]; } } //применяем коэффициент и добавить смещения, заданного параметром фильтра. blue = factor * blue + bias; green = factor * green + bias; red = factor * red + bias; //Цветовые компоненты могут содержать только значение в диапазоне от 0 до 255 включительно. //Прежде, чем мы присвоить рассчитанное значение цветового компонента, мы гарантируем, //что значение находится в пределах требуемого диапазона. //Значения, которые превышают 255 установим на 255, а менее 0 устанавливаются в 0. if (blue > 255) { blue = 255; } else if (blue < 0) { blue = 0; } if (green > 255) { green = 255; } else if (green < 0) { green = 0; } if (red > 255) { red = 255; } else if (red < 0) { red = 0; } if ((channels & Channel.BLUE) == Channel.BLUE) { resultBuffer[byteOffset1] = (byte)Math.Round(blue); } else { resultBuffer[byteOffset1] = pixelBuffer[byteOffset]; } if ((channels & Channel.GREEN) == Channel.GREEN) { resultBuffer[byteOffset1 + 1] = (byte)Math.Round(green); } else { resultBuffer[byteOffset1 + 1] = pixelBuffer[byteOffset + 1]; } if ((channels & Channel.RED) == Channel.RED) { resultBuffer[byteOffset1 + 2] = (byte)Math.Round(red); } else { resultBuffer[byteOffset1 + 2] = pixelBuffer[byteOffset + 2]; } resultBuffer[byteOffset1 + 3] = 255; } } //создание нового растрового экземпляр объекта и копирование результат расчета буфера Bitmap resultBitmap = new Bitmap(resWidth, resHeight); BitmapData resultData = resultBitmap.LockBits(new Rectangle(0, 0, resWidth, resHeight), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); Marshal.Copy(resultBuffer, 0, resultData.Scan0, resultBuffer.Length); resultBitmap.UnlockBits(resultData); return(resultBitmap); }