334 lines
14 KiB
Java
334 lines
14 KiB
Java
package com.ycwl.basic.facebody.adapter;
|
|
|
|
import com.aliyuncs.DefaultAcsClient;
|
|
import com.aliyuncs.IAcsClient;
|
|
import com.aliyuncs.exceptions.ClientException;
|
|
import com.aliyuncs.facebody.model.v20191230.AddFaceEntityRequest;
|
|
import com.aliyuncs.facebody.model.v20191230.AddFaceRequest;
|
|
import com.aliyuncs.facebody.model.v20191230.AddFaceResponse;
|
|
import com.aliyuncs.facebody.model.v20191230.CreateFaceDbRequest;
|
|
import com.aliyuncs.facebody.model.v20191230.DeleteFaceDbRequest;
|
|
import com.aliyuncs.facebody.model.v20191230.DeleteFaceEntityRequest;
|
|
import com.aliyuncs.facebody.model.v20191230.ListFaceDbsRequest;
|
|
import com.aliyuncs.facebody.model.v20191230.ListFaceDbsResponse;
|
|
import com.aliyuncs.facebody.model.v20191230.ListFaceEntitiesRequest;
|
|
import com.aliyuncs.facebody.model.v20191230.ListFaceEntitiesResponse;
|
|
import com.aliyuncs.facebody.model.v20191230.SearchFaceRequest;
|
|
import com.aliyuncs.facebody.model.v20191230.SearchFaceResponse;
|
|
import com.aliyuncs.profile.DefaultProfile;
|
|
import com.ycwl.basic.facebody.entity.AddFaceResp;
|
|
import com.ycwl.basic.facebody.entity.AliFaceBodyConfig;
|
|
import com.ycwl.basic.facebody.entity.SearchFaceResp;
|
|
import com.ycwl.basic.facebody.entity.SearchFaceResultItem;
|
|
import com.ycwl.basic.utils.ratelimiter.FixedRateLimiter;
|
|
import com.ycwl.basic.utils.ratelimiter.IRateLimiter;
|
|
import lombok.Getter;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.stream.Collectors;
|
|
|
|
@Slf4j
|
|
public class AliFaceBodyAdapter implements IFaceBodyAdapter {
|
|
private static final Map<String, IRateLimiter> addEntityLimiters = new ConcurrentHashMap<>();
|
|
private static final Map<String, IRateLimiter> addFaceLimiters = new ConcurrentHashMap<>();
|
|
private static final Map<String, IRateLimiter> addDbLimiters = new ConcurrentHashMap<>();
|
|
private static final Map<String, IRateLimiter> listDbLimiters = new ConcurrentHashMap<>();
|
|
private static final Map<String, IRateLimiter> listFaceLimiters = new ConcurrentHashMap<>();
|
|
private static final Map<String, IRateLimiter> searchFaceLimiters = new ConcurrentHashMap<>();
|
|
private static final Map<String, IRateLimiter> deleteDbLimiters = new ConcurrentHashMap<>();
|
|
private static final Map<String, IRateLimiter> deleteEntityLimiters = new ConcurrentHashMap<>();
|
|
|
|
private AliFaceBodyConfig config;
|
|
|
|
public boolean setConfig(AliFaceBodyConfig config) {
|
|
this.config = config;
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean loadConfig(Map<String, String> _config) {
|
|
AliFaceBodyConfig config = new AliFaceBodyConfig();
|
|
config.setAccessKeyId(_config.get("accessKeyId"));
|
|
config.setAccessKeySecret(_config.get("accessKeySecret"));
|
|
config.setRegion(_config.get("region"));
|
|
this.config = config;
|
|
return true;
|
|
}
|
|
|
|
private IRateLimiter getLimiter(LOCK_TYPE type) {
|
|
switch (type) {
|
|
case ADD_DB:
|
|
return addDbLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(600, TimeUnit.MILLISECONDS));
|
|
case ADD_ENTITY:
|
|
return addEntityLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(600, TimeUnit.MILLISECONDS));
|
|
case ADD_FACE:
|
|
return addFaceLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(600, TimeUnit.MILLISECONDS));
|
|
case LIST_DB:
|
|
return listDbLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(500, TimeUnit.MILLISECONDS));
|
|
case LIST_FACE:
|
|
return listFaceLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(2));
|
|
case SEARCH_FACE:
|
|
return searchFaceLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(200, TimeUnit.MILLISECONDS));
|
|
case DELETE_DB:
|
|
return deleteDbLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(600, TimeUnit.MILLISECONDS));
|
|
case DELETE_ENTITY:
|
|
return deleteEntityLimiters.computeIfAbsent(config.getAccessKeyId(), k -> new FixedRateLimiter(600, TimeUnit.MILLISECONDS));
|
|
default:
|
|
return new FixedRateLimiter(600, TimeUnit.MILLISECONDS);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean addFaceDb(String dbName) {
|
|
IRateLimiter addDbLimiter = getLimiter(LOCK_TYPE.ADD_DB);
|
|
try (ClientWrapper clientWrapper = getClient()) {
|
|
IAcsClient client = clientWrapper.getClient();
|
|
CreateFaceDbRequest request = new CreateFaceDbRequest();
|
|
request.setName(dbName);
|
|
try {
|
|
addDbLimiter.acquire();
|
|
} catch (InterruptedException ignored) {
|
|
}
|
|
client.getAcsResponse(request);
|
|
return true;
|
|
} catch (ClientException e) {
|
|
log.error("阿里云添加人脸数据库失败!", e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean deleteFaceDb(String dbName) {
|
|
ListFaceEntitiesRequest request = new ListFaceEntitiesRequest();
|
|
IRateLimiter deleteEntityLimiter = getLimiter(LOCK_TYPE.DELETE_ENTITY);
|
|
IRateLimiter deleteDbLimiter = getLimiter(LOCK_TYPE.DELETE_DB);
|
|
request.setDbName(dbName);
|
|
request.setLimit(200);
|
|
try (ClientWrapper clientWrapper = getClient()) {
|
|
IAcsClient client = clientWrapper.getClient();
|
|
while (true) {
|
|
ListFaceEntitiesResponse response = client.getAcsResponse(request);
|
|
if (response.getData().getTotalCount() == 0) {
|
|
break;
|
|
}
|
|
response.getData().getEntities().forEach(entity -> {
|
|
DeleteFaceEntityRequest deleteFaceEntityRequest = new DeleteFaceEntityRequest();
|
|
deleteFaceEntityRequest.setDbName(entity.getDbName());
|
|
deleteFaceEntityRequest.setEntityId(entity.getEntityId());
|
|
try {
|
|
deleteEntityLimiter.acquire();
|
|
} catch (InterruptedException ignored) {
|
|
}
|
|
try {
|
|
client.getAcsResponse(deleteFaceEntityRequest);
|
|
} catch (ClientException e) {
|
|
log.error("删除人脸数据失败!", e);
|
|
}
|
|
});
|
|
}
|
|
DeleteFaceDbRequest deleteFaceDbRequest = new DeleteFaceDbRequest();
|
|
deleteFaceDbRequest.setName(dbName);
|
|
try {
|
|
deleteDbLimiter.acquire();
|
|
} catch (InterruptedException ignored) {
|
|
}
|
|
client.getAcsResponse(deleteFaceDbRequest);
|
|
} catch (ClientException e) {
|
|
log.error("删除人脸数据库失败!", e);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public List<String> listFaceDb() {
|
|
ListFaceDbsRequest request = new ListFaceDbsRequest();
|
|
try (ClientWrapper clientWrapper = getClient()) {
|
|
IAcsClient client = clientWrapper.getClient();
|
|
ListFaceDbsResponse response = client.getAcsResponse(request);
|
|
return response.getData().getDbList().stream().map(ListFaceDbsResponse.Data.DbListItem::getName).collect(Collectors.toList());
|
|
} catch (ClientException e) {
|
|
log.error("获取人脸数据库失败!", e);
|
|
return Collections.emptyList();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public AddFaceResp addFace(String dbName, String entityId, String faceUrl, String extData) {
|
|
IRateLimiter addEntityLimiter = getLimiter(LOCK_TYPE.ADD_ENTITY);
|
|
IRateLimiter addFaceLimiter = getLimiter(LOCK_TYPE.ADD_FACE);
|
|
AddFaceEntityRequest request = new AddFaceEntityRequest();
|
|
request.setDbName(dbName);
|
|
request.setEntityId(entityId);
|
|
try (ClientWrapper clientWrapper = getClient()) {
|
|
IAcsClient client = clientWrapper.getClient();
|
|
try {
|
|
addEntityLimiter.acquire();
|
|
} catch (InterruptedException ignored) {
|
|
}
|
|
try {
|
|
client.getAcsResponse(request);
|
|
} catch (ClientException e) {
|
|
log.error("addFaceEntity, {}/{}", dbName, entityId, e);
|
|
return null;
|
|
}
|
|
AddFaceRequest addFaceRequest = new AddFaceRequest();
|
|
addFaceRequest.setDbName(dbName);
|
|
addFaceRequest.setEntityId(entityId);
|
|
addFaceRequest.setImageUrl(faceUrl);
|
|
addFaceRequest.setExtraData(extData);
|
|
AddFaceResp respVo = new AddFaceResp();
|
|
try {
|
|
addFaceLimiter.acquire();
|
|
} catch (InterruptedException ignored) {
|
|
}
|
|
try {
|
|
AddFaceResponse acsResponse = client.getAcsResponse(addFaceRequest);
|
|
respVo.setScore(acsResponse.getData().getQualitieScore());
|
|
return respVo;
|
|
} catch (ClientException e) {
|
|
log.error("addFace, {}/{}", dbName, entityId, e);
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean deleteFace(String dbName, String entityId) {
|
|
IRateLimiter deleteEntityLimiter = getLimiter(LOCK_TYPE.DELETE_ENTITY);
|
|
DeleteFaceEntityRequest request = new DeleteFaceEntityRequest();
|
|
request.setDbName(dbName);
|
|
request.setEntityId(entityId);
|
|
try (ClientWrapper clientWrapper = getClient()) {
|
|
IAcsClient client = clientWrapper.getClient();
|
|
try {
|
|
deleteEntityLimiter.acquire();
|
|
} catch (InterruptedException ignored) {
|
|
}
|
|
try {
|
|
client.getAcsResponse(request);
|
|
return true;
|
|
} catch (ClientException e) {
|
|
log.error("删除人脸数据失败!", e);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public List<String> listFace(String dbName, String prefix, Integer offset, Integer size) {
|
|
IRateLimiter listFaceLimiter = getLimiter(LOCK_TYPE.LIST_FACE);
|
|
ListFaceEntitiesRequest listFaceEntitiesRequest = new ListFaceEntitiesRequest();
|
|
listFaceEntitiesRequest.setDbName(dbName);
|
|
listFaceEntitiesRequest.setOrder("asc");
|
|
if (offset != null) {
|
|
listFaceEntitiesRequest.setOffset(offset);
|
|
}
|
|
if (size != null) {
|
|
listFaceEntitiesRequest.setLimit(size);
|
|
} else {
|
|
listFaceEntitiesRequest.setLimit(200);
|
|
}
|
|
if (StringUtils.isNotEmpty(prefix)) {
|
|
listFaceEntitiesRequest.setEntityIdPrefix(prefix);
|
|
}
|
|
try (ClientWrapper clientWrapper = getClient()) {
|
|
IAcsClient client = clientWrapper.getClient();
|
|
try {
|
|
listFaceLimiter.acquire();
|
|
} catch (InterruptedException ignored) {
|
|
}
|
|
try {
|
|
ListFaceEntitiesResponse response = client.getAcsResponse(listFaceEntitiesRequest);
|
|
return response.getData().getEntities().stream().map(ListFaceEntitiesResponse.Data.Entity::getEntityId).collect(Collectors.toList());
|
|
} catch (ClientException e) {
|
|
log.error("获取人脸数据失败!", e);
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public SearchFaceResp searchFace(String dbName, String faceUrl) {
|
|
SearchFaceResp resp = new SearchFaceResp();
|
|
IRateLimiter searchFaceLimiter = getLimiter(LOCK_TYPE.SEARCH_FACE);
|
|
try (ClientWrapper clientWrapper = getClient()) {
|
|
IAcsClient client = clientWrapper.getClient();
|
|
SearchFaceRequest request = new SearchFaceRequest();
|
|
request.setDbName(dbName);
|
|
request.setImageUrl(faceUrl);
|
|
request.setLimit(100);
|
|
try {
|
|
searchFaceLimiter.acquire();
|
|
} catch (InterruptedException ignored) {
|
|
}
|
|
try {
|
|
SearchFaceResponse response = client.getAcsResponse(request);
|
|
List<SearchFaceResponse.Data.MatchListItem> matchList = response.getData().getMatchList();
|
|
if (matchList.isEmpty()) {
|
|
resp.setOriginalFaceScore(0f);
|
|
return resp;
|
|
}
|
|
SearchFaceResponse.Data.MatchListItem matchItem = matchList.get(0);
|
|
resp.setOriginalFaceScore(matchItem.getQualitieScore());
|
|
resp.setResult(matchItem.getFaceItems().stream().map(item -> {
|
|
SearchFaceResultItem resultItem = new SearchFaceResultItem();
|
|
resultItem.setDbName(dbName);
|
|
resultItem.setFaceId(item.getFaceId());
|
|
resultItem.setExtData(item.getExtraData());
|
|
resultItem.setScore(item.getScore());
|
|
return resultItem;
|
|
}).collect(Collectors.toList()));
|
|
if (!resp.getResult().isEmpty()) {
|
|
resp.setFirstMatchRate(resp.getResult().get(0).getScore());
|
|
}
|
|
return resp;
|
|
} catch (ClientException e) {
|
|
log.error("搜索人脸失败!", e);
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
public ClientWrapper getClient() {
|
|
DefaultProfile profile = DefaultProfile.getProfile(
|
|
config.getRegion(), config.getAccessKeyId(), config.getAccessKeySecret());
|
|
IAcsClient client = new DefaultAcsClient(profile);
|
|
return new ClientWrapper(client);
|
|
}
|
|
|
|
@Getter
|
|
public static class ClientWrapper implements AutoCloseable {
|
|
private final IAcsClient client;
|
|
|
|
public ClientWrapper(IAcsClient client) {
|
|
this.client = client;
|
|
}
|
|
|
|
@Override
|
|
public void close() {
|
|
if (client == null) {
|
|
return;
|
|
}
|
|
client.shutdown();
|
|
}
|
|
|
|
}
|
|
protected enum LOCK_TYPE {
|
|
ADD_DB,
|
|
ADD_ENTITY,
|
|
ADD_FACE,
|
|
LIST_DB,
|
|
LIST_FACE,
|
|
SEARCH_FACE,
|
|
DELETE_DB,
|
|
DELETE_ENTITY,
|
|
}
|
|
}
|