protected override void ApplyFilter() { // get source image size int width = _sourceData[0].GetLength(0), height = _sourceData[0].GetLength(1); int channels = _sourceData.Length; // Estimate a good filter size for the gaussian. // Note that gaussian isn't an ideal bandpass filter // so this is an experimentally determined quantity double std = (width / _newWidth) * 0.50; for (int i = 0; i < channels; i++) { GrayImage channel = new GrayImage(_sourceData[i]); channel = Convolution.Instance.GaussianConv(channel, std); _sourceData[i] = channel.ToByteArray2D(); } // number of pixels to shift in the original image double xStep = (double)width / _newWidth, yStep = (double)height / _newHeight; NNResize resizer = new NNResize(); _destinationData = resizer.Apply(_sourceData, _newWidth, _newHeight); }
protected override void ApplyFilter() { // get source image size int width = _sourceData[0].GetLength(0), height = _sourceData[0].GetLength(1); int channels = _sourceData.Length; // Estimate a good filter size for the gaussian. // Note that gaussian isn't an ideal bandpass filter // so this is an experimentally determined quantity double std = (width / _newWidth) * 0.50; for(int i = 0; i < channels; i++) { GrayImage channel = new GrayImage(_sourceData[i]); channel = Convolution.Instance.GaussianConv(channel, std); _sourceData[i] = channel.ToByteArray2D(); } // number of pixels to shift in the original image double xStep = (double)width / _newWidth, yStep = (double)height / _newHeight; NNResize resizer = new NNResize(); _destinationData = resizer.Apply(_sourceData, _newWidth, _newHeight); }
public GrayImage Conv2DSeparable(GrayImage data, float[] filter) { GrayImage pass1 = Filter1DSymmetric(data, filter, true); GrayImage result = Filter1DSymmetric(pass1, filter, true); return result; }
/// <summary> /// Convolves an GrayImage with a 2D-symmetric operator. /// </summary> /// <param name="data">Data to be convolved with the operator</param> /// <param name="opUL">Lower-right quadrant of the operator.</param> /// <returns></returns> private GrayImage Conv2DSymm(GrayImage data, GrayImage opLR) { if (opLR.Width % 2 != 0 || opLR.Height % 2 != 0) throw new ArgumentException("Operator must have an even number of rows and columns."); int xPad = opLR.Width - 1; int yPad = opLR.Height - 1; GrayImage result = new GrayImage(data.Width - 2 * xPad, data.Height - 2 * yPad); for (int y = yPad; y < data.Height - yPad; y++) { for (int x = xPad; x < data.Width - xPad; x++) { // Center pixel float pixel = data[x, y] * opLR.Scan0[0]; // Vertical center for (int op_y = 1; op_y < opLR.Height; op_y++) pixel += (data[x, y + op_y] + data[x, y - op_y]) * opLR[0, op_y]; //Horizontal center for (int op_x = 1; op_x < opLR.Width; op_x++) pixel += (data[x + op_x, y] + data[x - op_x, y]) * opLR[op_x, 0]; //Quadrants int opIdx = 1; for (int op_y = 1; op_y < opLR.Height; op_y++) { int baseIdx1 = ((y + op_y) * data.Width) + x; int baseIdx2 = ((y - op_y) * data.Width) + x; // Loop unrolling can save 25% execution time here for (int op_x = 1; op_x < opLR.Width; op_x++) { pixel += (data.Scan0[baseIdx1 + op_x] + data.Scan0[baseIdx2 + op_x] + data.Scan0[baseIdx1 - op_x] + data.Scan0[baseIdx2 - op_x]) * opLR.Scan0[opIdx]; opIdx++; } opIdx++; // Skip 0th col on next row } result[x - xPad, y - yPad] = pixel; } // loop over data x } // loop over data y return result; }
/// <summary> /// Vanilla 2D convolution. Not optimized. /// </summary> /// <param name="data"></param> /// <param name="op"></param> /// <returns></returns> public GrayImage Conv2D(GrayImage data, GrayImage op) { GrayImage result = new GrayImage(data.Width, data.Height); if (op.Width % 2 == 0 || op.Height % 2 == 0) throw new ArgumentException("Operator must have an odd number of rows and columns."); int x_offset = op.Width / 2; int y_offset = op.Height / 2; for (int y = 0; y < data.Height; y++) { for (int x = 0; x < data.Width; x++) { float pixel = 0; float wt = 0; for (int op_y = 0; op_y < op.Height; op_y++) { int d_y = y - y_offset + op_y; if (d_y < 0 || d_y >= data.Height) continue; for (int op_x = 0; op_x < op.Width; op_x++) { int d_x = x - x_offset + op_x; if (d_x < 0 || d_x >= data.Width) continue; float op_val = op[op_x, op_y]; /* Perform actual convolution */ wt += Math.Abs(op_val); pixel += data[d_x, d_y] * op_val; } } result[x, y] = pixel / wt; } // loop over data x } // loop over data y return result; }
/// <summary> /// Filters an GrayImage with a 1D symmetric filter along the X-axis. /// (This operation is multithreaded) /// </summary> /// <param name="data">GrayImage to be operated on</param> /// <param name="filter">Filter to use (center tap plus right-hand-side)</param> /// <param name="transpose">Transpose the result?</param> /// <returns>Transposed, filtered GrayImage.</returns> public GrayImage Filter1DSymmetric(GrayImage data, float[] filter, bool transpose) { GrayImage result = transpose ? new GrayImage(data.Height, data.Width) : new GrayImage(data.Width, data.Height); int startY = 0; int destPtr = transpose ? startY : (startY * result.Width); FilterJob job = new FilterJob { filter = filter, data = data, destPtr = destPtr, result = result, start = startY, end = data.Height / 2 }; ParameterizedThreadStart del = transpose ? new ParameterizedThreadStart(FilterPartSymmetricT) : new ParameterizedThreadStart(FilterPartSymmetric); Thread worker = new Thread(del); worker.Start(job); startY = data.Height / 2; destPtr = transpose ? startY : (startY * result.Width); job.start = startY; job.destPtr = destPtr; job.end = data.Height; del((object)job); // Run the appropriate filter in this thread, too worker.Join(); return result; }
public GrayImage Conv2DSymmetric(GrayImage data, GrayImage opLR) { int xPad = opLR.Width - 1; int yPad = opLR.Height - 1; GrayImage padded = new GrayImage(data.Width + 2 * xPad, data.Height + 2 * yPad); int dataIdx = 0; for (int y = 0; y < data.Height; y++) { int rowStart = (y + yPad) * (data.Width + 2 * xPad) + xPad; for (int x = 0; x < data.Width; x++) { padded.Scan0[rowStart + x] = data.Scan0[dataIdx]; dataIdx++; } } return Conv2DSymm(padded, opLR); }
public GrayImage GaussianConv(GrayImage data, double std) { float[] filter = GaussianFilter(std); return Conv2DSeparable(data, filter); }
/// <summary> /// Convolves an GrayImage with a 1D filter along the X-axis. /// </summary> /// <param name="filterJob">Filter operation details</param> private void FilterPartSymmetric(object filterJob) { FilterJob fj = (FilterJob)filterJob; GrayImage data = fj.data; float[] srcData = data.Scan0; float[] filter = fj.filter; GrayImage result = fj.result; float[] resData = result.Scan0; int pad = filter.Length - 1; int destPtr = fj.destPtr; #region Filter (no transpose) for (int y = fj.start; y < fj.end; y++) { int rowStart = y * data.Width; int ptr = fj.dataPtr + rowStart; // Left checked region for (int x = 0; x < pad; x++) { float pixel = srcData[ptr] * filter[0]; // Part of the filter that fits within the GrayImage for (int i = 1; i < x + 1; i++) pixel += (srcData[ptr + i] + srcData[ptr - i]) * filter[i]; // Part of the filter that falls off the left side for (int i = x + 1; i < filter.Length; i++) pixel += (srcData[ptr + i] + srcData[ptr + i]) * filter[i]; resData[destPtr++] = pixel; ptr++; } // Unchecked region for (int x = pad; x < data.Width - pad; x++) { float pixel = srcData[ptr] * filter[0]; for (int i = 1; i < filter.Length; i++) pixel += (srcData[ptr + i] + srcData[ptr - i]) * filter[i]; resData[destPtr++] = pixel; ptr++; } // Right checked region for (int x = data.Width - pad; x < data.Width; x++) { float pixel = srcData[ptr] * filter[0]; // Part of the filter that fits within the GrayImage for (int i = 0; i < (data.Width - x); i++) pixel += (srcData[ptr + i] + srcData[ptr - i]) * filter[i]; // Part of the filter that falls off the right side for (int i = (data.Width - x); i < filter.Length; i++) pixel += (srcData[ptr + i] + srcData[ptr - i]) * filter[i]; resData[destPtr++] = pixel; ptr++; } } #endregion }