分享
 
 
 

J2ME插值算法实现图片的放大缩小

王朝java/jsp·作者佚名  2006-01-10
窄屏简体版  字體: |||超大  

文章来源:J2ME开发网

前段时间接触了一些数字图像处理的问题,在1位师兄的指导下,在j2me平台,完成了一些基本的2D图像处理算法。就当是对这段知识做一下总结,决定把这些算法写出来,和各位朋友共同探讨。这篇文章先介绍图像放大缩小的实现,程序是以Nokia S40的机器为平台实现的。

1、实现图形缩放的基本思想:

图像的变形变换,简单的说就是把源图像每个点坐标通过变形运算转为目标图像相应点的新坐标,但是这样会导致一个问题就是目标点的坐标通常不会是整数。所以我们在做放大变换时,需要计算生成没有被映射到的点;而在缩小变换时,需要删除一些点。这里我们采用最简单的一种插值算法:“最近邻域法”。顾名思义,就是把非整数坐标作一个四舍五入,取最近的整数点。

看下面的一个图片放大的例子,左图为原始图像,右图为放大1倍的图像。里面的数字,表示所在像素的信息

2、对于图片像素的操作:

获取Image图片像素信息:

标准的midp1.0没有提供获取图片像素信息的函数,对于NOKIA的机器,我们可以采用Nokia SDK提供的API获取像素信息。具体程序如下:

g = image.getGraphics()

DirectGraphics dg = DirectUtils.getDirectGraphics(g);

dg.getPixels(short[] pixels, int offset, int scanlength, int x,int y, int width, int height, int format)

参数介绍:

short[] pixels: 用于接收像素信息的数组

int offset:这篇文章中的用到的地方,添0就可以了

int scanlength:添图片的宽度就行了

int x:添0

int y:添0

int width:图片宽度

int height:图片高度

int format:444,表示图形格式,好像Nokia S40的机器都是采用444格式表示RGB颜色的。就是红,绿,蓝各用4位表示,至于可以表示透明色ARGB的4444格式,应该是机器硬件实现的。

想具体了解Nokia SDK的信息,可以查看Nokia SDK的帮助文档。

使用像素信息数组生成Image图片:

image = Image.createImage(w, h);

g = image.getGraphics()

DirectGraphics dg = DirectUtils.getDirectGraphics(g);

dg.drawPixels(short[] pixels,boolean transparency, int offset, int scanlength, int x, int y, int width,int height, int manipulation, int format)

short[] pixels:像素信息数组

boolean transparency:是否包含alpha位信息

int offset:添 0

int scanlength:添图片的宽度就行了

int x:添 0

int y:添 0

int width:图片宽度

int height:图片高度

int manipulation:添 0

int format:444

下面开始介绍具体的算法,首先给出图像缩放的完整函数,然后对代码,分段进行解释

/*********************************

* @todo 图片放大缩小

* @param srcImg 原始图片

* @param desW 变化后图片的宽

* @param desH 变化后图片的高

* @return 处理后的图片

*********************************/

