一 人脸识别
1.1 使用步骤
https://cloud.baidu.com/product/face.html


1.2 上传-删除-匹配人脸
from aip import AipFace
import base64
from pypinyin import lazy_pinyin, Style
class BaiDuAI:
def __init__(self,APP_ID='62643893',API_KEY='hfAwQUE1fwyCjXG01ZCHbaSG',SECRET_KEY='qgxJneAt0ovmGA2rLrdq99GEFs1apjte'):
""" 你的 APPID AK SK """
self.APP_ID = APP_ID
self.API_KEY = API_KEY
self.SECRET_KEY = SECRET_KEY
self.client = AipFace(self.APP_ID, self.API_KEY, self.SECRET_KEY)
def name_to_pinyin(self,text):
style = Style.TONE3
name_list=lazy_pinyin(text, style=style)
return ''.join(name_list)
def add_user(self):
data = base64.b64encode(open('./gtl.png','rb').read()).decode('utf-8')
image = data
imageType = "BASE64"
groupId = "100"
userId=self.name_to_pinyin('古天乐')
""" 调用人脸注册 """
self.client.addUser(image, imageType, groupId, userId);
""" 如果有可选参数 """
options = {}
options["user_info"] = "彭于晏"
options["quality_control"] = "NORMAL"
options["liveness_control"] = "LOW"
options["action_type"] = "REPLACE"
""" 带参数调用人脸注册 """
self.client.addUser(image, imageType, groupId, userId, options)
def search(self):
data = base64.b64encode(open('./pyy2.png', 'rb').read()).decode('utf-8')
image = data
imageType = "BASE64"
groupIdList = "100,2"
""" 调用人脸搜索 """
self.client.search(image, imageType, groupIdList);
""" 如果有可选参数 """
options = {}
options["match_threshold"] = 70
options["quality_control"] = "NORMAL"
options["liveness_control"] = "LOW"
options["max_user_num"] = 3
""" 带参数调用人脸搜索 """
res=self.client.search(image, imageType, groupIdList, options)
print(res)
def delete(self):
userId = "user1"
groupId = "group1"
faceToken = "face_token_23123"
""" 调用人脸删除 """
self.client.faceDelete(userId, groupId, faceToken);
if __name__ == '__main__':
ai=BaiDuAI()
ai.search()
1.3 项目中集成
class CollectionSaveSerializer(serializers.ModelSerializer):
class Meta:
model = Collection
fields = ['name', 'avatar', 'area']
def create(self, validated_data):
from libs.baidu_ai import BaiDuAI
baidu=BaiDuAI()
avatar_file_object = validated_data.get('avatar')
print(avatar_file_object)
name = validated_data.get('name')
name_pinyin=baidu.name_to_pinyin(name)
res=baidu.add_user(avatar_file_object,name,name_pinyin)
validated_data['name_pinyin'] = name_pinyin
validated_data['face_token'] = res.get('result').get('face_token')
instance=super().create(validated_data)
return instance
def destroy(self, request, *args, **kwargs):
from libs.baidu_ai import BaiDuAI
instance = self.get_object()
baidu=BaiDuAI()
res=baidu.delete(instance.name_pinyin,face_token=instance.face_token)
print(res)
self.perform_destroy(instance)
return Response()
二 语音识别
2.1 收费方案

