首页 > 其他分享 >基于 Flutter + 百度人工智能 开发出的一款测颜值的 App

基于 Flutter + 百度人工智能 开发出的一款测颜值的 App

时间:2023-11-11 20:36:48浏览次数:31  
标签:dio return 渲染 测颜值 Text App faceInfo image Flutter

Flutter 颜值大师

基于 Flutter + 百度人工智能 开发出的一款测颜值的 App。

最重要的一点:一颗满怀学习热情的心

项目核心知识点

1.渲染头部区域

// 头部 AppBar 区域
appBar: AppBar(
  title: Text(
  "人脸识别",
    // 设置标题文字样式
    style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
  ),
  // 设置标题居中显示
  centerTitle: true,
)

2. 渲染多个浮动按钮

正常情况下,一个页面中,通过 floatingActionButton 选项,默认只能渲染一个浮动按钮。如果需要渲染多个浮动按钮,可以通过 ButtonBar 控件来实现,代码示例如下:

floatingActionButton: ButtonBar(
  // alignment 属性用来指定子元素如何在横轴上进行排列
  // MainAxisAlignment.spaceAround 表示分散对齐
  alignment: MainAxisAlignment.spaceAround,
  // 子元素
  children: <Widget>[
    // 第一个浮动按钮
    FloatingActionButton(
      onPressed: () {},
      tooltip: 'takephoto',
      child: Icon(Icons.photo_camera),
    ),
    // 第二个浮动按钮
    FloatingActionButton(
      onPressed: () {},
      tooltip: 'takepicture',
      child: Icon(Icons.photo_library),
    )
  ],
)

3. 使用第三方插件实现选择照片的功能

一些特殊的功能,可以在插件商店中搜索对应的插件,从而轻松实现,插件商店的地址为 https://pub.dev/flutter

  1. pubspec.yamldependencies 节点中,新增插件如下:
dependencies:
  image_picker: ^0.6.7+4
  1. lib/main.dart 文件的头部,导入对应的插件:
import 'package:image_picker/image_picker.dart';
  1. _MyHomePageState 这个状态管理类中,定义 _image 私有数据,用来存储用户选择的照片:
class _MyHomePageState extends State<MyHomePage> {
  // 用户通过摄像头或图片库选择的照片
  File _image;
}
  1. lib/main.dart 文件的头部,导入 File 类对应的类库:
import 'dart:io';
  1. lib/main.dart 中,定义函数 choosePic 来实现选取照片的功能:
// 点击按钮,选择图片
// 形参中的 source 为选取照片的方式,有两种,分别为:
//    ImageSource.camera   从相机拍照并得到照片
//    ImageSource.gallery  从本地相册选择照片
void choosePic(source) async {
  // 得到选取的照片
  var image = await ImagePicker.pickImage(source: source);

  setState(() {
    _image = image;
    faceInfo = null;
  });

  // 如果选取的照片为空,则不执行后续人脸检测的业务逻辑
  if (image == null) {
    return;
  }
}
  1. 在浮动按钮的 onPressed 事件处理函数中,调用第 5 步中定义的 choosePic 函数,并把选取照片的方式传递到函数中:
floatingActionButton: ButtonBar(
  alignment: MainAxisAlignment.spaceAround,
  children: <Widget>[
    FloatingActionButton(
      onPressed: () {
+++     choosePic(ImageSource.camera);
      },
      tooltip: 'takephoto',
      child: Icon(Icons.photo_camera),
    ),
    FloatingActionButton(
      onPressed: () {
+++     choosePic(ImageSource.gallery);
      },
      tooltip: 'takepicture',
      child: Icon(Icons.photo_library),
    )
  ],
)

4. 把用户选择的照片渲染到页面

  1. Scaffold 控件的 body 参数,修改成 renderBody() 函数的调用,通过 renderBody() 函数,返回被渲染的页面结构,具体代码如下:
@override
Widget build(BuildContext context) {
  return Scaffold(
    // 头部 AppBar 区域
    appBar: AppBar(),
    // 中间页面主体区域
    body: renderBody(),
    // 浮动按钮区域
    floatingActionButton: ButtonBar()
  )
}
  1. 定义 renderBody() 函数如下:
// 渲染页面主体区域
Widget renderBody() {
  // 如果用户没有选择任何图片,则只渲染文本
  if (_image == null) {
    return Center(
      child: Text('暂无图片!'),
    );
  }
  // 在页面上渲染对应的图片
  return Stack(
    children: <Widget>[
      Image.file(
        _image, // 被渲染的图片
        height: double.infinity, // 图片高度撑满整个页面的高度
        fit: BoxFit.cover, // 图片的填充模式
      )
    ]);
}

