java 图片插值二值化_图像基本处理算法的简单实现(一)

本文介绍了Java中使用JNI进行图像处理的基本算法实现,包括最邻近插值法的图像缩放、灰度化转换以及基于Otsu算法的二值化处理。详细阐述了双线性内插值的缩放过程,以及灰度化和二值化的数学原理和代码实现。
摘要由CSDN通过智能技术生成

图像基本处理算法的简单实现(一)

一、引言

图像处理基本算法整理。

拿来举例的实现代码是在JNI方法内直接实现的,且传入参数为int[]颜色值,返回为新的int[]颜色值,可能头上还包括了长宽。(很丑,见谅T^T)

2.2的NDK提供了Bitmap.h,这种方式可参考《Android NDK基础样例》的样例3,灰度化图像(Bitmap作为参数)。

二、目录

1)缩放算法

据说有最邻近插值、双线性内插值、高阶插值、三次卷积法等等。(我已经晕了~)

缩放是从原图像->目标图像的过程。目标图像的新颜色值,由图像长宽比反向计算在原图像的位置,从而获得。反向计算得到的坐标一般为浮点坐标,表示为(i+u,j+v)(i,j整数整数、u,v小数部分)。

1)最邻近插值:取(i,j)的颜色值即可,效果不咋的==

2)双线性内插值:由(i,j)、(i+1,j)、(i,j+1)、(i+1,j+1)四点距(i+u,j+v)远近计算比例求得(四领域乘以相应的权重)。效果不错了哈==

公式:f(i+u,j+v)=(1-u)(1-v)*f(i,j)+(1-u)v*f(i,j+1)+u(1-v)*f(i+1,j)+uv*f(i+1,j+1)

复制:双线性内插值具有低通滤波器性质,使高频风量受损,可能会使图像轮廓在一定程度上变得模糊。尤其放大处理,影响将更为明显。

3)高阶插值、三次卷积法等:说是双线性使细节柔化、会有锯齿什么的。这些算法就是能够更好的修正这些不足,但计算量更大==。(高阶插值没搜索到具体算法啊,是指一类概念么?双三次插值属于高阶插值这类的意思?)

双线性内插值的实现:

intmin(intx,inty) {

return(x <= y) ? x : y;

}

intalpha(intcolor) {

return(color >> 24) & 0xFF;

}

intred(intcolor) {

return(color >> 16) & 0xFF;

}

intgreen(intcolor) {

return(color >> 8) & 0xFF;

}

intblue(intcolor) {

returncolor & 0xFF;

}

intARGB(intalpha,intred,intgreen,intblue) {

return(alpha <

}

/**

* 按双线性内插值算法将对应源图像四点颜色某一颜色值混合

*

* int(*fun)(int)指向从color中获取某一颜色值的方法

*/

intmixARGB(int*color,inti,intj,floatu,floatv,int(*fun)(int)) {

// f(i+u,j+v)=(1-u)(1-v)*f(i,j)+(1-u)v*f(i,j+1)+u(1-v)*f(i+1,j)+uv*f(i+1,j+1)

return(1 - u) * (1 - v) * (*fun)(color[0]) + (1 - u) * v * (*fun)(color[1])

+ u * (1 - v) * (*fun)(color[2]) + u * v * (*fun)(color[3]);

}

/**

* 按双线性内插值算法将对应源图像四点颜色值混合

*

* color[]需要有四个颜色值,避免越界

*/

intmixColor(int*color,inti,intj,floatu,floatv) {

inta = mixARGB(color, i, j, u, v, alpha);// 获取alpha混合值

intr = mixARGB(color, i, j, u, v, red);// 获取red混合值

intg = mixARGB(color, i, j, u, v, green);// 获取green混合值

intb = mixARGB(color, i, j, u, v, blue);// 获取blue混合值

returnARGB(a, r, g, b);

}

/**

* 将Bitmap缩放后返回(双线性内插值算法)

*

* JNIEnv*  jni环境(jni必要参数)

* jobject  java对象(jni必要参数)

* jintArray    Bitmap所有像素值

* int  Bitmap宽度

* int  Bitmap高度

* int  Bitmap新宽度

* int  Bitmap新高度

*/

JNIEXPORT jintArray JNICALL Java_org_join_p_w_picpath_util_JoinImage_stretch(

JNIEnv* env, jobject obj, jintArray buf, intsrcW,intsrcH,intdstW,

intdstH) {

LOGE("==stretch==");

jint * cbuf;

cbuf = (*env)->GetIntArrayElements(env, buf, 0); // 获取int数组元素

intnewSize = dstW * dstH;

jint rbuf[newSize]; // 新图像像素值

floatrateH = (float) srcH / dstH;// 高度缩放比例

floatrateW = (float) srcW / dstW;// 宽度缩放比例

intdstX, dstY;// 目标图像XY坐标

floatsrcX, srcY;// 目标图像对应源图像XY坐标

inti, j;// 对应源图像XY坐标整数部分

inti1, j1;// 对应源图像XY坐标整数部分+1

floatu, v;// 对应源图像XY坐标小数部分

intcolor[4];// f(i+u,j+v)对应源图像(i,j)、(i+1,j)、(i,j+1)、(i+1,j+1)的像素值

for(dstY = 0; dstY <= dstH - 1; dstY++) {

srcY = dstY * rateH; // 对应源图像Y坐标

j = (int) srcY;// 对应源图像Y坐标整数部分

j1 = min(j + 1, srcH - 1); // 对应源图像Y坐标整数部分+1

v = srcY - j; // 对应源图像Y坐标小数部分

for(dstX = 0; dstX <= dstW - 1; dstX++) {

srcX = dstX * rateW; // 对应源图像X坐标

i = (int) srcX;// 对应源图像X坐标整数部分

i1 = min(i + 1, srcW - 1); // 对应源图像X坐标整数部分+1

u = srcX - i; // 对应源图像X坐标小数部分

// 双线性内插值算法(注意ARGB时,需要分别由插值算法求得后重组):

// f(i+u,j+v)=(1-u)(1-v)*f(i,j)+(1-u)v*f(i,j+1)+u(1-v)*f(i+1,j)+uv*f(i+1,j+1)

color[0] = cbuf[j * srcW + i]; // f(i,j)颜色值

color[1] = cbuf[j1 * srcW + i]; // f(i,j+1)颜色值

color[2] = cbuf[j * srcW + i1]; // f(i+1,j)颜色值

color[3] = cbuf[j1 * srcW + i1]; // f(i+1,j+1)颜色值

// 给目标图像赋值为双线性内插值求得的混合色

rbuf[dstY * dstW + dstX] = mixColor(color, i, j, u, v);

}

}

jintArray result = (*env)->NewIntArray(env, newSize); // 新建一个jintArray

(*env)->SetIntArrayRegion(env, result, 0, newSize, rbuf); // 将rbuf转存入result

(*env)->ReleaseIntArrayElements(env, buf, cbuf, 0); // 释放int数组元素

returnresult;

}

2)灰度化

