【CNTK/OpenCV/Android】Server+Android+CNN实现移动端图像识别系统

来源:互联网 发布:航海家软件 编辑:程序博客网 时间:2024/05/21 14:58

使用CNTK训练的网络,和OpenCV提供的图片处理支持。在Android平台将图片上传服务器并返回图片种类。

留自己备忘。

转载请注明出处->http://blog.csdn.net/bless2015

首先是第一个BUG:
用NDK太麻烦,所以讲CNTK和OpenCV接口直接编译好了一个exe,用Java Runtime直接调用。但是除了一个问题,花了半天时间。原因是CNTK函数返回了很多日志信息,这些日志信息如果不读出来,超过JVM分配的缓冲区大小,就会造成程序阻塞。产生的效果就是,虽然已经调用了exe,但是该exe只有在Java程序执行结束后,才会继续执行。

第二个BUG:
Android端经过剪裁处理后的图片上传有白边。这个原因虽然解决了,但是理解的还不是很好。因为在进行Crop处理时,需要传进要裁剪图片size的大小,如果你裁剪的大小超过这个size,那么没事,如果小于这个size,那么会以白边填充。这样传到服务器的图片也有白边,这基本就识别不准了。

Eval.cpp

#include <cv.h>#include <highgui.h>#include "Eval.h"#ifdef _WIN32#include "Windows.h"#endif#include <fstream>   #include <iostream>  using namespace Microsoft::MSR::CNTK;using namespace std;template<typename ElemType>using GetEvalProc = void(*)(IEvaluateModel<ElemType>**);typedef std::pair<std::wstring, std::vector<float>*> MapEntry;typedef std::map<std::wstring, std::vector<float>*> Layer;int main(int argc, char* argv[]){    argc = 0;    std::string app = "D:/cntk/Examples/Image/";    std::string path;    ofstream myfile("D:/get.txt", ios::out);    char *imagevector = argv[1];    IEvaluateModel<float> *model;#ifdef _WIN32    path = app.substr(0, app.rfind("\\"));    const std::string modelWorkingDirectory = path + "/../../Examples/Image/MyTestDNN/Data/";#else // on Linux    path = app.substr(0, app.rfind("/"));    // This relative path assumes launching from CNTK's binary folder, e.g. build/release/bin/    const std::string modelWorkingDirectory = path + "/../../../Examples/Image/MNIST/Data/";#endif    GetEvalF(&model);    const std::string modelFilePath = modelWorkingDirectory + "../Output/Models/02_Convolution";    std::string networkConfiguration;    networkConfiguration += "modelPath=\"" + modelFilePath + "\"";    model->CreateNetwork(networkConfiguration);    std::map<std::wstring, size_t> inDims;    std::map<std::wstring, size_t> outDims;    model->GetNodeDimensions(inDims, NodeGroup::nodeInput);    model->GetNodeDimensions(outDims, NodeGroup::nodeOutput);    auto inputLayerName = inDims.begin()->first;    std::vector<float> inputs;    //读取输入进来的向量值(以“,”分割的)    char *p = NULL;    p = strtok(imagevector, ",");    float f = atof(p);    inputs.push_back(f);    while ((p = strtok(NULL, ",")) != NULL)    {        f = atof(p);        inputs.push_back(f);    }    std::vector<float> outputs;    Layer inputLayer;    inputLayer.insert(MapEntry(inputLayerName, &inputs));    Layer outputLayer;    auto outputLayerName = outDims.begin()->first;    outputLayer.insert(MapEntry(outputLayerName, &outputs));    model->Evaluate(inputLayer, outputLayer);    for (auto& value : outputs)    {        cout << value << endl;        myfile << value <<",";        myfile.flush();    }    return 0;}

Vector.cpp

#include <cv.h>#include <highgui.h>#include<io.h>#include <string.h> #include <fstream>   #include <iostream>using   namespace   std;using   namespace   cv;int main(int argc, char* argv[]){    char *classimage = argv[1];    IplImage *image, *imageresize = 0;    image = cvLoadImage(classimage, 1);    imageresize = cvCreateImage(cvSize(28, 28), IPL_DEPTH_8U, 3);    cvResize(image, imageresize, CV_INTER_LINEAR);    IplImage* gray = cvCreateImage(cvGetSize(imageresize), IPL_DEPTH_8U, 1);//用原图像指针创建新图像    cvCvtColor(imageresize, gray, CV_BGR2GRAY);    //提取向量    stringstream ss;    string s;    ofstream myfile("D:\\vector.txt", ios::out);    for (int m = 0; m < gray->height; m++){        for (int j = 0; j < gray->width; j++){            //获取灰度图的像素            CvScalar dPixelVal = cvGet2D(gray, m, j);            char temp[20];            sprintf(temp, "%d", (int)dPixelVal.val[0]);            s.append(temp);            s.append(",");        }    }    s = s.substr(0, s.length() - 1);    //输出向量    myfile << s << endl;    cout << s << endl;    cvReleaseImage(&image);    cvReleaseImage(&imageresize);    system("pause");    return 0;}