5. 申请百度 AI 开放平台账号并创建人脸识别的应用

  1. 浏览器访问 http://ai.baidu.com/ 后,注册百度 AI 开放平台账号
  2. 登录账号,并进入控制台,在左侧菜单中选择 人脸识别 后,点击 创建应用 按钮
  3. 填写对应的应用信息后,点击 立即创建 按钮,最终获取到对应的 API KeySecret Key

6. 鉴权认证机制

如果要成功调用百度 AI 的接口,必须先通过百度的鉴权认证。百度的鉴权认证非常简单,只要能够成功获取到 Access Token,就可以拿着百度颁发给我们的 Access Token 访问对应的 AI 接口。百度 AI 鉴权的文档地址为 https://ai.baidu.com/docs#/Auth/top

7. 通过 dio 发起网络数据请求

插件地址 https://pub.dev/packages/dio ,使用步骤如下:

  1. pubspec.yamldependencies 节点中,新增插件如下:
dependencies:
dio: ^3.0.10
  1. lib/main.dart 头部,引入 dio,并创建实例对象:
import 'package:dio/dio.dart';
Dio dio = new Dio();
  1. 通过 dio.post() 发起 post 请求,代码格式如下:
// 通过 async 和 await 简化异步 API 调用方式
void getHttp() async {
  // 发起 post 请求
  // 参数1:请求的URL地址【必选】
  // 参数2:通过请求体发送的数据【可选】
  // 参数3:请求配置项【可选】
  var response = await dio.post("请求地址", data: {/* body请求体 */}, options: new Options());

  // 打印服务器返回的数据
  print(response.data);
}

8. Toast 提示

  1. pubspec.yamldependencies 节点中,新增插件如下:
dependencies:
  toast: ^0.1.3
  1. lib/main.dart 中导入对应的插件:
import 'package:toast/toast.dart';
  1. 调用 Toast.show() 函数提示消息:
// 参数1:提示消息
// 参数2:提示消息多久后自动隐藏
// 参数3:位置
Toast.show("鉴权失败!", context, duration: Toast.LENGTH_LONG, gravity: Toast.CENTER);

9. 图片转 base64 字符串

在调用测颜值的 API 期间,需要先把图片转为 base64 的字符串,转换过程如下:

// 将照片转换为字节数组
var imageBytes = await image.readAsBytes();
// 将字节数组转换为 base64 格式的字符串
var imageBase64 = base64Encode(imageBytes);

10. 为 dio 的 post 请求设置 data 和 options

在发送 post 请求期间,如果需要设置 body 请求体和 options 配置项,可以参考如下代码:

// 请求的URL地址
var testFaceURL = 'https://aip.baidubce.com/rest/2.0/face/v3/detect?access_token=' + accessResult.data['access_token'];

// 发起请求
var testFaceResult = await dio.post(testFaceURL,
    // 发送到后台的 body 数据
    data: {
      'image': imageBase64,
      'image_type': 'BASE64',
      // face_field 是要获取的人脸信息字段,
      // 年龄,性别,颜值,表情,眼镜,情绪
      'face_field': 'age,gender,beauty,expression,glasses,emotion'
    },
    // 请求配置
      options: new Options(responseType: ResponseType.json));

11. 渲染人脸信息

  1. 修改 renderBody() 函数,在 Stack 控件中,通过调用 renderFaceInfo() 函数,渲染人脸信息区域:
// 渲染页面主体区域
Widget renderBody() {
  // 如果用户没有选择任何图片,则只渲染文本
  if (_image == null) {
    return Center(
      child: Text('暂无图片!'),
    );
  }
  // 在页面上渲染对应的图片
  return Stack(
    children: <Widget>[
      Image.file(
        _image,
        height: double.infinity,
        fit: BoxFit.cover,
      ),
      // 渲染人脸信息区域
+++   renderFaceInfo()
    ]);
}
  1. 定义 renderFaceInfo() 函数如下:
