用csv文件读写一个人脸数据库,实现能够并显示对应的人脸结果
写这篇博客背景是,前面博客中有说,最近正在做一个云端人脸识别的APP,之前写好了Java云端客户端,通信返回来的是一个512维的人脸向量。我这次的工作就是把向量存取到一个csv文件里面当做数据可来使用(其实使用JDBC技术调用数据库是最佳,但是为了简单先用的是csv代替),再写一个测试类遍历csv中的人脸向量,根据欧氏距算出得分值,再和某个阈值进行比对,如果小于那个阈值则返回我对应的人名,整个代码就是实现这样一个功能来的
话不多说先直接上代码
先把向量(浮点型集合数组)写进去csv文件中
//定义一个写入流的操作类,用来把我通信中返回的人脸向量写进去
public class CsvWrite {
public static void main(String[] args) {
//用一个浮点型集合来接受传回来的512维人脸向量,这个代码我就不贴了,涉及到我通信的代码
List<Float> floatList = HttpChuanShu("C:\\Users\\rhp\\Desktop\\111\\name2.jpg");
String f = "C:\\Users\\rhp\\Desktop\\ceshi1.csv";
//因为WriteFile接受的是String型参数,所以转换一下
writeFile(f, floatList.toString());
}
public static void writeFile(String fileFullPath,String content) {
//定义一个字节输出流
FileOutputStream fos = null;
try {
//这里,我要说一下有点坑,我自己一开始选的构造方法中不带true的,这样我每次写进去,都会覆盖之前,以至于csv文件打开后只有一行,这里的true意思就是判断文件中可有内容,如果有,从内容的最后一行转换符下一行开始写入,就解决了被覆盖的问题
fos = new FileOutputStream(fileFullPath, true);
这里我为了在Eclipse开发,故意先用键盘输入来代替Android上面的人员主动注册留下的name
Scanner sc = new Scanner(System.in);
System.out.println("请输入保存的name");
String name = sc.next();
sc.close();
//先写入名字
fos.write(name.getBytes());
//再写入内容,注意这点都是字节输入
fos.write(content.getBytes());
// 写入一个换行,上面已经写入成功了,但是注意,写进去的是一个字符串类型,下面读取流加计算欧氏距离时,需要装箱+切割得到你需要的浮点型数组
fos.write("\r\n".getBytes());
//加个日常的IO异常捕获操作
} catch (IOException e) {
e.printStackTrace();
}finally{
if(fos != null){
try {
fos.flush();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//这里写入就成功了,但是有个小问题我没有解决,就是,写进去第一行为空,默认从第二行开始读,我查阅了资料好像是csv默认第一行为表头信息,第一行为空在下面读写中会产生一个小问题,就是遍历字符串是,读到第一行null,会出现索引超出边界异常,所以你要想顺利遍历读取每一行,简单的操作就是直接手动删除第一行,这样就可以了
//下面是读取操作并计算欧式距离输入对应的人名,这是我的功能目的
public class CsvRead {
public static void main(String[] args) {
同理,先传入通信的浮点型集合
List<Float> floatList = HttpChuanShu("C:\\Users\\rhp\\Desktop\\111\\name1.jpg");
String f = "C:\\Users\\rhp\\Desktop\\ceshi3.csv";
//后面计算欧式距离需要,所以先定一个一个512大小的浮点型数组
float[] a = new float[512];
//遍历
for (int i = 0; i < floatList.size(); i++) {
a[i] = floatList.get(i);
}
}
public static String CSV2Array(String path, float[] a) {
try {
定义好缓冲流去接受,编码这里用UTF-8
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(path), "UTF-8"));
//定义一个String类型的变量line
String line;
// 定义一个全局变量,int类型i=0
int i = 0;
//循环遍历csv文件的每一行,知道读到的行内容为空跳出循环
while ((line = in.readLine()) != null) {
这里遍历字节,是为了取出每一行前面的name,为了后面输出对应的人的名字
for (i = 0; i < 100; i++) {
//因为我的每一行都是这样的类型
为了获取名字左后一个字节索引以便后面字符串截取名字用的
if ('[' == line.charAt(i)) {
System.out.println(i);
//这里加一个中断,不想浪费资源,遍历到了我要的索引就跳出来就可以了
break;
}
}
定义一个名字的变量line1name,截取从0索引开始到刚遍历出来的那个索引结束,可以加一个打印看看,
String line1name = line.substring(0, i);
System.out.println(line1name);
、、这里截取我要的字符串数组,把[ ]给除去
String line1 = line.substring(i + 1, line.length() - 1);
System.out.println(line1);
//字符串split操作,因为上一步操作剩下来的是字符串,里面是有规律的,每个字符串数组以逗号分开,这也是csv默认的分隔符
String[] a1 = line1.split(","); //分割之后就是一个字符串数组了,你也可以打印看看长度,肯定是512维的数组,只不过类型现在还是字符串类型罢了
System.out.println(a1.length);
//计算欧氏距离,定义一个求和变量sum
double sum = 0;
//遍历字符串数组中每一个浮点型字符串
for (; i < a1.length; i++) {
//装箱操作,获取字符串浮点型对应的浮点值
float a1f = Float.valueOf(a1[i]);
//计算欧氏距离
sum = Math.pow((double) (a1f - a[i]), 2) + sum;
}
System.out.println("sum: " + sum);
//这是我设置的阈值,小于这个值返回return,终止while循环,输出对应的名字,否则一直遍历每一行,没有最终循环结束,返回null
if (sum >= 1.24) {
System.out.println("无法识别");
} else {
System.out.println("名字: " + line1name);
in.close();
return line1name;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//哎,我是一个Java初学者,写的代码我相信稍微入门一点的都能轻易看明白,对自己而言也是更加熟悉,也是为了避免更多人和我一样进坑浪费大量时间,我看了网上很多博客,多而不实,真正完整的且实用的很少,自己每次找个资源也是百度一大片,差的还很远,慢慢学咯,嘻嘻