Android图片识别应用做法

来源:互联网 发布:mac奶瓶粉底液好用吗 编辑:程序博客网 时间:2024/04/28 05:22

最近由于参加一个小小的创意比赛,用安卓做了一个小小的图片识别应用,主要是通过拍照识别图片中的菜品,还有对象位置查找的东西。之前没有做过安卓,都是拼拼凑凑多篇博客完成的,我也把这个项目的一些过程分享一下。先把功能贴一下,其实就是点击拍照,将照片保存在本地,然后识别出图中的菜品,然后用红色方框圈出来,并显示菜品种类。采用最新的Camera2的API,的确是比Camera好用。



1、界面

我采用了一个SurfaceView用来显示摄像头的预览画面,重写了一个SurfaceView来进行红色方框还有菜品名字的绘制。图片是一个ImageVIew,相当于拍照按钮的功能。

                        

SVDraw,,继承SurfaceView,用于绘制红色方框
package com.hd.hd;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.PixelFormat;import android.graphics.PorterDuff;import android.util.AttributeSet;import android.view.SurfaceHolder;import android.view.SurfaceView;import java.util.List;/*定义一个画矩形框的类*/public class SVDraw extends SurfaceView implements SurfaceHolder.Callback{    protected SurfaceHolder sh;    private int mWidth;    private int mHeight;    public SVDraw(Context context, AttributeSet attrs) {        super(context, attrs);        // TODO Auto-generated constructor stub          sh = getHolder();        sh.addCallback(this);        sh.setFormat(PixelFormat.TRANSPARENT);        setZOrderOnTop(true);    }    public void surfaceChanged(SurfaceHolder arg0, int arg1, int w, int h) {        // TODO Auto-generated method stub      }    public void surfaceCreated(SurfaceHolder sh) {        // TODO Auto-generated method stub        mWidth = this.getWidth();        mHeight = this.getHeight();    }    public void surfaceDestroyed(SurfaceHolder arg0) {        // TODO Auto-generated method stub      }    void clearDraw()    {        Canvas canvas = sh.lockCanvas();        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);        sh.unlockCanvasAndPost(canvas);    }    public void drawLine(List keys, List values)    {        Canvas canvas = sh.lockCanvas();        canvas.drawColor(Color.TRANSPARENT);        Paint p = new Paint();        p.setAntiAlias(true);        p.setColor(Color.RED);        p.setStrokeWidth(6);        p.setStyle(Paint.Style.STROKE);//设置空心        p.setTextSize(160);        Paint p1 = new Paint();        p1.setColor(Color.WHITE);        p1.setTextSize(80);        for(int i = 0;i < keys.size();i++){            String v = values.get(i);            v = v.replace("[","");            v = v.replace("]","");            String[] value = v.split(",");            canvas.drawRect(mWidth - Integer.parseInt(value[3]), Integer.parseInt(value[0]), mHeight - Integer.parseInt(value[1]), Integer.parseInt(value[2]), p);// 正方形            canvas.drawText(keys.get(i), mWidth - Integer.parseInt(value[3]), Integer.parseInt(value[0])-5, p1);        }        sh.unlockCanvasAndPost(canvas);    }}

2、上传图片到服务器,我没有采用JSon的格式,而是直接将图片文件转化为字节数组,发送给服务器。使用一个异步任务,完成后,直接在onPostExcute()方法里绘制。
package com.hd.hd;import android.os.AsyncTask;import android.util.Log;import android.widget.TextView;import org.json.JSONException;import org.json.JSONObject;import java.io.DataOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;import java.util.ArrayList;import java.util.Iterator;import java.util.List;import java.util.UUID;/** * Created by asus on 2017/8/13. */public class MyTask extends AsyncTask {    private static String TAG = "MainActivity";    private File file;  //需要发送的图片    private String result_content;  //服务器返回的结果    private SVDraw surfaceView;     //需要绘制的surfaceview    private TextView tv;        //显示文字    private static final int TIME_OUT = 10 * 1000; // 超时时间    private static final String CHARSET = "utf-8"; // 设置编码    public MyTask(File f,SVDraw s,TextView tv){        this.file = f;        this.surfaceView = s;        this.tv = tv;    }    @Override    protected void onPreExecute() {    }    //doInBackground方法内部执行后台任务,不可在此方法内修改UI    @Override    protected String doInBackground(String... params) {        //调用文件上传方法        result_content = uploadFile(file,"http://13.76.211.62/");        return null;    }    //onProgressUpdate方法用于更新进度信息    @Override    protected void onProgressUpdate(Integer... progresses) {    }    //onPostExecute方法用于在执行完后台任务后更新UI,显示结果    @Override    protected void onPostExecute(String result) {        //由于返回的是一个python的字典形式的字符串,用json来解析        JSONObject obj = null;        List keys = new ArrayList();        List values = new ArrayList();        try {            obj = new JSONObject(result_content);            //json对象的Key的迭代器,用来遍历json            Iterator it = obj.keys();            while (it.hasNext()) {                String key = (String) it.next();                String value = obj.getString(key);                keys.add(key);                values.add(value);            }        } catch (JSONException e) {            e.printStackTrace();        }        //绘制图形        surfaceView.clearDraw();        surfaceView.drawLine(keys,values);        tv.setText("搭配很赞哦");    }    //onCancelled方法用于在取消执行中的任务时更改UI    @Override    protected void onCancelled() {    }    /**     * 上传图片文件到服务器     * @param file     * @param RequestURL     * @return     */    public static String uploadFile(File file, String RequestURL) {        String result = null;        String BOUNDARY = UUID.randomUUID().toString(); // 边界标识 随机生成        String PREFIX = "--", LINE_END = "\r\n";        String CONTENT_TYPE = "multipart/form-data"; // 内容类型        try {            //创建URL连接,指明连接地址            URL url = new URL(RequestURL);            HttpURLConnection conn = (HttpURLConnection) url.openConnection();            //设置http请求的属性为POST            conn.setReadTimeout(TIME_OUT);            conn.setConnectTimeout(TIME_OUT);            conn.setDoInput(true); // 允许输入流            conn.setDoOutput(true); // 允许输出流            conn.setUseCaches(false); // 不允许使用缓存            conn.setRequestMethod("POST"); // 请求方式            conn.setRequestProperty("Charset", CHARSET); // 设置编码            conn.setRequestProperty("connection", "keep-alive");            conn.setRequestProperty("Content-Type", CONTENT_TYPE + ";boundary=" + BOUNDARY);            if (file != null) {                /**                 * 当文件不为空,把文件包装并且上传                 */                Log.i(TAG,"upload");                DataOutputStream dos = new DataOutputStream(conn.getOutputStream());                Log.e(TAG,"not null");                /**                 * 这里重点注意: name里面的值为服务端需要key 只有这个key 才可以得到对应的文件                 * filename是文件的名字,包含后缀名的 比如:abc.png                 */                InputStream is = new FileInputStream(file);                byte[] bytes = new byte[1024];                int len;                while ((len = is.read(bytes)) != -1) {                    dos.write(bytes, 0, len);                }                is.close();                dos.flush();                Log.e(TAG,"sent");                /**                 * 获取响应码 200=成功 当响应成功,获取响应的流                 */                int res = conn.getResponseCode();                Log.e(TAG, "response code:" + res);                Log.e(TAG, "request success");                InputStream input = conn.getInputStream();                StringBuffer sb1 = new StringBuffer();                int ss;                while ((ss = input.read()) != -1) {                    sb1.append((char) ss);                }                result = sb1.toString();                Log.e(TAG, "result : " + result);            }        } catch (MalformedURLException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }        return result;    }}

3、初始化界面、照相机,使得照相机能够实时预览,并实现拍照功能
package com.hd.hd;import android.Manifest;import android.content.Context;import android.content.pm.PackageManager;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.ImageFormat;import android.hardware.camera2.CameraAccessException;import android.hardware.camera2.CameraCaptureSession;import android.hardware.camera2.CameraCharacteristics;import android.hardware.camera2.CameraDevice;import android.hardware.camera2.CameraManager;import android.hardware.camera2.CaptureRequest;import android.hardware.camera2.CaptureResult;import android.hardware.camera2.TotalCaptureResult;import android.media.Image;import android.media.ImageReader;import android.os.Build;import android.os.Bundle;import android.os.Environment;import android.os.Handler;import android.os.HandlerThread;import android.support.annotation.NonNull;import android.support.annotation.RequiresApi;import android.support.v4.app.ActivityCompat;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.util.SparseIntArray;import android.view.Surface;import android.view.SurfaceHolder;import android.view.SurfaceView;import android.view.View;import android.widget.ImageView;import android.widget.TextView;import android.widget.Toast;import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.nio.ByteBuffer;import java.util.Arrays;public class MainActivity extends AppCompatActivity{    private static final SparseIntArray ORIENTATIONS = new SparseIntArray();    private String TAG = "MainActivity";    ///为了使照片竖直显示    static {        ORIENTATIONS.append(Surface.ROTATION_0, 90);        ORIENTATIONS.append(Surface.ROTATION_90, 0);        ORIENTATIONS.append(Surface.ROTATION_180, 270);        ORIENTATIONS.append(Surface.ROTATION_270, 180);    }    private SurfaceView mSurfaceView;    private SurfaceHolder mSurfaceHolder;    private CameraManager mCameraManager;//摄像头管理器    private Handler childHandler, mainHandler;    private String mCameraID;//摄像头Id 0 为后  1 为前    private ImageReader mImageReader;    private CameraCaptureSession mCameraCaptureSession;    private CameraDevice mCameraDevice;    private SVDraw hSurfaceView;    private MyTask myTask;    private CaptureRequest.Builder captureRequestBuilder;    private TextView tv;    private final int DRAW_ORDER = 10;    private Handler myHandler;    private ImageView imageView;    private String dir = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Healthy_d/";    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //此步骤非常重要,安卓不用自动帮你创建文件夹来保存拍照的照片        File dirFirstFolder = new File(dir);//方法二:通过变量文件来获取需要创建的文件夹名字        if(!dirFirstFolder.exists())        { //如果该文件夹不存在,则进行创建            dirFirstFolder.mkdirs();//创建文件夹        }        //Android 6后有些敏感的权限不能随意分配,必须向用户发送请求赋予        //这里请求用户赋予拍照,读写内存卡,连接网络的权限,其实只有拍照权限需要向用户请求,但是有备无患吧        if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {            Log.e(TAG,ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)+"");            ActivityCompat.requestPermissions(MainActivity.this,                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},                    43);        }        if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {            ActivityCompat.requestPermissions(MainActivity.this,                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},                    44);        }        if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED) {            ActivityCompat.requestPermissions(MainActivity.this,                    new String[]{Manifest.permission.INTERNET},                    45);        }        initVIew();    }    /**     * 初始化视图     */    private void initVIew() {        HandlerThread handlerThread = new HandlerThread("Camera2");        handlerThread.start();        childHandler = new Handler(handlerThread.getLooper());        //mSurfaceView        mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView);        hSurfaceView = (SVDraw) findViewById(R.id.mySurfaceView);        imageView = (ImageView) findViewById(R.id.btngal);        tv = (TextView)findViewById(R.id.textview);        //设置ImageView监听器,点击图片,拍照        imageView.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                Toast.makeText(MainActivity.this, "正在识别,请稍等", Toast.LENGTH_LONG).show();                if (mCameraDevice == null) return;                // 创建拍照需要的CaptureRequest.Builder                try {                    captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);                    // 将imageReader的surface作为CaptureRequest.Builder的目标                    captureRequestBuilder.addTarget(mImageReader.getSurface());                    // 自动对焦                    captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);                    // 自动曝光                    captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);                    // 获取手机方向                    int rotation = getWindowManager().getDefaultDisplay().getRotation();                    // 根据设备方向计算设置照片的方向                    captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));                    //拍照                    CaptureRequest mCaptureRequest = captureRequestBuilder.build();                    mCameraCaptureSession.capture(mCaptureRequest, mSessionCaptureCallback, childHandler);                } catch (CameraAccessException e) {                    e.printStackTrace();                }            }        });        mSurfaceHolder = mSurfaceView.getHolder();        mSurfaceHolder.setKeepScreenOn(true);        // mSurfaceView添加回调        mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {            @Override            public void surfaceCreated(SurfaceHolder holder) { //SurfaceView创建                // 初始化Camera                initCamera2();            }            @Override            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {            }            @Override            public void surfaceDestroyed(SurfaceHolder holder) { //SurfaceView销毁                // 释放Camera资源                if (null != mCameraDevice) {                    mCameraDevice.close();                    mCameraDevice = null;                }            }        });    }    //拍照时,可以对照片进行操作,这里可以不写,因为我没对其进行操作    private CameraCaptureSession.CaptureCallback mSessionCaptureCallback =            new CameraCaptureSession.CaptureCallback() {                @Override                public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,                                               TotalCaptureResult result) {}                @Override                public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request,                                                CaptureResult partialResult){}};    /**     * 初始化Camera2     */    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)    private void initCamera2() {        HandlerThread handlerThread = new HandlerThread("Camera2");        handlerThread.start();        childHandler = new Handler(handlerThread.getLooper());        mainHandler = new Handler(getMainLooper());        mCameraID = "" + CameraCharacteristics.LENS_FACING_FRONT;//后摄像头        mImageReader = ImageReader.newInstance(mSurfaceView.getWidth(), mSurfaceView.getHeight(), ImageFormat.JPEG,1);        mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { //可以在这里处理拍照得到的临时照片 例如,写入本地            @Override            public void onImageAvailable(ImageReader reader) {                Image image = reader.acquireNextImage();                ByteBuffer buffer = image.getPlanes()[0].getBuffer();                byte[] bytes = new byte[buffer.remaining()];                buffer.get(bytes);//由缓冲区存入字节数组                Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);                String fileName = "test";                File file = new File(dir + fileName + ".jpg");                String state = Environment.getExternalStorageState();                //如果状态不是mounted,无法读写                if (!state.equals(Environment.MEDIA_MOUNTED)) {                    return;                }                FileOutputStream out = null;                try {                    out = new FileOutputStream(file);                    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);//转化为jpeg图片                    out.flush();                    out.close();                    image.close();//一定要记得关,否则会出现程序崩溃                } catch (FileNotFoundException e) {                    e.printStackTrace();                } catch (IOException e) {                    e.printStackTrace();                }                new MyTask(file,hSurfaceView,tv).execute();            }        }, mainHandler);        //获取摄像头管理        mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);        try {            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {                ActivityCompat.requestPermissions(this,                        new String[]{Manifest.permission.CAMERA},                        42);            }            //打开摄像头            mCameraManager.openCamera(mCameraID, stateCallback, mainHandler);        } catch (CameraAccessException e) {            e.printStackTrace();        }    }    /**     * 当发送权限请求用户响应时,回调该函数     * @param requestCode     * @param permissions     * @param grantResults     */    @Override    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {        if (requestCode == 42) {            Toast.makeText(this, "CAMERA PERMISSION GRANTED", Toast.LENGTH_SHORT).show();            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {                //申请成功,可以拍照                Log.i(TAG,"apply camera success");                CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);                try {                    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {                        Toast.makeText(this, "CAMERA PERMISSION DENIED", Toast.LENGTH_SHORT).show();                    }                    mCameraManager.openCamera(mCameraID, stateCallback, mainHandler);                } catch (CameraAccessException e) {                    e.printStackTrace();                }            } else {                Toast.makeText(this, "CAMERA PERMISSION DENIED", Toast.LENGTH_SHORT).show();            }            return;        }        super.onRequestPermissionsResult(requestCode, permissions, grantResults);    }    /**     * 摄像头创建监听     */    private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {        @Override        public void onOpened(CameraDevice camera) {//打开摄像头            mCameraDevice = camera;            //开启预览            takePreview();        }        @Override        public void onDisconnected(CameraDevice camera) {//关闭摄像头            if (null != mCameraDevice) {                mCameraDevice.close();                mCameraDevice = null;            }        }        @Override        public void onError(CameraDevice camera, int error) {//发生错误            Toast.makeText(MainActivity.this, "摄像头开启失败", Toast.LENGTH_SHORT).show();        }    };    /**     * 开始预览     */    private void takePreview() {        try {            // 创建预览需要的CaptureRequest.Builder            final CaptureRequest.Builder previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);            // 将SurfaceView的surface作为CaptureRequest.Builder的目标            previewRequestBuilder.addTarget(mSurfaceHolder.getSurface());//            previewRequestBuilder.addTarget(mImageReader.getSurface());            // 创建CameraCaptureSession,该对象负责管理处理预览请求和拍照请求            mCameraDevice.createCaptureSession(Arrays.asList(mSurfaceHolder.getSurface(), mImageReader.getSurface()), new CameraCaptureSession.StateCallback() // ③            {                @Override                public void onConfigured(CameraCaptureSession cameraCaptureSession) {                    if (null == mCameraDevice) return;                    // 当摄像头已经准备好时,开始显示预览                    mCameraCaptureSession = cameraCaptureSession;                    try {                        // 自动对焦                        previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);                        // 打开闪光灯                        previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);                        // 显示预览                        CaptureRequest previewRequest = previewRequestBuilder.build();                        mCameraCaptureSession.setRepeatingRequest(previewRequest, null, childHandler);                    } catch (CameraAccessException e) {                        e.printStackTrace();                    }                }                @Override                public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {                    Toast.makeText(MainActivity.this, "配置失败", Toast.LENGTH_SHORT).show();                }            }, childHandler);        } catch (CameraAccessException e) {            e.printStackTrace();        }    }}

4、AndroidManifest.xml
                                                                                                    

今天代码先分享到那么多,明天给大家分享一下Camera2的架构。有不懂的可以评论,一起讨论。







原创粉丝点击