OpenCV4Android實現圖像二值化

原帖地址:http://vaero.blog.51cto.com/4350852/822997

最近在學習OpenCV4Android,需要實現對圖像二值化處理,找了很多的資料和文獻都沒有找到需要的,還好看到了winorlose2000 寫的文章,這裏表示感謝!接下來貼出自己的源代碼,希望和大家交流,不妥之處還請指正啊!


程序調用了OpenCV4Android 2.4.9,低版本應該也可以實現。

使用JNI編程。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        >
        <Button
        android:id="@+id/bt1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#4B0082"
        android:text="@string/bt1" />
        <Button
        android:id="@+id/bt2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#000080"
        android:text="還原" />
    </LinearLayout>
    

    <TextView
        android:id="@+id/tv1"
        android:background="#FFF8DC"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

    <ImageView
        android:id="@+id/image1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

主程序如下:

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<jni.h>
#include <ImageProc.h>
#include<opencv2/opencv.hpp>
#include <stdlib.h>

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<Point> > contours;
	vector < Vec4i > hierarchy;
	findContours(mydata, contours, hierarchy, CV_RETR_CCOMP,
			CV_CHAIN_APPROX_SIMPLE);

	//事實證明,這樣得到的個數不是輪廓的個數
	int j = contours.size();
	return contours.size();
}

其中返回int類型的程序與實現圖像二值化無關,特此說明。

效果如下所示:



點擊按鈕後效果如下:


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章