http://blog.youkuaiyun.com/cxf7394373/article/details/5788040
最近有一段時間做圖像處理方面的東西,遇到了一些樣張需要進行分辨率的增強或者說是放大圖像,如何在圖像不失真的情況下放大圖像呢?總的來說就是插值,插值包括零階插值、雙線性插值(Bilinear Interpolation)、立方卷積法(Bicubic Interpolation)等方法。
我實驗了雙線性插值和雙三次插值,雙三次插值的計算量比較大,但是效果也相對較好。有一篇叫「數字圖像最佳插值算法研究」的中文論文中詳細介紹了這三種方法的理論,還做了簡單的比較。
Bilinear Interpolation的公式很簡單,就是一個線性插值,f(i+u,j+ v)=(1-u)(1-v)f(i,j)+(1-u)vf(i,j+1) + u(1-v)f(i+1,j)+uvf(i+1,j+1),與(i,j)相鄰的四個像素點乘以權值的加和即為插值後的灰度值。
Bicubic Interpolation相對比較復雜,公式如下:
f(x,y) = A*B*C,其中:
由此可以計算插值像素點的灰度值。該插值方法的C#代碼如下:
- //把圖像放大兩倍
- private void resizeToolStripMenuItem_Click(object sender, EventArgs e)
- {
- Graphics dc = this.CreateGraphics();
- dc.DrawImage(picture, 10, 30, this.ClientSize.Width - 20, this.ClientSize.Height - 20);
- Color color;
- pic = new Bitmap(picture);
- int i, j;
- int width = picture.Width;
- int height = picture.Height;
- int newWidth = Convert.ToInt32(picture.Width * 2);
- int newHeight = Convert.ToInt32(picture.Height * 2);
- Bitmap newpic = new Bitmap(newWidth, newHeight);
- //int pixelSize =3;
- int pixelSize = 1;
- int srcStride = width-1;
- int dstOffset = newWidth - pixelSize * newWidth;
- double xFactor = (double)width / newWidth;
- double yFactor = (double)height / newHeight;
- // do the job
- byte[,] src = new byte[width, height]; //(byte*)sourceData.ImageData.ToPointer();
- byte[,] dst = new byte[newWidth, newHeight];//(byte*)destinationData.ImageData.ToPointer();
- //double[,] pBuffer = new double[usW, usH];
- //double[,] pBuffer1 = new double[usW, usH];
- // coordinates of source points and coefficiens
- double ox, oy, dx, dy, k1, k2;
- int ox1, oy1, ox2, oy2;
- // destination pixel values
- double r, g, b;
- // width and height decreased by 1
- int ymax = height - 1;
- int xmax = width - 1;
- // temporary pointer
- //byte* p;
- for (i = 0; i < width; i++)
- {
- for (j = 0; j < height; j++)
- {
- color = pic.GetPixel(i, j);
- src[i, j] =(byte)((color.R + color.G + color.B) / 3);
- }
- }
- for (int y = 0; y < newHeight; y++)
- {
- // Y coordinates
- oy = (double)y * yFactor - 0.5;
- oy1 = (int)oy;
- dy = oy - (double)oy1;
- for (int x = 0; x < newWidth; x++)
- {
- // X coordinates
- ox = (double)x * xFactor - 0.5f;
- ox1 = (int)ox;
- dx = ox - (double)ox1;
- // initial pixel value
- g = 0;
- for (int n = -1; n < 3; n++)
- {
- // get Y cooefficient
- k1 = BiCubicKernel(dy - (double)n);
- oy2 = oy1 + n;
- if (oy2 < 0)
- oy2 = 0;
- if (oy2 > ymax)
- oy2 = ymax;
- for (int m = -1; m < 3; m++)
- {
- // get X cooefficient
- k2 = k1 * BiCubicKernel((double)m - dx);
- ox2 = ox1 + m;
- if (ox2 < 0)
- ox2 = 0;
- if (ox2 > xmax)
- ox2 = xmax;
- // int tmp = oy2 * srcStride;
- g += k2 * src[ox2, oy2];
- }
- }
- dst[x,y] = (byte)g;
- }
- }
- int gray = 0;
- for (i = 0; i < newWidth; i++)
- {
- for (j = 0; j < newHeight; j++)
- {
- gray = dst[i, j];
- color = Color.FromArgb(gray, gray, gray);
- newpic.SetPixel(i, j, color);
- }
- }
- //Show the processing result
- dc.DrawImage(newpic, 10, 30, this.ClientSize.Width - 20, this.ClientSize.Height - 20);
- image = new Bitmap(newpic);
- pic.Dispose();
- }
- public double BiCubicKernel(double x)
- {
- if (x > 2.0)
- return 0.0;
- double a, b, c, d;
- double xm1 = x - 1.0;
- double xp1 = x + 1.0;
- double xp2 = x + 2.0;
- a = (xp2 <= 0.0) ? 0.0 : xp2 * xp2 * xp2;
- b = (xp1 <= 0.0) ? 0.0 : xp1 * xp1 * xp1;
- c = (x <= 0.0) ? 0.0 : x * x * x;
- d = (xm1 <= 0.0) ? 0.0 : xm1 * xm1 * xm1;
- return (0.16666666666666666667 * (a - (4.0 * b) + (6.0 * c) - (4.0 * d)));
- }
大家不一定有耐心去看懂它,雙線性插值的方法也是這樣寫,只不過比這個要簡單!