把图像变灰,有好些方法,求RGB平均值啊,RGB最大值啊什么的。不过还是建议按规范的标准来。

彩色转灰度的著名心理学公式:Gray = R*0.299 + G*0.587 + B*0.114(话说我心理学与生活一本书都看完了也没提到这公式啊==)

实际应用中为了避免浮点运算,然后就有了移位运算代替了。

2至20位精度的系数:

Gray = (R*1 + G*2 + B*1) >> 2

Gray = (R*2 + G*5 + B*1) >> 3

Gray = (R*4 + G*10 + B*2) >> 4

Gray = (R*9 + G*19 + B*4) >> 5

Gray = (R*19 + G*37 + B*8) >> 6

Gray = (R*38 + G*75 + B*15) >> 7

Gray = (R*76 + G*150 + B*30) >> 8

Gray = (R*153 + G*300 + B*59) >> 9

Gray = (R*306 + G*601 + B*117) >> 10

Gray = (R*612 + G*1202 + B*234) >> 11

Gray = (R*1224 + G*2405 + B*467) >> 12

Gray = (R*2449 + G*4809 + B*934) >> 13

Gray = (R*4898 + G*9618 + B*1868) >> 14

Gray = (R*9797 + G*19235 + B*3736) >> 15

Gray = (R*19595 + G*38469 + B*7472) >> 16

Gray = (R*39190 + G*76939 + B*14943) >> 17

Gray = (R*78381 + G*153878 + B*29885) >> 18

Gray = (R*156762 + G*307757 + B*59769) >> 19

Gray = (R*313524 + G*615514 + B*119538) >> 20

3与4、7与8、10与11、13与14、19与20的精度说是一样的==。16位运算下最好的计算公式是使用7位精度。而游戏由于场景经常变化,用户感觉不到,最常用2位精度。

灰度化实现:

JNIEXPORT jintArray JNICALL Java_org_join_p_w_picpath_util_JoinImage_imgToGray(

JNIEnv* env, jobject obj, jintArray buf, intw,inth) {

LOGE("==imgToGray==");

jint * cbuf;

cbuf = (*env)->GetIntArrayElements(env, buf, 0); // 获取int数组元素

intalpha = 0xFF;// 不透明值

inti, j, color, red, green, blue;

for(i = 0; i 

for(j = 0; j 

color = cbuf[w * i + j]; // 获得color值

red = (color >> 16) & 0xFF; // 获得red值

green = (color >> 8) & 0xFF; // 获得green值

blue = color & 0xFF; // 获得blue值

color = (red * 38 + green * 75 + blue * 15) >> 7; // 灰度算法(16位运算下7位精度)

color = (alpha <

cbuf[w * i + j] = color; // 设置新color值

}

}

intsize = w * h;

jintArray result = (*env)->NewIntArray(env, size); // 新建一个jintArray

(*env)->SetIntArrayRegion(env, result, 0, size, cbuf); // 将cbuf转存入result

(*env)->ReleaseIntArrayElements(env, buf, cbuf, 0); // 释放int数组元素

returnresult;

}

3)二值化

