前言:这个App是之前《数字图像处理》课程的一次课程设计中的产物,现在整理一下记录下来,里面涉及到了比较多的控件以及拓展包,功能不是很丰富但是也比较算齐全,其中使用的技术原理包括在安卓上使用OpenCV库对图像进行一些预处理、利用透视变换方法纠正歪斜图像、生成excel统计表格、以及做一些柱状图统计数据等。因为设计的控件等比较多,所以下文以功能模块的形式分块介绍,有需要的可以直接参考相关模块即可。

目录

一、相机拍照及从内存获取图片

二、OpenCV导入(免安装Opencv Manager)

三、图像倾斜纠正及局部裁剪

四、数据统计柱状图控件

五、数据统计导出excel表格

六、总结


一、相机拍照及从内存获取图片

(1)调用系统摄像头开启相机拍照方式获取图片

开启系统相机的功能主要通过Android API中的File、URI(Uniform Resource Identifier)和Intent类来实现。URI是统一资源定位符。在Android 7.0以下,通过调用URI的fromFile(File file)方法可以获取到参数中file文件对应的资源引用URI,用来定位到图片拍摄后放置的文件位置。而在Android 7.0以上加入了文件保护的功能,需要通过FileProvider类的getUriForFile(File file)方法来获取file对应的URI。考虑到Android版本的兼容性,这里封装为getMediaFileUri方法,代码如下所示:

    /*** @param activity* @Author: Linzs email:linzs.online@gmail.com* @Date: 2019/10/24* @Description: 打开系统摄像头并返回图片(处理了兼容性问题)*/public void openCamera(Activity activity) {int currentapiVersion = Build.VERSION.SDK_INT;// 获取系統版本Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);// 申请调用系统相机// 判断存储卡是否可以用,可用进行存储if (hasSdcard()) {//设置一个日期格式SimpleDateFormat timeStampFormat = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");//图片的命名为日期时间string_camera = timeStampFormat.format(new Date());tempFile = new File(Environment.getExternalStorageDirectory(),string_camera + ".jpg");//判断系统版本号,兼容Android 7.0//如果系统版本号小于Android 7.0if (currentapiVersion < Build.VERSION_CODES.N) {// 从文件中创建uriimageUri = Uri.fromFile(tempFile);intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);} else {//当系统版本号大于Android 7.0使用共享文件的形式ContentValues contentValues = new ContentValues(1);contentValues.put(MediaStore.Images.Media.DATA, tempFile.getAbsolutePath());//检查是否有存储权限,以免崩溃if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED) {//申请内存写权限Toast.makeText(this, "请开启存储权限", Toast.LENGTH_SHORT).show();return;}imageUri = activity.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);}}// 开启一个带有返回值的Activity,请求码为CODE_TAKE_PHOTOactivity.startActivityForResult(intent, CODE_TAKE_PHOTO);}

首先声明File文件的位置,通过getMediaFileUri方法获取到mediaFile文件对应的imageUri。然后声明一个调用系统拍照功能的takePhotoIntent,将对应的imageUri传入到takePhotoIntent的putExtra方法中进行传递。最后将takePhotoIntent传入到startActivityForResult中实现启动系统相机的拍照功能。其中onActivityResult函数中接收到请求码CODE_TAKE_PHOTO之后这样处理事件:

case CODE_TAKE_PHOTO:if (resultCode == RESULT_OK) {Intent Camera_intent=new Intent(MainActivity.this,SmartCrop_Activity.class);Camera_intent.setData(imageUri);startActivityForResult(Camera_intent,PHONE_CODE_CUT);}break;

(2)直接读手机内存从相册中调出图片

同样的当触发从图库中调用照片时候,也开一个intent处理申请图片事件

    /*** @Author: Linzs email:linzs.online@gmail.com* @Date: 2019/10/24* @Description: 从图库中提取照片*/public void openAlbum() {Intent intent = new Intent(Intent.ACTION_PICK);intent.setType("image/*");// 开启一个带有返回值的Activity,请求码为GET_PHOTOstartActivityForResult(intent, CODE_GET_PHOTO);}

其中onActivityResult函数中接收到请求码CODE_GET_PHOTO之后这样处理事件:

 case CODE_GET_PHOTO:if (resultCode == RESULT_OK) {if (data != null) {Uri uri = data.getData();imageUri = uri;}Intent Camera_intent=new Intent(MainActivity.this,SmartCrop_Activity.class);Camera_intent.setData(imageUri);startActivityForResult(Camera_intent,PHONE_CODE_CUT);}break;

二、OpenCV导入(免安装Opencv Manager)

(1)下载OpenCV的Android支持包。

在OpenCV官网https://opencv.org/releases/支持库下载页面中,有很多不同版本的Android支持包。可以根据自己的需要选择不同的版本进行下载,本文选用的是3.4.8版本。下载完成的支持库是压缩包的形式,解压完成后可以看到目录下有两个配置文件以及sdk等三个文件夹,如图所示。

其中apk文件夹下提供的是相应版本的OpenCVManager,samples文件夹下提供的是OpenCV官方的一些例子,方便开发者进行学习,sdk文件下的才是我们需要的。在sdk文件下有etc、java、native三个文件夹和一个build.gradle文件。其中java文件夹中的内容是最终需要导入到Android应用程序中OpenCV封装的Java库(后文简称为OpenCV4Android库),记录下这个Java库所在路径,下面的步骤需要通过这个路径将OpenCV4Android库导进来。

(2)Android Studio引入OpenCV4Android库。

在Android Studio中新建一个工程,全部为默认设置即可。在新建好的工程中,点击“File --> New --> Import Module”导入OpenCV4Android库,然后选择OpenCV库的路径。Module name可以自由定义,本文自动识别为OpenCVLibrary348,然后Next --> Finish即可。

(3)修改模块OpenCVLibrary348中build.gradle文件的配置。

OpenCVLibrary341模块的build.gradle文件中的compileSDKVersion、builtToolsVersion、minSDKVersion、targetSDKVersion几个版本号需要修改为与app模块的build.gradle文件中的版本号一致,本文的app模块下build.gradle文件的compileSDKVersion、builtToolsVersion、minSDKVersion、targetSDKVersion分别为28、28.0.3、19、28。

修改后OpenCVLibrary348中build.gradle文件内容如下所示:

apply plugin: 'com.android.library'android {compileSdkVersion 29buildToolsVersion "29.0.2"defaultConfig {minSdkVersion 15targetSdkVersion 29}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'}}
}

(4)为app主模块添加OpenCVLibrary348模块依赖。

点击“File --> Project Structure”打开项目结构。选中app模块,选择标签栏的“Dependencies”,点击右边“+”出现下拉框,在下拉框中选择“Module dependency”,即可看到OpenCVLibrary348,选中OpenCVLibrary348后完成整个OpenCV4Android库的导入以及配置,如图所示。

(5)设置免安装OpenCVManager。

OpenCVManager保存了为Java库提供底层支持的libOpenCV_java3.so库,因此我们只要手动地将libOpenCV_java3.so这个库添加到Android应用程序后即可实现相同的效果,就不需要额外再下载OpenCVManager管理应用了。

libOpenCV_java3.so放在图7中sdk文件下的native/libs文件夹,点开可以看到armeabi-v7a、armeabi、arm64-v8a、x8等多个不同的架构,每个架构文件夹下就是对应架构的libOpenCV_java3.so。

一般来说,市面上的Android手机基本都可以兼容armeabi-v7a架构,考虑到应用体积优化的问题,因此我们将armeabi-v7a文件夹下的libOpenCV_java3.so导入到应用中即可。为了兼容性的问题也可以全部导入进去。

Android Studio中切换到Android视图,在app目录下创建一个jniLibs文件夹,并在jniLibs下创建armeabi-v7a文件夹,将libOpenCV_java3.so等文件导入到armeabi-v7a文件夹中,如图所示。

最后在MainActivity.class文件中将libOpenCV_java3.so加载进来,初始代码onResume函数中这样写:

    public void onResume() {super.onResume();if (!OpenCVLoader.initDebug()) {Log.i("cv", "Internal OpenCV library not found. Using OpenCV Manager for initialization");} else {Log.i("cv", "OpenCV library found inside package. Using it!");}}

配置完成后即可在应用程序的工程下使用OpenCV4Android库中提供的API。

OpenCV for Android 3.0版本里,示例程序直接就可免OpenCV Manager的安装,所以不必使用包含JNI部分那种方法。上面onResume函数写法也是参照官方例子写法,方法的确可行。

三、图像倾斜纠正及局部裁剪

在这里我们是调用了一个github上很不错的开源库SmartCrop,它是一个基于机器学习 HED 网络优化的图片裁剪库。这里给出它的github地址:https://github.com/pqpo/SmartCropper

博主在他的个人博客上发布了使用该库的方法:https://pqpo.me/2019/08/02/machine-learning-hed-smartcropper/

SmartCropper 是一个智能图片裁剪框架,能够智能识别图片中的文档边框,自动瞄点选中文档边框,用户只需按下裁剪即可获得选取区放大图片。支持特性:

  1. 使用智能算法识别图片中的边框
  2. 支持拖动锚点,手动调节选区
  3. 使用透视变换裁剪并矫正选区

下面直接来说调用方法:

1、在这个库的github上下载最新的release压缩库

这里给出下载地址:https://github.com/pqpo/SmartCropper/releases/download/v2.1.1/smartcropperlib-v2.1.1-release.aar

下载好之后放入到~/app/libs目录下:

2、在根目录下的 build.gradle 添加如下code:

注意区分如下的各个build.gradle

这里是在Module:app中的bulid.gradle文件里面添加如下代码:

repositories {maven { url "https://jitpack.io" }flatDir {dirs 'libs'  //智能裁剪}
}

3、然后添加库依赖

dependencies {compile 'com.github.pqpo:SmartCropper:v2.1.3'
}

到此该库已经添加入工程中了,下面进行调用即可,他是直接封装成一个控件了,直接使用控件然后调用相应的函数即可实现相应的功能,关于该库的详细介绍在它的github上有介绍https://github.com/pqpo/SmartCropper

4、裁剪布局文件

<me.pqpo.smartcropperlib.view.CropImageView
android:id="@+id/iv_crop"
android:layout_width="match_parent"
android:layout_height="match_parent" />

5、启动自动选区并进行透视变换纠正图像

这是是通过intent机制启动另外一个Activity的方式来进行图片裁剪,在图片获取完成之后即抛出了一个请求码PHONE_CODE_CUT

 startActivityForResult(Camera_intent,PHONE_CODE_CUT);

之后SmartCorp的Activity响应,下面给出相应的布局以及Activity的code:

先是Activity的:

package com.example.dropletanalysisapp;import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;import androidx.appcompat.app.AppCompatActivity;import java.io.FileNotFoundException;import me.pqpo.smartcropperlib.SmartCropper;
import me.pqpo.smartcropperlib.view.CropImageView;import static com.example.dropletanalysisapp.MainActivity.crop_bitmap;public class SmartCrop_Activity extends AppCompatActivity implements View.OnClickListener {private CropImageView iv_crop;private Button btn_cancel;private Button btn_ok;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.smartcrop_activity);initView();SmartCropper();}private void initView() {iv_crop = (CropImageView) findViewById(R.id.iv_crop);iv_crop.setOnClickListener(this);btn_cancel = (Button) findViewById(R.id.btn_cancel);btn_ok = (Button) findViewById(R.id.btn_ok);btn_cancel.setOnClickListener(this);btn_ok.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.btn_cancel:iv_crop.setCropPoints(null);finish();break;case R.id.btn_ok:crop_bitmap = iv_crop.crop();//iv_crop.setCropPoints(null);setResult(RESULT_OK);finish();break;}}/*** @Author: Linzs email:linzs.online@gmail.com* @Date: 2019/11/16* @Description: 调用智能裁剪库*/private void SmartCropper() {Intent get_intent = getIntent();if (get_intent != null) {Uri uri = getIntent().getData();Bitmap slectBitmap = null;try {slectBitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));} catch (FileNotFoundException e) {e.printStackTrace();}Point[] points = SmartCropper.scan(slectBitmap);iv_crop.setCropPoints(points);iv_crop.setImageToCrop(slectBitmap);}}
}

然后是布局文件的code:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@android:color/background_dark"android:orientation="vertical"><me.pqpo.smartcropperlib.view.CropImageViewandroid:id="@+id/iv_crop"android:layout_width="match_parent"android:padding="20dp"android:layout_height="0dp"android:layout_weight="1"app:civShowEdgeMidPoint="true"app:civLineColor="@color/colorPrimary"app:civMagnifierCrossColor="@color/colorPrimaryDark"/><LinearLayoutandroid:id="@+id/ll_btn"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@color/colorPrimary"android:padding="10dp"android:orientation="horizontal"><Buttonandroid:id="@+id/btn_cancel"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:text="取消"/><Buttonandroid:id="@+id/btn_ok"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:text="确定"/></LinearLayout></LinearLayout>

布局效果如下图:

其实主要的是两条函数使用

1)设置待裁剪图片

ivCrop.setImageToCrop(selectedBitmap);

2)裁剪选区内的图片并做透视变换纠正倾斜