服务器端

Servlet

package com.sunyang.servlet;public class UploadServlet extends HttpServlet {    private static final long serialVersionUID = 1L;    String temp = new File("").getAbsolutePath();       String webappPath = temp.replace("bin", "webapps\\Image");    public void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doPost(request, response);    }    public void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        String ImageToVector = "";        String CNTK03 = "";        request.setCharacterEncoding("utf-8");        response.setCharacterEncoding("utf-8");        response.setContentType("text/html;charset=utf-8");        PrintWriter out = response.getWriter();        SmartUpload smartUpload = new SmartUpload();        String msg=request.getParameter("msg");//      out.print(msg);          try {              smartUpload.initialize(this.getServletConfig(), request, response);             smartUpload.upload();              com.jspsmart.upload.File smartFile = smartUpload.getFiles().getFile(0);              if (!smartFile.isMissing()) {                 String saveFileName = "images/" + smartFile.getFileName();                  smartFile.saveAs(saveFileName, SmartUpload.SAVE_VIRTUAL);                  String path = webappPath+File.separator+saveFileName;                String imagePath = path.replace("\\", "/");                System.out.println(imagePath);                String vector = GetModel.transImage(ImageToVector, imagePath);                int family = GetModel.GetResult(CNTK03, vector);                System.out.println(family);                out.print("The plant is:"+family+"!");             } else {                  out.print("missing...");              }          } catch (Exception e) {              out.print(e+","+msg);          }         out.flush();        out.close();    }}

GetModel

