前段时间接触了一些数字图像处理的问题在位师兄的指导下在JME平台完成了一些基本的D图像处理算法就当是对这段知识做一下总结决定把这些算法写出来和各位朋友共同探讨这篇文章先介绍图像放大缩小的实现程序是以Nokia S的机器为平台实现的
实现图形缩放的基本思想
图像的变形变换简单的说就是把源图像每个点坐标通过变形运算转为目标图像相应点的新坐标但是这样会导致一个问题就是目标点的坐标通常不会是整数所以我们在做放大变换时需要计算生成没有被映射到的点;而在缩小变换时需要删除一些点这里我们采用最简单的一种插值算法最近邻域法顾名思义就是把非整数坐标作一个四捨五入取最近的整数点
看下面的一个图片放大的例子左图为原始图像右图为放大倍的图像里面的数字表示所在像素的信息
对于图片像素的操作
获取Image图片像素信息
标准的midp没有提供获取图片像素信息的函数对于NOKIA的机器我们可以采用Nokia SDK提供的API获取像素信息具体程序如下
g=imagegetGraphics()
DirectGraphicsdg=DirectUtilsgetDirectGraphics(g);
dggetPixels(short[]pixelsintoffsetintscanlengthintxintyintwidthintheightintformat)
参数介绍
short[] pixels 用于接收像素信息的数组
int offset这篇文章中的用到的地方添就可以了
int scanlength添图片的宽度就行了
int x添
int y添
int width图片宽度
int height图片高度
int format表示图形格式好像Nokia S的机器都是采用格式表示RGB颜色的就是红绿蓝各用位表示至于可以表示透明色ARGB的格式应该是机器硬件实现的
想具体了解Nokia SDK的信息可以查看Nokia SDK的帮助文档
使用像素信息数组生成Image图片
image=ImagecreateImage(wh);
g=imagegetGraphics()
DirectGraphicsdg=DirectUtilsgetDirectGraphics(g);
dgdrawPixels(short[]pixelsbooleantransparencyintoffsetintscanlengthintxintyintwidthintheightintmanipulationintformat)
short[]pixels像素信息数组
booleantransparency是否包含alpha位信息
intoffset添
intscanlength添图片的宽度就行了
intx添
inty添
intwidth图片宽度
intheight图片高度
intmanipulation添
intformat
下面开始介绍具体的算法首先给出图像缩放的完整函数然后对代码分段进行解释
/*********************************
* @todo 图片放大缩小
* @param srcImg 原始图片
* @param desW 变化后图片的宽
* @param desH 变化后图片的高
* @return 处理后的图片
*********************************/
privateImageZoomImage(ImagesrcImgintdesWintdesH){
intsrcW=srcImggetWidth();//原始图像宽
intsrcH=srcImggetHeight();//原始图像高
short[]srcBuf=newshort[srcW*srcH];//原始图片像素信息缓存
//srcBuf获取图片像素信息
ImagedesImg=ImagecreateImage(srcWsrcH);
if(srcImgisMutable()){/*如果是可变图像*/
DirectUtilsgetDirectGraphics(srcImggetGraphics())
getPixels(srcBufsrcWsrcWsrcH);
}else{/*如果是非可变图像*/
desImggetGraphics()drawImage(srcImg);
DirectUtilsgetDirectGraphics(desImggetGraphics())
getPixels(srcBufsrcWsrcWsrcH);
}
//计算插值表
short[]tabY=newshort[desH];
short[]tabX=newshort[desW];
intsb=;
intdb=;
inttems=;
inttemd=;
intdistance=srcH>desH?srcH:desH;
for(inti=;i<=distance;i++){/*垂直方向*/
tabY[db]=(short)sb;
tems+=srcH;
temd+=desH;
if(tems>distance){
tems=distance;
sb++;
}
if(temd>distance){
temd=distance;
db++;
}
}
sb=;
db=;
tems=;
temd=;
distance=srcW>desW?srcW:desW;
for(inti=;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=newshort[desW*desH];
intdx=;
intdy=;
intsx=;
intsy=;
intoldy=;
for(inti=;i<desH;i++){
if(oldy==tabY[i]){
Systemarraycopy(desBufdydesWdesBufdydesW);
}else{
dx=;
for(intj=;j<desW;j++){
desBuf[dy+dx]=srcBuf[sy+tabX[j]];
dx++;
}
sy+=(tabY[i]oldy)*srcW;
}
oldy=tabY[i];
dy+=desW;
}
//生成图片
desImg=ImagecreateImage(desWdesH);
DirectUtilsgetDirectGraphics(desImggetGraphics())
drawPixels(desBuftruedesWdesWdesH);
returndesImg;
}
首先看函数的头两句很容易就是获取原始图片的宽度和高度
intsrcW=srcImggetWidth();//原始图像宽
intsrcH=srcImggetHeight();//原始图像高
接下来一句我们要定义一个short型数组作为获取原始图片像素信息的缓存
short[]srcBuf=newshort[srcW*srcH];
再下来一段有的朋友可能会有些不明白这里要解释一下由于getPixels()这个函数只能获取可变图像的像素信息非可变图像无法获取像素信息所以我们要用srcImgisMutable() 来判断原始图像是不是可变图像然后分两种情况来处理如果srcImg是可变图像我们就直接用getPixels()来获取它的像素信息并保存在srcBuf里如果srcImg不是可变图像我们就需要把srcImage画到事先生成的可变图像desImg上然后再获取desImg的像素信息
ImagedesImg=ImagecreateImage(srcWsrcH);
if(srcImgisMutable()){/*如果是可变图像*/
DirectUtilsgetDirectGraphics(srcImggetGraphics())
getPixels(srcBufsrcWsrcWsrcH);
}else{/*如果是非可变图像*/
desImggetGraphics()drawImage(srcImg);
DirectUtilsgetDirectGraphics(desImggetGraphics())
getPixels(srcBufsrcWsrcWsrcH);
}
再往下就是缩放算法的重点插值表的生成插值表分水平差值表和垂直插值表我们要分别生成原始图像矩阵的种插值表然后利用插值表生成放大缩小后的图像矩阵由于这个内容比较抽象很难用文字表述清楚所以我们用实例进行介绍
大家看下面这个水平的*的表格
| | | | |
如果要将这个表格放大成*的表格放大的表格比原始表格多出了个格子我们只能对这多出来的个格子进行插值才能完成放大的操作现在结合生成水平插值表的代码来完成这个过程
distance=srcW>desW?srcW:desW;
for(inti=;i<=distance;i++){/*水平方向*/
tabX[db]=(short)sb;
tems+=srcW;
temd+=desW;
if(tems>distance){
tems=distance;
sb++;
}
if(temd>distance){
temd=distance;
db++;
}
}
很明显原始表格宽度srcW = ;放大后的表格宽度desW = ;所以distance = desW =
接下来进入for循环我们一步步的演算其循环的过程
| i| tabX赋值操作| tems | temd| sb | db |
|| tabX[] = | | | | |
|| tabX[] = | | | | |
|| tabX[] = | | | | |
|| tabX[] = | | | | |
|| tabX[] = | | | | |
|| tabX[] = | | | | |
|| tabX[] = | | | | |
有此得到放大后*的表格为下图所示其中每一个单元格中的数字n表示这个单元格的内容和原始表格中第n个单元格的内容一样
| | | | | | |
例如左图为原始表格右图为放大的表格
| 红 | 绿 | 兰 | 紫 | | 红 | 绿 | 绿 | 兰 | 紫 | 紫 |
同样垂直方向的插值表我们也可以用相同的方法获得
有了个插值表下面就可以生成放大和缩小后的图像了
short[]desBuf=newshort[desW*desH];
intdx=;
intdy=;
intsx=;
intsy=;
intoldy=;
for(inti=;i<desH;i++){
if(oldy==tabY[i]){/**********情况一**********/
Systemarraycopy(desBufdydesWdesBufdydesW);
}else{/**********情况二**********/
dx=;
for(intj=;j<desW;j++){
desBuf[dy+dx]=srcBuf[sy+tabX[j]];
dx++;
}
sy+=(tabY[i]oldy)*srcW;
}
oldy=tabY[i];
dy+=desW;
}
desBuf是用来保存放大缩小后的图像数据例如我们把一个*像素的图像A放大成*的图像B据前面的介绍我们可以生成个插值表tabX = {}tabY = {}
在循环中会判断是否oldy 等于 tabY[i]这个操作等同于tabY[i]是否等于tabY[i]如果等于表示图像B前一行已经生成的数据和即将要生成的第i行数据相同则只要执行Systemarraycopy(desBuf dy desW desBuf dy desW)把上一行的数据复制过来即可;如果不等则需要对照水平插值表tabX生成这一行的数据
算法演示过程如下
| i|oldy|tabY[i]|运算情况|
|| | | 情况 |
|| | | 情况 |
|| | | 情况 |
|| | | 情况 |
|| | | 情况 |
|| | | 情况 |
然后我们用desBuf生成最终放大或缩小后的图片
desImg=ImagecreateImage(desWdesH);
DirectUtilsgetDirectGraphics(desImggetGraphics())
drawPixels(desBuftruedesWdesWdesH);
returndesImg;
最后要说明一点的是由于该算法中使用了ImagecreateImage(w h)来创建图像这个函数会创建一个w*h像素的全白可变图像所以透明图片放大缩小后背景不再透明而是白色了这是JME本身的缺憾和算法没有关系对于这个问题有一个解决办法就是程序的图片不采用Image来保存而是采用short[]数组保存画图的时候用drawPixels()来画图