// 渲染识别出来的人脸信息
  Widget renderFaceInfo() {
    // 如果人脸信息为空,则渲染空字符串
    if (faceInfo == null) {
      return Text('');
    }
    // 如果人脸信息不为空,则渲染人脸信息区域
    return Center(
      // 渲染矩形盒子区域
      child: Container(
        decoration: BoxDecoration(
            // 背景颜色【半透明的白色】
            color: Colors.white54,
            // 圆角
            borderRadius: BorderRadius.all(Radius.circular(5))),
        // 盒子的宽度
        width: 300,
        // 盒子的高度
        height: 200,
        // 【列组件】
        child: Column(
          // 子元素在纵轴上分散对齐
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          // 在列组件中,渲染多个【行组件】
          children: <Widget>[
            Row(
              // 子元素在横轴上分散对齐
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: <Widget>[
                Text('年龄:${faceInfo['age']}岁'),
                Text('性别:' + genderMap[faceInfo['gender']['type']]),
              ],
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: <Widget>[
                Text('颜值:${faceInfo['beauty']}分'),
                Text('表情:' + expressionMap[faceInfo['expression']['type']]),
              ],
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: <Widget>[
                Text('眼镜:' + glassesMap[faceInfo['glasses']['type']]),
                Text('情绪:' + emotionMap[faceInfo['emotion']['type']]),
              ],
            )
          ],
        ),
      ),
    );
  }
  1. _MyHomePageState 状态管理类中,定义 Map 映射,辅助渲染人脸信息:
// 性别
Map genderMap = {'male': '男', 'female': '女'};
// 表情
Map expressionMap = {'none': '不笑', 'smile': '微笑', 'laugh': '大笑'};
// 眼镜
Map glassesMap = {'none': '无眼镜', 'common': '普通眼镜', 'sun': '墨镜'};
// 情绪
Map emotionMap = {
  'angry': '愤怒',
  'disgust': '厌恶',
  'fear': '恐惧',
  'happy': '高兴',
  'sad': '伤心',
  'surprise': '惊讶',
  'neutral': '无情绪'
};

12. 实现 loading 效果

  1. _MyHomePageState 状态管理类中,定义 isloading 数据如下:
class _MyHomePageState extends State<MyHomePage> {
  // false 为不显示 loading
  // true 为显示 loading
  bool isloading = false;
}
  1. 修改 getFaceInfo() 函数如下,在适当的时机重置 isloading 的值:
// 发起请求,获取人脸信息
void getFaceInfo(image) async {
  // 只要调用这个函数,就立即展示 loading 效果
  setState(() {
    isloading = true;
  });

  // 鉴权
  // ... 省略不必要的代码

  // 鉴权失败
  if (accessResult.data['access_token'] == null) {
    // 鉴权失败,隐藏 loading 效果
    setState(() {
      isloading = false;
    });
    // ... 省略不必要的代码
    return;
  }

  // 鉴权成功
  // 检测人脸信息
  // ... 省略不必要的代码

  // 检测失败
  if (testFaceResult.data['error_msg'] != 'SUCCESS' ||testFaceResult.data['result']['face_num'] <= 0) {
    // 检测失败,隐藏 loading 效果
    setState(() {
      isloading = false;
    });
    // ... 省略不必要的代码
    return;
  }

  // 检测成功,隐藏 loading 效果
  setState(() {
    faceInfo = testFaceResult.data['result']['face_list'][0];
    isloading = false;
  });
}
  1. 修改 renderFaceInfo() 函数,按需渲染 loading 效果:
// 渲染识别出来的人脸信息
Widget renderFaceInfo() {
  if (faceInfo == null) {
++  //如果 isloading 为 true,就在页面正中央渲染 loading 效果
++  if (isloading) {
++    return Center(child: CircularProgressIndicator());
++  }
    // 否则,直接渲染空字符串
    return Text('');
  }

  // ... 省略不必要的代码
}
// 导入依赖项
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:toast/toast.dart';

Dio dio = new Dio();

class MyFacePage extends StatefulWidget {
  MyFacePage({
    Key key,
  }) : super(key: key);
  @override
  _MyFacePageState createState() => _MyFacePageState();
}

