Commit ac310f68 by xmh

动态匹配

1 parent e0a8d848
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
<dependency> <dependency>
<groupId>com.viontech.keliu</groupId> <groupId>com.viontech.keliu</groupId>
<artifactId>keliu-base</artifactId> <artifactId>keliu-base</artifactId>
<version>6.0.9</version> <version>6.0.9-SNAPSHOT</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<artifactId>pagehelper-spring-boot-starter</artifactId> <artifactId>pagehelper-spring-boot-starter</artifactId>
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
<dependency> <dependency>
<groupId>com.viontech.keliu</groupId> <groupId>com.viontech.keliu</groupId>
<artifactId>AlgApiClient</artifactId> <artifactId>AlgApiClient</artifactId>
<version>6.0.5</version> <version>6.0.6-SNAPSHOT</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>javax</groupId> <groupId>javax</groupId>
......
package com.viontech.match.config; package com.viontech.match.config;
import java.text.SimpleDateFormat;
/** /**
* 常量 * 常量
* *
...@@ -25,4 +27,6 @@ public class Constant { ...@@ -25,4 +27,6 @@ public class Constant {
public final static int FACE_MATCH_RESULT_SIZE = 5; public final static int FACE_MATCH_RESULT_SIZE = 5;
/** 人体匹配结果的数量 */ /** 人体匹配结果的数量 */
public final static int BODY_MATCH_RESULT_SIZE = 10; public final static int BODY_MATCH_RESULT_SIZE = 10;
public final static ThreadLocal<SimpleDateFormat> DATE_FORMAT = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
} }
...@@ -19,7 +19,9 @@ import org.elasticsearch.action.search.SearchResponse; ...@@ -19,7 +19,9 @@ import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.index.query.TermQueryBuilder; import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScriptScoreQueryBuilder; import org.elasticsearch.index.query.functionscore.ScriptScoreQueryBuilder;
import org.elasticsearch.index.reindex.BulkByScrollResponse; import org.elasticsearch.index.reindex.BulkByScrollResponse;
...@@ -48,6 +50,7 @@ import java.util.stream.Stream; ...@@ -48,6 +50,7 @@ import java.util.stream.Stream;
@Service @Service
@Slf4j @Slf4j
public class PersonService { public class PersonService {
private static final String[] FETCH_SOURCE = new String[]{"personId", "age", "gender", "fid", "counttime", "channelSerialNum", "body_type"};
@Resource @Resource
private RestHighLevelClient client; private RestHighLevelClient client;
@Resource @Resource
...@@ -103,7 +106,7 @@ public class PersonService { ...@@ -103,7 +106,7 @@ public class PersonService {
} }
success.setMatch(1); success.setMatch(1);
success.setPersonPoolStatus(poolStatus); success.setPersonPoolStatus(poolStatus);
log.info("人员匹配操作完成,PoolIds:[{}},结果:[{}]", poolIds.toString(), objectMapper.writeValueAsString(success)); log.info("人员匹配操作完成,PoolIds:[{}}", poolIds.toString());
return success; return success;
} catch (ElasticsearchStatusException e) { } catch (ElasticsearchStatusException e) {
if (e.status() == RestStatus.BAD_REQUEST && e.getDetailedMessage().contains(Constant.CLASS_CAST_EXCEPTION)) { if (e.status() == RestStatus.BAD_REQUEST && e.getDetailedMessage().contains(Constant.CLASS_CAST_EXCEPTION)) {
...@@ -124,7 +127,7 @@ public class PersonService { ...@@ -124,7 +127,7 @@ public class PersonService {
} }
/** /**
* 修改人员特征 * 修改人员特征(先删除再添加)
* *
* @param requestVo rid,person, poolId, * @param requestVo rid,person, poolId,
* *
...@@ -136,7 +139,6 @@ public class PersonService { ...@@ -136,7 +139,6 @@ public class PersonService {
String personId = person.getPersonId(); String personId = person.getPersonId();
String poolId = requestVo.getPoolId(); String poolId = requestVo.getPoolId();
log.info("人员修改操作开始,poolId:[{}],personId:[{}]", poolId, personId); log.info("人员修改操作开始,poolId:[{}],personId:[{}]", poolId, personId);
// 先删除
try { try {
BulkByScrollResponse bulkByScrollResponse = deletePerson(poolId, personId); BulkByScrollResponse bulkByScrollResponse = deletePerson(poolId, personId);
BulkResponse bulkItemResponses = addPerson(poolId, Collections.singletonList(person)); BulkResponse bulkItemResponses = addPerson(poolId, Collections.singletonList(person));
...@@ -166,6 +168,9 @@ public class PersonService { ...@@ -166,6 +168,9 @@ public class PersonService {
Integer age = person.getAge(); Integer age = person.getAge();
String gender = person.getGender(); String gender = person.getGender();
String personId = person.getPersonId(); String personId = person.getPersonId();
Date counttime = person.getCounttime();
Integer bodyType = person.getBodyType();
String channelSerialNum = person.getChannelSerialNum();
List<FaceFeature> faceFeatures = person.getFaceFeatures(); List<FaceFeature> faceFeatures = person.getFaceFeatures();
if (faceFeatures != null && faceFeatures.size() > 0) { if (faceFeatures != null && faceFeatures.size() > 0) {
for (FaceFeature faceFeature : faceFeatures) { for (FaceFeature faceFeature : faceFeatures) {
...@@ -174,7 +179,9 @@ public class PersonService { ...@@ -174,7 +179,9 @@ public class PersonService {
String fid = faceFeature.getFid(); String fid = faceFeature.getFid();
IndexRequest indexRequest = new IndexRequest(poolId) IndexRequest indexRequest = new IndexRequest(poolId)
.source(XContentType.JSON, "personId", personId, "data", feature, "fid", fid, "age", age, "gender", gender); .source(XContentType.JSON, "personId", personId,
"data", feature, "fid", fid, "age", age, "gender", gender, "body_type", bodyType,
"counttime", Constant.DATE_FORMAT.get().format(counttime), "channelSerialNum", channelSerialNum);
bulkRequest.add(indexRequest); bulkRequest.add(indexRequest);
} }
} }
...@@ -196,7 +203,9 @@ public class PersonService { ...@@ -196,7 +203,9 @@ public class PersonService {
String fid = bodyFeature.getBid(); String fid = bodyFeature.getBid();
IndexRequest indexRequest = new IndexRequest(poolId) IndexRequest indexRequest = new IndexRequest(poolId)
.source(XContentType.JSON, "personId", personId, "body", feature, "fid", fid, "age", age, "gender", gender); .source(XContentType.JSON, "personId", personId,
"body", feature, "fid", fid, "age", age, "gender", gender, "body_type", bodyType,
"counttime", Constant.DATE_FORMAT.get().format(counttime), "channelSerialNum", channelSerialNum);
bulkRequest.add(indexRequest); bulkRequest.add(indexRequest);
} }
} }
...@@ -211,8 +220,10 @@ public class PersonService { ...@@ -211,8 +220,10 @@ public class PersonService {
/** /**
* 删除人员 * 删除人员
* @param poolId 特征池Id *
* @param poolId 特征池Id
* @param personId 人员Id,对应数据库personUnid * @param personId 人员Id,对应数据库personUnid
*
* @return 删除结果 * @return 删除结果
* @throws IOException elasticsearch 所产生的异常 * @throws IOException elasticsearch 所产生的异常
*/ */
...@@ -223,17 +234,24 @@ public class PersonService { ...@@ -223,17 +234,24 @@ public class PersonService {
return client.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT); return client.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT);
} }
/**
* 人员匹配入口
*
* @param poolId 在哪个特征池中匹配
* @param person 用来匹配的人员信息
* @param type 匹配类型 0人脸 1人体
*
* @return 匹配结果
* @throws Exception --
*/
public List<Person> matchPerson(String poolId, Person person, int type) throws Exception { public List<Person> matchPerson(String poolId, Person person, int type) throws Exception {
List<Person> matchResult = new ArrayList<>(); List<Person> matchResult = new ArrayList<>();
int matchResultSize = 0; int matchResultSize;
if (type == 0) { if (type == 0) {
List<FaceFeature> faceFeatures = person.getFaceFeatures(); matchFace(poolId, matchResult, person);
matchFace(faceFeatures, poolId, matchResult);
matchResultSize = Constant.FACE_MATCH_RESULT_SIZE; matchResultSize = Constant.FACE_MATCH_RESULT_SIZE;
} else { } else {
List<BodyFeature> bodyFeatures = person.getBodyFeatures(); matchBody(poolId, matchResult, person);
matchBody(bodyFeatures, poolId, matchResult);
matchResultSize = Constant.BODY_MATCH_RESULT_SIZE; matchResultSize = Constant.BODY_MATCH_RESULT_SIZE;
} }
...@@ -245,10 +263,9 @@ public class PersonService { ...@@ -245,10 +263,9 @@ public class PersonService {
return matchResult; return matchResult;
} }
private void matchFace(List<FaceFeature> faceFeatures, String poolId, List<Person> matchResult) throws IOException { private void matchFace(String poolId, List<Person> matchResult, Person person) throws Exception {
List<FaceFeature> faceFeatures = person.getFaceFeatures();
if (faceFeatures != null && faceFeatures.size() > 0) { if (faceFeatures != null && faceFeatures.size() > 0) {
for (FaceFeature faceFeature : faceFeatures) { for (FaceFeature faceFeature : faceFeatures) {
Double[] feature = faceFeature.getFeature(); Double[] feature = faceFeature.getFeature();
if (feature == null || feature.length != Constant.FACE_FEATURE_DIMS) { if (feature == null || feature.length != Constant.FACE_FEATURE_DIMS) {
...@@ -256,19 +273,16 @@ public class PersonService { ...@@ -256,19 +273,16 @@ public class PersonService {
continue; continue;
} }
Script script = new Script( SearchRequest searchRequest = getSearchRequest(poolId, Constant.FACE_MATCH_RESULT_SIZE, feature, person, 0);
ScriptType.INLINE, Script.DEFAULT_SCRIPT_LANG, matchResult.addAll(match0(searchRequest));
"(cosineSimilarity(params.face, 'data') + 1) / 2 * 100", Collections.singletonMap("face", feature));
ScriptScoreQueryBuilder queryBuilder = QueryBuilders.scriptScoreQuery(QueryBuilders.existsQuery("data"), script);
matchResult.addAll(match0(poolId, queryBuilder));
} }
} else { } else {
log.info("no face feature"); log.info("no face feature");
} }
} }
private void matchBody(List<BodyFeature> bodyFeatures, String poolId, List<Person> matchResult) throws IOException { private void matchBody(String poolId, List<Person> matchResult, Person person) throws Exception {
List<BodyFeature> bodyFeatures = person.getBodyFeatures();
if (bodyFeatures != null && bodyFeatures.size() > 0) { if (bodyFeatures != null && bodyFeatures.size() > 0) {
for (BodyFeature faceFeature : bodyFeatures) { for (BodyFeature faceFeature : bodyFeatures) {
Double[] feature = faceFeature.getFeature(); Double[] feature = faceFeature.getFeature();
...@@ -283,29 +297,60 @@ public class PersonService { ...@@ -283,29 +297,60 @@ public class PersonService {
feature = Arrays.copyOfRange(feature, 3, Constant.BODY_FEATURE_DIMS_2048 + 3); feature = Arrays.copyOfRange(feature, 3, Constant.BODY_FEATURE_DIMS_2048 + 3);
} }
Script script = new Script( SearchRequest searchRequest = getSearchRequest(poolId, Constant.BODY_MATCH_RESULT_SIZE, feature, person, 1);
ScriptType.INLINE, Script.DEFAULT_SCRIPT_LANG,
"(cosineSimilarity(params.body, 'body') + 1) / 2 * 100", Collections.singletonMap("body", feature));
ScriptScoreQueryBuilder queryBuilder = QueryBuilders.scriptScoreQuery(QueryBuilders.existsQuery("body"), script);
matchResult.addAll(match0(poolId, queryBuilder, Constant.BODY_MATCH_RESULT_SIZE)); matchResult.addAll(match0(searchRequest));
} }
} }
} }
private List<Person> match0(String poolId, ScriptScoreQueryBuilder scriptScoreQueryBuilder) throws IOException { private SearchRequest getSearchRequest(String poolId, Integer matchResultSize, Double[] feature, Person person, int type) {
return match0(poolId, scriptScoreQueryBuilder, Constant.FACE_MATCH_RESULT_SIZE); Script script;
} BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
if (type == 0) {
script = new Script(
ScriptType.INLINE, Script.DEFAULT_SCRIPT_LANG,
"(cosineSimilarity(params.face, 'data') + 1) / 2 * 100", Collections.singletonMap("face", feature));
boolQuery.filter(QueryBuilders.existsQuery("data"));
} else {
script = new Script(
ScriptType.INLINE, Script.DEFAULT_SCRIPT_LANG,
"(cosineSimilarity(params.body, 'body') + 1) / 2 * 100", Collections.singletonMap("body", feature));
boolQuery.filter(QueryBuilders.existsQuery("body"));
}
private List<Person> match0(String poolId, ScriptScoreQueryBuilder scriptScoreQueryBuilder, Integer matchResultSize) throws IOException { // 根据通道号过滤
List<Person> persons = new ArrayList<>(); List<String> channelSerialNums = person.getChannelSerialNums();
if (channelSerialNums != null && channelSerialNums.size() > 0) {
boolQuery.filter(QueryBuilders.termsQuery("channelSerialNum", channelSerialNums));
}
// 根据时间过滤
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("counttime");
Date counttimeGTE = person.getCounttimeGTE();
Date counttimeLTE = person.getCounttimeLTE();
if (counttimeGTE != null) {
rangeQueryBuilder.gte(Constant.DATE_FORMAT.get().format(counttimeGTE));
}
if (counttimeLTE != null) {
rangeQueryBuilder.lte(Constant.DATE_FORMAT.get().format(counttimeLTE));
}
if (counttimeGTE != null || counttimeLTE != null) {
boolQuery.filter(rangeQueryBuilder);
}
ScriptScoreQueryBuilder queryBuilder = QueryBuilders.scriptScoreQuery(boolQuery, script);
SearchSourceBuilder builder = new SearchSourceBuilder() SearchSourceBuilder builder = new SearchSourceBuilder()
.size(matchResultSize) .size(matchResultSize)
.query(scriptScoreQueryBuilder) .query(queryBuilder)
.fetchSource(new String[]{"personId", "age", "gender", "fid"}, null); .fetchSource(FETCH_SOURCE, null);
SearchRequest searchRequest = new SearchRequest(poolId)
.source(builder); return new SearchRequest(poolId).source(builder);
}
private List<Person> match0(SearchRequest searchRequest) throws Exception {
List<Person> persons = new ArrayList<>();
SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT); SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
SearchHits hits = search.getHits(); SearchHits hits = search.getHits();
...@@ -316,6 +361,9 @@ public class PersonService { ...@@ -316,6 +361,9 @@ public class PersonService {
p.setPersonId((String) source.get("personId")); p.setPersonId((String) source.get("personId"));
p.setAge((Integer) source.get("age")); p.setAge((Integer) source.get("age"));
p.setGender((String) source.get("gender")); p.setGender((String) source.get("gender"));
p.setChannelSerialNum((String) source.get("channelSerialNum"));
p.setBodyType((Integer) source.get("body_type"));
p.setCounttime(Constant.DATE_FORMAT.get().parse((String) source.get("counttime")));
p.setScore((int) item.getScore()); p.setScore((int) item.getScore());
p.setPersonPoolId(item.getIndex()); p.setPersonPoolId(item.getIndex());
persons.add(p); persons.add(p);
......
...@@ -22,6 +22,7 @@ import org.elasticsearch.common.settings.Settings; ...@@ -22,6 +22,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.rest.RestStatus; import org.elasticsearch.rest.RestStatus;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
...@@ -47,6 +48,10 @@ public class PoolService { ...@@ -47,6 +48,10 @@ public class PoolService {
private RestHighLevelClient client; private RestHighLevelClient client;
@Resource @Resource
private ObjectMapper objectMapper; private ObjectMapper objectMapper;
@Value("${vion.index.number_of_shards:1}")
private Integer shards;
@Value("${vion.index.number_of_replicas:0}")
private Integer replicas;
/** /**
* 添加特征池 * 添加特征池
...@@ -65,8 +70,8 @@ public class PoolService { ...@@ -65,8 +70,8 @@ public class PoolService {
XContentBuilder builder = getCreateIndexContentBuilder(); XContentBuilder builder = getCreateIndexContentBuilder();
createIndexRequest.mapping(builder); createIndexRequest.mapping(builder);
Settings.Builder setting = Settings.builder(); Settings.Builder setting = Settings.builder();
setting.put("index.number_of_shards", 1); setting.put("index.number_of_shards", shards);
setting.put("index.number_of_replicas", 0); setting.put("index.number_of_replicas", replicas);
createIndexRequest.settings(setting); createIndexRequest.settings(setting);
CreateIndexResponse createIndexResponse = client.indices().create(createIndexRequest, RequestOptions.DEFAULT); CreateIndexResponse createIndexResponse = client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
...@@ -147,6 +152,7 @@ public class PoolService { ...@@ -147,6 +152,7 @@ public class PoolService {
BulkResponse bulkItemResponses = personService.addPerson(poolId, personPool); BulkResponse bulkItemResponses = personService.addPerson(poolId, personPool);
if (bulkItemResponses != null && bulkItemResponses.hasFailures()) { if (bulkItemResponses != null && bulkItemResponses.hasFailures()) {
log.info(bulkItemResponses.buildFailureMessage()); log.info(bulkItemResponses.buildFailureMessage());
return ResponseVo.error(rid, bulkItemResponses.buildFailureMessage());
} }
log.info("特征池修改操作完成:[{}],updateType:[{}]", poolId, updateType); log.info("特征池修改操作完成:[{}],updateType:[{}]", poolId, updateType);
...@@ -159,7 +165,9 @@ public class PoolService { ...@@ -159,7 +165,9 @@ public class PoolService {
/** /**
* 查询特征池信息 * 查询特征池信息
*
* @param requestVo rid,listAll,poolId * @param requestVo rid,listAll,poolId
*
* @return ResponseVo * @return ResponseVo
* @throws Exception -- * @throws Exception --
*/ */
...@@ -210,6 +218,7 @@ public class PoolService { ...@@ -210,6 +218,7 @@ public class PoolService {
builder.field("dims", Constant.FACE_FEATURE_DIMS); builder.field("dims", Constant.FACE_FEATURE_DIMS);
} }
builder.endObject(); builder.endObject();
// 人体特征 // 人体特征
builder.startObject("body"); builder.startObject("body");
{ {
...@@ -217,12 +226,14 @@ public class PoolService { ...@@ -217,12 +226,14 @@ public class PoolService {
builder.field("dims", Constant.BODY_FEATURE_DIMS_2048); builder.field("dims", Constant.BODY_FEATURE_DIMS_2048);
} }
builder.endObject(); builder.endObject();
// 人员id // 人员id
builder.startObject("personId"); builder.startObject("personId");
{ {
builder.field("type", "keyword"); builder.field("type", "keyword");
} }
builder.endObject(); builder.endObject();
// 特征id // 特征id
builder.startObject("fid"); builder.startObject("fid");
{ {
...@@ -230,6 +241,7 @@ public class PoolService { ...@@ -230,6 +241,7 @@ public class PoolService {
} }
builder.endObject(); builder.endObject();
// 年龄
builder.startObject("age"); builder.startObject("age");
{ {
builder.field("type", "integer"); builder.field("type", "integer");
...@@ -238,6 +250,7 @@ public class PoolService { ...@@ -238,6 +250,7 @@ public class PoolService {
} }
builder.endObject(); builder.endObject();
// 性别
builder.startObject("gender"); builder.startObject("gender");
{ {
builder.field("type", "keyword"); builder.field("type", "keyword");
...@@ -245,6 +258,32 @@ public class PoolService { ...@@ -245,6 +258,32 @@ public class PoolService {
builder.field("index", false); builder.field("index", false);
} }
builder.endObject(); builder.endObject();
// 时间
builder.startObject("counttime");
{
builder.field("type", "date");
builder.field("format", "yyyy-MM-dd HH:mm:ss");
}
builder.endObject();
// 通道序列号
builder.startObject("channelSerialNum");
{
builder.field("type", "keyword");
}
builder.endObject();
// 身体特征类型类型
builder.startObject("body_type");
{
builder.field("type", "integer");
builder.field("doc_values", false);
builder.field("index", false);
}
builder.endObject();
} }
builder.endObject(); builder.endObject();
} }
......
...@@ -3,4 +3,8 @@ server.port=12000 ...@@ -3,4 +3,8 @@ server.port=12000
spring.jackson.time-zone=GMT+8 spring.jackson.time-zone=GMT+8
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
# es # es
spring.elasticsearch.rest.uris=http://127.0.0.1:9200
\ No newline at end of file \ No newline at end of file
spring.elasticsearch.rest.uris=http://127.0.0.1:9200
#\u5206\u7247\u6570\u91CF
vion.index.number_of_shards=1
#\u526F\u672C\u6570\u91CF
vion.index.number_of_replicas=0
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!