package com.sunyang.servlet;public class GetModel {    public static String transImage(String cmd, String path) {        String command[] = new String[] { cmd, path };        String vector=null;        try {            Runtime.getRuntime().exec(command);            Thread.sleep(1000);            // 读文件                        try {                            String encoding = "GBK";                            File file = new File("D:/vector.txt");                            if (file.isFile() && file.exists()) { // 判断文件是否存在                                InputStreamReader read = new InputStreamReader(                                        new FileInputStream(file), encoding);// 考虑到编码格式                                BufferedReader bufferedReader = new BufferedReader(read);                                String lineTxt = null;                                while ((lineTxt = bufferedReader.readLine()) != null) {                                    System.out.println(lineTxt);                                    vector = lineTxt;                                }                                read.close();                            } else {                                System.out.println("找不到指定的文件");                            }                        } catch (Exception e) {                            System.out.println("读取文件内容出错");                            e.printStackTrace();                        }        } catch (IOException e) {            e.printStackTrace();        } catch (InterruptedException e) {            e.printStackTrace();        }        return vector;    }    public static int GetResult(String cmd, String vector) {        int result=-1;        final Process process;        try {            String cmdStr = "cmd /c "+cmd+" "+vector ;            process = Runtime.getRuntime().exec(cmdStr);            printMessage(process.getInputStream());            printMessage(process.getErrorStream());            Thread.sleep(1000);            // 读文件            try {                File file = new File("D:/get.txt");                if (file.isFile() && file.exists()) { // 判断文件是否存在                    InputStreamReader read = new InputStreamReader(                            new FileInputStream(file));// 考虑到编码格式                    BufferedReader bufferedReader = new BufferedReader(read);                    String lineTxt = null;                    lineTxt = bufferedReader.readLine();                    System.out.println(lineTxt);                    result = GetFamily(lineTxt);                    read.close();                } else {                    System.out.println("找不到指定的文件");                }            } catch (Exception e) {                System.out.println("读取文件内容出错");                e.printStackTrace();            }        } catch (IOException e) {            e.printStackTrace();        } catch (InterruptedException e) {            e.printStackTrace();        }        return result;    }    //查找并排序    public static int GetFamily(String data){        //去掉data最后一个“,”        String myData = data.substring(0, data.length()-1);        String[] split = myData.split(",");        final Hashtable<Double, Integer> ht = new Hashtable<Double, Integer>();        int count = 1;        for (int i = 0; i < split.length; i++) {            ht.put(Double.parseDouble(split[i]),count);            count++;        }        List<Double> list = new ArrayList<Double>(ht.keySet());        Collections.sort(list);        int result = ht.get(list.get(ht.size()-1));        System.out.println("输出类别"+result);        return result;    }}

Android端

MainActivity

package com.sunyang.Image;public class MainActivity extends Activity {    private ImageView img;    private Button btnUpload;    private HttpUtils httpUtils;    private String URL="http://192.168.1.119:8080/Person_proj/upload";    private String[] items = { "打开相机", "从相册选择" };    private String title = "树叶识别";    private static final int PHOTO_CARMERA = 1;    private static final int PHOTO_PICK = 2;    private static final int PHOTO_CUT = 3;    private File tempFile = new File(Environment.getExternalStorageDirectory(),            getPhotoFileName());    @SuppressLint("SimpleDateFormat") private String getPhotoFileName() {        Date date = new Date(System.currentTimeMillis());        SimpleDateFormat sdf = new SimpleDateFormat("'PNG'_yyyyMMdd_HHmmss");        return sdf.format(date) + ".png";    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        img = (ImageView) findViewById(R.id.main_img);        btnUpload = (Button) findViewById(R.id.upload);        btnUpload.setOnClickListener(clickListener);        img.setOnClickListener(clickListener);        httpUtils=new HttpUtils(10000);    }    private OnClickListener clickListener = new OnClickListener() {        @Override        public void onClick(View v) {            switch (v.getId()) {            case R.id.main_img:                AlertDialog.Builder dialog = AndroidUtil.getListDialogBuilder(                        MainActivity.this, items, title, dialogListener);                dialog.show();                break;            case R.id.upload:                upload();                break;            default:                break;            }        }    };    //上传    protected void upload() {        RequestParams params=new RequestParams();        params.addBodyParameter(tempFile.getPath().replace("/", ""), tempFile);        httpUtils.send(HttpMethod.POST,URL, params,new RequestCallBack<String>() {            @Override            public void onFailure(HttpException e, String msg) {                Toast.makeText(MainActivity.this, "上传成功", Toast.LENGTH_SHORT).show();                Log.i("MainActivity", e.getExceptionCode() + "====="                        + msg);            }            @Override            public void onSuccess(ResponseInfo<String> responseInfo) {                Log.i("MainActivity", "====upload_success====="                        + responseInfo.result);                Toast.makeText(MainActivity.this, responseInfo.result,Toast.LENGTH_LONG).show();            }        });    }    private android.content.DialogInterface.OnClickListener dialogListener = new DialogInterface.OnClickListener() {        @Override        public void onClick(DialogInterface dialog, int which) {            switch (which) {            case 0:                // 弹出对话框                startCamera(dialog);                break;            case 1:                // 从相册选                startPick(dialog);                break;            default:                break;            }        }    };    // 打开相机    protected void startCamera(DialogInterface dialog) {        dialog.dismiss();        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);        intent.putExtra("camerasensortype", 2);         intent.putExtra("autofocus", true);         intent.putExtra("fullScreen", false);         intent.putExtra("showActionIcons", false);        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(tempFile));        startActivityForResult(intent, PHOTO_CARMERA);    }    // 找相册里的    protected void startPick(DialogInterface dialog) {        dialog.dismiss();        Intent intent = new Intent(Intent.ACTION_PICK, null);        intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,                "image/*");        startActivityForResult(intent, PHOTO_PICK);    }    @Override    protected void onActivityResult(int requestCode, int resultCode, Intent data) {        switch (requestCode) {        case PHOTO_CARMERA:            startPhotoZoom(Uri.fromFile(tempFile), 100);            break;        case PHOTO_PICK:            if (null != data) {                startPhotoZoom(data.getData(), 100);            }            break;        case PHOTO_CUT:            if (null != data) {                setPicToView(data);            }            break;        default:            break;        }        super.onActivityResult(requestCode, resultCode, data);    }    // 缩放图片    private void startPhotoZoom(Uri uri, int size) {        Intent intent = new Intent("com.android.camera.action.CROP");        intent.setDataAndType(uri, "image/*");        intent.putExtra("crop", true);        // 比例宽高        intent.putExtra("aspectX", 1);        intent.putExtra("aspectY", 1);        // 剪裁大小        intent.putExtra("outputX", size);        intent.putExtra("outputY", size);        intent.putExtra("return-data", true);        startActivityForResult(intent, PHOTO_CUT);    }    //把图片加载到view上    private void setPicToView(Intent data) {        Bundle bundle = data.getExtras();        if (null != bundle) {            final Bitmap bmp = bundle.getParcelable("data");            img.setImageBitmap(bmp);            saveCropPic(bmp);            Log.i("MainActivity", tempFile.getAbsolutePath());        }    }    // 图片保存到sd卡    private void saveCropPic(Bitmap bmp) {        ByteArrayOutputStream baos = new ByteArrayOutputStream();        FileOutputStream fis = null;        bmp.compress(Bitmap.CompressFormat.PNG, 100, baos);        try {            fis = new FileOutputStream(tempFile);            fis.write(baos.toByteArray());            fis.flush();        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                if (null != baos) {                    baos.close();                }                if (null != fis) {                    fis.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }    }}

Layout

<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:padding="10dp"    android:orientation="vertical"    tools:context="com.example.uploadimage_test.MainActivity" >    <ImageView         android:id="@+id/main_img"        android:layout_width="fill_parent"        android:layout_height="500dp"        android:src="@drawable/ic_launcher"        />    <Button         android:id="@+id/upload"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:text="识别图片"        /></LinearLayout>
0 0