OpenCV4Android实现图像二值化

原帖地址: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);
	};

}

JNI接口程序bitmapbinary如下:

#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();
}

其中返回int类型的程序与实现图像二值化无关,特此说明。

效果如下所示:

OpenCV4Android实现图像二值化_第1张图片


点击按钮后效果如下:

OpenCV4Android实现图像二值化_第2张图片

你可能感兴趣的:(OpenCV,Android)