private Kernel1D getKernel(Kernel1D[] cache, double scale, double frac) { int kid = (int)(frac * 100); Kernel1D k = cache[kid]; if (k == null) { k = precompute(scale, frac); cache[kid] = k; if (k.size > tmpbuffer_r.Length) { tmpbuffer_r = new float[k.size]; } if (k.size > tmpbuffer_g.Length) { tmpbuffer_g = new float[k.size]; } if (k.size > tmpbuffer_b.Length) { tmpbuffer_b = new float[k.size]; } if (k.size > tmpbuffer_a.Length) { tmpbuffer_a = new float[k.size]; } } return(k); }
public SColor[] Filter(int _targetWidth, int _targetHeight) { double xfactor = _targetWidth / (double)m_width; double yfactor = _targetHeight / (double)m_height; minsupport = 5; // reset kernel cache m_kernelsCacheX = new Kernel1D[100]; m_kernelsCacheY = new Kernel1D[100]; // create empty output image m_targetWidth = (int)(0.5 + m_width * xfactor); m_targetHeight = (int)(0.5 + m_height * yfactor); m_output = new SColor[m_targetWidth * m_targetHeight]; // for each pixel in output image for (int y = 0; y < m_targetHeight; y++) { for (int x = 0; x < m_targetWidth; x++) { // ideal sample point in the source image double xo = x / xfactor; double yo = y / yfactor; // separate integer part and fractionnal part int x_int = (int)xo; double x_frac = xo - x_int; int y_int = (int)yo; double y_frac = yo - y_int; // get/compute resampling Kernels Kernel1D kx = getKernelX(xfactor, x_frac); Kernel1D ky = getKernelY(yfactor, y_frac); // compute resampled value SColor rgb = fastconvolve(x_int, y_int, kx, ky); // set to output image m_output[x + y * m_targetWidth] = rgb; } } return(m_output); }
/** * Compute a Lanczos resampling kernel * * @param scale scale to apply on the original image * @param frac fractionnal part of ideal sample point * @return the Lanczos resampling kernel */ private Kernel1D precompute(double scale, double frac) { // compute support size = how many source pixels for 1 sampled pixel ? int support = (int)(1 + 1.0 / scale); // minimum support if (support < minsupport) { support = minsupport; } // support size must be odd if (support % 2 == 0) { support++; } // scale limiter (minimum unit = 1 pixel) scale = Math.Min(scale, 1.0); // construct an empty kernel Kernel1D kernel = new Kernel1D(support, new float[support], 0); int i = 0; int halfwindow = support / 2; for (int dx = -halfwindow; dx <= halfwindow; dx++) { // ideal sample points (in the source image) double x = scale * (dx + frac); // corresponding weight (=contribution) of closest source pixel double coef = lanczos(halfwindow, x); // store to kernel float c = (float)(1000 * coef + 0.5); kernel.coefs[i++] = c; kernel.normalizer += c; } return(kernel); }
/** * convolve an image with a kernel for one pixel * * @param c input image * @param x,y coords of the pixel * @param kernelx,kernely kernels to use * @return new value of the pixel */ private SColor fastconvolve(int x, int y, Kernel1D kernelx, Kernel1D kernely) { int halfwindowy = kernely.size / 2; // assume a odd size int halfwindowx = kernelx.size / 2; // assume a odd size // empty tmpbuffer ArrayFill(tmpbuffer_r, 0); ArrayFill(tmpbuffer_g, 0); ArrayFill(tmpbuffer_b, 0); ArrayFill(tmpbuffer_a, 0); // pass 1 : horizontal convolution of image lines aree stored in tmpbuffer for (int dy = -halfwindowy; dy <= halfwindowy; dy++) { if (y + dy < 0 || y + dy >= m_height) { continue; } for (int dx = -halfwindowx; dx <= halfwindowx; dx++) { if (x + dx < 0 || x + dx >= m_width) { continue; } SColor rgb = m_input[x + dx + (y + dy) * m_width]; tmpbuffer_r[halfwindowy + dy] += kernelx.coefs[halfwindowx - dx] * rgb.r; tmpbuffer_g[halfwindowy + dy] += kernelx.coefs[halfwindowx - dx] * rgb.g; tmpbuffer_b[halfwindowy + dy] += kernelx.coefs[halfwindowx - dx] * rgb.b; tmpbuffer_a[halfwindowy + dy] += kernelx.coefs[halfwindowx - dx] * rgb.a; } } // pass 2 : vertical convolution of values stored in tmpbuffer double rc = 0, gc = 0, bc = 0, ac = 0; for (int dy = -halfwindowy; dy <= halfwindowy; dy++) { rc += kernely.coefs[halfwindowy - dy] * tmpbuffer_r[halfwindowy + dy]; gc += kernely.coefs[halfwindowy - dy] * tmpbuffer_g[halfwindowy + dy]; bc += kernely.coefs[halfwindowy - dy] * tmpbuffer_b[halfwindowy + dy]; ac += kernely.coefs[halfwindowy - dy] * tmpbuffer_a[halfwindowy + dy]; } // normalization double norm = kernelx.normalizer * kernely.normalizer; rc /= norm; gc /= norm; bc /= norm; ac /= norm; // return in argb format float r = (float)Math.Min(1.0, Math.Max(0, rc)); float g = (float)Math.Min(1, Math.Max(0, gc)); float b = (float)Math.Min(1, Math.Max(0, bc)); float a = (float)Math.Min(1, Math.Max(0, ac)); return(new SColor(r, g, b, a)); }