基于Spring Boot 的小區(qū)人臉識別與出入記錄管理系統(tǒng)功能
在智慧社區(qū)建設中,人臉識別技術的應用極大提升了小區(qū)管理效率和安全性。本文將介紹如何使用 Spring Boot 框架結合百度 AI 人臉識別 API,實現(xiàn)小區(qū)人員出入自動識別與記錄管理功能。
系統(tǒng)功能概述
本系統(tǒng)主要包含兩大核心功能:
- 人臉識別出入管理:通過攝像頭采集人臉圖像,自動識別人員身份并記錄出入時間
- 出入記錄查詢:支持按時間范圍、人員姓名等條件查詢出入記錄,方便管理人員統(tǒng)計分析
技術棧選擇
- 后端框架:Spring Boot 2.7.4
- 持久層框架:MyBatis-Plus 3.5.1
- 數(shù)據(jù)庫:MySQL
- 人臉識別:百度 AI 開放平臺
- 工具類:Hutool、Lombok
- 前端交互:RESTful API
核心依賴配置
首先在pom.xml中添加核心依賴:
<!-- 百度AI SDK -->
<dependency>
<groupId>com.baidu.aip</groupId>
<artifactId>java-sdk</artifactId>
<version>4.16.19</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Spring Boot核心依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 數(shù)據(jù)訪問 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 工具類 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.2.4</version>
</dependency>
<!-- 文件處理 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>數(shù)據(jù)模型設計
出入記錄實體類
@Data
@TableName("in_out_record")
public class InOutRecordEntity implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "in_out_record_id", type = IdType.AUTO)
private Integer inOutRecordId;
@TableField("person_id")
private Integer personId;
@TableField("community_id")
private Integer communityId;
@TableField("in_time")
private LocalDateTime inTime;
@TableField("out_time")
private LocalDateTime outTime;
@TableField("in_pic")
private String inPic;
@TableField("out_pic")
private String outPic;
}出入記錄查詢表單
@Data
public class InOutForm {
private Long page;
private Long limit;
private String userName;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime startDate;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime endDate;
}出入記錄 VO 類(用于前端展示)
@Data
public class InOutRecordVO {
@TableId(value = "in_out_record_id", type = IdType.AUTO)
private Integer inOutRecordId;
private Integer personId;
private Integer communityId;
private LocalDateTime inTime;
private LocalDateTime outTime;
private String inPic;
private String outPic;
// 擴展字段,用于展示
private String userName;
private String communityName;
private String termName;
private String houseNo;
}百度 AI 工具類實現(xiàn)
封裝百度 AI 人臉識別相關操作:
@Component
@Slf4j
public class BaiduAiUtils {
@Value("${baidu.face.appId}")
private String APP_ID;
@Value("${baidu.face.apiKey}")
private String API_KEY;
@Value("${baidu.face.secretKey}")
private String SECRET_KEY;
@Value("${baidu.face.imageType}")
private String IMAGE_TYPE;
@Value("${baidu.face.groupId}")
private String groupId;
private AipFace client;
private HashMap<String, Object> options = new HashMap<>();
public BaiduAiUtils() {
// 設置圖像質量控制
options.put("quality_control", "NORMAL");
// 設置活體檢測控制級別
options.put("liveness_control", "LOW");
}
@PostConstruct
public void init() {
// 初始化百度AI客戶端
client = new AipFace(APP_ID, API_KEY, SECRET_KEY);
}
/**
* 人臉檢測(檢查是否有且僅有一張人臉)
*/
public Boolean faceCheck(String image) {
JSONObject res = client.detect(image, IMAGE_TYPE, options);
log.info("detect result :{}", res);
if (res.has("error_code") && res.getInt("error_code") == 0) {
JSONObject resultObject = res.getJSONObject("result");
Integer faceNum = resultObject.getInt("face_num");
return faceNum == 1;
}
return false;
}
/**
* 人臉搜索(匹配用戶)
*/
public String faceSearch(String image) {
JSONObject res = client.search(image, IMAGE_TYPE, groupId, options);
log.info("search result :{}", res);
if (res.has("error_code") && res.getInt("error_code") == 0) {
JSONObject result = res.getJSONObject("result");
JSONArray userList = result.getJSONArray("user_list");
if (userList.length() > 0) {
JSONObject user = userList.getJSONObject(0);
double score = user.getDouble("score");
// 置信度大于80分認為匹配成功
if (score > 80) {
return user.getString("user_id");
}
}
}
return null;
}
}業(yè)務邏輯實現(xiàn)
出入記錄服務實現(xiàn)類
@Service
public class InOutRecordServiceImpl extends ServiceImpl<InOutRecordMapper, InOutRecordEntity> implements InOutRecordService {
@Autowired
private InOutRecordMapper inOutRecordMapper;
@Autowired
private PersonMapper personMapper;
@Override
public InOutPageListVO getInOutList(InOutForm form) {
Page<InOutRecordEntity> page = new Page<>(form.getPage(), form.getLimit());
QueryWrapper<InOutRecordEntity> queryWrapper = new QueryWrapper<>();
// 時間范圍查詢
if (form.getStartDate() != null && form.getEndDate() != null) {
queryWrapper.between("in_time", form.getStartDate(), form.getEndDate());
}
// 如果需要按用戶名查詢,可以在這里添加關聯(lián)查詢條件
Page<InOutRecordEntity> pages = inOutRecordMapper.selectPage(page, queryWrapper);
List<InOutRecordVO> inOutRecordVOList = new ArrayList<>();
// 轉換為VO對象并補充關聯(lián)信息
for(InOutRecordEntity entity : pages.getRecords()){
InOutRecordVO vo = new InOutRecordVO();
BeanUtils.copyProperties(entity, vo);
PersonEntity person = personMapper.selectById(entity.getPersonId());
if (person != null) {
vo.setUserName(person.getUserName());
vo.setHouseNo(person.getHouseNo());
}
// 獲取小區(qū)名稱
vo.setCommunityName(personMapper.selectCommunityNameByID(entity.getCommunityId()));
inOutRecordVOList.add(vo);
}
// 封裝分頁結果
InOutPageListVO result = new InOutPageListVO();
result.setRecords(inOutRecordVOList);
result.setTotalCount(pages.getTotal());
result.setPageSize(pages.getSize());
result.setTotalPage(pages.getPages());
result.setCurrPage(pages.getCurrent());
return result;
}
@Override
public InOutRecordEntity findLatestRecord(Integer personId) {
QueryWrapper<InOutRecordEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("person_id", personId)
.orderByDesc("in_time")
.last("LIMIT 1");
return this.getOne(queryWrapper);
}
}控制器實現(xiàn)
人臉識別與出入記錄控制器
@RestController
@RequestMapping("/sys/inOut")
public class InOutRecordController {
@Autowired
private BaiduAiUtils baiduAiUtils;
@Autowired
private PersonService personService;
@Autowired
private InOutRecordService recordService;
@Value("${file.upload-dir}")
private String uploadDir;
/**
* 人臉識別接口
*/
@PostMapping("/add")
public Result add(@RequestBody FaceForm faceForm) {
// 提取Base64圖像數(shù)據(jù)
String fileBase64 = faceForm.getFileBase64();
if (fileBase64.contains(",")) {
fileBase64 = fileBase64.split(",")[1];
}
// 1. 檢測人臉
boolean hasValidFace = baiduAiUtils.faceCheck(fileBase64);
if (!hasValidFace) {
return Result.error("人臉檢測失敗");
}
// 2. 人臉搜索匹配用戶
String userId = baiduAiUtils.faceSearch(fileBase64);
if (userId == null) {
return Result.ok().put("data", "人員信息不存在").put("status", "fail");
}
// 3. 查詢用戶信息
int personId;
try {
personId = Integer.parseInt(userId);
} catch (NumberFormatException e) {
return Result.error("用戶ID格式錯誤");
}
PersonEntity person = personService.getById(personId);
if (person == null) {
return Result.ok().put("data", "人員信息不存在").put("status", "fail");
}
try {
// 4. 保存圖片到本地
String fileName = System.currentTimeMillis() + ".png";
String filePath = Paths.get(uploadDir, fileName).toString();
// 確保目錄存在
File dir = new File(uploadDir);
if (!dir.exists()) {
dir.mkdirs();
}
// 解碼并保存圖片
byte[] imageBytes = Base64.getDecoder().decode(fileBase64);
Files.write(Paths.get(filePath), imageBytes);
// 構建圖片URL
String fullUrl = "http://localhost:8080/photos/" + fileName;
// 5. 查找最近記錄判斷是入場還是出場
InOutRecordEntity latestRecord = recordService.findLatestRecord(personId);
if (latestRecord == null || latestRecord.getOutTime() != null) {
// 入場記錄
InOutRecordEntity newRecord = new InOutRecordEntity();
newRecord.setCommunityId(faceForm.getCommunityId());
newRecord.setPersonId(personId);
newRecord.setInTime(LocalDateTime.now());
newRecord.setInPic(fullUrl);
recordService.save(newRecord);
return Result.ok().put("data", person.getUserName() + "進入小區(qū)").put("status", "success");
} else {
// 出場記錄
latestRecord.setOutTime(LocalDateTime.now());
latestRecord.setOutPic(fullUrl);
recordService.updateById(latestRecord);
return Result.ok().put("data", person.getUserName() + "離開小區(qū)").put("status", "success");
}
} catch (Exception e) {
e.printStackTrace();
return Result.error("操作失敗: " + e.getMessage());
}
}
/**
* 出入記錄查詢接口
*/
@GetMapping("/list")
public Result list(InOutForm form) {
// 獲取分頁數(shù)據(jù)
InOutPageListVO pageListVO = inOutRecordService.getInOutList(form);
// 構建返回結構
Map<String, Object> pageListMap = new HashMap<>();
pageListMap.put("totalCount", pageListVO.getTotalCount());
pageListMap.put("pageSize", pageListVO.getPageSize());
pageListMap.put("totalPage", pageListVO.getTotalPage());
pageListMap.put("currPage", pageListVO.getCurrPage());
pageListMap.put("list", pageListVO.getRecords());
Map<String, Object> dataMap = new HashMap<>();
dataMap.put("pageList", pageListMap);
return Result.ok().put("data", dataMap);
}
}接口使用說明
人臉識別接口
請求地址:POST /sys/inOut/add
請求參數(shù):
{
"communityId": 2,
"extName": "png",
"fileBase64": "iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAAAXNSR0IArs4c6QAAIABJREFUeF7sXe2OHblu3BljTX1lQUgyxEvv9HMQ5yIZq239/8B8gtdpbgl/6cAAAAASUVORK5CYII="
}成功響應(人員進入):
{
"msg": "操作成功",
"code": 200,
"data": "張三進入小區(qū)",
"status": "success"
}失敗響應(人員不存在):
{
"msg": "操作成功",
"code": 200,
"data": "人員信息不存在",
"status": "fail"
}出入記錄查詢接口
請求地址:GET /sys/inOut/list
請求參數(shù):
{
"page": 1,
"limit": 10,
"userName": "張三",
"startDate": "2023-07-20 12:59:54",
"endDate": "2023-07-20 23:00:00"
}響應結果:
{
"msg": "操作成功",
"code": 200,
"data": {
"pageList": {
"totalCount": 1,
"pageSize": 10,
"totalPage": 1,
"currPage": 1,
"list": [
{
"inOutRecordId": 44,
"inTime": "2023-07-19 16:51:55",
"outTime": "2023-07-19 16:52:07",
"inPic": "http://localhost:8181/villegePic/face/47b49187-a5e9-486a-b8ac-4409710b3323.png",
"outPic": "http://localhost:8181/villegePic/face/4cbfb2b9-a691-4d0a-a4d4-4bf602cb33ac.png",
"communityName": "棲海澐頌",
"termName": "8棟",
"houseNo": "802",
"userName": "麗麗"
}
]
}
}
}系統(tǒng)優(yōu)化建議
- 性能優(yōu)化:
- 對人臉圖片進行壓縮處理,減少傳輸和存儲開銷
- 對查詢接口添加緩存,提高高頻查詢效率
- 安全性增強:
- 提高活體檢測級別,防止照片、視頻等欺騙手段
- 對敏感接口添加權限控制
- 對 Base64 圖片傳輸進行加密
- 功能擴展:
- 添加異常出入提醒功能
- 實現(xiàn)批量導出記錄報表功能
- 增加訪客臨時授權功能
通過以上實現(xiàn),我們構建了一個完整的小區(qū)人臉識別出入管理系統(tǒng),該系統(tǒng)能夠自動識別人員身份并記錄出入信息,同時提供靈活的查詢功能,為小區(qū)管理提供了便捷高效的解決方案。
到此這篇關于基于 Spring Boot 的小區(qū)人臉識別與出入記錄管理系統(tǒng)功能的文章就介紹到這了,更多相關Spring Boot 人臉識別內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
java讀寫excel文件實現(xiàn)POI解析Excel的方法
在日常工作中,我們常常會進行Excel文件讀寫操作,這篇文章主要介紹了java讀寫excel文件實現(xiàn)POI解析Excel的方法,實例分析了java讀寫excel的技巧,非常具有實用價值,需要的朋友可以參考下2018-10-10
解決bootstrap.yml不生效,無法優(yōu)先于application.yml文件加載問題
文章主要討論了在Spring Boot項目中,`bootstrap.yml`文件無法優(yōu)先于`application.yml`文件加載的問題,原因是缺少了`nacos-config`依賴,且需要確保Spring Boot版本與`nacos-config`版本匹配,作者希望通過分享個人經驗,幫助他人解決類似問題2024-12-12
Spring Boot使用AOP實現(xiàn)REST接口簡易靈活的安全認證的方法
這篇文章主要介紹了Spring Boot使用AOP實現(xiàn)REST接口簡易靈活的安全認證的方法,非常具有實用價值,需要的朋友可以參考下2018-11-11
Spring boot創(chuàng)建自定義starter的完整步驟
這篇文章主要給大家介紹了關于Spring boot創(chuàng)建自定義starter的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Spring boot具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧2019-09-09
SpringBoot Application的exclude不生效問題及排查
這篇文章主要介紹了SpringBoot Application的exclude不生效問題及排查,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11

