• 实现的功能:从手机中选择一张图片,检测图片的基本特征,通过menu菜单选择要检测的特征,包括Canny边缘检测、Harris角点检测、霍夫直线检测 说明:对于检测图像的基本特征的算法就不加以详细说明了,网上的资料很...

    实现平台:windows下的Android studio1.4

    依赖库:openCV3.1.0

    程序安装平台:Android6.0

    实现的功能:从手机中选择一张图片,检测图片的基本特征,通过menu菜单选择要检测的特征,包括Canny边缘检测、Harris角点检测、霍夫直线检测

    说明:对于检测图像的基本特征的算法就不加以详细说明了,网上的资料很多,现在这里主要介绍算法以及代码的编写

    1.在Androidmanifest.xml文件中添加如下代码:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.wangshuailpp.myapplication" >
    
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme" >
            <activity
                android:name=".MainActivity"
                android:label="@string/app_name" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>

    这里最重要的是,表示要开启手机内存的读取权限:

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

    2.在布局文件中activity_main.xml文件中添加一个图片控件:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout 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:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
    
        <ImageView
            android:id="@+id/Picture"
            android:layout_height="fill_parent"
            android:layout_width="fill_parent"
            android:visibility="visible"
            />
    </RelativeLayout>

    3.菜单menu_main.xml文件中添加成员:

    <menu xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
       <item
           android:id="@+id/Canny"
           android:title="Canny"
           android:showAsAction="never"
           />
        <item
            android:id="@+id/Harris"
            android:title="Harris"
            android:showAsAction="never"
            />
        <item
            android:id="@+id/Hough"
            android:title="Hough"
            android:showAsAction="never"
            />
    </menu>

    4.在MainActivity.java主类中的代码:

    package com.example.wangshuailpp.myapplication;
    /*
    功能介绍:深入OpenCV Android应用开发第二章代码,检测图像的基本特征
            包括了Canny边缘检测法Sobel边缘检测法等
    实现步骤:1.从手机中取出一张图片作为原始图片,通过点击menu对应的按钮开始选择图片
            2.通过menu按钮选择要对照片进行的图像处理
     */
    
    import android.content.Intent;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.net.Uri;
    import android.os.Bundle;
    import android.support.v7.app.ActionBarActivity;
    import android.util.Log;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.widget.ImageView;
    import android.widget.Toast;
    
    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.Point;
    import org.opencv.core.Scalar;
    import org.opencv.imgproc.Imgproc;
    
    import java.io.FileNotFoundException;
    import java.io.InputStream;
    import java.util.Random;
    
    public class MainActivity extends ActionBarActivity {
    
        private final static int CANNY = 0;
        private final static int HARRIS = 1;
        private final static int HOUGH = 2;
        private final static String TAG = "infor";
    
        private Mat src = null;//定义一个Mat型类用于临时存放选择的图片
        private Mat image = null;//用于存放得到的图片
        private Mat des = null;//用于临时存放Mat型类的图片
        private Bitmap resultBitmap;
        private ImageView pictureView = null;//定义一个ImageView类视图用于存放选择的图片
    
        private BaseLoaderCallback mOpenCVCallBack = new BaseLoaderCallback(this) {
            @Override
            public void onManagerConnected(int status) {
    
                switch (status){
                    case LoaderCallbackInterface.SUCCESS:
                        /*在这里执行自己的语句*/
    
                        break;
                    default:
                        super.onManagerConnected(status);
                        break;
                }
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            pictureView = (ImageView)findViewById(R.id.Picture);
    
        }
    
        /*启动openCV*/
        @Override
        protected void onResume() {
            super.onResume();
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_1_0, this, mOpenCVCallBack);
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            // Inflate the menu; this adds items to the action bar if it is present.
            getMenuInflater().inflate(R.menu.menu_main, menu);
            return true;
        }
    
        /*在这里选取要进行的操作*/
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            // Handle action bar item clicks here. The action bar will
            // automatically handle clicks on the Home/Up button, so long
            // as you specify a parent activity in AndroidManifest.xml.
            int id = item.getItemId();
    
            //对应Canny边缘检测的按钮
            if (id == R.id.Canny) {
                /*下面对通过Intent对象得到选择图片的Activity,最后返回图片的信息,得到图片*/
                Intent pictureSelectIntent = new Intent(Intent.ACTION_PICK);//设置Action
                pictureSelectIntent.setType("image/");//设置数据的类型
                startActivityForResult(pictureSelectIntent,CANNY);
                return true;
            }
    
            //对应Harris边缘检测的按钮
            if (R.id.Harris == id){
                Intent pictureSelectIntent = new Intent(Intent.ACTION_PICK);
                pictureSelectIntent.setType("image/");
                startActivityForResult(pictureSelectIntent,HARRIS);
                return true;
            }
            //对应Hough的直线检测按钮
            if(R.id.Hough == id){
                Intent pictureSelectIntent = new Intent(Intent.ACTION_PICK);
                pictureSelectIntent.setType("image/");
                startActivityForResult(pictureSelectIntent,HOUGH);
                return true;
            }
    
            return super.onOptionsItemSelected(item);
        }
    
    
        /*调用StartActivityForResult后的回调函数
        * 在这个函数里面得到图片然后进行相应的处理
        * */
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            if(RESULT_OK == resultCode){
                switch(requestCode){
                    case CANNY:
                        try {
                            Log.i(TAG,"onActivityResult00000000000");
                            image = GetPicture(data);
                            Toast.makeText(MainActivity.this, "图片选取成功", Toast.LENGTH_SHORT).show();
                            Log.i(TAG,"onActivityResult11111111111");
                            resultBitmap = MyCanny(image);
                            Log.i(TAG,"onActivityResult22222222222222");
                            pictureView.setImageBitmap(resultBitmap);
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        }
                        break;
                    case HARRIS:
                        try {
                            image = GetPicture(data);//得到图片
                            Toast.makeText(MainActivity.this, "图片选取成功", Toast.LENGTH_SHORT).show();
                            Log.i(TAG,"onActivityResult11111111111");
                            resultBitmap = MyHarris(image);//角点检测的图像处理
                            Log.i(TAG,"onActivityResult22222222222222");
                            pictureView.setImageBitmap(resultBitmap);
    
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        }
                    case HOUGH:
                        try {
                            image = GetPicture(data);//得到图片
                            Toast.makeText(MainActivity.this, "图片选取成功", Toast.LENGTH_SHORT).show();
                            Log.i(TAG,"onActivityResult11111111111");
                            resultBitmap = MyHoughLine(image);
                            pictureView.setImageBitmap(resultBitmap);
    
                        }catch (FileNotFoundException e) {
                            e.printStackTrace();
                        }
                }
            }
        }
    
        /*得到图片*/
        public Mat GetPicture(Intent data) throws FileNotFoundException {
    
            /*下面的代码是获得手机内的图片*/
            final Uri imageUri = data.getData();//得到图片的路径
            final InputStream imageStream = getContentResolver().openInputStream(imageUri);//得到基于路径的流文件
            final Bitmap selectImage = BitmapFactory.decodeStream(imageStream);//得到了图片的位图
    
            /*下面将位图转换成Mat型,可以进行图片的处理*/
            src = new Mat(selectImage.getHeight(),selectImage.getWidth(), CvType.CV_8UC4);
            Utils.bitmapToMat(selectImage,src);
    
            return src;
        }
    
        /*下面进行图片的处理
        *
        * */
    
        /*Canny边缘处理*/
        public Bitmap MyCanny(Mat src){
    
            Bitmap result;
            Mat grayMat = new Mat();
            Mat cannyEdges = new Mat();
            Log.i(TAG,"MyCanny0000000000");
    
            /*将图片转换成灰度图*/
            Imgproc.cvtColor(src, grayMat, Imgproc.COLOR_BGR2GRAY);
            Log.i(TAG, "MyCanny1111111111111111");
            /*得到边缘图,这里最后两个参数控制着选择边缘的阀值上限和下限*/
            Imgproc.Canny(grayMat,cannyEdges,50,300);
            Log.i(TAG, "MyCanny222222222222222222222222");
            /*Mat图转换成位图*/
            result = Bitmap.createBitmap(src.cols(),src.rows(),Bitmap.Config.ARGB_8888);
            Utils.matToBitmap(cannyEdges,result);
            Log.i(TAG, "MyCanny3333333333333333333333");
    
            return result;
        }
    
        /*Harris角点检测*/
        public Bitmap MyHarris(Mat src){
            Bitmap resultHarris;
    
            Mat grayMat = new Mat();
            Mat corners = new Mat();
    
            Log.i(TAG,"MyHarris00000000000000000000");
            /*将图片转换成灰度图*/
            Imgproc.cvtColor(src,grayMat,Imgproc.COLOR_BGR2GRAY);
            Log.i(TAG, "MyHarris1111111111111111111");
            /*找出角点*/
            Mat tempDst = new Mat();
            Imgproc.cornerHarris(grayMat,tempDst,2,3,0.04);
            Log.i(TAG, "MyHarris2222222222222222222");
            /*归一化Harris角点的输出*/
            Mat tempDstNorm = new Mat();
            Core.normalize(tempDst,tempDstNorm,0,255,Core.NORM_MINMAX);
            Core.convertScaleAbs(tempDstNorm, corners);
            Log.i(TAG, "MyHarris33333333333333333333");
            /*在新的图片上绘制角点*/
            Random r = new Random();
            for(int i = 0; i < tempDstNorm.cols(); i++){
                for (int j = 0;j <tempDstNorm.rows(); j++){
                    double[] value = tempDstNorm.get(j,i);
                    if(value[0] > 250){//决定了画出哪些角点,值越大选择画出的点就越少。如果程序跑的比较慢,就是由于值选取的太小,导致画的点过多
                        Imgproc.circle(corners, new Point(i,j),5,new Scalar(r.nextInt(255)),2);
                    }
                }
            }
            Log.i(TAG,"MyHarris4444444444444444444444444");
            /*Mat图转换成位图*/
            resultHarris = Bitmap.createBitmap(src.cols(),src.rows(),Bitmap.Config.ARGB_8888);//这一步至关重要,必须初始化Bitmap对象的大小
            Utils.matToBitmap(corners, resultHarris);
    
            return resultHarris;
        }
    
        /*Hough直线检测*/
        public Bitmap MyHoughLine(Mat src){
            Bitmap resultHough;
    
            Mat grayMat = new Mat();
            Mat cannyEdges = new Mat();
            Mat lines = new Mat();
            Mat origination = new Mat(src.size(),CvType.CV_8UC1);
            src.copyTo(origination);//拷贝
    
            /*通过Canny得到边缘图*/
            Imgproc.cvtColor(origination,grayMat,Imgproc.COLOR_BGR2GRAY);
            Imgproc.Canny(grayMat,cannyEdges,50,300);
            //Mat cannyEdges = new Mat(resultHough.getHeight(),resultHough.getWidth(),CvType.CV_8UC1);
    
    
            Log.i(TAG,"MyHoughLine00000000000000");
            /*获得直线图*/
            Imgproc.HoughLinesP(cannyEdges,lines,1,Math.PI/180,10,0,50);
            Log.i(TAG, "MyHoughLine111111111111111");
    
            Mat houghLines = new Mat();
            houghLines.create(cannyEdges.rows(),cannyEdges.cols(),CvType.CV_8UC1);
            Log.i(TAG, "MyHoughLine2222222222222222222");
            /*在图线的上绘制直线*/
            for(int i = 0;i < lines.rows();i++){
                double[] points = lines.get(i,0);
                if(null != points){
                    double x1,y1,x2,y2;
    
                    x1 = points[0];
                    y1 = points[1];
                    x2 = points[2];
                    y2 = points[3];
    
                    Point pt1 = new Point(x1,y1);
                    Point pt2 = new Point(x2,y2);
                /*在一幅图像上绘制直线*/
                    Imgproc.line(houghLines,pt1,pt2,new Scalar(55,100,195),3);
                }
            }
            Log.i(TAG, "MyHoughLine3333333333333333333333333");
            resultHough = Bitmap.createBitmap(src.cols(),src.rows(),Bitmap.Config.ARGB_8888);
            Utils.matToBitmap(houghLines,resultHough);
            Log.i(TAG, "MyHoughLine44444444444444444444444444444");
    
            return resultHough;
        }
    }
    
    这里需要注意的事项:

    1.在霍夫直线检测中有一句代码,很多网上的程序都不对,都写成了

    double[] points = lines.get(0,i);
    其实是
    double[] points = lines.get(i,0);
    写成第一种,会导致只会画出一条直线。


    其他的都可以在程序的解释中看到,在这里就不都说了,下面直接贴结果,分别是原图,Canny,Harri,霍夫直线。































































    展开全文
  • opencv实现边缘检测

    2020-06-03 23:32:10
    利用opencvAndroid上实现的边缘检测
  • 由于项目的需要,这几天开始接触用opencv

    转载请注明本文出自这么远_那么近的博客(http://blog.csdn.net/u010585964),谢谢支持!

    由于项目的需要,这几天开始接触opencv,要在android平台上利用opencv进行图像处理。以下是这几天的成果:

    运行结果如下:

    原始图片:


    处理后的图片:



    Java代码:

    package com.cdq.opencvtest;
    
    import java.io.File;
    
    import org.opencv.android.BaseLoaderCallback;
    import org.opencv.android.LoaderCallbackInterface;
    import org.opencv.android.OpenCVLoader;
    import org.opencv.core.CvType;
    import org.opencv.core.Mat;
    import org.opencv.core.Size;
    import org.opencv.highgui.Highgui;
    import org.opencv.imgproc.Imgproc;
    
    import android.os.Bundle;
    import android.app.Activity;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.util.Log;
    import android.view.Menu;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.ImageView;
    import android.widget.Toast;
    
    public class MainActivity extends Activity
    {
    
    	private ImageView iv = null;
    	private Button btn1 = null;
    	private Button btn2 = null;
    	String tag="MainActivity";
    	 //OpenCV类库加载并初始化成功后的回调函数
    	private BaseLoaderCallback mLoader = new BaseLoaderCallback(this)
    	{
    
    		@Override
    		public void onManagerConnected(int status)
    		{
    			switch (status)
    			{
    			case LoaderCallbackInterface.SUCCESS:
    			{
    			}break;
    			default:
    			{
    				super.onManagerConnected(status);
    			}break;
    			}
    		}
    
    	};
    	@Override
    	protected void onResume()
    	{
    		super.onResume();
    		//通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 OpenCV Manager
    		OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_9, this,mLoader);
    	}
    	@Override
    	protected void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		iv = (ImageView) findViewById(R.id.iv);
    		btn1 = (Button) findViewById(R.id.btn1);
    		btn2 = (Button) findViewById(R.id.btn2);
    		Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/face8.jpg");
    		iv.setImageBitmap(bitmap);
    		btn1.setOnClickListener(new OnClickListener()
    		{
    			@Override
    			public void onClick(View arg0)
    			{
    				Mat img = Highgui.imread("/sdcard/face8.jpg", 0);
    				Size dSize = new Size((double) img.width(), (double) img.height());
    				Mat img2 = new Mat(dSize, CvType.CV_8SC1);
    				Mat img3 = new Mat();
    				img.convertTo(img2, CvType.CV_8SC1);
    				Imgproc.Canny(img, img3, 123, 250);
    				boolean flag = Highgui.imwrite("/sdcard/new.jpg", img3);
    				Log.i(tag, "onClick");
    				if (flag)
    				{
    					Log.i(tag, "flag");
    					File file = new File("/sdcard/new.jpg");
    					if (file.exists())
    					{
    						Bitmap bitmap2 = BitmapFactory.decodeFile("/sdcard/new.jpg");
    						iv.setImageBitmap(bitmap2);
    					}
    				}
    				else
    				{
    					Toast.makeText(getApplicationContext(), "图片写入失败",
    							Toast.LENGTH_SHORT).show();
    				}
    				
    			}
    		});
    		btn2.setOnClickListener(new OnClickListener()
    		{
    			
    			@Override
    			public void onClick(View arg0)
    			{
    				Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/face8.jpg");
    				iv.setImageBitmap(bitmap);
    			}
    		});
    	}
    	@Override
    	public boolean onCreateOptionsMenu(Menu menu)
    	{
    		// Inflate the menu; this adds items to the action bar if it is present.
    		getMenuInflater().inflate(R.menu.main, menu);
    		return true;
    	}
    }
    
    AndroidMenifest.xml文件:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.cdq.opencvtest"
        android:versionCode="1"
        android:versionName="1.0" >
        <uses-sdk
            android:minSdkVersion="14"
            android:targetSdkVersion="14" />
    	<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
        <application
            android:allowBackup="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >
            <activity
                android:name="com.cdq.opencvtest.MainActivity"
                android:label="@string/app_name" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>

    布局文件:

    <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:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:orientation="vertical"
        tools:context=".MainActivity" >
    
        <TableLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content" 
            android:layout_weight="1"
            >
    
            <ImageView
                android:id="@+id/iv"
                android:layout_width="fill_parent"
                android:layout_height="0dp"
                android:layout_weight="9" />
    
            <TableRow
                android:id="@+id/tableRow1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" >
    
                <Button
                    android:id="@+id/btn1"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:text="处理"
                    android:layout_weight="1"/>
    
                <Button
                    android:id="@+id/btn2"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:text="恢复" 
                    android:layout_weight="1"/>
            </TableRow>
    
        </TableLayout>
    
    </LinearLayout>
    

    实践过程中,一直有个问题纠结了我很久——java.lang.UnsatisfiedLinkError: Native method not found: org.opencv.highgui.Highgui.imread_0:(Ljava/lang/String;I)。在网上看了很多资料后,才明白其实是程序没有加载opencv类库。于是我加入了以下代码后,问题得到了解决

    //OpenCV类库加载并初始化成功后的回调函数
    	private BaseLoaderCallback mLoader = new BaseLoaderCallback(this)
    	{
    
    		@Override
    		public void onManagerConnected(int status)
    		{
    			switch (status)
    			{
    			case LoaderCallbackInterface.SUCCESS:
    			{
    			}break;
    			default:
    			{
    				super.onManagerConnected(status);
    			}break;
    			}
    		}
    
    	};
    	@Override
    	protected void onResume()
    	{
    		super.onResume();
    		//通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 OpenCV Manager
    		OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_9, this,mLoader);
    	}


    展开全文
  • 自己编写camera,实现camera preview, take picture并保存照片。同时基于opencv 2.4.8库,对保存的照片实现边缘检测。建议用Opencv2.4.8的库,不保证在其他opencv版本上能正常运行。
  • 程序在运行前需要修改local.properties文件里ndk路径,我的是ndk.dir=F\:\\...以及android.mk文件里OpenCV.mk的路径,我的是F:\AndroidStudioProjects\OpenCV-android-sdk\sdk\native\jni\OpenCV.mk
  • Android opencv(三) 边缘检测Sobel、La 、Canny

    Sobel

    Sobel是检测边缘的一阶导数,在检测边缘前需要通过滤波(blur)去噪来使图片光滑,一般使用高斯滤波,大小取7x7.

      //高斯滤波
        public static Mat removeNoiseGaussianBlur(Mat srcMat){
            final Mat blurredImage=new Mat();
            Size size=new Size(7,7);
            Imgproc.GaussianBlur(srcMat,blurredImage,size,0,0);
            return blurredImage;
        }

    之后进行灰度处理

      //灰度处理
        public static Mat grayImage(Mat srcMat){
            Mat grayImg=new Mat();
            Imgproc.cvtColor(srcMat,grayImg,Imgproc.COLOR_RGB2GRAY);
            return grayImg;
        }

    下面是sobel进行过程

             //声明x,y的一阶导数
            Mat xDervative=new Mat();
            Mat yDervative=new Mat();
            /*一般输入图片的depth与输出图片的depth值相同,但是当我们一阶导数的时候会出现负数-255—255,使用unsignded 8-bit depth只能包含0-255,因此采用了16-bit
            */
            int ddepth= CvType.CV_16S;
            //计算x,y的一阶导数,参数1,0来设置x,y轴
            Imgproc.Sobel(srcMat,xDervative,ddepth,1,0);
            Imgproc.Sobel(srcMat,yDervative,ddepth,0,1);
            Mat absXD=new Mat();
            Mat absYD=new Mat();
            //Mat转换
            Core.convertScaleAbs(xDervative,absXD);
            Core.convertScaleAbs(yDervative,absYD);
            //根号(x*x+y*y)
            Mat edgeImage=new Mat();
            Core.addWeighted(absXD,0.5,absYD,0.5,0,edgeImage);

    Canny

    canny检测仅在灰度图下,同时滤波只能采用高斯滤波,原因如下:
    Canny算子在选择滤波器时有两个标准,首先滤波器在频域中应该是有限带宽的,这样才能保证减少由于频率的变化而导致的图像函数的变化;其次,canny经典论文中“Good Localization”标准要求滤波器的响应需要来自于图像中邻近的点。要知道,上述两个标准相互间是矛盾的,频域有限说明时域无限,而高斯分布可以较好的使上述两个标准得到优化,因为高斯函数的离中心超过3倍标准差以外的像素的影响可以忽略不计,同时高斯函数在频域上也是有限带宽的,因此才使用高斯滤波

    在Canny边缘检测算法中,将图像中的点归为三类:

    被抑制点

    灰度梯度值 < 低阈值

    弱边缘点

    低阈值 <= 灰度梯度值 <= 高阈值

    强边缘点
    高阈值 < 灰度梯度值
    ps:canny边缘检测是通过梯度与夹角进行检测,具体原理参考下面链接,大于最大阈值的线被确认是边缘线条,小于最小阈值的线被抛弃,在最大阈值与最小阈值之间的线条如果线条的边缘有明确的边缘线交点则被认为是在一条线上,从而被保留,否则被抛弃。

    https://docs.opencv.org/3.3.1/da/d22/tutorial_py_canny.html

     //canny边缘检测
        public static Mat cannyEdge(Mat srcMat){
            final Mat edgeImage=new Mat();
            //100低阈值 200:高阈值
            Imgproc.Canny(srcMat,edgeImage,100,200);
            return edgeImage;
        }

    自适应阈值Canny边缘检测
    http://blog.csdn.net/tianzhaixing2013/article/details/53504426

    展开全文
  • 目前比较火热的图像识别技术,如车牌号识别、身份证识别、人脸识别等,都广泛运用到了图像边缘检测,今天我所介绍的就是OpenCV边缘检测,实现边缘检测有三个步骤:滤波->增强->检测,opencv中有三个常用的边缘检测...

    目前比较火热的图像识别技术,如车牌号识别、身份证识别、人脸识别等,都广泛运用到了图像边缘检测,今天我所介绍的就是OpenCV边缘检测,实现边缘检测有三个步骤:滤波->增强->检测,opencv中有三个常用的边缘检测算子函数:canny、sobel和laplace。

    现附上一张原图:
    这里写图片描述

    canny算子

    Canny边缘检测算子是一种多级检测算法,Canny的目标是找到一个最优的边缘检测算法,算法能够尽可能多地标识出图像中的实际边缘,标识出的边缘要与实际图像中的实际边缘尽可能接近,图像中的边缘只能标识一次,并且可能存在的图像噪声不应标识为边缘。Canny算法步骤:降噪->寻找图片的亮梯度->在图像中跟踪边缘。任何边缘检测算法都不可能在未经处理的原始数据上很好地处理,所以第一步是对原始数据与高斯平滑模板作卷积,得到的图像与原始图像相比有些轻微的模糊(blurred)。这样,单独的一个像素噪声在经过高斯平滑的图像上变得几乎没有影响。图像中的边缘可能会指向不同的方向,所以Canny算法使用4个mask检测水平、垂直以及对角线方向的边缘。原始图像与每个mask所作的卷积都存储起来。对于每个点我们都标识在这个点上的最大值以及生成的边缘的方向。这样我们就从原始图像生成了图像中每个点亮度梯度图以及亮度梯度的方向。较高的亮度梯度比较有可能是边缘,但是没有一个确切的值来限定多大的亮度梯度是边缘多大又不是,所以Canny使用了滞后阈值。滞后阈值需要两个阈值——高阈值与低阈值。假设图像中的重要边缘都是连续的曲线,这样我们就可以跟踪给定曲线中模糊的部分,并且避免将没有组成曲线的噪声像素当成边缘。所以我们从一个较大的阈值开始,这将标识出我们比较确信的真实边缘,使用前面导出的方向信息,我们从这些真正的边缘开始在图像中跟踪整个的边缘。在跟踪的时候,我们使用一个较小的阈值,这样就可以跟踪曲线的模糊部分直到我们回到起点。一旦这个过程完成,我们就得到了一个二值图像,每个点表示是否是一个边缘点。
    一个获得亚像素精度边缘的改进实现是在梯度方向检测二阶方向导数的过零点:
    这里写图片描述
    它在梯度方向的三阶方向导数满足符号条件:
    这里写图片描述
    这里写图片描述
    表示用高斯核平滑原始图像得到的尺度空间表示 L计算得到的偏导数。用这种方法得到的边缘片断是连续曲线,这样就不需要另外的边缘跟踪改进。滞后阈值也可以用于亚像素边缘检测。
    格罗宁根大学学生的Canny算子实现边缘检测的一个web项目

      /**
       * canny算子 三通道
       * 第一个参数,输入图像,需为单通道8位图像。
       * 第二个参数,输出的边缘图,需要和源图片有一样的尺寸和类型。
       * 第三个参数,第一个滞后性阈值。
       * 第四个参数,第二个滞后性阈值。
       * 第五个参数,表示孔径大小,其有默认值3。
       * 第六个参数,计算图像梯度幅值的标识,有默认值false。
       * 需要注意的是,这个函数阈值1和阈值2两者的小者用于边缘连接,
       * 而大者用来控制强边缘的初始段,推荐的高低阈值比在2:1到3:1之间
       */
    CV_EXPORTS_W void Canny( InputArray dx, InputArray dy,
                             OutputArray edges,
                             double threshold1, double threshold2,
                             bool L2gradient = false );
     Mat img(h,w,CV_8UC4,pixels);
        Mat outImg;
        cvtColor(img,outImg,COLOR_BGR2BGR);
        //滤波
         blur(outImg,outImg,Size(3,3));
    //    //Canny算子边缘检测
        Canny(outImg,outImg,3,9);
        Mat out;
        out = Scalar::all(0);
        img.copyTo(out,outImg);
        uchar *ptr = img.ptr(0);
        uchar *outPtr = outImg.ptr(0);
        for (int i = 0; i < w * h; ++i) {
            ptr[4*i+0] = outPtr[3*i+0];
            ptr[4*i+1] = outPtr[3*i+1];
            ptr[4*i+2] = outPtr[3*i+2];
        }

    这里写图片描述

    Sobel算子

    它是一个离散微分算子,近似于是一个计算图像强度梯度的函数。在图像中的每个点,Sobel-Feldman算子的结果是相应的梯度向量或者这个向量的范数。Sobel算子基于将图像与水平和垂直方向上的小的可分离的整数值的滤波器进行卷积,因此在计算上相对比较节约时间。另一方面,它产生的梯度相对比较粗糙,特别是对于图像中的高频变化。
    运算符使用与原始图像卷积的两个3×3内核来计算导数的近似值- 一个用于水平变化,另一个用于垂直。如果我们将A定义为源图像,则G x和G y分别是包含水平和垂直微分近似的两幅图像,计算结果如下( *这里表示二维信号处理卷积操作):
    这里写图片描述
    由于Sobel核可以分解为平均值和微分核的乘积,所以他们可以用平滑的方式来计算梯度。例如,G x可以写成:
    这里写图片描述
    该X坐标在这里被定义为向右方向增加,并y坐标被定义为向下方向增加。在图像中的每个点,可以将得到的梯度近似值结合起来,得到梯度的大小:
    这里写图片描述
    使用这些信息,我们也可以计算出梯度的方向:
    这里写图片描述
    例如,对于在右侧较亮的垂直边缘,θ是0。

     /**
         * sobel算子 单通道
         * 第一个参数,输入图像
         * 第二个参数,目标图像
         * 第三个参数,输出图像的深度
                使用CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F
                使用CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F
                使用CV_32F, 取ddepth =-1/CV_32F/CV_64F
                使用CV_64F, 取ddepth = -1/CV_64F
         * 第四个参数,x 方向上的差分阶数。
         * 第五个参数,y方向上的差分阶数。
         * 第六个参数,核的大小;必须取1,3,5或7。
         * 第七个参数,计算导数值时可选的缩放因子,默认值是1,表示默认情况下是没有应用缩放的。
         * 第八个参数,表示在结果存入目标图(第二个参数dst)之前可选的delta值,有默认值0。
         * 第九个参数,边界模式,默认值为BORDER_DEFAULT。
         * 一般情况下,都是用ksize x ksize内核来计算导数的。
         * 然而,有一种特殊情况——当ksize为1时,往往会使用3 x 1或者1 x 3的内核。
         * 且这种情况下,并没有进行高斯平滑操作。
         */
    CV_EXPORTS_W void Sobel( InputArray src, OutputArray dst, int ddepth,
                             int dx, int dy, int ksize = 3,
                             double scale = 1, double delta = 0,
                             int borderType = BORDER_DEFAULT );
      Mat img(h,w,CV_8UC4,pixels);
       Mat outImg;
      cvtColor(img,outImg,COLOR_BGR2GRAY);//单通道
       Mat outimgX,outimgY,outXY;
        //x方向sobel梯度
        Sobel(outImg,outimgX,CV_8U,1,0);
        //y方向sobel梯度
        Sobel(outImg,outimgY,CV_8U,0,1);
        //合并梯度
        addWeighted(outimgX,0.5,outimgY,0.5,0,outXY);
        uchar *ptr = img.ptr(0);
        uchar *outPtr = outXY.ptr(0);
        for (int i = 0; i < w * h; ++i) {
            ptr[4*i+0] = outPtr[i];
            ptr[4*i+1] = outPtr[i];
            ptr[4*i+2] = outPtr[i];
        }

    这里写图片描述

    laplacian算子
    在opencv中laplacian算子通过二阶导数来检测边缘,由于图像是2D的,所以需要在两个维度上采用导数。Opencv中使用laplacian计算图像的梯度,然后内部有调用了Sobel算子。
    laplacian算子定义:
    这里写图片描述

    /**
           * laplacian函数
           * 第一个参数,输入图像,需为单通道8位图像。
           * 第二个参数,输出的边缘图,需要和源图片有一样的尺寸和通道数。
           * 第三个参数,目标图像的深度。
           *  IPL_DEPTH_8U - 无符号8位整型              0--255
           *  IPL_DEPTH_8S - 有符号8位整型                   -128--127
           *  IPL_DEPTH_16U - 无符号16位整型                0--65535
           *  IPL_DEPTH_16S - 有符号16位整型                -32768--32767
           *  IPL_DEPTH_32S - 有符号32位整型                00.0--1.0
           *  IPL_DEPTH_64F - 双精度浮点数                  0.0--1.0
           * 第四个参数,用于计算二阶导数的滤波器的孔径尺寸,大小必须为正奇数,且有默认值1。
           * 第五个参数,计算拉普拉斯值的时候可选的比例因子,有默认值1。放大或者缩小因子。
           * 第六个参数,表示在结果存入目标图(第二个参数dst)之前可选的delta值,有默认值0。
           * 第七个参数,边界模式,默认值为BORDER_DEFAULT。
           */
    CV_EXPORTS_W void Laplacian( InputArray src, OutputArray dst, int ddepth,
     int ksize = 1, double scale = 1, double delta = 0,int borderType = BORDER_DEFAULT );
        Mat img(h,w,CV_8UC4,pixels);
        Mat outImg;
        cvtColor(img,outImg,COLOR_BGR2GRAY);//单通道
         Laplacian(outImg,outImg,CV_8U);
          uchar *ptr = img.ptr(0);
        uchar *outPtr = outXY.ptr(0);
        for (int i = 0; i < w * h; ++i) {
            ptr[4*i+0] = outPtr[i];
            ptr[4*i+1] = outPtr[i];
            ptr[4*i+2] = outPtr[i];
        }
    

    这里写图片描述

    展开全文
  • Android 上使用jni和opencv 实现边缘检测首先来看实验效果这是在android上运行的一个小demo,使用真机调试,运行在红米Note3上。上面是原始图片,下面是边缘检测的结果图。这里的边缘检测使用OpenCV中的canny算法。...

    Android 上使用jni和opencv 实现边缘检测和直线检测

    项目的github地址:https://github.com/lijialinneu/MyApplication

    首先来看实验效果

    这里写图片描述

    这是在android上运行的一个小demo,使用真机调试,运行在红米Note3上。上面是原始图片,下面是边缘检测的结果图。这里的边缘检测使用OpenCV中的canny算法。


    下面说明如何创建这样一个项目

    分为以下几步:

    • 1、配置ndk开发环境
    • 2、写好make文件:Android.mk和Application.mk
    • 3、写好canny.h、canny.cpp(c++)和java封装类
    • 4、写好界面,包括MainActivity,activity_main.xml

    名词解释:

    sdk,即software development kit,Android的软件开发工具包。

    ndk,即native develop kit,是google为了方便在android上使用原生代码语言而推出的开发工具。简单地说,ndk作用之一就是帮助开发者在android上使用c++开发程序。

    jni,即java native interface,提供了API实现java和C/C++的交互。可以看做java程序和C/C++程序间的桥梁。


    1、配置ndk开发环境

    **(1)首先需要安装AndroidStudio,并新建一个空项目:MyApplication。**这一步可以参考:http://blog.csdn.net/liranke/article/details/49636927/

    **(2)然后下载android-ndk-r10并解压,搭建ndk环境。**在Project Structure设置项中,可以配置sdk和ndk的位置。这里面的ndk就是刚刚下载好的android-ndk-r10。

    这里写图片描述

    (3)修改app的build.gradle:

    这部分代码,是根据网上资料修改而来,某些配置项可以按照开发时的实际情况修改。

    apply plugin: 'com.android.application'
    
    android {
        compileSdkVersion 25
        buildToolsVersion "23.0.3"
        defaultConfig {
            applicationId "com.example.lijialin.myapplication"
            minSdkVersion 15
            targetSdkVersion 25
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
            ndk {
                moduleName "OpenCV"          //生成的so名字
            }
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
        /**
         *  禁止自带的ndk功能
         *  @author 10405
         *  add on 2017-10-20
         */
        sourceSets.main.jni.srcDirs = []
        sourceSets.main.jniLibs.srcDirs = ['src/main/libs', 'src/main/jniLibs']
        //重定向so目录为src/main/libs和src/main/jniLibs,原来为src/main/jniLibs
    
        task ndkBuild(type: Exec, description: 'Compile JNI source with NDK') {
            Properties properties = new Properties()
            properties.load(project.rootProject.file('local.properties').newDataInputStream())
            def ndkDir = properties.getProperty('ndk.dir')
    
            if (org.apache.tools.ant.taskdefs.condition.Os.isFamily(org.apache.tools.ant.taskdefs.condition.Os.FAMILY_WINDOWS)) {
                commandLine "$ndkDir/ndk-build.cmd", '-C', file('src/main/jni').absolutePath
            } else {
                commandLine "$ndkDir/ndk-build", '-C', file('src/main/jni').absolutePath
            }
        }
    
        tasks.withType(JavaCompile) {
            compileTask -> compileTask.dependsOn ndkBuild
        }
    
        task ndkClean(type: Exec, description: 'Clean NDK Binaries') {
            Properties properties = new Properties()
            properties.load(project.rootProject.file('local.properties').newDataInputStream())
            def ndkDir = properties.getProperty('ndk.dir')
    
            if (org.apache.tools.ant.taskdefs.condition.Os.isFamily(org.apache.tools.ant.taskdefs.condition.Os.FAMILY_WINDOWS)) {
                commandLine "$ndkDir/ndk-build.cmd  ", 'clean', '-C', file('src/main/jni').absolutePath
            } else {
                commandLine "$ndkDir/ndk-build  ", 'clean', '-C', file('src/main/jni').absolutePath
            }
        }
    
        clean.dependsOn 'ndkClean'
    }
    
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
            exclude group: 'com.android.support', module: 'support-annotations'
        })
        compile 'com.android.support:appcompat-v7:25.+'
        compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha9'
        testCompile 'junit:junit:4.12'
    }
    
    

    (4)从opencv官网下载opencv为android平台准备的开发工具:OpenCV-3.1.0-android-sdk,当然使用其他版本也可以。下载后解压,在路径OpenCV-3.1.0-android-sdk\OpenCV-android-sdk\sdk\下,有一个native文件。将native文件复制到MyApplication中。复制后的目录结构如下图所示:

    这里写图片描述

    (5)在app\src\main目录下,新建一个jni目录,在jni目录中,添加三个文件:
    Android.mkApplication.mk,canny.cpp

    其中,Android.mk的内容如下:

    LOCAL_PATH := $(call my-dir)
    
    include $(CLEAR_VARS)
    
    
    OpenCV_INSTALL_MODULES := on
    OpenCV_CAMERA_MODULES := off
    
    OPENCV_LIB_TYPE :=STATIC
    
    ifeq ("$(wildcard $(OPENCV_MK_PATH))","")
    #include ..\..\..\..\native\jni\OpenCV.mk
    # 根据自己的路径修改
    include C:\Users\lijialin\AndroidStudioProjects\MyApplication\native\jni\OpenCV.mk
    
    else
    include $(OPENCV_MK_PATH)
    endif
    
    LOCAL_MODULE := OpenCV
    
    LOCAL_SRC_FILES := canny.cpp
    #LOCAL_SRC_FILES += lsd.cpp #在这里可以添加其他cpp文件
    
    #LOCAL_LDLIBS +=  -lm -llog
    LOCAL_LDLIBS += -llog
    
    include $(BUILD_SHARED_LIBRARY)
    

    Application.mk的内容如下:

    APP_STL := gnustl_static
    APP_CPPFLAGS := -frtti -fexceptions
    APP_ABI := armeabi
    APP_PLATFORM := android-8
    APP_OPIM := debug
    

    (6)在app\src\main目录下,新建一个libs目录,我们编译后生成的libOpenCV.so会生存储在这个目录下。


    2、编写canny.h,canny.cpp和Java封装类

    canny.h是canny.cpp的头文件,功能只有一个,就是声明了native函数:
    Java_com_example_lijialin_myapplication_OpenCVCanny_canny

    • com_example_lijialin是包名
    • myapplication是项目名
    • OpenCVCanny是java封装类的类名
    • canny是函数名
    //
    // Created by lijialin on 2017/10/20.
    //
    
    /* Header for class canny */
    
    #include<jni.h>
    
    
    #ifndef MYAPPLICATION_CANNY_H
    #define MYAPPLICATION_CANNY_H
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     canny
     * Method:    canny
     * Signature: ([III)[I
     */
    JNIEXPORT jintArray JNICALL Java_com_example_lijialin_myapplication_OpenCVCanny_canny (JNIEnv *, jclass, jintArray, jint, jint);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    

    canny.cpp是我们实现边缘检测的核心程序,在这个程序中,首先将图像转为灰度图,然后调用高斯滤波对图像进行平滑去噪,最后使用canny边缘检测算法,提取出图像中的边缘。

    canny.cpp

    //
    // Created by lijialin on 2017/10/20.
    //
    
    #include "canny.h"
    #include <stdio.h>
    #include <stdlib.h>
    #include <opencv2/opencv.hpp>
    #include <opencv2/imgproc/imgproc.hpp>
    #include <android/log.h>
    #include <iostream>
    
    
    #define LOG_TAG    "asdf"
    #define LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG, __VA_ARGS__) // 定义LOGD类型,便于调试
    
    using namespace cv;
    using namespace std;
    
    extern  "C" {
    
    // JNIEnv 代表java环境,通过这个参数可以调用java中的方法
    
    JNIEXPORT jintArray JNICALL Java_com_example_lijialin_myapplication_OpenCVCanny_canny(JNIEnv *env, jclass obj, jintArray buf, int w, int h);
    
    JNIEXPORT jintArray JNICALL Java_com_example_lijialin_myapplication_OpenCVCanny_canny(JNIEnv *env, jclass obj, jintArray buf, int w, int h) {
    
        jint *cbuf;
        cbuf = env->GetIntArrayElements(buf, false); // 读取输入参数
        if (cbuf == NULL) {
            return 0;
        }
    
        Mat image(h, w, CV_8UC4, (unsigned char*) cbuf); // 初始化一个矩阵(图像)4通道的图像
        cvtColor(image, image, COLOR_BGR2GRAY); // 转为灰度图
        GaussianBlur(image, image, Size(5,5), 0, 0); // 高斯滤波
        Canny(image, image, 50, 150, 3); // 边缘检测
    
        // LSD 直线检测
        Mat image2(image.size(), image.type()); // 用于绘制直线
        Ptr<LineSegmentDetector> ls = createLineSegmentDetector(LSD_REFINE_STD, 0.80);
        vector<Vec4f> lines_std;
        ls->detect(image, lines_std);
    
        // LOGD("channels = %d", image.channels());
        ls->drawSegments(image2, lines_std);
        cvtColor(image2, image2, COLOR_BGR2GRAY); // 此处要转为灰度图
    
        // TODO 这里还可以添加其他的功能
    
        // 构造返回结果
        int* outImage = new int[w * h];
        int n = 0;
        for(int i = 0; i < h; i++) {
            uchar* data = image.ptr<uchar>(i);
            // uchar* data = image2.ptr<uchar>(i); // 如果是直线检测,就用image2
            for(int j = 0; j < w; j++) {
                if(data[j] == 255) {
                    outImage[n++] = 0;
                }else {
                    outImage[n++] = -1;
                }
            }
        }
    
        int size = w * h;
        jintArray result = env->NewIntArray(size);
        env->SetIntArrayRegion(result, 0, size, outImage);
        env->ReleaseIntArrayElements(buf, cbuf, 0);
        return result;
    
    }
    
    
    }
    
    

    在写好canny.cpp后,还需要在外面封装一层java代码,定义native方法以便调用本地的C/C++代码。

    OpenCVCanny.java:

    package com.example.lijialin.myapplication;
    
    /**
     * Created by lijialin on 2017/10/20.
     */
    
    public class OpenCVCanny {
        static {
            System.loadLibrary("OpenCV"); // 加载编译好的.so动态库
        }
    
        /**
         * 声明native方法,调用OpenCV的边缘检测
         *
         * @param buf 图像
         * @param w 宽
         * @param h 高
         * @return 边缘图
         */
        public static native int[] canny(int[] buf, int w, int h);
    }
    

    如果我们此时编译,也就是make,可以看到在libs目录下生成了libOpenCV.so

    这里写图片描述


    如果make报错,需要修改好错误。修改make时的错误是一个痛苦的过程,我一般借助android studio上的message和Gradle Console工具,同时通过自己打Log来分析代码中的错误。

    这里写图片描述


    这样核心的部分就写好了,下面开始编写android的界面。


    3、编写android界面

    app只有一个界面,只需把MainActivity写好就可以了。在MainActivity中,程序首先从资源文件中获取了building图像,然后调用上一步封装好的OpenCVCanny.canny()方法,并传递相应的参数进去。canny()方法会使用native方法,也就是canny.cpp中的Java_com_example_lijialin_myapplication_OpenCVCanny_canny函数,实现边缘检测的功能。

    MainActivity.java

    package com.example.lijialin.myapplication;
    
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.ImageView;
    
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            // 获取图片,使用边缘检测提取图像的边缘
            Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.building);
            int width = bitmap.getWidth();
            int height = bitmap.getHeight();
            int[] pix = new int[width * height];
            bitmap.getPixels(pix, 0, width, 0, 0, width, height);
            int[] resultPixels = OpenCVCanny.canny(pix, width, height); // 调用canny方法
    
            Bitmap resultBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444); // 创建一个4通道的图像
            resultBitmap.setPixels(resultPixels, 0, width, 0, 0, width, height);
    
            // 将边缘图显示出来
            ImageView view = (ImageView) findViewById(R.id.resultView);
            view.setImageBitmap(resultBitmap);
            view.setVisibility(View.VISIBLE);
        }
    }
    
    

    界面:activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorPrimary"
        tools:context="com.example.lijialin.myapplication.MainActivity">
    
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
             android:orientation="vertical">
    
        <TextView
            android:id="@+id/title1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@android:color/white"
            android:text="原始图"/>
    
        <ImageView
            android:id="@+id/imageView"
            android:layout_width="wrap_content"
            android:layout_height="259dp"
            app:srcCompat="@mipmap/building" />
    
        <TextView
            android:id="@+id/title2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@android:color/white"
            android:text="边缘图" />
    
        <ImageView
            android:id="@+id/resultView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            />
    
        </LinearLayout>
    
    </android.support.constraint.ConstraintLayout>
    
    

    4、真机调试

    编写完上述的代码后,可以进行真机调试了。如果程序没有其他报错,就能看到下面的运行结果。

    这里写图片描述

    总结:

    如果我们要在android上使用opencv,除了jni外,还可以直接使用opencv提供的java api,但是对于实时性要求较高的系统,不建议直接使用java api。

    大致思路就是这样,项目的github地址:https://github.com/lijialinneu/MyApplication,欢迎下载。(因为android版本的关系,不保证适配所有机型)




    补充:DreameraJni小项目

    求学时期写过一个小项目,主要用到了上问提及的jni+opencv技术,不过功能更加强大,还可以实现图片的水印效果。代码年久失修,已经不能正常运行了,但是一些解决问题的思路,还是可以参考借鉴的。

    项目地址:
    https://gitee.com/lijialin/DreameraJni


    主要有以下几个功能:

    1、使用百度地图api,实现地图聚合、选点,展示文字介绍(实际图片有出入)
    在这里插入图片描述

    2、调用系统相机,添加图层拍照
    在这里插入图片描述

    3、实现一个简单的水印叠加效果,包含简单的滤镜
    在这里插入图片描述

    4、分享图片至微信等

    拜拜~

    这里写图片描述

    展开全文
  • OpenCV提供很多边缘检测的滤波函数,比如 Laplacian,Sobel,Scharr,Canny等。这些函数会将非边缘区域转化为黑色,将边缘区域转为白色或其他颜色**。但是,这些函数容易将噪声错误的识别为边缘,所以在进行边缘检测...
  • OpenCV中对于图像的基本角点和边缘检测的方式分为:高斯差分,Canny边缘检测器,Sobel算子和Harris角点。 边缘其实就是我们一副图片中像素亮度变化最明显的点:例如人脸和人脸之外的背景的像素亮度,发生改变的临界...
  • 本文所采用的技术大体来源于网络上另一作者的开源项目,https://pqpo.me/2017/09/11/opencv-border-recognition/#reward,只是对其智能裁剪...OpenCV 的全称是 Open Source Computer Vision Library,是一个使用 C++
  • Canny算法边缘检测 Canny边缘检测算法是John F. Canny于 1986 年开发出来的一个多级边缘检测算法。截止2014年8月, Canny发表的该篇论文,已被引用19000余次。Canny 创立了边缘检测计算理论(Computational theory ...
  • 边缘检测 什么是图像的边缘? 图像的边缘是图像最基本的特征之一。所谓边缘(或边沿)是指周围像素灰度有跳跃性变化或“屋顶”变化的那些像素的集合。边缘是图像局部强度变化最明显的地方,它主要存在于目标与目标...
  • 在网上找资料学习利用opencv开发Android还算挺顺利的,以下是今天在Android平台上利用opencv对图片进行边缘检测、灰度变换、缩小的实现。运行结果如下: 代码如下:MainActivity:package com.cdq.opencvtest; ...
  • OpenCV边缘检测算法

    2019-07-10 14:54:15
    首页博客学院CSDN学院下载论坛APPCSDN问答商城活动VIP会员专题招聘ITeyeGitChatGitChat图文课写博客 消息 评论关注点赞回答系统通知登录注册我的关注我的...转一种基于边缘的模版匹配算法2018年09月29日 16:38:19 ...
  • 先读取视频,再将视频的每一帧图片灰度化,然后进行模糊降噪,再然后进行Canny边缘检测,输出每一帧画面。 /** *Copyright (c) 2018 Young Fan.All Right Reserved. *Filename: 7 对视频进行Canny边缘检测 *...
  • Opencv--Canny边缘检测

    2018-09-28 20:25:19
    该Canny边缘检测是由约翰·F·坎尼在1986年也知道很多的开发最佳的检测,坎尼算法旨在满足三个主要标准: 低错误率:意味着只有现有边缘的良好检测。 良好的定位:检测到的边缘像素与实际边缘像素之间的距离必须...
  • 轮廓检测函数: Imgproc. findContours(image, contours, hierarchy, mode, method) 通过轮廓检测找到面积最大的轮廓并绘制轮廓: List contours=new ArrayList(); Imgproc.findContours(blurredImage,contours...
  • 一,利用OpenCV提供给Java封装好的调用摄像头接口,获取MAt @Override public void onCameraViewStarted(int width, int height) { rgba = new Mat(width,height, CvType.CV_8UC4); }@Override public void ...
  • 实战深度学习OpenCV(一):canny边缘检测 利用canny边缘检测,我们可以很好地得到哦一个图像的轮廓,下面是基于C++的,这是我们通过这段代码得到的结果: #include "pch.h" #include <...
1 2 3 4 5 ... 20
收藏数 1,850
精华内容 740