灰度值[0,255]和一阈值比较,变成0或255,要么纯黑要么纯白==。但是阈值的获取就牵扯算法了。只知道有Otsu、Bernsen…,具体算法查下就好^^

Otsu:最大类间方差法,整体算出一个阈值。计算次数少但抗干扰性差,适合光照均匀的图像。

Bernsen:局部阈值法,在一点周围一定范围内(相当于一窗口)计算出一阈值。计算次数多但抗干扰性强,用于非均匀光照的图像。

二值化(Otsu)的实现:

/**

* 将灰度化Bitmap各像素值二值化后返回

*

* JNIEnv*  jni环境(jni必要参数)

* jobject  java对象(jni必要参数)

* jintArray    Bitmap所有像素值

* int  Bitmap宽度

* int  Bitmap高度

*/

JNIEXPORT jintArray JNICALL Java_org_join_p_w_picpath_util_JoinImage_binarization(

JNIEnv* env, jobject obj, jintArray buf, intw,inth) {

LOGE("==binarization==");

jint * cbuf;

cbuf = (*env)->GetIntArrayElements(env, buf, 0); // 获取int数组元素

intwhite = 0xFFFFFFFF;// 不透明白色

intblack = 0xFF000000;// 不透明黑色

intthresh = otsu(cbuf, w, h);// OTSU获取分割阀值

LOGE("==[阀值=%d]==", thresh);

inti, j, gray;

for(i = 0; i 

for(j = 0; j 

gray = (cbuf[w * i + j]) & 0xFF; // 获得灰度值(red=green=blue)

if(gray 

cbuf[w * i + j] = white; // 小于阀值设置为白色(前景)

} else{

cbuf[w * i + j] = black; // 否则设置为黑色(背景)

}

}

}

intsize = w * h;

jintArray result = (*env)->NewIntArray(env, size); // 新建一个jintArray

(*env)->SetIntArrayRegion(env, result, 0, size, cbuf); // 将cbuf转存入result

(*env)->ReleaseIntArrayElements(env, buf, cbuf, 0); // 释放int数组元素

returnresult;

}

/**

* OTSU算法求最适分割阈值

*/

intotsu(jint* colors,intw,inth) {

unsigned intpixelNum[256];// 图象灰度直方图[0, 255]

intcolor;// 灰度值

intn, n0, n1;//  图像总点数,前景点数, 后景点数(n0 + n1 = n)

intw0, w1;// 前景所占比例, 后景所占比例(w0 = n0 / n, w0 + w1 = 1)

doubleu, u0, u1;// 总平均灰度,前景平均灰度,后景平均灰度(u = w0 * u0 + w1 * u1)

doubleg, gMax;// 图像类间方差,最大类间方差(g = w0*(u0-u)^2+w1*(u1-u)^2 = w0*w1*(u0-u1)^2)

doublesum_u, sum_u0, sum_u1;// 图像灰度总和,前景灰度总和, 后景平均总和(sum_u = n * u)

intthresh;// 阈值

memset(pixelNum, 0, 256 * sizeof(unsignedint));// 数组置0

// 统计各灰度数目

inti, j;

for(i = 0; i 

for(j = 0; j 

color = (colors[w * i + j]) & 0xFF; // 获得灰度值

pixelNum[color]++; // 相应灰度数目加1

}

}

// 图像总点数

n = w * h;

// 计算总灰度

intk;

for(k = 0; k <= 255; k++) {

sum_u += k * pixelNum[k];

}

// 遍历判断最大类间方差,得到最佳阈值

for(k = 0; k <= 255; k++) {

n0 += pixelNum[k]; // 图像前景点数

if(0 == n0) {// 未获取前景,直接继续增加前景点数

continue;

}

if(n == n0) {// 前景点数包括了全部时,不可能再增加,退出循环

break;

}

n1 = n - n0; // 图像后景点数

sum_u0 += k * pixelNum[k]; // 前景灰度总和

u0 = sum_u0 / n0; // 前景平均灰度

u1 = (sum_u - sum_u0) / n1; // 后景平均灰度

g = n0 * n1 * (u0 - u1) * (u0 - u1); // 类间方差(少除了n^2)

if(g > gMax) {// 大于最大类间方差时

gMax = g; // 设置最大类间方差

thresh = k; // 取最大类间方差时对应的灰度的k就是最佳阈值

}

}

returnthresh;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值