class _MyFacePageState extends State<MyFacePage> {
  // 用户通过摄像头或图片库选择的照片
  File _image;
  var faceInfo;
  bool isloading = false;
  Map genderMap = {'male': '男', 'female': '女'};
  Map expressionMap = {'none': '不笑', 'smile': '微笑', 'laugh': '大笑'};
  Map glassesMap = {'none': '无眼镜', 'common': '普通眼镜', 'sun': '墨镜'};
  Map emotionMap = {
    'angry': '愤怒',
    'disgust': '厌恶',
    'fear': '恐惧',
    'happy': '高兴',
    'sad': '伤心',
    'surprise': '惊讶',
    'neutral': '无情绪'
  };

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // 头部 AppBar 区域
      appBar: AppBar(
        title: Text(
          "人脸识别",
          style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
        ),
        centerTitle: true,
      ),
      // 中间页面主体区域
      body: renderBody(),
      // 底部浮动按钮区域
      floatingActionButton: ButtonBar(
        alignment: MainAxisAlignment.spaceAround,
        children: <Widget>[
          FloatingActionButton(
            onPressed: () {
              choosePic(ImageSource.camera);
            },
            tooltip: 'takephoto',
            child: Icon(Icons.photo_camera),
          ),
          FloatingActionButton(
            onPressed: () {
              choosePic(ImageSource.gallery);
            },
            tooltip: 'takepicture',
            child: Icon(Icons.photo_library),
          )
        ],
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }

  // 渲染页面主体区域
  Widget renderBody() {
    // 如果用户没有选择任何图片,则只渲染文本
    if (_image == null) {
      return Center(
        child: Text('暂无图片!'),
      );
    }
    // 在页面上渲染对应的图片
    return Stack(
      children: <Widget>[
        Image.file(
          _image,
          height: double.infinity,
          fit: BoxFit.cover,
        ),
        renderFaceInfo()
      ],
    );
  }

  // 渲染识别出来的人脸信息
  Widget renderFaceInfo() {
    if (faceInfo == null) {
      if (isloading) {
        return Center(child: CircularProgressIndicator());
      }
      return Text('');
    }
    return Center(
      child: Container(
        decoration: BoxDecoration(
            // 背景颜色
            color: Colors.white54,
            // 圆角
            borderRadius: BorderRadius.all(Radius.circular(5))),
        width: 300,
        height: 200,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: <Widget>[
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: <Widget>[
                Text('年龄:${faceInfo['age']}岁'),
                Text('性别:' + genderMap[faceInfo['gender']['type']]),
              ],
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: <Widget>[
                Text('颜值:${faceInfo['beauty']}分'),
                Text('表情:' + expressionMap[faceInfo['expression']['type']]),
              ],
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: <Widget>[
                Text('眼镜:' + glassesMap[faceInfo['glasses']['type']]),
                Text('情绪:' + emotionMap[faceInfo['emotion']['type']]),
              ],
            )
          ],
        ),
      ),
    );
  }

  // 点击按钮,选择图片
  void choosePic(source) async {
    // 得到选取的照片
    var image = await ImagePicker.pickImage(source: source);

    setState(() {
      _image = image;
      faceInfo = null;
    });

    // 如果选取的照片为空,则不执行后续人脸检测的业务逻辑
    if (image == null) {
      return;
    }

    // 调用获取人脸信息的函数
    getFaceInfo(image);
  }

  // 发起请求,获取人脸信息
  void getFaceInfo(image) async {
    setState(() {
      isloading = true;
    });
    // 将照片转换为字节数组
    var imageBytes = await image.readAsBytes();
    // 将字节数组转换为 base64 格式的字符串
    var imageBase64 = base64Encode(imageBytes);

    // 人工智能API接口鉴权
    var accessURL =
        "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=o631j8eLGBQq88uj8IXsVeGO&client_secret=Pses7CgBpF5Hf9owM6N8v8iX6iLAP7G5";
    var accessResult = await dio.post(accessURL);
    // 判断是否获取到了access_token
    if (accessResult.data['access_token'] == null) {
      setState(() {
        isloading = false;
      });
      Toast.show("鉴权失败!", context,
          duration: Toast.LENGTH_LONG, gravity: Toast.CENTER);
      return;
    }

    // 发起请求,获取检测结果
    var testFaceURL =
        'https://aip.baidubce.com/rest/2.0/face/v3/detect?access_token=' +
            accessResult.data['access_token'];
    var testFaceResult = await dio.post(testFaceURL,
        data: {
          'image': imageBase64,
          'image_type': 'BASE64',
          'face_field': 'age,gender,beauty,expression,glasses,emotion'
        },
        options: new Options(responseType: ResponseType.json));
    // print(testFaceResult.data);

    // 识别失败
    if (testFaceResult.data['error_msg'] != 'SUCCESS' ||
        testFaceResult.data['result']['face_num'] <= 0) {
      setState(() {
        isloading = false;
      });
      Toast.show("人脸识别失败!", context,
          duration: Toast.LENGTH_LONG, gravity: Toast.CENTER);
      return;
    }

    // 识别成功
    setState(() {
      faceInfo = testFaceResult.data['result']['face_list'][0];
      isloading = false;
    });
    print(faceInfo);
  }
}