Bitmap crop = ivCrop.crop();

经过SmartCorp处理之后的图片通过 Android API 中的 BitmapFactory 类来实现展示。BitmapFactory 的decodeStream 方 法 对裁 剪 完 成 或 相册 选 择 图 片 完成 后 返 回 的Uri 进行输入流解析,最终转换成图片返回到主Activity显示在相应区域,代码如下:

case PHONE_CODE_CUT:if (resultCode == RESULT_OK) {PHOTO.setImageBitmap(crop_bitmap);//getPhotoSize();}break;

四、数据统计柱状图控件

这里的数据统计控件是选择使用了MPChart库,这是github上的一个开源流行库,功能丰富完善。下面说说使用流程:

1、添加github依赖

在app目录下的build.gradle中加上下面依赖

    implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'

2、layout中用法

    <com.github.mikephil.charting.charts.BarChartandroid:id="@+id/chart"android:layout_width="350dp"android:layout_height="220dp"android:background="#DFE4E6"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent" />

3、在Activity中对控件进行初始化

其实就是配置控件的一些初始化参数,比如:x、y轴的范围等

private void initBarChart(BarChart barChart) {YAxis leftAxis;             //左侧Y轴YAxis rightAxis;            //右侧Y轴XAxis xAxis;                //X轴Legend legend;              //图例//setTitle("BarChartActivity");// 不显示右下角的descriptionbarChart.getDescription().setEnabled(false);//背景颜色barChart.setBackgroundColor(Color.WHITE);//不显示图表网格barChart.setDrawBarShadow(false);barChart.setHighlightFullBarEnabled(false);//不显示边框barChart.setDrawBorders(false);//设置动画效果barChart.animateY(1000, Easing.Linear);barChart.animateX(1000, Easing.Linear);/***XY轴的设置***///X轴设置显示位置在底部xAxis = barChart.getXAxis();// 获取 x 轴xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);// 设置 x 轴显示位置xAxis.setDrawGridLines(false);// 取消 垂直 网格线xAxis.setLabelRotationAngle(0f);// 设置 x 轴 坐标字体旋转角度xAxis.setTextSize(10f);// 设置 x 轴 坐标字体大小xAxis.setAxisLineColor(Color.GREEN);// 设置 x 坐标轴 颜色//xAxis.setAxisLineWidth(10f);// 设置 x 坐标轴线宽//xAxis.setLabelCount(10);// 设置 x轴 的刻度数量//xAxis.setCenterAxisLabels(true);xAxis.setAxisMinimum(1);xAxis.setAxisMaximum(30f);xAxis.setLabelCount(15);leftAxis = barChart.getAxisLeft();// 获取 y轴rightAxis = barChart.getAxisRight();//保证Y轴从0开始,不然会上移一点leftAxis.setAxisMinimum(0f);//左边坐标rightAxis.setAxisMinimum(0f);/***图例 标签 设置***/legend = barChart.getLegend();legend.setForm(Legend.LegendForm.LINE);legend.setTextSize(11f);//显示位置legend.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM);legend.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT);legend.setOrientation(Legend.LegendOrientation.HORIZONTAL);//是否绘制在图表里面legend.setDrawInside(false);/***数据 设置***/// 定义一个list来存储x和y数据ArrayList<BarEntry> yValues = new ArrayList<>();for (int x = 2; x < 30; x++) {int y = (int)(Math.random()*30);yValues.add(new BarEntry(x, y));}// y 轴数据集BarDataSet barDataSet = new BarDataSet(yValues,"雾滴粒径(mm)");BarData mbarData = new BarData(barDataSet);//直方图数据初始化barChart.setData(mbarData);//显示直方图barChart.invalidate();//刷新}

之后可以通过给list刷新数据达到动态改变表格的统计数据,这里简单给出一个实例,第一个入口参数是图表控件、第二个是X轴的数据、第三个是Y轴的数据。通过调用该函数即可达到改变图表上的数据。

private void BarChar_DataSet(BarChart mbarChart, int[] x, int[] y){ArrayList<BarEntry> y_Values = new ArrayList<>();for(int i=0; i<x.length; i++){y_Values.add(new BarEntry(x[i],y[i]));}BarDataSet barDataSet = new BarDataSet(y_Values, "雾滴数量");BarData mbarData = new BarData(barDataSet);//直方图数据初始化mbarChart.setData(mbarData);//显示直方图mbarChart.invalidate();//刷新}

五、数据统计导出excel表格

本次项目的需求是将应用内的数据导出为excel表格,这里我们也是使用了一个现成的库,下面讲下详细使用过程:

1、添加依赖包

implementation group: 'net.sourceforge.jexcelapi', name: 'jxl', version: '2.6.12'

2、编写excel工具类 ‘

新建一个类ExcelUtil如下:

写入如下内容:

package com.example.dropletanalysisapp;import android.content.Context;
import android.widget.Toast;import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;import jxl.Workbook;
import jxl.WorkbookSettings;
import jxl.format.Colour;
import jxl.write.Label;
import jxl.write.WritableCell;
import jxl.write.WritableCellFormat;
import jxl.write.WritableFont;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;/*** @date 2019/2/14*/public class ExcelUtil {private static WritableFont arial14font = null;private static WritableCellFormat arial14format = null;private static WritableFont arial10font = null;private static WritableCellFormat arial10format = null;private static WritableFont arial12font = null;private static WritableCellFormat arial12format = null;private final static String UTF8_ENCODING = "UTF-8";/*** 单元格的格式设置 字体大小 颜色 对齐方式、背景颜色等...*/private static void format() {try {arial14font = new WritableFont(WritableFont.ARIAL, 14, WritableFont.BOLD);arial14font.setColour(jxl.format.Colour.LIGHT_BLUE);arial14format = new WritableCellFormat(arial14font);arial14format.setAlignment(jxl.format.Alignment.CENTRE);arial14format.setBorder(jxl.format.Border.ALL, jxl.format.BorderLineStyle.THIN);arial14format.setBackground(jxl.format.Colour.VERY_LIGHT_YELLOW);arial10font = new WritableFont(WritableFont.ARIAL, 10, WritableFont.BOLD);arial10format = new WritableCellFormat(arial10font);arial10format.setAlignment(jxl.format.Alignment.CENTRE);arial10format.setBorder(jxl.format.Border.ALL, jxl.format.BorderLineStyle.THIN);arial10format.setBackground(Colour.GRAY_25);arial12font = new WritableFont(WritableFont.ARIAL, 10);arial12format = new WritableCellFormat(arial12font);//对齐格式arial10format.setAlignment(jxl.format.Alignment.CENTRE);//设置边框arial12format.setBorder(jxl.format.Border.ALL, jxl.format.BorderLineStyle.THIN);} catch (WriteException e) {e.printStackTrace();}}/*** 初始化Excel表格** @param filePath  存放excel文件的路径(path/demo.xls)* @param sheetName Excel表格的表名* @param colName   excel中包含的列名(可以有多个)*/public static void initExcel(String filePath, String sheetName, String[] colName) {format();WritableWorkbook workbook = null;try {File file = new File(filePath);if (!file.exists()) {file.createNewFile();} else {return;}workbook = Workbook.createWorkbook(file);//设置表格的名字WritableSheet sheet = workbook.createSheet(sheetName, 0);//创建标题栏sheet.addCell((WritableCell) new Label(0, 0, filePath, arial14format));for (int col = 0; col < colName.length; col++) {sheet.addCell(new Label(col, 0, colName[col], arial10format));}//设置行高sheet.setRowView(0, 340);workbook.write();} catch (Exception e) {e.printStackTrace();} finally {if (workbook != null) {try {workbook.close();} catch (Exception e) {e.printStackTrace();}}}}/*** 将制定类型的List写入Excel中** @param objList  待写入的list* @param fileName* @param c* @param <T>*/@SuppressWarnings("unchecked")public static <T> void writeObjListToExcel(List<T> objList, String fileName, Context c) {if (objList != null && objList.size() > 0) {WritableWorkbook writebook = null;InputStream in = null;try {WorkbookSettings setEncode = new WorkbookSettings();setEncode.setEncoding(UTF8_ENCODING);in = new FileInputStream(new File(fileName));Workbook workbook = Workbook.getWorkbook(in);writebook = Workbook.createWorkbook(new File(fileName), workbook);WritableSheet sheet = writebook.getSheet(0);for (int j = 0; j < objList.size(); j++) {DropletAnalysis circle = (DropletAnalysis) objList.get(j);List<String> list = new ArrayList<>();list.add(String.valueOf(circle.getRad()));list.add(String.valueOf(circle.getNum()));for (int i = 0; i < list.size(); i++) {sheet.addCell(new Label(i, j + 1, list.get(i), arial12format));if (list.get(i).length() <= 4) {//设置列宽sheet.setColumnView(i, list.get(i).length() + 8);} else {//设置列宽sheet.setColumnView(i, list.get(i).length() + 5);}}//设置行高sheet.setRowView(j + 1, 350);}writebook.write();workbook.close();Toast.makeText(c, "导出Excel成功", Toast.LENGTH_SHORT).show();} catch (Exception e) {e.printStackTrace();} finally {if (writebook != null) {try {writebook.close();} catch (Exception e) {e.printStackTrace();}}if (in != null) {try {in.close();} catch (IOException e) {e.printStackTrace();}}}}}}

3、然后在主Activity里面调用即可

我这里是直接封装成一个函数来调用了

   /*** @Date: 2019/11/21* @Description: 写入excel*/private void exportExcel(int[] circle_size, int circle_num){Context context=getApplicationContext();List<DropletAnalysis> drops=new ArrayList<>();File file = new File(filePath);if (!file.exists()) {file.mkdirs();}String excelFileName = "雾滴分析";SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String time = sdf.format(new Date());//Calendar.getInstance().toString();String fileName = file.toString() + "/"+excelFileName+"-"+time+".xls";String[] title = {"半径", "数量"};String sheetName = "雾滴分析";for(int i=0;i<circle_num;i++) {DropletAnalysis circle = new DropletAnalysis(circle_size[i], i);drops.add(circle);}filePath = fileName;ExcelUtil.initExcel(filePath, sheetName, title);ExcelUtil.writeObjListToExcel(drops, filePath, context);}

关于设置文件名称、格式、标题等等详细看里面函数即可见名思义了。

六、总结

本次的Android探索是本人大学时期的一次课程设计,本人非计算机类专业,对Android也是一知半解,本次设计的App意义是做一个为了安装在手机上方便携带的喷施雾滴粒径分析仪,效果一般,当时一种课外学习。初次接触Android这门学科,其中还有很多掌握不到位的,通过这次学习掌握了一个App设计的大致流程以及一些基础Android控件和Java语法的使用。对于OpenCV部分函数在此不做展开,网上其他文章有更多详细的资料,OpenCV部分用法和PC端使用大致相同,调用相关API即可对图像做预处理。个人水平有限,文章中细节以及不足之处欢迎多多指正。

【Android探索】基于OpenCV的微液滴粒径分析APP相关推荐

  1. Android:基于OpenCV实现身份证识别(C++)——移植图像算法

    系列文章目录 第一章 Android:基于OpenCV实现身份证识别(C++)--图像处理 第二章 Android:基于OpenCV实现身份证识别(C++)--移植图像算法 文章目录 系列文章目录 前 ...

  2. Rosin-Rammler液滴粒径分布

    转自:微信公众号 cfd之道 用来自记!!!!! 转载链接:https://mp.weixin.qq.com/s/nhDoHNsTwenXZNBYkDVoVg 在液体喷雾过程中,常利用Rosin-Ra ...

  3. 彩色乳胶PS微球/聚苯乙烯Polystyrene微球 粒径尺寸0.02μm至100μm

    彩色乳胶PS微球/聚苯乙烯Polystyrene微球 粒径尺寸0.02μm至100μm PS乳胶微球主要特点 粒径尺寸的选择在0.02μm至100μm 可接受不同表面电荷以及不同规格大小的定制 少量的 ...

  4. python sanic orm_基于sanic的微服务框架 - 架构分析

    感谢@songcser分享的<基于sanic的微服务基础架构>https://github.com/songcser/sanic-ms 最近正在学习微服务,发现这个repo不错,但不完整, ...

  5. Android开发-基于百度地图API开发仿滴滴出行APP界面的实现

    前 言 近年来,由于移动互联网快速的发展以及基于移动设备的APP的普及,移动互联网改变了人们的生活方式.从线上的电子支付到线下的出行,移动互联网是当今社会人们生活不可或缺的一部分,而线下出行的网约车的 ...

  6. Android端基于OpenCV边缘识别技术

    本文所采用的技术大体来源于网络上另一作者的开源项目,https://pqpo.me/2017/09/11/opencv-border-recognition/#reward,只是对其智能裁剪部分做了优 ...

  7. android端基于openCV给图片打马赛克

    最近一直在捣腾openCV,看到别人朋友圈总喜欢给图片打一点马赛克,充满神秘感.准备用openCV来实现打码,其实足够模糊就能实现马赛克效果.查看API发现ImgProc模块提供四种模糊方法:高斯模糊 ...

  8. SEM纳米颗粒图像粒径分析 基于ImageJ

    Chuang Li on Apr 2, 2019 下图为示例图像: Example figure 操作步骤 使用ImageJ打开上面的图片: 使用直线工具沿着图像中的scale bar做一条等宽的直线 ...

  9. Android无需权限显示悬浮窗, 兼谈逆向分析app

    前言 最近UC浏览器中文版出了一个快速搜索的功能, 在使用其他app的时候, 如果复制了一些内容, 屏幕顶部会弹一个窗口, 提示一些操作, 点击后跳转到UC, 显示这个悬浮窗不需要申请android. ...

最新文章

  1. C#学习笔记——密封类与密封方法
  2. TImage 的一些操作
  3. WatiN-Html元素的操作
  4. 【Java并发编程:volatile关键字之解析】
  5. html5 PHP 分片上传,H5分片上传含前端JS和后端处理(thinkphp)
  6. nginx关于错误页面重定向的问题
  7. 优质书籍资源仓库推荐【欢迎推送书籍】
  8. 银监计算机类 考试题库,银监会(计算机类)笔试资料-微观经济学试题库 .doc...
  9. python 修改文件名 修改日期_python实现批量修改文件名
  10. 【NFC】 NfcA/NfcB/NfcF/NfcV/IsoDep/Ndef/Mifare/Felica/Pboc/ISOxxxx 都是些什么鸟玩意?
  11. dsp2812 linux开发板,dsp2812开发板自制编程流程
  12. mysql服务器无法启动
  13. answer的汉语_answers怎么读(answers中文是什么意思)
  14. DFD图转换成SC图
  15. camera相关术语
  16. Java教程!Java标识符与关键字的区别是什么?
  17. Office2016免费下载:Office 2016 Pro Plus 64位 (迅雷复制链接就能下)
  18. Golang实现经典算法
  19. excel 的操作 http://wwwb.pconline.com.cn/pcedu/soft/excel.html
  20. 在github/gitlab上下载单个文件

热门文章

  1. 天津商业大学计算机科学学院,天津商业大学信息工程学院
  2. 天津大学计算机学院王思宇,复杂计算精准化,天津大学选择了宝德HPC
  3. mariadb登陆报错: 1698 - Access denied for user
  4. [bzoj2563] 阿狸和桃子的游戏 贪心
  5. vs 2019生成类试图
  6. 数据挖掘与机器学习——数据挖掘概述
  7. linux u盘 中毒,linux对中毒u盘分区和格式化
  8. 【重要公告】包头市新型冠状病毒感染肺炎防控工作指挥部公告(2022年第4、5、6、7号)
  9. SEM竞价推广创意快速撰写的方法,智能创意制作
  10. Android应用声明Open Source Licenses