• Android——简易照相机


    Android——简易照相机

    实验名称:Android多媒体应用-拍照
    实验目的:设计一个简易照相机
    实验内容:利用Camera类设计一个简易照相机,通过SurfaceView组件实现取景预览,拍照保存等功能,拍照保存时按camera+序号.jpg的方式保存照片,序号自动增加,确保拍照保存不会覆盖前面的照片。
    实验日期:2019年5月

    一、实验原理

    该相机程序,需要应用surfaceview组件来预览摄像头拍摄到的景物,再使用回调接口surfaceholder.callback监控取景视图。使用照片服务类Camera实现拍照功能,并通过imageview组件显示。最后通过onPicturetaken方法,将拍摄的照片保存至手机。编写getFile和checkFileName方法,用来对文件名进行处理,实现了实验对于文件名的要求。

    二、实验过程记录

    2.1 布局文件

    为实现基本的相机程序效果,使用最简单的线性布局,通过组建的嵌套生成需要的布局。首先指定最外层的线性布局对齐方式为垂直布局,之后再编写textview组件显示程序的标题,然后嵌套一层水平对齐方式的线性布局,在其中加入两个button组件用于防止“拍照”和“退出”按钮。之后在第一层线性布局中加入ImageView组件用于相片的显示,最后加入surfaceView组件用于取景预览。

    2.2 控制文件

    2.2.1 实例化对象

    在MainActivity.java文件中修改文件,以实现对程序的控制。首先在公共类 “MainActivity” 扩展 “Activity” 之后,添加 “implements SurfaceHolder.Callback” ,实现Callback接口,以处理取景预览功能。之后,如下图所示实例化所需的对象,并声明用于保存图片文件的路径。
    在这里插入图片描述

    图2.1

    在上图2.1中,分别实例化了用于拍照功能的“Camera”对象“mCamera”、用于取景预览的“SurfaceView”对象“surfaceView”、用于回调图片参数的“SurfaceHolder”对象“holder”、用于显示图片的“ImageView”对象“mImageView”、用于拍照退出的按钮对象“cameraBtn”和“exitBtn”,以及用于记录图片保存路径的对象“path”。

    2.2.2 重载构造函数

    对构造函数进行修改,使其实现“关联布局文件和控制文件,注册回调监听器”的功能。如图2.2所示:
    在这里插入图片描述

    图2.2

    首先,通过findViewById方法关联图1中的相关组件,并为“cameraBtn”和“exitBtn”分别设置监听事件,最后设置SurfaceHolder对象的相关内容。创建SurfaceHolder对象“holder”,注册回调监听器并设置SurfaceHolder的类型。

    2.2.3 编写mClick类

    为按钮的监听事件编写mClick类,以实现拍照和退出功能。如图2.3所示:
    在这里插入图片描述

    图2.3

    上图中,构造了一个继承于OnClickListener的mClick类。通过判断点击按钮传入的参数v,可以对拍照和退出进行区分,拍照使用takePicture方法,退出则调用exit()函数,对相机资源进行重置。

    2.2.4 编写jpegCallback类

    在图3编写的mClick类中,takePicture方法用到了获取照片事件的回调接口jpegCallback,现在需要对jpegCallback类进行编写。如图2.4所示:
    在这里插入图片描述

    图2.4

    在上图中,编写了继承于Camera. PictureCallback的类“jpegCallback” ,通过重载onPictureTaken函数,实现了拍照、显示图片以及保存图片的功能。

    2.2.5重载surfaceCreated方法

    创建相机对象之后,会默认调用三个构造函数,分别为surfaceCreated、surfaceChanged和surfaceDestroyed,首先重载surfaceCreated函数,如图2.5所示。
    在这里插入图片描述

    图2.5

    上图中,首先释放掉相机资源,然后开启摄像头。之后尝试进行相机预览,如果捕获到预览错误信息,则输出报错。其中的ReleaseCamera()函数用来重置相机,释放相机资源。

    2.2.6重载surfaceChanged方法

    相机画面发生变化时,将触发surfaceChanged方法,如下图2.6所示重载surfaceChanged方法。
    在这里插入图片描述

    图2.6

    重载的surfaceChanged方法会调用initCamera函数,重新设置预览的画面。重置包括预览画面的格式、大小、预览框大小等信息。最后调用startPreview方法开始预览。

    2.2.7重载surfaceDestroyed方法

    关闭相机时,会触发surfaceDestroyed方法,如下图2.7所示重载surfaceDestroyed方法。
    在这里插入图片描述

    图2.7

    可以看到,重载的surfaceDestroyed方法并没有实现具体的功能。

    2.3添加权限

    由于程序中用到了SD卡读写和相机的调用,所以需要在程序文件中声明需要申请的权限。包括SD卡读取、写入权限,以及相机调用权限。如下图2.8所示,在AndroidManifest.xml文件中添加下列信息,以申请权限。
    在这里插入图片描述

    图2.8

    2.4安装APK测试

    由于AndroidStudio模拟器在电脑上经常卡顿,所以我决定使用安卓手机进行测试。首先将编译好的项目打包成APK文件,然后将该APK文件发送至手机端安装。安装截图如图2.9所示,可以看到安装时会提示该APP将获取的手机权限。
    在这里插入图片描述

    图2.9

    点击安装即可完成APK文件的安装。

    三、实验中存在的问题及解决方案

    3.1 预览倾斜问题及解决过程

    安装完成,运行该APP程序,点击拍照按钮,可以看到如图3.1所示的手机界面。其中上面的小画面是imageView组件显示的拍完的照片,下面的大图片是surfaceView组件显示的相机预览照片。
    在这里插入图片描述

    图3.1

    拍摄完成之后,推迟该APP,并在手机自带的文件管理界面,找到test文件夹(如图3.2),这是刚刚图片拍摄之后,程序生成的文件夹,用于保存图片文件。在test文件夹下,可以看到刚刚生成的camera.jpg文件(如图3.3所示) ,浏览该文件,可以查看之前拍摄的图片(如图3.4) 。
    在这里插入图片描述

    图3.2

    在这里插入图片描述

    图3.3

    在这里插入图片描述

    图3.4

    查看该图片,发现拍摄出来的图片,与正常视角相差了90°,而且在图片拍摄和预览过程中,图片和正常视角都是不相符的,都旋转了90°。
    经过查阅资料发现,主要是由于相机传感器像素坐标信息,与手机显示屏像素坐标不相符导致的。图3.5展示了两像素坐标的不同之处。
    在这里插入图片描述

    图3.5

    (图片引用于:https://blog.csdn.net/xx326664162/article/details/53350551)
    相机传感器获取得到图像,就确定了这幅图像每个坐标的像素值,但是要显示到手机屏幕上,就需要按照屏幕的坐标系来显示,于是就需要将相机传感器的坐标系逆时针旋转90度,才能显示到屏幕的坐标系上。
    在安卓程序中,提供了相关的setRotation()方法和setDisplayOrientation()方法。其中,setRotation()方法的作用是将相机传感器获取到的位图坐标旋转一定的角度,而setDisplayOrientation()方法的作用是将预览时的画面旋转一定的角度。通过调用这两个方法,我们就可以实现图片的正常拍摄和预览了。如图3.6所示,在原有程序的基础上添加该方法, 再生成APK文件进行测试。
    在这里插入图片描述

    图3.6

    重新安装APK文件,测试运行,运行结果如图3.7所示。可以看到,预览的图片已经可以正常显示了。然后再到test文件夹中查看camera.jpg文件(如图3.8) ,同样,拍摄的图片也是可以正常显示了。
    在这里插入图片描述

    图3.7

    在这里插入图片描述

    图3.8

    3.2 文件命名问题及解决过程

    目前的程序所能实现的功能,只能拍摄一张照片,如果拍摄多张照片,由于文件名相同,后面的文件就会将前面已存在的文件覆盖掉,很不方便。本次实验要求的是“拍照保存时按camera+序号.jpg的方式保存照片,序号自动增加”。

    3.2.1问题分析

    为了实现这个功能,需要在每次文件保存使,都要考虑我们需要生成的文件名是什么。如果企图保存当前程序生成的图片数量,在关闭APP之后,数据不易保存。所以我想的解决办法是:每次生成的文件名都需要经历两次函数,第一个函数需要返回所需文件夹中的所有文件,第二个函数需要判断指定的文件在该文件夹中是否存在,如果存在则返回一个我们需要的、正确的文件名。

    3.2.2测试程序

    ① getFile()函数
    为了不破坏原有的安卓程序,我在Eclipse中进行相关函数的编写和测试。首先编写第一个函数getFile(),该函数需要输入文件夹的路径字符串,输出该文件夹下的所有文件名。程序文件如图3.9所示:
    在这里插入图片描述

    图3.9

    该程序,首先通过path参数获取指定的文件对象,再通过listFiles()方法列出该文件夹下,所有的文件,包括文件夹、普通文件等等,并将其保存在array数组中。之后逐个判断是否为普通文件,将文件名保存在list中,最后返回list。
    现在,将path指定为桌面上的android文件夹,调用getFiles方法,查看程序输出。
    图3.10是android文件夹下的文件信息,图11是程序输出的信息。
    在这里插入图片描述

    图3.10

    在这里插入图片描述

    图3.11

    可以看到,除了临时文件,普通文件均可以正常输出。
    ② checkFileName方法
    下面实现checkFileName函数,该方法的作用是:在文件名字符串中,判断所需要的字符串是否存在,如果存在则返回原文件名;不存在则重新调用,利用index参数,实现文件名的递增过程。最后返回一个正确的、合理的文件名。
    checkFileName方法的实现过程是:首先将传入的文件名参数根据“.”进行分割,分成“.”之前的字符串+起始索引数字index+“.”字符+“.”之后的字符串,然后判断整个文件名是否包含于长文件名参数中,如果存在则再次调用该方法,同时起始索引数字加一;如果不存在,就直接返回上面连接好的文件名。
    现在将checkFileName方法和getFile进行合并,通过main函数进行调用测试。首先在桌面的android目录下新建一个mydir目录,在mydir目录下新建一个test.txt文件,然后运行java程序进行测试。
    在这里插入图片描述

    图3.12

    上图3.12展示了当前的mydir目录以及Java程序的运行结果。由于起始索引参数index设置的是0,而文件夹下只有一个test.txt文件,所以程序返回的结果“test.txt”是正确的。为了测试的准确性,我在mydir目录下又新建了一个test0.txt文件,如果程序功能正常,应该是会返回test1.txt。下面运行程序测试:
    在这里插入图片描述

    图3.13

    可以看到,程序运行结果正确。

    3.2.3安卓测试

    现在可以放心地将代码移植到AndroidStudio程序中去了。在原有的onPictureTaken()方法的基础上,增加红色框中的内容,如图3.14所示。其含义为:通过getFile方法查找手机"/sdcard/test"目录下的所有文件,将结果返回至list中。然后通过调用checkFileName方法,检查list中是否有符合filename的文件名(filename已在之前定义),checkFileName方法会返回合理的文件名,再通过字符串连接,将完整的路径保存到path字符串中,作为完整的文件名路径。
    为了更清晰的显示文件保存的路径和文件名信息,我在布局文件中增加了一个textview组件,在path生成之后,将path输出到textview显示,这样就会对文件的路径信息更加直观。
    最后,将getFile和checkFileName方法都移植到java文件中,就可以运行测试了。
    在这里插入图片描述

    图3.14

    同样重新生成APK文件测试运行,进行多次拍摄,下面是我拍摄第8次时的界面截图(图3.15)。
    在这里插入图片描述

    图3.15

    因为文件名起始索引为0,所以第8次生成的文件名应该是camera7.jpg,可以从APP界面中看到文件的保存路径。现在在test文件夹下,会有8张我刚刚拍摄的图片,命名方式为camera+i。图3.16展示了test文件夹下的文件信息,文件的保存也是正常的。
    在这里插入图片描述

    图3.16

    3.3 预览画面卡顿问题及解决过程

    之前的多次测试中,我发现该程序的一个bug:在每次点击“拍照“按钮之后,预览画面就会卡住(静止且没有响应),而且如果此时再次点击”拍照“或”退出“按钮,程序就会意外退出。
    经过查阅资料,我了解到,该问题是由以下原因造成的:
    在执行拍照命令时,会调用camera.takePicture()方法,该方法在执行过程中会调用camera.stopPreview来获取拍摄帧数据,从而进行数据回调。而调用camera.stopPreview方法,就会暂停相机的预览,并表现为预览画面卡住。如果此时用户点击了按钮的话,也就会再次调用camera.takepicture方法,由于相机还没有开始预览,没有进行相关进程的初始化,就会出现之前遇到的意外退出问题。
    解决的方法也很简单,既然camera.takePicture () 方法调用了camera.stopPreview来停止预览,那么只要在camera.takePicture()方法结束之后,手动调用一次camera.startPreview方法,来开启相机预览就可以了。
    于是进行添加图3.17红框中的部分:
    在这里插入图片描述

    图3.17

    再次生成APK文件进行测试,图3.18为测试结果。
    在这里插入图片描述

    图3.18

    这是我拍摄第9张图片之后的界面。上方的imageview组件显示的是第9张的图片,而下面的surfaceView显示的是此时的预览画面。此时如果我连续拍摄,继续点击“拍照“按钮,程序也不会因为bug而意外退出了。实现了连续拍照的功能。

    四、实验结果

    现在,程序已经实现了实验所要求的功能,我将目前程序所能实现的效果录制成GIF图片并进行上传,程序的效果如下所示:http://47.95.13.239/Study/Android/test.gif

    五、源代码

    我已经将整理过的该项目的源代码上传到了GitHub:
    https://github.com/ZHJ0125/AndroidSimpleCameraApp

    5.1布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/LinearLayout1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:layout_gravity="center_horizontal"
            android:gravity="center_horizontal"
            android:text="拍照测试"
            android:textSize="20dp"
            tools:context=".MainActivity"/>
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/test"
            android:textSize="15dp"/>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:gravity="center_horizontal">
            <Button
                android:id="@+id/btn1"
                android:layout_width="110dp"
                android:layout_height="wrap_content"
                android:text="拍照"/>
            <Button
                android:id="@+id/btn2"
                android:layout_width="110dp"
                android:layout_height="wrap_content"
                android:text="退出"/>
        </LinearLayout>
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/imageView1"
            android:layout_gravity="center" />
        <SurfaceView
            android:id="@+id/surfaceView1"
            android:layout_width="320dp"
            android:layout_height="240dp"
            android:layout_gravity="center_horizontal"/>
    </LinearLayout>
    

    5.2程序控制文件

    package zhj.com.simplecamera;
    
    import android.os.Bundle;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.PixelFormat;
    import android.hardware.Camera;
    import android.hardware.Camera.PictureCallback;
    import android.util.Log;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;
    import android.widget.Button;
    import android.widget.ImageView;
    import android.app.Activity;
    import android.widget.TextView;
    import java.io.BufferedOutputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.File;
    import java.util.ArrayList;
    
    public class MainActivity extends Activity implements SurfaceHolder.Callback{
        Camera mCamera = null;
        SurfaceView surfaceView;
        SurfaceHolder holder;
        ImageView mImageView;
        Button cameraBtn, exitBtn;
        TextView textView;
        int i = 0;
        String filename = "camera.jpg";    //图片文件名
        String path = "";   //图片保存路径
    
        /**
         *  Override the onCreate
         *  function:重载构造函数,关联布局文件和控制文件,注册回调监听器
         **/
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //关联ID
            mImageView = (ImageView)findViewById(R.id.imageView1);
            cameraBtn = (Button)findViewById(R.id.btn1);
            exitBtn = (Button)findViewById(R.id.btn2);
            cameraBtn.setOnClickListener(new mClick()); //设置监听事件
            exitBtn.setOnClickListener(new mClick());
            surfaceView = (SurfaceView)findViewById(R.id.surfaceView1);
            textView = (TextView)findViewById(R.id.test);
            //System.out.println("begin to holder...");
            holder = surfaceView.getHolder();
            holder.addCallback(this);
            holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        }
    
    
        /**
         *  class:mClick
         *  function:设置按键监听事件,对应拍照和退出按钮
         * */
        class mClick implements OnClickListener{
            @Override
            public void onClick(View v) {
                if (v == cameraBtn){
                    mCamera.takePicture(null, null, new jpegCallback());    //拍照
                    //surfaceCreated(holder);         //调用构造函数
                }
                else if (v == exitBtn){
                    exit(); //退出程序
                }
            }
        }
        void exit(){
            mCamera.release();
            mCamera = null;
        }
    
    
        /**
         * class:jpegCallback
         * function:实现拍照和保存图片功能
         * */
        public class jpegCallback implements PictureCallback{
            @Override
            public void onPictureTaken (byte[] data, Camera camera){
                Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                ArrayList<String> list = getFile("/sdcard/test");
                path = "/sdcard/test/" + checkFileName(list, filename, 0);
                textView.setText("文件保存路径:" + path);
                try{
                    BufferedOutputStream outStream = new BufferedOutputStream(new FileOutputStream(path));
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 80, outStream);
                    outStream.flush();
                    outStream.close();
                    mImageView.setImageBitmap(bitmap);  //在ImageView显示拍照的图片
                    mCamera.startPreview();
                }catch (Exception e){
                    Log.e("error", e.getMessage());
                }
            }
        }
    
        private static ArrayList<String> getFile(String path){   //检测路径下所有的文件,并返回文件名列表
            File file = new File(path);       // 获得指定文件对象
            File[] array = file.listFiles();       // 获得该文件夹内的所有文件
            ArrayList<String> list = new ArrayList<String>();
            for(int i=0;i<array.length;i++){
                if(array[i].isFile()){       //如果是文件,将文件名保存在列表中
                    list.add(array[i].getName());
                }
            }
            return list;   //返回储存文件名的列表
        }
        private static String checkFileName(ArrayList<String> names,String name,int index) {   //检测文件是否存在,并返回合理的文件名
            if(names.contains(name.substring(0,name.indexOf("."))+index+name.substring(name.indexOf("."),name.length()))) {
                //System.out.println(name.substring(0,name.indexOf("."))+index+name.substring(name.indexOf("."),name.length())+ "Hello");
                //name.substring(0,name.indexOf("."))  -->  返回"."之前的字符串
                //文件存在,再次调用checkFileName方法
                name = checkFileName(names,name,index+1);
            } else {
                //文件不存在,返回合理的文件名
                return name.substring(0,name.indexOf("."))+index+name.substring(name.indexOf("."),name.length());
            }
            return name;
        }
    
    
        /**
         *  Override the surfaceCreated
         *  function:创建相机时触发,开启相机预览功能
         * */
        @Override
        public void surfaceCreated(SurfaceHolder holder){
            if (mCamera != null){
                ReleaseCamera();    //首先释放相机资源
            }
            mCamera = Camera.open();    //开启摄像头
            //System.out.println("
    
    
    Camera.open() is OK !!!
    
    
    ");
    
            //mCamera = android.hardware.Camera.open();
            //mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
            //System.out.println("Camera is OK!");
            try{
                mCamera.setPreviewDisplay(holder);  //设置相机预览
            }catch (IOException e){
                System.out.println("预览错误");
            }
        }
        private void ReleaseCamera()    //重置相机
        {
            if(mCamera != null)
            {
                mCamera.release();
                mCamera = null;
            }
        }
    
    
        /**
         *  Override the surfaceChanged
         *  function:当画面发生改变时触发,重置相机参数
         * */
        @Override
        public void surfaceChanged (SurfaceHolder holder, int format, int width, int height){
            //System.out.println("Camera is going to ready...");
            initCamera();   //重置相机参数
        }
        //设置相机参数
        public void initCamera(){
            //System.out.println("here1....");
            Camera.Parameters parameters = mCamera.getParameters();
            parameters.setPictureFormat(PixelFormat.JPEG);  //照片格式
            parameters.setPreviewSize(320, 240);    //预览规格大小
            parameters.setPictureSize(320, 240);    //图片大小
            parameters.setRotation(90);           //设置照片数据旋转90°
            mCamera.setParameters(parameters);
            mCamera.setDisplayOrientation(90);    //设置浏览画面水平转90°
    
            mCamera.startPreview(); //开始预览
        }
    
    
        /**
         *  Override the surfaceDestroyed
         *  function:关闭相机时触发,空
         * */
        @Override
        public void surfaceDestroyed(SurfaceHolder holder){    }    //消灭相机时触发
    
    }
    

    六、待解决问题和设想

    6.1 待解决问题

    ①功能单一
    该APP还有很多不完善的地方,功能非常单一,只能实现简单的拍照功能。
    ②版本兼容问题
    本次实验中所有的APP测试均在安卓7.1.2版本的手机上进行,在新建工程时,我将最底支持版本选择为API 14和Android 4.0,但是在Android版本为4.4.2的手机上测试时,依然会出现很多问题,导致程序异常结束。程序还未在更高Android版本的手机上测试过,尚不清楚将会有什么bug。版本的兼容问题是亟待解决的关键问题之一。
    ③关于连拍功能
    报告中已经说明了连续拍照的实现过程,到目前为止,程序已经实现该过程:点击“拍照“按钮  拍摄照片  imageView显示照片 surfaceView启动预览 循环拍摄。但在调试过程中我发现,如果点击”拍摄“按钮过快,可能导致相机预览camera.startPreview还未开始,就点击了”拍摄“按钮,于是就会导致跟之前相同的问题,让程序意外结束。这也是待解决的问题之一。

    6.2 设想

    ①版本问题
    现在版本问题是最严重的一个问题,因为大家使用的Android手机,其Android版本必然不同,如果不能解决该问题,可能导致很多手机无法正常运行该程序,或者会出现很多bug。现在需要查阅一下Android版本的资料,并进行兼容性的改进。
    ②加功能
    现在功能很单一,以后希望能加上摄像等功能,做到比安卓自带的相机功能更加完善(笑)。

    七、实验总结

    整个实验项目进行的还算顺利,一开始还有很多问题,最后基本上都通过查阅资料以及和同学的交流,解决了问题。
    问题还算挺多的,主要是一些细节问题导致的,需要对整个工程的结构,以及各个函数的功能都理清楚,才能更好地解决问题。
    调试也有很多技巧,最简便的是使用输出语句判断程序的执行,还有就是通过注释部分功能,判断出问题的位置等等。

    八、部分参考资料

    1. 关于androidcamera相机的方向的理解http://blog.sina.com.cn/s/blog_68f23d9f0102y2cc.html
    2. Java检测文件名是否重复 https://blog.csdn.net/u014804332/article/details/80385217
    3. JAVA中方法的调用 https://www.imooc.com/article/13423
    4. 预览卡住解决办法思路引导
      https://www.jianshu.com/p/586af3a2dc8d?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation
    5. Android相机开发和遇到的坑 https://blog.csdn.net/xx326664162/article/details/53350551
  • 相关阅读:
    【数位dp】Beautiful Numbers @2018acm上海大都会赛J
    【状压dp】Trie 树 @中山纪念中学20170304
    两个给点染色的问题-树上染色与图上染色
    【贪心】经营与开发 @upc_exam_5500
    【二分+拓扑排序】Milking Order @USACO 2018 US Open Contest, Gold/upc_exam_6348
    【并查集】Connectivity @ABC049&amp;ARC065/upcexam6492
    【倍增】T-shirt @2018acm徐州邀请赛 I
    Sparse Coding: Autoencoder Interpretation
    Sparse Coding
    Pooling
  • 原文地址:https://www.cnblogs.com/ZHJ0125/p/12904517.html
Copyright © 2020-2023  润新知