基于上一节“等距采样法”实现图片放大与缩小的缺点。要对其进行改进,对图像的缩小则可以用“局部均值法”,对于图像的放大则可以用“双线性插值法”。
效果如下:
设原图的大小为W*H,将其放大(缩小)为(k1*W)*(K2*H),则采样区间为
ii=1/k1; jj=1/k2;
当k1==k2时为等比例缩小;当k1!=k2时为不等比例放大(缩小);当k1<1 && k2<1时为图片缩小,k1>1 && k2>1时图片放大。
设原图为F(x,y)(i=1,2,……W; j=1,2,……H),缩小的图像为G(x,y)(x=1,2, ……M; y=1,2,……N,其中M=W*k1,N=H*k2),则有原图像局部子块为
f’(x,y) = f(ii*i, jj*j) …… f(ii*i + ii-1, jj*j)
…… ……
f(ii*i, jj*j+jj-1) …… f(ii*i + ii-1, jj*j+jj-1)
G(x, y) = f’(x,y)的均值
例:
缩小后的图像
例如g11=(f11 +f12 + f21 + f22)/4
算法源代码(java)
/** * 局部均值的图像缩小 * @param img 要缩小的图像对象 * @param m 缩小后图像的宽 * @param n 缩小后图像的高 * @return 返回处理后的图像对象 */ public static BufferedImage shrink(BufferedImage img, int m, int n) { float k1 = (float)m/img.getWidth(); float k2 = (float)n/img.getHeight(); return shrink(img, k1, k2); } /** * 局部均值的图像缩小 * @param img 要缩小的图像对象 * @param k1 要缩小的列比列 * @param k2 要缩小的行比列 * @return 返回处理后的图像对象 */ public static BufferedImage shrink(BufferedImage img, float k1, float k2) { if(k1 >1 || k2>1) {//如果k1 >1 || k2>1则是图片放大,不是缩小 System.err.println("this is shrink image funcation, please set k1<=1 and k2<=1!"); return null; } float ii = 1/k1; //采样的行间距 float jj = 1/k2; //采样的列间距 int dd = (int)(ii*jj); //int m=0 , n=0; int imgType = img.getType(); int w = img.getWidth(); int h = img.getHeight(); int m = (int) (k1*w); int n = (int) (k2*h); int[] pix = new int[w*h]; pix = img.getRGB(0, 0, w, h, pix, 0, w); System.out.println(w + " * " + h); System.out.println(m + " * " + n); int[] newpix = new int[m*n]; for(int j=0; j<n; j++) { for(int i=0; i<m; i++) { int r = 0, g=0, b=0; ColorModel cm = ColorModel.getRGBdefault(); for(int k=0; k<(int)jj; k++) { for(int l=0; l<(int)ii; l++) { r = r + cm.getRed(pix[(int)(jj*j+k)*w + (int)(ii*i+l)]); g = g + cm.getGreen(pix[(int)(jj*j+k)*w + (int)(ii*i+l)]); b = b + cm.getBlue(pix[(int)(jj*j+k)*w + (int)(ii*i+l)]); } } r = r/dd; g = g/dd; b = b/dd; newpix[j*m + i] = 255<<24 | r<<16 | g<<8 | b; //255<<24 | r<<16 | g<<8 | b 这个公式解释一下,颜色的RGB在内存中是 //以二进制的形式保存的,从右到左1-8位表示blue,9-16表示green,17-24表示red //所以"<<24" "<<16" "<<8"分别表示左移24,16,8位 //newpix[j*m + i] = new Color(r,g,b).getRGB(); } } BufferedImage imgOut = new BufferedImage( m, n, imgType); imgOut.setRGB(0, 0, m, n, newpix, 0, m); return imgOut; }
/** * 局部均值的图像缩小 * @param img 要缩小的图像对象 * @param m 缩小后图像的宽 * @param n 缩小后图像的高 * @return 返回处理后的图像对象 */ public static BufferedImage shrink(BufferedImage img, int m, int n) { float k1 = (float)m/img.getWidth(); float k2 = (float)n/img.getHeight(); return shrink(img, k1, k2); } /** * 局部均值的图像缩小 * @param img 要缩小的图像对象 * @param k1 要缩小的列比列 * @param k2 要缩小的行比列 * @return 返回处理后的图像对象 */ public static BufferedImage shrink(BufferedImage img, float k1, float k2) { if(k1 >1 || k2>1) {//如果k1 >1 || k2>1则是图片放大,不是缩小 System.err.println("this is shrink image funcation, please set k1<=1 and k2<=1!"); return null; } float ii = 1/k1; //采样的行间距 float jj = 1/k2; //采样的列间距 int dd = (int)(ii*jj); //int m=0 , n=0; int imgType = img.getType(); int w = img.getWidth(); int h = img.getHeight(); int m = (int) (k1*w); int n = (int) (k2*h); int[] pix = new int[w*h]; pix = img.getRGB(0, 0, w, h, pix, 0, w); System.out.println(w + " * " + h); System.out.println(m + " * " + n); int[] newpix = new int[m*n]; for(int j=0; j<n; j++) { for(int i=0; i<m; i++) { int r = 0, g=0, b=0; ColorModel cm = ColorModel.getRGBdefault(); for(int k=0; k<(int)jj; k++) { for(int l=0; l<(int)ii; l++) { r = r + cm.getRed(pix[(int)(jj*j+k)*w + (int)(ii*i+l)]); g = g + cm.getGreen(pix[(int)(jj*j+k)*w + (int)(ii*i+l)]); b = b + cm.getBlue(pix[(int)(jj*j+k)*w + (int)(ii*i+l)]); } } r = r/dd; g = g/dd; b = b/dd; newpix[j*m + i] = 255<<24 | r<<16 | g<<8 | b; //255<<24 | r<<16 | g<<8 | b 这个公式解释一下,颜色的RGB在内存中是 //以二进制的形式保存的,从右到左1-8位表示blue,9-16表示green,17-24表示red //所以"<<24" "<<16" "<<8"分别表示左移24,16,8位 //newpix[j*m + i] = new Color(r,g,b).getRGB(); } } BufferedImage imgOut = new BufferedImage( m, n, imgType); imgOut.setRGB(0, 0, m, n, newpix, 0, m); return imgOut; }
子块四个顶点的坐标分别设为(0,0)、(1,0)、(0,1)、(1,1),对应的带处理的像素的坐标(c1,c2),0<c1<1, 0<y<1.则f(x,y)由上到下得到
例,原图的像素矩阵如下。
将其放大成2.5*1.2倍,双线性插值发,填充顶点如下:
(1)
(2)
1 2 3 4 5 6 7 7
2 3 4 5 7 8 8 8
3 4 5 6 7 8 9 9
3 4 5 6 7 8 9 9
(3)
算法源代码(java)
/** * 双线性插值法图像的放大 * @param img 要缩小的图像对象 * @param k1 要缩小的列比列 * @param k2 要缩小的行比列 * @return 返回处理后的图像对象 */ public static BufferedImage amplify(BufferedImage img, float k1, float k2) { if(k1 <1 || k2<1) {//如果k1 <1 || k2<1则是图片缩小,不是放大 System.err.println("this is shrink image funcation, please set k1<=1 and k2<=1!"); return null; } float ii = 1/k1; //采样的行间距 float jj = (1/k2); //采样的列间距 int dd = (int)(ii*jj); //int m=0 , n=0; int imgType = img.getType(); int w = img.getWidth(); //原图片的宽 int h = img.getHeight(); //原图片的宽 int m = Math.round(k1*w); //放大后图片的宽 int n = Math.round(k2*h); //放大后图片的宽 int[] pix = new int[w*h]; pix = img.getRGB(0, 0, w, h, pix, 0, w); /*System.out.println(w + " * " + h); System.out.println(m + " * " + n);*/ int[] newpix = new int[m*n]; for(int j=0; j<h-1; j++){ for(int i=0; i<w-1; i++) { int x0 = Math.round(i*k1); int y0 = Math.round(j*k2); int x1, y1; if(i == w-2) { x1 = m-1; } else { x1 = Math.round((i+1)*k1); } if(j == h-2) { y1 = n-1; } else { y1 = Math.round((j+1)*k2); } int d1 = x1 - x0; int d2 = y1 - y0; if(0 == newpix[y0*m + x0]) { newpix[y0*m + x0] = pix[j*w+i]; } if(0 == newpix[y0*m + x1]) { if(i == w-2) { newpix[y0*m + x1] = pix[j*w+w-1]; } else { newpix[y0*m + x1] = pix[j*w+i+1]; } } if(0 == newpix[y1*m + x0]){ if(j == h-2) { newpix[y1*m + x0] = pix[(h-1)*w+i]; } else { newpix[y1*m + x0] = pix[(j+1)*w+i]; } } if(0 == newpix[y1*m + x1]) { if(i==w-2 && j==h-2) { newpix[y1*m + x1] = pix[(h-1)*w+w-1]; } else { newpix[y1*m + x1] = pix[(j+1)*w+i+1]; } } int r, g, b; float c; ColorModel cm = ColorModel.getRGBdefault(); for(int l=0; l<d2; l++) { for(int k=0; k<d1; k++) { if(0 == l) { //f(x,0) = f(0,0) + c1*(f(1,0)-f(0,0)) if(j<h-1 && newpix[y0*m + x0 + k] == 0) { c = (float)k/d1; r = cm.getRed(newpix[y0*m + x0]) + (int)(c*(cm.getRed(newpix[y0*m + x1]) - cm.getRed(newpix[y0*m + x0])));//newpix[(y0+l)*m + k] g = cm.getGreen(newpix[y0*m + x0]) + (int)(c*(cm.getGreen(newpix[y0*m + x1]) - cm.getGreen(newpix[y0*m + x0]))); b = cm.getBlue(newpix[y0*m + x0]) + (int)(c*(cm.getBlue(newpix[y0*m + x1]) - cm.getBlue(newpix[y0*m + x0]))); newpix[y0*m + x0 + k] = new Color(r,g,b).getRGB(); } if(j+1<h && newpix[y1*m + x0 + k] == 0) { c = (float)k/d1; r = cm.getRed(newpix[y1*m + x0]) + (int)(c*(cm.getRed(newpix[y1*m + x1]) - cm.getRed(newpix[y1*m + x0]))); g = cm.getGreen(newpix[y1*m + x0]) + (int)(c*(cm.getGreen(newpix[y1*m + x1]) - cm.getGreen(newpix[y1*m + x0]))); b = cm.getBlue(newpix[y1*m + x0]) + (int)(c*(cm.getBlue(newpix[y1*m + x1]) - cm.getBlue(newpix[y1*m + x0]))); newpix[y1*m + x0 + k] = new Color(r,g,b).getRGB(); } //System.out.println(c); } else { //f(x,y) = f(x,0) + c2*f(f(x,1)-f(x,0)) c = (float)l/d2; r = cm.getRed(newpix[y0*m + x0+k]) + (int)(c*(cm.getRed(newpix[y1*m + x0+k]) - cm.getRed(newpix[y0*m + x0+k]))); g = cm.getGreen(newpix[y0*m + x0+k]) + (int)(c*(cm.getGreen(newpix[y1*m + x0+k]) - cm.getGreen(newpix[y0*m + x0+k]))); b = cm.getBlue(newpix[y0*m + x0+k]) + (int)(c*(cm.getBlue(newpix[y1*m + x0+k]) - cm.getBlue(newpix[y0*m + x0+k]))); newpix[(y0+l)*m + x0 + k] = new Color(r,g,b).getRGB(); //System.out.println((int)(c*(cm.getRed(newpix[y1*m + x0+k]) - cm.getRed(newpix[y0*m + x0+k])))); } } if(i==w-2 || l==d2-1) { //最后一列的计算 //f(1,y) = f(1,0) + c2*f(f(1,1)-f(1,0)) c = (float)l/d2; r = cm.getRed(newpix[y0*m + x1]) + (int)(c*(cm.getRed(newpix[y1*m + x1]) - cm.getRed(newpix[y0*m + x1]))); g = cm.getGreen(newpix[y0*m + x1]) + (int)(c*(cm.getGreen(newpix[y1*m + x1]) - cm.getGreen(newpix[y0*m + x1]))); b = cm.getBlue(newpix[y0*m + x1]) + (int)(c*(cm.getBlue(newpix[y1*m + x1]) - cm.getBlue(newpix[y0*m + x1]))); newpix[(y0+l)*m + x1] = new Color(r,g,b).getRGB(); } } } } /* for(int j=0; j<50; j++){ for(int i=0; i<50; i++) { System.out.print(new Color(newpix[j*m + i]).getRed() + "\t"); } System.out.println(); } */ BufferedImage imgOut = new BufferedImage( m, n, imgType); imgOut.setRGB(0, 0, m, n, newpix, 0, m); return imgOut; }