这里直接给个链接:https://blog.csdn.net/qq402335257/article/details/52884525
一个opencv的学习网站:http://www.opencv.org.cn/
二、建立项目
1.整体思路:
(1):点击拍照,调用手机摄像头拍照并保存。
(2):调用刚才拍的照片进行处理。
(3):在界面显示结果。
2.具体步骤:
(1)调用手机摄像头拍照保存并读取的相关权限:
<uses-permission android:name="android.permission.CAMERA" />
//摄像头权限
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<Button
android:text="照相"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/btn_gray_process"
android:layout_alignStart="@+id/btn_gray_process"
android:id="@+id/take_photo" />
<ImageView
android:id="@+id/picture"
android:layout_width="match_parent"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="16dp"
android:layout_height="wrap_content" />
<TextView
android:text="手指个数"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_alignBottom="@+id/take_photo"
android:layout_alignParentEnd="true"
android:layout_marginEnd="91dp"
android:id="@+id/textView"
android:layout_alignParentTop="true" />
package com.example.yanhuojun.handgesture1;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.example.yanhuojun.handgesture1.R;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.android.Utils;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfInt4;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.imgproc.Moments;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Vector;
import static java.lang.Math.abs;
import static org.opencv.core.CvType.CV_8UC3;
import static org.opencv.imgproc.Imgproc.THRESH_BINARY;
import static org.opencv.imgproc.Imgproc.circle;
import static org.opencv.imgproc.Imgproc.line;
import static org.opencv.imgproc.Imgproc.moments;
//public class MainActivity extends AppCompatActivity {
public class MainActivity extends Activity {
public static final int TAKE_PHOTO = 1;
//public static final int CROP_PHOTO = 2;
private TextView textView;
private Button takephoto;
private ImageView picture;
private Uri imageUri;
double treash=100;
Mat imageMat;
private static final String TAG = "OCVSample::Activity";
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS:
{
Log.i(TAG, "OpenCV loaded successfully");
imageMat=new Mat();
} break;
default:
{
super.onManagerConnected(status);
} break;
}
}
};
@Override
public void onResume()
{
super.onResume();
if (!OpenCVLoader.initDebug()) {
Log.d("OpenCV", "Internal OpenCV library not found. Using OpenCV Manager for initialization");
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_10, this, mLoaderCallback);
} else {
Log.d("OpenCV", "OpenCV library found inside package. Using it!");
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
takephoto = (Button)findViewById(R.id.take_photo);
textView =(TextView) findViewById(R.id.textView);
picture = (ImageView)findViewById(R.id.picture);
takephoto.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
File outputImage = new File(Environment.getExternalStorageDirectory(), "output_image.jpg");
try {
if (outputImage.exists()) {
outputImage.delete();
}
outputImage.createNewFile();
}catch (IOException e){
e.printStackTrace();
}
imageUri = Uri.fromFile(outputImage);
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");//调用系统相机拍照
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, TAKE_PHOTO);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
switch (requestCode){
case TAKE_PHOTO:
//if (resultCode == RESULT_OK){
//Intent intent = new Intent("com.android.camera.action.CROP");
//intent.setDataAndType(imageUri, "image/*");
// intent.putExtra("scale", true);
//intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
//startActivityForResult(intent, CROP_PHOTO);
//}
//break;
//case CROP_PHOTO:
if (resultCode == RESULT_OK){
try{
//获取原图
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
picture.setImageBitmap(bitmap);//设置显示图像
//图像处理
Mat mat_src = new Mat(bitmap.getHeight(), bitmap.getWidth(), CvType.CV_8UC4);//获取原图(8位无符号的四通道,带透明的RGB图像)
Utils.bitmapToMat(bitmap, mat_src);//将bitmap转化成mat
Mat mat_gray = new Mat(mat_src.cols(), mat_src.rows(), CvType.CV_8UC1);//灰度图,单通道
Imgproc.cvtColor(mat_src, mat_gray, Imgproc.COLOR_BGRA2GRAY, 1);//转变颜色
//-------------------------------------------------------------------
Mat frame = new Mat(bitmap.getHeight(), bitmap.getWidth(), CvType.CV_8UC4);//取出原图
mat_src.copyTo(frame);//复制,不会牵连改变
Mat frameHSV = new Mat(mat_src.cols(), mat_src.rows(), CV_8UC3);//三通道的RGB图像
; // hsv空间(色调,饱和度,明度)
Mat mask = new Mat(mat_src.cols(), mat_src.rows(), CvType.CV_8UC1);
Mat dst = new Mat(mat_src.cols(), mat_src.rows(), CV_8UC3); // 输出图像
//dst.copyTo(frame);
// 中值滤波,去除椒盐噪声
Imgproc.medianBlur(frame, frame, 5);
Imgproc.cvtColor(frame, frameHSV, Imgproc.COLOR_RGB2HSV, 3);//把frame的颜色空间转换后复制到frameHSV
Mat dstTemp1 = new Mat(mat_src.cols(), mat_src.rows(), CvType.CV_8UC1);
Mat dstTemp2 = new Mat(mat_src.cols(), mat_src.rows(), CvType.CV_8UC1);
// 对HSV空间进行量化,得到二值图像,亮的部分为手的形状
Core.inRange(frameHSV, new Scalar(0, 30, 30), new Scalar(40, 170, 256), dstTemp1);//比较三个通道中的元素是否在相应的区间类,不在的画的则改成255
Core.inRange(frameHSV, new Scalar(156, 30, 30), new Scalar(180, 170, 256), dstTemp2);
Core.bitwise_or(dstTemp1, dstTemp2, mask);//对前面的两张图片(二值图像)进行异或处理,并将其结果给第三章图
// 形态学操作,去除噪声,并使手的边界更加清晰
Mat element = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(3, 3));//定义一个合适大小的核
Imgproc.erode(mask, mask, element);//扩大暗区,腐蚀
Imgproc.morphologyEx(mask, mask, Imgproc.MORPH_OPEN, element);//对输入图像执行开运算
Imgproc.dilate(mask, mask, element);//扩大亮区,膨胀
Imgproc.morphologyEx(mask, mask, Imgproc.MORPH_CLOSE, element);//执行闭运算
frame.copyTo(dst, mask);
List<MatOfPoint> contourList = new ArrayList<MatOfPoint>();
Vector<MatOfPoint> contours=new Vector<MatOfPoint>();
Vector<Vector<Point>> con=new Vector<Vector<Point>>();
MatOfInt4 hierarchy=new MatOfInt4();
//Imgproc.Canny(mat_src,mask,mask,treash*2,3);
// // 得到手的轮廓
Imgproc.findContours(mask, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
//List<Moments> mu=new ArrayList<Moments>(contourList.size()) ;
//mu.set(0,moments(contourList.get(0),true));
// for( int i = 0; i < contourList.size(); i++ )
//{ mu.set(i,moments( contourList.get(i), false )); }
/// 计算中心矩:
//Log.e(TAG, "index=:"+index+" counter.size="+contours.size()+" area="+area);
//**********************************************************8
// dst = Mat::zeros( mask.size(), CV_8UC3 );
//Random r = new Random();
//int nContoursNum = contours.size();
//for (int i = 0; i < nContoursNum; i++) {
// Imgproc.drawContours(dst, contours, i, new Scalar(r.nextInt(255), r.nextInt(255), r.nextInt(255)), -1);
// }
//Point center=mc.get(0);
// Mat mat_gray1 = new Mat(mat_src.cols(), mat_src.rows(), CvType.CV_8UC1);//灰度图
//Imgproc.cvtColor(dst, mat_gray1, Imgproc.COLOR_BGRA2GRAY, 1);//转变颜色
Moments moment = moments(mask,false);
Point center = new Point(moment.m10 / moment.m00, moment.m01 / moment.m00);//计算图形重心
circle(dst, center, 8, new Scalar(0, 0, 255), -1);//最后一位参数不确定*/
//-------------------------------------------------------------------
//图像显示
Vector<MatOfPoint2f> newcont=new Vector<MatOfPoint2f>();
for(MatOfPoint point : contours) {
MatOfPoint2f newPoint = new MatOfPoint2f(point.toArray());
newcont.add(newPoint);
}
Vector<MatOfPoint2f> cont=new Vector<>();
for(int i=0;i<newcont.size();i++)
{
MatOfPoint2f first=newcont.get(i);
MatOfPoint2f second=new MatOfPoint2f();
Imgproc.approxPolyDP(first,second, 80, true);
cont.add(second);
}
Vector<MatOfPoint> polyedges = new Vector<>();
for(MatOfPoint2f point : cont) {
MatOfPoint nPoint = new MatOfPoint(point.toArray());
polyedges.add(nPoint);
}
Vector<Point> poly=new Vector<Point>();
MatOfPoint aPoint = polyedges.get(0);
Point[] ab=aPoint.toArray();
//Iterator<Point> itp = poly.listIterator(0);
//Point aa=poly.get(0);
for(int j=0;j<polyedges.size();j++) {
for (int i = 0; i < polyedges.get(j).toArray().length - 1; i++) {
line(dst, polyedges.get(j).toArray()[i], polyedges.get(j).toArray()[i + 1], new Scalar(255, 255, 255), 5);
//aa=itp.next();
}
line(dst,polyedges.get(j).toArray()[polyedges.get(j).toArray().length - 1],polyedges.get(j).toArray()[0], new Scalar(255,255,255), 5);
}
//Imgproc.threshold( mat_gray, dst, 100, 255, THRESH_BINARY );
/*RNG rng(12345);
Vector<Vector<Point> >hull=new Vector<Vector<Point>>( contours.size() );
for( int i = 0; i < contours.size(); i++ )
{ Imgproc.convexHull(new Mat((MatOfPoint2f)(contours.get(i))), hull.get(i), false ); }
/// 绘出轮廓及其凸包
Mat drawing = Mat.zeros( dst.size(), CV_8UC3 );
for( int i = 0; i< contours.size(); i++ )
{
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
Imgproc.drawContours( drawing, contours, i, color, 1, 8, Vector<Vec4i>(), 0, Point() );
drawContours( drawing, hull, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
}*/
//**********************************************************************
int index = 0;
double area = 0, maxArea=0;
for (int i=0;i < polyedges.size(); i++)
{
area = Imgproc.contourArea(polyedges.get(i));
if (area > maxArea)
{
maxArea = area;
index = i;
}
}
MatOfPoint couPoint = polyedges.get(index);
Point[] a=couPoint.toArray();
// List<Point> couPoint = contours.get(nContoursNum);
Vector<Point> fingerTips = new Vector<Point>();
Point tmp = new Point();
double max = 0;
int count = 0, notice = 0;
for (int i = 0; i < a.length; i++) {
tmp = a[i];
double dist = (tmp.x - center.x) * (tmp.x - center.x) + (tmp.y - center.y) * (tmp.y - center.y);
if (dist > max) {
max = dist;
notice = i;
}
// 计算最大值保持的点数,如果大于40(这个值需要设置,本来想根据max值来设置,
// 但是不成功,不知道为何),那么就认为这个是指尖
if (dist != max) {
max = 0;
boolean flag = false;
// 低于手心的点不算
if (center.y < a[notice].y)
continue;
// 离得太近的不算
for (int j = 0; j < fingerTips.size(); j++) {
if (abs(a[notice].x - fingerTips.get(j).x) < 40) {
flag = true;
break;
}
}
if (flag) continue;
fingerTips.add(a[notice]);
circle(dst, a[notice], 6, new Scalar(255, 255, 255), -1);
line(dst, center, a[notice], new Scalar(0, 255, 255), 5);
}
}
textView.setText(String.valueOf(fingerTips.size()));
//*****************************************************
Bitmap bmp_gray = Bitmap.createBitmap(mat_gray.cols(), mat_gray.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(dst, bmp_gray);
picture.setImageBitmap(bmp_gray);
}catch (FileNotFoundException e){
e.printStackTrace();
}
}
break;
default:
break;
}
}
}
四、程序效果:
1.程序鲁棒性不足,容易收到背景环境的干扰,尤其是与肤色相近的环境。
2.处理速度慢,要等待数秒左右。
3.弯曲手指不彻底就会影响结果。