原帖地址:http://vaero.blog.51cto.com/4350852/822997
最近在学习OpenCV4Android,需要实现对图像二值化处理,找了很多的资料和文献都没有找到需要的,还好看到了winorlose2000 写的文章,这里表示感谢!接下来贴出自己的源代码,希望和大家交流,不妥之处还请指正啊!
程序调用了OpenCV4Android 2.4.9,低版本应该也可以实现。
使用JNI编程。
package com.example.bitmapbinary;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Bitmap.Config;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener {
public Button bt1, bt2;
public ImageView imageView1;
public TextView textView1;
public Bitmap map;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bt1 = (Button) findViewById(R.id.bt1);
bt2 = (Button) findViewById(R.id.bt2);
imageView1 = (ImageView) findViewById(R.id.image1);
textView1 = (TextView) findViewById(R.id.tv1);
textView1.setText("个数:");
bt1.setOnClickListener(this);
bt2.setOnClickListener(this);
// 灰度化图片,显示
map = BitmapFactory.decodeResource(getResources(), R.drawable.lena0);
imageView1.setImageBitmap(map);
}
public Bitmap rotate(Bitmap ori,int degree){
Matrix matrix = new Matrix();
matrix.postRotate(degree);
Bitmap rotateBitmap = Bitmap.createBitmap(ori, 0, 0,
ori.getWidth(), ori.getHeight(), matrix, true);
if(rotateBitmap != ori)
{
ori.recycle();
ori = rotateBitmap;
}
return ori;
}
@Override
public void onClick(View v) {
if (v == bt1) {
//map=rotate(map,270);//旋转图片而已
int w = map.getWidth();
int h = map.getHeight();
int[] piexl = new int[w * h];
map.getPixels(piexl, 0, w, 0, 0, w, h);// 检索指定坐标点的GRB像素值
int result[] = ImageProc.imgbinary(piexl, w, h);
Bitmap resultmap = Bitmap.createBitmap(w, h, Config.ARGB_8888);
resultmap.setPixels(result, 0, w, 0, 0, w, h);
imageView1.setImageBitmap(resultmap);
// show轮廓个数
textView1.setText(w * h + "个数:" + ImageProc.num(piexl, w, h));
} else if (v == bt2) {
imageView1.setImageBitmap(map);
}
}
// OpenCV类库加载并初始化成功后的回调函数,在此我们不进行任何操作
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS: {
System.loadLibrary("bitmapbinary");
}
break;
default: {
super.onManagerConnected(status);
}
break;
}
}
};
protected void onResume() {
super.onResume();
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this,
mLoaderCallback);
};
}
#include
#include
#include
#include
using namespace cv;
int otsu2(jint* colors, int w, int h) {
unsigned int pixelNum[256]; // 图象灰度直方图[0, 255]
int color; // 灰度值
int n, n0, n1; // 图像总点数,前景点数, 后景点数(n0 + n1 = n)
int w0, w1; // 前景所占比例, 后景所占比例(w0 = n0 / n, w0 + w1 = 1)
double u, u0, u1; // 总平均灰度,前景平均灰度,后景平均灰度(u = w0 * u0 + w1 * u1)
double g, gMax; // 图像类间方差,最大类间方差(g = w0*(u0-u)^2+w1*(u1-u)^2 = w0*w1*(u0-u1)^2)
double sum_u, sum_u0, sum_u1; // 图像灰度总和,前景灰度总和, 后景平均总和(sum_u = n * u)
int thresh; // 阈值
memset(pixelNum, 0, 256 * sizeof(unsigned int)); // 数组置0
// 统计各灰度数目
int i, j;
for (i = 0; i < h; i++) {
for (j = 0; j < w; j++) {
color = (colors[w * i + j]) & 0xFF; // 获得灰度值
pixelNum[color]++; // 相应灰度数目加1
}
}
// 图像总点数
n = w * h;
// 计算总灰度
int k;
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就是最佳阈值
}
}
return thresh;
}
JNIEXPORT jintArray JNICALL Java_com_example_bitmapbinary_ImageProc_imgbinary(
JNIEnv* env, jclass obj, jintArray buf, jint w, jint h) {
//图像灰度化的源程序
jint *cbuf;
cbuf = env->GetIntArrayElements(buf, false);
if (cbuf == NULL) {
return 0;
}
//threshold(src,myimg,0,255,THRESH_OTSU);
int white = 0xFFFFFFFF; // 不透明白色
int black = 0xFF000000; // 不透明黑色
int thresh = otsu2(cbuf, w, h); // OTSU获取分割阀值
int i, j, gray;
for (i = 0; i < h; i++) {
for (j = 0; j < w; j++) {
gray = (cbuf[w * i + j]) & 0xFF; // 获得灰度值(red=green=blue)
if (gray < thresh) {
cbuf[w * i + j] = white; // 小于阀值设置为白色(前景)
} else {
cbuf[w * i + j] = black; // 否则设置为黑色(背景)
}
}
}
int size = w * h;
jintArray result = env->NewIntArray(size);//为其分配空间
env->SetIntArrayRegion(result, 0, size, cbuf);//为result赋值
env->ReleaseIntArrayElements(buf, cbuf, 0);
return result;
}
JNIEXPORT jint JNICALL Java_com_example_bitmapbinary_ImageProc_num(JNIEnv *env,
jclass obj, jintArray buf, jint w, jint h) {
jint *cbuf;
cbuf = env->GetIntArrayElements(buf, false);
if (cbuf == NULL) {
return 0;
}
Mat mydata(h, w, CV_8UC1, (unsigned char*) cbuf);
vector < vector > contours;
vector < Vec4i > hierarchy;
findContours(mydata, contours, hierarchy, CV_RETR_CCOMP,
CV_CHAIN_APPROX_SIMPLE);
//事实证明,这样得到的个数不是轮廓的个数
int j = contours.size();
return contours.size();
}
效果如下所示:
点击按钮后效果如下: