|
@@ -1,37 +1,14 @@
|
|
package cn.com.qmth.examcloud.starters.face.verify.service.impl;
|
|
package cn.com.qmth.examcloud.starters.face.verify.service.impl;
|
|
|
|
|
|
import cn.com.qmth.examcloud.starters.face.verify.FaceVerifyProperties;
|
|
import cn.com.qmth.examcloud.starters.face.verify.FaceVerifyProperties;
|
|
-import cn.com.qmth.examcloud.starters.face.verify.model.IResult;
|
|
|
|
-import cn.com.qmth.examcloud.starters.face.verify.model.baidu.BaiduAccessInfo;
|
|
|
|
-import cn.com.qmth.examcloud.starters.face.verify.model.baidu.BaiduAccessToken;
|
|
|
|
-import cn.com.qmth.examcloud.starters.face.verify.model.baidu.BaiduResponseInfo;
|
|
|
|
-import cn.com.qmth.examcloud.starters.face.verify.model.faceplus.FacePlusResponseInfo;
|
|
|
|
|
|
+import cn.com.qmth.examcloud.starters.face.verify.common.IResult;
|
|
import cn.com.qmth.examcloud.starters.face.verify.service.FaceVerifyService;
|
|
import cn.com.qmth.examcloud.starters.face.verify.service.FaceVerifyService;
|
|
-import cn.com.qmth.examcloud.starters.face.verify.utils.HttpClientBuilder;
|
|
|
|
-import cn.com.qmth.examcloud.starters.face.verify.utils.JsonHelper;
|
|
|
|
-import cn.com.qmth.examcloud.starters.face.verify.utils.StrUtils;
|
|
|
|
-import okhttp3.*;
|
|
|
|
-import org.apache.commons.io.IOUtils;
|
|
|
|
-import org.apache.commons.lang3.StringUtils;
|
|
|
|
|
|
+import cn.com.qmth.examcloud.starters.face.verify.service.baidu.BaiduApiHelper;
|
|
|
|
+import cn.com.qmth.examcloud.starters.face.verify.service.baidu.BaiduSession;
|
|
|
|
+import cn.com.qmth.examcloud.starters.face.verify.service.faceplus.FacePlusApiHelper;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import org.slf4j.LoggerFactory;
|
|
-import org.springframework.util.Base64Utils;
|
|
|
|
|
|
|
|
-import java.io.File;
|
|
|
|
-import java.io.FileInputStream;
|
|
|
|
-import java.io.IOException;
|
|
|
|
-import java.io.InputStream;
|
|
|
|
-import java.util.ArrayList;
|
|
|
|
-import java.util.List;
|
|
|
|
-import java.util.Map;
|
|
|
|
-import java.util.TreeMap;
|
|
|
|
-
|
|
|
|
-import static cn.com.qmth.examcloud.starters.face.verify.model.Constants.*;
|
|
|
|
-
|
|
|
|
-/**
|
|
|
|
- * @author: QMTH
|
|
|
|
- * @since: 2019/3/25
|
|
|
|
- */
|
|
|
|
public class FaceVerifyServiceImpl implements FaceVerifyService {
|
|
public class FaceVerifyServiceImpl implements FaceVerifyService {
|
|
|
|
|
|
private final static Logger log = LoggerFactory.getLogger(FaceVerifyServiceImpl.class);
|
|
private final static Logger log = LoggerFactory.getLogger(FaceVerifyServiceImpl.class);
|
|
@@ -39,319 +16,39 @@ public class FaceVerifyServiceImpl implements FaceVerifyService {
|
|
private FaceVerifyProperties properties;
|
|
private FaceVerifyProperties properties;
|
|
|
|
|
|
@Override
|
|
@Override
|
|
- public IResult detectFaceByBaidu(String targetImageUrl) {
|
|
|
|
- String accessToken = this.getAccessTokenByBaidu();
|
|
|
|
- return this.detectFaceByBaidu(accessToken, targetImageUrl, false);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- @Override
|
|
|
|
- public IResult detectFaceByBaidu(byte[] imageFileBytes) {
|
|
|
|
- String accessToken = this.getAccessTokenByBaidu();
|
|
|
|
- String base64 = Base64Utils.encodeToString(imageFileBytes);
|
|
|
|
- return this.detectFaceByBaidu(accessToken, base64, true);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- @Override
|
|
|
|
- public IResult detectFaceByBaidu(File targetImageFile) {
|
|
|
|
- String accessToken = this.getAccessTokenByBaidu();
|
|
|
|
- String base64 = StrUtils.toBase64(targetImageFile);
|
|
|
|
- return this.detectFaceByBaidu(accessToken, base64, true);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private IResult detectFaceByBaidu(String accessToken, String targetImage, boolean isBases64) {
|
|
|
|
- if (StringUtils.isBlank(accessToken)) {
|
|
|
|
- throw new IllegalArgumentException("Baidu accessToken must be not empty.");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (StringUtils.isBlank(targetImage)) {
|
|
|
|
- throw new IllegalArgumentException("targetImage must be not empty.");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- List params = new ArrayList();
|
|
|
|
- Map<String, String> values = new TreeMap<>();
|
|
|
|
- if (isBases64) {
|
|
|
|
- values.put("image_type", "BASE64");
|
|
|
|
- } else {
|
|
|
|
- values.put("image_type", "URL");
|
|
|
|
- }
|
|
|
|
- values.put("image", targetImage);
|
|
|
|
- params.add(values);
|
|
|
|
-
|
|
|
|
- JsonHelper jsonMapper = new JsonHelper();
|
|
|
|
- String jsonParams = jsonMapper.toJson(params);
|
|
|
|
-
|
|
|
|
- String requestUrl = String.format(BAI_DU_DETECT_FACE_URL, accessToken);
|
|
|
|
- IResult result = this.callForBaidu(jsonMapper, jsonParams, requestUrl);
|
|
|
|
-
|
|
|
|
- if (result != null && result.isTokenExpired()) {
|
|
|
|
- //Token过期,则重刷
|
|
|
|
- accessToken = refreshAccessTokenByBaidu();
|
|
|
|
- requestUrl = String.format(BAI_DU_DETECT_FACE_URL, accessToken);
|
|
|
|
- return this.callForBaidu(jsonMapper, jsonParams, requestUrl);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return result;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- @Override
|
|
|
|
- public IResult detectFaceWithFaceSizeByBaidu(File targetImageFile) {
|
|
|
|
- if (targetImageFile == null) {
|
|
|
|
- throw new IllegalArgumentException("targetImageFile must be not empty.");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- byte[] targetBytes;
|
|
|
|
- try (InputStream targetStream = new FileInputStream(targetImageFile);) {
|
|
|
|
- targetBytes = IOUtils.toByteArray(targetStream);
|
|
|
|
- } catch (IOException e) {
|
|
|
|
- log.error(e.getMessage(), e);
|
|
|
|
- return null;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return this.detectFaceWithFaceSizeByBaidu(targetBytes);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- @Override
|
|
|
|
- public IResult detectFaceWithFaceSizeByBaidu(byte[] imageFileBytes) {
|
|
|
|
- String accessToken = this.getAccessTokenByBaidu();
|
|
|
|
- String base64 = Base64Utils.encodeToString(imageFileBytes);
|
|
|
|
- Map<String, String> params = new TreeMap<>();
|
|
|
|
- params.put("max_face_num", "2");// 最多处理人脸的数目(默认值为1)
|
|
|
|
- params.put("image_type", "BASE64");
|
|
|
|
- params.put("image", base64);
|
|
|
|
-
|
|
|
|
- JsonHelper jsonMapper = new JsonHelper();
|
|
|
|
- String jsonParams = jsonMapper.toJson(params);
|
|
|
|
-
|
|
|
|
- String requestUrl = String.format(BAIDU_DETECT_FACE_WITH_FACE_SIZE_URL, accessToken);
|
|
|
|
- IResult result = this.callForBaidu(jsonMapper, jsonParams, requestUrl);
|
|
|
|
-
|
|
|
|
- if (result != null && result.isTokenExpired()) {
|
|
|
|
- //Token过期,则重刷
|
|
|
|
- accessToken = this.refreshAccessTokenByBaidu();
|
|
|
|
- requestUrl = String.format(BAIDU_DETECT_FACE_WITH_FACE_SIZE_URL, accessToken);
|
|
|
|
- return this.callForBaidu(jsonMapper, jsonParams, requestUrl);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return result;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private String getAccessTokenByBaidu() {
|
|
|
|
- if (BaiduAccessToken.isNotEmpty()) {
|
|
|
|
- return BaiduAccessToken.getValue();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- BaiduAccessInfo info = this.loadAccessTokenByBaidu();
|
|
|
|
- if (info == null || info.getAccess_token() == null) {
|
|
|
|
- throw new IllegalArgumentException("Baidu loadAccessToken fail.");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- BaiduAccessToken.setValue(info.getAccess_token());
|
|
|
|
- return info.getAccess_token();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private String refreshAccessTokenByBaidu() {
|
|
|
|
- BaiduAccessToken.setValue(null);
|
|
|
|
- return this.getAccessTokenByBaidu();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private BaiduAccessInfo loadAccessTokenByBaidu() {
|
|
|
|
- if (StringUtils.isBlank(properties.getBaiduKey()) || StringUtils.isBlank(properties.getBaiduSecret())) {
|
|
|
|
- throw new IllegalArgumentException("Baidu apiKey or apiSecret must be not empty.");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- String requestUrl = String.format(BAI_DU_TOKEN_URL, properties.getBaiduKey(), properties.getBaiduSecret());
|
|
|
|
- Request.Builder request = new Request.Builder().url(requestUrl).get();
|
|
|
|
-
|
|
|
|
- try (Response response = HttpClientBuilder.getClient().newCall(request.build()).execute();
|
|
|
|
- ResponseBody body = response.body();) {
|
|
|
|
- String bodyStr = body.string();
|
|
|
|
-
|
|
|
|
- if (log.isDebugEnabled()) {
|
|
|
|
- log.debug("[response] code: " + response.code() + " body: " + bodyStr);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (response.isSuccessful()) {
|
|
|
|
- return new JsonHelper().parseJson(bodyStr, BaiduAccessInfo.class);
|
|
|
|
- }
|
|
|
|
- } catch (IOException e) {
|
|
|
|
- log.error(e.getMessage(), e);
|
|
|
|
- }
|
|
|
|
- return null;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private BaiduResponseInfo callForBaidu(JsonHelper jsonMapper, String jsonParams, String requestUrl) {
|
|
|
|
- final String contentType = "application/json; charset=utf-8";
|
|
|
|
- RequestBody formBody = FormBody.create(MediaType.parse(contentType), jsonParams);
|
|
|
|
- Request.Builder request = new Request.Builder().url(requestUrl).post(formBody);
|
|
|
|
-
|
|
|
|
- try (Response response = HttpClientBuilder.getClient().newCall(request.build()).execute();
|
|
|
|
- ResponseBody body = response.body();) {
|
|
|
|
- String bodyStr = body.string();
|
|
|
|
-
|
|
|
|
- if (log.isDebugEnabled()) {
|
|
|
|
- log.debug("[response] code: " + response.code() + " body: " + bodyStr);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (response.isSuccessful()) {
|
|
|
|
- return jsonMapper.parseJson(bodyStr, BaiduResponseInfo.class);
|
|
|
|
- }
|
|
|
|
- } catch (IOException e) {
|
|
|
|
- log.error(e.getMessage(), e);
|
|
|
|
- }
|
|
|
|
- return null;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- @Override
|
|
|
|
- public IResult detectFaceByFacePlus(File targetImageFile) {
|
|
|
|
- if (targetImageFile == null) {
|
|
|
|
- throw new IllegalArgumentException("targetImageFile must be not empty.");
|
|
|
|
|
|
+ public IResult faceDetectByBaidu(String imageUrl) {
|
|
|
|
+ String accessToken = null;
|
|
|
|
+ if (properties.getBaiduAccessTokenEnabled()) {
|
|
|
|
+ BaiduSession session = BaiduApiHelper.getAccessToken(properties.getBaiduKey(), properties.getBaiduSecret(), properties.getBaiduTokenUrl());
|
|
|
|
+ accessToken = session.getAccess_token();
|
|
}
|
|
}
|
|
|
|
|
|
- byte[] targetBytes;
|
|
|
|
- try (InputStream targetStream = new FileInputStream(targetImageFile);) {
|
|
|
|
- targetBytes = IOUtils.toByteArray(targetStream);
|
|
|
|
- } catch (IOException e) {
|
|
|
|
- log.error(e.getMessage());
|
|
|
|
- return null;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return this.detectFaceByFacePlus(targetBytes, targetImageFile.getName());
|
|
|
|
|
|
+ return BaiduApiHelper.faceDetect(accessToken, properties.getBaiduFaceDetectUrl(), imageUrl);
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
- public IResult detectFaceByFacePlus(byte[] imageFileBytes, String imageFileName) {
|
|
|
|
- if (StringUtils.isBlank(properties.getFacePlusKey()) || StringUtils.isBlank(properties.getFacePlusSecret())) {
|
|
|
|
- throw new IllegalArgumentException("Face++ apiKey or apiSecret must be not empty.");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (imageFileBytes == null || imageFileBytes.length == 0) {
|
|
|
|
- throw new IllegalArgumentException("fileBytes must be not empty.");
|
|
|
|
|
|
+ public IResult faceCompareByBaidu(String imageUrl, String imageUrl2) {
|
|
|
|
+ String accessToken = null;
|
|
|
|
+ if (properties.getBaiduAccessTokenEnabled()) {
|
|
|
|
+ BaiduSession session = BaiduApiHelper.getAccessToken(properties.getBaiduKey(), properties.getBaiduSecret(), properties.getBaiduTokenUrl());
|
|
|
|
+ accessToken = session.getAccess_token();
|
|
}
|
|
}
|
|
|
|
|
|
- if (log.isDebugEnabled()) {
|
|
|
|
- log.debug("[request] " + FACE_PLUS_DETECT_FACE_URL + " imageFileName: " + imageFileName);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- RequestBody targetBody = RequestBody.create(MediaType.parse("application/octet-stream"), imageFileBytes);
|
|
|
|
- MultipartBody.Builder formBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
|
|
|
|
- .addFormDataPart("api_key", properties.getFacePlusKey())
|
|
|
|
- .addFormDataPart("api_secret", properties.getFacePlusSecret())
|
|
|
|
- .addFormDataPart("image_file", StrUtils.urlEncode(imageFileName), targetBody);
|
|
|
|
- return callForFacePlus(FACE_PLUS_DETECT_FACE_URL, formBody.build());
|
|
|
|
|
|
+ return BaiduApiHelper.faceCompare(accessToken, properties.getBaiduFaceCompareUrl(), imageUrl, imageUrl2);
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
- public IResult compareFaceByFacePlus(String sourceImageUrl, String targetImageUrl) {
|
|
|
|
- if (StringUtils.isBlank(properties.getFacePlusKey()) || StringUtils.isBlank(properties.getFacePlusSecret())) {
|
|
|
|
- throw new IllegalArgumentException("Face++ apiKey or apiSecret must be not empty.");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (StringUtils.isBlank(sourceImageUrl) || StringUtils.isBlank(targetImageUrl)) {
|
|
|
|
- throw new IllegalArgumentException("sourceImageUrl or targetImageUrl must be not empty.");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (log.isDebugEnabled()) {
|
|
|
|
- log.debug("[request] " + FACE_PLUS_COMPARE_FACE_URL + " sourceImageUrl: " + sourceImageUrl + " targetImageUrl: " + targetImageUrl);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- RequestBody formBody = new FormBody.Builder()
|
|
|
|
- .add("api_key", properties.getFacePlusKey())
|
|
|
|
- .add("api_secret", properties.getFacePlusSecret())
|
|
|
|
- .add("image_url1", sourceImageUrl)
|
|
|
|
- .add("image_url2", targetImageUrl)
|
|
|
|
- .build();
|
|
|
|
- return callForFacePlus(FACE_PLUS_COMPARE_FACE_URL, formBody);
|
|
|
|
|
|
+ public IResult faceDetectByFacePlus(String imageUrl) {
|
|
|
|
+ return FacePlusApiHelper.faceDetect(properties.getFacePlusKey(), properties.getFacePlusSecret(), properties.getFacePlusFaceDetectUrl(), imageUrl);
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
- public IResult compareFaceByFacePlus(File sourceImageFile, File targetImageFile) {
|
|
|
|
- if (StringUtils.isBlank(properties.getFacePlusKey()) || StringUtils.isBlank(properties.getFacePlusSecret())) {
|
|
|
|
- throw new IllegalArgumentException("Face++ apiKey or apiSecret must be not empty.");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (sourceImageFile == null || targetImageFile == null) {
|
|
|
|
- throw new IllegalArgumentException("sourceImageFile or targetImageFile must be not empty.");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (log.isDebugEnabled()) {
|
|
|
|
- log.debug("[request] " + FACE_PLUS_COMPARE_FACE_URL + " sourceImageFile: " + sourceImageFile.getName() + " targetImageFile: " + targetImageFile.getName());
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- byte[] sourceBytes, targetBytes;
|
|
|
|
- try (InputStream sourceStream = new FileInputStream(sourceImageFile);
|
|
|
|
- InputStream targetStream = new FileInputStream(targetImageFile);) {
|
|
|
|
- sourceBytes = IOUtils.toByteArray(sourceStream);
|
|
|
|
- targetBytes = IOUtils.toByteArray(targetStream);
|
|
|
|
- } catch (IOException e) {
|
|
|
|
- log.error(e.getMessage(), e);
|
|
|
|
- return null;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- RequestBody sourceBody = RequestBody.create(MediaType.parse("application/octet-stream"), sourceBytes);
|
|
|
|
- RequestBody targetBody = RequestBody.create(MediaType.parse("application/octet-stream"), targetBytes);
|
|
|
|
- MultipartBody.Builder formBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
|
|
|
|
- .addFormDataPart("api_key", properties.getFacePlusKey())
|
|
|
|
- .addFormDataPart("api_secret", properties.getFacePlusSecret())
|
|
|
|
- .addFormDataPart("image_file1", StrUtils.urlEncode(sourceImageFile.getName()), sourceBody)
|
|
|
|
- .addFormDataPart("image_file2", StrUtils.urlEncode(targetImageFile.getName()), targetBody);
|
|
|
|
- return callForFacePlus(FACE_PLUS_COMPARE_FACE_URL, formBody.build());
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- @Override
|
|
|
|
- public IResult compareFaceByFacePlus(String sourceImageUrl, File targetImageFile) {
|
|
|
|
- if (StringUtils.isBlank(properties.getFacePlusKey()) || StringUtils.isBlank(properties.getFacePlusSecret())) {
|
|
|
|
- throw new IllegalArgumentException("Face++ apiKey or apiSecret must be not empty.");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (StringUtils.isBlank(sourceImageUrl) || targetImageFile == null) {
|
|
|
|
- throw new IllegalArgumentException("sourceImageUrl or targetImageFile must be not empty.");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (log.isDebugEnabled()) {
|
|
|
|
- log.debug("[request] " + FACE_PLUS_COMPARE_FACE_URL + " sourceImageUrl: " + sourceImageUrl + " targetImageFile: " + targetImageFile.getName());
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- byte[] targetBytes;
|
|
|
|
- try (InputStream targetStream = new FileInputStream(targetImageFile);) {
|
|
|
|
- targetBytes = IOUtils.toByteArray(targetStream);
|
|
|
|
- } catch (IOException e) {
|
|
|
|
- log.error(e.getMessage(), e);
|
|
|
|
- return null;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- RequestBody targetBody = RequestBody.create(MediaType.parse("application/octet-stream"), targetBytes);
|
|
|
|
- MultipartBody.Builder formBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
|
|
|
|
- .addFormDataPart("api_key", properties.getFacePlusKey())
|
|
|
|
- .addFormDataPart("api_secret", properties.getFacePlusSecret())
|
|
|
|
- .addFormDataPart("image_url1", sourceImageUrl)
|
|
|
|
- .addFormDataPart("image_file2", StrUtils.urlEncode(targetImageFile.getName()), targetBody);
|
|
|
|
- return callForFacePlus(FACE_PLUS_COMPARE_FACE_URL, formBody.build());
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private FacePlusResponseInfo callForFacePlus(String url, RequestBody formBody) {
|
|
|
|
- Request.Builder request = new Request.Builder().url(url).post(formBody);
|
|
|
|
-
|
|
|
|
- try (Response response = HttpClientBuilder.getClient().newCall(request.build()).execute();
|
|
|
|
- ResponseBody body = response.body();) {
|
|
|
|
- String bodyStr = body.string();
|
|
|
|
-
|
|
|
|
- if (log.isDebugEnabled()) {
|
|
|
|
- log.debug("[response] code: " + response.code() + " body: " + bodyStr);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- //403有并发超限情况
|
|
|
|
- if (response.code() == 403 || response.isSuccessful()) {
|
|
|
|
- return new JsonHelper().parseJson(bodyStr, FacePlusResponseInfo.class);
|
|
|
|
- }
|
|
|
|
- } catch (IOException e) {
|
|
|
|
- log.error(e.getMessage(), e);
|
|
|
|
- }
|
|
|
|
- return null;
|
|
|
|
|
|
+ public IResult faceCompareByFacePlus(String imageUrl, String imageUrl2) {
|
|
|
|
+ return FacePlusApiHelper.faceCompare(properties.getFacePlusKey(), properties.getFacePlusSecret(), properties.getFacePlusFaceCompareUrl(), imageUrl, imageUrl2);
|
|
}
|
|
}
|
|
|
|
|
|
public void setProperties(FaceVerifyProperties properties) {
|
|
public void setProperties(FaceVerifyProperties properties) {
|
|
this.properties = properties;
|
|
this.properties = properties;
|
|
}
|
|
}
|
|
|
|
|
|
-}
|
|
|
|
|
|
+}
|