private Image ZoomImage(Image srcImg, int desW, int desH) {

int srcW = srcImg.getWidth(); //原始图像宽

int srcH = srcImg.getHeight(); //原始图像高

short[] srcBuf = new short[srcW * srcH]; //原始图片像素信息缓存

//srcBuf获取图片像素信息

Image desImg = Image.createImage(srcW, srcH);

if (srcImg.isMutable()) { /*如果是可变图像*/

DirectUtils.getDirectGraphics(srcImg.getGraphics()).

getPixels(srcBuf, 0, srcW, 0, 0, srcW, srcH, 444);

} else { /*如果是非可变图像*/

desImg.getGraphics().drawImage(srcImg, 0, 0, 0);

DirectUtils.getDirectGraphics(desImg.getGraphics()).

getPixels(srcBuf, 0, srcW, 0, 0, srcW, srcH, 444);

}

//计算插值表

short[] tabY = new short[desH];

short[] tabX = new short[desW];

int sb = 0;

int db = 0;

int tems = 0;

int temd = 0;

int distance = srcH > desH ? srcH : desH;

for (int i = 0; i <= distance; i++) { /*垂直方向*/

tabY[db] = (short) sb;

tems += srcH;

temd += desH;

if (tems > distance) {

tems -= distance;

sb++;

}

if (temd > distance) {

temd -= distance;

db++;

}

}

sb = 0;

db = 0;

tems = 0;

temd = 0;

distance = srcW > desW ? srcW : desW;

for (int i = 0; i <= distance; i++) { /*水平方向*/

tabX[db] = (short) sb;

tems += srcW;

temd += desW;

if (tems > distance) {

tems -= distance;

sb++;

}

if (temd > distance) {

temd -= distance;

db++;

}

}

//生成放大缩小后图形像素buf

short[] desBuf = new short[desW * desH];

int dx = 0;

int dy = 0;

int sx = 0;

int sy = 0;

int oldy = -1;

for (int i = 0; i < desH; i++) {

if (oldy == tabY[i]) {

System.arraycopy(desBuf, dy - desW, desBuf, dy, desW);

} else {

dx = 0;

for (int j = 0; j < desW; j++) {

desBuf[dy + dx] = srcBuf[sy + tabX[j]];

dx++;

}

sy += (tabY[i] - oldy) * srcW;

}

oldy = tabY[i];

dy += desW;

}

//生成图片

desImg = Image.createImage(desW, desH);

DirectUtils.getDirectGraphics(desImg.getGraphics()).

drawPixels(desBuf, true, 0, desW, 0, 0, desW, desH, 0, 444);

return desImg;

}

首先看函数的头两句,很容易,就是获取原始图片的宽度和高度

int srcW = srcImg.getWidth(); //原始图像宽

int srcH = srcImg.getHeight(); //原始图像高

接下来一句我们要定义一个short型数组,作为获取原始图片像素信息的缓存

short[] srcBuf = new short[srcW * srcH];

再下来一段,有的朋友可能会有些不明白,这里要解释一下。由于getPixels()这个函数,只能获取可变图像的像素信息,非可变图像,无法获取像素信息。所以我们要用srcImg.isMutable() 来判断,原始图像是不是可变图像,然后分两种情况来处理。如果srcImg是可变图像,我们就直接用getPixels()来获取它的像素信息,并保存在srcBuf里。如果srcImg不是可变图像,我们就需要把srcImage画到事先生成的可变图像desImg上,然后再获取desImg的像素信息。

Image desImg = Image.createImage(srcW, srcH);

if (srcImg.isMutable()) { /*如果是可变图像*/

DirectUtils.getDirectGraphics(srcImg.getGraphics()).

getPixels(srcBuf, 0, srcW, 0, 0, srcW, srcH, 444);

} else { /*如果是非可变图像*/

desImg.getGraphics().drawImage(srcImg, 0, 0, 0);

DirectUtils.getDirectGraphics(desImg.getGraphics()).

getPixels(srcBuf, 0, srcW, 0, 0, srcW, srcH, 444);

}

再往下就是缩放算法的重点:插值表的生成。插值表分水平差值表和垂直插值表,我们要分别生成原始图像矩阵的2种插值表,然后利用插值表生成放大缩小后的图像矩阵。由于这个内容比较抽象,很难用文字表述清楚,所以我们用实例进行介绍。

大家看下面这个水平的1*4的表格

-----------------

| 0 | 1 | 2 | 3 |

-----------------

如果要将这个表格放大成1*6的表格,放大的表格比原始表格多出了2个格子,我们只能对这多出来的2个格子进行插值,才能完成放大的操作。现在结合生成水平插值表的代码来完成这个过程。

distance = srcW > desW ? srcW : desW;

for (int i = 0; i <= distance; i++) { /*水平方向*/

tabX[db] = (short) sb;

tems += srcW;

temd += desW;

if (tems > distance) {

tems -= distance;

sb++;

}

if (temd > distance) {

temd -= distance;

db++;

}

}

很明显原始表格宽度srcW = 4;放大后的表格宽度desW = 6;所以distance = desW = 6

