public ContributorEntry[] PrecomputeResample(double scale, int srcWidth, int destWidth) { // all variables are named as if we're scaling horizontally for the sake of readability, but the results work for both orientations var dest = new ContributorEntry[destWidth]; // one entry for every pixel in the resulting (destination) row of pixels double r = scale < 1 ? Radius / scale : Radius; // filter radius in terms of source image pixels double s = scale < 1 ? scale : 1; // filter scale relative to source pixels for (int destX = 0; destX < destWidth; destX++) { dest[destX].SrcPixelCount = 0; dest[destX].SrcPixel = new Contributor[(int)Math.Floor(2 * r + 1)]; double center = (destX + 0.5) / scale; int srcFromX = (int)Math.Floor(center - r); int srcToX = (int)Math.Ceiling(center + r); for (int srcX = srcFromX; srcX <= srcToX; srcX++) { double weight = GetValue((center - srcX - 0.5) * s); if ((weight == 0) || (srcX < 0) || (srcX >= srcWidth)) { continue; } dest[destX].SrcPixel[dest[destX].SrcPixelCount].Coord = srcX; dest[destX].SrcPixel[dest[destX].SrcPixelCount].Weight = weight; dest[destX].SrcPixelCount++; } } return(dest); }
/// <summary> /// Resamples input array to a new array using current resampling filter. /// </summary> /// <param name="input">Input array.</param> /// <param name="nWidth">Width of the output array.</param> /// <param name="nHeight">Height of the output array.</param> /// <returns>Output array.</returns> public ushort[][,] Resample(ushort[][,] input, int nWidth, int nHeight) { if (input == null || input.Length == 0 || nWidth <= 1 || nHeight <= 1) return null; ResamplingFilter filter = ResamplingFilter.Create(this.filter); int width = input[0].GetLength(0); int height = input[0].GetLength(1); int planes = input.Length; this.covered = 0; this.total = (nWidth + height); // create bitmaps ushort[][,] work = new ushort[planes][,]; ushort[][,] output = new ushort[planes][,]; int c = 0; for (c = 0; c < planes; c++) { work[c] = new ushort[nWidth, height]; output[c] = new ushort[nWidth, nHeight]; } double xScale = ((double)nWidth / width); double yScale = ((double)nHeight / height); ContributorEntry[] contrib = new ContributorEntry[nWidth]; double wdth = 0, center = 0, weight = 0, intensity = 0; int left = 0, right = 0, i = 0, j = 0, k = 0; // horizontal downsampling if (xScale < 1.0) { // scales from bigger to smaller width wdth = (filter.defaultFilterRadius / xScale); for (i = 0; i < nWidth; i++) { contrib[i].n = 0; contrib[i].p = new Contributor[(int)Math.Floor(2 * wdth + 1)]; contrib[i].wsum = 0; center = ((i + 0.5) / xScale); left = (int)(center - wdth); right = (int)(center + wdth); for (j = left; j <= right; j++) { weight = filter.GetValue((center - j - 0.5) * xScale); if ((weight == 0) || (j < 0) || (j >= width)) continue; contrib[i].p[contrib[i].n].pixel = j; contrib[i].p[contrib[i].n].weight = weight; contrib[i].wsum += weight; contrib[i].n++; } if (aborting) goto End; } } else { // horizontal upsampling // scales from smaller to bigger width for (i = 0; i < nWidth; i++) { contrib[i].n = 0; contrib[i].p = new Contributor[(int)Math.Floor(2 * filter.defaultFilterRadius + 1)]; contrib[i].wsum = 0; center = ((i + 0.5) / xScale); left = (int)Math.Floor(center - filter.defaultFilterRadius); right = (int)Math.Ceiling(center + filter.defaultFilterRadius); for (j = left; j <= right; j++) { weight = filter.GetValue(center - j - 0.5); if ((weight == 0) || (j < 0) || (j >= width)) continue; contrib[i].p[contrib[i].n].pixel = j; contrib[i].p[contrib[i].n].weight = weight; contrib[i].wsum += weight; contrib[i].n++; } if (aborting) goto End; } } // filter horizontally from input to work for (c = 0; c < planes; c++) { for (k = 0; k < height; k++) { for (i = 0; i < nWidth; i++) { intensity = 0; for (j = 0; j < contrib[i].n; j++) { weight = contrib[i].p[j].weight; if (weight == 0) continue; intensity += (input[c][contrib[i].p[j].pixel, k] * weight); } work[c][i, k] = (ushort)Math.Min(Math.Max(intensity / contrib[i].wsum, UInt16.MinValue), UInt16.MaxValue); } if (aborting) goto End; this.covered++; } } // pre-calculate filter contributions for a column contrib = new ContributorEntry[nHeight]; // vertical downsampling if (yScale < 1.0) { // scales from bigger to smaller height wdth = (filter.defaultFilterRadius / yScale); for (i = 0; i < nHeight; i++) { contrib[i].n = 0; contrib[i].p = new Contributor[(int)Math.Floor(2 * wdth + 1)]; contrib[i].wsum = 0; center = ((i + 0.5) / yScale); left = (int)(center - wdth); right = (int)(center + wdth); for (j = left; j <= right; j++) { weight = filter.GetValue((center - j - 0.5) * yScale); if ((weight == 0) || (j < 0) || (j >= height)) continue; contrib[i].p[contrib[i].n].pixel = j; contrib[i].p[contrib[i].n].weight = weight; contrib[i].wsum += weight; contrib[i].n++; } if (aborting) goto End; } } else { // vertical upsampling // scales from smaller to bigger height for (i = 0; i < nHeight; i++) { contrib[i].n = 0; contrib[i].p = new Contributor[(int)Math.Floor(2 * filter.defaultFilterRadius + 1)]; contrib[i].wsum = 0; center = ((i + 0.5) / yScale); left = (int)(center - filter.defaultFilterRadius); right = (int)(center + filter.defaultFilterRadius); for (j = left; j <= right; j++) { weight = filter.GetValue(center - j - 0.5); if ((weight == 0) || (j < 0) || (j >= height)) continue; contrib[i].p[contrib[i].n].pixel = j; contrib[i].p[contrib[i].n].weight = weight; contrib[i].wsum += weight; contrib[i].n++; } if (aborting) goto End; } } // filter vertically from work to output for (c = 0; c < planes; c++) { for (k = 0; k < nWidth; k++) { for (i = 0; i < nHeight; i++) { intensity = 0; for (j = 0; j < contrib[i].n; j++) { weight = contrib[i].p[j].weight; if (weight == 0) continue; intensity += (work[c][k, contrib[i].p[j].pixel] * weight); } output[c][k, i] = (ushort)Math.Min(Math.Max(intensity, UInt16.MinValue), UInt16.MaxValue); } if (aborting) goto End; this.covered++; } } End:; work = null; return output; }
/// <summary> /// Resamples input array to a new array using current resampling filter. /// </summary> /// <param name="input">Input array.</param> /// <param name="nWidth">Width of the output array.</param> /// <param name="nHeight">Height of the output array.</param> /// <returns>Output array.</returns> public ushort[][,] Resample(ushort[][,] input, int nWidth, int nHeight) { if (input == null || input.Length == 0 || nWidth < 1 || nHeight < 1) { return(null); } ResamplingFilter filter = ResamplingFilter.Create(this.filter); int width = input[0].GetLength(0); int height = input[0].GetLength(1); int planes = input.Length; this.covered = 0; this.total = (nWidth + height); // create bitmaps ushort[][,] work = new ushort[planes][, ]; ushort[][,] output = new ushort[planes][, ]; int c = 0; for (c = 0; c < planes; c++) { work[c] = new ushort[nWidth, height]; output[c] = new ushort[nWidth, nHeight]; } double xScale = ((double)nWidth / width); double yScale = ((double)nHeight / height); ContributorEntry[] contrib = new ContributorEntry[nWidth]; double wdth = 0, center = 0, weight = 0, intensity = 0; int left = 0, right = 0, i = 0, j = 0, k = 0; // horizontal downsampling if (xScale < 1.0) { // scales from bigger to smaller width wdth = (filter.defaultFilterRadius / xScale); for (i = 0; i < nWidth; i++) { contrib[i].n = 0; contrib[i].p = new Contributor[(int)Math.Floor(2 * wdth + 1)]; contrib[i].wsum = 0; center = ((i + 0.5) / xScale); left = (int)(center - wdth); right = (int)(center + wdth); for (j = left; j <= right; j++) { weight = filter.GetValue((center - j - 0.5) * xScale); if ((weight == 0) || (j < 0) || (j >= width)) { continue; } contrib[i].p[contrib[i].n].pixel = j; contrib[i].p[contrib[i].n].weight = weight; contrib[i].wsum += weight; contrib[i].n++; } if (aborting) { goto End; } } } else { // horizontal upsampling // scales from smaller to bigger width for (i = 0; i < nWidth; i++) { contrib[i].n = 0; contrib[i].p = new Contributor[(int)Math.Floor(2 * filter.defaultFilterRadius + 1)]; contrib[i].wsum = 0; center = ((i + 0.5) / xScale); left = (int)Math.Floor(center - filter.defaultFilterRadius); right = (int)Math.Ceiling(center + filter.defaultFilterRadius); for (j = left; j <= right; j++) { weight = filter.GetValue(center - j - 0.5); if ((weight == 0) || (j < 0) || (j >= width)) { continue; } contrib[i].p[contrib[i].n].pixel = j; contrib[i].p[contrib[i].n].weight = weight; contrib[i].wsum += weight; contrib[i].n++; } if (aborting) { goto End; } } } // filter horizontally from input to work for (c = 0; c < planes; c++) { for (k = 0; k < height; k++) { for (i = 0; i < nWidth; i++) { intensity = 0; for (j = 0; j < contrib[i].n; j++) { weight = contrib[i].p[j].weight; if (weight == 0) { continue; } intensity += (input[c][contrib[i].p[j].pixel, k] * weight); } work[c][i, k] = (ushort)Math.Min(Math.Max(intensity / contrib[i].wsum + 0.5, UInt16.MinValue), UInt16.MaxValue); } if (aborting) { goto End; } this.covered++; } } // pre-calculate filter contributions for a column contrib = new ContributorEntry[nHeight]; // vertical downsampling if (yScale < 1.0) { // scales from bigger to smaller height wdth = (filter.defaultFilterRadius / yScale); for (i = 0; i < nHeight; i++) { contrib[i].n = 0; contrib[i].p = new Contributor[(int)Math.Floor(2 * wdth + 1)]; contrib[i].wsum = 0; center = ((i + 0.5) / yScale); left = (int)(center - wdth); right = (int)(center + wdth); for (j = left; j <= right; j++) { weight = filter.GetValue((center - j - 0.5) * yScale); if ((weight == 0) || (j < 0) || (j >= height)) { continue; } contrib[i].p[contrib[i].n].pixel = j; contrib[i].p[contrib[i].n].weight = weight; contrib[i].wsum += weight; contrib[i].n++; } if (aborting) { goto End; } } } else { // vertical upsampling // scales from smaller to bigger height for (i = 0; i < nHeight; i++) { contrib[i].n = 0; contrib[i].p = new Contributor[(int)Math.Floor(2 * filter.defaultFilterRadius + 1)]; contrib[i].wsum = 0; center = ((i + 0.5) / yScale); left = (int)(center - filter.defaultFilterRadius); right = (int)(center + filter.defaultFilterRadius); for (j = left; j <= right; j++) { weight = filter.GetValue(center - j - 0.5); if ((weight == 0) || (j < 0) || (j >= height)) { continue; } contrib[i].p[contrib[i].n].pixel = j; contrib[i].p[contrib[i].n].weight = weight; contrib[i].wsum += weight; contrib[i].n++; } if (aborting) { goto End; } } } // filter vertically from work to output for (c = 0; c < planes; c++) { for (k = 0; k < nWidth; k++) { for (i = 0; i < nHeight; i++) { intensity = 0; for (j = 0; j < contrib[i].n; j++) { weight = contrib[i].p[j].weight; if (weight == 0) { continue; } intensity += (work[c][k, contrib[i].p[j].pixel] * weight); } output[c][k, i] = (ushort)Math.Min(Math.Max(intensity / contrib[i].wsum + 0.5, UInt16.MinValue), UInt16.MaxValue); } if (aborting) { goto End; } this.covered++; } } End :; work = null; return(output); }