Android 识别手指个数

一、在Android Studio导入opencv 库。

  这里直接给个链接:(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" />

(2)界面:包含一个拍照按钮,一个图片展示框和一个文本输出显示手指的个数。


<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.弯曲手指不彻底就会影响结果。


猜你喜欢

转载自blog.csdn.net/qq_38247544/article/details/80579801