接下来进入for循环,我们一步步的演算其循环的过程

-----------------------------------------------

| i| tabX赋值操作| tems | temd| sb | db |

-----------------------------------------------

|0| tabX[0] = 0 | 4 | 6 | 0 | 0 |

-----------------------------------------------

|1| tabX[0] = 0 | 2 | 6 | 1 | 1 |

-----------------------------------------------

|2| tabX[0] = 1 | 6 | 6 | 1 | 2 |

-----------------------------------------------

|3| tabX[0] = 1 | 4 | 6 | 2 | 3 |

-----------------------------------------------

|4| tabX[0] = 2 | 2 | 6 | 3 | 4 |

-----------------------------------------------

|5| tabX[0] = 3 | 6 | 6 | 3 | 5 |

-----------------------------------------------

|6| tabX[0] = 3 | 4 | 6 | 4 | 6 |

-----------------------------------------------

有此得到放大后1*6的表格为下图所示。其中每一个单元格中的数字n表示这个单元格的内容,和原始表格中第n个单元格的内容一样。

--------------------------

| 0 | 1 | 1 | 2 | 3 | 3 |

--------------------------

例如,左图为原始表格,右图为放大的表格

--------------------- --------------------------------

| 红 | 绿 | 兰 | 紫 | | 红 | 绿 | 绿 | 兰 | 紫 | 紫 |

--------------------- --------------------------------

同样,垂直方向的插值表我们也可以用相同的方法获得。

有了2个插值表,下面就可以生成放大和缩小后的图像了。

short[] desBuf = new short[desW * desH];

int dx = 0;

int dy = 0;

int sx = 0;

int sy = 0;

int oldy = -1;

for (int i = 0; i < desH; i++) {

if (oldy == tabY[i]) { /**********情况一**********/

System.arraycopy(desBuf, dy - desW, desBuf, dy, desW);

} else { /**********情况二**********/

dx = 0;

for (int j = 0; j < desW; j++) {

desBuf[dy + dx] = srcBuf[sy + tabX[j]];

dx++;

}

sy += (tabY[i] - oldy) * srcW;

}

oldy = tabY[i];

dy += desW;

}

desBuf是用来保存放大缩小后的图像数据。例如我们把一个4*4像素的图像A放大成6*6的图像B,据前面的介绍我们可以生成2个插值表。tabX = {0,1,1,2,3,3},tabY = {0,1,1,2,3,3}。

在循环中会判断是否oldy 等于 tabY[i],这个操作等同于tabY[i-1]是否等于tabY[i]。如果等于,表示图像B前一行已经生成的数据和即将要生成的第i行数据相同,则只要执行System.arraycopy(desBuf, dy - desW, desBuf, dy, desW)把上一行的数据复制过来即可;如果不等,则需要对照水平插值表tabX生成这一行的数据。

算法演示过程如下:

----------------------------

| i|oldy|tabY[i]|运算情况|

----------------------------

|0|-1 | 0 | 情况2 |

----------------------------

|1| 0 | 1 | 情况2 |

----------------------------

|2| 1 | 1 | 情况1 |

----------------------------

|3| 1 | 2 | 情况2 |

----------------------------

|4| 2 | 3 | 情况2 |

----------------------------

|5| 3 | 3 | 情况1 |

----------------------------

然后我们用desBuf生成最终放大或缩小后的图片

desImg = Image.createImage(desW, desH);

DirectUtils.getDirectGraphics(desImg.getGraphics()).

drawPixels(desBuf, true, 0, desW, 0, 0, desW, desH, 0, 444);

return desImg;

下面是采用此算法实现放大缩小的效果图

最后要说明一点的是,由于该算法中使用了Image.createImage(w, h)来创建图像,这个函数会创建一个w*h像素的全白可变图像,所以透明图片放大缩小后,背景不再透明,而是白色了,这是j2me本身的缺憾和算法没有关系。对于这个问题,有一个解决办法,就是程序的图片不采用Image来保存,而是采用short[]数组保存,画图的时候用drawPixels()来画图。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有