def speed(file_object):
from aip import AipSpeech
APP_ID = ''
API_KEY = ''
SECRET_KEY = ''
client = AipSpeech(APP_ID, API_KEY, SECRET_KEY)
data = file_object.read()
return client.asr(data, 'pcm', 16000, {'dev_pid': 1537})
2.2 免费方案
\site-packages\speech_recognition\pocketsphinx-data
1.WAV
2.AIFF/AIFF-C
3.FLAC
import speech_recognition as sr
audio_file = './test1.wav'
r = sr.Recognizer()
with sr.AudioFile(audio_file) as source:
audio = r.record(source)
result = r.recognize_sphinx(audio, language="zh-CN")
print(result)
三 采集统计
小程序端statistics
<view class="container">
<view class="menu" wx:for="{{dataList}}" wx:key="index">
<view> <label class="iconfont icon-SCHEDULE" ></label> {{item.date}}</view>
<label>{{item.count}}个</label>
</view>
</view>
.container{
border-top: 1px solid
}
.container .menu{
font-size: small;
padding: 10px 40rpx;
border-bottom: 1px dotted
text-align: center;
display: flex;
flex-direction: row;
justify-content: space-between;
background-color: white;
}
var app = getApp();
var api = require("../../config/settings.js")
Page({
/**
* 页面的初始数据
*/
data: {
dataList:[{'date':'2024年4月20日','count':22},{'date':'2024年4月21日','count':12},{'date':'2024年4月22日','count':232}]
},
getRecord:function(){
wx.showLoading({mask:true})
wx.request({
url: api.statistics,
method:"GET",
success :(res) =>{
this.setData({
dataList:res.data
})
},
complete:()=>{
wx.hideLoading()
}
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.getRecord();
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
this.getRecord();
},
})
{
"usingComponents": {},
"navigationBarTitleText": "采集统计",
"enablePullDownRefresh": true
}
后端接口
class StatisticsView(GenericViewSet, ListModelMixin):
queryset = Collection.objects.annotate(date=Trunc('create_time', 'day')).values('date').annotate(count=Count('id')).values('date', 'count')
serializer_class = StatisticsListSerializer
class StatisticsListSerializer(serializers.Serializer):
date = serializers.DateTimeField(format="%Y年%m月%d日")
count = serializers.IntegerField()
class Collection(models.Model):
name = models.CharField(max_length=32, verbose_name='采集人员姓名')
name_pinyin=models.CharField(max_length=32, verbose_name='姓名拼音',null=True)
avatar = models.ImageField(upload_to='collection/%Y/%m/%d/', default='default.png', verbose_name='头像')
create_time = models.DateTimeField(verbose_name='采集时间',default=datetime.now())
face_token=models.CharField(max_length=128, verbose_name='百度ai的Token',null=True)
area = models.ForeignKey(to='Area', null=True, verbose_name='网格区域', on_delete=models.CASCADE)
class Meta:
verbose_name_plural = '采集表'
def __str__(self):
return self.name
四 人脸检测
4.1 小程序端
<!--pages/face/face.wxml-->
<view class="header">
<camera class="camera" device-position="{{ backFront ? 'back' : 'front' }}" flash="off" frame-size="medium"></camera>
<view class="switch" bindtap="switchCamera">
<image src="/images/camera/rotate-camera-white.png"></image>
</view>
<button class="submit" bindtap="takePhoto"> 拍照检测 </button>
</view>
<view class="table">
<view class="item">
<view class="title">检测记录</view>
</view>
<view class="item" wx:for="{{record}}" wx:for-item="row" wx:key="index">
<view class="record">
<view class="avatar">
<image src="{{row.avatar}}"></image>
</view>
<view class="desc">
<view wx:if="{{row.code == 100}}" class="username">检测成功:{{row.user_id}}</view>
<view wx:else class="username">检测失败:{{row.msg}}</view>
<view>
<view class="txt-group">
<label class="zh">{{row.error_msg}}</label>
</view>
</view>
</view>
<view class="delete">
<block wx:if="{{row.code == 100}}">
<label class="iconfont icon-ziyuanxhdpi" style="color:green"></label>
</block>
<block wx:else>
<label class="iconfont icon-ziyuanxhdpi" style="color:red"></label>
</block>
</view>
</view>
</view>
</view>
/* pages/face/face.wxss */
.header{
position: relative;
}
.camera{
height: 600rpx;
width: 100%;
}
.switch{
position: absolute;
top: 10rpx;
right: 20rpx;
height: 80rpx;
width: 80rpx;
}
.switch image{
height: 100%;
width: 100%;
}
.submit{
margin-top: 40rpx;
color:
border: 2rpx solid
background-color:
font-size: 32rpx;
font-weight: normal;
}
.table{
margin-top: 40rpx;
border-top: 1rpx solid
}
.table .item {
border-bottom: 1rpx solid
}
.table .item .title{
margin: 20rpx 30rpx;
padding-left: 10rpx;
border-left: 5rpx solid
font-size: 26rpx;
}
.record{
margin: 10rpx 40rpx;
display: flex;
flex-direction: row;
justify-content: space-between;
}
.record .avatar{
width: 100rpx;
height: 100rpx;
}
.record .avatar image{
width: 100%;
height: 100%;
border-radius: 30rpx;
}
.record .desc{
margin: 0 40rpx;
}
.desc{
width: 290rpx;
display: flex;
flex-direction: column;
justify-content: space-around;
}
.desc .username{
font-size: 25rpx;
}
.txt-group{
font-size: 20rpx;
margin: 5rpx 0;
}
.txt-group .zh{
color:
}
.txt-group .en{
color:
}
.area{
color:
font-weight: bold;
}
.delete{
width: 100rpx;
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
}
var api = require("../../config/settings.js")
Page({
data: {
backFront:true,
record:[]
},
switchCamera(e) {
var old = this.data.backFront
this.setData({
backFront: !old
})
},
takePhoto(e){
wx.showLoading({
title: '检测中',
mask:true
})
const ctx = wx.createCameraContext()
ctx.takePhoto({
quality: 'high',
success: (res) => {
wx.uploadFile({
url: api.face,
filePath: res.tempImagePath,
name: 'avatar',
success:(response)=>{
let resdata = JSON.parse(response.data)
console.log(resdata)
if(resdata.code==100 || resdata.code==102){
console.log(resdata)
resdata.avatar = res.tempImagePath
var oldRecord = this.data.record
oldRecord.unshift(resdata)
console.log(oldRecord)
this.setData({
record:oldRecord
})
}else{
wx.showToast({
title: '请正常拍照'
})
}
},
complete:function(){
wx.hideLoading()
}
})
}
})
},
})
{
"usingComponents": {},
"navigationBarTitleText": "人脸检测"
}
4.2 后端
class FaceView(GenericViewSet):
def create(self, request, *args, **kwargs):
avatar_object = request.data.get('avatar')
if not avatar_object:
return Response({"msg": "未提交图像", "code": 101})
from libs.baidu_ai import BaiDuAI
ai = BaiDuAI()
result = ai.search(avatar_object)
if result.get('error_code') == 0:
user = result.get('result').get('user_list')[0]
user_info = user.get('user_info')
user_id = user.get('user_id')
score = user.get('score')
return Response({"code": 100, 'msg': '匹配成功', 'user_info': user_info, 'user_id': user_id, 'score': score,'avatar':''})
else:
return Response({"code": 102, 'msg': '匹配失败,该人员可能不是我社区人员,注意防范'})
from aip import AipFace
import base64
from pypinyin import lazy_pinyin, Style
class BaiDuAI:
def __init__(self, APP_ID='62643893', API_KEY='hfAwQUE1fwyCjXG01ZCHbaSG',
SECRET_KEY='qgxJneAt0ovmGA2rLrdq99GEFs1apjte'):
""" 你的 APPID AK SK """
self.APP_ID = APP_ID
self.API_KEY = API_KEY
self.SECRET_KEY = SECRET_KEY
self.client = AipFace(self.APP_ID, self.API_KEY, self.SECRET_KEY)
def name_to_pinyin(self, text):
style = Style.TONE3
name_list = lazy_pinyin(text, style=style)
return ''.join(name_list)
def add_user(self, path, name, userId,groupId=100):
image = base64.b64encode(path.read()).decode('utf-8')
imageType = "BASE64"
""" 调用人脸注册 """
res=self.client.addUser(image, imageType, groupId, userId);
print(res)
return res
def search(self,img_obj):
image = base64.b64encode(img_obj.read()).decode('utf-8')
image_type = "BASE64"
group_id_list = "100,101"
""" 调用人脸搜索 """
res=self.client.search(image, image_type, group_id_list);
print(res)
return res
def delete(self,user_id,face_token,group_id=100):
""" 调用人脸删除 """
res=self.client.faceDelete(user_id, group_id, face_token)
return res
if __name__ == '__main__':
ai = BaiDuAI()
ai.search()
五 语音识别
5.1 小程序前端
// https://developers.weixin.qq.com/miniprogram/dev/api/media/recorder/RecorderManager.start.html
const recorderManager = wx.getRecorderManager()
var api = require("../../config/settings.js")
Page({
/**
* 页面的初始数据
*/
data: {
content:"",
record:false
},
recordStart:function(){
this.setData({record:true})
const options = {
// duration: 6000,//指定录音的时长,单位 ms
sampleRate: 16000,//采样率
numberOfChannels: 1,//录音通道数
encodeBitRate: 48000,//编码码率
format: 'wav'//音频格式,有效值
}
//开始录音
recorderManager.start(options)
},
recordCancel:function(){
console.log("停止");
this.setData({record:false})
wx.hideLoading()
},
recordStop:function(){
if(!this.data.record){return}
recorderManager.stop();
recorderManager.onStop((res) => {
// this.tempFilePath = res.tempFilePath
wx.showLoading()
wx.uploadFile({
filePath: res.tempFilePath,
name: 'voice',
url: api.voice,
success:(response)=>{
console.log(response)
// {'code': 100, 'msg':'成功','result': ['欢迎欢迎']}
let voiceResponse = JSON.parse(response.data)
if(voiceResponse.code == 100){
console.log(voiceResponse)
this.setData({
content:this.data.content + voiceResponse.result[0]
})
}else{
wx.showToast({
title: '识别失败,请重新操作!',
icon: "none"
})
}
},
complete:()=>{
wx.hideLoading()
}
},
)
})
this.setData({record:false})
},
})
<!--pages/voice/voice.wxml-->
<textarea class="text" placeholder="等待语音识别自动录入..." placeholder-class="hoder" model:value="{{content}}" maxlength="{{-1}}"></textarea>
<button class="btn" hover-class="press" bind:longpress="recordStart" bind:touchcancel="recordCancel" bind:touchend="recordStop"> <label class="fa fa-microphone"></label> 按住说话</button>
/* pages/voice/voice.wxss */
page{
background-color:
}
.text{
height: 400rpx;
background-color: white;
width: 100%;
padding: 20rpx;
}
.btn{
margin-top: 30rpx;
/* color:
border: 2rpx solid
background-color: white;
font-size: 32rpx;
font-weight: normal;
}
.press label{
color:
}
.press{
background-color:
}
.hoder{
font-size: 28rpx;
}
{
"usingComponents": {},
"navigationBarTitleText": "语音识别"
}
5.2 后端
from libs.baidu_ai import BaiDuVoice
class VoiceView(GenericViewSet):
def create(self, request, *args, **kwargs):
voice_object = request.data.get('voice')
ai = BaiDuVoice()
result = ai.speed(voice_object)
if result.get('err_no') == 0:
return Response({'code': 100, 'msg': '识别成功', 'result': result.get('result')})
else:
return Response({'code': 101, 'msg': '识别失败'})
from aip import AipFace
import base64
from pypinyin import lazy_pinyin, Style
from aip import AipSpeech
class BaiDuAI:
def __init__(self, APP_ID='62643893', API_KEY='hfAwQUE1fwyCjXG01ZCHbaSG',
SECRET_KEY='qgxJneAt0ovmGA2rLrdq99GEFs1apjte'):
""" 你的 APPID AK SK """
self.APP_ID = APP_ID
self.API_KEY = API_KEY
self.SECRET_KEY = SECRET_KEY
self.client = AipFace(self.APP_ID, self.API_KEY, self.SECRET_KEY)
def name_to_pinyin(self, text):
style = Style.TONE3
name_list = lazy_pinyin(text, style=style)
return ''.join(name_list)
def add_user(self, path, name, userId,groupId=100):
image = base64.b64encode(path.read()).decode('utf-8')
imageType = "BASE64"
""" 调用人脸注册 """
res=self.client.addUser(image, imageType, groupId, userId);
print(res)
return res
def search(self,img_obj):
image = base64.b64encode(img_obj.read()).decode('utf-8')
image_type = "BASE64"
group_id_list = "100,101"
""" 调用人脸搜索 """
res=self.client.search(image, image_type, group_id_list);
print(res)
return res
def delete(self,user_id,face_token,group_id=100):
""" 调用人脸删除 """
res=self.client.faceDelete(user_id, group_id, face_token)
return res
class BaiDuVoice:
def __init__(self, APP_ID='63701411', API_KEY='b0tYjKxPmcuoSm4SAen19p2c',SECRET_KEY='m8ramzeLtMLHEa9XRPXQJNbcAWGiIuZ8'):
""" 你的 APPID AK SK """
self.APP_ID = APP_ID
self.API_KEY = API_KEY
self.SECRET_KEY = SECRET_KEY
self.client = AipSpeech(self.APP_ID, self.API_KEY, self.SECRET_KEY)
def speed(self, voice_object):
res=self.client.asr(voice_object.read(), 'pcm', 16000, {
'dev_pid': 1537,
})
return res
if __name__ == '__main__':
ai = BaiDuVoice()
file=open('../a.wav','rb')
res=ai.speed(file)
print(res)
六 公告
6.1 微信小程序端
const api = require("../../config/settings.js")
Page({
data: {
noticeList: [
{
title: '公告标题1',
create_time: '2024-04-25',
content: '公告内容描述1,公告内容描述1,公告内容描述1。', // 可以根据实际情况添加更多内容
igm: '/images/notice/notice1.jpg' // 图片路径,根据实际情况修改
},
{
title: '公告标题2',
create_time: '2024-04-26',
content: '公告内容描述2,公告内容描述2,公告内容描述2。', // 可以根据实际情况添加更多内容
igm: '/images/notice/notice2.jpg' // 图片路径,根据实际情况修改
},
// 可以添加更多社区公告数据
]
},
onLoad: function () {
// 页面加载时执行的逻辑
this.refresh()
},
refresh(){
wx.showLoading({
mask: true
})
wx.request({
url: api.notice,
method: "GET",
success: (res) => {
this.setData({
noticeList: res.data
})
},
complete() {
wx.hideLoading()
}
})
}
})
<!-- community_notice.wxml -->
<view class="container">
<!-- 使用wx:for循环遍历社区公告列表 -->
<view wx:for="{{noticeList}}" wx:key="index" class="notice-item">
<!-- 左侧图片 -->
<image class="notice-image" src="{{item.igm}}" mode="aspectFill"></image>
<!-- 右侧内容 -->
<view class="notice-content">
<view class="notice-title">{{item.title}}</view>
<view class="notice-time">{{item.create_time}}</view>
<view class="notice-details">{{item.content}}</view>
</view>
</view>
</view>
/* community_notice.wxss */
.container {
padding: 20rpx;
}
.notice-item {
display: flex;
align-items: flex-start;
margin-bottom: 20rpx; /* 添加间距 */
border-bottom: 1px solid
padding-bottom: 20rpx; /* 增加底部内边距 */
}
.notice-image {
width: 150rpx;
height: 120rpx;
border-radius: 6rpx;
margin-right: 20rpx;
}
.notice-content {
flex: 1;
}
.notice-title {
font-size: 28rpx;
font-weight: bold;
margin-bottom: 10rpx;
}
.notice-time {
font-size: 24rpx;
color:
margin-bottom: 10rpx;
}
.notice-details {
font-size: 24rpx;
color:
}
6.2 后端接口
from .models import Notice
from .serializer import NoticeSerializer
class NoticeView(GenericViewSet,ListModelMixin):
queryset =Notice.objects.all().order_by('create_time')
serializer_class = NoticeSerializer
class NoticeSerializer(serializers.ModelSerializer):
class Meta:
model = Notice
fields = ['id', 'title','igm','create_time','content']
extra_kwargs={
'create_time':{'format':"%Y-%m-%d"}
}
七 活动列表
小程序端
var app = getApp();
var api = require("../../config/settings.js")
Page({
data: {
activityList: [
]
},
onLoad: function () {
// 页面加载时执行的逻辑
this.refresh()
},
refresh(){
wx.showLoading({
mask: true
})
wx.request({
url: api.activity,
method: "GET",
success: (res) => {
this.setData({
activityList: res.data
})
},
complete() {
wx.hideLoading()
}
})
},
handleSignup: function (event) {
// 处理报名按钮点击事件
var index = event.currentTarget.dataset.index; // 获取当前点击的活动索引
console.log('点击了报名按钮,索引为:', index);
}
})
<!-- activity_signup.wxml -->
<view class="container">
<!-- 使用wx:for循环遍历活动报名列表 -->
<view wx:for="{{activityList}}" wx:key="index" class="activity-item">
<!-- 活动内容 -->
<view class="activity-content">
<view class="activity-title">{{item.title}}</view>
<view class="activity-enrollment">报名人数:{{item.count}} | 总人数:{{item.total_count}}</view>
<view class="activity-time">获得积分:{{item.score}}</view>
<view class="activity-time">{{item.date}}</view>
<view class="activity-description">{{item.text}}</view>
</view>
<!-- 报名按钮 -->
<button class="signup-btn" bindtap="handleSignup">报名</button>
</view>
</view>
/* activity_signup.wxss */
.container {
padding: 20rpx;
}
.activity-item {
display: flex;
align-items: flex-start;
justify-content: space-between;
margin-bottom: 20rpx;
border-bottom: 1px solid
padding-bottom: 20rpx;
}
.activity-content {
flex: 1;
}
.activity-title {
font-size: 28rpx;
font-weight: bold;
margin-bottom: 10rpx;
}
.activity-time {
font-size: 24rpx;
color:
margin-bottom: 10rpx;
}
.activity-enrollment {
font-size: 24rpx;
color:
margin-bottom: 10rpx;
}
.activity-description {
font-size: 24rpx;
color:
margin-top: 10rpx;
white-space: pre-wrap; /* 自动换行 */
}
.signup-btn {
background-color:
color:
border: none;
border-radius: 4rpx;
padding: 10rpx 20rpx;
font-size: 24rpx;
}
后端接口
from .models import Activity
from .serializer import ActivitySerializer
class ActivityView(GenericViewSet,ListModelMixin):
queryset =Activity.objects.all().order_by('date')
serializer_class = ActivitySerializer
class ActivitySerializer(serializers.ModelSerializer):
class Meta:
model = Activity
fields = ['id', 'title','text','date','count','score','total_count']
extra_kwargs={
'date':{'format':"%Y-%m-%d"}
}
class UserInfo(models.Model):
name = models.CharField(verbose_name="姓名", max_length=32)
avatar = models.FileField(verbose_name="头像", max_length=128, upload_to='avatar')
create_date = models.DateField(verbose_name="日期", auto_now_add=True)
score = models.IntegerField(verbose_name="积分", default=0)
class Meta:
verbose_name_plural = '用户表'
def __str__(self):
return self.name
class Activity(models.Model):
title = models.CharField(verbose_name="活动标题", max_length=128)
text = models.TextField(verbose_name="活动描述", null=True, blank=True)
date = models.DateField(verbose_name="举办活动日期")
count = models.IntegerField(verbose_name='报名人数', default=0)
total_count = models.IntegerField(verbose_name='总人数', default=0)
score = models.IntegerField(verbose_name="积分", default=0)
join_record = models.ManyToManyField(verbose_name="参与者",
through="JoinRecord",
through_fields=("activity", "user"),
to="UserInfo")
class Meta:
verbose_name_plural = '活动表'
def __str__(self):
return self.title
class JoinRecord(models.Model):
user = models.ForeignKey(verbose_name='用户', to="UserInfo", on_delete=models.CASCADE)
activity = models.ForeignKey(verbose_name="活动", to="Activity", on_delete=models.CASCADE, related_name='ac')
exchange = models.BooleanField(verbose_name="是否已兑换", default=False)
class Meta:
verbose_name_plural = '活动报名记录'