基于 Flutter + 百度人工智能 开发出的一款测颜值的 App_Text

标签:dio,return,渲染,测颜值,Text,App,faceInfo,image,Flutter
From: https://blog.51cto.com/u_16163480/8319413

相关文章

  • 开发一款量化交易APP软件系统
    市场的行情都是在不断变化的,量化也会成为一种创业的趋势,为了实现广大创业者的量化交易需求,我们对市场的分析后开发一款量化交易的app软件。一、系统概述该系统软件基于移动端的量化交易,体现为创业者提供便捷,高效的交易服务。该系统软件采用的是量化交易算法,结合大数据,历史数据,人......
  • 量化交易APP开发解决方案
    一、项目背景量化软件的需求日益的增加,开发一款量化交易app软件显得特别的钟涛,该软件的能够快速帮助用户获得市场的重要信息,通过分析得到相关的商机,制定相关的交易策略,提高交易的利润。二、功能设计1.数据获取:量化数据的获取,通过市场的接口,大数据的分析软件对接,得到市场的变化规......
  • 合约量化交易系统APP开发案例
    项目背景:该案例软件的开发是一款针对市场交易员而设计的软件系统,它能帮助交易者实现高效率的合约交易。我们目的是以用户的需求为主进行开发,应对市场上未知的风险和把控。开发过程:软件在开发过程中,采用了多种的开发按技术,软件工具包,开发框架,数据库技术,移动端的开发。在开发前先......
  • uniapp 404页面
    需求:uniapp在写H5时,如果在地址栏乱输入,会跳转到404页面。思路:uniapp有个应用生命周期onPageNotFound,让跳转不存在的页面时会执行这个回调函数。所以需求就很好解决了!具体操作:第一步:写一个404页面,并在pages.json中配置其路由。第二步:在App.vue的onPageNotFound......
  • Appscan使用方法总结
    一、典型流程和术语介绍A. 新建一个扫描,并为这个扫描选择一个扫描模板。扫描模板:包含用户自定义扫描参数或者是工具默认参数的集合,目的是方面用户快速配置扫描参数。B. 打开配置向导并选择Web应用扫描和WebService扫描中的一种。注意:Appscan在安装了GSC(通用服务客户端)组件的环......
  • call与apply的第一个参数都为this的指向,call后面的参数为传入的参数列表,apply为参数
    【摘自JavaScript高级程序设计】函数还有两个方法:apply()和call()。这两个方法都会以指定的this值来调用函数,即会设置调用函数时函数体内this对象的值。apply()方法接收两个参数:函数内this的值和一个参数数组。第二个参数可以是Array的实例,但也可以是arguments对象。来看下面的例......
  • 通过hook技术拦截某个APP的用户列表可能实现吗,举个例子
    ​ 拦截某个应用程序的用户列表通常涉及到对应用程序的通信进行监控或修改。使用钩子(hook)技术是一种常见的方法,但需要注意的是,这种行为可能违反应用程序的使用条款,可能会引起法律问题。在进行此类活动之前,请确保你有权进行这样的操作,并且你的目的是合法的。以下是一个可能的例......
  • 电脑app开发教程!
    电脑应用程序已经成为了我们日常生活和工作中不可或缺的一部分,无论是购物、社交还是工作,电脑应用程序都扮演着重要的角色,因此,许多企业和个人都希望开发自己的电脑应用程序,以更好地满足自己的需求。然而,对于初学者来说,开发一个电脑应用程序可能会有些困难,今天,我将为大家提供一个简单......
  • [USACO22OPEN] Apple Catching G
    [USACO22OPEN]AppleCatchingG题目描述天上下苹果了!在某些时刻,一定数量的苹果会落到数轴上。在某些时刻,FarmerJohn的一些奶牛将到达数轴并开始接苹果。如果一个苹果在没有奶牛接住的情况下落到数轴上,它就会永远消失。如果一头奶牛和一个苹果同时到达,奶牛就会接住苹果。每头......
  • 【虹科干货】Lambda数据架构和Kappa数据架构——构建现代数据架构
    如何更好地构建我们的数据处理架构,如何对IT系统中的遗留问题进行现代化改造并将其转变为现代数据架构?该怎么为你的需求匹配最适合的架构设计呢,本文将分析两种最流行的基于速度的数据架构,为你提供一些思路。文章速览:什么是数据架构?基于速度的数据架构结语 一、什么是数据......